【问题标题】:How to a create a new dataframe of consolidated values from multiple columns in R如何从 R 中的多列创建合并值的新数据框
【发布时间】:2021-12-14 11:21:27
【问题描述】:

我有一个数据框 df1,如下所示:

sample 99_Ape_1 93_Cat_1 87_Ape_2 84_Cat_2 90_Dog_1 92_Dog_2
A 2 3 1 7 4 6
B 5 9 7 0 3 7
C 6 8 9 2 3 0
D 3 9 0 5 8 3

我想通过对标题行中存在的动物(即“Ape”、“Cat”、“Dog”)中的值求和来合并数据框,并最终得到以下数据框:

sample Ape Cat Dog
A 3 10 10
B 12 9 10
C 15 10 3
D 3 14 11

我创建了一个代表所有动物的列表,称为“animals_list”

然后我创建了一个数据帧列表,将每个动物子集到一个单独的数据帧中:

animals_extract <- c()

for (i in 1:length(animals_list)){
  species_extract[[i]] <- df1[, grep(animals_list[i], names(df1))]
}

然后我尝试按样本对行中的每个变量求和:

for (i in 1:length(species_extract)){
  species_extract[[i]]$total <- rowSums(species_extract[[i]])
} 

然后通过绑定新的“total”列中的所有值来创建数据框“animal_total”。

animal_total <- NULL

for (i in 1:length(species_extract)){
  animal_total[i] <- cbind(species_extract[[i]]$total)
}

不幸的是,这似乎根本不起作用,我想我可能走错了路。任何帮助将不胜感激!

编辑:我的数据框有 300 多只动物,这意味着将非常感谢使用我的标识符列表 (animals_list)!我还要注意,一些列名不遵循结构“number_animal_number”,因此我不能使用重复搜索(对不起!)。

