使用dplyr跨列进行条件求和

时间:2017-10-31 19:13:56

标签: r dataframe dplyr plyr summarize

我有一个数据框,在八个月内有四个栖息地。每个月从每个栖息地收集十个样本。计算每个样品中物种的个体数量。以下代码生成类似结构的较小数据框。

for (int a = 1; a <= 100; a++) {
    if (a % 5 == 0) {
        println(a / 5);
    } else if (a % 12 == 0) {
        println(a / 12);
    } else {
        println(a);
    }
}

我想在所有采样的物种中按月总计个体总数。我正在使用# 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

这有效,但我想知道是否有一种通用的方法来处理具有“未知”物种数量的案例。也就是说,第一个物种总是从第4列开始,但最后一个物种可以在第10或第42列。我不想将实际物种名称硬编码到摘要函数中。请注意,物种名称差别很大,如Doryflav和Pheibica。

4 个答案:

答案 0 :(得分:3)

这是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)

答案 1 :(得分:3)

与@ useR对data.table&#39; s 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

如果您知道按位置而不是模式的列,则匹配&#34; ...

gather(df, Species, Value, -(1:3)) %>% 
  group_by(Month) %>% summarise(z = sum(Value))

(使用@ akrun&#39; s set.seed(123)示例数据显示结果。)

答案 2 :(得分:2)

假设Species的所有列都以Species开头,您可以使用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

如果列名不遵循模式,则可以按列位置进行选择,例如,如果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

答案 3 :(得分:2)

以下是var userTimeline = []; var params = { screen_name: "ZodiacFacts" }; cb.__call('statuses_userTimeline', params, function (reply, rate, error) { userTimeline = reply.filter(function (status) { return /^Aquarius/i.test(status); }); console.log(userTimeline); }); 的另一个选项,但没有重新定义为&#39; long&#39;格式

data.table

或者使用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