2017-01-19 3 views
0

Я работаю с продольным набором данных с повторными наблюдениями для объектов в формате long.table. У большинства испытуемых есть несколько (< 10) повторных наблюдений, в то время как у нескольких испытуемых есть много (> 100) наблюдений. Я могу преобразовать этот набор данных от длинного к широкому, как показано ниже, но он становится чрезвычайно широким (у меня много переменных в каждый момент времени) и в основном заполнено НС, так как большинство субъектов не имеют данных для переменных в разы от 11 до 100 Есть ли более элегантный способ перевести эти данные в широкий формат? Я думаю о чем-то в строю оборванного массива на других языках ...Сжатие подобных столбцов при преобразовании данных.table от long to wide

Некоторые решения существуют here, но для меня большая проблема - размер объекта: широкая матрица с большим количеством НС занимает много ненужных пространство.

MWE с моим текущим (нежелательно разреженной матрицей) решением ниже. В идеале, если какой-либо подход с оборванным списком возможен, результирующий объект будет иметь 3 строки и 3 столбца, где столбцы «год» и «код» являются списками или похожими. В качестве бонуса было бы замечательно, если бы я мог вложить переменную «code» внутри переменной «year» в виде вложенных оборванных массивов.

library(data.table) 

dat <- data.table(id=c(rep(1,5), rep(2,10), rep(3,85)), 
    year=sample(2013:2016, 100, replace=TRUE), 
    code=sample(LETTERS, 100, replace=TRUE)) 

wideDat <- dcast(dat, id~paste0("code", dat[,seq_len(.N), by=id]$V1), 
    value.var="code") 
+1

Не могли бы Вы также предоставить объект, который выглядит как ваш желаемый результат? Я предполагаю, что это не похоже на wideDat – Frank

+0

@Frank Это на самом деле часть моего вопроса: я только недавно занялся использованием data.table и много раз встречал замечательные сюрпризы относительно гибкого определения объектов данных. Надеясь, что кто-то тоже может просветить меня здесь. В псевдообъекте: столбец 'id = c (1,2,3)'; столбец 'year' - это список длины 3, каждый элемент имеет только столько записей, сколько есть продольных наблюдений (не сохраняя сотни НС); column 'code' - это список длин 3, определенный аналогично' year'. Не обязательно застревать в списках (и не уверен, если это возможно в контексте data.table), это только то, что приходит на ум. –

+1

Хорошо. Вы можете сделать 'dat [, lapply (.SD, list), by = id]', но это не очень полезный формат для анализа или печати или что-то еще, о чем я могу думать. – Frank

ответ

2

Несколько идей

object.size(wideDat) 
# 22432 bytes 

# the following structures leverages the fact that years are missing 
wideDat2 <- dcast(dat, id+year~code) 
# id year A B C D E F G I J K L M N O P Q R S T U V W X Y Z 
#1: 1 2014 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 
#2: 1 2015 0 0 1 0 0 0 0 0 1 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
#3: 2 2013 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
#4: 2 2014 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 
#5: 2 2015 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
#6: 2 2016 0 1 0 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 
#7: 3 2013 1 0 0 1 0 1 1 2 2 0 1 1 2 1 1 2 3 0 1 0 0 2 0 0 1 
#8: 3 2014 1 2 0 0 2 1 0 3 0 0 3 0 0 0 3 1 0 2 1 1 0 2 0 0 2 
#9: 3 2015 0 2 1 0 0 0 0 2 2 1 1 0 0 0 1 0 3 1 2 1 2 1 1 0 0 
#10: 3 2016 1 0 0 2 0 1 0 0 0 1 0 2 1 2 1 1 1 0 1 0 1 1 0 1 0 

object.size(wideDat2) 
# 6872 bytes 

## the following struture just compresses the codes as strings 
library(dplyr) 
wideDat3 <- dat %>% 
    group_by(id, year) %>% 
    arrange(id, year, code) %>% 
    summarize(codes = paste0(code, collapse=",")) 
#  id year           codes 
<# dbl> <int>           <chr> 
#1  1 2014            P 
#2  1 2015           C,J,L,L 
#3  2 2013            B 
#4  2 2014            S,W 
#5  2 2015            A,A 
#6  2 2016          B,G,K,O,S 
#7  3 2013 A,D,F,G,I,I,J,J,L,M,N,N,O,P,Q,Q,R,R,R,T,W,W,Z 
#8  3 2014 A,B,B,E,E,F,I,I,I,L,L,L,P,P,P,Q,S,S,T,U,W,W,Z,Z 
#9  3 2015  B,B,C,I,I,J,J,K,L,P,R,R,R,S,T,T,U,V,V,W,X 
#10  3 2016    A,D,D,F,K,M,M,N,O,O,P,Q,R,T,V,W,Y 
object.size(wideDat3) 
# 2856 bytes 

## .. or as nested list 
wideDat4 <- dat %>% 
    group_by(id, year) %>% 
    arrange(id, year, code) %>% 
    summarize(codes = list(code)) 
# id year  codes 
#<dbl> <int>  <list> 
# 1  1 2014 <chr [1]> 
# 2  1 2015 <chr [4]> 
# 3  2 2013 <chr [1]> 
# 4  2 2014 <chr [2]> 
# 5  2 2015 <chr [2]> 
# 6  2 2016 <chr [5]> 
# 7  3 2013 <chr [23]> 
# 8  3 2014 <chr [24]> 
# 9  3 2015 <chr [21]> 
# 10  3 2016 <chr [17]> 

object.size(widedat4) 
# 6776 bytes 
+0

Так полезно, и спасибо за посадку на ключевую идею, которую я забыл упомянуть в моем первоначальном вопросе: размер объекта. Мои фактические данные намного больше, чем MWE, поэтому я использую много ненужного пространства со всеми NA в wideDat. Ваш wideDat4 получил меня туда, куда я хотел пойти, с модификацией 'wideDat5 <- dat %>% group_by (id)%>% аранжировка (id, year, code)%>% summary (years = list (year), codes = list (код)) 'доставки объекта« tibble »(размер 4960 байт), который именно то, что я искал. –

+2

@ T.Gerke Fyi 'wideDat6 = setnames (dat [order (id, year, code), lapply (.SD, list), by = id], c (" id "," years "," codes ")) '- это еще один способ добраться туда (используя data.table). 'all.equal (data.frame (wideDat5), data.frame (wideDat6)) # TRUE' с размером объекта' 4904 bytes' – Frank

+1

@Frank Это _exactly_, что я искал: печатает немного более чисто, чем wideDat5, работает в рамках data.table (не то, что я ужасно против вызова dplyr, но делает вещи чище), и немного меньше по размеру, чем wideDat5 –