我想使用dplyr为数据集添加“delta”列。 delta将被计算为当前行值与前一行的值之间的差值。挑战在于前一行不一定是正确的行,因为需要进行一些过滤。
考虑这个数据集:
LEVEL, TIME
3, 0000
2, 0010
2, 0020
1, 0030
2, 0040
3, 0050
我想添加一个新列DELTA,其中包含TIME值与前一个TIME值之间的差值,对于具有相同LEVEL或更高级别的行。也就是说,我不想与紧接在前的行进行比较,而是向后搜索并跳过任何具有较低LEVEL的行。
对于此示例,预期输出为:
LEVEL, TIME, DELTA
3, 0000, NA
2, 0010, 10
2, 0020, 10
1, 0030, 10
2, 0040, 20
3, 0050, 50
这可以用dplyr直接完成吗? (或者其他?)
我想要一个有效的解决方案,因为我的真实数据集大约有十亿行,并且有七个时间戳列(但只有一个级别。)
(背景:数据来自软件应用程序日志文件,使用CPU提供的许多时间源,例如周期,指令和L1 / L2 / L3 / DRAM访问计数器。我想测量事件之间经过的时间。较低级别的消息不是单独的先前事件,而是更精细的细节。)
使用新信息进行编辑:
我尝试使用dplyr的解决方案实际上并没有使用我的百万元素数据集。它们似乎很慢并且炸毁了R过程。
我已经退回学习一些基础R并编写一个相当实用的(1M行数据帧约2秒)实现,如下所示:
level <- c(3,2,2,1,2,3,6,4,7,8,2) # recycled to 1M elements, below
time <- seq(0, 10000000, 10)
# reference timestamp accumulator for update inside closure.
# index is log level and value is reference timestamp for delta.
ref <- numeric(9)
f <- function(level, time) {
delta <- time - ref[level]
ref[1:level] <<- time
delta
}
delta <- mapply(f, level, time)
这合理吗?是否有类似的dplyr解决方案?
我基本满意。我觉得这应该快〜10倍,每个向量元素大约5000个CPU周期似乎有点疯狂,但它对我有用,并且在解释器的上下文中可能是合理的,复制每个向量元素ref
累加器步骤
EDIT2:经过反思,这个配方的表现有点拖累。如果可能,我希望加速10倍!
答案 0 :(得分:1)
我自己加入了data.frame。然后选择符合条件的所有行。然后选择最接近的匹配行。
为了在结果中获得相同数量的行(第一行中的NA),我再次加入基数data.frame(right_join
)。
LEVEL <- c(3,2,2,1,2,3)
TIME <- c('0000','0010','0020','0030','0040','0050')
df <- data.frame(LEVEL, TIME, stringsAsFactors = F)
df %>%
merge(df, by = NULL, all=T) %>%
filter(LEVEL.y >= LEVEL.x & TIME.x > TIME.y) %>%
group_by(TIME.x, LEVEL.x) %>%
filter(row_number(desc(TIME.y))==1) %>%
mutate(delta = as.numeric(TIME.x) - as.numeric(TIME.y)) %>%
rename(LEVEL = LEVEL.x, TIME=TIME.x) %>%
select(TIME, LEVEL, delta) %>%
right_join(df)
另一种方法是计算每个组的min(delta)
,而不是排序和选择第一行。我更喜欢上面的解决方案,因为您可以使用匹配行的其他信息。
df %>% merge(df, by = NULL, all=T) %>%
filter(LEVEL.y >= LEVEL.x & TIME.x > TIME.y) %>%
group_by(TIME.x, LEVEL.x) %>%
summarise(delta = min(as.numeric(TIME.x) - as.numeric(TIME.y))) %>%
rename(LEVEL = LEVEL.x, TIME=TIME.x) %>%
select(TIME, LEVEL, delta) %>%
right_join(df)