每份交付的 Excel 文件须同时满足以下四点:
#REF! #DIV/0! #VALUE! #N/A #NAME?所有 Python 代码统一通过 jupyter_cell_exec工具 执行(用户每次提问时自动启动新内核)。输出文件保存到工作目录(OUTPUT_ROOT)下。注意:用户每发起新提问时 Jupyter 环境会重置,变量与状态仅在当轮多次调用之间保留。
jupyter_cell_exec工具 在同一个代码块中完成文件创建与公式重算。使用了公式时,必须在保存文件后紧接着调用 recalc() 验证:```python
# ... 创建/编辑 Excel 文件的代码 ...
wb.save(output_path)
# 重算公式并验证(使用了公式时必须执行)
import sys, os
sys.path.insert(0, os.path.join(os.getenv('skill_path'), 'xlsx', 'scripts'))
from recalc import recalc
result = recalc(output_path)
print(result)
```
recalc(filename: str, timeout: int = 60) -> dictstatus: "errors_found",根据 error_summary 中的位置修复后再次调用 recalc()#REF!(无效引用)、#DIV/0!(除以零)、#VALUE!(数据类型错误)、#NAME?(未识别的公式名)始终使用 Excel 公式,而非在 Python 中计算后将结果硬编码写入。 这样表格才能在源数据变化时自动重算。
total = df['Sales'].sum()
sheet['B10'] = total # 硬编码为 5000
growth = (df.iloc[-1]['Revenue'] - df.iloc[0]['Revenue']) / df.iloc[0]['Revenue']
sheet['C5'] = growth # 硬编码为 0.15
sheet['B10'] = '=SUM(B2:B9)'
sheet['C5'] = '=(C4-C2)/C2'
sheet['D20'] = '=AVERAGE(D2:D19)'
所有计算——合计、百分比、比率、差值——均适用此原则。
当任务涉及多个维度、多个数据集或数据量较大时,将内容合理拆分到多个 Sheet,而非堆在一张表上。每个 Sheet 聚焦单一主题,并在顶部用简短说明交代该 Sheet 的用途与数据范围,帮助读者快速定位所需信息。
import pandas as pd
df = pd.read_excel('file.xlsx')
all_sheets = pd.read_excel('file.xlsx', sheet_name=None)
df.head()
df.info()
df.describe()
df.to_excel('output.xlsx', index=False)
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment
wb = Workbook()
sheet = wb.active
sheet['A1'] = 'Hello'
sheet['B1'] = 'World'
sheet.append(['Row', 'of', 'data'])
sheet['B2'] = '=SUM(A1:A10)'
sheet['A1'].font = Font(bold=True, color='FF0000')
sheet['A1'].fill = PatternFill('solid', start_color='FFFF00')
sheet['A1'].alignment = Alignment(horizontal='center')
sheet.column_dimensions['A'].width = 20
wb.save('output.xlsx')
from openpyxl import load_workbook
wb = load_workbook('existing.xlsx')
sheet = wb.active
sheet['A1'] = 'New Value'
sheet.insert_rows(2)
sheet.delete_cols(3)
new_sheet = wb.create_sheet('NewSheet')
new_sheet['A1'] = 'Data'
wb.save('modified.xlsx')
openpyxl 写入的公式只是字符串,Excel 打开前不会有计算值。通过 recalc 模块使用纯 Python 引擎重算所有公式,并扫描全部单元格检查错误。无需安装任何外部软件。
import sys, os
sys.path.insert(0, os.path.join(os.getenv('skill_path'), 'xlsx', 'scripts'))
from recalc import recalc
result = recalc("/path/to/output.xlsx")
print(result)
recalc(filename: str, timeout: int = 60) -> dict
filename:xlsx 文件的绝对路径timeout:重算超时秒数,默认 60{
"status": "success",
"total_errors": 0,
"total_formulas": 42,
"error_summary": {}
}
若 status 为 errors_found,error_summary 会列出每种错误的位置(最多 20 处):
{
"status": "errors_found",
"total_errors": 2,
"total_formulas": 42,
"error_summary": {
"#REF!": {
"count": 2,
"locations": ["Sheet1!B5", "Sheet1!C10"]
}
}
}
recalc 使用纯 Python 公式引擎,覆盖大多数常用 Excel 函数:
若遇到不支持的函数,recalc 会降级为静态分析(检查引用和结构),并在返回值中包含 warning 字段说明情况。
pd.notna() 检查空值/ 前检查分母(#DIV/0!)Sheet1!A1)$#,##0,表头注明单位(如 "Revenue ($mm)")$#,##0;($#,##0);-)0.0%0.0x(123)=B5(1+$B$6) 而非 =B51.05Source: [系统/文档], [日期], [具体引用], [URL(如有)]Source: 公司年报, FY2024, 第45页, 营收附注Source: Bloomberg Terminal, 2025/8/15, AAPL US EquitySource: Wind, 2025/8/20, 一致预期数据使用 TwoCellAnchor 精确定位,必须遵循网格布局规则防止重叠。
将图表区域看作网格,先确定布局参数,再算每个图表的坐标:
布局参数(先确定再写代码):
DATA_END_ROW = 数据区最后一行(0-indexed)
CHART_START = DATA_END_ROW + 2(图表起始行,留 1 行缓冲)
COLS_PER_CHART = 7(每个图表占的列数,推荐 6-8)
ROWS_PER_CHART = 15(每个图表占的行数,推荐 14-16)
GAP_COLS = 1(列间距,必须 ≥ 1)
GAP_ROWS = 2(行间距,必须 ≥ 2)
网格坐标计算公式:
第 i 行第 j 列图表(i, j 从 0 开始):
from_col = j * (COLS_PER_CHART + GAP_COLS)
from_row = CHART_START + i * (ROWS_PER_CHART + GAP_ROWS)
to_col = from_col + COLS_PER_CHART
to_row = from_row + ROWS_PER_CHART
核心规则:
to_col 必须 ≤ 下一列图表的 from_col,to_row 必须 ≤ 下一行图表的 from_rowCHART_START 行、第 0 列开始,到最后一个图表的 to_row、to_col)内不得写入辅助数据from openpyxl.chart import BarChart, PieChart, LineChart
from openpyxl.drawing.spreadsheet_drawing import TwoCellAnchor
# 1. 确定布局参数
CHART_START = 19 # 数据区结束后 +2
COLS = 7 # 每图表占 7 列
ROWS = 15 # 每图表占 15 行
GAP_C = 1 # 列间距(图表之间留 1 列空白)
GAP_R = 2 # 行间距(图表之间留 2 行空白)
def place_chart(ws, chart, grid_row, grid_col):
"""将图表放入网格的 (grid_row, grid_col) 位置,0-indexed"""
a = TwoCellAnchor()
a._from.col = grid_col * (COLS + GAP_C)
a._from.row = CHART_START + grid_row * (ROWS + GAP_R)
a.to.col = a._from.col + COLS
a.to.row = a._from.row + ROWS
chart.anchor = a
ws._charts.append(chart)
# 2. 创建图表并放入网格(图表之间自动留白)
# 第 0 行: chart1(0,0) [1列空白] chart2(0,1)
# [2行空白]
# 第 1 行: chart3(1,0) [1列空白] chart4(1,1)
# [2行空白]
# 第 2 行: chart5(2,0) [1列空白] chart6(2,1)
place_chart(ws, chart1, 0, 0) # A20:G34
place_chart(ws, chart2, 0, 1) # I20:O34 (H列空白)
place_chart(ws, chart3, 1, 0) # A37:G51 (35-36行空白)
place_chart(ws, chart4, 1, 1) # I37:O51
place_chart(ws, chart5, 2, 0) # A54:G68
place_chart(ws, chart6, 2, 1) # I54:O68
关键点:
anchor._from.col/row 和 anchor.to.col/row 都是 0-indexedrow = 0,A 列 = col = 0to.row - _from.rowplace_chart 函数或等价的公式统一计算,避免算错关键原则:Reference 的行列范围必须精确匹配数据区域,避免包含多余的表头或空行。
from openpyxl.chart import Reference
# 假设数据结构:
# A1: 姓名 B1: 语文 C1: 数学 D1: 英语 E1: 总分
# A2: 张三 B2: 85 C2: 90 D2: 88 E2: 263
# A3: 李四 B3: 78 C3: 82 D3: 85 E3: 245
# ... (共 8 行数据,A2:E9)
# ✅ 正确:柱状图显示总分
data = Reference(ws, min_col=5, min_row=1, max_row=9) # E1:E9(包含表头"总分")
categories = Reference(ws, min_col=1, min_row=2, max_row=9) # A2:A9(学生姓名,不含表头)
chart.add_data(data, titles_from_data=True) # titles_from_data=True 会把 E1 作为系列名
chart.set_categories(categories)
# ✅ 正确:折线图显示三科成绩
data = Reference(ws, min_col=2, max_col=4, min_row=1, max_row=9) # B1:D9(包含表头)
categories = Reference(ws, min_col=1, min_row=2, max_row=9) # A2:A9(学生姓名)
chart.add_data(data, titles_from_data=True) # B1:D1 作为系列名(语文、数学、英语)
chart.set_categories(categories)
# ❌ 错误:data 包含了姓名列
data = Reference(ws, min_col=1, max_col=4, min_row=1, max_row=9) # 错误地包含了 A 列
chart.add_data(data, titles_from_data=True) # 会把"姓名"也当成数据系列
# ❌ 错误:categories 包含了表头
categories = Reference(ws, min_col=1, min_row=1, max_row=9) # 错误地包含了 A1
chart.set_categories(categories) # 横轴会显示"姓名 张三 李四..."
关键点:
min_row=1 且 titles_from_data=True → 第 1 行作为系列名(图例)min_row=2 → 从第 2 行开始取数据categories 通常不包含表头(min_row=2)data 包含表头时设置 titles_from_data=True饼图特殊说明:
# 饼图通常只有一个数据系列,不需要系列名
# ✅ 正确:data 和 categories 都不包含表头
data = Reference(ws, min_col=2, min_row=2, max_row=5) # B2:B5(数值)
categories = Reference(ws, min_col=1, min_row=2, max_row=5) # A2:A5(分类名)
pie_chart.add_data(data, titles_from_data=False) # 饼图不需要系列名
pie_chart.set_categories(categories)
# ❌ 错误:data 包含表头会导致标签显示 "表头名, 分类名, 值, 百分比"
data = Reference(ws, min_col=2, min_row=1, max_row=5) # B1:B5(包含表头)
pie_chart.add_data(data, titles_from_data=True) # 错误!饼图标签会变成 "人数, 90分以上, 10, 100%"
add_chart + width/height不要使用 ws.add_chart(chart, 'A3') + chart.width/height,该方案的单位是 cm,极易算错导致重叠。始终使用上述 TwoCellAnchor 网格系统。
data_only=True 读取已计算的值;以此模式保存会永久丢失公式read_only=True,写入用 write_only=Truepd.read_excel('file.xlsx', dtype={'id': str})pd.read_excel('file.xlsx', usecols=['A', 'C', 'E'])pd.read_excel('file.xlsx', parse_dates=['date_column'])Python 脚本:编写简洁代码,避免多余注释、冗长变量名和不必要的 print。
Excel 文件本身:为复杂公式或关键假设添加单元格注释,为硬编码值注明数据来源。
共 1 个版本