【问题标题】:Cumulative count of unique values in RR中唯一值的累积计数
【发布时间】:2013-03-19 20:50:43
【问题描述】:

我的数据集的简化版本如下所示:

depth value
   1     a
   1     b
   2     a
   2     b
   2     b
   3     c

我想创建一个新数据集,其中对于“深度”的每个值,我将拥有从顶部开始的唯一值的累积数量。例如

depth cumsum
 1      2
 2      2
 3      3

关于如何做到这一点的任何想法?我对 R 比较陌生。

【问题讨论】:

    标签: r unique cumulative-sum


    【解决方案1】:

    dplyr 尝试。

    df %>%
      #group_by(group)%>% if you have a third variable and you want to achieve the same results for each group
      mutate(cum_unique_entries = cumsum(!duplicated(value))) %>%
      group_by(depth) %>% # add group variable for more layers
      summarise(cum_unique_entries = last(cum_unique_entries))
    

    【讨论】:

    • 这很好地解决了我的问题,感谢您的回答!
    【解决方案2】:

    这是另一个尝试:

    numvals <- cummax(as.numeric(factor(mydf$value)))
    aggregate(numvals, list(depth=mydf$depth), max)
    

    这给出了:

      depth x
    1     1 2
    2     2 2
    3     3 3
    

    它似乎也适用于@Arun 的示例:

      depth x
    1     1 2
    2     2 4
    3     3 4
    4     4 5
    5     5 6
    6     6 6
    

    【讨论】:

    • 我不完全确定,但似乎depthvalue 必须同时排序。例如,无论您如何setkey() this data.table: mydf = data.table(data.frame(depth=c(1,1,2,2,6,7), value=c("a", "b", "g", "h", "b", "c"))),此方法都不会计算c 的唯一出现次数。
    【解决方案3】:

    这可以通过使用sqldf 包的单个SQL 语句以相对简洁的方式编写。假设DF是原始数据框:

    library(sqldf)
    
    sqldf("select b.depth, count(distinct a.value) as cumsum
        from DF a join DF b 
        on a.depth <= b.depth
        group by b.depth"
    )
    

    【讨论】:

    • 假设depth 是数字,这非常有用。如果depth 是日期的字符串或字符串表示形式,就像我的情况一样,这可能是一个非常昂贵的操作。
    • 在许多情况下,速度并不重要,清晰度才是更重要的问题。如果性能很重要,那么您真的必须对其进行测试而不是做出假设,如果发现速度太慢,请添加索引并再次测试。
    【解决方案4】:

    我发现这是使用factor 并仔细设置levels 的完美案例。我将在这里使用data.table 来表达这个想法。确保您的 value 列是 character(不是绝对要求)。

    • 第 1 步:只需使用 unique 行,即可将您的 data.frame 转换为 data.table

      require(data.table)
      dt <- as.data.table(unique(df))
      setkey(dt, "depth") # just to be sure before factoring "value"
      
    • 第 2 步:将 value 转换为 factor 并强制转换为 numeric。确保自己设置关卡(这很重要)。

      dt[, id := as.numeric(factor(value, levels = unique(value)))]
      
    • 第 3 步:将键列设置为 depth 以进行子集化,只需选择最后一个值

       setkey(dt, "depth", "id")
       dt.out <- dt[J(unique(depth)), mult="last"][, value := NULL]
      
      #    depth id
      # 1:     1  2
      # 2:     2  2
      # 3:     3  3
      
    • 第 4 步:由于深度增加的行中的所有值都应至少具有上一行的值,因此您应该使用cummax 来获得最终输出。

      dt.out[, id := cummax(id)]
      

    编辑:以上代码仅用于说明目的。实际上,您根本不需要第三列。这就是我编写最终代码的方式。

    require(data.table)
    dt <- as.data.table(unique(df))
    setkey(dt, "depth")
    dt[, value := as.numeric(factor(value, levels = unique(value)))]
    setkey(dt, "depth", "value")
    dt.out <- dt[J(unique(depth)), mult="last"]
    dt.out[, value := cummax(value)]
    

    这是一个更棘手的例子和代码的输出:

    df <- structure(list(depth = c(1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 6), 
                    value = structure(c(1L, 2L, 3L, 4L, 1L, 3L, 4L, 5L, 6L, 1L, 1L), 
                    .Label = c("a", "b", "c", "d", "f", "g"), class = "factor")), 
                    .Names = c("depth", "value"), row.names = c(NA, -11L), 
                    class = "data.frame")
    #    depth value
    # 1:     1     2
    # 2:     2     4
    # 3:     3     4
    # 4:     4     5
    # 5:     5     6
    # 6:     6     6
    

    【讨论】:

    • 这是dplyr 版本:df %&gt;% arrange(depth) %&gt;% mutate(value = cummax(as.numeric(factor(value, levels = unique(value))))) %&gt;% arrange(depth, desc(value)) %&gt;% distinct(depth)
    • 这种方法一般适用于depthvalue都是字符串值的情况。谢谢!
    • @Arun 这是一个很棒的解决方案!谢谢!
    • 如果我们有第三个变量叫 group 并且我们想为每个组实现相同的结果?
    【解决方案5】:

    这是另一个使用lapply() 的解决方案。使用unique(df$depth) 创建唯一的depth 值向量,然后对于每个这样的值子集,只有depth 等于或小于特定depth 值的value 值。然后计算唯一value 值的长度。此长度值存储在cumsum 中,然后depth=x 将给出特定深度级别的值。使用do.call(rbind,...) 使其成为一个数据框。

    do.call(rbind,lapply(unique(df$depth), 
                   function(x)
                 data.frame(depth=x,cumsum=length(unique(df$value[df$depth<=x])))))
      depth cumsum
    1     1      2
    2     2      2
    3     3      3
    

    【讨论】:

      【解决方案6】:

      一个好的第一步是创建一个TRUEFALSE 列,其中TRUE 用于每个值的第一个,FALSE 用于该值的后续出现。这可以使用duplicated 轻松完成:

      mydata$first.appearance = !duplicated(mydata$value)
      

      重塑数据最好使用aggregate。在这种情况下,它表示对 depth 的每个子集中的 first.appearance 列求和:

      newdata = aggregate(first.appearance ~ depth, data=mydata, FUN=sum)
      

      结果将如下所示:

        depth first.appearance
      1     1  2
      2     2  0
      3     3  1
      

      不过,这仍然不是一个累积和。为此,您可以使用 cumsum 函数(然后摆脱旧列):

      newdata$cumsum = cumsum(newdata$first.appearance)
      newdata$first.appearance = NULL
      

      回顾一下:

      mydata$first.appearance = !duplicated(mydata$value)
      newdata = aggregate(first.appearance ~ depth, data=mydata, FUN=sum)
      newdata$cumsum = cumsum(newdata$first.appearance)
      newdata$first.appearance = NULL
      

      输出:

        depth cumsum
      1     1      2
      2     2      2
      3     3      3
      

      【讨论】:

        猜你喜欢
        • 2014-01-03
        • 1970-01-01
        • 2013-06-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-15
        相关资源
        最近更新 更多