R/dplyr:基于多个动态变量名进行变异

R/dplyr:基于多个动态变量名进行变异,r,dplyr,tidyverse,lapply,mutate,R,Dplyr,Tidyverse,Lapply,Mutate,我有一个数据帧列表,每个数据帧包含多个包含表面积值的变量(以“\u区域”结尾)。对于每个表面积变量,都有相应的转换因子(以“_单位”结尾),我想用它来计算第三个变量,该变量包含标准测量单位中的面积。我希望这些变量以“\u area\u ha”结尾。 下面是我的示例数据帧: a <- tibble(a1_area = c(1,1,1), a2_area_unit = c(1,1,0.5), a2_area = c(1,1,1), a1_area_unit = c(1,

我有一个数据帧列表,每个数据帧包含多个包含表面积值的变量(以“\u区域”结尾)。对于每个表面积变量,都有相应的转换因子(以“_单位”结尾),我想用它来计算第三个变量,该变量包含标准测量单位中的面积。我希望这些变量以“\u area\u ha”结尾。

下面是我的示例数据帧:

a <- tibble(a1_area = c(1,1,1), a2_area_unit = c(1,1,0.5), a2_area = c(1,1,1),
            a1_area_unit = c(1,0.5,0.5), abc = c(1,2,3))

b <- tibble(b1_area = c(1,1,1), b1_area_unit = c(1,1,0.5), b2_area = c(1,1,1),
            b2_area_unit = c(1,0.5,0.5), abc = c(1,2,3))

ab_list <- list(a, b)

names(ab_list) <- c("a", "b")
有没有办法让mutate_at中的函数根据函数中初始变量的名称从父数据帧访问变量?


当然,我很高兴看到关于tidyverse方法的任何其他建议,即基于动态变量名称计算“_ha”变量。

tidyverse函数最适合处理“长”格式的数据,其中每一行代表一个唯一的数据点。为此,您需要使用
tidyr::pivot\u longer
函数:

# Join dataframes
dplyr::bind_cols(a, b) %>%
# Convert to area columns to long format
tidyr::pivot_longer(
  cols = dplyr::ends_with('area'),
  names_to = 'site',
  values_to = 'area'
) %>%
# Convert unit columns to long format
tidyr::pivot_longer(
  cols = dplyr::ends_with('unit'),
  names_to = 'site2',
  values_to = 'unit'
) %>%
# Just extract first 2 characters of the site column to get unique ID
dplyr::mutate(
  site = stringr::str_sub(site, 1, 2)
) %>%
# Remove redundant columns
dplyr::select(abc, site, area, unit) %>%
# Calculate area in HA
dplyr::mutate(
  area_ha = area * unit
)

一旦数据是长格式,就可以使用
dplyr::mutate
将面积列乘以单位列,得到面积列。如果您想将数据转换回原始格式,可以使用
tidyr::pivot\u wider
将数据转换回宽频格式,这将为您提供名称为a1\u area\u ha、a2\u area\u ha等的列。

好问题。下面是一个基本的R解决方案。我确信它可以适应tidyverse解决方案(例如,使用
purr::map2()
)。在这里,我构建了一个进行基本测试的函数,然后将其与
lappy()
一起使用。注意:答案是根据您的示例定制的,因此如果您的值/单位有不同的列名,则需要对其进行调整。希望这有帮助

val_by_unit <- function(data) {

  df <- data[order(names(data))]

  # Selecting columns for values and units
  val <- df[endsWith(names(df), "area")]
  unit <- df[endsWith(names(df), "unit")]


  # Check names are multiplying correctly
  if(!all(names(val) == sub("_unit", "", names(unit)))) {
    stop("Not all areas have a corresponding unit")
  }

  # Multiplying corresponding columns
  output <- Map(`*`, val, unit)

  # Renaming output and adding columns   
  data[paste0(names(output), "_ha")] <- output
  data
}

val\u by\u单位谢谢!这对我来说很好,避免了循环。我也会研究map2。由于我的实际数据帧较长,并且我有多个值/单位组合(面积/公顷、重量/千克等),因此我修改了您的函数,以同时获取值的参数,例如“面积”、单位(例如“单位”)和输出单位(例如“公顷”)。此解决方案对我来说效果很好,直到我尝试使用pivot\u Wither将其转换回原始格式:tidyr::pivot\u Wither(名称\u from=site,值\u from=c(面积,面积\u ha,单位)。使用此方法会为值\u from中的每个元素提供错误消息(1:
区域
中的值没有唯一标识;输出将包含列表列等)。我尝试在pivot_加宽(mutate(row=row_number())之前使用唯一标识符,但这只会导致更大的数据帧(12 x 8)每一行包含a1或a2的值,而其余的是NA的值。你知道如何解决这个问题吗?
# Join dataframes
dplyr::bind_cols(a, b) %>%
# Convert to area columns to long format
tidyr::pivot_longer(
  cols = dplyr::ends_with('area'),
  names_to = 'site',
  values_to = 'area'
) %>%
# Convert unit columns to long format
tidyr::pivot_longer(
  cols = dplyr::ends_with('unit'),
  names_to = 'site2',
  values_to = 'unit'
) %>%
# Just extract first 2 characters of the site column to get unique ID
dplyr::mutate(
  site = stringr::str_sub(site, 1, 2)
) %>%
# Remove redundant columns
dplyr::select(abc, site, area, unit) %>%
# Calculate area in HA
dplyr::mutate(
  area_ha = area * unit
)
val_by_unit <- function(data) {

  df <- data[order(names(data))]

  # Selecting columns for values and units
  val <- df[endsWith(names(df), "area")]
  unit <- df[endsWith(names(df), "unit")]


  # Check names are multiplying correctly
  if(!all(names(val) == sub("_unit", "", names(unit)))) {
    stop("Not all areas have a corresponding unit")
  }

  # Multiplying corresponding columns
  output <- Map(`*`, val, unit)

  # Renaming output and adding columns   
  data[paste0(names(output), "_ha")] <- output
  data
}
lapply(ab_list, val_by_unit)

$a
# A tibble: 3 x 7
  a1_area a2_area_unit a2_area a1_area_unit   abc a1_area_ha a2_area_ha
    <dbl>        <dbl>   <dbl>        <dbl> <dbl>      <dbl>      <dbl>
1       1          1         1          1       1        1          1  
2       1          1         1          0.5     2        0.5        1  
3       1          0.5       1          0.5     3        0.5        0.5

$b
# A tibble: 3 x 7
  b1_area b1_area_unit b2_area b2_area_unit   abc b1_area_ha b2_area_ha
    <dbl>        <dbl>   <dbl>        <dbl> <dbl>      <dbl>      <dbl>
1       1          1         1          1       1        1          1  
2       1          1         1          0.5     2        1          0.5
3       1          0.5       1          0.5     3        0.5        0.5