从 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].texttable.rows[row_idx].cells[column_idx].text 一样好用,不需要 table正好是矩形

补充一下我的经验,如果您必须创建一个巨大的 table,请先创建整个结构,这意味着您将需要的所有行和单元格;然后像这样存储单元格

table_cells = table._cells(根据@kztopia)

然后您可以随心所欲地操作单元格、合并、添加文本等...具有相当优化的牢度,因为您只调用一次 cell()

在我的用例中,在我看来,table 不是那么大(~130 行,每行 8 个单元格),它过去需要 9 秒来创建整个东西,现在我在 .5 左右。

请记住,table 越大,执行 cell() 所需的时间就越多。