Ste*_*eMc 1 r dataset dataframe dplyr data.table
我真的需要加速一些R代码.我有一个特定运动的大型数据集.数据框中的每一行代表游戏中的某种类型的动作.对于每个游戏(game_id),我们有两个团队(team_id)参与游戏.time_ref在数据框中是每个游戏按时间顺序的动作.type_id是游戏中的动作类型.player_off被设置为TRUE或被FALSE链接到action_id=3.action_id=3代表玩家获得一张牌并被player_off设置为TRUE/ FALSE如果玩家在获得该牌时被罚下.示例data.frame:
> df
game_id team_id action_id player_off time_ref
100 10 1 NA 1000
100 10 1 NA 1001
100 10 1 NA 1002
100 11 1 NA 1003
100 11 2 NA 1004
100 11 1 NA 1005
100 10 3 1 1006
100 11 1 NA 1007
100 10 1 NA 1008
100 10 1 NA 1009
101 12 3 0 1000
101 12 1 NA 1001
101 12 1 NA 1002
101 13 2 NA 1003
101 13 3 1 1004
101 12 1 NA 1005
101 13 1 NA 1006
101 13 1 NA 1007
101 12 1 NA 1008
101 12 1 NA 1009
Run Code Online (Sandbox Code Playgroud)
我需要的是数据框中的另一个栏目,它给出了我TRUE或FALSE两个球队在每个动作(排)发生时球场上是否有相同/不等数量的球员.
所以,game_id=100有一个action_id=3与player_off=1用于team_id=10在time_ref=1006.所以我们知道球队在场上的数量与场上球员的数量相同,但在比赛剩余时间内则不相等(time_ref>1006).同样的事情game_id=101也发生了.
这是一个数据框的示例,其中包含我希望为数据集添加的额外列.
>df
game_id team_id action_id player_off time_ref is_even
100 10 1 NA 1000 1
100 10 1 NA 1001 1
100 10 1 NA 1002 1
100 11 1 NA 1003 1
100 11 2 NA 1004 1
100 11 1 NA 1005 1
100 10 3 1 1006 1
100 11 1 NA 1007 0
100 10 1 NA 1008 0
100 10 1 NA 1009 0
101 12 3 0 1000 1
101 12 1 NA 1001 1
101 12 1 NA 1002 1
101 13 2 NA 1003 1
101 13 3 1 1004 1
101 12 1 NA 1005 0
101 13 1 NA 1006 0
101 13 1 NA 1007 0
101 12 1 NA 1008 0
101 12 1 NA 1009 0
Run Code Online (Sandbox Code Playgroud)
所以你可以看到在game_id=100一个玩家被发送的时候time_ref=1006所有以前的行被标记为is_even=1,随后标记为不均匀或0.类似的game_id=101在time_ref=1004.
实现这个额外列的最有效方法是什么?优选不使用for循环.
对于一些矢量
x = c(0, NA, NA, NA, 1, NA, NA, NA)
Run Code Online (Sandbox Code Playgroud)
编写一个函数来标准化数据(0或1个玩家丢失),计算丢失的玩家的累积数量,并将其与零进行比较,
fun0 = function(x) {
x[is.na(x)] = 0
cumsum(x) == 0
}
Run Code Online (Sandbox Code Playgroud)
对于多个组,请使用ave()分组变量
x = c(x, rev(x))
grp = rep(1:2, each = length(x) / 2)
ave(x, grp, FUN = fun0)
Run Code Online (Sandbox Code Playgroud)
对于问题中的数据,请尝试
df$is_even = ave(df$player_off, df$game_id, FUN = fun)
Run Code Online (Sandbox Code Playgroud)
在语义上,似乎fun0()比这个解决方案中隐含的更复杂,特别是如果每个团队失去一个玩家,他们甚至会再次,就像@SunLisa所说的那样.如果是,请清理数据
df$player_off[is.na(df$player_off)] = 0
Run Code Online (Sandbox Code Playgroud)
并改变fun0(),例如,
fun1 <- function(x, team) {
is_team_1 <- team == head(team, 1) # is 'team' the first team?
x1 <- x & is_team_1 # lost player & team 1
x2 <- x & !is_team_1 # lost player & team 2
cumsum(x1) == cumsum(x2) # same total number of players?
}
Run Code Online (Sandbox Code Playgroud)
(将逻辑返回值强制转换为整数似乎不是一个好主意).这可以通过组来应用
df$is_even = ave(seq_len(nrow(df)), df$game_id, FUN = function(i) {
fun1(df$player_off[i], df$team_id[i])
})
Run Code Online (Sandbox Code Playgroud)
要么
split(df$is_even, df$game_id) <-
Map(fun1,
split(df$player_off, df$game_id),
split(df$team_id, df$game_id)
)
Run Code Online (Sandbox Code Playgroud)
执行ave()是有用的,重要的是
split(x, g) <- lapply(split(x, g), FUN)
Run Code Online (Sandbox Code Playgroud)
右侧x按组拆分g,然后应用于FUN()每个组.左侧split<-()是一个棘手的操作,使用组索引来更新原始向量x.
最初的问题是'no for loops',但实际上是lapply()(in ave())并且Map()正是如此; ave()由于它采用的分割 - 应用 - 组合策略,而不是OP可能实现的,可能会迭代游戏,对数据帧进行子集化,然后更新每个游戏的数据帧,因此效率相对较高.子集将具有整个数据集的重复子集,并且特别是更新将至少复制每个赋值的整个结果列; 这种复制会大大减慢执行速度.OP也有可能在努力fun0(); 这将有助于澄清问题,特别是标题,以确定这是问题.
有更快的方法,特别是使用data.table包,但原理是相同的 - 确定一个按照你想要的方式对向量进行操作的函数,并按组应用它.
另一种完全矢量化的解决方案遵循此建议来按组计算累积总和.因为fun0(),标准化x为在特定时间点离开游戏的玩家数量,没有NA
x[is.na(x)] = 0
Run Code Online (Sandbox Code Playgroud)
相当于fun(),计算离开游戏的玩家的累积总和,而不考虑群体
cs = cumsum(x)
Run Code Online (Sandbox Code Playgroud)
对累积和适用的组更正此问题
in_game = cs - (grp - 1)
Run Code Online (Sandbox Code Playgroud)
当0名玩家离开游戏时,将其设置为"TRUE"
is_even = (in_game == 0)
Run Code Online (Sandbox Code Playgroud)
这取决于grp从1到组数的索引; 对于这里的数据可能grp = match(df$game_id, unique(df$game_id)).存在类似的解决方案fun1().