Chat2table,简易表格分析助手

一 写在前面

之前用智谱AI的Chatglm3-6b模型写过一个简单的论文阅读助手,可用来辅助论文阅读等。而像表格,如Excel、CSV文件等内容的分析,也是不可忽略的需要,因此本文同样使用Chatglm3-6b来搭建一个表格分析助手,用于快速分析表格的内容,提取有效的信息。

Chatglm3采用了全新的对话格式,除最基本的对话外,还支持工具调用和代码执行。简单来说,代码执行属于工具调用的子类,只是提示词不一样,而这两种功能是通过修改微调阶段的提示词来实现的。本文展示的模型作用类似代码执行,但是提示词略不一样,并且只用了最常见的对话提示词模板来完成该功能。

二 表格理解

读取表格非常简单,使用pandas库中的read_csv或者read_excel即可。

1 直接读取完整的表格内容

利用to_json方法将df转化为一个json字符串

python 复制代码
def read_from_csv(filename):
	df = pd.read_csv(filename)
	return df.to_json(force_ascii=False)

s = read_from_csv('/test_short.csv')
print(s)
bash 复制代码
'{"id":{"0":22501,"1":22502,"2":22503,"3":22504,"4":22505,"5":22506,"6":22507,"7":22508},"age":{"0":35,"1":26,"2":44,"3":36,"4":41,"5":24,"6":25,"7":33},"nr_employed":{"0":5205,"1":4925,"2":4947,"3":5203,"4":4992,"5":4993,"6":5155,"7":5034}}'

接着把上述表格内容的字符串放进提示词中

python 复制代码
prompt = f"已知信息:{s}\n\n请回答问题:age大于35的数量有多少?\n\n"

用了上述的提示词生成的python代码如下:

python 复制代码
data = {
    "id": {"0": 22501, "1": 22502, "2": 22503, "3": 22504, "4": 22505, "5": 22506, "6": 22507, "7": 22508},
    "age": {"0": 35, "1": 26, "2": 44, "3": 36, "4": 41, "5": 24, "6": 25, "7": 33},
    "nr_employed": {"0": 5205, "1": 4925, "2": 4947, "3": 5203, "4": 4992, "5": 4993, "6": 5155, "7": 5034}
}

# Calculate the number of individuals with age greater than 35
age_greater_than_35 = sum(1 for age in data["age"].values() if age > 35)
age_greater_than_35

可以看出,生成的python代码含有原表格的所有内容

2 只读取表格路径和基础信息:

python 复制代码
import pandas as pd
csv_filename = '/test_short.csv'
query= 'age最大值是多少?'
prompt = f"已知csv文件:{csv_filename}\n\n文件Schema:{pd.read_csv(csv_filename).columns}\n\n问题:{query}\n\n请生成Python代码解决这个问题,将结果赋值给变量result\n\ndPython代码:\n\n"

生成的代码:

python 复制代码
import pandas as pd

# 读取csv文件
data = pd.read_csv('/test_short.csv')
# 找到age列的最大值
result = data['age'].max()
print(result)

可以看出,生成的python代码只有当真正执行的时候才会从文件路径中读取表格内容

这两种方法的优缺点总结如下: 1.读取完整的表格内容:简单,但是受模型长度限制不能读取太大的表格 2.只读取表格路径和基础信息:需要一个目录用于保存文件,需要给出列的信息,模型根据这些信息生成代码,可以支持非常大的表格

三 运行代码字符串

在python脚本中动态执行python代码,可以用eval或者exec函数。一般来说,eval函数只能计算一个表达式的值,而exec可以执行复杂的代码,一般是多行的python字符串。

bash 复制代码
exec函数定义如下:
exec(object[, globals[, locals]])

参数说明:
object:必选参数,表示需要被指定的Python代码
globals:可选参数,全局变量,同eval函数
locals:可选参数,局部变量,一般指的是代码中用到的变量,同eval函数

返回值:
exec函数的返回值永远为None.

除了execeval,还可以利用ipython进行代码执行,即用jupyter-notebook的内核来执行代码,这里不赘述。

四 核心模块

如前所述,利用文件路径和信息构建合适的提示词:

python 复制代码
import pandas as pd
csv_filename = '/test_short.csv'
query= 'age最大值是多少?'
prompt = f"已知csv文件:{csv_filename}\n\n文件Schema:{pd.read_csv(csv_filename).columns}\n\n问题:{query}\n\n请生成Python代码解决这个问题,将结果赋值给变量result\n\ndPython代码:\n\n"
response, history = model.chat(tokenizer, prompt, history=[])
print(response)

模型的回答如下:

bash 复制代码
首先,我们需要导入pandas库,然后读取csv文件。接下来,我们可以使用pandas的`max()`函数来找到age列的最大值,并将结果赋值给变量result。以下是完整的代码:

