16

私は 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      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

ID、クラス、および 4 か月のローリング ウィンドウ内のローリング サムとして必要な列を計算したいと考えています。id と class の組み合わせごとにすべての月が存在するわけではないことに注意してください。

SAS では、通常、次の 2 つの方法のいずれかでこれを行います。

  1. RETAINプラスIDとクラスによる。
  2. PROC SQL適切なウィンドウ内の id、class、および df1.d-df2.d で、df1 としての df から df2 としての df への左結合

この種の問題に対する最適な R のアプローチは何ですか?

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)
4

4 に答える 4

18

以下にいくつかの解決策を示します。

1)動物園を使用して、各グループに対して、元のシリーズをグリッド とマージすることによりave、月次シリーズ を作成します。次に、ローリング合計を計算し、元の時点のみを保持します。mzg

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
  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

注:時間は各グループ内で順序付けられていると想定しています (質問のように)。そうでない場合は、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")

与える:

  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)ベース R sqldf ソリューションは、SQL を R に変換するだけのこのベース 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")

結果は次のとおりです。

> ag
           t class id count desired
1 2010-01-15     A  1     1       1
2 2010-02-15     A  1     2       3
3 2010-04-15     B  1     3       3
4 2010-09-15     B  1     4       4
5 2010-01-15     A  2     5       5
6 2010-06-15     B  2     6       6
7 2010-08-15     B  2     7      13
8 2010-09-15     B  2     8      21

注:これはメモリ内でマージを行いますが、データ セットが非常に大きい場合に問題になる可能性があります。

更新: 最初のソリューションのマイナーな単純化と、2 番目のソリューションの追加。

更新 2: 3 番目のソリューションを追加しました。

于 2013-06-04T19:32:16.780 に答える
4

この問題に対する非常に効率的な答えは、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
于 2014-04-17T14:21:15.680 に答える