【问题标题】:Removing all rows under a specified row in a time series删除时间序列中指定行下的所有行
【发布时间】:2016-12-07 02:30:38
【问题描述】:

我正在尝试分析游戏数据,但我需要删除指定行之后的所有行。

在以下情况下,我想为每个用户删除 EVENT "Die" 之后的所有行。数据按 UID、TIME.HOUR 排序。

df:

 UID  TIME.HOUR  EVENT
  1      5       Run
  1      5       Run
  1      6       Run
  1      7       Die
  1      8       Run
  1      9       Run
  2      14      Jump
  2      15      Die
  2      16      Run
  2      17      Run

预期结果:

 UID  TIME.HOUR  EVENT
  1      5       Run
  1      5       Run
  1      6       Run
  1      7       Die
  2      14      Jump
  2      15      Die

我认为我使用下面的代码走在正确的轨道上,但不要为下一步而苦苦挣扎。

 args <- which(df$EVENT== "Die")
 df[,c(sapply(args, function(x) ???), by = UID] #seq? range? 

谢谢。

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    我们可以使用data.table。将'data.frame'转换为'data.table',按'UID'分组,得到逻辑向量(EVENT == "Die")的doublecumsum,检查是否小于2对Data.table(@ 987654325@)

    library(data.table)
    setDT(df)[, .SD[cumsum(cumsum(EVENT=="Die"))<2] , UID]
    #   UID TIME.HOUR EVENT
    #1:   1         5   Run
    #2:   1         5   Run
    #3:   1         6   Run
    #4:   1         7   Die
    #5:   2        14  Jump
    #6:   2        15   Die
    

    Or a faster approach:要获取行索引,提取该列 ($V1) 以对数据进行子集化

    setDT(df)[df[, .I[cumsum(cumsum(EVENT=="Die"))<2] , UID]$V1]
    

    或者修改@Psidom的方法

    setDT(df)[df[, .I[seq(match("Die", EVENT, nomatch = .N))] , UID]$V1]
    

    或使用dplyr

    library(dplyr)
    df %>%
       group_by(UID) %>% 
       slice(seq(match("Die", EVENT, nomatch = n())))
    #    UID TIME.HOUR EVENT
    #  <int>     <int> <chr>
    #1     1         5   Run
    #2     1         5   Run
    #3     1         6   Run
    #4     1         7   Die
    #5     2        14  Jump
    #6     2        15   Die
    

    如果我们需要data.frame 输出,与%&gt;% as.data.frame 链接(来自@R.S. cmets)

    【讨论】:

    • 这是关于dplyr 的部分——这很漂亮,而且非常灵活。 ` slice` 部分需要seq,否则它将只返回匹配的行。我做对了吗?而nomatch=n() 意味着在没有匹配的情况下一直到最后?顺便说一句,也许您还可以建议在末尾添加 %&gt;% ungroup() %&gt;% as.data.frame() 以获得清理后的数据框。
    • @R.S.你是对的,如果你不使用seq,它只会得到匹配的行,而nomatch = n() 是针对特定UID没有匹配的特殊情况。有些人更喜欢将他们的输出设为tbl_df。所以,我将保持原样并添加您的 cmets
    • 啊。谢谢。洞察力有所帮助。
    【解决方案2】:

    这可能效率不高,但你可以做一个花哨的连接:

    mdf = df[EVENT == "Die", head(.SD, 1L), by=UID, .SDcols = "TIME.HOUR"]
    df[!mdf, on=.(UID, TIME.HOUR > TIME.HOUR)]
    
       UID TIME.HOUR EVENT
    1:   1         5   Run
    2:   1         5   Run
    3:   1         6   Run
    4:   1         7   Die
    5:   2        14  Jump
    6:   2        15   Die
    

    当然,您实际上并不需要将mdf 表保存为单独的对象。


    它是如何工作的

    • x[!i],其中i 是另一个data.table 或向量列表,是反连接,告诉R 基于i 排除x 的行,类似于它的工作原理带有向量(其中i 必须是逻辑向量)。

    • on=.(ID, v &gt;= v) 选项告诉 R 我们正在执行“非等值连接”。 v &gt;= v 部分意味着来自i(左侧)的v 应该大于来自x(右侧)的v

    结合这两者,我们排除了符合on= 中指定条件的行。


    旁注。有几件事我不确定:我们有比 non-equi join 更好的名字吗?为什么vi 的左侧,即使x[i]i 的左侧有x

    我借用了 Psidom 和 akrun 的答案,使用了head 和一个不等式。这里的一个(也许?)优势是head(.SD, 1L) is optimizedhead(.SD, expr) 还没有。

    【讨论】:

    • 善用非等连接
    【解决方案3】:

    另一种选择,您可以使用head()match()(找到第一个Die 索引):

    dt[, head(.SD, match("Die", EVENT, nomatch = .N)), UID]   # if no match is found within the 
                                                              # group, return the whole group
    
    #   UID TIME.HOUR EVENT
    #1:   1         5   Run
    #2:   1         5   Run
    #3:   1         6   Run
    #4:   1         7   Die
    #5:   2        14  Jump
    #6:   2        15   Die
    

    【讨论】:

      猜你喜欢
      • 2020-07-22
      • 1970-01-01
      • 1970-01-01
      • 2012-06-17
      • 1970-01-01
      • 2019-04-25
      • 1970-01-01
      • 2020-04-04
      • 1970-01-01
      相关资源
      最近更新 更多