将 data.table 从长转换为宽时压缩相似的列

compressing similar columns when converting a data.table from long to wide

我正在处理一个纵向数据集,其中包含对长格式受试者的重复观察 data.table。大多数受试者有一些(<10)次重复观察,而少数受试者有很多(>100)次观察。我可以将这个数据集从长转换为宽,如下所示,但它变得非常宽(我在每个时间点都有很多变量)并且大部分都是 NA,因为大多数受试者在时间 11 到 100 没有变量数据. 有没有更优雅的方法将这些数据重铸为宽格式?我在想其他语言中参差不齐的数组的一些东西......

存在一些解决方案here,但我最关心的是对象大小:具有大量 NA 的宽矩阵占用了很多不必要的 space。

下面是一个 MWE 和我当前的(不理想的稀疏矩阵)解决方案。理想情况下,如果某种参差不齐的列表方法可行,生成的对象将有 3 行和 3 列,其中 "year" 和 "code" 列是列表或类似的。作为奖励,如果我可以将 "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")

一些想法

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