excel 大文件 如何用python 分段读取

新手上路,请多包涵

问题描述

项目中需要读取非常大的excel 文件。有几百兆也有可能上2G。使用常规的xlrd 的open_workbook 会一次性把文件load到内存。如果同时出现接个大文件的一个处理就会出现OOM。想要找一种方式分批读取excel 内容,不知道excel是否有支持的相关包?

问题出现的环境背景及自己尝试过哪些方法

尝试 xlrd, pandas 均没有相关的操作。csv倒是简单,但是不符合业务方的需求。

相关代码

// 请把代码文本粘贴到下方(请勿用图片代替代码)

你期待的结果是什么?实际看到的错误信息又是什么?

阅读 9k
3 个回答
新手上路,请多包涵

Hi, 我试了一下。
pd.ExcelFile(file_name)
这一行就已经把excel 整个加载到内存了。
调用前:
now 2019-05-19-22:29:38
内存使用: 0.06
总内存: 16.0
内存占比: 65.5
cpu个数: 8
file_size:66.02 MB
调用后:
now 2019-05-19-22:31:20
内存使用: 0.51
总内存: 16.0
内存占比: 69.6
cpu个数: 8

题主发现的问题如果还是一下子读取,可以尝试根据一楼回答修改下代码就可以做到不全部读取了。
代码中主动指定sheetname,不用pd.ExcelFile读取方式获取sheetname就可以运行以下函数了。

import os
import pandas as pd


HERE = os.path.abspath(os.path.dirname(__file__))
DATA_DIR = os.path.abspath(os.path.join(HERE, '..', 'data'))


def make_df_from_excel(file_name, nrows):
    """Read from an Excel file in chunks and make a single DataFrame.

    Parameters
    ----------
    file_name : str
    nrows : int
        Number of rows to read at a time. These Excel files are too big,
        so we can't read all rows in one go.
    """
    file_path = os.path.abspath(os.path.join(DATA_DIR, file_name))
    
    # 源代码注释掉以下这一段
    #xl = pd.ExcelFile(file_path)
    ## In this case, there was only a single Worksheet in the Workbook.
    #sheetname = xl.sheet_names[0]
    
    # 主动给予sheet名字
    sheetname = "sheet1"
    # Read the header outside of the loop, so all chunk reads are
    # consistent across all loop iterations.
    df_header = pd.read_excel(file_path, sheetname=sheetname, nrows=1)
    print(f"Excel file: {file_name} (worksheet: {sheetname})")

    chunks = []
    i_chunk = 0
    # The first row is the header. We have already read it, so we skip it.
    skiprows = 1
    while True:
        df_chunk = pd.read_excel(
            file_path, sheetname=sheetname,
            nrows=nrows, skiprows=skiprows, header=None)
        skiprows += nrows
        # When there is no data, we know we can break out of the loop.
        if not df_chunk.shape[0]:
            break
        else:
            print(f"  - chunk {i_chunk} ({df_chunk.shape[0]} rows)")
            chunks.append(df_chunk)
        i_chunk += 1

    df_chunks = pd.concat(chunks)
    # Rename the columns to concatenate the chunks with the header.
    columns = {i: col for i, col in enumerate(df_header.columns.tolist())}
    df_chunks.rename(columns=columns, inplace=True)
    df = pd.concat([df_header, df_chunks])
    return df

if __name__ == '__main__':
    df = make_df_from_excel('claims-2002-2006_0.xls', nrows=10000)
推荐问题