使用包readxl将xlsx数据导入R时指定列类型

时间:2015-07-26 05:15:49

标签: r readxl

我使用xlsx下的R 3.2.1patched包将readxl 0.1.0 2007表导入Windows 7 64。表格'大小是25,000行乘200列。

功能read_excel()有效。我唯一的问题是它将列类(数据类型)分配给稀疏填充的列。例如,给定列可以是20,000行的NA,然后在行20,001上取字符值。在扫描列的前n行并仅查找read_excel()时,NAs似乎默认为列类型数字。导致问题的数据是指定为数字的列中的字符。达到错误限制时,执行停止。我实际上想要稀疏列中的数据,因此将错误限制设置得更高并不是解决方案。

我可以通过查看引发的警告来识别麻烦的列。并且read_excel()可以根据包文档设置参数col_types来声明列的数据类型:

可以从电子表格中猜出NULL或包含blanknumericdatetext的字符向量。

但这是否意味着我必须构建一个长度为200的向量,其中几乎每个位置都有blanktext,其中少数位置对应于有问题的列?

这可能是通过几行R代码执行此操作的方法。创建一个所需长度的向量,并用blank s填充它。可能是另一个包含要强制为text的列数的向量,然后......或者可能只能为read_excel()调用其猜测所针对的列&# 39;根据需要。

我很感激任何建议。

提前致谢。

6 个答案:

答案 0 :(得分:6)

我遇到过类似的问题。

在我的例子中,空行和列用作分隔符。并且表中包含许多表格(具有不同的格式)。因此,{openxlsx}{readxl}包不适合这种情况,导致openxlsx删除空列(并且没有参数可以更改此行为)。 Readxl包就像你描述的那样工作,有些数据可能会丢失。

在结果中,我认为,如果您想自动处理大量的Excel数据,最佳解决方案是在不更改“文本”格式的情况下阅读工作表,然后根据您的规则处理data.frames。

此功能可以无需更改即可读取工作表(感谢@ 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 
}

答案 1 :(得分: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)

然后您可以手动更新任何仍然出错的列。

答案 2 :(得分:4)

readxl版本1.x后的新解决方案:

由于使用的包内部函数readxl不再存在,solution in the currently preferred answer不再适用于readxl:::xlsx_col_types的0.1.0以上的新版本。

新解决方案是使用新引入的参数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似乎只读取文件一次并且猜测是在内存中完成的那么性能与保存的工作相比,惩罚很小。

答案 3 :(得分:2)

阅读源代码,看起来列函数是由函数xls_col_typesxlsx_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更改为更大的数字。然后重新运行您的文件导入。

答案 4 :(得分: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 ++计数中的第0行)或不跳过任何行。我只是使用nskip = 1L来避免歧义,因为跳过我的数据集行并不会影响整个列类型的猜测。

答案 5 :(得分:1)

可以将用于猜测列类型的内部函数设置为要扫描的任意数量的行。但是read_excel()没有实现(但是?)。

下面的解决方案只是重写了带有参数read_excel()的orignal函数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)
}

由于这种扩展猜测,你可能会受到惊吓。还没有尝试过真正的大数据集,只是尝试使用较小的数据来验证功能。