使用tidyverse的R中多列的加权和

Weighted sum of multiple columns in R using tidyverse

我正在尝试生成每个因子水平的加权和。我有四列数据:

col1 = surface area
col 2 = dominant
col 3 = codominant
col 4 = sub

1    2    3     4
125  A    NA    NA
130  A    NA    B
150  C    B     NA
160  B    NA    NA
90   B    A     NA
180  C    A     B

因此,对于上面的示例输出,我的新数据框将是:

1    2
A    326.9
B    331.4
C    134.4

我摆弄了 ifelse 并提出了类似的东西(对于这个例子的两列):

     df1 <- df %>% 
            mutate(weighted_dominant = ifelse(!is.na(dominant) & is.na(codominant), Surface_Area, 
            Surface_Area/2),
                   weighted_codominant = ifelse(!is.na(codominant), Surface_Area/2, NA )

现在我隔离了兴趣集的列:

df2 <- df1 %>% select(dominant, weighted_dominant) %>% 
               group by (dominant) %>%
               summarise (sum = sum(weighted_dominant) 

也对共显列执行此操作,绑定两个新数据帧的行并再次执行汇总功能。

这完成了工作,但也需要大约 50 行代码,在我看来,这不是很干净。

我的问题: 是否有更好的(tidyverse)方法来进行这种加权汇总?

这是您要找的吗:

# your data
df <- read.table(text = "
125  A    NA    NA
130  A    NA    B
150  C    B     NA
160  B    NA    NA
90   B    A     NA
180  C    A     B", header = FALSE)
names(df) <- c("surface_area", "dominant", "codominant", "sub")


# make a matrix out of the last 3 columns
m <- as.matrix(df[2:4])


# get a logical matrix of non-NA
x <- !is.na(m)


# calculate as follow:
# 2  NA  NA  ->  1    0    0
# 2   3  NA  ->  1/2  1/2  0
# 2  NA   4  ->  1/2  0    1/2
# 2   3   4  ->  1/3  1/3  1/3
x <- x * (1/rowSums(x))


# correct
# 2  NA   4  ->  0.75  0  0.25
x[apply(x, 1, identical, c(dominant=0.5,codominant=0,sub=0.5)),] <- c(dominant=0.75,codominant=0,sub=0.25)


# multiply by surface_area
x <- x * df$surface_area


# get unique letters
l <- sort(unique(c(m)))
l <- l[!is.na(l)]


# sum by each letter
r <- sapply(l, function(i) sum(x[m==i], na.rm = TRUE))


# create final dataframe
data.frame(X1 = names(r), X2 = unname(r))
#>   X1    X2
#> 1  A 327.5
#> 2  B 372.5
#> 3  C 135.0

最后的数字不一样,我是不是理解错了你的解释?我不是很清楚。

有了tidyverse,您可以考虑以下方法。

将行号作为单独的列包括在内,以便您可以在每一行内进行评估。 pivot_longer 会将您的数据转换为长格式。

按行号分组后,您可以根据缺少的列确定 A、B 和 C 的值。这假设始终有一个“主导”列(否则,您可以在此处调整逻辑)。

然后,删除你的 NA,并计算 A、B 和 C 的权重值。

df %>%
  mutate(rn = row_number()) %>%
  pivot_longer(cols = c(dominant, codominant, sub)) %>%
  group_by(rn) %>%
  mutate(weight = case_when(
    is.na(value[name == "codominant"]) & is.na(value[name == "sub"]) ~ as.numeric(Surface_Area),
    is.na(value[name == "codominant"]) & name == "dominant" ~ Surface_Area * .75,
    is.na(value[name == "codominant"]) & name == "sub" ~ Surface_Area * .25,
    is.na(value[name == "sub"]) ~ Surface_Area / 2,
    TRUE ~ Surface_Area / 3
  )) %>%
  drop_na() %>%
  group_by(value) %>%
  summarise(total = sum(weight))

输出

  value total
  <chr> <dbl>
1 A      328.
2 B      372.
3 C      135