Указание типов столбцов при импорте данных 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
, похоже, читает файл только один раз, и догадка выполняется в памяти, а значит, очень мало по сравнению с сохраненной работой.