import pandas as pd
# 读取csv文件
data = pd.read_csv('/test_short.csv')
# 找到age列的最大值
result = data['age'].max()
print(result)

这段代码将输出age列的最大值。

接下来用正则提取出模型回答中的python代码部分:

python 复制代码
import re
pat = re.compile(r'```python\n([\s\S]+)\n```')
code_string = pat.findall(response)[0]
print(code_string)

提取出来的python代码字符串如下:

bash 复制代码
"import pandas as pd\n\n# 读取csv文件\ndata = pd.read_csv('/test_short.csv')\n\n# 找到age列的最大值\nresult = data['age'].max()\n\nprint(result)"

利用exec执行代码,并且把结果赋给大模型。注意这时候需要设置参数role='observation'

python 复制代码
loc = {}
exec(code_string, None, loc)
response, history = model.chat(tokenizer, f"result:{loc['result']}", history=history, role='observation')
print(response)
bash 复制代码
根据提供的CSV文件,age列的最大值是44。

五 效果展示

Gradio库有dataframe组件,可以用来显示上传表格的内容,实现预览功能。此外,上传的文档会存放在一个临时的路径下,当会话断开后则删除,不会保存到本地中,不占用本地存储。

表格分析助手搭建效果如图:

完整代码如下:

python 复制代码
from transformers import AutoTokenizer, AutoModel
import gradio as gr
from pathlib import Path
import re
import pandas as pd

# 加载模型
model = AutoModel.from_pretrained("/chatglm3-6b", trust_remote_code=True).to("mps").eval()
tokenizer = AutoTokenizer.from_pretrained("/chatglm3-6b", trust_remote_code=True)



def read_tbl_2_pd(filename):
    if filename.endswith('.csv'):
        df = pd.read_csv(filename)
    elif filename.endswith('.xlsx') or filename.endswith('.xls'):
        pd.read_excel(filename, sheet_name=None)
    return df

def fn_analysis_table(query, robot,  filename):


    if robot is None:
        robot = []
    robot.append([query, " "])
    
    if filename.endswith('.csv'):
        schema = pd.read_csv(filename).columns
    elif filename.endswith('.xlsx') or filename.endswith('.xls'):
        schema = pd.read_excel(filename, sheet_name=None)['Sheet1'].columns
    
    
    chat_history = []
    
    prompt = f"已知文件:{filename}\n\n文件Schema:{schema}\n\n问题:{query}\n\n请利用Pandas生成Python代码解决这个问题,最后的结果务必赋值给变量result\n\ndPython代码:\n\n"
    
    print(prompt)
    
    response, history = model.chat(tokenizer, prompt, history=[])
    print(response)
    
    pat = re.compile(r'```python\n([\s\S]+)\n```')
    code_string = pat.findall(response)[0]
    print(code_string)


    loc = {}
    exec(code_string, None, loc)
    result = loc['result']    
    
    response, history = model.chat(tokenizer, f'result:{result}', history=history, role='observation')
    
    robot[-1] = [query, response]
    yield robot



with gr.Blocks() as app:

    with gr.Tab("与CSV对话"):

         with gr.Row():

            with gr.Column(scale=1):
                upload = gr.File(label="上传csv文档")
                df = gr.Dataframe()

                chatbot = gr.Chatbot(
                    label="ChatBot",
                    height=500,
                    bubble_full_width=False
                )
                instruction = gr.Textbox(lines=2, label="请输入您的问题", placeholder="问题...", max_lines=2)
                with gr.Row():
                    submit = gr.Button("提交", size="sm",interactive=True)
                    clean = gr.Button("清除", size="sm")
   
             
            upload.upload(fn=read_tbl_2_pd, inputs=[upload], outputs=[df], queue=False)
            
            submit.click(
                fn=fn_analysis_table,
                inputs=[instruction, chatbot,  upload],
                outputs=[chatbot],
                queue=True
                
            )
            clean.click(fn=lambda: None, inputs=None, outputs=chatbot, queue=False)
app.queue(max_size=3)
app.launch(share=False)
相关推荐
海棠AI实验室36 分钟前
AI的进阶之路:从机器学习到深度学习的演变(一)
人工智能·深度学习·机器学习
XH华38 分钟前
初识C语言之二维数组(下)
c语言·算法
南宫生1 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_1 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
IT古董1 小时前
【机器学习】机器学习的基本分类-强化学习-策略梯度(Policy Gradient,PG)
人工智能·机器学习·分类
落魄君子1 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
菜鸡中的奋斗鸡→挣扎鸡2 小时前
滑动窗口 + 算法复习
数据结构·算法
睡觉狂魔er2 小时前
自动驾驶控制与规划——Project 3: LQR车辆横向控制
人工智能·机器学习·自动驾驶
Lenyiin2 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
郭wes代码2 小时前
Cmd命令大全(万字详细版)
python·算法·小程序