2015-07-26 14 views
15

Importo tabelle xlsx 2007 in R 3.2.1patched utilizzando il pacchetto readxl 0.1.0 in Windows 7 64. La dimensione delle tabelle è dell'ordine di 25.000 righe per 200 colonne.Specificazione dei tipi di colonna durante l'importazione di dati xlsx in R con pacchetto readxl

Funzione read_excel() funziona a meraviglia. Il mio unico problema è con l'assegnazione della classe di colonna (tipo di dati) alle colonne scarsamente popolate. Ad esempio, una data colonna può essere NA per 20.000 righe e quindi prenderà un valore di carattere sulla riga 20.001. read_excel() appare come predefinito per il tipo di colonna numerico quando si esegue la scansione delle prime n righe di una colonna e si trova solo NAs. I dati che causano il problema sono caratteri in una colonna assegnata numerica. Quando viene raggiunto il limite di errore, l'esecuzione si ferma. In realtà voglio i dati nelle colonne sparse, quindi impostare il limite di errore più alto non è una soluzione.

Posso identificare le colonne problematiche rivedendo gli avvisi lanciati. E read_excel() ha un'opzione per affermare tipo di dati di una colonna impostando argomento col_types Secondo la documentazione del pacchetto:

O NULL a intuire dal foglio di calcolo o un vettore di caratteri contenente blank, numeric, date o text.

Ma questo significa che devo costruire un vettore di lunghezza 200 popolata in quasi ogni posizione con blank e text in poche posizioni corrispondenti alle colonne offendere?

Probabilmente c'è un modo per farlo in un paio di linee del codice R. Crea un vettore della lunghezza richiesta e riempilo con blank s. Forse un altro vettore contenente i numeri delle colonne deve essere forzato a text, e poi ... O forse è possibile chiamare per read_excel() solo le colonne per le quali le sue ipotesi non sono quelle desiderate.

Apprezzerei qualsiasi suggerimento.

Grazie in anticipo.

risposta

2

Leggendo la fonte, sembra che tipi di colonna sono indovinati dalle funzioni xls_col_types o xlsx_col_types, che sono attuate in Rcpp, ma hanno i valori di default:

xls_col_types <- function(path, na, sheet = 0L, nskip = 0L, n = 100L, has_col_names = FALSE) { 
    .Call('readxl_xls_col_types', PACKAGE = 'readxl', path, na, sheet, nskip, n, has_col_names) 
} 

xlsx_col_types <- function(path, sheet = 0L, na = "", nskip = 0L, n = 100L) { 
    .Call('readxl_xlsx_col_types', PACKAGE = 'readxl', path, sheet, na, nskip, n) 
} 

mio C++ è molto arrugginito, ma sembra il comando n=100L indica il numero di righe da leggere.

come queste sono funzioni non esportate, incollare:

fixInNamespace("xls_col_types", "readxl") 
fixInNamespace("xlsx_col_types", "readxl") 

E nel pop-up, cambiare il n = 100L a un numero maggiore. Quindi rieseguire l'importazione dei file.

+0

In alcuni casi è utile poter controllare il numero di righe scansionate per determinare il tipo di dati. In questo problema, tuttavia, potrebbe essere necessario eseguire la scansione di cols molto scarsamente popolati. Penso che il problema principale sia un modo conveniente per specificare il tipo di dati desiderato di alcune colonne in cui il comportamento predefinito non è voluto. Nella sua risposta a http://stackoverflow.com/q/6099243 Mikko sembra suggerire un modo per farlo per read.xlsx2 nel pacchetto xlsx. Forse lavori simili per readxl. Grazie mille per aver controllato l'origine di readxl_xlsx_col_types. – jackw19

4

Dipende se i dati sono sparsi in punti diversi in colonne diverse e quanto sparsi. Ho scoperto che avere più righe non migliorava l'analisi: la maggior parte erano ancora vuote e interpretate come testo, anche se successivamente diventano date, ecc.

Una soluzione consiste nel generare la prima riga di dati della tabella excel per includere dati rappresentativi per ogni colonna e utilizzarli per indovinare i tipi di colonna. Non mi piace perché voglio lasciare intatti i dati originali.

Un'altra soluzione, se si dispone di righe complete da qualche parte nel foglio di calcolo, è utilizzare nskip anziché n. Questo dà il punto di partenza per l'ipotesi della colonna. Dire riga di dati 117 dispone di una serie completa di dati:

