我想知道如何简化这项操作 想象一下,我有一个像这样的data.frame:
set.seed(1)
ID <- rep(1:3,each=4)
XX <- round(runif(12),3)
TT <- rep(1:4, 3)
ZZ <- ave(XX*TT,ID, FUN = cumsum)
DF <- data.frame(ID, XX, ZZ)
ID TT XX ZZ
1 1 0.266 0.266
1 2 0.372 1.010
1 3 0.573 2.729
1 4 0.908 6.361
2 1 0.202 0.202
2 2 0.898 1.998
2 3 0.945 4.833
2 4 0.661 7.477
3 1 0.629 0.629
3 2 0.062 0.753
3 3 0.206 1.371
3 4 0.177 2.079
我&#39;我希望通过ID组获得每列的增量(两个连续元素之间的差异)。保留第一个(就好像前一个零)。
ID TT XX ZZ
1 1 0.266 0.266
1 2 0.106 0.744
1 3 0.201 1.719
1 4 0.335 3.632
2 1 0.202 0.202
2 2 0.696 1.796
2 3 0.047 2.835
2 4 -0.284 2.644
3 1 0.629 0.629
3 2 -0.567 0.124
3 3 0.144 0.618
3 4 -0.029 0.708
我已经尝试了
ave(DF[3:4],DF$ID,FUN=function(x) diff(c(0,x)))
但它不起作用,它会产生错误:
Error in r[i1] - r[-length(r):-(length(r) - lag + 1L)] :
non-numeric argument to binary operator
难道没有简单的方法吗?
我发现我可以通过以下方式获得正确的输出:
ave(DF[3:4],DF$ID,FUN=function(x)
sapply(x, FUN=function(y) diff(c(0,y))))
但是如此简单的操作会变得非常复杂。 我发现我也可以通过使用data.table来实现它,但我更喜欢用基数R来做。
setDT(DF)
DF[, lapply(.SD, FUN=function(x) diff(c(0,x)) ), keyby = ID ]
我也不知道如何在每个小组的开头 插入新行(大量零)或给出一些条件。
ID XX ZZ
1 0 0
1 0.266 0.266
1 0.372 1.010
1 0.573 2.729
1 0.908 6.361
2 0 0
2 0.202 0.202
2 0.898 1.998
2 0.945 4.833
2 0.661 7.477
3 0 0
3 0.629 0.629
3 0.062 0.753
3 0.206 1.371
3 0.177 2.079
我试过了:
ave(DF[3:4],DF$ID,FUN=function(x) sapply(x, FUN=function(y) (c(0,y))))
警告:
data length [10] is not a sub-multiple or multiple of the number of
rows [4]
我想这样做的一般方法是使用行的索引。
PD:我已经更新了帖子。
尝试做得更简单我删除了TT专栏,但我知道这很重要。
我的解决方案假设该表是按TT排序的,但有时它不是那样的。 我真正想要的是:
XX1
XX2-XX1
XX3-XX2
XX4-XX3
我们得到的子索引不是来自桌子上的位置,而是来自T. 我不知道通过首先按TT对列进行排序还是通过创建paste()语法来实现它是否更有效。
答案 0 :(得分:3)
我认为您需要在相关列中使用lapply()
,因为ave()
不会在其第一个参数中列出列表。试试这个:
df[-1] <- lapply(
df[-1],
function(x) ave(x, df$ID, FUN = function(x) c(x[1], diff(x)))
)
提供更新后的df
ID XX ZZ 1 1 0.266 0.266 2 1 0.106 0.744 3 1 0.201 1.719 4 1 0.335 3.632 5 2 0.202 0.202 6 2 0.696 1.796 7 2 0.047 2.835 8 2 -0.284 2.644 9 3 0.629 0.629 10 3 -0.567 0.124 11 3 0.144 0.618 12 3 -0.029 0.708
数据:强>
df <- structure(list(ID = c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L, 3L,
3L, 3L), XX = c(0.266, 0.372, 0.573, 0.908, 0.202, 0.898, 0.945,
0.661, 0.629, 0.062, 0.206, 0.177), ZZ = c(0.266, 1.01, 2.729,
6.361, 0.202, 1.998, 4.833, 7.477, 0.629, 0.753, 1.371, 2.079
)), .Names = c("ID", "XX", "ZZ"), class = "data.frame", row.names = c(NA,
-12L))
答案 1 :(得分:2)
这是在data.table
set.seed(1)
ID <- rep(1:3, each=4)
XX <- round(runif(12), 3)
##ZZ <- ave(XX*TT,ID, FUN = cumsum) #we don't have TT
DF <- data.table(ID, XX)
DF[,XX_dif:=XX-c(0,head(XX,length(XX)-1)),by=ID]
# or alternatively using shift()
# DF[, XX_dif := XX-shift(XX, fill=0L), by=ID]
ID XX XX_dif
1: 1 0.266 0.266
2: 1 0.372 0.106
3: 1 0.573 0.201
4: 1 0.908 0.335
5: 2 0.202 0.202
6: 2 0.898 0.696
7: 2 0.945 0.047
8: 2 0.661 -0.284
9: 3 0.629 0.629
10: 3 0.062 -0.567
11: 3 0.206 0.144
12: 3 0.177 -0.029
答案 2 :(得分:1)
以下是使用dplyr
library(dplyr)
DF %>%
group_by(ID) %>%
mutate(ZZ = c(ZZ[1], diff(ZZ)))
# ID XX ZZ
# <int> <dbl> <dbl>
#1 1 0.266 0.266
#2 1 0.372 0.744
#3 1 0.573 1.719
#4 1 0.908 3.632
#5 2 0.202 0.202
#6 2 0.898 1.796
#7 2 0.945 2.835
#8 2 0.661 2.644
#9 3 0.629 0.629
#10 3 0.062 0.124
#11 3 0.206 0.618
#12 3 0.177 0.708