2016-06-17 2 views
2

У меня есть список, как это:Преобразование списка смешанных элементов класса для фрейма данных, сохраняя при этом класс

list(
    structure(
     list(
      time = structure(
       1452841800, 
       class = c("POSIXct", "POSIXt") 
      ), 
      latitude = 34.0128987, 
      longitude = -84.7879747, 
      location = structure(
       list(), 
       .Names = character(0) 
      ), 
      day = "FRIDAY" 
     ), 
     .Names = c("time", "latitude", "longitude", "location", "day") 
    ), 
    structure(
     list(
      time = structure(
       1456875240, 
       class = c("POSIXct", "POSIXt") 
       ), 
      latitude = 35.85285882, 
      longitude = -78.69758511, 
      location = structure(
       list(
        postcode = "27612" 
       ), 
       .Names = "postcode" 
      ), 
      day = "TUESDAY" 
     ), 
     .Names = c("time", "latitude", "longitude", "location", "day") 
    ), 
    structure(
     list(
      time = structure(
       1456621440, 
       class = c("POSIXct", "POSIXt") 
      ), 
      latitude = 33.81418132, 
      longitude = -84.73134873, 
      location = structure(
       list(
        postcode = "30127" 
       ), 
       .Names = "postcode" 
      ), 
      day = "SATURDAY" 
     ), 
     .Names = c("time", "latitude", "longitude", "location", "day") 
    ), 
    structure(
     list(
      time = structure(
       1451953320, 
       class = c("POSIXct", "POSIXt") 
      ), 
      latitude = 33.6678031, 
      longitude = -86.5398931, 
      location = structure(
       list(
        postcode = "35173" 
       ), 
       .Names = "postcode" 
      ), 
      day = "MONDAY" 
     ), 
     .Names = c("time", "latitude", "longitude", "location", "day") 
    ), 
    structure(
     list(
      time = structure(
       1452966960, 
       class = c("POSIXct", "POSIXt") 
      ), 
      latitude = 33.8458767, 
      longitude = -84.0986578, 
      location = structure(
       list(
        postcode = "30047" 
       ), 
       .Names = "postcode" 
      ), 
      day = "SATURDAY" 
     ), 
     .Names = c("time", "latitude", "longitude", "location", "day") 
    ), 
    structure(
     list(
      time = structure(
       1455584160, 
       class = c("POSIXct", "POSIXt") 
      ), 
      latitude = 36.4001153, 
      longitude = -105.5727933, 
      location = structure(
       list(
        postcode = "87571" 
       ), 
       .Names = "postcode" 
      ), 
      day = "MONDAY" 
     ), 
     .Names = c("time", "latitude", "longitude", "location", "day") 
    ) 
) 

Я хочу, чтобы включить в кадр данных. Я почти добираюсь туда, но имею некоторые проблемы. Когда я удалить элементы списка, которые не являются «числовой», я получаю хороший кадр данных с числовыми столбцами следующим образом:

df <- as.data.frame(
    do.call(rbind, lapply(d, function(x) unlist(x[-c(4, 5)]))), 
    stringsAsFactors = FALSE 
) 
str(df) 
'data.frame': 6 obs. of 3 variables: 
$ time  : num 1.45e+09 1.46e+09 1.46e+09 1.45e+09 1.45e+09 ... 
$ latitude : num 34 35.9 33.8 33.7 33.8 ... 
$ longitude: num -84.8 -78.7 -84.7 -86.5 -84.1 ... 

До сих пор так хорошо ...

Теперь, когда у меня есть элемент символов в списке, я получаю все столбцы, принуждаемые к классу символов. Не то, что я хочу. Конечно, я могу снова вернуться обратно. Но ...

df <- as.data.frame(
     do.call(rbind, lapply(d, function(x) unlist(x[-4]))), 
     stringsAsFactors = FALSE 
     ) 
str(df) 
'data.frame': 6 obs. of 4 variables: 
$ time  : chr "1452841800" "1456875240" "1456621440" "1451953320" ... 
$ latitude : chr "34.0128987" "35.85285882" "33.81418132" "33.6678031" ... 
$ longitude: chr "-84.7879747" "-78.69758511" "-84.73134873" "-86.5398931" ... 
$ day  : chr "FRIDAY" "TUESDAY" "SATURDAY" "MONDAY" ... 

Наконец, поскольку location$postcode поле имеет пустой список, весь этот механизм не может даже дать мне правильный кадр данных. Я работаю вокруг него, извлекая из этого поля в отдельности, а столбец привязки его следующим образом:

postcode <- sapply(d, function(x) if (length(x$location)) unlist(x$location) else NA) 
df$postcode <- postcode 
df 
     time latitude longitude  day postcode 
1 1452841800 34.0128987 -84.7879747 FRIDAY  <NA> 
2 1456875240 35.85285882 -78.69758511 TUESDAY 27612 
3 1456621440 33.81418132 -84.73134873 SATURDAY 30127 
4 1451953320 33.6678031 -86.5398931 MONDAY 35173 
5 1452966960 33.8458767 -84.0986578 SATURDAY 30047 
6 1455584160 36.4001153 -105.5727933 MONDAY 87571 

Три вопроса:

1) Как сохранить класс при преобразовании моего списка в кадр данных?

