使用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
- 如果只填充第2列,则该值获取第1列的全部数量。
- 如果填充了第 2 列和第 3 列,则第 1 列中的值被分成两半。
- 如果填充了第 2、3 和 4 列,则第 1 列中的值将一分为三。
- 如果填充了第 2 列和第 4 列,则第 1 列中的值除以 75/25。
因此,对于上面的示例输出,我的新数据框将是:
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
我正在尝试生成每个因子水平的加权和。我有四列数据:
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
- 如果只填充第2列,则该值获取第1列的全部数量。
- 如果填充了第 2 列和第 3 列,则第 1 列中的值被分成两半。
- 如果填充了第 2、3 和 4 列,则第 1 列中的值将一分为三。
- 如果填充了第 2 列和第 4 列,则第 1 列中的值除以 75/25。
因此,对于上面的示例输出,我的新数据框将是:
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