首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从数据框中的每个分组行中减去上一年的from值

从数据框中的每个分组行中减去上一年的from值
EN

Stack Overflow用户
提问于 2012-03-04 14:10:19
回答 3查看 8.4K关注 0票数 6

我正在尝试计算无意中聚合的数据的滞后差异(或实际增长)。数据中的每个连续年份都包含前一年的值。可以使用以下代码创建样本数据集:

代码语言:javascript
复制
set.seed(1234)
x <- data.frame(id=1:5, value=sample(20:30, 5, replace=T), year=3)
y <- data.frame(id=1:5, value=sample(10:19, 5, replace=T), year=2)
z <- data.frame(id=1:5, value=sample(0:9, 5, replace=T), year=1)
(df <- rbind(x, y, z))

我可以使用lapply()split()的组合来计算每个唯一id的每年之间的差异,如下所示:

代码语言:javascript
复制
(diffs <- lapply(split(df, df$id), function(x){-diff(x$value)}))

但是,由于diff()函数的性质,第1年的值没有结果,这意味着在我用Reduce()展平列表的diffs列表后,我不能将实际的年增量添加回数据框中,如下所示:

代码语言:javascript
复制
df$actual <- Reduce(c, diffs)  # flatten the list of lists

在本例中,只有10个计算出的差异或滞后,而数据框中有15行,因此R在尝试添加新列时会抛出错误。

如何使用(1)第1年的值和(2)计算出的所有后续年份的差异/滞后来创建实际增长的新列?

这就是我最终要找的输出。我的diffs列表可以很好地计算出第二年和第三年的实际值。

代码语言:javascript
复制
id value year actual
 1    21    3      5
 2    26    3     16
 3    26    3     14
 4    26    3     10
 5    29    3     14
 1    16    2     10
 2    10    2      5
 3    12    2     10
 4    16    2      7
 5    15    2     13
 1     6    1      6
 2     5    1      5
 3     2    1      2
 4     9    1      9
 5     2    1      2
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-03-04 14:50:15

我想这对你会有用的。当你遇到diff问题时,只需将0作为第一个数字来加长向量即可。

代码语言:javascript
复制
df <- df[order(df$id, df$year), ]
sdf <-split(df, df$id)
df$actual <- as.vector(sapply(seq_along(sdf), function(x) diff(c(0, sdf[[x]][,2]))))
df[order(as.numeric(rownames(df))),]

有很多方法可以做到这一点,但这一种方法相当快,并且使用了base。

这里有第二和第三种方法来解决这个问题,利用聚合和:

聚合:

代码语言:javascript
复制
df <- df[order(df$id, df$year), ]
diff2 <- function(x) diff(c(0, x))
df$actual <- c(unlist(t(aggregate(value~id, df, diff2)[, -1])))
df[order(as.numeric(rownames(df))),]

作者:

代码语言:javascript
复制
df <- df[order(df$id, df$year), ]
diff2 <- function(x) diff(c(0, x))
df$actual <- unlist(by(df$value, df$id, diff2))
df[order(as.numeric(rownames(df))),]

plyr

代码语言:javascript
复制
df <- df[order(df$id, df$year), ]
df <- data.frame(temp=1:nrow(df), df)
library(plyr)
df <- ddply(df, .(id), transform, actual=diff2(value))
df[order(-df$year, df$temp),][, -1]

它为您提供了以下内容的最终产品:

代码语言:javascript
复制
> df[order(as.numeric(rownames(df))),]
   id value year actual
1   1    21    3      5
2   2    26    3     16
3   3    26    3     14
4   4    26    3     10
5   5    29    3     14
6   1    16    2     10
7   2    10    2      5
8   3    12    2     10
9   4    16    2      7
10  5    15    2     13
11  1     6    1      6
12  2     5    1      5
13  3     2    1      2
14  4     9    1      9
15  5     2    1      2

循环编辑:避免循环

我建议避免这个循环,把我给你的东西变成一个函数( by解决方案对我来说是最容易使用的),并将它应用于你想要的两列。

