在R中重新格式化Excel工作表

时间:2013-12-04 15:59:52

标签: r excel

我有一个包含多张工作表的Excel文件。每个工作表看起来像这样,底部有一些多余的数据

    A      B     C    D.... 
1   time  USA   USA   USA   
2          MD     CA   PX   
3         pork  peas  nuts 
4   jan-11  4      2      2
5   feb-11  4      9      3
6   mar-11  8      8      3

。 。 workbook1 | workbook2 .....

该文件为11 MB,但是当我尝试使用

sheet<-readWorksheetFromFile("excelfile.xlsx", sheet = 1)

我得到了

Error: OutOfMemoryError (Java): Java heap space

对于每个工作表,数据占用行和列的不同数字,我想写一些为每个工作表生成此数据的内容。

我正在尝试将每列转换为

country state product unit time 
USA     MD     pork    3    jan-11
USA     MD     pork    3    feb-11
USA     MD     pork    3    mar-11
...
..
.

在R中有没有办法做到这一点?

4 个答案:

答案 0 :(得分:2)

如果您的电子表格中包含公式,则可能需要将这些公式转换为值,以便轻松读取它们。否则,我建议使用tool like this one(除此之外)将工作簿中的所有工作表转换为CSV文件并从那里开始工作。

如果你已经走得那么远,可以尝试一下这个问题的“重塑”部分。在这里,我们假设“A”实际上代表一个CSV文件,其内容是您问题中显示为样本数据的六行:

## Create some sample data
A <- tempfile()
writeLines(sep="\n", con = A,
           text = c("time,  USA,   USA,   USA",
                    ",   MD,    CA,    PX",
                    ", pork,  peas,  nuts",
                    "jan-11,    4,     2,     2",
                    "feb-11,    4,     9,     3",
                    "mar-11,    8,     8,     3"))

我要做的第一件事就是分别读入标题和数据。要单独读取标头,请使用nrows指定包含标头信息的行数。要单独读取数据,请指定skip以跳过标题行。

B <- read.csv(A, header = FALSE, skip = 3, strip.white = TRUE)
Bnames <- read.csv(A, header = FALSE, nrows = 3, strip.white = TRUE)

applypaste标题行一起使用,以形成结果data.frame的名称:

names(B) <- apply(Bnames, 2, function(x) paste(x[x != ""], collapse = "_"))
B
#     time USA_MD_pork USA_CA_peas USA_PX_nuts
# 1 jan-11           4           2           2
# 2 feb-11           4           9           3
# 3 mar-11           8           8           3

现在是将数据从“宽”转换为“长”格式的部分。有很多方法可以做到这一点,有些方法也使用基数R,但最直接的方法是使用“reshape2”包中的meltcolsplit

library(reshape2)
BL <- melt(B, id.vars="time")
cbind(BL[c("time", "value")], 
      colsplit(BL$variable, "_", 
               c("country", "state", "product")))
#     time value country state product
# 1 jan-11     4     USA    MD    pork
# 2 feb-11     4     USA    MD    pork
# 3 mar-11     8     USA    MD    pork
# 4 jan-11     2     USA    CA    peas
# 5 feb-11     9     USA    CA    peas
# 6 mar-11     8     USA    CA    peas
# 7 jan-11     2     USA    PX    nuts
# 8 feb-11     3     USA    PX    nuts
# 9 mar-11     3     USA    PX    nuts

答案 1 :(得分:2)

不幸的是,XLConnect不太适用于您的应用程序。我可以确认在具有8GB RAM的系统上运行Win 7 64位和64位R 3.0.2时,XLConnect会因22 MB .xlsx文件而失败,并且会出现相同的错误。正如@Ista所指出的,正如here所解释的,在重做R和之后再做其他事情

options(java.parameters = "-Xmx4096m")
library(XLConnect)
wb    <- loadWorkbook("myWorkBook.xlsx")
sheet <- readWorksheet(wb,"Data")

避免错误。但是,导入仍需要一个多小时(!!)。

相比之下,正如@Gaffi指出的那样,一旦将“数据”表保存到csv文件(~7MB),就可以按如下方式导入:

library(data.table)
system.time(sheet <- fread("Data.csv"))
   user  system elapsed 
   0.84    0.00    0.86 

不到1秒钟。在我的测试用例sheet中有6列和~376,000行。

答案 2 :(得分:0)

对于这个“第二个答案”感到抱歉,但你真的有两个问题...... @Ananda的重塑数据的解决方案非常优雅。这只是考虑它的另一种方式。

如果转置输入矩阵,则得到一个新矩阵,其中第一列是国家,第二列是城市,第三列是“类型”(缺少更好的术语),实际数据是其他列(因此,每个“时间”都有一个额外的列)。

因此,另一种方法是首先转置然后融化新矩阵。这样可以避免创建所有连接的列名并稍后将其拆分。问题是melt.data.frame非常低效,有很多列(你可以在这里)。所以这样做会比@Ananda的方法慢10X

解决方案是使用melt.array(只需使用数组而不是数据框调用melt(...))。如下所示,这种方法的速度提高了约20倍,数据集更大(您的数据集为11MB)。

library(reshape)        # for melt(...)
library(microbenchmark) # for microbenchmark(...)

# this is just to model your situation with more realistic size
# create a large data frame (250 columns of country, city, type; 1000 rows of time)
df <- rep(c("USA","UK","FR","CHN","GER"),each=50)   # time + 250 columns
df <- rbind(df,rep(c(c("NY","SF","CHI","BOS","LA")),each=10))
df <- rbind(df,rep(c("pork","peas","nuts","fruit","other")))
df <- rbind(df,matrix(sample(1:1000,250*1000,replace=T),ncol=250))
df <- cbind(c("time","","",
               as.character(as.Date(1:1000,origin="2010-01-01"))),df)
df <- data.frame(df)    # big warning here about duplicated row names; not important

# @Ananda'a approach:
transform.orig <- function(df){
  B        <- df[-(1:3),]
  Bnames   <- df[1:3,]
  names(B) <- apply(Bnames, 2, function(x) paste(x[x != ""], collapse = "_"))
  BL       <- melt(B, id.vars="time")
  final    <- cbind(BL[c("time", "value")], 
                         colsplit(BL$variable, "_", 
                                  c("country", "state", "product")))
  return(final)
}

# transpose approach:
transform.new <- function(df) {
  zz           <- t(df)
  times        <- t(zz[1,4:ncol(zz)])
  colnames(zz) <- c("country","city","type", times)
  data  <- melt(zz[-1,-(1:3)],varnames=c("id","time"))
  final <- cbind(country=rep(zz[-1,1],each=ncol(zz)-3),
                 city   =rep(zz[-1,2],each=ncol(zz)-3),
                 type   =rep(zz[-1,3],each=ncol(zz)-3),
                 data[,-1])
  return(final)
}

# benchmark
microbenchmark(transform.orig(df),transform.new(df), times=5, unit="s")
Unit: seconds
               expr       min        lq   median         uq        max neval
 transform.orig(df) 9.2511679 9.6986330 9.889457 10.1518191 10.3354328     5
  transform.new(df) 0.4383197 0.4724145 0.474212  0.5815531  0.6886383     5

答案 3 :(得分:0)

要从excel读取数据,请尝试 openxlsx 包。它使用c ++而不是java,更好地处理更大的excel文件。

要重塑您的数据,请查看 tidyr 包。 聚集功能可以帮助您。