将 Excel 命名范围读入 pandas DataFrame

Posted

技术标签:

【中文标题】将 Excel 命名范围读入 pandas DataFrame【英文标题】:Reading an Excel named range into a pandas DataFrame 【发布时间】:2013-12-27 11:55:18 【问题描述】:

如何将 Excel 中命名范围的数据读取到 pandas DataFrame 中?

不幸的是,规范函数 pandas.read_excel() 旨在仅读取工作簿中的整个工作表。

【问题讨论】:

为自己省去一个痛苦的世界,并创建一个名为“foo”的新工作表,左上角引用您的命名范围“foo”。如果你必须隐藏它。然后只需使用pd.read_excel(workbook, sheet_name='foo') 【参考方案1】:

您可以使用read_excel 以一种全面的方式执行此操作,它提供:

skiprows : list-like
    Rows to skip at the beginning (0-indexed)

skip_footer : int, default 0
    Rows at the end to skip (0-indexed)

parse_cols : int or list, default None
        If None then parse all columns,
        If int then indicates last column to be parsed
        If list of ints then indicates list of column numbers to be parsed
        If string then indicates comma separated list of column names and column ranges (e.g. “A:E” or “A,C,E:F”)

这意味着如果您知道列名和行号(大概是“命名范围”的意思?),您可以只选择该部分来制作 DataFrame。

【讨论】:

这允许您在开始时跳过行。它仍然会读取到工作表中的最后一个空单元格。 查看 DataNitro。它是一个非常棒的 excel 插件,它允许这种事情以及许多其他事情。它对于非商业项目是免费的。我广泛使用它 @DavidNehme 这不是 skip_footer 的重点吗? (我同意这可以用一个很好的包装函数来做......) skip_footer 会假设您也知道列的长度(行数),以便您可以计算从末尾弹出多少 此答案与“命名范围”无关。【参考方案2】:

引用Microsoft Office help pages!:

[命名范围] 是一种有意义的速记,它可以更容易地理解单元格引用、常量、公式或表格的用途,其中每一个都可能乍一看难以理解。”

命名范围更常用于电子表格中,以便通过 ODBC 更轻松地访问数据,并且在同一个工作表中有多个数据范围时特别有用。要通过 ODBC 连接到 Excel,只需选择适当的 Excel driver 并发送 SQL 语句,例如:

SELECT * 
FROM namedRange

Pandas 中有用的命令可能是 read_sql。

但是,在 Windows 中,此解决方案要求您对齐/简化已安装的 Excel 软件版本(32 位或 64 位)、ODBC 驱动程序和打开 ODBC 连接的软件包。例如,安装的 Excel 32 位版本需要 32 位 ODBC 驱动程序,通常需要 32 位安装的 Python。 注意:后一点对于 Python 案例仍有待确认(我是 Python 的初学者),但对于从 SAS、SPSS 或 Stata 启动的 ODBC 连接,我绝对可以确认这一点。 p>

前面的要求是一个非常重要的缺点,实际上它支持任何根本不涉及 ODBC 的解决方案。也就是说,如果 read_Excel 提供了这样的工具,那就太好了。在这种情况下,有趣的是,SAS、SPSS 和 Stata 目前不允许在其各自的 Excel 过滤器中直接访问命名范围 - 所以也许缺少这种功能是有客观原因的。 .

【讨论】:

【参考方案3】:

您可以使用底层的xlrd 包来执行此操作。

xlrd 包附带一个包含xlrdnameAPIdemo.pyexamples 目录,如文档中的here 所述。

简而言之,命名范围 print_area 尝试:

book = xlrd.open_workbook('examples/namesdemo.xls')
name_obj = book.name_map['print_area'][0]
print name_obj.__dict__

您会看到name_obj 有一个条目:

'result': Operand(kind=oREF, value=[Ref3D(coords=(2, 3, 0, 4, 0, 14))], text=u'Sheet3!$A$1:$N$4')

您可以按照示例进行解释,尽管它看起来并不简单 - 例如。范围可能是相对的,也可能不是,取决于值result.kind

此外,当我尝试使用它来阅读我自己的电子表格(在 Mac 上创建)时,我发现 resultNone;相反,name_obj 中范围的唯一引用是:

'formula_text': u'Sheet1!$B$6:$E$11'

因此,在一般情况下,可能有一种方法可以使其工作,但看起来需要一些试验和错误。

作为替代方案,如果您可以格式化您的电子表格,而不是命名范围,您的表格紧跟在唯一标题 (key) 之后的行中,并以空白行结束,这里有一个函数可以找到正确的参数发送到pd.read_excel:

