Как добавить вычисляемые столбцы во вложенные фреймы данных (столбцы списка) с помощью purrr

Я хотел бы выполнить вычисления во вложенном фрейме данных (хранящемся в виде столбца списка) и добавить вычисленную переменную обратно в каждый фрейм данных, используя функции purrr. Я буду использовать этот результат для присоединения к другим данным, и сохранение его компактности помогает мне лучше организовать и изучить его. Я могу сделать это за пару шагов, но похоже, что есть решение, с которым я еще не сталкивался. Если есть решение, мне нелегко его найти.

Загрузить библиотеки. для примера требуются следующие пакеты (доступны на CRAN):

library(dplyr)
library(purrr)
library(RcppRoll) # to calculate rolling mean

Примеры данных с 3 объектами и повторяющиеся измерения с течением времени:

test <- data_frame(
  id= rep(1:3, each=20),
  time = rep(1:20, 3),
  var1 = rnorm(60, mean=10, sd=3),
  var2 = rnorm(60, mean=95, sd=5)
  )

Сохраните данные как вложенный фрейм данных:

t_nest <- test %>% nest(-id)

     id              data
  <int>            <list>
1     1 <tibble [20 x 3]>
2     2 <tibble [20 x 3]>
3     3 <tibble [20 x 3]>

Выполните расчеты. Я рассчитаю несколько новых переменных на основе данных, хотя решение только для одной можно было бы расширить позже. Результатом каждого вычисления будет числовой вектор той же длины, что и вход (n = 20):

t1 <- t_nest %>% 
  mutate(var1_rollmean4 = map(data, ~RcppRoll::roll_mean(.$var1, n=4, align="right", fill=NA)),
         var2_delta4 = map(data, ~(.$var2 - lag(.$var2, 3))*0.095),
         var3 = map2(var1_rollmean4, var2_delta4, ~.x -.y))

     id              data var1_rollmean4 var2_delta4       var3
  <int>            <list>         <list>      <list>     <list>
1     1 <tibble [20 x 3]>     <dbl [20]>  <dbl [20]> <dbl [20]>
2     2 <tibble [20 x 3]>     <dbl [20]>  <dbl [20]> <dbl [20]>
3     3 <tibble [20 x 3]>     <dbl [20]>  <dbl [20]> <dbl [20]>

мое решение - это unnest эти данные, а затем снова nest. В этом нет ничего плохого, но похоже, что существует лучшее решение.

t1 %>% unnest %>% 
  nest(-id)

     id              data
  <int>            <list>
1     1 <tibble [20 x 6]>
2     2 <tibble [20 x 6]>
3     3 <tibble [20 x 6]>

Это другое решение (из SO 42028710) близок, но не совсем потому, что это список, а не вложенные фреймы данных:

map_df(t_nest$data, ~ mutate(.x, var1calc = .$var1*100))   

Я нашел довольно много полезной информации с помощью purrr Cheatsheet, но не могу найти ответ.


person Matt L.    schedule 26.09.2017    source источник


Ответы (2)


Вы можете обернуть другой mutate при отображении через столбец data и добавить столбцы в каждый вложенный тиббл:

t11 <- t_nest %>% 
    mutate(data = map(data, 
        ~ mutate(.x, 
            var1_rollmean4 = RcppRoll::roll_mean(var1, n=4, align="right", fill=NA),
            var2_delta4 = (var2 - lag(var2, 3))*0.095,
            var3 = var1_rollmean4 - var2_delta4
        )
   ))

t11
# A tibble: 3 x 2
#     id              data
#  <int>            <list>
#1     1 <tibble [20 x 6]>
#2     2 <tibble [20 x 6]>
#3     3 <tibble [20 x 6]>

unnest-nest, а затем измените порядок столбцов внутри:

nest_unnest <- t1 %>% 
    unnest %>% nest(-id) %>% 
    mutate(data = map(data, ~ select(.x, time, var1, var2, var1_rollmean4, var2_delta4, var3)))

identical(nest_unnest, t11)
# [1] TRUE
person Psidom    schedule 26.09.2017

Похоже, что для того, что вы пытаетесь сделать, вложение не требуется

library(tidyverse)
library(zoo)
test %>%
  group_by(id) %>%
  mutate(var1_rollmean4 = rollapplyr(var1, 4, mean, fill=NA),
       var2_delta4 = (var2 - lag(var2, 3))*0.095,
         var3 = (var1_rollmean4 - var2_delta4))

# A tibble: 60 x 7
# Groups:   id [3]
      # id  time      var1      var2 var1_rollmean4 var2_delta4      var3
   # <int> <int>     <dbl>     <dbl>          <dbl>       <dbl>     <dbl>
 # 1     1     1  9.865199  96.45723             NA          NA        NA
 # 2     1     2  9.951429  92.78354             NA          NA        NA
 # 3     1     3 12.831509  95.00553             NA          NA        NA
 # 4     1     4 12.463664  95.37171      11.277950 -0.10312483 11.381075
 # 5     1     5 11.781704  92.05240      11.757076 -0.06945881 11.826535
 # 6     1     6 12.756932  92.15666      12.458452 -0.27064269 12.729095
 # 7     1     7 12.346409  94.32411      12.337177 -0.09952197 12.436699
 # 8     1     8 10.223695 100.89043      11.777185  0.83961377 10.937571
 # 9     1     9  4.031945  87.38217       9.839745 -0.45357658 10.293322
# 10     1    10 11.859477  97.96973       9.615382  0.34633428  9.269047
# ... with 50 more rows

Изменить Вы можете вложить результат, оставив %>% nest(-id) еще

Если вы по-прежнему предпочитаете гнездиться или гнездитесь по другим причинам, это будет похоже на

t1 <- t_nest %>% 
        mutate(data = map(data, ~.x %>% mutate(...)))

То есть вы изменяете .x в операторе map. Это будет рассматривать data как data.frame, а mutate привяжет к нему результаты с привязкой к столбцу.

person CPak    schedule 26.09.2017
comment
спасибо @Cpak. да, это может быть проще сделать вне вложенных данных, но в моем реальном наборе данных это более сложно, и я хотел бы хранить продольные данные вложенными для компактности. - person Matt L.; 27.09.2017