做交通数据分析时,常常会有一些比较常态化的分析内容,需要我们周期性提交分析报告,比如分析gps数据质量,卡口设备情况等,但这些报告语言文字比较结构化,每次提交时只需要改动某些文字或者数据,为了减少人工,我们可以通过python-docx库(python-docx — python-docx 1.1.2 documentation)自动生成我们所要提交的结构化分析报告。
欢迎关注本人公众号--交通数据探索师
下面我们介绍一下python-docx库实际用到的一些函数。
可先在cmd中通过下面命令安装
pip install python-docx
日常在操作word时,我们通常会涉及到的内容就是添加标题、添加段落、添加图片、添加表格等四大部分,本文也围绕这四部分展开。
在进行以上操作之前,我们需要通过一下命令生成或者打开一个word文档。
from docx import Document # 创建一个新文档, doc = Document() # 打开一个现有文档 # doc = Document(r'./temp/demo.docx') # 通过下面语句设置整个文档的字体均是楷体 doc.styles['Normal'].font.name = u'楷体' doc.styles['Normal'].element.rPr.rFonts.set(qn('w:eastAsia'), u'楷体') # 设置整个文档中的英文字体为Times New Roman doc.styles['Normal'].font.name = 'Times New Roman'
1、添加标题
通过add_heading函数添加标题
add_heading(text: str = '', level: int = 1) # text: 标题内容 # level: 标题级别,取值0-9,默认为1(一级标题)
标题常用设置
from docx import Document from docx.oxml.ns import qn from docx.shared import RGBColor, Pt from docx.enum.text import WD_ALIGN_PARAGRAPH def add_title(): """添加标题""" # 新建一个word文档 doc = Document() # 通过add_heading()方法添加一个一级标题,且标题文本为'标题文本' heading = doc.add_heading('标题文本', level=1) # 获取标题中的第一个run对象(相当于把标题分成了几部分,一个run对象就代表一部分,这样可以为每个部分设置不同的字体、大小等属性), # 用于设置标题的字体、大小等属性 run = heading.runs[0] # 设置字体为宋体 run.font.name = '宋体' # 这一行必须加,用于设置东亚字体。在Word处理中,对于中文字体,需要特别指定东亚字体,否则可能不会正确显示 run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体') # 设置字体大小, Pt(12)表示12磅,相当于小四号字体 run.font.size = Pt(12) # 设置字体颜色,RGBColor(0, 0, 0)表示黑色 run.font.color.rgb = RGBColor(0, 0, 0) # 设置标题前后间距 heading.paragraph_format.space_before = Pt(18) heading.paragraph_format.space_after = Pt(12) # 设置标题对齐方式 heading.alignment = WD_ALIGN_PARAGRAPH.CENTER # 居中对齐 # heading.alignment = WD_ALIGN_PARAGRAPH.LEFT # 左对齐 # heading.alignment = WD_ALIGN_PARAGRAPH.RIGHT # 右对齐 # 保存文档 doc.save('test.docx') add_title()
输出示例如下,
2、添加段落
通过add_paragraph函数添加段落
add_paragraph(text: str = '', style: str | ParagraphStyle | None = None)
段落常用设置
from docx import Document from docx.oxml.ns import qn from docx.shared import RGBColor, Pt from docx.enum.text import WD_ALIGN_PARAGRAPH def add_content(): """添加正文内容""" # 创建文档对象 doc = Document() # 添加段落 paragraph = doc.add_paragraph('''画堂晨起,来报雪花坠。高卷帘栊看佳瑞,皓色远迷庭砌。盛气光引炉烟,素草寒生玉佩。应是天仙狂醉,乱把白云揉碎。''') # 设置字体 # 获取段落中的第一个run对象,可以遍历paragraph.runs来获取所有run对象,为所有run对象设置字体 run = paragraph.runs[0] run.font.name = '宋体' run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体') # 设置字体大小 run.font.size = Pt(12) # 设置字体颜色 run.font.color.rgb = RGBColor(0, 250, 0) # 设置首行缩进2字符 这里只能设置缩进多少磅,不能直接设置多少字符 # 但是上面设置字体大小时,一个字体设置为12磅,则缩进两个字符就是缩进24磅 paragraph.paragraph_format.first_line_indent = Pt(24) # 设置行间距 1.5表示行间距为1.5倍字体大小 paragraph.paragraph_format.line_spacing = 1.5 # 设置行间距为多少磅呢?通过一下函数, 设置行间距为12磅 # paragraph.paragraph_format.line_spacing = Pt(12) # 段前、段后间距 # 设置段前间距为12磅 paragraph.paragraph_format.space_before = Pt(12) # 设置段后间距为12磅 paragraph.paragraph_format.space_after = Pt(12) doc.save('test.docx') add_content()
输出示例如下,
3、添加图片
通过add_picture函数添加图片
add_picture(image_path_or_stream: str | IO[bytes], width: , height:) # image_path_or_stream: 图片路径 如r'./temp/demo.png' # width: 设置图片宽度 # height: 设置图片高度
图片常用设置
def add_figure(): """添加图片""" # 创建文档对象 doc = Document() # 添加图片 doc.add_picture(r'demo.png') # 保存文档 doc.save('test.docx') add_figure()
输出示例如下,
4、添加表格
通过add_table函数添加表格
add_table(rows: int, cols: int, style: str | _TableStyle | None = None) # rows: 表格行数 # cols: 表格的列数 # style: 表格的样式
表格常用设置
目前涉及到的场景主要是将程序中生成的DataFrame作为表格添加到word中。
from docx import Document from docx.oxml.ns import qn from docx.shared import RGBColor, Pt from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.enum.table import WD_TABLE_ALIGNMENT import pandas as pd data = pd.DataFrame({'name': ['张三', '李四', '王五'], 'age': [18, 20, 30], 'sex': ['male', 'male', 'male']}) def add_tables(df): """添加表格""" # 创建文档对象 doc = Document() doc.styles['Normal'].font.name = u'楷体' doc.styles['Normal'].element.rPr.rFonts.set(qn('w:eastAsia'), u'楷体') doc.styles['Normal'].font.name = 'Times New Roman' # 先创建一个与我们的数据行列数相同的表格对象, 再往表格中填充数据 rows = df.shape[0]+1 # 表格行数 cols = df.shape[1] # 表格列数 # 创建一个行数为rows,列数为cols,样式为Table Grid的表格 table = doc.add_table(rows=rows, cols=cols, style='Table Grid') # 表格居中对齐 table.alignment = WD_TABLE_ALIGNMENT.CENTER # 填充数据, 先填充表头,即先将df的列名填充进去 for row in [0]: for col in range(cols): cell = table.cell(row, col) # 获取单元格 cell.text = df.columns[col] # 填充列名 # 设置单元格中字体大小 cell.paragraphs[0].runs[0].font.size = Pt(15) # 设置单元格中字体为宋体 cell.paragraphs[0].runs[0].font.name = '宋体' cell.paragraphs[0].runs[0]._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体') # 设置字体颜色 cell.paragraphs[0].runs[0].font.color.rgb = RGBColor(250, 0, 0) # 单元格内容水平居中 cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER # 单元格内容垂直居中 cell.vertical_alignment = WD_ALIGN_PARAGRAPH.CENTER # 填充主体数据 for row in range(1, rows): for col in range(cols): cell = table.cell(row, col) # 填充进单元格的值得是字符形式 cell.text = f'{df.iat[row-1, col]}' cell.paragraphs[0].runs[0].font.size = Pt(12) # 设置表格的行高 for row in table.rows: row.height = Inches(1/72*12) # 设置表格的列宽 for col in table.columns: col.width = Inches(1/72*30) doc.save('test.docx') add_tables(data)
输出示例如下,
5、总结示例
假如每天都要输出的示例报告如下,其中数据为个人杜撰。
import pandas as pd import matplotlib.pyplot as plt from docx import Document from docx.oxml.ns import qn from docx.shared import RGBColor, Pt, Cm from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_PARAGRAPH_ALIGNMENT from docx.enum.table import WD_TABLE_ALIGNMENT import warnings warnings.filterwarnings('ignore') # 解决中文乱码问题,并设置字体 plt.rcParams['axes.unicode_minus'] = False plt.rcParams['font.family'] = ['SimHei'] plt.rcParams['font.sans-serif'] = ['SimHei'] def plot_bar(data): """绘制柱状图""" data['时间'] = data['时间'].astype(str) fig, ax = plt.subplots(figsize=(10, 8), dpi=150) plt.plot(data['时间'], data['流量'], color='#73de94', label=data['路段'].unique()[0]) plt.legend() plt.tight_layout() plt.savefig('demo.png') def generate_report(data, year, month, day): """生成报告""" # 新建一个文档 doc = Document() # 设置整个文档的字体为楷体 doc.styles['Normal'].font.name = u'楷体' doc.styles['Normal'].element.rPr.rFonts.set(qn('w:eastAsia'), u'楷体') # 设置整个文档中的英文字体为Times New Roman doc.styles['Normal'].font.name = 'Times New Roman' # 添加标题 将要改动的地方设置成格式化字符串 heading = doc.add_heading(f'{year}年{month}月流量报告', level=1) heading.runs[0].font.color.rgb = RGBColor(0, 0, 0) heading.alignment = WD_ALIGN_PARAGRAPH.CENTER # 居中对齐 # 添加段落 paragraph = doc.add_paragraph() for road in data['路段'].unique(): # 计算早高峰均值 morning_peak_avg = data.loc[data['路段'] == road, '流量'].mean().round(0).astype(int) paragraph = doc.add_paragraph() paragraph.text = f'{road}{year}年{month}月{day}号早高峰流量均值为{morning_peak_avg}pcu/h,上午流量变化如下图,' # 添加图像 plot_bar(data.loc[data['路段'] == road]) # 图片居中设置 paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER run = paragraph.add_run("") # 添加图片并设置图片宽为10厘米,高为5厘米 run.add_picture('demo.png', width=Cm(10), height=Cm(5)) # 添加最后的表格 doc.add_paragraph('具体流量数据如下表:') rows = data.shape[0] + 1 cols = data.shape[1] table = doc.add_table(rows, cols, style='Table Grid') # 填充数据, 先填充表头,即先将data的列名填充进去 for row in [0]: for col in range(cols): cell = table.cell(row, col) # 获取单元格 cell.text = data.columns[col] # 填充列名 print(data.columns[col]) # 设置单元格中字体大小 cell.paragraphs[0].runs[0].font.size = Pt(15) # 单元格内容水平居中 cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER # 单元格内容垂直居中 cell.vertical_alignment = WD_ALIGN_PARAGRAPH.CENTER # 填充主体数据 for row in range(1, rows): for col in range(cols): cell = table.cell(row, col) # 填充进单元格的值得是字符形式 cell.text = f'{data.iat[row-1, col]}' cell.paragraphs[0].runs[0].font.size = Pt(12) # 单元格内容水平居中 cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER # 单元格内容垂直居中 cell.vertical_alignment = WD_ALIGN_PARAGRAPH.CENTER doc.save('demo.docx') if __name__ == '__main__': # 读取示例数据 df = pd.read_excel(r'test.xlsx') generate_report(df, 2024, 5, 8)