【问题标题】:R: sum values in rows when name is the sameR:当名称相同时,对行中的值求和
【发布时间】:2017-10-24 02:11:58
【问题描述】:

我在 R 中有一个数据框,我想对共享名称的行中的值求和,并记录每一行的假名称。谁能想到一种有效的方法来做到这一点?我写了一个 for 循环(复制如下),但在大型数据集上效率极低。

提前致谢!

此数据集中给出了一些信息:(1) 不超过 2 个站点共享一个名称,(2) 这两个站点将始终在连续行中紧随其后。尽管我对其他框架不太熟悉,但我也对非 R 解决方案持开放态度。

初始数据示例:

name, fake_name, value1, value2, value3
siteX, siteX, 4, 2, 0.5
siteX, siteX2, 1, 4, 0.2
siteY, siteY, 2, 1, 0.4
siteZ, siteZ, 8, 3, 0.2

转换为:

name, value1, value2, value3, fake_name, dup_fake_name
siteX, 5, 6, 0.7, siteX, siteX2
siteY, 2, 1, 0.4, siteY, NA
siteZ, 8, 3, 0.2, siteZ, NA

for循环版本:

data[,c(1,3:5,2)] -> d2; d2$dup_fake_name <- NA
for (i in 1:(nrow(data)-1) {
  if (data$name[i] == data$name[i+1]) {
    data$value[i] <- data$value[i] + data$value[i+1]
    data$dup_fake_name[i] <- data$fake_name[i+1]
}}

【问题讨论】:

    标签: r for-loop duplicates


    【解决方案1】:

    如果您不关心转换后数据中的最后两列,您可以aggregate 条目

     df <- structure(list(
         name = c("siteX", "siteX", "siteY", "siteZ"),
         fake_name = c("siteX", "siteX2", "siteY", "siteZ"), 
         value1 = c(4L, 1L, 2L, 8L), 
         value2 = c(2L, 4L, 1L, 3L), 
         value3 = c(0.5, 0.2, 0.4, 0.2)), 
         .Names = c("name", "fake_name", "value1", "value2", "value3"), 
         class = "data.frame", row.names = c(NA, -4L))
    
     df.agg <- aggregate(cbind(value1, value2, value3) ~ name, data = df, FUN = sum);
     #name value1 value2 value3
     #1 siteX      5      6    0.7
     #2 siteY      2      1    0.4
     #3 siteZ      8      3    0.2
    

    添加 ID 有点多(丑陋)的工作:

    # Split based on df$name
    ID <- lapply(split(df, df$name), function(x) x$fake_name)
    
    # Pad with NA's to have the same number of columns 
    ID <- do.call(rbind.data.frame, lapply(ID, function(x) {
        if (length(x) < max(sapply(ID, length))) {
            x <- c(x, rep(NA, max(sapply(ID, length)) - length(x)))
        }
        return(x); 
    }))
    colnames(ID) <- paste("fake_name_", 1:ncol(ID), sep = "");
    
    # Add ID columns to df.agg
    df.agg <- cbind.data.frame(df.agg, ID);
    df.agg;
    #  name value1 value2 value3 fake_name_1 fake_name_2
    #1 siteX      5      6    0.7       siteX      siteX2
    #2 siteY      2      1    0.4       siteY        <NA>
    #3 siteZ      8      3    0.2       siteZ        <NA>
    

    【讨论】:

    • 这看起来差不多完美,但我不知道tmp 指的是什么
    • 抱歉。 ID 曾经被称为 tmp,当我试图把这个例子放在一起时。我已经更正了我的代码。
    • 不用担心,很高兴它成功了。如果这回答了您的问题,请接受答案以关闭问题。
    • @jogo 是的,我之前也尝试过类似的方法。当您尝试根据 OP 请求添加 NA 以填充条目时,它会变得“丑陋”; @akrun 使用 data.table 有一个非常简洁(而且更短)的解决方案。
    【解决方案2】:

    这是一个使用data.table 的选项。将'data.frame'转换为'data.table'(setDT(df1)),按'name'分组,得到'value'列的sumdcast'fake_name'为宽格式并加入@987654325 @“名称”列

    library(data.table)
    setDT(df1)[, lapply(.SD, sum), by = name, .SDcols = value1:value3
           ][dcast(df1, name~ paste0("fake_name", rowid(name)), value.var = 'fake_name'), 
              on = .(name)]
    #    name value1 value2 value3 fake_name1 fake_name2
    #1: siteX      5      6    0.7      siteX     siteX2
    #2: siteY      2      1    0.4      siteY         NA
    #3: siteZ      8      3    0.2      siteZ         NA
    

    【讨论】:

      【解决方案3】:

      fake_name 列可以根据需要拆分,但这里有一个版本

      library(tidyverse)
      
      df <- data.frame(name = c('siteX', 'siteX', 'siteY', 'siteZ'), 
                 fake_name = c('siteX', 'siteX2', 'siteY', 'siteZ'),
                 value1 = c(4,1,2,8),
                 value2 = c(2,4,1,3),
                 value3 = c(0.5,0.2,0.4,0.2))
      
      df %>% 
        group_by(name) %>% 
        mutate(id = 1:n(),
               dup_fake_name = ifelse(id==2, fake_name, NA)) %>% 
        summarise(value1 = sum(value1),
                  value2 = sum(value2),
                  value3 = sum(value3),
                  fake_name = toString(fake_name))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-09
        • 1970-01-01
        • 2021-08-29
        • 1970-01-01
        • 2021-01-18
        • 1970-01-01
        相关资源
        最近更新 更多