2017-01-19 1 views
0

Je travaille avec un ensemble de données longitudinales avec observations répétées pour les sujets dans un format long data.table. La plupart des sujets ont quelques observations répétées (< 10), tandis que quelques sujets ont de nombreuses observations (> 100). Je peux convertir cet ensemble de données de temps à large comme ci-dessous, mais il devient extrêmement large (j'ai beaucoup de variables à chaque point de temps) et est surtout pleine de NAs, étant donné que la plupart des sujets ne disposent pas de données pour les variables parfois 11 à 100 Existe-t-il un moyen plus élégant de refondre ces données au format large? Je pense à quelque chose comme un tableau irrégulier dans d'autres langues ...compression colonnes similaires lors de la conversion d'un data.table de temps à large

Certaines solutions existent here, mais une grande préoccupation pour moi est la taille de l'objet: la matrice large avec beaucoup de NA prend beaucoup de inutile espace.

A MWF avec ma solution actuelle (matrice creuse de façon indésirable) est ci-dessous. Idéalement, si une sorte d'approche de liste irrégulière est réalisable, l'objet résultant aurait 3 lignes et 3 colonnes, où les colonnes "année" et "code" sont des listes ou similaires. En prime, ce serait merveilleux si je pouvais imbriquer la variable "code" dans la variable "year" sous la forme de tableaux ragged imbriqués.

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

Pourriez-vous donner aussi un objet qui ressemble à votre sortie désirée? Je devine que c'est la différence wideDat – Frank

+0

@Frank C'est en fait une partie de ma question: je n'ai récemment pris à l'aide data.table, et ont à plusieurs reprises trouvé de belles surprises sur la définition flexible des objets de données. En espérant que quelqu'un pourrait m'éclairer de la même manière ici. Dans un pseudo-objet: colonne 'id = c (1,2,3)'; la colonne 'year' est une liste de longueur 3, chaque item ayant seulement autant d'entrées qu'il y a d'observations longitudinales (ne stockant pas des centaines d'AN); colonne 'code' est une liste de longueur 3 définie de manière analogue à' year'. Pas nécessairement coincé sur des listes (et pas sûr si possible dans le contexte de data.table), c'est juste ce qui vient à l'esprit. –

+1

Ok. Vous pouvez faire 'dat [, lapply (.SD, list), by = id]', mais ce n'est pas un format très utile pour l'analyse ou l'impression ou tout autre chose que je peux penser. – Frank

Répondre

2

Quelques idées

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

Très utile, et merci d'avoir atterri sur une idée clé que j'ai oublié de mentionner dans ma question originale: la taille de l'objet. Mes données réelles sont beaucoup plus grandes que le MWE, donc j'utilise beaucoup d'espace inutile avec toutes les NA dans wideDat. Votre wideDat4 m'a obtenu où je voulais aller, avec la modification 'wideDat5 <- dat %>% group_by (id)%>% organiser (id, année, code)%>% résumer (années = liste (année), codes = liste (code)) 'livrer l'objet" tibble "(taille 4960 octets) qui est exactement ce que je cherchais. –

+2

@ T.Gerke 'Fyi wideDat6 = setnames (dat [ordre (id, année, code), lapply (.SD, liste), par = id], c ("id", "années", "codes")) 'est un autre moyen d'y arriver (en utilisant data.table). 'All.equal (data.frame (wideDat5), data.frame (wideDat6)) # TRUE' avec la taille de l'objet de' 4904 bytes' – Frank

+1

@Frank C'est _exactement_ ce que je cherchais: imprime un peu plus proprement que wideDat5, fonctionne dans les limites de data.table (pas que je suis terriblement opposé à l'invocation de dplyr, mais rend les choses plus propres), et est légèrement plus petit que wideDat5 –