readxl:::xlsx_col_types(path = "a.xlsx", nskip = 116, n = 1) 

Nota che si può chiamare la funzione direttamente, senza dover modificare la funzione nello spazio dei nomi.

È quindi possibile utilizzare il vettore di tipi di fogli di calcolo di chiamare read_excel:

col_types <- readxl:::xlsx_col_types(path = "a.xlsx", nskip = 116, n = 1) 
dat <- readxl::read_excel(path = "a.xlsx", col_types = col_types) 

Quindi è possibile aggiornare manualmente le colonne che si ottiene ancora sbagliata.

+0

Potresti per favore approfondire le notazioni che usi? Mi piacerebbe fare qualcosa di simile con il pacchetto xlsx, la funzione read.xlsx e l'argomento colClasses. Grazie! –

5

Ho riscontrato un problema simile.

Nel mio caso le righe e le colonne vuote sono state utilizzate come separatori. E c'erano molti tavoli (con diversi formati) contenuti nei fogli. Quindi, i pacchetti {openxlsx} e {readxl} non si adattano a questa situazione, perché openxlsx rimuove le colonne vuote (e non c'è parametro per modificare questo comportamento). Il pacchetto Readxl funziona come hai descritto e alcuni dati potrebbero andare persi.

Nel risultato, penso che la soluzione migliore, se si desidera gestire automaticamente grandi quantità di dati excel, è di leggere i fogli senza modifiche nel formato 'testo' e quindi elaborare data.frames in base alle proprie regole .

Questa funzione può leggere fogli senza modifiche (grazie a @ jack-Wasey):

loadExcelSheet<-function(excel.file, sheet) 
{ 
    require("readxl") 
    sheets <- readxl::excel_sheets(excel.file) 
    sheet.num <- match(sheet, sheets) - 1 
    num.columns <- length(readxl:::xlsx_col_types(excel.file, sheet = sheet.num, 
               nskip = 0, n = 1)) 

    return.sheet <- readxl::read_excel(excel.file, sheet = sheet, 
           col_types = rep("text", num.columns), 
           col_names = F) 
    return.sheet 
} 
+0

con columnNames loadExcelSheet <-funzione (excel.file, foglio) { richiedono ("readxl") fogli <- readxl :: excel_sheets (excel.file) sheet.num <- Partita (foglio, fogli) - 1 num.columns <- length (readxl ::: xlsx_col_types (excel.file, sheet = sheet.num, nskip = 0, n = 1)) colNames <- readxl ::: xlsx_nome_col (excel.file, foglio = sheet.num) return.sheet <- readxl :: read_excel (excel.file, sheet = sheet, col_types = rep ("text", num.columns), col_names = colNames, skip = 1) return.sheet } – adam

+0

Questa soluzione non funzionerà più nella versione 1.x di 'readxl' poiché la funzione interna' readxl ::: xlsx_ col_types' è stato rimosso. Per risolvere il problema vedi questa risposta: https://stackoverflow.com/a/46122161/4468078 –

1

I funcitons interne per indovinare tipi di colonna può essere impostato a qualsiasi numero di righe da esaminare. Ma lo read_excel() non lo implementa (ancora?).

La soluzione di seguito è solo una riscrittura della funzione originale read_excel() con argomento n_max che si imposta automaticamente su tutte le righe. A causa della mancanza di immaginazione, questa funzione estesa è denominata read_excel2.

Basta sostituire read_excel con read_excel2 per valutare i tipi di colonna in tutte le righe.

# Inspiration: https://github.com/hadley/readxl/blob/master/R/read_excel.R 
# Rewrote read_excel() to read_excel2() with additional argument 'n_max' for number 
# of rows to evaluate in function readxl:::xls_col_types and 
# readxl:::xlsx_col_types() 
# This is probably an unstable solution, since it calls internal functions from readxl. 
# May or may not survive next update of readxl. Seems to work in version 0.1.0 
library(readxl) 

