如何使用dplyr在R数据框中找到两行之间值的差异

21

我有一个 R 数据框,例如:

df <- data.frame(period=rep(1:4,2), 
                 farm=c(rep('A',4),rep('B',4)), 
                 cumVol=c(1,5,15,31,10,12,16,24),
                 other = 1:8);

  period farm cumVol other
1      1    A      1     1
2      2    A      5     2
3      3    A     15     3
4      4    A     31     4
5      1    B     10     5
6      2    B     12     6
7      3    B     16     7
8      4    B     24     8

我如何在每个时间段内找到每个农场累计体积的变化,忽略“其他”列?我想得到以下数据框(可选择保留cumVol列):

如何在每个周期中查找每个农场的cumVol变化,而忽略“other”列?我希望获得类似于以下数据框的结果(如果需要,可以包含cumVol列):

  period farm volume other
1      1    A      0     1
2      2    A      4     2
3      3    A     10     3
4      4    A     16     4
5      1    B      0     5
6      2    B      2     6
7      3    B      4     7
8      4    B      8     8

在实践中,可能会有许多类似于“农场”的列,以及许多类似于“其他”的(即被忽略的)列。我希望能够使用变量指定所有列名。

我正在使用dplyr包。


2
几乎可以确定这是一个重复的问题 - 尝试使用 with(df, ave(cumVol,farm,FUN=function(x) c(0,diff(x))) ) - thelatemail
4
如果OP寻求的是dplyr答案而不是plyr答案,为什么会被视为重复? - Vincent
4个回答

54

在 dplyr 中:

require(dplyr)
df %>%
  group_by(farm) %>%
  mutate(volume = cumVol - lag(cumVol, default = cumVol[1]))

Source: local data frame [8 x 5]
Groups: farm

  period farm cumVol other volume
1      1    A      1     1      0
2      2    A      5     2      4
3      3    A     15     3     10
4      4    A     31     4     16
5      1    B     10     5      0
6      2    B     12     6      2
7      3    B     16     7      4
8      4    B     24     8      8

也许实际想要的输出应该是这样的?

df %>%
  group_by(farm) %>%
  mutate(volume = cumVol - lag(cumVol, default = 0))

  period farm cumVol other volume
1      1    A      1     1      1
2      2    A      5     2      4
3      3    A     15     3     10
4      4    A     31     4     16
5      1    B     10     5     10
6      2    B     12     6      2
7      3    B     16     7      4
8      4    B     24     8      8

编辑:根据您的评论,我认为您正在寻找arrange()函数。如果不是这种情况,最好新开一个问题。

df1 <- data.frame(period=rep(1:4,4), farm=rep(c(rep('A',4),rep('B',4)),2), crop=(c(rep('apple',8), rep('pear',8))), cumCropVol=c(1,5,15,31,10,12,16,24,11,15,25,31,20,22,26,34), other = rep(1:8,2) ); 
df1 %>% 
  arrange(desc(period), desc(farm)) %>%
  group_by(period, farm) %>% 
  summarise(cumVol=sum(cumCropVol))

编辑:跟进 #2

df1 <- data.frame(period=rep(1:4,4), farm=rep(c(rep('A',4),rep('B',4)),2), crop=(c(rep('apple',8), rep('pear',8))), cumCropVol=c(1,5,15,31,10,12,16,24,11,15,25,31,20,22,26,34), other = rep(1:8,2) ); 
df <- df1 %>% 
  arrange(desc(period), desc(farm)) %>% 
  group_by(period, farm) %>% 
  summarise(cumVol=sum(cumCropVol))

ungroup(df) %>% 
  arrange(farm) %>%
  group_by(farm) %>% 
  mutate(volume = cumVol - lag(cumVol, default = 0))

Source: local data frame [8 x 4]
Groups: farm

  period farm cumVol volume
1      1    A     12     12
2      2    A     20      8
3      3    A     40     20
4      4    A     62     22
5      1    B     30     30
6      2    B     34      4
7      3    B     42      8
8      4    B     58     16

我认为这不是预期的输出。volume 应该是:> DT$volume [1] 0 4 10 16 0 2 4 8 - marbel
3
我更新了我的回答,以便提供正好符合 OP 所要求的内容。然而,我更喜欢在我的回答中保留另一种解决方案,因为那似乎是首选输出。 - Vincent
我同意你的看法,@Vincent。第二个输出似乎更合乎逻辑。 - Tim Cameron
1
啊,我找到了:将mutate替换为:mutate(volume = cumVol - lag(cumVol, default = cumVol[1], order_by=period))。太好了! - Racing Tadpole
@RacingTadpole 请查看我回答的修改。希望能解决你的后续问题。 - Vincent
显示剩余6条评论

16

dplyr中,您无需替换NA值

library(dplyr)
df %>%
 group_by(farm)%>%
 mutate(volume = c(0,diff(cumVol)))


   period farm cumVol other volume
1      1    A      1     1      0
2      2    A      5     2      4
3      3    A     15     3     10
4      4    A     31     4     16
5      1    B     10     5      0
6      2    B     12     6      2
7      3    B     16     7      4
8      4    B     24     8      8

好的,这很容易解决,只需将 cumVol[1] 替换为 0 - Tim Cameron

3

tapplytransform有什么区别?

> transform(df, volumen=unlist(tapply(cumVol, farm, function(x) c(0, diff(x)))))
   period farm cumVol other volumen
A1      1    A      1     1       0
A2      2    A      5     2       4
A3      3    A     15     3      10
A4      4    A     31     4      16
B1      1    B     10     5       0
B2      2    B     12     6       2
B3      3    B     16     7       4
B4      4    B     24     8       8

ave 是更好的选择,参见 @ thelatemail 的评论。

with(df, ave(cumVol,farm,FUN=function(x) c(0,diff(x))) )

3

您是否考虑在原始数据集中创建一个新列?

以下是使用data.table运算符:=的一种选项。

require("data.table")
DT <- data.table(df)
DT[, volume := c(0,diff(cumVol)), by="farm"]

or

diff_2 <- function(x) c(0,diff(x))
DT[, volume := diff_2(cumVol), by="farm"]

输出:

# > DT
#    period farm cumVol other volume
# 1:      1    A      1     1      0
# 2:      2    A      5     2      4
# 3:      3    A     15     3     10
# 4:      4    A     31     4     16
# 5:      1    B     10     5      0
# 6:      2    B     12     6      2
# 7:      3    B     16     7      4
# 8:      4    B     24     8      8

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接