2016-03-23 13 views
5

私は、ユーザーの特定のタイプのトランザクションを表す不規則な時系列データを持っています。各データ行はタイムスタンプを付けられ、その時点でのトランザクションを表します。データの不規則な性質によって、あるユーザは1日に100行を持ち、他のユーザは1日に0または1トランザクションを持つことがあります。dplyr:ローリング時間ウィンドウを使用したデータのグループ化と要約/変更

data.frame(
    id = c(1, 1, 1, 1, 1, 2, 2, 3, 4), 
    date = c("2015-01-01", 
      "2015-01-01", 
      "2015-01-05", 
      "2015-01-25", 
      "2015-02-15", 
      "2015-05-05", 
      "2015-01-01", 
      "2015-08-01", 
      "2015-01-01"), 
    n_widgets = c(1,2,3,4,4,5,2,4,5) 
) 

    id  date n_widgets 
1 1 2015-01-01   1 
2 1 2015-01-01   2 
3 1 2015-01-05   3 
4 1 2015-01-25   4 
5 1 2015-02-15   4 
6 2 2015-05-05   5 
7 2 2015-01-01   2 
8 3 2015-08-01   4 
9 4 2015-01-01   5 

多くの場合、私は、ユーザーに関するいくつかのローリング統計情報を知りたい:

データは次のようになります。例えば:ある日のこのユーザについて、過去30日間に何回取引が行われたか、過去30日間に売られたウィジェットの数など

上記の例に対応して、データは次のようになります。

時間ウィンドウが毎日ある場合 は
id  date n_widgets n_trans_30 total_widgets_30 
1 1 2015-01-01   1   1    1 
2 1 2015-01-01   2   2    3 
3 1 2015-01-05   3   3    6 
4 1 2015-01-25   4   4    10 
5 1 2015-02-15   4   2    8 
6 2 2015-05-05   5   1    5 
7 2 2015-01-01   2   1    2 
8 3 2015-08-01   4   1    4 
9 4 2015-01-01   5   1    5 

その後、解決策は単純です:同様にdata %>% group_by(id, date) %>% summarize(...)

時間ウィンドウがある場合は、毎月これもlubridateと比較的簡単です:data %>% group_by(id, year(date), month(date)) %>% summarize(...)

は、私がいる課題であるしかし、どのように任意の期間のためのセットアップ時間ウィンドウをする:5日間、10日間など

ありもRcppRollライブラリだが、両方RcppRollzooでローリング機能は、より多くのセットアップに見えます定期的な時系列です。これらのウィンドウ関数は、指定された期間ではなく行数に基づいて動作することがわかります。主な違いは、日付とユーザーによって異なる期間の行がある可能性があることです。例えば、2015-01-01の前5日間のトランザクションの数が100件の取引に等しく、同一のユーザに対して2015-02-01の前5日間のトランザクションの数が5に等しいことを、ユーザ1のために可能

をですトランザクション。したがって、設定された数の行を検索するだけでは機能しません。

また、別のSOしかし受け入れソリューションはdata.tableを使用していたし、私はこれを具体的に実現するdplyr方法を探しています(Create new column based on condition that exists within a rolling date)不規則時系列タイプデータのローリング日付を議論するスレッドがあります。

この問題の中心にあると思いますが、この問題は次の質問に答えることで解決できます。group_byの任意の期間をdplyrにするにはどうすればよいですか。また、group_byという複雑なことなしに上記のように異なるdplyrの方法がある場合、どうすればいいですか?

EDIT:ローリングウィンドウの性質をより明確にするための例を更新しました。

答えて

0

EDITEDは以下のコメントに基づいています。

あなたは5日間まで、このような何かを試すことができます。この場合

df %>% 
    arrange(id, date) %>% 
    group_by(id) %>% 
    filter(as.numeric(difftime(Sys.Date(), date, unit = 'days')) <= 5) %>% 
    summarise(n_total_widgets = sum(n_widgets)) 

を、現在の5日以内にはありません。したがって、出力は生成されません。