read_excel2 <- function(path, sheet = 1, col_names = TRUE, col_types = NULL, 
         na = "", skip = 0, n_max = 1050000L) { 

    path <- readxl:::check_file(path) 
    ext <- tolower(tools::file_ext(path)) 

    switch(readxl:::excel_format(path), 
     xls = read_xls2(path, sheet, col_names, col_types, na, skip, n_max), 
     xlsx = read_xlsx2(path, sheet, col_names, col_types, na, skip, n_max) 
) 
} 
read_xls2 <- function(path, sheet = 1, col_names = TRUE, col_types = NULL, 
        na = "", skip = 0, n_max = n_max) { 

    sheet <- readxl:::standardise_sheet(sheet, readxl:::xls_sheets(path)) 

    has_col_names <- isTRUE(col_names) 
    if (has_col_names) { 
    col_names <- readxl:::xls_col_names(path, sheet, nskip = skip) 
    } else if (readxl:::isFALSE(col_names)) { 
    col_names <- paste0("X", seq_along(readxl:::xls_col_names(path, sheet))) 
    } 

    if (is.null(col_types)) { 
    col_types <- readxl:::xls_col_types(
     path, sheet, na = na, nskip = skip, has_col_names = has_col_names, n = n_max 
    ) 
    } 

    readxl:::xls_cols(path, sheet, col_names = col_names, col_types = col_types, 
        na = na, nskip = skip + has_col_names) 
} 

read_xlsx2 <- function(path, sheet = 1L, col_names = TRUE, col_types = NULL, 
         na = "", skip = 0, n_max = n_max) { 
    path <- readxl:::check_file(path) 
    sheet <- 
    readxl:::standardise_sheet(sheet, readxl:::xlsx_sheets(path)) 

    if (is.null(col_types)) { 
    col_types <- 
     readxl:::xlsx_col_types(
     path = path, sheet = sheet, na = na, nskip = skip + isTRUE(col_names), n = n_max 
    ) 
    } 

    readxl:::read_xlsx_(path, sheet, col_names = col_names, col_types = col_types, na = na, 
      nskip = skip) 
} 

Potresti ottenere un malvagio successo a causa di questa ipotesi estesa. Non ho ancora provato su set di dati veramente grandi, ho appena provato su dati più piccoli quanto basta per verificare la funzione.

2

Rivedere il source, vediamo che c'è una chiamata in Rcpp che restituisce i tipi di colonna indovinato:

xlsx_col_types <- function(path, sheet = 0L, na = "", nskip = 0L, n = 100L) { 
    .Call('readxl_xlsx_col_types', PACKAGE = 'readxl', path, sheet, na, nskip, n) 
} 

Si può vedere che per impostazione predefinita, nskip = 0L, n = 100L controlli le prime 100 righe da indovinare tipo di colonna. È possibile modificare nskip di ignorare il testo dell'intestazione e aumentare n (al costo di un tempo di esecuzione molto più lento) facendo:

col_types <- .Call('readxl_xlsx_col_types', PACKAGE = 'readxl', 
        path = file_loc, sheet = 0L, na = "", 
        nskip = 1L, n = 10000L) 

# if a column type is "blank", no values yet encountered -- increase n or just guess "text" 
col_types[col_types=="blank"] <- "text" 

raw <- read_excel(path = file_loc, col_types = col_types) 

senza guardare il .Rcpp, non è immediatamente chiaro se nskip = 0L salta l'intestazione riga (la riga zeroth nel conteggio C++) o salta nessuna riga.Ho evitato l'ambiguità usando semplicemente nskip = 1L, dal momento che saltare una riga del mio set di dati non ha alcun impatto sul tipo di colonna nel suo complesso.

2

nuova soluzione poiché readxl versione 1.x:

Il solution in the currently preferred answer vuol più lavorare con le versioni più recenti rispetto a 0.1.0 del readxl in quanto la funzione pacchetto-interno utilizzato readxl:::xlsx_col_types non esiste più.

La nuova soluzione è quella di utilizzare il parametro recente introduzione guess_max per aumentare il numero di righe utilizzate per "indovinare" il tipo di dati appropriato delle colonne:

read_excel("My_Excel_file.xlsx", sheet = 1, guess_max = 1048576) 

Il valore 1.048.576 è il numero massimo di righe supportato da Excel attualmente, vedere le specifiche di Excel: https://support.office.com/en-us/article/Excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3

PS: Se vi preoccupate per prestazioni usando tutte le righe di indovinare il tipo di dati: read_excel sembra di leggere il file una sola volta e l'ipotesi è fatto in memoria alloraquindi la penalizzazione delle prestazioni è molto piccola rispetto al lavoro salvato.

Problemi correlati