Преобразовать рекурсивный список в data.table R - PullRequest
1 голос
/ 09 февраля 2020

У меня есть список рекурсивных списков, которые я хотел бы объединить в один data.table. Каждый элемент списка верхнего уровня (mylist) имеет два элемента:

  1. Timestamp, который представляет собой вектор символов
  2. Value, который представляет собой список

Несмотря на структуру (изображение ниже), каждый элемент имеет класс data.table.

На рисунке ниже показана структура этого списка (код был бы слишком длинным):

enter image description here

> str(mylist[[1]])
Classes ‘data.table’ and 'data.frame':  10 obs. of  2 variables:
 $ Timestamp: chr  "2019-06-01T00:00:00Z" "2019-06-01T00:15:00Z" "2019-06-01T00:30:00Z" "2019-06-01T00:45:00Z" ...
 $ Value    :List of 10
  ..$ : num 100
  ..$ : num 100
  ..$ : num 100
  ..$ : num 100
  ..$ : num 100
  ..$ : num 100
  ..$ : num 100
  ..$ : num 100
  ..$ : num 100
  ..$ : num 100

Right теперь я запускаю два цикла, чтобы получить объединенное data.table:

L oop 1 для преобразования Timestamp в R дату и время и установить key

new_list <- lapply(1:length(mylist), function(n){
  z <- mylist[[n]]
  c1 <- as.POSIXct(z$Timestamp, format = '%Y-%m-%dT%H:%M:%S', tz = 'UTC')
  c2 <- as.numeric(unlist(z$Value))
  dt <- data.table(c1 = c1, c2 = c2)
  colnames(dt) <- c('time', names(mylist)[n])
  setkey(dt, 'time')
  return((dt))
})

key настроен для быстрого слияния (открыт для других более быстрых способов). Это l oop завершается ошибкой, когда он встречает пустой data.table (4-й элемент в этом списке).

L oop 2, чтобы объединить список в один data.table

Очевидно, что это работает только тогда, когда L oop 1 не дает сбой, т.е. нет нуля data.table в списке.

dt <- new_list [<a href="https://i.stack.imgur.com/nqJYq.png" rel="nofollow noreferrer"> 1 ] lapply (2: length (new_list), function (k) {dt << - объединить (dt, new_list [[k]) ], by = 'time', all = T)}) </p>

Итак, мои проблемы:

  1. Что делать, если одна из записей в mylist пуста data.table или list.
  2. Каков наилучший способ объединить их всех с точки зрения скорости и возможных ошибок.

Пример данных приведен ниже, мой фактический список содержит 40 записей, каждая из которых ~ 30 000 строк.

Обновление: Комбинированный L oop

listMerge <- function(listname){
  ret_list <- lapply(1:length(listname), function(n){
    z <- listname[[n]]
    c1 <- as.POSIXct(z$Timestamp, format = '%Y-%m-%dT%H:%M:%S', tz = 'UTC')
    c2 <- as.numeric(unlist(z$Value))
    dt <- data.table(c1 = c1, c2 = c2)
    colnames(dt) <- c('time', names(listname)[n])
    setkey(dt, 'time')
    return((dt))
  })

  ndat <- ret_list[[1]]
  lapply(2:length(ret_list), function(k){
    if(nrow(ret_list[[k]]) > 0){
      ndat <<- merge(ndat, h[[k]], by = 'time', all = T)
    }
  })

  return(ndat)
}

Это работает - не уверен, что есть более быстрый способ получить желаемый стол.

Данные

