等效于在列名称上拆分的melt + reshape

时间:2019-07-03 15:28:35

标签: r split reshape2 melt

要点:如果您要投票决定关闭,那是一种糟糕的形式,没有给出原因。如果无需关闭就可以改进,请花10秒钟写简短的评论。

问题:
如何以内存可以支持的方式进行以下“部分融化”?

详细信息:
我有几百万行和大约1000列。列的名称中包含2条信息。

通常,我将融合到由一对列组成的数据框(或表)中,然后对变量名进行拆分以创建两个新列,然后使用新拆分中的一个对新列名进行转换,一个用于行名。

这不起作用。我数十亿行的数据使更多的列淹没了我的记忆。

在for循环的“迭代力”(相对于蛮力)之外,是否有一种干净有效的方法?

想法:

  • 这有点像熔化共裂铸造
  • 为此通用的库似乎是“ dplyr”,“ tidyr”,“ reshape2”和“ data.table”。
  • tidyr的collect + separate + spread看起来不错,但不喜欢没有唯一的行标识符
  • reshape2的dcast(我正在寻找2d输出)想要汇总
  • 强力使标签丢失。通过蛮力,我的意思是df <-rbind(df [,block1],...),其中block是前200个列索引,block2是第二个列索引,依此类推。

更新(虚拟代码):

#libraries
library(stringr)

#reproducibility
set.seed(56873504)

#geometry
Ncol <- 2e3
Nrow <- 1e6

#column names
namelist <- numeric(length=Ncol)
for(i in 1:(Ncol/200)){
  col_idx <- 1:200+200*(i-1)
  if(i<26){
  namelist[col_idx] <- paste0(intToUtf8(64+i),str_pad(string=1:200,width=3,pad="0"))
  } else {
    namelist[col_idx] <- paste0(intToUtf8(96+i),str_pad(string=1:200,width=3,pad="0"))
  }
}

#random data
df <- as.data.frame(matrix(runif(n=Nrow*Ncol,min=0, max=16384),nrow=Nrow,ncol=Ncol))
names(df) <- namelist

我要查找的输出将有一列,其中包含当前名称的第一个字符(单个字母字符),而colnames将为1到200。它的宽度比“ df”小得多,但没有完全融化。它也不会杀死我的CPU或内存。

(丑陋/手动)蛮力版本:

(正在处理...)

1 个答案:

答案 0 :(得分:1)

这里有两个都使用data.table的选项。

如果您知道每个列字符串始终都有200个(或n)与之关联的字段(即A001-A200),则可以使用melt()并列出测量变量。 / p>

melt(dt
     , measure.vars = lapply(seq_len(Ncol_p_grp), seq.int, to = Ncol_p_grp * n_grp, by = Ncol_p_grp)
     , value.name = as.character(seq_len(Ncol_p_grp))
)[, variable := rep(namelist_letters, each = Nrow)][]

#this data set used Ncol_p_grp <- 5 to help condense the data. 
        variable         1          2         3          4          5
     1:        A 0.2655087 0.06471249 0.2106027 0.41530902 0.59303088
     2:        A 0.3721239 0.67661240 0.1147864 0.14097138 0.55288322
     3:        A 0.5728534 0.73537169 0.1453641 0.45750426 0.59670404
     4:        A 0.9082078 0.11129967 0.3099322 0.80301300 0.39263068
     5:        A 0.2016819 0.04665462 0.1502421 0.32111280 0.26037592
    ---                                                              
259996:        Z 0.5215874 0.78318812 0.7857528 0.61409610 0.67813484
259997:        Z 0.6841282 0.99271480 0.7106837 0.82174887 0.92676493
259998:        Z 0.1698301 0.70759513 0.5345685 0.09007727 0.77255570
259999:        Z 0.2190295 0.14661878 0.1041779 0.96782695 0.99447460
260000:        Z 0.4364768 0.06679642 0.6148842 0.91976255 0.08949571

或者,我们可以使用rbindlist(lapply(...))浏览数据集并根据列中的字母对其进行子集化。

rbindlist(
  lapply(namelist_letters,
       function(x) setnames(
         dt[, grep(x, names(dt), value = T), with = F]
         , as.character(seq_len(Ncol_p_grp)))
  )
  , idcol = 'ID'
, use.names = F)[, ID := rep(namelist_letters, each = Nrow)][]

此数据集中有7800万个元素,大约需要四分之一秒。我曾尝试将其增加到7.8亿,但实际上我真的没有RAM来快速生成数据。

#78 million elements - 10,000 rows * 26 grps * 200 cols_per_group
Unit: milliseconds
             expr      min       lq     mean   median       uq      max neval
      melt_option 134.0395 135.5959 137.3480 137.1523 139.0022 140.8521     3
 rbindlist_option 290.2455 323.4414 350.1658 356.6373 380.1260 403.6147     3

数据:在执行以上所有操作之前,请执行以下操作:

#packages ----
library(data.table)
library(stringr)

#data info
Nrow <- 10000
Ncol_p_grp <- 200
n_grp <- 26

#generate data
set.seed(1)
dt <- data.table(replicate(Ncol_p_grp * n_grp, runif(n = Nrow)))

names(dt) <- paste0(rep(LETTERS[1:n_grp], each = Ncol_p_grp)
                    , str_pad(rep(seq_len(Ncol_p_grp), n_grp), width = 3, pad = '0'))

#first letter
namelist_letters <- unique(substr(names(dt), 1, 1))