【问题标题】:Summing adjacent rows based on conditionals根据条件对相邻行求和
【发布时间】:2013-12-03 18:45:17
【问题描述】:

我有一个类似的data.frame

id <- c(1,1,1,2,2,3,3,3,3,3)
action <- c("for","l","for","f","l","l","for","for","for","f")
time <- c(45,35,24,56,100,121,30,10,35,143)
dframe <- data.frame(id,action,time)

只有“for”操作在每个唯一 ID 内的连续行中重复。我想将这些行折叠成一行,总结“for”行动的时间。我只想在每个唯一 id 中执行此操作,并且当它们彼此跟随时(如 id==3,而不是 id==1)

我尝试了以下代码,但这并不能区分一个接一个的动作,而是将唯一 ID 中所有出现的“for”相加。

aggregate(action_time ~ id + act, data=mean.event, FUN=sum)

感谢您的宝贵时间。

【问题讨论】:

    标签: r sum conditional


    【解决方案1】:

    使用 rle()inverse.rle()data.table 包:

    ## Reproduce example data, naming it df and setting stringsAsFactors=FALSE    
    id <- c(1,1,1,2,2,3,3,3,3,3)
    action <- c("for","l","for","f","l","l","for","for","for","f")
    time <- c(45,35,24,56,100,121,30,10,35,143)
    df <- data.frame(id,action,time, stringsAsFactors=FALSE)
    
    ## Use rle() and inverse.rle() to give each run of "for"s a distinct name
    r <- rle(df$action)
    r$values <- paste0(r$values, seq_along(r$values))
    (r <- inverse.rle(r))
    #  [1] "for1" "l2"   "for3" "f4"   "l5"   "l5"   "for6" "for6" "for6" "f7"  
    
    ## Use data.table to subset by run of "for"s *and* by id, collapsing only
    ## sub-data.tables consisting of consecutive "for"s within an id.
    library(data.table)
    dt <- data.table(df)
    
    dt[ , if(action[1]=="for") {
              X <- .SD[1,]       
              X$time <- sum(time) 
              X
          } else {.SD}, 
       by=list(r, id)][,-1,with=FALSE]
    #    id action time
    # 1:  1    for   45
    # 2:  1      l   35
    # 3:  1    for   24
    # 4:  2      f   56
    # 5:  2      l  100
    # 6:  3      l  121
    # 7:  3    for   75
    # 8:  3      f  143
    

    【讨论】:

      【解决方案2】:

      您可以创建一个虚拟变量来指示您的约束是否得到满足。 例如,对于每组连续行,虚拟变量“x1”将是唯一的,其中 action=="for":

      dframe$x1 <- with(dframe, cumsum(c(1,action[1:(length(action)-1)] != action[2:length(action)])))
      

      在您的聚合函数中使用此变量(注意问题中代码的子集和其他一些更改):

      aggregate(time ~ id + x1, data=dframe[dframe$action=="for",], FUN=sum)
      
        id x1 time
      1  1  1   45
      2  1  3   24
      3  3  6   75
      

      请注意,正如 cryo11 指出的那样,您还需要在创建数据框时设置 stringsAsFactors=F。

      【讨论】:

      • 这也将折叠 data.frame 的第 5 行和第 6 行的两个 l
      • 据我了解,他不希望将前两个fors 计算在内,即“我只想在每个唯一 ID 内执行此操作并且当他们相互关注时(如 id==3,不是 id==1)”。
      • 感谢您的帮助。我选择了@Josh O'Brien,因为我是 data.table 的粉丝,它保留了值的顺序。
      【解决方案3】:

      请检查这是否是您想要的结果。 顺便说一句:我假设你已经设置了options(stringsAsFactors = FALSE)

      res=Reduce("rbind",lapply(split(dframe,id),function(x) {
        tmp=rle(x$action)
        tmp$values=ifelse(tmp$values!="for"|(tmp$values=="for"&tmp$lengths==1),
                          TRUE,
                          FALSE)
        idx=inverse.rle(tmp)
        na.omit(rbind(data.frame(x[idx,setdiff(colnames(x),"time")],
                                 time=x[idx,"time"]),
                      data.frame(x[!idx,setdiff(colnames(x),"time")][1,],
                                 time=sum(x[!idx,"time"]))
                      )
                )
        }))
      rownames(res)=NULL
      res
      

      给予

      #  id action time
      #1  1    for   45
      #2  1      l   35
      #3  1    for   24
      #4  2      f   56
      #5  2      l  100
      #6  3      l  121
      #7  3      f  143
      #8  3    for   75
      

      【讨论】:

      • "for"s 序列从一个 id 开始并运行到下一个 id 时,您确定这会给出正确答案吗? (即,考虑一个包含多个id 值的data.frame,其中所有行的action 列中的值都为"for"...)
      猜你喜欢
      • 1970-01-01
      • 2017-12-11
      • 1970-01-01
      • 2023-01-16
      • 2021-01-25
      • 1970-01-01
      • 2021-10-12
      • 1970-01-01
      相关资源
      最近更新 更多