读取Excel表格,而不是简单的命名区域

7
为了避免"重复"的关闭请求:我知道如何读取Excel中的名称范围; 代码下面给出了示例。这与Excel中的“真实”表有关。
Excel2007及更高版本具有有用的表格概念:可以将范围转换为表格,并在排序和重新排列时避免麻烦。当您在Excel范围中创建表格时,它会获得默认名称(德语版中的Tabelle1,以下示例中的TableName),但您还可以简单地命名表格的范围(TableAsRangeName); 正如Excel范围名称编辑器中的图标所示,这两个似乎被不同地处理。
我无法从R中读取这些表格(严格意义上)。唯一已知的解决方法是使用CSV中间件,或将表格转换为普通的命名范围,这会在使用单元格引用中的列名时产生令人讨厌的不可逆副作用; 这些被转换为A1表示法。
下面的示例显示了问题。使用不同的32/64位ODBC驱动程序和32/64位Java可能会导致不同的结果。
# Read Excel Tables (not simply named ranges)
# Test Computer: 64 Bit Windows 7, R 32 bit  
# My ODBC drivers are 32 bit
library(RODBC)
# Test file has three ranges
# NonTable Simple named range
# TableName Name of table 
# TableAsRangeName Named Range covering the above table
sampleFile = "ExcelTables.xlsx"
if (!file.exists(sampleFile)){
  download.file("http://www.menne-biomed.de/uni/ExcelTables.xlsx",sampleFile)
  # Or do it manually, if this fails
}
# ODBC
channel = odbcConnectExcel2007(sampleFile)
sqlQuery(channel, "SELECT * from NonTable") # Ok
sqlQuery(channel, "SELECT * from TableName") # Could not find range
sqlQuery(channel, "SELECT * from TableAsRangeName") # Could not find range
close(channel)

# gdata has read.xls, but seems not to support named regions

library(xlsx)
wb = loadWorkbook(sampleFile)
getRanges(wb) # This one fails already with "TableName" does not exist
ws = getSheets(wb)[[1]]
readRange("NonTable",ws) # Invalid range address
readRange("TableName",ws) # Invalid range address
readRange("TableAsRangeName",ws) # Invalid range address

# my machine requires 64 bit for this one; depends on your Java installation
sampleFile = "ExcelTables.xlsx"
library(XLConnect) # requires Java
readNamedRegionFromFile(sampleFile,"NonTable") # OK
readNamedRegionFromFile(sampleFile,"TableName") # "TableName" does not exist
readNamedRegionFromFile(sampleFile,"TableAsRangeName") # NullPointerException

wb <- loadWorkbook(sampleFile)
readNamedRegion(wb,"NonTable") # Ok
readNamedRegion(wb,"TableName") # does not exist
readNamedRegion(wb,"TableAsRangeName") # Null Pointer

我打赌最简单的方法是返回Excel工作簿并创建一个新工作表,其中包含对命名表格单元格的简单链接(或公式)。然后告诉 R 从该工作表中读取。 - Carl Witthoft
XLConnect目前还不支持Excel表格。此外,基于表格公式的命名区域也尚未得到支持。但是,我会研究一下这个问题,看看能否在这方面做些什么。 - Martin Studer
感谢您,XLConnect的作者Martin Studer。昨天我在XML中进行了一些研究,并发现表格被映射到额外目录中的范围。如果您能够使它正常运行,请在此发布。 - Dieter Menne
抱歉并感谢您。已经更正,请再试一次。 - Dieter Menne
1
以防其他人尝试此操作:您必须使用IF(ISBLANK..结构来避免空单元格。 - Dieter Menne
显示剩余3条评论
3个回答

4
我已经为XLConnect添加了Excel表格的初步支持。请在https://github.com/miraisolutions/xlconnect上查找最新更改。
以下是一个小样例:
require(XLConnect)
sampleFile = "ExcelTables.xlsx"
wb = loadWorkbook(sampleFile)
readTable(wb, sheet = "ExcelTable", table = "TableName")

请注意,Excel表格关联到一个工作表。因此,据我所见,可以在不同的工作表中使用相同名称的多个表格。出于这个原因,在使用readTable时需要有一个sheet参数。

看起来RJava存在一个令人讨厌的64/32位依赖问题。我已经安装了工具链并经常使用它,但无法构建xlconnect;尝试了32位和64位R。错误:无法加载共享对象“D:/R/R/library/rJava/libs/i386/rJava.dll”:LoadLibrary失败:%1不是有效的Win32应用程序。明天再检查一下。 - Dieter Menne
请参见 https://github.com/miraisolutions/xlconnect/issues/21 和 https://github.com/miraisolutions/xlconnect/issues/20。 - Dieter Menne
这太棒了,能够刷新和写回/追加到Excel表格有多难?似乎无法使其工作,尝试使用writeNamedRegion()。 - MattV

3

您说的没错,表定义存储在XML中。

sampleFile = "ExcelTables.xlsx"
unzip(sampleFile, exdir = 'test')
library(XML)
tData <- xmlParse('test/xl/tables/table1.xml')
tables <- xpathApply(tData, "//*[local-name() = 'table']", xmlAttrs)
[[1]]
            id           name    displayName            ref totalsRowShown 
           "1"    "TableName"    "TableName"        "G1:I4"            "0" 
library(XLConnect)

readWorksheetFromFile(sampleFile, sheet = "ExcelTable", region = tables[[1]]['ref'], header = TRUE)
    Name Age AgeGroup
1  Anton  44        4
2 Bertha  33        3
3  Cäsar  21        2

根据您的情况,您可以在XML文件中搜索适当的数量。


我刚刚实现了类似于你的解决方案,还参考了http://housesofstones.com/blog/2013/06/20/quickly-read-excel-xlsx-worksheets-into-r-on-any-platform/#.UeaP6417IzY中的一些额外内容,因为我担心readWorksheetFromFile会在处理过大的真实文件时出现问题。 - Dieter Menne
一个有趣的博客。以前可以通过对底层的 xml 文件进行一些修改来删除 Excel 文件上的密码保护。 - user1609452
抱歉把信用转给了马丁,我给你加了一个+1。 - Dieter Menne
@user1609452,你的回答节省了我很多时间。如果你的Excel工作簿有多个工作表怎么办?有没有一种方法可以确定你的表存储在哪个工作表中? - rjss

0

后续添加:

readxl::readxl 可以读取“真实”的表格,当您想要读取数据框/表格时,这可能是最不麻烦的解决方案。

** 在 @Jamzy 的评论之后 ** 我再次尝试,但无法读取命名区域。那么现在是误报还是漏报呢?


1
请您能否详细说明一下?我在文档中找不到任何提及。 - Jamzy
没有,我在文档中没有找到,只是尝试了一下。另一方面,大多数其他读者不支持“真正”的表格这一事实也没有被记录。你试过吗?有其他的经验吗? - Dieter Menne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接