2) Есть ли лучший способ справиться с нулевым перечисленными элементами в списке (мой пост поле кода)

3) Если нет другого пути на # 2, есть более эффективный способ сделать то, что я чем один цикл через данные? Я полагаю, я могу совместить нулевой чек-лист на почтовый индекс поля и сцепить в lapply я использую с do.call(rbind, ...)

EDIT: Для ясности, эти классы названных элементов в моем списке:

sapply(d[[1]], class) 
$time 
[1] "POSIXct" "POSIXt" 

$latitude 
[1] "numeric" 

$longitude 
[1] "numeric" 

$location 
[1] "list" 

$day 
[1] "character" 

В той степени, в которой «первый» случай работает, сохраняя числовые значения, которые все еще после преобразования моего элемента POSIXct time в числовой. Я бы предпочел, чтобы он остался нетронутым. :)

+0

См, также, 'do.call (data.frame, C (= stringsAsFactors значение FALSE, .mapply (с, lapply (д, функция (х) {х $ =, если местоположение (длина (х! $ location $ postcode)) NA else x $ location $ postcode; x}), NULL))) ', чтобы избежать принуждения. –

+0

И это невероятно быстро по сравнению с чем-либо еще, что я пробовал выше и выше моего исходного кода. Можете ли вы написать это как ответ, и я соглашусь? Очень хорошо...! – Gopala

ответ

3

Сделка с $location сама по себе (обходит проблему с пустым списком), а затем использует as.data.frame для каждого элемента списка (обходит проблему «все как символ»).

d2 <- lapply(d, function(df) { 
      as.data.frame(within(df, location <- if (length(location) > 0) location$postcode else NA), 
         stringsAsFactors = FALSE) 
     }) 
str(do.call(rbind, d2)) 
# 'data.frame': 6 obs. of 5 variables: 
# $ time  : POSIXct, format: "2016-01-14 23:10:00" "2016-03-01 15:34:00" "2016-02-27 17:04:00" ... 
# $ latitude : num 34 35.9 33.8 33.7 33.8 ... 
# $ longitude: num -84.8 -78.7 -84.7 -86.5 -84.1 ... 
# $ location : chr NA "27612" "30127" "35173" ... 
# $ day  : Factor w/ 4 levels "FRIDAY","TUESDAY",..: 1 2 3 4 3 4 

Редактировать: в качестве комментариев, производительность выше немного мрачная. Это может быть улучшено:

d3 <- lapply(d, function(df) { 
      within(df, location <- if (length(location) > 0) location$postcode else NA) 
     }) 
str(do.call(rbind.data.frame, c(d3, list(stringsAsFactors = FALSE)))) 
# 'data.frame': 6 obs. of 5 variables: 
# $ time  : num 1.45e+09 1.46e+09 1.46e+09 1.45e+09 1.45e+09 ... 
# $ latitude : num 34 35.9 33.8 33.7 33.8 ... 
# $ longitude: num -84.8 -78.7 -84.7 -86.5 -84.1 ... 
# $ location : chr NA "27612" "30127" "35173" ... 
# $ day  : chr "FRIDAY" "TUESDAY" "SATURDAY" "MONDAY" ... 

(. К сожалению, POSIX класс теряется в процессе Это может быть исправлено с помощью вызова as.POSIXct.)

Производительность последней техники является немного лучше, примерно в 3-4 раза быстрее.

+1

Мне нравится @ rawr использовать 'lengths' лучше, в противном случае они близки. – r2evans

+0

Я не могу проголосовать за еще 20 минут – rawr

+0

Это точно, так как heck - очень компактное решение моей проблемы, которая решает все проблемы класса. Но он довольно медленный, потому что он покрывает каждый элемент списка кадром данных и привязкой строк. В то время как мой исходный код невероятно быстрый, но заставляет чертовы объекты из-за преобразования матрицы. Dang .... Я не уверен, что здесь торговать. – Gopala

0

Во-первых, данные в $location области должны быть изменены соответствующим образом:

for(i in seq_along(d)) 
    d[[i]]$location = if(length((tmp <- d[[i]]$location$postcode))) tmp else NA_character_ 

Затем конкатенации, параллельно, каждый суб-элемент каждого элемента d в Map(c, d[[1]], d[[2]], ...) форме, используя удобный .mapply версию , Также й по крайней мере, для неявных/явных class эсов в этом ПРИМЕР- есть метод c доступен так без потери class атрибута не происходит:

ans = .mapply(c, d, NULL) 

и конвертировать в «data.frame» с соответствующими «именами» :

ans = structure(ans, 
       class = "data.frame", 
       row.names = .set_row_names(length(ans[[1]])), 
       names = names(d[[1]])) 
str(ans) 
#'data.frame': 6 obs. of 5 variables: 
# $ time  : POSIXct, format: "2016-01-15 09:10:00" "2016-03-02 01:34:00" "2016-02-28 03:04:00" "2016-01-05 02:22:00" ... 
# $ latitude : num 34 35.9 33.8 33.7 33.8 ... 
# $ longitude: num -84.8 -78.7 -84.7 -86.5 -84.1 ... 
# $ location : chr NA "27612" "30127" "35173" ... 
# $ day  : chr "FRIDAY" "TUESDAY" "SATURDAY" "MONDAY" ...