2015-07-26 4 views
15

Я импортирую xlsx 2007 таблицы в R 3.2.1patched с использованием пакета readxl 0.1.0 под Windows 7 64. Размер таблиц составляет порядка 25 000 строк на 200 столбцов.Указание типов столбцов при импорте данных xlsx в R с пакетом readxl

Функция read_excel() работает с удовольствием. Моя единственная проблема заключается в том, что его назначение класса столбца (типа данных) для малозаселенных столбцов. Например, данный столбец может быть NA для 20 000 строк, а затем примет значение символа в строке 20,001. read_excel() по умолчанию используется тип столбца с числовым номером при сканировании первых n строк столбца и только для определения NAs. Данные, вызывающие проблему, являются символами в столбце, присвоенном числовой. Когда достигнут предел ошибки, выполнение останавливается. Я действительно хочу данные в разреженных столбцах, поэтому установка предела ошибки выше не является решением.

Я могу определить неприятные столбцы, просмотрев предупреждающие сообщения. И read_excel() имеет возможность для утверждения типа данных столбца, установив аргумент col_types в соответствии с пакетом документации:

Либо NULL угадать из таблицы или вектора символов, содержащего blank, numeric, date или text.

Но значит ли это, что я должен построить вектор длиной 200 населен почти в каждой позиции с blank и text в кучки позициях, соответствующих столбцам нарушителями?

Возможно, есть способ сделать это в парах строк R. Создайте вектор требуемой длины и заполните его blank s. Может быть, еще один вектор, содержащий числа столбцов, которые должны быть загнаны в text, а затем ... Или, возможно, можно вызвать для read_excel() только столбцы, для которых его догадки не так желательны.

Буду признателен за любые предложения.

Заранее спасибо.

ответ

2

Чтения источника, это выглядит как типы столбцов угадываются функциями xls_col_types или xlsx_col_types, которые реализуются в Rcpp, но имеет значение по умолчанию:

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) 
} 

Моего C++ является очень ржавым, но это выглядит n=100L - это команда, сообщающая, сколько строк нужно читать.

Поскольку эти не имели экспортируемые функции, паста в:

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

И в всплывающем окне, измените n = 100L на большее число. Затем повторите попытку импорта файла.

+0

Бывают случаи, когда было бы полезно контролировать количество строк, отсканированных для определения типа данных. Однако в этой задаче может потребоваться сканирование очень далеко вниз по малонаселенным коллегам. Я думаю, что основная проблема - удобный способ указать желаемый тип данных нескольких столбцов, где поведение по умолчанию не требуется. В своем ответе на http://stackoverflow.com/q/6099243 Mikko, похоже, предлагает способ сделать это для read.xlsx2 в пакете xlsx. Возможно, подобные работы для readxl. Большое спасибо за проверку источника для readxl_xlsx_col_types. – jackw19

4

Это зависит от того, являются ли ваши данные разреженными в разных местах в разных колонках и насколько они разрежены. Я обнаружил, что наличие большего количества строк не улучшило синтаксический анализ: большинство было по-прежнему пустым и интерпретировалось как текст, даже если позже они становятся датами и т. Д.

Один рабочий процесс - это создание первой строки данных вашей таблицы excel, чтобы включить репрезентативные данные для каждого столбца и использовать их для определения типов столбцов. Мне это не нравится, потому что я хочу оставить исходные данные целыми.

Другим обходным решением, если у вас есть полные строки где-то в электронной таблице, является использование nskip вместо n. Это дает отправную точку для угадывания столбцов. Скажем, строка 117 данных имеет полный набор данных:

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

Обратите внимание, что вы можете вызвать функцию непосредственно, без необходимости редактирования функции в пространстве имен.

Вы можете использовать вектор типов электронных таблиц для вызова 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) 

Затем вы можете вручную обновить все столбцы, которые он по-прежнему получает неверные.

+0

Не могли бы вы рассказать о своих обозначениях? Я хотел бы сделать что-то подобное с пакетом xlsx, функцией read.xlsx и аргументом colClasses. Благодаря! –

5

Я столкнулся с аналогичной проблемой.

В моем случае пустые строки и столбцы использовались в качестве разделителей. И в листах было много таблиц (с разными форматами). Таким образом, пакеты {openxlsx} и {readxl} не подходят в этой ситуации, поэтому openxlsx удаляет пустые столбцы (и для изменения этого параметра нет параметра). Пакет Readxl работает, как вы описали, и некоторые данные могут быть потеряны.

В результате я считаю, что лучшим решением, если вы хотите автоматически обрабатывать большие суммы данных excel, является чтение листов без изменений в текстовом формате, а затем обработка data.frames в соответствии с вашими правилами ,

Эта функция может читать листы без изменений (спасибо @ джек-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

с columnNames loadExcelSheet <-функции (excel.file, лист) { требуется ("readxl") листов <- readxl :: excel_sheets (excel.file) sheet.num <- матч (лист, листов) - 1 num.columns <- length (readxl ::: xlsx_col_types (excel.file, sheet = sheet.num, nskip = 0, n = 1)) colNames <- readxl ::: xlsx_col_names (excel.file, sheet = 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

Это решение больше не будет работать в 'readxl' версии 1.x, так как внутренняя функция' readxl ::: xlsx_ col_types' удален. Чтобы решить проблему, вы видите этот ответ: https://stackoverflow.com/a/46122161/4468078 –

1

Внутренних Funcitons для угадывания типов столбцов могут быть установлены на любое количество строк для сканирования. Но read_excel() не реализует это (пока?).

Решение ниже - это просто переписать функцию orignal read_excel() с аргументом n_max, который по умолчанию соответствует всем строкам. Из-за отсутствия воображения эта расширенная функция называется read_excel2.

Просто замените read_excel на read_excel2, чтобы оценить типы столбцов по всем строкам.

# 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) 
} 

Вы можете получить злое падение производительности из-за этого продлено догадкой. Не пробовал на действительно больших наборах данных, просто попробовал на меньшие данные достаточно для проверки функции.

2

Рассматривая source, мы видим, что есть вызов Rcpp, который возвращает догадывались типы столбцов:

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

Вы можете увидеть, что по умолчанию, nskip = 0L, n = 100L проверяет первые 100 строк угадать тип столбца. Вы можете изменить nskip игнорировать текст заголовка и увеличить n (по стоимости гораздо медленнее выполнения), выполнив:

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) 

Не глядя на .Rcpp, это не сразу понятно мне скачет ли nskip = 0L заголовок строка (нулевая строка в подсчете C++) или пропускает строки.Я избегал двусмысленности, просто используя nskip = 1L, так как пропуски строки моего набора данных не влияют на общие предположения типа столбца.

2

Новое решение так readxl версии 1.x:

solution in the currently preferred answer уже больше не работает с новыми версиями, чем 0.1.0 из readxl начиная используемого для пакета внутренней функции readxl:::xlsx_col_types уже больше не существует.

Новое решение заключается в использовании вновь введенный параметр guess_max, чтобы увеличить количество строк, используемых для «угадать» соответствующий тип данных столбцов:

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

Значение 1048576 максимальное число линий поддерживается Excel в настоящее время, см спецификация Excel: https://support.office.com/en-us/article/Excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3

PS: Если вы заботитесь о производительность используя все строки, чтобы определить тип данных: read_excel кажется, только один раз прочитать файл и догадка делается в памяти, топоэтому штраф за производительность очень мал по сравнению с сохраненной работой.