【发布时间】:2022-04-11 13:00:34
【问题描述】:
我有一批大小从 10Mb 到 400Mb 的 XLSX 文件。它们始终具有相同的表格和内部结构,但有些包含的数据比其他的多。
我正在尝试使用 AWS Lambda 处理这些;它是提交过程的一部分,因此 S3 中的文件删除是 Lambda 的事件。
我很快了解到 XLSX 是一种可怕的格式,但我无法更改它。目前,我的主要 Lambda 正在使用我在网上找到并稍作改动的此类工作。内存使用率和速度比 Pandas read_excel 有所提高,但仍然不够。对于 400Mb 的文件,Lambda 只会超时或耗尽其内存分配(即使是最大)。
对脚本进行一些内存分析,我可以看到在枢轴操作期间大小减小,但我不能真正跳过。
关于如何改进它以提高内存效率的任何提示?
每个工作表都需要保存到自己的 CSV,但如果有帮助,可以将其拆分为多个 CSV 文件,比如可能是块大小样式的迭代器?
import io
import zipfile
from lxml import etree
from pandas import read_csv, to_numeric
class ExcelParse:
sheet_xslt = etree.XML('''
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:sp="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
>
<xsl:output method="text"/>
<xsl:template match="sp:row">
<xsl:for-each select="sp:c">
<xsl:value-of select="parent::*/@r"/> <!-- ROW -->
<xsl:text>,</xsl:text>
<xsl:value-of select="@r"/> <!--REMOVEME-->
<xsl:text>,</xsl:text>
<xsl:value-of select="@t"/> <!-- TYPE -->
<xsl:text>,</xsl:text>
<xsl:value-of select="sp:v/text()"/> <!-- VALUE -->
<xsl:text>\n</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
''')
def __init__(self, file):
self.fh = zipfile.ZipFile(file)
self.ns = {
'ns': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
}
self.shared = self.load_shared()
self.workbook = self.load_workbook()
def load_workbook(self):
# Load workbook
name = 'xl/workbook.xml'
root = etree.parse(self.fh.open(name))
res = {}
for el in etree.XPath("//ns:sheet", namespaces=self.ns)(root):
res[el.attrib['name']] = str(
int(el.attrib['sheetId']) -
1) # Sheet ID in the XML starts at 2 for some reason?
return res
def load_shared(self):
# Load shared strings
name = 'xl/sharedStrings.xml'
root = etree.parse(self.fh.open(name))
res = etree.XPath("/ns:sst/ns:si/ns:t", namespaces=self.ns)(root)
return {str(pos): el.text for pos, el in enumerate(res)}
def _parse_sheet(self, root):
transform = etree.XSLT(self.sheet_xslt)
result = transform(root)
df = read_csv(io.StringIO(str(result)),
header=None,
names=['row', 'cell', 'type', 'value'])
return df
def read(self, sheet_name):
sheet_id = self.workbook[sheet_name]
sheet_path = f'xl/worksheets/sheet{sheet_id}.xml'
root = etree.parse(self.fh.open(sheet_path))
df = self._parse_sheet(root)
# First row numbers are filled with nan
df['row'] = to_numeric(df['row'].fillna(0))
# Translate string contents
cond = (df.type == 's') & (~df.value.isnull())
df.loc[cond, 'value'] = df[cond]['value'].map(self.shared)
# Add column number and sort rows
df['col'] = df.cell.str.replace(r'[0-9]+', '', regex=True)
# Pivot everything
df = df.pivot(
index='row', columns='col',
values='value').reset_index(drop=True).reset_index(drop=True)
df.columns.name = None # pivot adds a name to the "columns" array
# Sort columns (pivot will put AA before B)
cols = sorted(df.columns, key=lambda x: (len(x), x))
df = df[cols]
df = df.dropna(how='all') # Ignore empty lines
df = df.dropna(how='all', axis=1) # Ignore empty cols
new_header = df.iloc[0] # Grab the first row for the header
df = df[1:] # Take the data less the header row
df.columns = new_header # Set the header row as the df header
return df
def new_method():
xlsx = ExcelParse(
'BigFile.xlsx'
)
print(xlsx.workbook)
df = xlsx.read('Task')
# for sheet_name, sheet_id in xlsx.workbook.items():
# df = xlsx.read(sheet_name)
# do stuff
new_method()
【问题讨论】:
-
这段代码是什么?这似乎不是 lambda 函数。
-
这只是我在 lambda 函数中使用的一个类。我只是将它导入到 Lambda 函数中。
标签: python excel pandas amazon-web-services