在R中模拟SQL的窗口函数

时间:2015-06-04 10:24:35

标签: r data.table

我有一张表如下:

id   name  amount  year
001  A     10      2010
001  A     10      2011
001  A     12      2012
-----------------------
002  A     3       2012
002  A     4       2013
-----------------------
003  B     20      2011
003  B     20      2012

(注意两个实体具有相同的名称A但它们不同,id是唯一标识符。)

我想计算前一年amount的增量,即结果如下:

id   name   increment   year
001  A      0           2010
001  A      0           2011
001  A      2           2012
----------------------------
002  A      0           2012
002  A      1           2013
----------------------------
003  B      0           2011
003  B      0           2012

请注意,第一年的增量被视为" 0"。

在MSSQL中,它可以通过以下方式实现:

SELECT id,
       name,
       amount - LAG(amount, 1, amount) OVER (PARTITION BY id ORDER BY YEAR) as increment,
       year
FROM table

我正在尝试使用data.table完成R中的任务。我找到了一个简洁的例子here
DT[, increment := amount - shift(amount, 1), by=id]。但是提示错误:could not find function "shift"

版本是:

  • R:3.2.0_1
  • data.table:1.9.4

问题是:

  1. 我发现在data.table' Github上实现了shift函数,为什么我没有调用该函数?
  2. 我认为data.table中的by相当于SQL中的PARTITION BY,那么R中ORDER BY的对应部分是什么?在执行任何聚合之前,我是否必须设置data.table的键,以便对data.table进行排序?

1 个答案:

答案 0 :(得分:3)

这种情况属于通过单独的分组列对列进行操作的一般结构。

output = `ruby #{filename}`

数据

fun <- function(v) c(0, diff(v)) #to take the difference and account for the starting value

#function tapply()
df1 <- df
df1$amount <- unlist(with(df, by(amount, id, fun)))
df1
   id name amount year
1 001    A      0 2010
2 001    A      0 2011
3 001    A      2 2012
4 002    A      0 2012
5 002    A      1 2013
6 003    B      0 2011
7 003    B      0 2012

#using data.table
df2 <- df
setDT(df2)[, list(name, Increment = fun(amount), year), by = id]
    id name Increment year
1: 001    A         0 2010
2: 001    A         0 2011
3: 001    A         2 2012
4: 002    A         0 2012
5: 002    A         1 2013
6: 003    B         0 2011
7: 003    B         0 2012

#function: by()
df3 <- df
df3$amount <- unlist(with(df3, by(amount, id, fun)))
df3
   id name amount year
1 001    A      0 2010
2 001    A      0 2011
3 001    A      2 2012
4 002    A      0 2012
5 002    A      1 2013
6 003    B      0 2011
7 003    B      0 2012

#using dplyr with data.table
DT %>%
  group_by(id) %>%
  summarise(name, increment = fun(amount), year)
Source: local data table [7 x 4]

   id name increment year
1 001    A         0 2010
2 001    A         0 2011
3 001    A         2 2012
4 002    A         0 2012
5 002    A         1 2013
6 003    B         0 2011
7 003    B         0 2012

#using aggregate
df5$amount <- unlist(aggregate(amount ~ id, data=df5, FUN=fun)$amount)
df5
   id name amount year
1 001    A      0 2010
2 001    A      0 2011
3 001    A      2 2012
4 002    A      0 2012
5 002    A      1 2013
6 003    B      0 2011
7 003    B      0 2012

#function: ave
df6 <- df
df6$amount <- with(df, ave(amount, id, FUN-fun))
df6
   id name amount year
1 001    A      0 2010
2 001    A      0 2011
3 001    A      2 2012
4 002    A      0 2012
5 002    A      1 2013
6 003    B      0 2011
7 003    B      0 2012

#dplyr (non-data.table)
df7 <- df
df %>%
  group_by(id) %>%
  mutate(increment = fun(amount))
   id name amount year increment
1 001    A     10 2010         0
2 001    A     10 2011         0
3 001    A     12 2012         2
4 002    A      3 2012         0
5 002    A      4 2013         1
6 003    B     20 2011         0
7 003    B     20 2012         0

#dplyr (with extra command 'select' to give the desired output of the OP)
df %>%
   group_by(id) %>%
     mutate(increment = fun(amount)) %>%
       select(id, name, increment, year)
Source: local data frame [7 x 4]
Groups: id

   id name increment year
1 001    A         0 2010
2 001    A         0 2011
3 001    A         2 2012
4 002    A         0 2012
5 002    A         1 2013
6 003    B         0 2011
7 003    B         0 2012