代码之家  ›  专栏  ›  技术社区  ›  JerryWho

在分组的时间序列中填充缺失的日期-一种整洁的方式?

  •  17
  • JerryWho  · 技术社区  · 7 年前

    给定数据。包含时间序列和一个或多个分组字段的框架。所以我们有几个时间序列-每个分组组合一个。 但有些日期不见了。

    让我们来看一个例子:

    library(dplyr)
    library(lubridate)
    
    set.seed(1234)
    # Time series should run vom 2017-01-01 til 2017-01-10
    date <- data.frame(date = seq.Date(from=ymd("2017-01-01"), to=ymd("2017-01-10"), by="days"), v = 1)
    # Two grouping dimensions
    d1   <- data.frame(d1 = c("A", "B", "C", "D"), v = 1)
    d2   <- data.frame(d2 = c(1, 2, 3, 4, 5), v = 1)
    
    # Generate the data.frame
    df <- full_join(date, full_join(d1, d2)) %>%
      select(date, d1, d2) 
    # and ad to value columns
    df$v1 <- runif(200)
    df$v2 <- runif(200)
    
    # group by the dimension columns
    df <- df %>% 
      group_by(d1, d2)
    
    # create missing dates
    df.missing <- df %>%
      filter(v1 <= 0.8)
    
    # So now  2017-01-01 and 2017-01-10, A, 5 are missing now
    df.missing %>%
      filter(d1 == "A" & d2 == 5)
    
    # A tibble: 8 x 5
    # Groups:   d1, d2 [1]
            date     d1    d2         v1        v2
          <date> <fctr> <dbl>      <dbl>     <dbl>
    1 2017-01-02      A     5 0.21879954 0.1335497
    2 2017-01-03      A     5 0.32977018 0.9802127
    3 2017-01-04      A     5 0.23902573 0.1206089
    4 2017-01-05      A     5 0.19617465 0.7378315
    5 2017-01-06      A     5 0.13373890 0.9493668
    6 2017-01-07      A     5 0.48613541 0.3392834
    7 2017-01-08      A     5 0.35698708 0.3696965
    8 2017-01-09      A     5 0.08498474 0.8354756
    

    为了添加缺失的日期,我生成了一个数据。包含所有日期的框架:

    start <- min(df.missing$date)
    end   <- max(df.missing$date)
    
    all.dates <- data.frame(date=seq.Date(start, end, by="day"))
    

    不,我想做一些类似的事情(记住:df.missing是group_by(d1,d2))

    df.missing %>%
      do(my_join())
    

    那么让我们定义my_join():

    my_join <- function(data) {
      # get value of both dimensions
      d1.set <- data$d1[[1]]
      d2.set <- data$d2[[1]]
    
      tmp <- full_join(data, all.dates) %>%
        # First we need to ungroup.  Otherwise we can't change d1 and d2 because they are grouping variables
        ungroup() %>%
        mutate(
          d1 = d1.set,
          d2 = d2.set 
        ) %>%
        group_by(d1, d2)
    
      return(tmp)
    }
    

    df.missing %>%
      do(my_join(.)) %>%
      filter(d1 == "A" & d2 == 5)
    
    # A tibble: 10 x 5
    # Groups:   d1, d2 [1]
             date     d1    d2         v1        v2
           <date> <fctr> <dbl>      <dbl>     <dbl>
     1 2017-01-02      A     5 0.21879954 0.1335497
     2 2017-01-03      A     5 0.32977018 0.9802127
     3 2017-01-04      A     5 0.23902573 0.1206089
     4 2017-01-05      A     5 0.19617465 0.7378315
     5 2017-01-06      A     5 0.13373890 0.9493668
     6 2017-01-07      A     5 0.48613541 0.3392834
     7 2017-01-08      A     5 0.35698708 0.3696965
     8 2017-01-09      A     5 0.08498474 0.8354756
     9 2017-01-01      A     5         NA        NA
    10 2017-01-10      A     5         NA        NA
    

    太棒了这就是我们要找的。 但是我们需要在my_join中定义d1和d2,这感觉有点笨拙。

    P、 美国:我把代码写进了要点: https://gist.github.com/JerryWho/1bf919ef73792569eb38f6462c6d7a8e

    4 回复  |  直到 7 年前
        1
  •  24
  •   austensen    4 年前

    tidyr 有一些很好的工具来解决这些问题。看看 complete .


    library(dplyr)
    library(tidyr)
    library(lubridate)
    
    want <- df.missing %>% 
      ungroup() %>%
      complete(nesting(d1, d2), date = seq(min(date), max(date), by = "day"))
    
    want %>% filter(d1 == "A" & d2 == 5) 
    
    #> # A tibble: 10 x 5
    #>        d1    d2       date         v1        v2
    #>    <fctr> <dbl>     <date>      <dbl>     <dbl>
    #>  1      A     5 2017-01-01         NA        NA
    #>  2      A     5 2017-01-02 0.21879954 0.1335497
    #>  3      A     5 2017-01-03 0.32977018 0.9802127
    #>  4      A     5 2017-01-04 0.23902573 0.1206089
    #>  5      A     5 2017-01-05 0.19617465 0.7378315
    #>  6      A     5 2017-01-06 0.13373890 0.9493668
    #>  7      A     5 2017-01-07 0.48613541 0.3392834
    #>  8      A     5 2017-01-08 0.35698708 0.3696965
    #>  9      A     5 2017-01-09 0.08498474 0.8354756
    #> 10      A     5 2017-01-10         NA        NA
    
        2
  •  2
  •   June    5 年前

    tsibble 作用 fill_gaps 应该轻松地完成这项工作。

    library(tsibble)
    df.missing %>% 
      # tsibble format
      as_tsibble(key = c(d1, d2), index = date) %>% 
      # fill gaps
      fill_gaps(.full = TRUE)
    
        3
  •  0
  •   CPak    7 年前

    这里有一个整洁的方式,从 df.missing

    library(tidyverse)
    ans <- df.missing %>% 
              nest(date) %>% 
              mutate(data = map(data, ~seq.Date(start, end, by="day"))) %>% 
              unnest(data) %>%
              rename(date = data) %>%
              left_join(., df.missing, by=c("date","d1","d2"))
    
    ans %>% filter(d1 == "A" & d2 == 5) 
    

          d1    d2       date         v1        v2
       <fctr> <dbl>     <date>      <dbl>     <dbl>
     1      A     5 2017-01-01         NA        NA
     2      A     5 2017-01-02 0.21879954 0.1335497
     3      A     5 2017-01-03 0.32977018 0.9802127
     4      A     5 2017-01-04 0.23902573 0.1206089
     5      A     5 2017-01-05 0.19617465 0.7378315
     6      A     5 2017-01-06 0.13373890 0.9493668
     7      A     5 2017-01-07 0.48613541 0.3392834
     8      A     5 2017-01-08 0.35698708 0.3696965
     9      A     5 2017-01-09 0.08498474 0.8354756
    10      A     5 2017-01-10         NA        NA
    

    -------------------------------------------------------------------------------------------------
    这里有一种替代方法 expand.grid dplyr

    with(df.missing, expand.grid(unique(date), unique(d1), unique(d2))) %>%
      setNames(c("date", "d1", "d2")) %>%
      left_join(., df.missing, by=c("date","d1","d2"))
    

              date d1 d2          v1          v2
    1   2017-01-01  A  1 0.113703411 0.660754634
    2   2017-01-02  A  1 0.316612455 0.422330675
    3   2017-01-03  A  1 0.553333591 0.424109178
    4   2017-01-04  A  1          NA          NA
    5   2017-01-05  A  1          NA          NA
    6   2017-01-06  A  1 0.035456727 0.352998502   
    
        4
  •  0
  •   G. Grothendieck    7 年前

    在这里 read.zoo fortify.zoo 然后展开 v1 v2 spread

    请注意:

    • 如果我们可以假设每个日期出现在至少一个分割变量的组合中,即。 sort(unique(df.missing$date)) merge df.missing 问题中显示的确实具有此属性:

      all(all.dates$date %in% df.missing$date)
      ## [1] TRUE
      
    • 我们可以在 合并 (或之后 如果每个日期与前一点一样至少存在一次),则可以使用宽形动物园对象,因为它已经具有所有日期。

    在代码中,标记为###的行可以在zoo(1.8.1)的开发版本中省略:

    library(dplyr)
    library(tidyr)
    library(zoo)
    
    split.vars <- c("d1", "d2")
    df.missing %>%
       as.data.frame %>%     ###
       read.zoo(split = split.vars) %>%
       merge(zoo(, seq(start(.), end(.), "day"))) %>%
       fortify.zoo(melt = TRUE) %>%
       separate(Series, c("v", split.vars)) %>%
       spread(v, Value)
    

    更新: 注意zoo 1.8.1中的简化。