dput(mylist)
list(A = structure(list(Timestamp = c("2019-06-01T00:00:00Z", 
"2019-06-01T00:15:00Z", "2019-06-01T00:30:00Z", "2019-06-01T00:45:00Z", 
"2019-06-01T01:00:00Z", "2019-06-01T01:15:00Z", "2019-06-01T01:30:00Z", 
"2019-06-01T01:45:00Z", "2019-06-01T02:00:00Z", "2019-06-01T02:15:00Z"
), Value = list(100.050957, 100.080826, 100.120308, 100.053459, 
    100.053825, 100.04792, 100.0679, 100.088554, 100.102737, 
    100.103653)), row.names = c(NA, -10L), class = c("data.table", 
"data.frame"), .internal.selfref = <pointer: 0x7fe0a100a6e0>), 
    B = structure(list(Timestamp = c("2019-06-01T00:00:00Z", 
    "2019-06-01T00:15:00Z", "2019-06-01T00:30:00Z", "2019-06-01T00:45:00Z", 
    "2019-06-01T01:00:00Z", "2019-06-01T01:15:00Z", "2019-06-01T01:30:00Z", 
    "2019-06-01T01:45:00Z", "2019-06-01T02:00:00Z", "2019-06-01T02:15:00Z"
    ), Value = list(38.892395, 45.7738266, 53.21701, 57.08103, 
        62.1048546, 68.58914, 68.98703, 69.5170746, 71.49378, 
        78.59612)), row.names = c(NA, -10L), class = c("data.table", 
    "data.frame"), .internal.selfref = <pointer: 0x7fe0a100a6e0>), 
    C = structure(list(Timestamp = c("2019-06-01T00:00:00Z", 
    "2019-06-01T00:15:00Z", "2019-06-01T00:30:00Z", "2019-06-01T00:45:00Z", 
    "2019-06-01T01:00:00Z", "2019-06-01T01:15:00Z", "2019-06-01T01:30:00Z", 
    "2019-06-01T01:45:00Z", "2019-06-01T02:00:00Z", "2019-06-01T02:15:00Z"
    ), Value = list(30.5898361, 29.75237, 27.63596, 26.5089836, 
        25.6826324, 24.909977, 24.4333439, 23.5524445, 23.1864853, 
        22.7402916)), row.names = c(NA, -10L), class = c("data.table", 
    "data.frame"), .internal.selfref = <pointer: 0x7fe0a100a6e0>), 
    D = NULL, E = structure(list(Timestamp = c("2019-06-01T00:00:00Z", 
    "2019-06-01T00:15:00Z", "2019-06-01T00:30:00Z", "2019-06-01T00:45:00Z", 
    "2019-06-01T01:00:00Z", "2019-06-01T01:15:00Z", "2019-06-01T01:30:00Z", 
    "2019-06-01T01:45:00Z", "2019-06-01T02:00:00Z", "2019-06-01T02:15:00Z"
    ), Value = list(8.299942, 8.44268, 8.440144, 8.445086, 8.41551, 
        8.424382, 8.438655, 8.46398, 8.445853, 8.476906)), row.names = c(NA, 
    -10L), class = c("data.table", "data.frame"), .internal.selfref = <pointer: 0x7fe0a100a6e0>), 
    F = structure(list(Timestamp = c("2019-06-01T00:00:00Z", 
    "2019-06-01T00:15:00Z", "2019-06-01T00:30:00Z", "2019-06-01T00:45:00Z", 
    "2019-06-01T01:00:00Z", "2019-06-01T01:15:00Z", "2019-06-01T01:30:00Z", 
    "2019-06-01T01:45:00Z", "2019-06-01T02:00:00Z", "2019-06-01T02:15:00Z"
    ), Value = list(85.48002, 88.071, 87.71461, 86.2900848, 85.50101, 
        82.4923248, 81.78603, 82.4504547, 82.00605, 82.12493)), row.names = c(NA, 
    -10L), class = c("data.table", "data.frame"), .internal.selfref = <pointer: 0x7fe0a100a6e0>), 
    G = structure(list(Timestamp = c("2019-06-01T00:00:00Z", 
    "2019-06-01T00:15:00Z", "2019-06-01T00:30:00Z", "2019-06-01T00:45:00Z", 
    "2019-06-01T01:00:00Z", "2019-06-01T01:15:00Z", "2019-06-01T01:30:00Z", 
    "2019-06-01T01:45:00Z", "2019-06-01T02:00:00Z", "2019-06-01T02:15:00Z"
    ), Value = list(0.870313, 0.862552762, 0.8827777, 0.8639478, 
        0.849139452, 0.874981, 0.833493, 0.89307636, 0.8647241, 
        0.8711139)), row.names = c(NA, -10L), class = c("data.table", 
    "data.frame"), .internal.selfref = <pointer: 0x7fe0a100a6e0>))

Ответы [ 3 ]

