Указание типов столбцов при импорте данных xlsx в R с пакетом readxl

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

Функция 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() только столбцы, для которых его догадки не являются желательными.

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

Спасибо заранее.

Ответы

Ответ 1

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

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

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

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

Ответ 2

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

Один рабочий процесс состоит в том, чтобы сгенерировать первую строку данных вашей таблицы 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)

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

Ответ 3

Считывая источник, похоже, что типы столбцов угадываются функциями 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)
}

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

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

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

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

Ответ 4

Пересматривая , мы видим, что есть вызов 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 строку заголовка (нулевую строку в подсчете С++) или пропускает строки. Я избегал двусмысленности, просто используя nskip = 1L, так как пропуская строку моего набора данных не влияет на общие предположения типа столбца.

Ответ 5

Внутренние функции для угадывания типов столбцов могут быть установлены на любое количество строк для сканирования. Но 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)
}

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

Ответ 6

Новое решение с readxl версия 1.x:

Решение в текущем предпочтительном ответе больше не работает с более новыми версиями, чем 0,1.0 readxl, поскольку используемая внутренняя функция readxl:::xlsx_col_types больше не существует.

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

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

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

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