使用 dplyr 跨列进行条件求和
Conditional summing across columns with dplyr
我有一个数据框,其中包含在八个月内采样的四个栖息地。每个月从每个栖息地收集十个样本。计算每个样本中物种的个体数量。以下代码生成一个结构相似的较小数据框。
# Pseudo data
Habitat <- factor(c(rep("Dry",6), rep("Wet",6)), levels = c("Dry","Wet"))
Month <- factor(rep(c(rep("Jan",2), rep("Feb",2), rep("Mar",2)),2), levels=c("Jan","Feb","Mar"))
Sample <- rep(c(1,2),6)
Species1 <- rpois(12,6)
Species2 <- rpois(12,6)
Species3 <- rpois(12,6)
df <- data.frame(Habitat,Month, Sample, Species1, Species2, Species3)
我想按月对所有采样物种的个体总数求和。我正在使用 ddply
(首选),但我愿意接受其他建议。
我得到的最接近结果是将每列的总和加在一起,如此处所示。
library(plyr)
ddply(df, ~ Month, summarize, tot_by_mon = sum(Species1) + sum(Species2) + sum(Species3))
# Month tot_by_mon
# 1 Jan 84
# 2 Feb 92
# 3 Mar 67
这可行,但我想知道是否有通用方法来处理具有 "unknown" 个物种的案例。也就是说,第一个物种总是从第 4 列开始,但最后一个物种可能在第 10 列或第 42 列。我不想将实际物种名称硬编码到摘要函数中。请注意,物种名称差异很大,例如 Doryflav 和 Pheibica。
假设 Species
s 列都以 Species
开头,您可以通过前缀 select 它们并使用 group_by %>% do
:
求和
library(tidyverse)
df %>%
group_by(Month) %>%
do(tot_by_mon = sum(select(., starts_with('Species')))) %>%
unnest()
# A tibble: 3 x 2
# Month tot_by_mon
# <fctr> <int>
#1 Jan 63
#2 Feb 67
#3 Mar 58
如果列名不遵循某种模式,您可以按列位置 select,例如,如果 Species 列从数据框的第 4 位到末尾:
df %>%
group_by(Month) %>%
do(tot_by_mon = sum(select(., 4:ncol(.)))) %>%
unnest()
# A tibble: 3 x 2
# Month tot_by_mon
# <fctr> <int>
#1 Jan 63
#2 Feb 67
#3 Mar 58
这是 data.table
的另一个解决方案,无需知道 "Species" 列的名称:
library(data.table)
DT = melt(setDT(df), id.vars = c("Habitat", "Month", "Sample"))
DT[, .(tot_by_mon=sum(value)), by = "Month"]
或者如果你想要它紧凑,这里有一个单行:
melt(setDT(df), 1:3)[, .(tot_by_mon=sum(value)), by = "Month"]
结果:
Month tot_by_mon
1: Jan 90
2: Feb 81
3: Mar 70
数据:(设置种子以使示例可重现)
set.seed(123)
Habitat <- factor(c(rep("Dry",6), rep("Wet",6)), levels = c("Dry","Wet"))
Month <- factor(rep(c(rep("Jan",2), rep("Feb",2), rep("Mar",2)),2), levels=c("Jan","Feb","Mar"))
Sample <- rep(c(1,2),6)
Species1 <- rpois(12,6)
Species2 <- rpois(12,6)
Species3 <- rpois(12,6)
df <- data.frame(Habitat,Month, Sample, Species1, Species2, Species3)
这是 data.table
的另一个选项,无需重塑为 'long' 格式
library(data.table)
setDT(df)[, .(tot_by_mon = Reduce(`+`, lapply(.SD, sum))), Month,
.SDcols = Species1:Species3]
# Month tot_by_mon
#1: Jan 90
#2: Feb 81
#3: Mar 70
或者使用 tidyverse
,我们还可以使用 map
高效的函数
library(dplyr)
library(purrr)
df %>%
group_by(Month) %>%
nest(starts_with('Species')) %>%
mutate(tot_by_mon = map_int(data, ~sum(unlist(.x)))) %>%
select(-data)
# A tibble: 3 x 2
# Month tot_by_mon
# <fctr> <int>
#1 Jan 90
#2 Feb 81
#3 Mar 70
数据
set.seed(123)
Habitat <- factor(c(rep("Dry",6), rep("Wet",6)), levels = c("Dry","Wet"))
Month <- factor(rep(c(rep("Jan",2), rep("Feb",2), rep("Mar",2)),2),
levels=c("Jan","Feb","Mar"))
Sample <- rep(c(1,2),6)
Species1 <- rpois(12,6)
Species2 <- rpois(12,6)
Species3 <- rpois(12,6)
df <- data.frame(Habitat,Month, Sample, Species1, Species2, Species3)
与@useR 对 data.table 的 melt
的回答类似,您可以使用 tidyr 对 gather
:
进行整形
library(tidyr)
library(dplyr)
gather(df, Species, Value, matches("Species")) %>%
group_by(Month) %>% summarise(z = sum(Value))
# A tibble: 3 x 2
Month z
<fctr> <int>
1 Jan 90
2 Feb 81
3 Mar 70
如果您知道按位置而不是模式的列是 "matched"...
gather(df, Species, Value, -(1:3)) %>%
group_by(Month) %>% summarise(z = sum(Value))
(使用@akrun 的 set.seed(123)
示例数据显示的结果。)
我有一个数据框,其中包含在八个月内采样的四个栖息地。每个月从每个栖息地收集十个样本。计算每个样本中物种的个体数量。以下代码生成一个结构相似的较小数据框。
# Pseudo data
Habitat <- factor(c(rep("Dry",6), rep("Wet",6)), levels = c("Dry","Wet"))
Month <- factor(rep(c(rep("Jan",2), rep("Feb",2), rep("Mar",2)),2), levels=c("Jan","Feb","Mar"))
Sample <- rep(c(1,2),6)
Species1 <- rpois(12,6)
Species2 <- rpois(12,6)
Species3 <- rpois(12,6)
df <- data.frame(Habitat,Month, Sample, Species1, Species2, Species3)
我想按月对所有采样物种的个体总数求和。我正在使用 ddply
(首选),但我愿意接受其他建议。
我得到的最接近结果是将每列的总和加在一起,如此处所示。
library(plyr)
ddply(df, ~ Month, summarize, tot_by_mon = sum(Species1) + sum(Species2) + sum(Species3))
# Month tot_by_mon
# 1 Jan 84
# 2 Feb 92
# 3 Mar 67
这可行,但我想知道是否有通用方法来处理具有 "unknown" 个物种的案例。也就是说,第一个物种总是从第 4 列开始,但最后一个物种可能在第 10 列或第 42 列。我不想将实际物种名称硬编码到摘要函数中。请注意,物种名称差异很大,例如 Doryflav 和 Pheibica。
假设 Species
s 列都以 Species
开头,您可以通过前缀 select 它们并使用 group_by %>% do
:
library(tidyverse)
df %>%
group_by(Month) %>%
do(tot_by_mon = sum(select(., starts_with('Species')))) %>%
unnest()
# A tibble: 3 x 2
# Month tot_by_mon
# <fctr> <int>
#1 Jan 63
#2 Feb 67
#3 Mar 58
如果列名不遵循某种模式,您可以按列位置 select,例如,如果 Species 列从数据框的第 4 位到末尾:
df %>%
group_by(Month) %>%
do(tot_by_mon = sum(select(., 4:ncol(.)))) %>%
unnest()
# A tibble: 3 x 2
# Month tot_by_mon
# <fctr> <int>
#1 Jan 63
#2 Feb 67
#3 Mar 58
这是 data.table
的另一个解决方案,无需知道 "Species" 列的名称:
library(data.table)
DT = melt(setDT(df), id.vars = c("Habitat", "Month", "Sample"))
DT[, .(tot_by_mon=sum(value)), by = "Month"]
或者如果你想要它紧凑,这里有一个单行:
melt(setDT(df), 1:3)[, .(tot_by_mon=sum(value)), by = "Month"]
结果:
Month tot_by_mon
1: Jan 90
2: Feb 81
3: Mar 70
数据:(设置种子以使示例可重现)
set.seed(123)
Habitat <- factor(c(rep("Dry",6), rep("Wet",6)), levels = c("Dry","Wet"))
Month <- factor(rep(c(rep("Jan",2), rep("Feb",2), rep("Mar",2)),2), levels=c("Jan","Feb","Mar"))
Sample <- rep(c(1,2),6)
Species1 <- rpois(12,6)
Species2 <- rpois(12,6)
Species3 <- rpois(12,6)
df <- data.frame(Habitat,Month, Sample, Species1, Species2, Species3)
这是 data.table
的另一个选项,无需重塑为 'long' 格式
library(data.table)
setDT(df)[, .(tot_by_mon = Reduce(`+`, lapply(.SD, sum))), Month,
.SDcols = Species1:Species3]
# Month tot_by_mon
#1: Jan 90
#2: Feb 81
#3: Mar 70
或者使用 tidyverse
,我们还可以使用 map
高效的函数
library(dplyr)
library(purrr)
df %>%
group_by(Month) %>%
nest(starts_with('Species')) %>%
mutate(tot_by_mon = map_int(data, ~sum(unlist(.x)))) %>%
select(-data)
# A tibble: 3 x 2
# Month tot_by_mon
# <fctr> <int>
#1 Jan 90
#2 Feb 81
#3 Mar 70
数据
set.seed(123)
Habitat <- factor(c(rep("Dry",6), rep("Wet",6)), levels = c("Dry","Wet"))
Month <- factor(rep(c(rep("Jan",2), rep("Feb",2), rep("Mar",2)),2),
levels=c("Jan","Feb","Mar"))
Sample <- rep(c(1,2),6)
Species1 <- rpois(12,6)
Species2 <- rpois(12,6)
Species3 <- rpois(12,6)
df <- data.frame(Habitat,Month, Sample, Species1, Species2, Species3)
与@useR 对 data.table 的 melt
的回答类似,您可以使用 tidyr 对 gather
:
library(tidyr)
library(dplyr)
gather(df, Species, Value, matches("Species")) %>%
group_by(Month) %>% summarise(z = sum(Value))
# A tibble: 3 x 2
Month z
<fctr> <int>
1 Jan 90
2 Feb 81
3 Mar 70
如果您知道按位置而不是模式的列是 "matched"...
gather(df, Species, Value, -(1:3)) %>%
group_by(Month) %>% summarise(z = sum(Value))
(使用@akrun 的 set.seed(123)
示例数据显示的结果。)