def table_position(path, sheet_name, key):
    """
    Find the start and end rows of a table in an Excel spreadsheet
    based on the first occurence of key text on the sheet, and down
    to the first blank line.

    Returns (col, start_row, end_row, skip_footer)

    where: 
        col is the column number containing the key text,
        start_row is the row after this, 
        end_row is the row number of the next blank line,
        skip_footer is how many rows from the end of the sheet this is.

    You can then read in the table with:
        x = pd.read_excel(path, sheet_name, skiprows=start, skip_footer=skip_footer, header=0)
        x = x.dropna(axis=1, how='all')
    """
    import xlrd
    book = xlrd.open_workbook(path)
    sheet = book.sheet_by_name(sheet_name)
    # find the first occurrence of the key, and the next line break
    (col, start, end) = (-1, -1, sheet.nrows)
    for rownum in xrange(sheet.nrows):
        if col<0: # look for key to start the table off
            try:
                test_col = next(c for c in xrange(sheet.ncols) if sheet.cell(rownum, c).value==key)
            except StopIteration:
                pass
            else:
                col, start = test_col, rownum+1 # row after key text is the start
        else: # test for blank line as end of table
            if not [True for cell in sheet.row(rownum) if cell.value]:
                end = rownum
                break
    skip_footer = sheet.nrows - end
    return (col, start, end, skip_footer)

如果您确实使用 pd.read_excel 来执行此操作,那么您将读取数据文件两次,这很愚蠢,但您明白了。

【讨论】:

【参考方案4】:

也许有一天 pandas 会原生支持这一点。在那之前,我使用了一个辅助函数:

import pandas as pd
import openpyxl

def data_frame_from_xlsx(xlsx_file, range_name):
    """ Get a single rectangular region from the specified file.
    range_name can be a standard Excel reference ('Sheet1!A2:B7') or 
    refer to a named region ('my_cells')."""
    wb = openpyxl.load_workbook(xlsx_file, data_only=True, read_only=True)
    if '!' in range_name:
        # passed a worksheet!cell reference
        ws_name, reg = range_name.split('!')
        if ws_name.startswith("'") and ws_name.endswith("'"):
            # optionally strip single quotes around sheet name
            ws_name = ws_name[1:-1]
        region = wb[ws_name][reg]
    else:
        # passed a named range; find the cells in the workbook
        full_range = wb.get_named_range(range_name)
        if full_range is None:
            raise ValueError(
                'Range "" not found in workbook "".'.format(range_name, xlsx_file)
            )
        # convert to list (openpyxl 2.3 returns a list but 2.4+ returns a generator)
        destinations = list(full_range.destinations) 
        if len(destinations) > 1:
            raise ValueError(
                'Range "" in workbook "" contains more than one region.'
                .format(range_name, xlsx_file)
            )
        ws, reg = destinations[0]
        # convert to worksheet object (openpyxl 2.3 returns a worksheet object 
        # but 2.4+ returns the name of a worksheet)
        if isinstance(ws, str):
            ws = wb[ws]
        region = ws[reg]
    # an anonymous user suggested this to catch a single-cell range (untested):
    # if not isinstance(region, 'tuple'): df = pd.DataFrame(region.value)
    df = pd.DataFrame([cell.value for cell in row] for row in region)
    return df

【讨论】:

我从region = wb[ws_name][reg] 行收到错误“INPUT 不是有效的坐标或范围”。我有许多具有相同范围名称的工作表。我尝试将range_name 输入为SIGNALS_01!INPUT。我在想问题是,起初在 Excel 名称管理器中,范围被设置为工作簿,但将其更改为 SIGNALS_01 并没有解决问题。如果我通过SIGNALS_01!A1:C3 或具有范围为工作簿的区域“INPUTS”并通过INPUTS,则该功能有效。我认为我的问题与 Excel 更相关:如果不像我尝试过的那样,我应该如何引用某个工作表中的某个区域?【参考方案5】:

这是我使用 openpyxl 在 [[]] 中复制范围的方式:

wb = load_workbook(filename=xlPath)
ws, range= next(wb.defined_names["rangename"].destinations)
materials = [[cell.value for cell in row] for row in wb[ws][range]]

【讨论】:

【参考方案6】:

嗯,已经有一段时间了,但我绝对会推荐给xlwings 一个机会。

另见Xlwings take value from defined names 。

【讨论】:

以上是关于将 Excel 命名范围读入 pandas DataFrame的主要内容,如果未能解决你的问题,请参考以下文章

将excel电子表格读入pandas DataFrame时将数字转换为字符串

如何将带有 NaN 的合并 Excel 单元格读入 Pandas DataFrame

将 S3 中的 excel 文件读入 Pandas DataFrame

通过 pandas.read_excel 在标题后跳过行范围

For Loop - 将所有 excel 选项卡读入 Panda Df

只将包含某个单词的 Excel sheet_names 读入 pandas 数据框