【问题讨论】:

    标签: r for-loop sum data-manipulation


    【解决方案1】:

    data.table 方法

    library(data.table)
    library(rlist)
    #set data to data.table format
    setDT(df1)
    # split column 2:n by regex on column names
    L <- split.default(df1[,-1], gsub(".*_(.*)_.*", "\\1", names(df1)[-1]))
    # Bind together again
    data.table(sample = df1$sample, 
               as.data.table(list.cbind(lapply(L, rowSums))))
    #    sample Ape Cat Dog
    # 1:      A   3  10  10
    # 2:      B  12   9  10
    # 3:      C  15  10   3
    # 4:      D   3  14  11
    

    【讨论】:

    • 谢谢!这正是我正在寻找的,但不幸的是,并非所有列名都具有相同的“number_animal_number”结构。有没有办法使用我创建的名称列表来做同样的事情?抱歉,我的问题不清楚。
    • 完美。不知何故,我必须学习 data.table 编码!
    • @code_rookie 可以在正则表达式中捕获所需的列(可能的答案是 Yes ;-)),如果是这样,您可以使用上述方法。如果您不确定如何,您可以提出一个新问题,要求使用正则表达式,并将您的列名作为示例数据。
    【解决方案2】:

    更新:澄清后: 这可能取决于您的动物的其他名称。但这是一个开始:

    library(dplyr)
    library(tidyr)
    df %>% 
      pivot_longer(
        cols = -sample
      ) %>% 
      mutate(name1 = str_extract(name, '(?<=\\_)(.*?)(?=\\_)')) %>% 
      group_by(sample, name1) %>% 
      summarise(sum=sum(value)) %>% 
      pivot_wider(
        names_from = name1,
        values_from= sum
      )
    

    输出:

      sample   Ape   Cat   Dog
      <chr>  <int> <int> <int>
    1 A          3    10    10
    2 B         12     9    10
    3 C         15    10     3
    4 D          3    14    11
    

    第一个答案: 以下是我们如何使用dplyr

    library(dplyr)
    
    df %>% 
      mutate(Cat = rowSums(select(., contains("Cat"))),
             Ape = rowSums(select(., contains("Ape"))),
             Dog = rowSums(select(., contains("Dog")))) %>% 
      select(sample, Cat, Ape, Dog)
    
      sample   Ape   Cat   Dog
      <chr>  <int> <int> <int>
    1 A          3    10    10
    2 B         12     9    10
    3 C         15    10     3
    4 D          3    14    11
    

    【讨论】:

    • 谢谢!我的整个数据框有大约 300 只动物,这意味着这种方式的代码会很长。有没有办法使用我创建的名称列表来做同样的事情?抱歉,我的问题不清楚。我将编辑我的问题以使这一点显而易见。
    • 哦,我明白了。我去看看。
    • 非常感谢!
    • 请看我的更新。这可能取决于您的其他列名的模式。告诉我!
    • 谢谢你们。在我清理列名以使其具有常规模式后,此方法起到了一种治疗作用,如示例数据框中所示。谢谢!
    【解决方案3】:

    另一种 data.table 解决方案

    library(data.table)
    
    # Construct data table 
    dt <- as.data.table(list(sample = c("A", "B", "C", "D"), 
                             `99_Ape_1` = c(2, 5, 6, 3), 
                             `93_Cat_1` = c(3, 9, 8, 9), 
                             `87_Ape_2` = c(1, 7, 9, 0),
                             `84_Cat_2` = c(7, 0, 2, 5),
                             `90_Dog_1` = c(4, 3, 3, 8),
                             `92_Dog_2` = c(6, 7, 0, 3)))
    
    # Alternatively convert existing dataframe
    # dt <- setDT(df)
    
    # Use Regex pattern to drop ids from column names
    names(dt) <- gsub("((^[0-9_]{3})|(_[0-9]{1}$))", "", names(dt))
    
    # Pivot long (columns to rows)
    dt <- melt(dt, id.vars = "sample")
    
    # Aggregate sample by variable
    dt <- dt[, .(value=sum(value)), by=.(sample, variable)]
    
    # Unpivot (rows to colums)
    dcast(dt, sample ~ variable)
    
    #     sample Ape Cat Dog
    # 1:      A   3  10  10
    # 2:      B  12   9  10
    # 3:      C  15  10   3
    # 4:      D   3  14  11
    

    或者,保留列名(在 OP 对上一个答案的评论之后)并假设对相同样本有多个观察结果:

    dt <- as.data.table(list(sample = c("A", "B", "C", "D", "A"), 
                             `99_Ape_1` = c(2, 5, 6, 3, 1), 
                             `93_Cat_1` = c(3, 9, 8, 9, 1), 
                             `87_Ape_2` = c(1, 7, 9, 0, 1),
                             `84_Cat_2` = c(7, 0, 2, 5, 1),
                             `90_Dog_1` = c(4, 3, 3, 8, 1),
                             `92_Dog_2` = c(6, 7, 0, 3, 1)))
    
    dt
    
    #     sample 99_Ape_1 93_Cat_1 87_Ape_2 84_Cat_2 90_Dog_1 92_Dog_2
    # 1:      A        2        3        1        7        4        6
    # 2:      B        5        9        7        0        3        7
    # 3:      C        6        8        9        2        3        0
    # 4:      D        3        9        0        5        8        3
    # 5:      A        1        1        1        1        1        1
    
    # Pivot long (columns to rows)
    dt <- melt(dt, id.vars = "sample")
    
    # Aggregate sample by variable
    dt <- dt[, .(value=sum(value)), by=.(sample, variable)]
    
    # Unpivot (rows to colums)
    dcast(dt, sample ~ variable)
    
    #     sample 99_Ape_1 93_Cat_1 87_Ape_2 84_Cat_2 90_Dog_1 92_Dog_2
    # 1:      A        3        4        2        8        5        7
    # 2:      B        5        9        7        0        3        7
    # 3:      C        6        8        9        2        3        0
    # 4:      D        3        9        0        5        8        3
    
    

    【讨论】:

    • 感谢您的回答。我的编辑并不是说我希望保留相同的列名,我希望将它们聚合到基于动物的合并列中,但由于我有很多变量并且它们不遵循重复模式,我希望使用列表“animals_list”搜索和巩固。我希望这是有道理的。
    • 如果动物列表只是与列名匹配的值的字符向量,那么答案是有效的。如果它是将列名中的值映射到所讨论动物的物种的数据框,那么这需要在枢轴长转换之后进行额外的连接,以允许通过目标值而不是列名来聚合数据。如果动物列表是所需解决方案的关键部分,您应该在问题中包含一个最低限度的工作示例。
    猜你喜欢
    • 2019-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多