R转换为长格式,模式

时间:2016-09-22 12:45:59

标签: r data.table

我想将像这样的data.table从宽格式转换为长格式。

set.seed(1)
DT <- data.table(   
  ID = c(1:5, NA),
  Name = c("Bob","Ana","Smith","Sam","Big","Lulu"),
  Kind_2001 = factor(sample(c(letters[1:3], NA), 6, TRUE)),
  Kind_2002 = factor(sample(c(letters[1:3], NA), 6, TRUE)),
  Kind_2003 = factor(sample(c(letters[1:3], NA), 6, TRUE)),
  Conc_2001 = sample(99,6),
  Conc_2002 = sample(79,6),
  Conc_2003 = sample(49,6)
  )


ID  Name Kind_2001 Kind_2002 Kind_2003 Conc_2001 Conc_2002 Conc_2003
1   Bob         b        NA         c        38        22        24
2   Ana         b         c         b        77        31        29
3 Smith         c         c        NA        91         2        49
4   Sam        NA         a         b        21        30         9
5   Big         a         a         c        62        66        38
NA  Lulu       NA         a        NA        12        26        30

我想得到这样的东西:

ID   Name   Year    Kind   Conc    
1    Bob    2001    b      38
1    Bob    2002    NA     22
1    Bob    2003    c      24
2    Ana    2001    b      77
2    Ana    2002    c      31
2    Ana    2003    b      29
...

真实表有更多变量,我在没有明确说出每个列名或编号的情况下寻找解决方案,自动检测模式。
我有两种列,一些以下划线和一个四位数年份结束,例如_2001,另一列没有结束。
有些可以在名称的中间有一个下划线(这将保持不变形) 我想将以一年结尾的列转换为长格式。

我已经尝试了

melt(DT, id=1:2, variable.name = "year")
or with 
melt(DT, id=1:2, measure=patterns("_2[0-9][0-9][0-9]$"))

但我没有得到我想要的东西。

也许我首先需要使用gsub过滤名称。

PD:我找到了这个解决方案。

posi <- grep("_[0-9][0-9][0-9][0-9]$",colnames(DT))
work <- unique(gsub("_[0-9][0-9][0-9][0-9]$","",colnames(DT)[posi]))
melt(DT, measure=patterns(paste0("^",work)), variable="year", value.name=work)

它几乎可以正常工作,但年份列未正确填充。我错过了什么,或者它是一个错误。 而且我确信它可以写得更简单。

ID  Name year Kind Conc
 1   Bob    1    b   38
 2   Ana    1    b   77
 3 Smith    1    c   91
 4   Sam    1   NA   21
 5   Big    1    a   62
NA  Lulu    1   NA   12
 1   Bob    2   NA   22
 2   Ana    2    c   31
 3 Smith    2    c    2
 4   Sam    2    a   30
 5   Big    2    a   66
NA  Lulu    2    a   26
 1   Bob    3    c   24
 2   Ana    3    b   29
 3 Smith    3   NA   49
 4   Sam    3    b    9
 5   Big    3    c   38
NA  Lulu    3   NA   30

此致

我已经尝试使用我的数据库进行eddi解决方案,我收到错误:

&#34;错误:无法分配大小为756.5 Mb&#34; 即使我有16GB的内存。

4 个答案:

答案 0 :(得分:3)

我们可以使用reshape() R中的base按比例解决此问题,而无需明确命名变量。

# First we get indices of colnames that have format "_1234" at the end 
tomelt <- grep("_([0-9]{4})$",names(DT))

