2013-12-04 3 views
1

У меня есть файл Excel, который имеет несколько листов. каждый лист выглядит следующим образом с некоторыми избыточными данными внизуПереформатирование листа excel в R

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 мб, но когда я пытаюсь использовать

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

Так вы спрашиваете о чтении данных или изменении данных после того, как вы это сделали? – A5C1D2H2I1M1N2O1R2T1

+0

Я думаю, что оба они, я все еще пытаюсь получить R, чтобы прочитать книгу, не сказав, что ее нехватка памяти, также спасибо за редактирование сообщения, это мой первый вопрос, задающий вопрос. – BaconDoggie

+0

вы пробовали 'read.xls' в пакете' gdata'? Я думаю, что 11 МБ-файл не должен быть проблемой. – sparrow

ответ

2

Если ваша таблица заполнена формулами, вам может потребоваться преобразовать их в значения, чтобы их легко читать. В противном случае я бы предложил использовать tool like this one (среди прочих там), чтобы преобразовать все листы в книгу в файлы CSV и работать оттуда.

Если вы дошли до этого, вот что можно попробовать для «перестройки» части вашего вопроса. Здесь мы будем считать, что «А» на самом деле представляет собой файл 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 тоже, но самый прямой является использование melt и colsplit из пакета «reshape2»:

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 
+0

Спасибо, это очень помогло! – BaconDoggie

+0

Быстрый вопрос, как бы это изменение, если некоторые значения, где отсутствуют, как и некоторые государственные ценности были пусты я получаю эту ошибку ошибки в подстроках (строка, начало, конец): недействительной многобайтовая строка в «<99> тэ D» <49> voire_Abidjan_Adjame_Rice_Retail_kg_XOF» – BaconDoggie

2

К сожалению, XLConnect вряд ли будет работать в вашем приложении. Я могу подтвердить, что в системе с 8 ГБ оперативной памяти, работающей под управлением Win 7 64bit и 64bit R 3.0.2, XLConnect не работает с файлом 22MB .xlsx с той же ошибкой, что и вы. Как @Ista отметил, и, как описано here, после перезагрузки R и , прежде чем делать что-нибудь еще:

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

позволяет избежать ошибок. Однако импорт еще занимает больше часа (!!).

В противоположность этому, как @Gaffi указывал, как только лист "Data" сохраняется в 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 строк.

+0

I CSV и 'fread' все! Это довольно круто. – Gaffi

0

Извините об этом «втором ответе», но у вас действительно было два вопроса ... @ Решение Ананды по перестройке ваших данных чрезвычайно элегантно. Это еще один способ подумать об этом.

Если вы транспонируете входную матрицу, вы получаете новую матрицу, где первый столбец является страной, второй столбец является городом, третий столбец «тип» (из-за отсутствия лучшего термина), а фактические данные в других столбцах (так что для каждого «времени» имеется еще один столбец).

Таким образом, другой подход состоит в том, чтобы перенести сначала, а затем расплавить новую матрицу. Это позволяет избежать создания всех имен конкатенированных столбцов и их последующего разделения. Проблема в том, что melt.data.frame исключительно неэффективен с очень большим количеством столбцов (которые вы бы здесь). Таким образом, так будет bbe 10X медленнее, чем подход Ананды.

Решение состоит в использовании melt.array (просто позвоните melt(...) с массивом, а не с кадром данных). Как показано ниже, этот подход ~ 20X быстрее, с более крупными наборами данных (ваш был 11 МБ).

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 
0

Для чтения данных из Excel, попробуйте пакет openxlsx. Он использует C++ вместо java и лучше обрабатывает большие файлы excel.

Чтобы изменить ваши данные, посмотрите на tidyr. Команда может помочь вам.

Смежные вопросы