在 R 中模拟 SQL 的 window 函数
Emulate the window function of SQL in R
我有一个 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
问题是:
- 我在data.table的Github上找到了
shift
函数,为什么调用函数失败?
- 我认为 data.table 中的
by
等价于 SQL 中的 PARTITION BY
,那么 ORDER BY
在 R 中的对应物是什么?我是否必须在执行任何聚合之前设置 data.table 的键,以便订购 data.table?
这种情况属于通过单独的分组列对列进行操作的一般结构。
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
数据
df <- data.frame(id=factor(c('001', '001', '001', '002', '002', '003', '003')),
name=c(rep('A', 5), rep('B', 2)),
amount=c(10,10,12,3,4,20,20),
year=c(2010, 2011, 2012, 2012, 2013, 2011, 2012)
)
我有一个 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
问题是:
- 我在data.table的Github上找到了
shift
函数,为什么调用函数失败? - 我认为 data.table 中的
by
等价于 SQL 中的PARTITION BY
,那么ORDER BY
在 R 中的对应物是什么?我是否必须在执行任何聚合之前设置 data.table 的键,以便订购 data.table?
这种情况属于通过单独的分组列对列进行操作的一般结构。
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
数据
df <- data.frame(id=factor(c('001', '001', '001', '002', '002', '003', '003')),
name=c(rep('A', 5), rep('B', 2)),
amount=c(10,10,12,3,4,20,20),
year=c(2010, 2011, 2012, 2012, 2013, 2011, 2012)
)