# Now we use these indices to reshape data
reshape(DT, varying = tomelt, sep = "_", 
        direction = 'long', idvar = "ID", timevar = "Year)
#    ID  Name Year Kind Conc
# 1:  1   Bob 2001    b   38
# 2:  2   Ana 2001    b   77
# 3:  3 Smith 2001    c   91
# 4:  4   Sam 2001   NA   21
# 5:  5   Big 2001    a   62
# 6: NA  Lulu 2001   NA   12
...

答案 1 :(得分:2)

如果我们正在寻找names解决方案,请从“DT”的unique中提取前缀部分,并在{{patterns中使用measure元素在melt中的1}}参数。同样,提取“Year”的后缀并用该数字索引替换。

nm <- unique(sub("_\\d+", "", names(DT)[-(1:2)]))
yr <- unique(sub("\\D+_", "", names(DT)[-(1:2)]))
melt(DT, measure = patterns(paste0("^", nm)), value.name = nm, 
                   variable.name = "Year")[, Year := yr[Year]][]
#    ID  Name Year Kind Conc
# 1:  1   Bob 2001    b   38
# 2:  2   Ana 2001    b   77
# 3:  3 Smith 2001    c   91
# 4:  4   Sam 2001   NA   21
# 5:  5   Big 2001    a   62
# 6: NA  Lulu 2001   NA   12
# 7:  1   Bob 2002   NA   22
# 8:  2   Ana 2002    c   31
# 9:  3 Smith 2002    c    2
#10:  4   Sam 2002    a   30
#11:  5   Big 2002    a   66
#12: NA  Lulu 2002    a   26
#13:  1   Bob 2003    c   24
#14:  2   Ana 2003    b   29
#15:  3 Smith 2003   NA   49
#16:  4   Sam 2003    b    9
#17:  5   Big 2003    c   38
#18: NA  Lulu 2003   NA   30

答案 2 :(得分:1)

这里有一个选项,对于您的列顺序以及缺失/额外年份而言更加强大:

dcast(melt(DT, id.vars = c("ID", "Name"))
        [, .(ID, Name, sub('_.*', '', variable), sub('.*_', '', variable), value)],
      ID + Name + V4 ~ V3)
#    ID  Name   V4 Conc Kind
# 1:  1   Bob 2001   38    b
# 2:  1   Bob 2002   22   NA
# 3:  1   Bob 2003   24    c
# 4:  2   Ana 2001   77    b
# 5:  2   Ana 2002   31    c
# 6:  2   Ana 2003   29    b
# 7:  3 Smith 2001   91    c
# 8:  3 Smith 2002    2    c
# 9:  3 Smith 2003   49   NA
#10:  4   Sam 2001   21   NA
#11:  4   Sam 2002   30    a
#12:  4   Sam 2003    9    b
#13:  5   Big 2001   62    a
#14:  5   Big 2002   66    a
#15:  5   Big 2003   38    c
#16: NA  Lulu 2001   12   NA
#17: NA  Lulu 2002   26    a
#18: NA  Lulu 2003   30   NA

编辑许多id列:

idvars = grep("_", names(DT), invert = TRUE)
dcast(melt(DT, id.vars = idvars)
        [, `:=`(var      = sub('_.*', '', variable),
                year     = sub('.*_', '', variable),
                variable = NULL)],
      ... ~ var, value.var='value')

答案 3 :(得分:0)

如果有人感兴趣我在这里发布我的完整解决方案, 能够处理大于内存的数据集。它使用了你的一些想法和一些我的想法。 我的数据是文件file.csv(或者您甚至可以使用fread(“unzip -c name.zip”)使用压缩文件。

## Initialization
nline <- 1500000  # total number of lines  or use wc -l to do it automatically.
chunk <- 5000    # change it according to your memory and number of columns.
times <- ceiling(nline/chunk)
name <- names(fread("file.csv", stringsAsFactors=F, integer64 = "character", nrows=0, na.strings=c("", "NA")) )
idvars =  grep("_20[0-9][0-9]$",name , invert = TRUE) 

# Now we loop every chunk

for(iter in 0:(times-1)) {
  my <- fread("file.csv", stringsAsFactors=F, integer64 = "character", skip=1+(iter*chunk), nrows=chunk, na.strings=c("", "NA")) 
  colnames(my) <- name
  temp <- melt(my, id.vars = idvars)  
    newfile <- dcast(
    temp[, `:=`(var = sub('_20[0-9][0-9]$', '', variable), year = sub('.*_', '', variable), variable = NULL)],  
    ... ~ var, value.var='value')  
  fwrite(newfile, "long.csv", quote=FALSE, sep=",", append=T)
  rm(temp); rm(newfile); rm(my); gc()
  }

如前所述,此方法的问题是它将所有值转换为字符,但如果将它们保存到文件并再次读取文件(如此处),则会获得正确的类。

如果文件非常大,这种方法非常慢。

我鼓励您改进此解决方案或建议使用tidyr,splitstackshape或其他软件包的任何通用解决方案。

或者更好的方法是使用sqlite等数据库来实现它。

解决方案应该适用于具有无序列的数据集,甚至可以使用名称中间的“_”,例如:

set.seed(1)
DT <- data.table(   
  ID = c(1:15),
  Name = c("Bob","Ana","Smith","Sam","Big","Lulu", "Loli", "Chochi", "Tom", "Dick", "Pet", "Shin", "Rock", "Pep", "XXX"),
  Kind_2001 = factor(sample(c(letters[1:3], NA), 15, TRUE)),
  Kind_2002 = factor(sample(c(letters[1:3], NA), 15, TRUE)),
  Kind_2003 = factor(sample(c(letters[1:3], NA), 15, TRUE)),
  Conc_2004 = sample(49,15),
  aa_Conc_2001 = c(sample(99,14), NA),
  Conc_2002 = sample(79,15)
)