探究 Pandas 讀取 Excel 文件報錯問題

問題描述

使用 Pandas 的 read_excel 方法讀取一個 16 萬行的 Excel 文件報 AssertionError 錯誤:

  "/Users/XXX/excel_test/venv/lib/python3.7/site-packages/xlrd/xlsx.py", line 637, in do_row
    assert 0 <= self.rowx < X12_MAX_ROWS
AssertionError

背後原理

Excel 文件有兩種默認格式,在 Excel 2007 以前,使用擴展名爲 .xls 格式的文件,這種文件格式是一種特定的二進制格式,最多支持 65,536 行(在 Excel 97 之前支持的最大行數是 16,384),256 列表格。從 Excel 2007 版開始,默認採用了基於 XML 的新的文件格式 .xlsx,支持的表格行數達到了 1,048,576,列數達到了 16,384。需要注意的是,將 .xlsx 格式的文件轉換爲 .xls 格式的文件時,65,536 行和 256 列之後的數據都會被丟棄。

版本 最大行數 最大列數 文件格式
Excel 97 之前 16,384 256 .xls
Excel 97 到 Excel 2003 65,536 256 .xls
Excel 2007 及以後版本 1,048,576 16,384 .xlsx

Pandas 讀取 Excel 文件的引擎是 xlrdxlrd 在讀取 Excel 文件時,xlrd/xlsx.py 文件的 637 行會對行號做斷言,判斷行號是否在 0 - 1,048,576(Excel支持的最大行數) 的範圍內。這段代碼是這樣的:

row_number = row_elem.get('r')
if row_number is None: # Yes, it's optional.
    self.rowx += 1
    explicit_row_number = 0
    if self.verbosity and not self.warned_no_row_num:
        self.dumpout("no row number; assuming rowx=%d", self.rowx)
        self.warned_no_row_num = 1
else:
    self.rowx = row_number - 1
    explicit_row_number = 1
assert 0 <= self.rowx < X12_MAX_ROWS

代碼會從 Excel 文件中獲取 row_number,這個 row_number 是每一行的行號,正常文件行號從 1 開始,而出現問題的文件行號從 0 開始,當行號爲 0,進入 else 語句,導致越界問題。

解決辦法

除了 xlrd, Pandas 還支持 openpyxl(0.25 版),openpyxl 是一個專門用來操作 .xlsx 格式文件的 Python 庫,和 xlrd 相比它的速度會慢一些,但是不會碰到上面所說的問題。這是 openpyxl 中 reader/excel.py 文件處理行的代碼:

def parse_row(self, row):
    attrs = dict(row.attrib)

    if "r" in attrs:
        self.max_row = int(attrs['r'])
    else:
        self.max_row += 1
    keys = set(attrs)
    for key in keys:
        if key.startswith('{'):
            del attrs[key]

    keys = set(attrs)
    if keys != set(['r', 'spans']) and keys != set(['r']):
        # don't create dimension objects unless they have relevant information
        self.row_dimensions[attrs['r']] = attrs

    cells = [self.parse_cell(el) for el in row]
    return self.max_row, cells

openpyxl 在處理行時,並沒有對行號進行斷言,即使行號第一位是 0,也不會導致報錯,但這會導致第一行數據的缺失,需要進行額外處理

使用 Pandas + openpyxl 讀取 Excel 文件

首先安裝 openpyxl

pip install openpyxl

Pandas 的 read_excel 方法中,有 engine 字段,可以指定所使用的處理 Excel 文件的引擎,填入 openpyxl,再讀取文件就可以了。

import pandas as pd


df = pd.read_excel('./data.xlsx', engine='openpyxl')
print(len(df))  # 160000

參考文檔

https://office-watch.com/2010/excel-a-history-of-rows-and-columns/

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_excel.html

https://github.com/python-excel/xlrd/

https://bitbucket.org/openpyxl/openpyxl/src

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章