1 голос
/ 09 февраля 2020

Если я правильно понимаю, OP хочет создать единую таблицу data.table (термин «слияние», который используется OP, здесь несколько вводит в заблуждение). Это может быть достигнуто простым вызовом rbindlist(). rbindlist() заботится о пустых элементах в mylist.

library(data.table)
rbindlist(mylist, idcol = TRUE)
    .id            Timestamp     Value
 1:   A 2019-06-01T00:00:00Z   100.051
 2:   A 2019-06-01T00:15:00Z  100.0808
 3:   A 2019-06-01T00:30:00Z  100.1203
 4:   A 2019-06-01T00:45:00Z  100.0535
 5:   A 2019-06-01T01:00:00Z  100.0538
 6:   A 2019-06-01T01:15:00Z  100.0479
 7:   A 2019-06-01T01:30:00Z  100.0679
 8:   A 2019-06-01T01:45:00Z  100.0886
 9:   A 2019-06-01T02:00:00Z  100.1027
10:   A 2019-06-01T02:15:00Z  100.1037
11:   B 2019-06-01T00:00:00Z   38.8924
12:   B 2019-06-01T00:15:00Z  45.77383
13:   B 2019-06-01T00:30:00Z  53.21701
14:   B 2019-06-01T00:45:00Z  57.08103
15:   B 2019-06-01T01:00:00Z  62.10485
16:   B 2019-06-01T01:15:00Z  68.58914
17:   B 2019-06-01T01:30:00Z  68.98703
18:   B 2019-06-01T01:45:00Z  69.51707
19:   B 2019-06-01T02:00:00Z  71.49378
20:   B 2019-06-01T02:15:00Z  78.59612
21:   C 2019-06-01T00:00:00Z  30.58984
22:   C 2019-06-01T00:15:00Z  29.75237
23:   C 2019-06-01T00:30:00Z  27.63596
24:   C 2019-06-01T00:45:00Z  26.50898
25:   C 2019-06-01T01:00:00Z  25.68263
26:   C 2019-06-01T01:15:00Z  24.90998
27:   C 2019-06-01T01:30:00Z  24.43334
28:   C 2019-06-01T01:45:00Z  23.55244
29:   C 2019-06-01T02:00:00Z  23.18649
30:   C 2019-06-01T02:15:00Z  22.74029
31:   E 2019-06-01T00:00:00Z  8.299942
32:   E 2019-06-01T00:15:00Z   8.44268
33:   E 2019-06-01T00:30:00Z  8.440144
34:   E 2019-06-01T00:45:00Z  8.445086
35:   E 2019-06-01T01:00:00Z   8.41551
36:   E 2019-06-01T01:15:00Z  8.424382
37:   E 2019-06-01T01:30:00Z  8.438655
38:   E 2019-06-01T01:45:00Z   8.46398
39:   E 2019-06-01T02:00:00Z  8.445853
40:   E 2019-06-01T02:15:00Z  8.476906
41:   F 2019-06-01T00:00:00Z  85.48002
42:   F 2019-06-01T00:15:00Z    88.071
43:   F 2019-06-01T00:30:00Z  87.71461
44:   F 2019-06-01T00:45:00Z  86.29008
45:   F 2019-06-01T01:00:00Z  85.50101
46:   F 2019-06-01T01:15:00Z  82.49232
47:   F 2019-06-01T01:30:00Z  81.78603
48:   F 2019-06-01T01:45:00Z  82.45045
49:   F 2019-06-01T02:00:00Z  82.00605
50:   F 2019-06-01T02:15:00Z  82.12493
51:   G 2019-06-01T00:00:00Z  0.870313
52:   G 2019-06-01T00:15:00Z 0.8625528
53:   G 2019-06-01T00:30:00Z 0.8827777
54:   G 2019-06-01T00:45:00Z 0.8639478
55:   G 2019-06-01T01:00:00Z 0.8491395
56:   G 2019-06-01T01:15:00Z  0.874981
57:   G 2019-06-01T01:30:00Z  0.833493
58:   G 2019-06-01T01:45:00Z 0.8930764
59:   G 2019-06-01T02:00:00Z 0.8647241
60:   G 2019-06-01T02:15:00Z 0.8711139
    .id            Timestamp     Value

