来自excel的Pandas数据框命名范围

时间:2013-12-10 04:58:53

标签: python excel pandas

如何从excel中的命名范围读入pandas数据帧? read_excel旨在读取工作簿中的整个工作表。

6 个答案:

答案 0 :(得分: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连接的这一点。

之前的要求是一个非常显着的缺点,实际上支持任何不涉及ODBC的解决方案。也就是说,如果read_Excel提供了这样的便利,那就太好了。在这种情况下,值得注意的是,SAS,SPSS和Stata目前不允许在各自的Excel过滤器中直接访问命名区域 - 因此可能存在缺乏此功能的客观原因。

答案 1 :(得分:2)

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

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]
    df = pd.DataFrame([cell.value for cell in row] for row in region)
    return df

答案 2 :(得分:1)

您可以使用基础xlrd包来执行此操作。

xlrd包附带一个examples目录,其中包含xlrdnameAPIdemo.py,记录为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执行此操作,那么您正在读取数据文件两次,这很愚蠢,但您明白了。

答案 3 :(得分:0)

以下是我使用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]]

答案 4 :(得分:0)

好了,已经有一段时间了,但是我绝对建议给xlwings投一枪。

另请参阅Xlwings take value from defined names

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

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