我有一个包含多张工作表的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中有没有办法做到这一点?
答案 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)
将apply
与paste
标题行一起使用,以形成结果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”包中的melt
和colsplit
:
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 包。 聚集功能可以帮助您。