[英]R/dplyr: Mutate based on multiple dynamic variable names
我有一個數據框列表,每個數據框都包含多個包含表面積值的變量(以“_area”結尾)。 對於每個表面積變量,我想使用相應的轉換因子(以“_unit”結尾)來計算第三個變量,該變量包含標准測量單位的面積。 我希望這些變量以“_area_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")
我知道如何在循環的幫助下做到這一點,但想了解如何在 tidyverse/dplyr 邏輯中做到這一點。 我的循環(它給了我想要的輸出)看起來像這樣:
df_names <- names(ab_list)
for (d in df_names) {
df <- ab_list[[d]]
var_names <- names(select(df, matches("_area$")))
for (v in var_names) {
int <- df %>% select(all_of(v),)
int2 <- df %>% select(matches(paste0(names(int), "_unit")))
int3 <- int*int2
names(int3) <- paste0(names(int), "_ha")
df <- cbind(df, int3)
rm(int, int2, int3)
}
ab_list[[d]] <- tibble(df)
rm(df)
}
> ab_list
$`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
我曾嘗試使用 lapply 和 mutate_at 但我的方法不起作用。 如果我理解正確,這是因為我的環境是嵌套的,我無法訪問計算變量“ha”的 function 中的 x。
ab_list %>%
lapply(function(x) mutate_at(x, vars(matches("_area$")), list(ha = ~.*x[[paste0(names(.),"_unit")]])))
Error: Column `a1_area_ha` must be length 3 (the number of rows) or one, not 0
有沒有辦法讓 mutate_at 中的 function 根據 function 中的初始變量的名稱訪問父數據幀中的變量?
對於基於動態變量名稱計算“_ha”變量的 tidyverse 方法的任何其他建議,我當然會很高興。
好問題。 下面是一個基本的 R 解決方案。 我確信它可以適應 tidyverse 解決方案(例如,使用purrr::map2()
)。 在這里,我構建了一個 function 進行基本測試,然后將其與lapply()
一起使用。 注意:答案是為您的示例量身定制的,因此如果值/單位的列名不同,則需要對其進行調整。 希望這可以幫助!!
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
tidyverse
函數最適用於“長”格式的數據,其中每一行都代表一個唯一的數據點。 為此,您需要使用tidyr::pivot_longer
function:
# 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
將您的面積列乘以單位列以獲得 area_ha 列。 如果要將數據轉換回其原始格式,可以使用tidyr::pivot_wider
將數據轉換回寬格式,這將為您提供名稱為 a1_area_ha、a2_area_ha 等的列。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.