Python 如何使此函数从单个文件连接Excel工作表更有效?
我有几个Excel文件,我想从中生成单独的数据框,这些数据框是每个文件中任意图纸子集的串联 重要的是,我能够对工作表进行子集划分(在这里,我通过索引到Python 如何使此函数从单个文件连接Excel工作表更有效?,python,excel,python-3.x,pandas,Python,Excel,Python 3.x,Pandas,我有几个Excel文件,我想从中生成单独的数据框,这些数据框是每个文件中任意图纸子集的串联 重要的是,我能够对工作表进行子集划分(在这里,我通过索引到工作表名称列表),并以某种方式跟踪出处(就像我在这里使用赋值方法所做的那样) 这段代码目前可以工作,但我担心我缺少一些基本的方法来提高效率(比如当我得到一个有20张纸的文件时) 我已经回顾了其他几个关于连接(例如)多个CSV或Excel文件的问题1,但很难将这些问题推广到解析一个文件中的工作表的问题,即效率问题 这个问题的部分原因是我以前使用生成器
工作表名称列表
),并以某种方式跟踪出处(就像我在这里使用赋值
方法所做的那样)
这段代码目前可以工作,但我担心我缺少一些基本的方法来提高效率(比如当我得到一个有20张纸的文件时)
我已经回顾了其他几个关于连接(例如)多个CSV或Excel文件的问题1,但很难将这些问题推广到解析一个文件中的工作表的问题,即效率问题
这个问题的部分原因是我以前使用生成器连接Excel文件,但我很难将这些情况推广到这一个
一, “高效”可以有不同的解释。根据您的描述(特别是提到生成器),我猜您指的是内存和计算效率(使用尽可能少的内存,避免重复循环相同的数据)。考虑到这一点,我们有一个办法:
def df_gen(filename, sheet_names):
with xlrd.open_workbook(filename, on_demand=True) as xl_file:
for sheet in sheet_names:
yield pd.read_excel(
xl_file, sheetname=sheet, engine='xlrd').assign(source=sheet)
# tell xlrd to let the sheet leave memory
xl_file.unload_sheet(sheet)
这就利用了xlrd的特性来避免将整个Excel文档加载到内存中。在构造数据帧后,将从内存中显式卸载工作表。因为它使用yield
它是一个生成器,同时创建多少数据帧取决于您的使用情况。下面是将此生成器传递给pandas.concat的示例用法:
df = pd.concat(df_gen('file_name.xlsx', ['sheet1', 'sheet2']), ignore_index=True)
但是,请注意,在进行连接之前,生成器中的所有内容都要进行连接,因此这并不一定比您构建列表的示例更有效,只是我的函数有意管理xlrd工作簿的资源使用。在这种情况下,我认为根据concat
的内部结构,一次在内存中存储1到2份数据
如果您真的担心内存问题,可以使用生成器一次迭代地构建一个数据帧一张表:
# create a generator
gen = df_gen(str(filename), sheet_names)
# get starting point
df = next(gen)
# iterate over the rest of the generator
for next_df in gen:
df = df.append(next_df, ignore_index=True)
我认为这在计算效率上不如一次调用整个所需数据帧集的concat
,尽管我还没有研究这是否是真的。在这种情况下,我认为您一次只能得到内存中所有数据的一个副本,再加上通过生成器的每个循环的一个额外的工作表数据副本
你最了解自己的情况,但除非这些是真正令人印象深刻的Excel文件,否则我不会在优化内存和计算方面投入太多精力,而不仅仅是看似明显的胜利。考虑到这一点,这里有一个简短的函数,它利用了一次读取多张图纸的能力:
def sheets_to_df(filename, sheet_names):
df_dict = pd.read_excel(filename, sheetname=sheet_names)
return pd.concat(
(df.assign(source=sheet) for sheet, df in dfs.items()), ignore_index=True)
需要注意的一点是,当传入文件名时,read_excel
will(例如,不使用xlrd的“按需”功能)。因此,虽然这在代码行方面是有效的,但在内存方面肯定不是有效的。我认为这会导致内存中的所有数据出现2-3次:一次出现在df_dict
中,一次出现在最终连接的数据帧中(可能还取决于concat
的内部结构)。但是一旦这个函数返回,在最后的数据帧中只剩下一个副本。如果你打算阅读大部分的工作表,这不会是一个巨大的浪费(假设它们都能在内存中存储至少两次),但是如果你打算只阅读工作表的一小部分,这可能会有点浪费
我希望这有帮助!您可以在此处将其作为Jupyter笔记本使用:工作表名称是否与您需要的相同?或者你从来都不想要第一张纸?@djk47463对于前几个文件,我发现省略第一张纸就足够了,但从你的评论中我看到,你认为应该将其标记为关闭。我认为,经过编辑,这个人可以提出一个可行的问题,我理解你为什么不这样做。下次投票时,我会考虑的,谢谢。
def sheets_to_df(filename, sheet_names):
df_dict = pd.read_excel(filename, sheetname=sheet_names)
return pd.concat(
(df.assign(source=sheet) for sheet, df in dfs.items()), ignore_index=True)