代码语言:javascript
复制
set.seed(1234)  #make new data with another numeric column
x <- data.frame(id=1:5, value=sample(20:30, 5, replace=T), year=3)
y <- data.frame(id=1:5, value=sample(10:19, 5, replace=T), year=2)
z <- data.frame(id=1:5, value=sample(0:9, 5, replace=T), year=1)
df <- rbind(x, y, z)
df <- df.rep <- data.frame(df[, 1:2], new.var=df[, 2]+sample(1:5, nrow(df), 
          replace=T), year=df[, 3])


df <- df[order(df$id, df$year), ]
diff2 <- function(x) diff(c(0, x))                   #function one
group.diff<- function(x) unlist(by(x, df$id, diff2)) #answer turned function
df <- data.frame(df, sapply(df[, 2:3], group.diff))  #apply group.diff to col 2:3
df[order(as.numeric(rownames(df))),]                 #reorder it

当然,您必须重命名它们,除非您使用transform,如下所示:

代码语言:javascript
复制
df <- df[order(df$id, df$year), ]
diff2 <- function(x) diff(c(0, x))                   #function one
group.diff<- function(x) unlist(by(x, df$id, diff2)) #answer turned function
df <- transform(df, actual=group.diff(value), actual.new=group.diff(new.var))   
df[order(as.numeric(rownames(df))),]

这将取决于您对多少个变量执行此操作。

票数 4
EN

Stack Overflow用户

发布于 2012-03-05 01:28:54

1) diff.zoo。使用zoo包,只需使用split=将其转换为zoo,然后执行diff

代码语言:javascript
复制
library(zoo)

zz <- zz0 <- read.zoo(df, split = "id", index = "year", FUN = identity)
zz[2:3, ] <- diff(zz)

它提供了以下内容(以宽形式,而不是您提到的长形式),其中每列是一个id,每行是一年减去前一年:

代码语言:javascript
复制
> zz
   1  2  3  4  5
1  6  5  2  9  2
2 10  5 10  7 13
3  5 16 14 10 14

显示的宽表单实际上可能更可取,但如果您希望这样做,则可以将其转换为长表单:

代码语言:javascript
复制
dt <- function(x) as.data.frame.table(t(x))
setNames(cbind(dt(zz), dt(zz0)[3]), c("id", "year", "value", "actual"))

这使年份按升序排列,这是R中通常使用的约定。

2)滚动应用。同样使用zoo,这个替代方案使用滚动计算将实际列添加到数据中。它假设数据的结构如您所示,每个组中的年数按顺序排列:

代码语言:javascript
复制
df$actual <- rollapply(df$value, 6, partial = TRUE, align = "left",
   FUN = function(x) if (length(x) < 6) x[1] else x[1]-x[6])

3)减去。假设与前面的解决方案相同,我们可以将其进一步简化为从每个值中减去5个位置的值:

代码语言:javascript
复制
transform(df, actual = value - c(tail(value, -5), rep(0, 5)))

或者这个变体:

代码语言:javascript
复制
transform(df, actual = replace(value, year > 1, -diff(ts(value), 5)))

编辑:添加了rollapply和减法解决方案。

票数 3
EN

Stack Overflow用户

发布于 2012-03-04 15:14:33

你可以在0年的df中添加模拟行,这有点老土,但你可以保留你精彩的Reduce

代码语言:javascript
复制
mockRows <- data.frame(id = 1:5, value = 0, year = 0)
(df <- rbind(df, mockRows))
(df <- df[order(df$id, df$year), ])

(diffs <- lapply(split(df, df$id), function(x){diff(x$value)}))
(df <- df[df$year != 0,])

(df$actual <- Reduce(c, diffs)) # flatten the list of lists
df[order(as.numeric(rownames(df))),]

这是输出:

代码语言:javascript
复制
   id value year actual
1   1    21    3      5
2   2    26    3     16
3   3    26    3     14
4   4    26    3     10
5   5    29    3     14
6   1    16    2     10
7   2    10    2      5
8   3    12    2     10
9   4    16    2      7
10  5    15    2     13
11  1     6    1      6
12  2     5    1      5
13  3     2    1      2
14  4     9    1      9
15  5     2    1      2
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9552771

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档