На следующем шаге Timestamp может быть приведен к POSIXct за один go (вместо манипулирования отдельными элементами mylist заранее ):

library(data.table)
rbindlist(mylist, idcol = TRUE)[
  , Timestamp := lubridate::as_datetime(Timestamp)][]
1 голос
/ 09 февраля 2020

Мы можем преобразовать столбец «Значение» list в вектор по unlist, а затем rbindlist list из data.table в один data.table. Здесь мы также предполагаем, что ОП хотел выполнить некоторую другую предварительную обработку в дополнение к unlist, как показано в посте ОП

library(data.table)
rbindlist(lapply(mylist, function(dat) if(!is.null(dat))
      dat[, Value := unlist(Value)]), idcol = 'grp')
#grp            Timestamp       Value
#1:   A 2019-06-01T00:00:00Z 100.0509570
#2:   A 2019-06-01T00:15:00Z 100.0808260
#3:   A 2019-06-01T00:30:00Z 100.1203080
#4:   A 2019-06-01T00:45:00Z 100.0534590
#5:   A 2019-06-01T01:00:00Z 100.0538250
#6:   A 2019-06-01T01:15:00Z 100.0479200
 #..

Кроме того, обратите внимание на применение rbindlist непосредственно на list с вложенным столбцом list не будет автоматически преобразовывать этот столбец в vector, то есть

str(rbindlist(mylist, idcol = TRUE))
#Classes ‘data.table’ and 'data.frame': 60 obs. of  3 variables:
# $ .id      : chr  "A" "A" "A" "A" ...
# $ Timestamp: chr  "2019-06-01T00:00:00Z" "2019-06-01T00:15:00Z" #"2019-06-01T00:30:00Z" "2019-06-01T00:45:00Z" ...
# $ Value    :List of 60
#  ..$ : num 100
#  ..$ : num 100
#  ..$ : num 100
#  ..$ : num 100
#  ..$ : num 100
#  ..$ : num 100
#  ..$ : num 100
# ...

Так что, возможно, нам придется выполнить unlist до rbindlist приложение или после

out <- rbindlist(mylist, idcol = 'grp')[, Value := unlist(Value)]
str(out)
#Classes ‘data.table’ and 'data.frame': 60 obs. of  3 variables:
# $ grp      : chr  "A" "A" "A" "A" ...
#$ Timestamp: chr  "2019-06-01T00:00:00Z" "2019-06-01T00:15:00Z" "2019-06-01T00:30:00Z" "2019-06-01T00:45:00Z" ...
#$ Value    : num  100 100 100 100 100 ...

Чтобы преобразовать метку времени в DateTime, мы можем использовать as.POSIXct

out[, Timestamp :=  as.POSIXct(Timestamp, format = "%Y-%m-%dT%TZ")]

Обновление

, если нам нужно сделать merge с помощью 'Timestamp', один из вариантов - преобразовать в xts и затем сделать merge

library(xts)
i1 <- !sapply(mylist, is.null)
mylist1 <- lapply(mylist[i1], function(dat)  dat[, Value := unlist(Value)])
outn <- Reduce(merge, lapply(mylist1, function(x) 
   xts(x$Value, order.by = as.POSIXct(x$Timestamp, format = "%Y-%m-%dT%TZ"))))
colnames(outn) <- paste0("Value", seq_len(ncol(outn)))
outn
#                      Value1   Value2   Value3   Value4   Value5    Value6
#2019-06-01 00:00:00 100.0510 38.89240 30.58984 8.299942 85.48002 0.8703130
#2019-06-01 00:15:00 100.0808 45.77383 29.75237 8.442680 88.07100 0.8625528
#2019-06-01 00:30:00 100.1203 53.21701 27.63596 8.440144 87.71461 0.8827777
#2019-06-01 00:45:00 100.0535 57.08103 26.50898 8.445086 86.29008 0.8639478
#2019-06-01 01:00:00 100.0538 62.10485 25.68263 8.415510 85.50101 0.8491395
#2019-06-01 01:15:00 100.0479 68.58914 24.90998 8.424382 82.49232 0.8749810
#2019-06-01 01:30:00 100.0679 68.98703 24.43334 8.438655 81.78603 0.8334930
#2019-06-01 01:45:00 100.0886 69.51707 23.55244 8.463980 82.45045 0.8930764
#2019-06-01 02:00:00 100.1027 71.49378 23.18649 8.445853 82.00605 0.8647241
#2019-06-01 02:15:00 100.1037 78.59612 22.74029 8.476906 82.12493 0.8711139

