将一个大的宽格式数据(1982 列)融化成一个长格式,然后在 R 中有效地将其转换回宽格式

Melt a big wide-form data (1982 columns) into a long form and then cast it back to wide form in R efficiently

我有一个格式如下所示的数据集,df

# the original data format
df <- data.frame(id = 1:2,
       var1.20130101 = 1:2,
       var1.20130102 = 6:7,
       var2.20130101 = c(NA,1),
       var2.20130102 = NA)
df

# id var1.20130101 var1.20130102 var2.20130101 var2.20130102
# 1             1             6            NA            NA
# 2             2             7             1            NA

我想要的最终输出是:

df.out <- data.frame(id = c(1, 1, 2, 2),
                     date = c(20130101, 20130102),
                     var1 = c(1, 6, 2, 7),
                     var2 = c(NA, NA, 1, NA))
df.out
# id     date var1 var2
# 1 20130101    1   NA
# 1 20130102    6   NA
# 2 20130101    2    1
# 2 20130102    7   NA

希望这能解释我想要执行的实际操作。


实际上,我必须对 24 个 CSV 文件执行此任务。每个文件中大约有 1982 列(和几千行)。

  1. id 列,
  2. id列和
  3. 对应的列名
  4. 22 个变量加上三个月的日期(=> 22 * 90 = 1980 列)。

这是我的工作流程:

  1. 读取数据(使用read.table,不知为何fread()无法读取原始文件)并将每个文件拆分为3个子文件,每个大约450MB。
  2. 对于每个子文件,阅读fread()melt()
  3. 然后从列名称中提取日期和名称信息。
  4. dcast() 来自 id ~ date + name
  5. 将文件写回 CSV 文件。

这是我正在使用的脚本。我正在寻找更高效和自动化(不拆分文件)的解决方案。

setwd("C:/Users/Administrator/Desktop/chencheng/rawdata")
  source("code1.2.R")
  file.name <- list.files()
  n <- length(file.name)
  for (i in 1:n){
    fileDispart(file.name[i])
    gc(TRUE)
  }
  setwd("E:/chencheng/step2file")
  file.name2 <- list.files()
  n <- length(file.name2)
  for (i in 1:n){
    dataTran(file.name2[i])
    gc(TRUE)
  }

code1.2.R:

library(reshape2)
library(stringr)
library(data.table)
library(dplyr)

fileDispart <- function(file){
  dat <- read.csv(file)
  n <- dim(dat)[1]
  unit <- 120000
  m <- ceiling(n / unit)
  start <- 1
  file <- gsub('.csv', '', file)
  ####################################
  for(i in 1:m) {
    if(i < m) {
      end <- i * unit
      start <- end + 1
      write.csv(dat[start:end, ], paste0(file, i, '.csv'), 
                  quote = F, row.names = F, na = "")
    } else {
      write.csv(dat[start:n, ], paste0("E:/chencheng/step2file/", 
         file, "_", i, '.csv'), quote = F, row.names = F, na = "")
    }
  }
}


dataTran <- function(pathin = "") {
  pathout = "E:/chencheng/"
  # dat <- fread("e:/chencheng/step2file/2013Q1p2_3.csv")
  if(length(pathin) == 0 | length(pathout) == 0) stop("Wrong parameters!")
  dat <- fread(pathin)
  n <- dim(dat)[2]
  dat <- dat %>% select(-2) # remove the extra column, 
                            # just chinese names of the id
  dat.m <- melt(dat, id = 1)
  rm(dat)
  gc()
  name <- as.character(dat.m$variable)
  name.len <-  nchar(name)
  dat.m$name <- str_sub(name, 1, name.len - 8)
  dat.m$date <- str_sub(name, name.len - 7, name.len)
  names(dat.m)[1] <- 'id'
  dat.m <- dat.m %>% select(-2) 
  dat.m <- dcast.data.table(dat.m, id + date ~ name, identity)
  # write the file
  filename <- gsub('.csv', '', pathin)
  write.csv(dat.m, paste0(pathout, filename, '.csv'), 
            quote = F, row.names = F, na = "")
}

此外,我使用的服务器是 64G RAM (windows server 2008)。 R 和 SQL 服务器是我完成此任务的仅有的两个选项;也许一点 python 就可以了。

没有数据样本很难解决这样的问题,但应该可以进行一些明显的优化。
不确定它是否加速很多但你可以尝试不存储中间数据并使用 data.table 链接

dataTran <- function(pathin = ""){
    # ...
    dat.m <- fread(pathin)[,-2L,with=FALSE
                           ][,melt(.SD, id=1:2, measure=3)]
    # ...
    # likely the following are redundant: rm(dat); gc()
    # ...
    # as.character, nchar, str_sub - likely those can be also speedup
    # ...
    filename <- gsub('.csv', '', pathin)
    dat.m[,-2L,with=FALSE
          ][,dcast.data.table(.SD, id + date ~ name, identity)
            ][,write.csv(.SD, paste0(pathout, filename, '.csv'), quote = F, row.names = F, na = "")]
}