2016-10-24 9 views
1

私はグループと日付がdata.frameです。各グループの最小期間の日付範囲内に欠けている日付をすべて記入するにはどうすればよいですか?グループで不足している日付を記入する方法

dplyrでこれを行うのが理想です。しかし、最終的には、可能な限り数行の(読みやすい)コードでこれを効率的に実行したいと思っています。以下は最小限の例です。私は実際に多くの日付とグループを持っています。私のアプローチはどちらも一見醜いものに見える。より良い方法がなければなりません、そうですか?

#### setup #### 

library(sqldf) 
library(dplyr) 
df <- data.frame(the_group = rep(LETTERS[1:2], each=3), date = Sys.Date() + c(0:2, 1:3), stringsAsFactors = F) %>% 
    tbl_df() %>% 
    slice(-2) # represents that I may be missing data in a range! 

#### dplyr approach with cross join dummy #### 
full_seq <- data.frame(cross_join_dummy = 1, date = seq.Date(from=min(df$date), to=max(df$date), by = "day")) 

range_by_group <- df %>% 
    group_by(the_group) %>% 
    summarise(min_date = min(date), max_date = max(date)) %>% 
    ungroup() %>% 
    mutate(cross_join_dummy = 1) 

desired <- range_by_group %>% 
    inner_join(full_seq, by="cross_join_dummy") %>% 
    filter(date >= min_date, date <= max_date) %>% 
    select(the_group, date) 

#### sqldf approach #### 
full_seq <- data.frame(date = as.character(seq.Date(from=min(df$date), to=max(df$date), by="day"))) 

df <- df %>% 
    mutate(date = as.character(date)) 

range_by_group <- sqldf(" 
        SELECT the_group, MIN(date) AS min_date, MAX(date) AS max_date 
        FROM df 
        GROUP BY the_group 
        ") 

desired <- sqldf(" 
      SELECT rbg.the_group, fs.date 
      FROM range_by_group rbg 
      JOIN full_seq fs 
       ON fs.date BETWEEN rbg.min_date AND rbg.max_date 
      ") 

答えて

2

1)何のパッケージ -

によっては、これはすべてのパッケージを使用しません。 byは、dfdf$the_groupで分割し、それぞれの操作を実行します。 do.call("rbind", ...)はグループをまとめて戻します。ここ

seq_date <- function(x) seq(min(x), max(x), by = "day") 
do.call("rbind", by(df, df$the_group, with, 
    data.frame(the_group = the_group[1], date = seq_date(date)))) 

2)data.table data.tableを用いた溶液です。 seq_dateは tidyverseこれはグループ上式表記で指定された関数を適用し、データフレームにまとめ、結果を入れてpurrrからmap_dfを使用して(1)

library(data.table) 

dt <- as.data.table(df) 
dt[, list(date = seq_date(date)), by = the_group] 

3)からのものです。 data_frameは、チブルパッケージからのものです。 seq_dateは(1)からのものです。

library(tidyverse) 

df %>% 
    split(.$the_group) %>% 
    map_df(~ data_frame(the_group = .$the_group[1], date = seq_date(.$date))) 

4)tapply

4A)tapply - tidyr/reshape2seq_date(1)からのものです。

library(tidyr) 
library(reshape2) 

df %>% 
    { tapply(.$date, .$the_group, seq_date, simplify = FALSE) } %>% 
    melt %>% 
    unnest 

図4b)tapply - なしパッケージ最後の行ピース一緒にすべてのパッケージの必要性を回避するtapplyの出力。 seq_dateは(1)からのものです。

ta <- tapply(df$date, df$the_group, seq_date, simplify = FALSE) 
data.frame(the_group = rep(names(ta), lengths(ta)), date = do.call("c", ta)) 

図4c)tapplyは - 私たちが使用できる格子から格子パッケージのmake.groupsta上(図4b)。ラティスにはRがあらかじめインストールされているので、追加のパッケージをインストールする必要はありません。残念ながらmake.groupsは日付classの属性を削除して戻します。またmake.groupswhichdataの列名を使用するので、列名を修正します。

library(lattice) 
with(do.call("make.groups", ta), 
    data.frame(the_group = which, date = structure(data, class = "Date"))) 

4D)tapply - なしパッケージ - スタック我々は最初の"Date"クラスを削除して所望の形に(図4b)からtaを変換するstackを使用することができます。その後、stackを適用した後、"Date"クラスを復元​​できます。 stackは、setNamesを使用して置き換えられるハードコーディングされた列名を使用します。

stack_dates <- function(x) 
    transform(stack(lapply(x, as.vector)), values = structure(values, class = "Date")) 
setNames(stack_dates(ta)[2:1], c("the_group", "date")) 
+0

「パッケージなし」アプローチがなぜ機能するのかを理解することがなおも問題です。特に 'the_group [1]'と 'with' – lowndrul

+1

と同じです。do.call(" rbind "と同じです.df、df $ the_group、function(x) data.frame(the_group = x $ the_group [ 1]、date = seq(min(x $ date)、max(x $ date)、by = "day"))) '短縮化のために' with'を使いました。 'the_group'はグループ内では定数なので、データフレームに2つの異なる長さベクトルを混在させることができないので、最初のコンポーネントを使用しました。 –

+0

今日の改良点を踏まえて少し拡張された答え(OCT 2017 )は分割の代わりに 'nest()'機能を使い、OPがここで 'tidyverse'の例として使用するマップの組み合わせになります。やや清潔なプレゼンテーション。さもなければ、ここの論理は同じままです。 – jacobsg

関連する問題