【问题标题】:Using purrr's map on nested lubridate classes在嵌套的 lubridate 类上使用 purrr 的映射
【发布时间】:2018-07-27 22:49:40
【问题描述】:

我正在尝试在嵌套 tibble 上为非标准类使用 map,特别是 lubridateinterval() 结果。我似乎无法在正确的班级中将其发送给unnest()

require(tidyverse)
#> Loading required package: tidyverse
#> Warning: package 'ggplot2' was built under R version 3.4.4
require(lubridate)
#> Loading required package: lubridate
#> 
#> Attaching package: 'lubridate'
#> The following object is masked from 'package:base':
#> 
#>     date

df <- structure(list(date = structure(c(16073, 16073, 16210, 16286, 
                                  16486, 16498, 16518, 16539, 16618, 16426, 16496, 16588, 16602, 
                                  16602, 16629, 16654, 16714, 16769, 16776, 17379), class = "Date"), 
               id = c(8843, 8843, 8843, 8843, 8843, 8843, 8843, 8843, 8843, 
                      8843, 8843, 8843, 8843, 8843, 8843, 8843, 8843, 8843, 8843, 
                      8843)), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, 
                                                                                      -20L), .Names = c("date", "id"))

df %>% 
  group_by(id) %>% 
  nest() %>% 
  mutate(date_range = map(data, ~interval(min(.x$date), max(.x$date)))) %>% 
  unnest(date_range)
#> Warning in combine_all(args[[1]]): Vectorizing 'Interval' elements may not
#> preserve their attributes
#> # A tibble: 1 x 3
#>      id data              date_range
#>   <dbl> <list>                 <dbl>
#> 1  8843 <tibble [20 × 1]>  112838400

reprex package (v0.2.0) 于 2018 年 7 月 27 日创建。

有什么方法可以将 map 与非标准类一起使用,还是仍然不支持?

编辑(更新):

这会产生我想要的东西,但是这样做的效率很低。即使我有解决方法,我也希望学习如何正确执行此操作:

df %>% 
  group_by(id) %>% 
  nest() %>% 
  mutate(date_min = map(data, ~min(.x$date)),
         date_max = map(data, ~max(.x$date))) %>% 
  unnest(date_min, date_max) %>% 
  mutate(date_range = interval(date_min, date_max)) 

【问题讨论】:

  • 我认为map 正在做你所期望的,返回Interval 对象的列表,但是unnest() 将这些间隔强制为数字。 ?unnest 表示它将在可以是原子向量、列表或数据帧的列表列上工作,但没有说任何关于工作 S4 objectsIntervals 的内容,因此是连贯的(我猜)

标签: r lubridate purrr


【解决方案1】:

根据发帖者的 cmets,我尝试以此作为使用 list-columns 的示例,而不是针对特定问题的解决方案。第一步形成一个list-column,其中包含一个由id 分组的日期的tibble。第二步创建一个包含区间对象的中间list-column。最后一步使用间隔对象的访问器函数简化原子向量。

   df <- structure(list(date = structure(c(16073, 16073, 16210, 16286, 
                                        16486, 16498, 16518, 16539, 16618, 16426, 16496, 16588, 16602, 
                                        16602, 16629, 16654, 16714, 16769, 16776, 17379), class = "Date"), 
                     id = c(8840, 8840, 8840, 8840, 8840, 8840, 8840, 8840, 8840, 
                            8843, 8843, 8843, 8843, 8843, 8843, 8843, 8843, 8843, 8843, 
                            8843)), class = c("tbl_df", "tbl", "data.frame"), 
                     row.names = c(NA, -20L), .Names = c("date", "id"))
#
#   Example of three steps of list-column pipeline 
#
  df_int <- df %>%
    group_by(id) %>% 
    nest(date, .key="date_data")  %>%                       # step 1: create data list-column 
    mutate( date_range = map(.x=date_data,                  # step 2: create intermediate list-columns
                        ~interval(min(.x$date), max(.x$date))) ) %>%
    mutate(start = map(date_range, int_start),              # step 3: simplify the list-columns back down to atomic vectors
           length = map_dbl(date_range, int_length) ) %>%   
    unnest(start, length)

给了

df_int
# A tibble: 2 x 5
     id date_data         date_range     start                 length
  <dbl> <list>            <list>         <dttm>                 <dbl>
1  8840 <tibble [9 x 1]>  <S4: Interval> 2014-01-03 00:00:00 47088000
2  8843 <tibble [11 x 1]> <S4: Interval> 2014-12-22 00:00:00 82339200

结果是按标识符分组的单个 tibble,其中包含原始数据、中间对象和可用于进一步处理的简化常规数据。

完整的解释请参见 Hadley 的“R for Data Science”,尤其是第 20 章中关于 List-Columns 的部分。

interval的特例

interval 是一种特殊情况,因为它的参数startend 接受向量并生成包含多个间隔的interval 对象 这允许我们执行以下操作:

#  Use summarize to form the list-column with dates
#     and calculate the start and end dates as vectors for each id
#
  df_int2 <- df %>%
            group_by(id) %>%
            summarize( data = list(tibble(date)),
                       start_date = min(date),
                       end_date = max(date))
#
#   summarize has returned the grouped dates as a list of tibbles 
#           and has removed the grouping on id.  
#   mutate can then use the vectors start_date and end_date 
#     in interval to calcuate an interval object containing the two intervals
#
  df_int2 <- df_int2 %>% mutate(date_range = interval(start_date, end_date))
#

【讨论】:

  • 放弃unnest() 实际上是我开始的方式。问题在于它将结果保留为单项列表。由于interval() 结果已经包含起始值,因此再调用一次map 调用来访问它似乎是多余的。
  • 我已经更新了上面的答案。我假设您有兴趣获取 interval 对象,而不仅仅是计算每个组的最小和最大日期。
  • 谢谢。这解决了眼前的问题(就像我的编辑一样),但我真的很想解决取消嵌套非标准类的问题。
  • 虽然可能存在特殊情况,但`unnest` 通常不用于包含“非标准”类的列表列。见上文。
  • 那么有没有办法以保留它的方式将嵌套列出的列与单个项目解包?
猜你喜欢
  • 1970-01-01
  • 2019-12-27
  • 2021-02-28
  • 2018-07-28
  • 1970-01-01
  • 2018-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多