. Как объект xts, он может быть plot напрямую чтобы получить больше информации о столбцах «Значение»

plot(outn)

enter image description here


Или другой вариант - bind_rows из dplyr

library(dplyr)
library(lubridate)
library(purrr)
out1 <- bind_rows(mylist, .id = 'grp')%>% 
           mutate(Value = flatten_dbl(Value), Timestamp = ymd_hms(Timestamp))

str(out1)
#'data.frame':  60 obs. of  3 variables:
# $ Timestamp: POSIXct, format: "2019-06-01 00:00:00" "2019-06-01 00:15:00" "2019-06-01 00:30:00" "2019-06-01 00:45:00" ...
# $ Value    : num  100 100 100 100 100 ...
# $ grp      : chr  "A" "A" "A" "A" ...
0 голосов
/ 09 февраля 2020

Я думаю, вы хотите rbindlist и dcast. Поскольку это похоже на @Uwe и @akrun, я также сделаю .

library(data.table)
bind_list = rbindlist(mylist, idcol = "ID")
dcast(bind_list, Timestamp ~ ID)

               Timestamp        A        B        C        E        F         G
 1: 2019-06-01T00:00:00Z  100.051  38.8924 30.58984 8.299942 85.48002  0.870313
 2: 2019-06-01T00:15:00Z 100.0808 45.77383 29.75237  8.44268   88.071 0.8625528
 3: 2019-06-01T00:30:00Z 100.1203 53.21701 27.63596 8.440144 87.71461 0.8827777
 4: 2019-06-01T00:45:00Z 100.0535 57.08103 26.50898 8.445086 86.29008 0.8639478
 5: 2019-06-01T01:00:00Z 100.0538 62.10485 25.68263  8.41551 85.50101 0.8491395
 6: 2019-06-01T01:15:00Z 100.0479 68.58914 24.90998 8.424382 82.49232  0.874981
 7: 2019-06-01T01:30:00Z 100.0679 68.98703 24.43334 8.438655 81.78603  0.833493
 8: 2019-06-01T01:45:00Z 100.0886 69.51707 23.55244  8.46398 82.45045 0.8930764
 9: 2019-06-01T02:00:00Z 100.1027 71.49378 23.18649 8.445853 82.00605 0.8647241
10: 2019-06-01T02:15:00Z 100.1037 78.59612 22.74029 8.476906 82.12493 0.8711139
library(tidyr)
library(dplyr)
bind_list = bind_rows(mylist, .id = "ID")
pivot_wider(bind_list, id_cols = Timestamp, values_from = Value, names_from = ID)

              Timestamp        A        B        C        E        F         G
1  2019-06-01T00:00:00Z  100.051  38.8924 30.58984 8.299942 85.48002  0.870313
2  2019-06-01T00:15:00Z 100.0808 45.77383 29.75237  8.44268   88.071 0.8625528
3  2019-06-01T00:30:00Z 100.1203 53.21701 27.63596 8.440144 87.71461 0.8827777
4  2019-06-01T00:45:00Z 100.0535 57.08103 26.50898 8.445086 86.29008 0.8639478
5  2019-06-01T01:00:00Z 100.0538 62.10485 25.68263  8.41551 85.50101 0.8491395
6  2019-06-01T01:15:00Z 100.0479 68.58914 24.90998 8.424382 82.49232  0.874981
7  2019-06-01T01:30:00Z 100.0679 68.98703 24.43334 8.438655 81.78603  0.833493
8  2019-06-01T01:45:00Z 100.0886 69.51707 23.55244  8.46398 82.45045 0.8930764
9  2019-06-01T02:00:00Z 100.1027 71.49378 23.18649 8.445853 82.00605 0.8647241
10 2019-06-01T02:15:00Z 100.1037 78.59612 22.74029 8.476906 82.12493 0.8711139
...