R 通过id变量计算滚动和,缺少时间点
我正在努力学习R,我在SAS做了10多年的一些事情,但我不太清楚在R中做的最好方法。以以下数据为例:R 通过id变量计算滚动和,缺少时间点,r,sas,plyr,zoo,R,Sas,Plyr,Zoo,我正在努力学习R,我在SAS做了10多年的一些事情,但我不太清楚在R中做的最好方法。以以下数据为例: id class t count desired -- ----- ---------- ----- ------- 1 A 2010-01-15 1 1 1 A 2010-02-15 2 3 1 B 2010-04-15 3 3 1
id class t count desired
-- ----- ---------- ----- -------
1 A 2010-01-15 1 1
1 A 2010-02-15 2 3
1 B 2010-04-15 3 3
1 B 2010-09-15 4 4
2 A 2010-01-15 5 5
2 B 2010-06-15 6 6
2 B 2010-08-15 7 13
2 B 2010-09-15 8 21
我希望在4个月的滚动窗口内,按id、类计算所需的滚动和列。请注意,并非每个id和类的组合都有所有月份
在SAS中,我通常采用以下两种方式之一:
保留
加上一个按id和类别划分的密码李>
PROC SQL
在适当的窗口中,在id、class和df1.d-df2.d上使用从df as df1到df as df2的左连接t <- as.Date(c("2010-01-15","2010-02-15","2010-04-15","2010-09-15",
"2010-01-15","2010-06-15","2010-08-15","2010-09-15"))
class <- c("A","A","B","B","A","B","B","B")
id <- c(1,1,1,1,2,2,2,2)
count <- seq(1,8,length.out=8)
desired <- c(1,3,3,4,5,6,13,21)
df <- data.frame(id,class,t,count,desired)
t我几乎不好意思发布这篇文章。我通常都很好,但一定有更好的方法
这首先使用zoo
的as.yearmon
来获取月份和年份的日期,然后对其进行整形,为每个id
/类的组合获取一列,然后在之前、之后和缺少的月份填入零,然后使用zoo
来获取滚动总和,然后只提取所需的月份,并与原始数据帧合并
library(reshape2)
library(zoo)
df$yearmon <- as.yearmon(df$t)
dfa <- dcast(id + class ~ yearmon, data=df, value.var="count")
ida <- dfa[,1:2]
dfa <- t(as.matrix(dfa[,-c(1:2)]))
months <- with(df, seq(min(yearmon)-3/12, max(yearmon)+3/12, by=1/12))
dfb <- array(dim=c(length(months), ncol(dfa)),
dimnames=list(paste(months), colnames(dfa)))
dfb[rownames(dfa),] <- dfa
dfb[is.na(dfb)] <- 0
dfb <- rollsumr(dfb,4, fill=0)
rownames(dfb) <- paste(months)
dfb <- dfb[rownames(dfa),]
dfc <- cbind(ida, t(dfb))
dfc <- melt(dfc, id.vars=c("class", "id"))
names(dfc)[3:4] <- c("yearmon", "desired2")
dfc$yearmon <- as.yearmon(dfc$yearmon)
out <- merge(df,dfc)
> out
id class yearmon t count desired desired2
1 1 A Feb 2010 2010-02-15 2 3 3
2 1 A Jan 2010 2010-01-15 1 1 1
3 1 B Apr 2010 2010-04-15 3 3 3
4 1 B Sep 2010 2010-09-15 4 4 4
5 2 A Jan 2010 2010-01-15 5 5 5
6 2 B Aug 2010 2010-08-15 7 13 13
7 2 B Jun 2010 2010-06-15 6 6 6
8 2 B Sep 2010 2010-09-15 8 21 21
library(重塑2)
图书馆(动物园)
df$yearmon以下是一些解决方案:
1) zoo使用ave
,通过将原始系列z
与网格g
合并,为每组创建一个每月系列m
。然后计算滚动总和并仅保留原始时间点:
library(zoo)
f <- function(i) {
z <- with(df[i, ], zoo(count, t))
g <- zoo(, seq(start(z), end(z), by = "month"))
m <- merge(z, g)
window(rollapplyr(m, 4, sum, na.rm = TRUE, partial = TRUE), time(z))
}
df$desired <- ave(1:nrow(df), df$id, df$class, FUN = f)
注:我们假设时间在每个组内排序(如问题所示)。如果不是这样,则首先排序df
2) sqldf
library(sqldf)
sqldf("select id, class, a.t, a.'count', sum(b.'count') desired
from df a join df b
using(id, class)
where a.t - b.t between 0 and 100
group by id, class, a.t")
其中:
> df
id class t count desired
1 1 A 2010-01-15 1 1
2 1 A 2010-02-15 2 3
3 1 B 2010-04-15 3 3
4 1 B 2010-09-15 4 4
5 2 A 2010-01-15 5 5
6 2 B 2010-06-15 6 6
7 2 B 2010-08-15 7 13
8 2 B 2010-09-15 8 21
id class t count desired
1 1 A 2010-01-15 1 1
2 1 A 2010-02-15 2 3
3 1 B 2010-04-15 3 3
4 1 B 2010-09-15 4 4
5 2 A 2010-01-15 5 5
6 2 B 2010-06-15 6 6
7 2 B 2010-08-15 7 13
8 2 B 2010-09-15 8 21
注意:如果合并太大而无法放入内存,则使用sqldf(“…”,dbname=tempfile())
将中间结果存储在数据库中,然后动态创建并自动销毁
3) 基本Rsqldf解决方案激发了这个基本R解决方案,它只是将SQL转换为R:
m <- merge(df, df, by = 1:2)
s <- subset(m, t.x - t.y >= 0 & t.x - t.y <= 100)
ag <- aggregate(count.y ~ t.x + class + id, s, sum)
names(ag) <- c("t", "class", "id", "count", "desired")
注意:这会在内存中进行合并,如果数据集非常大,这可能是一个问题
更新:对第一个解决方案进行了较小的简化,并添加了第二个解决方案
更新2:添加了第三种解决方案。可以使用data.table库找到这个问题的非常有效的答案
##Utilize the data.table package
library("data.table")
data <- data.table(t,class,id,count,desired)[order(id,class)]
##Assign each customer an ID
data[,Cust_No:=.GRP,by=c("id","class")]
##Create "list" of comparison dates and values
Ref <- data[,list(Compare_Value=list(I(count)),Compare_Date=list(I(t))), by=c("id","class")]
##Compare two lists and see of the compare date is within N days
data$Roll.Val <- mapply(FUN = function(RD, NUM) {
d <- as.numeric(Ref$Compare_Date[[NUM]] - RD)
sum((d <= 0 & d >= -124)*Ref$Compare_Value[[NUM]])
}, RD = data$t,NUM=data$Cust_No)
##Print out data
data <- data[,list(id,class,t,count,desired,Roll.Val)][order(id,class)]
data
id class t count desired Roll.Val
1: 1 A 2010-01-15 1 1 1
2: 1 A 2010-02-15 2 3 3
3: 1 B 2010-04-15 3 3 3
4: 1 B 2010-09-15 4 4 4
5: 2 A 2010-01-15 5 5 5
6: 2 B 2010-06-15 6 6 6
7: 2 B 2010-08-15 7 13 13
8: 2 B 2010-09-15 8 21 21
##利用data.table包
库(“数据表”)
数据有了软件包,你可以在滚动窗口上计算一切。下面是使用sum\u run
library(runner)
df %>%
group_by(id) %>%
mutate(
output = sum_run(count, k = 30*4, idx = t)
)
# <dbl> <fct> <date> <dbl> <dbl> <dbl>
# 1 A 2010-01-15 1 1 1
# 1 A 2010-02-15 2 3 3
# 1 B 2010-04-15 3 3 6
# 1 B 2010-09-15 4 4 4
# 2 A 2010-01-15 5 5 5
# 2 B 2010-06-15 6 6 6
# 2 B 2010-08-15 7 13 13
# 2 B 2010-09-15 8 21 21
库(运行程序)
df%>%
分组依据(id)%>%
变异(
输出=总和运行(计数,k=30*4,idx=t)
)
#
#1A 2010-01-15 11
#1 A 2010-02-15 2 3 3
#1B 2010-04-15 3 6
#1B 2010-09-15 4
#2A 2010-01-15 5
#2b 2010-06-15 6
#2b 2010-08-15 7 13
#2b 2010-09-15 8 21
什么是d
?其定义未包含在安装代码中。请查看zoo
软件包。它可以相当容易地对时间一致的数据进行这些滚动总结。如果您对sql
感到满意,可以使用sqldf
包。@MatthewPlourd,我想@ADJ meandf我已经在使用sqldf包了。首先,我还没有完全弄清楚如何在sqldf查询中使用R函数,就像我在PROC SQL中使用许多SAS函数一样。在这种情况下,我的首选解决方案将涉及使用SAS的INTCK函数的R版本,以比我的原始示例中描述的更复杂的方式执行日期算法。但由于我是从零开始学习R,我更喜欢学习如何用R的方式做事(见Joe的评论),老实说,对于这样的东西,我只会继续使用SAS.)美好的很好地使用了ave
,我可能没有尽可能经常地使用它,另外还有一些使用zoo
的新方法。谢谢同时感谢您在zoo
软件包中所做的工作,非常感谢!悬赏,给一个当之无愧的回答。谢谢这是一个124天的滚动时间段。显然,这并不完全是4个月,但代码可以很容易地修改。
library(runner)
df %>%
group_by(id) %>%
mutate(
output = sum_run(count, k = 30*4, idx = t)
)
# <dbl> <fct> <date> <dbl> <dbl> <dbl>
# 1 A 2010-01-15 1 1 1
# 1 A 2010-02-15 2 3 3
# 1 B 2010-04-15 3 3 6
# 1 B 2010-09-15 4 4 4
# 2 A 2010-01-15 5 5 5
# 2 B 2010-06-15 6 6 6
# 2 B 2010-08-15 7 13 13
# 2 B 2010-09-15 8 21 21