数据框中的子集特定行和最后一行

时间:2017-01-26 21:08:30

标签: r

我有一个数据框,其中包含与不同事件得分相关的数据。一场比赛可以有很多得分事件。我想做的是分数高于5或低于-5的情况。我还希望得到每个ID的最后一行。因此,对于每个ID,我将有一行或多行,具体取决于分数是高于5还是低于-5。我的实际数据集包含许多其他信息列,但如果我学会了如何执行此操作,那么我将能够将其应用于我可能想要做的任何其他事情。

这是一个数据集

ID Score Time
1    0    0
1    3    5
1    -2   9
1    -4   17
1    -7   31
1    -1   43
2    0    0
2    -3   15
2    0    19
2    4    25
2    6    29
2    9    33
2    3    37
3    0    0
3    5    3
3    2    11

所以对于这个数据集,我希望得到这个输出:

ID Score Time
1   -7    31    
1   -1    43
2    6    29 
2    9    33
2    3    37
3    2    11

所以至少,对于每个ID,将会有一行打印出该ID的最后得分,无论在事件期间得分是高于5还是低于-5(这发生在ID 3中)。

我的尝试可以在值高于5或低于-5时进行分组,我只是不知道如何编写代码来获取每个ID的最后一行:

Data[Data$Score > 5 | Data$Score < -5]

如果您需要更多信息,请与我们联系。

4 个答案:

答案 0 :(得分:3)

以下是 data.table 中的内容,其中df是您的原始数据框。

library(data.table)
setDT(df)

df[df[, c(.I[!between(Score, -5, 5)], .I[.N]), by = ID]$V1]
#    ID Score Time
# 1:  1    -7   31
# 2:  1    -1   43
# 3:  2     6   29
# 4:  2     9   33
# 5:  2     3   37
# 6:  3     2   11

我们按ID进行分组。 between函数找到介于-5和5之间的值,我们否定它以获得超出该范围的所需值。然后,我们使用.I子集来获取每组的索引。然后.I[.N]为我们提供每组最后一个条目的行号。我们使用该结果的V1列作为整个表的行子集。如果需要唯一行,则可以采用唯一值。

注意: .I[c(which(!between(Score, -5, 5)), .N)]也可以在第一个操作的j条目中使用。不确定它是否效率更高或更低。

添加:另一种方法,即仅使用逻辑值并且永远不会在输出中产生重复行的方法

df[df[, .I == .I[.N] | !between(Score, -5, 5), by = ID]$V1]
#    ID Score Time
# 1:  1    -7   31
# 2:  1    -1   43
# 3:  2     6   29
# 4:  2     9   33
# 5:  2     3   37
# 6:  3     2   11

答案 1 :(得分:3)

您可以使用rle获取每个ID的最后一行。查看?rle以获取有关此有用功能的更多信息。

Data2 <- Data[cumsum(rle(Data$ID)$lengths), ]
Data2
#   ID Score Time
#6   1    -1   43
#13  2     3   37
#16  3     2   11

要结合这两个条件,请使用rbind

Data2 <- rbind(Data[Data$Score > 5 | Data$Score < -5, ], Data[cumsum(rle(Data$ID)$lengths), ])

要删除满足这两个条件的行,您可以使用duplicatedrownames

Data2 <- Data2[!duplicated(rownames(Data2)), ]

当然,您也可以根据需要进行排序。

答案 2 :(得分:2)

这是另一个基础R解决方案。

df[as.logical(ave(df$Score, df$ID,
                  FUN=function(i) abs(i) > 5 | seq_along(i) == length(i))), ]

   ID Score Time
5   1    -7   31
6   1    -1   43
11  2     6   29
12  2     9   33
13  2     3   37
16  3     2   11

abs(i) > 5 | seq_along(i) == length(i)构造一个逻辑向量,为符合条件的每个元素返回TRUE。 ave将此函数应用于每个ID。生成的逻辑向量用于选择data.frame的行。

答案 3 :(得分:0)

这是一个tidyverse解决方案。不像上面的内容那么简洁,但是更容易理解。

library(tidyverse)
lastrows  <- Data %>% group_by(ID) %>% top_n(1, Time)
scorerows <- Data %>% group_by(ID) %>% filter(!between(Score, -5, 5))
bind_rows(scorerows, lastrows) %>% arrange(ID, Time) %>% unique()

# A tibble: 6 x 3
# Groups:   ID [3]
#      ID Score  Time
#   <int> <int> <int>
# 1     1    -7    31
# 2     1    -1    43
# 3     2     6    29
# 4     2     9    33
# 5     2     3    37
# 6     3     2    11