IDごとに、最後の5日間取得するには、あなたはこのような何かを行うことができます。

df %>% 
    arrange(id, date) %>% 
    group_by(id) %>% 
    filter(as.numeric(difftime(max(date), date, unit = 'days')) <= 5) %>% 
    summarise(n_total_widgets = sum(n_widgets)) 

出力結果は次のようになります。

Source: local data frame [4 x 2] 

    id n_total_widgets 
    (dbl)   (dbl) 
1  1    4 
2  2    5 
3  3    4 
4  4    5 
+1

私は編集しました...あなたはそれを計算する方法を厳密にスイートに変更することができます。現在の日付を含むか含まないか、別の日付から数え始めます。 – Gopala

+0

上記は、すべての行を現在の日付または設定された日付と比較するときには便利です。ただし、すべての行が異なる時間帯に対応していることを確認してください:2015-01-01の場合は、2014-12-28 - 2015-01-01のデータと集計する必要があります.2015-01-25の場合は2015-01-21から2015-01-25までのデータを集計します。 –

+0

例えば、 'Sys.Date()'の代わりに 'max(date)'を使うことができますので、グループごとに異なる参照( 'id')があります。 – Gopala

4

これは、SQLを使用して行うことができます。

library(sqldf) 

dd <- transform(data, date = as.Date(date)) 
sqldf("select a.*, count(*) n_trans30, sum(b.n_widgets) 'total_widgets30' 
     from dd a 
     left join dd b on b.date between a.date - 30 and a.date 
         and b.id = a.id 
         and b.rowid <= a.rowid 
     group by a.rowid") 

付与:

id  date n_widgets n_trans30 total_widgets30 
1 1 2015-01-01   1   1    1 
2 1 2015-01-01   2   2    3 
3 1 2015-01-05   3   3    6 
4 1 2015-01-25   4   4    10 
5 2 2015-05-05   5   1    5 
6 2 2015-01-01   2   1    2 
7 3 2015-08-01   4   1    4 
8 4 2015-01-01   5   1    5 
2

別のアプローチは、あなたが一日ごとに複数の観測を持っているという事実は、おそらくかかわらず、問題を作成しているローリング機能(RcppRoll::roll_sum

を使用し、(tidyr::completeを使用して)すべての可能な日を格納するデータセットを拡大することです。.. 。

library(tidyr) 
library(RcppRoll) 
df2 <- df %>% 
    mutate(date=as.Date(date)) 

## create full dataset with all possible dates (go even 30 days back for first observation) 
df_full<- df2 %>% 
mutate(date=as.Date(date)) %>% 
    complete(id, 
     date=seq(from=min(.$date)-30,to=max(.$date), by=1), 
     fill=list(n_widgets=0)) 

## now use rolling function, and keep only original rows (left join) 
df_roll <- df_full %>% 
    group_by(id) %>% 
    mutate(n_trans_30=roll_sum(x=n_widgets!=0, n=30, fill=0, align="right"), 
     total_widgets_30=roll_sum(x=n_widgets, n=30, fill=0, align="right")) %>% 
    ungroup() %>% 
    right_join(df2, by = c("date", "id", "n_widgets")) 

結果が(偶然に)あなた

 id  date n_widgets n_trans_30 total_widgets_30 
    <dbl>  <date>  <dbl>  <dbl>   <dbl> 
1  1 2015-01-01   1   1    1 
2  1 2015-01-01   2   2    3 
3  1 2015-01-05   3   3    6 
4  1 2015-01-25   4   4    10 
5  1 2015-02-15   4   2    8 
6  2 2015-05-05   5   1    5 
7  2 2015-01-01   2   1    2 
8  3 2015-08-01   4   1    4 
9  4 2015-01-01   5   1    5 

と同じですが言ったように、それは意志最後の30日間ではなく、最後の30日間をカウントするので、何日間か失敗します。したがって、最初にsummariseの情報を1日ごとに入力してから適用することができます。

関連する問題