从 csv 文件添加 table 到 python-docx 非常慢
extremely slow add a table to python-docx from a csv file
我必须从 docx word 文档中大约 1500 行和 9 列(75 页)的 CSV 文件中添加一个 table。使用 python-docx.
我尝试了不同的方法,用 pandas 读取 csv 或直接打开 de csv 文件,我花了大约 150 分钟才以我选择的方式独立完成工作
我的问题是这是否可能是正常行为,或者是否存在任何其他方法来改进此任务。
我正在使用这个 for 循环读取几个 cvs 文件并以 table 格式解析它
for toTAB in listBRUTO:
df= pd.read_csv(toTAB)
# add a table to the end and create a reference variable
# extra row is so we can add the header row
t = doc.add_table(df.shape[0]+1, df.shape[1])
t.style = 'LightShading-Accent1' # border
# add the header rows.
for j in range(df.shape[-1]):
t.cell(0,j).text = df.columns[j]
# add the rest of the data frame
for i in range(df.shape[0]):
for j in range(df.shape[-1]):
t.cell(i+1,j).text = str(df.values[i,j])
#TABLE Format
for row in t.rows:
for cell in row.cells:
paragraphs = cell.paragraphs
for paragraph in paragraphs:
for run in paragraph.runs:
font = run.font
font.name = 'Calibri'
font.size= Pt(7)
doc.add_page_break()
doc.save('blabla.docx')
提前致谢
您需要尽量减少对 table.cell()
的调用次数。由于单元格合并的工作方式,这些是在紧密循环中执行时真正加起来的昂贵操作。
我将从重构这个块开始,看看会产生多少改进:
# --- add the rest of the data frame ---
for i in range(df.shape[0]):
for j, cell in enumerate(table.rows[i + 1].cells):
cell.text = str(df.values[i, j])
对于没有合并单元格的矩形 table,您可以将所有单元格导出到列表列表结构中并非常快速地填充它们(少于 0.5s vs 15s ~300 行 tables with 3 列):
from docx.table import _Cell
def get_cells_grid(table):
cells = [[]]
col_count = table._column_count
for tc in table._tbl.iter_tcs():
cells[-1].append(_Cell(tc, table))
if len(cells[-1]) == col_count:
cells.append([])
return cells
cells = get_cells_grid(t)
for i in range(df.shape[0]):
for j in range(df.shape[i]):
cells[i][j].text = str(df.values[i, j])
基于 table._cells()
代码的函数:https://github.com/python-openxml/python-docx/blob/da75fcf01f7f322e846e2ac3e1936aedd766acc8/docx/table.py#L162
python-docx 每次访问它的“单元格”时都会遍历整个 table 属性.
所以你最好尽可能少地调用“.cell”,而是为单元格使用缓存。
这是两个示例,访问大小为 3*1500 的 table:
代码1:约150.0s
for row in table.rows:
print('processing: {0:30s}'.format(row.cells[0].text),end='\r')
代码2:约1.4s
clls=table._cells
for row_idx in range(len(clls)//table._column_count):
print('processing: {0:30s}'.format(
clls[0 + row_idx*table._column_count].text),end='\r')
代码 2 中的 clls=table._cells
使用“_cells”处理单元格合并,因此 ccls[column_idx + row_idx*table._column_count].text
与 table.rows[row_idx].cells[column_idx].text
一样好用,不需要 table
正好是矩形
补充一下我的经验,如果您必须创建一个巨大的 table,请先创建整个结构,这意味着您将需要的所有行和单元格;然后像这样存储单元格
table_cells = table._cells
(根据@kztopia)
然后您可以随心所欲地操作单元格、合并、添加文本等...具有相当优化的牢度,因为您只调用一次 cell()
在我的用例中,在我看来,table 不是那么大(~130 行,每行 8 个单元格),它过去需要 9 秒来创建整个东西,现在我在 .5 左右。
请记住,table 越大,执行 cell() 所需的时间就越多。
我必须从 docx word 文档中大约 1500 行和 9 列(75 页)的 CSV 文件中添加一个 table。使用 python-docx.
我尝试了不同的方法,用 pandas 读取 csv 或直接打开 de csv 文件,我花了大约 150 分钟才以我选择的方式独立完成工作
我的问题是这是否可能是正常行为,或者是否存在任何其他方法来改进此任务。
我正在使用这个 for 循环读取几个 cvs 文件并以 table 格式解析它
for toTAB in listBRUTO:
df= pd.read_csv(toTAB)
# add a table to the end and create a reference variable
# extra row is so we can add the header row
t = doc.add_table(df.shape[0]+1, df.shape[1])
t.style = 'LightShading-Accent1' # border
# add the header rows.
for j in range(df.shape[-1]):
t.cell(0,j).text = df.columns[j]
# add the rest of the data frame
for i in range(df.shape[0]):
for j in range(df.shape[-1]):
t.cell(i+1,j).text = str(df.values[i,j])
#TABLE Format
for row in t.rows:
for cell in row.cells:
paragraphs = cell.paragraphs
for paragraph in paragraphs:
for run in paragraph.runs:
font = run.font
font.name = 'Calibri'
font.size= Pt(7)
doc.add_page_break()
doc.save('blabla.docx')
提前致谢
您需要尽量减少对 table.cell()
的调用次数。由于单元格合并的工作方式,这些是在紧密循环中执行时真正加起来的昂贵操作。
我将从重构这个块开始,看看会产生多少改进:
# --- add the rest of the data frame ---
for i in range(df.shape[0]):
for j, cell in enumerate(table.rows[i + 1].cells):
cell.text = str(df.values[i, j])
对于没有合并单元格的矩形 table,您可以将所有单元格导出到列表列表结构中并非常快速地填充它们(少于 0.5s vs 15s ~300 行 tables with 3 列):
from docx.table import _Cell
def get_cells_grid(table):
cells = [[]]
col_count = table._column_count
for tc in table._tbl.iter_tcs():
cells[-1].append(_Cell(tc, table))
if len(cells[-1]) == col_count:
cells.append([])
return cells
cells = get_cells_grid(t)
for i in range(df.shape[0]):
for j in range(df.shape[i]):
cells[i][j].text = str(df.values[i, j])
基于 table._cells()
代码的函数:https://github.com/python-openxml/python-docx/blob/da75fcf01f7f322e846e2ac3e1936aedd766acc8/docx/table.py#L162
python-docx 每次访问它的“单元格”时都会遍历整个 table 属性.
所以你最好尽可能少地调用“.cell”,而是为单元格使用缓存。
这是两个示例,访问大小为 3*1500 的 table:
代码1:约150.0s
for row in table.rows:
print('processing: {0:30s}'.format(row.cells[0].text),end='\r')
代码2:约1.4s
clls=table._cells
for row_idx in range(len(clls)//table._column_count):
print('processing: {0:30s}'.format(
clls[0 + row_idx*table._column_count].text),end='\r')
代码 2 中的 clls=table._cells
使用“_cells”处理单元格合并,因此 ccls[column_idx + row_idx*table._column_count].text
与 table.rows[row_idx].cells[column_idx].text
一样好用,不需要 table
正好是矩形
补充一下我的经验,如果您必须创建一个巨大的 table,请先创建整个结构,这意味着您将需要的所有行和单元格;然后像这样存储单元格
table_cells = table._cells
(根据@kztopia)
然后您可以随心所欲地操作单元格、合并、添加文本等...具有相当优化的牢度,因为您只调用一次 cell()
在我的用例中,在我看来,table 不是那么大(~130 行,每行 8 个单元格),它过去需要 9 秒来创建整个东西,现在我在 .5 左右。
请记住,table 越大,执行 cell() 所需的时间就越多。