
场景:上周五下午快6点了,我正盯着屏幕调一个接口的返回格式,工位隔板上探出一颗脑袋。是我们部门总监,手里攥着手机,微信界面亮着。
“小王,上个月华东地区Top5的产品销售数据,整理一下发我。”
我叹了口气,打开那个叫“2025全年销售汇总v3_final最终版.xlsx”的文件。十几个sheet,列名还不一样。找到“销售明细”那个sheet,筛选华东,排序,复制前5行,粘贴到微信。
刚要继续干活,手机又震了。“按产品分类再整理一份。”
那天下午,我被打断了十几次。领导想看各种维度的数据,但那个Excel太复杂,他自己搞不定,只能找我。我也没法怪他,那表格确实乱,我自己有时候都要找半天。
但我也不能一直当人工查询机。一天被打断十几次,正事全耽误了。
后来我想了个办法。既然现在大模型这么聪明,能不能让它来读Excel,领导问什么就答什么?我花了一个晚上写了个小工具,把Excel变成了一个可以问答的数据库。
思路很简单,三步走。先用pandas把Excel读进来,自动生成一份数据结构的描述(就是哪些列、什么类型、长什么样)。然后把这份描述发给大模型,让它把领导的自然语言问题翻译成pandas代码。最后我这边执行代码,拿到结果。
就这么个事。
# pip install pandas openai openpyxl
import pandas as pd
from openai import OpenAI
# 建连接,key换成你自己的
client = OpenAI(api_key="your_api_key_here")
def load_and_describe(filepath):
"""读Excel,顺便生成数据结构的描述"""
df = pd.read_excel(filepath)
# 拼一段描述文字,告诉大模型这个表长什么样
desc = f"这个DataFrame变量名叫df,一共有{len(df)}行,{len(df.columns)}列。\n\n"
desc += "列信息如下:\n"
for col in df.columns:
dtype = str(df[col].dtype)
nulls = df[col].isna().sum()
# 取几个示例值给大模型看看
samples = df[col].dropna().head(3).tolist()
desc += f"- {col}: 类型是{dtype}, 有{nulls}个空值, 示例值: {samples}\n"
# 如果行数太多,也给大模型看几行真实数据
desc += f"\n前3行数据预览:\n{df.head(3).to_string()}"
return df, desc
def generate_pandas_code(question, schema):
"""让大模型把自然语言问题转成pandas代码"""
prompt = f"""你是一个pandas数据分析专家。用户有一个已经加载好的DataFrame,变量名叫df。
数据结构如下:
{schema}
请根据用户的问题,写一段pandas代码来回答。
要求:
1. df变量已经存在,不需要重新读取文件
2. 把最终答案存到变量result里
3. 只写代码,不要写解释文字
4. 代码要简洁,能一行搞定的别写三行
5. 如果涉及日期,列名可能叫 date、order_date 或 created_at
6. 如果是数值统计,记得处理空值
用户的问题:{question}"""
resp = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}],
temperature=0 # 写代码用低温,稳定一点
)
code = resp.choices[0].message.content
# 把markdown代码块标记去掉
code = code.replace("```python", "").replace("```", "").strip()
return code
def run_query(question, df, schema):
"""生成代码并执行,返回结果"""
# 第一步:让大模型写代码
code = generate_pandas_code(question, schema)
print(f"[大模型生成的代码]\n{code}\n")
# 第二步:执行代码
# 把df和pd放到local变量里,exec才能用
local_vars = {"df": df, "pd": pd}
try:
exec(code, {}, local_vars)
result = local_vars.get("result", "代码执行了但没有result变量")
except Exception as e:
result = f"代码执行出错了: {e}"
return result
def chat_with_excel(filepath):
"""主函数:加载Excel,进入问答循环"""
print(f"正在加载文件: {filepath}")
df, schema = load_and_describe(filepath)
print(f"加载完成,{len(df)}行数据。")
print("="*50)
# 进入交互模式,可以一直问
while True:
question = input("\n请输入你的问题(输入q退出): ")
if question.lower() == "q":
print("拜拜。")
break
print(f"\n>>> 问题: {question}")
result = run_query(question, df, schema)
print(f">>> 结果:\n{result}")
print("-"*40)
if __name__ == "__main__":
# 换成你自己的Excel路径
chat_with_excel("sales_2024.xlsx")拿销售数据试了一下(数据脱敏过了),效果还不错。
问题1:“上个月哪个产品销售最多”
[大模型生成的代码]
# 假设日期列叫order_date
recent = df[df['order_date'] >= '2024-11-01']
result = recent.groupby('产品名称')['销售额'].sum().sort_values(ascending=False).head(5)
>>> 结果:
产品名称
智能手环Pro 285600.0
蓝牙耳机X3 234100.0
充电宝20000 198500.0
手机壳套装 156800.0
数据线三合一 123400.0问题2:“各个地区的销售占比是多少”
[大模型生成的代码]
result = df.groupby('地区')['销售额'].sum()
result = (result / result.sum() * 100).round(2)
>>> 结果:
地区
华东 35.42
华南 28.17
华北 18.63
西南 11.28
其他 6.50问题3:“退货率最高的5个产品”
[大模型生成的代码]
refund_rate = df.groupby('产品名称').agg(
退货数=('是否退货', 'sum'),
总数=('订单号', 'count')
)
refund_rate['退货率'] = (refund_rate['退货数'] / refund_rate['总数'] * 100).round(2)
result = refund_rate.sort_values('退货率', ascending=False).head(5)
>>> 结果:
退货数 总数 退货率
产品名称
USB转接头 89 423 21.04
耳机收纳袋 45 267 16.85
屏幕贴膜 112 789 14.20大模型生成的代码基本都能跑。偶尔有列名猜错的情况,但schema里已经给了所有列名,大部分时候不会搞错。
关于schema描述。 这个是最关键的。大模型写代码准不准,全看schema描述得清不清楚。我把列名、类型、空值数量、示例值都放进去了,大模型一看就知道该怎么写。如果示例值不够,还可以加一列unique值的数量,让大模型知道这列是不是适合做groupby。
关于安全性。 exec()这个东西嘛,懂的都懂。如果给领导用,最好加个白名单,只允许pandas操作,别让他执行什么os.system("rm -rf /")。我在代码里用的是空的globals字典,只传了df和pd进去,已经做了基本隔离。但如果是部署到线上,还是要再加固一下。
关于执行出错怎么办。 代码里有try-except,出错了会返回错误信息。你可以把错误信息再发给大模型,让它修正代码重试。我试了一下,大部分错误重试一次就能修好。
这个工具我在自己电脑上跑了一周。领导再来问数据,我直接把问题丢进去,几秒钟出结果,复制粘贴发过去。
以前每次查数据要5到10分钟,现在30秒。一天省下来的时间够我多喝两杯咖啡了。
“无他,惟手熟尔”!有需要的用起来!
本文分享自 Nicholas与Pypi 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!