原文:
towardsdatascience.com/process-pandas-dataframes-with-a-large-language-model-8362468aca47
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/c5683923ea6dc32244734ee781231858.png
Pandas,图片由
Stone
使用不同的大型语言模型(LLMs)很容易。
但我们能否无缝地将
LLM
使用该模型?实际上,我们可以,在这篇文章中,我将展示三种不同的方法来实现这一点。
像往常一样,文章中使用的所有组件都是免费的。
让我们开始吧!
1.Pandas
数据帧的问题。
作为一个玩具示例,我创建了一个包含所有欧盟国家和它们人口的小数据帧:
importpandasaspddf=pd.DataFrame({"Country":['Austria','Belgium','Bulgaria','Croatia','Cyprus','Czech
Republic','Denmark','Estonia','Finland','France','Germany','Greece','Hungary','Iceland','Ireland','Italy','Latvia','Liechtenstein','Lithuania','Luxembourg','Malta','Monaco','Montenegro','Ne***rlands','Norway','Poland','Portugal','Romania','Serbia','Slovakia','Slovenia','Spain','Sweden','Switzerland'],"Population":[8_205000,10_403000,7_148785,4_491000,1_102677,10_476000,5_484000,1_291170,5_244000,64_768389,82_369000,11_000000,9_930000,308910,4_622917,58_145000,2_217969,35000,3_565000,497538,403000,32965,666730,16_645000,4_907000,38_500000,10_676000,21_959278,7_344847,5_455000,2_007000,46_505963,9_045000,7_581000]})df.to_csv('data.csv',index=False)
在使用
Pandas
实例:
frompandasai.llm.local_llmimportLocalLLMfrompandasai.llmimportOpenAI#Local
LLM
pandas_llm=LocalLLM(api_base="http://localhost:8000/v1")OR#OpenAI
pandas_llm=OpenAI(api_token="...")在这里,我们有两种选择。
那些拥有
OpenAI
服务器是它的一个好选择)。
对于我的测试,我将在我的电脑上本地运行
LLM
提出一个关于数据帧的问题。
首先,让我们尝试简单的方法:
importloggingfrompandasaiimportSmartDataframelogging.basicConfig(level=logging.DEBUG,format="[%(levelname)s]
[%(asctime)-15s]
%(message)s")sdf=SmartDataframe(df,config={"llm":pandas_llm})sdf.chat("Find
country
population.")
在这里,我启用了日志记录来查看“内部”发生了什么。
日志输出显示,库创建了一个相当复杂的提示,如下所示:
{'messages':[{'role':'user','content':'Findcountry
population.'
},{'role':'user','content':'dfs[0]:ndescription:nullntype:pd.DataFramen
fields:n-name:Countryntype:objectn
samples:n-Austrian-Serbian-Switzerlandn-name:Populationntype:int64n
samples:n-1102677n-10676000n-4907000nnnnn
Update
声明结果变量:ntype(可能的值“string”,“number”,“dataframe”,“plot”)。
示例:{"type":"string","value":f"The
highest
is{highest_salary}."}或{"type":"number","value":125}或{"type":"dataframe","value":pd.DataFrame({...})}或{"type":"plot","value":"temp_chart.png"}nn```pynnnn###
QUERYn
`dfs:list[pd.DataFrame]`isalready
***
end,declare"result"variableasa
dictionary
oftypeandvalue.nn
you
chart,use"matplotlib"forcharts,saveaspng.nnn
Generate
codeandreturnfull
updated
code:'}],'model':''}}
如我们所见,提示要求模型生成
Python
代码,并且它还向模型提供了数据帧的前几个样本。
我用我的本地
LLM
运行代码,然后…什么也没发生。
我只得到了这个:“响应中没有找到代码”。
这个提示与“真实”的
OpenAI
模型来说太复杂了。
让我们使用高级方法,并通过
Agent
类指定更多参数:
agent=Agent([df],config={"llm":pandas_llm},description="<s>[INST]CreatePython
codeandhelpuser
answer
question.[/INST]")query="""You
have
Pandas."""code=agent.generate_code(query)agent.execute_code(code)
这里,我创建了一个更好的提示,以帮助模型理解要做什么。
首先,结果是
13B
模型没有理解到本地变量“dfs”(在原始提示中提到)的概念,但它能够从文件中加载
dataframe。
其次,我指定了提示中的“INST”部分,这是“CodeLlama-13B-Instruct”模型所必需的。
实际上,生成的提示看起来是这样的:
{'messages':[{'role':'system','content':'<s>[INST]CreatePython
question.[/INST]'
},{'role':'user','content':'Youhave
dataframewithfields"Country"and"Population",savedin"data.csv".Find
***
Pandas.'},{'role':'user','content':'dfs[0]:n
name......'model':''}}
正如我们所见,成功添加了带有“INST”文本的第一个“系统”部分。
运行代码后,我得到了以下结果:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/f116a68d33c5f9bce9960cf66d62b5ff.png
Python
笔记本中的输出,图片由作者提供
为了做到这一点,CodeLlama
LLM
的随机性,你的结果可能不同):
importmatplotlib.pyplotasplt#load
data
df=pd.read_csv("data.csv")#create
countries
df_top10=df.sort_values("Population",ascending=False).head(10)ax=df_top10.plot(kind="barh",x="Country",y="Population",figsize=(10,6))ax.set_title("TopMost
Countries"
)ax.set_xlabel("Country")ax.set_ylabel("Population")plt.show()有趣的是,我只要求模型“找到人口最多的国家”,并没有要求绘制图表。
显然,“matplotlib”在提示中提到了,模型将其作为“提示”来了解该做什么。
这个提示(见上面的例子)可能对于
13B
模型来说仍然太复杂。
但从技术上讲,模型正确地回答了我的问题,我们可以看到这种方法是有效的。
2.LangChain
Dataframe代理,可以执行类似的任务。
首先,我们需要创建一个语言模型实例。
和之前一样,我将使用本地的
CodeLlama
模型进行请求):
fromlangchain_openaiimportOpenAIasLangChainOpenAIllm=LangChainOpenAI(openai_api_key="12345678",openai_api_base="http://localhost:8000/v1",verbose=True)
现在我们已经准备好创建代理:
fromlangchain.agents.agent_typesimportAgentTypefromlangchain_experimental.agents.agent_toolkitsimportcreate_pandas_dataframe_agentfromlangchain_core.callbacksimportStdOutCallbackHandler,BaseCallbackManagerprefix="""<s>[INST]You
are
tool:"""handlers=[StdOutCallbackHandler()]callback_manager=BaseCallbackManager(handlers)agent=create_pandas_dataframe_agent(llm,df,verbose=True,agent_executor_kwargs={"handle_parsing_errors":True},agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION,callback_manager=callback_manager,prefix=prefix)
这里,我也添加了“INST”部分,这是
CodeLlama
所必需的。
一切准备就绪,是时候向模型提出关于我们的
dataframe
的问题了:
agent.invoke("Write***
countries."
)我们可以启用日志记录并看到
LangChain
生成了以下提示:
<s>[INST]Youare
codeandhelpuser
answer
question.[/INST].You
have
tool:python_repl_ast:A
Python
outputisabbreviated-make
sure
followingformat:Question:***inputquestion
you
of[python_repl_ast]Action
Input:***inputto
***
action...(this
Thought/Action/Action
can
times)Thought:I
now
originalinputquestion
result
`print(df.head())`:||Country|Population||---:|:----------|-------------:||0|Austria|8205000||1|Belgium|10403000||2|Bulgaria|7148785||3|Croatia|4491000||4|Cyprus|1102677|Begin!
***
ofallcountries.
"create_pandas_dataframe_agent"方法使用了PythonAstREPLTool,从提示中我们可以看到,我们要求模型生成
Python
代码并将其放入“动作输入”部分。
提示本身相当复杂,但令人惊讶的是,13B
模型能够理解它并生成这个答案:
#>Action:
python_repl_ast
#>Action
df['Population'].sum()
463034138尽管如此,对于
13B
模型来说,这个提示仍然很复杂,当我多次运行代码时,有时答案是不正确的:
#>Thought:
`df['Population'].sum()`
calculate
df['Population'].sum()
Unknownaction
有趣的是,我也尝试了一个
CodeLlama
模型,但它由于一个简单的原因而无法工作:提示中写道“_action:
要采取的操作,应该是[python_repl*ast]中的一个。
”模型直接使用了这个例子并给出了这个答案:
Thought:Ineed
tosumupallpopulation
dataframe.Action:[python_repl_ast]Action
Input:df['Population'].sum()
在我看来,答案是好的,模型也完成了它被要求做的事情,但
LangChain
库无法解析“[python_repl_ast]”字符串,它只期望“python_repl_ast”不带括号。
无论如何,LangChain
OpenAI
进行了优化,而与其他模型的结果则无法保证。
3.
文本处理
在前两个例子中,我们测试了使用代理生成
Python
擅长的任务——自然语言处理(NLP)。
作为第二个玩具示例,让我们创建一个包含“失物招领”部门商品清单的数据框:
df_goods=pd.DataFrame({"Item":["Toshibalaptop"
,"iPhone12"
,"iPhone14"
,"Oldbicycle"
,"PublicTransport
card"
,"Pairgloves"
,"KidsHelmet"
,"SamsungSmartphone"
,"iPhone14"
,"Cap","Shawl"],})display(df_goods)在
Jupyter
中,它看起来像这样:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/2a6617a5000f4e0a794afdab0895ca49.png
样本数据框,图片由作者提供
假设我们想使用
Pandas
任务。
让我们将所有项目分为四个类别:“电子产品”、“服装”、“文件”和“其他”,并将结果放入一个新列中。
一种“传统”的方法是编写一些规则或正则表达式,但我们可以直接让
LLM
来完成这项工作!
首先,让我们加载一个
Llama
模型:
fromllama_cppimportLlamallm=Llama(model_path="llama-2-13b-chat.Q4_K_M.gguf",n_gpu_layers=-1,n_ctx=2048,verbose=True)
这是一个对模型的请求:
question="Youare
categories:Electronics,Clo***s,Documents,andO***r.Write
item."
现在,让我们创建一个提示:
prompt=f"""<s>[INST]{question}Iwill
list:
{items_csv}.Now
[/INST]"""
提示尽可能通用,这样我们就可以为不同的问题重用它。
结果还显示,即使是免费的
13B
格式有很好的理解。
在我的情况下,它产生了以下输出:
Sure!Hereis***listof
corresponding
categories:{"Toshiba
laptop":"Electronics","iPhone
12":"Electronics","iPhone
14":"Electronics","Old
bicycle":"O***r","Public
Transport
card":"Documents","Pair
gloves":"Clo***s","Kids
Helmet":"Clo***s","Samsung
Smartphone":"Electronics","Cap":"Clo***s","Shawl":"Clo***s"}Let
need
anythingelse!
输出看起来足够好,我们准备用它来制作一个通用的解决方案。
首先,让我们创建一个辅助方法来从文本中提取
JSON:
importjsonimportredefextract_json(output_str:str)->dict:"""Extract
"""
try:json_str=re.search(r"{(?:[^{}])*}",output_str).group(0)data=json.loads(json_str)returndataexceptExceptionasexp:print(f"Cannotextract
from
{output_str}:{exp}")returnNone现在,让我们创建一个生成提示的方法:
defmake_prompt(question:str,items:List)->str:"""Generate
"""
items_csv=",".join(items)returnf"""<s>[INST]{question}Iwill
list:
{items_csv}.Now
[/INST]"""
最后,我们准备好使用
LLM
列:
defllm_map(model:Any,df_column:pd.Series,question:str,batch_size:int=64)->pd.Series:"""Create
"""
items=df_column.unique()chunks=[items[i*batch_size:(i+1)*batch_size]foriinrange((len(items)+batch_size-1)//batch_size)]results={}forchunkinchunks:prompt=make_prompt(question,chunk)res=model(prompt,max_tokens=1024,stream=False)output_text=res["choices"][0]["text"]data=extract_json(output_text)ifdataisnotNone:results.update(data)returndf_column.map(lambdaitem_name:results.get(item_name,None))在这里,我首先创建了一个列中唯一项目的列表——没有必要多次询问模型相同的问题。
我还添加了一个“batch_size”参数——如果数据框太大,我们可以分块发送数据。
结果还显示,有时
LLM
JSON。
然后,“extract_json”函数返回
None,因此重复提问
次是有意义的(出于清晰度考虑,这没有在代码中实现)。
现在,我们准备好在单行代码中应用“映射”到
Pandas
数据框:
question="Youare
categories:Electronics,Clo***s,Documents,andO***r.Write
df_goods["Category"]=llm_map(llm,df_goods["Item"],question)display(df_goods)
输出看起来很好且准确:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/ef7834727c1211b551e3e543281b4e23.png
处理结果,图片由作者提供
我们的解决方案是通用的,我们可以用
“llm_map”
来处理不同的问题:
question="Youare
item."
df_goods["Price"]=llm_map(llm,df_goods["Item"],question)display(df_goods)输出看起来像这样:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/b6bae10f0b43e3fcd6a45793f62788ac.png
处理结果,图片由作者提供
自然地,使用
13B
模型的处理相对较慢。
读者可以将“llm_map”方法中的本地模型调用更改为
OpenAI
API;这将更快,但不是免费的。
“batch_size”也是一个有趣的参数。
从理论上讲,像
个标记)。
这允许我们在单个请求中处理大量项目,但我有一种感觉,至少对于
13B
模型,长提示的结果不太准确。
结论
在这篇文章中,我们能够以两种不同的方式使用大型语言模型处理
Pandas
数据框。
第一种方法利用了工具和代理的概念:我们要求模型编写
Python
代码,然后使用本地代理执行此代码。
这种方法对数据分析有益。
首先,正如我们所知,LLMs
在数学方面并不擅长。
例如,我们可以要求模型计算平均值,但结果很可能是错误的。
其次,大量数据集根本无法适应
LLM
代码消除了这些缺点。
它还允许我们稍后重用生成的代码(每次调用模型进行相同的处理都会很慢且成本高昂)。
第二种方法利用了
LLM
处理文本的“自然”能力。
当我们需要处理大量非结构化文本数据时,这可能是有益的。
作为最后的思考,我喜欢软件库与
LLMs(大型语言模型)“无缝”集成的想法——我可以在
IDE
中直接尝试不同的问题,并查看代码和处理的直接结果。
唉,本地模型通常运行缓慢(在我的电脑上,使用
34B
分钟),而且结果并不总是完美的。
但未来,这样的
助手肯定将是开发人员和数据分析师的有力工具。
感谢阅读。
如果您喜欢这个故事,请随意订阅Medium,您将收到我新文章发布的通知,以及访问其他作者数千篇文章的完整权限。
您也可以通过LinkedIn与我建立联系。
如果您想获取这篇和其他文章的完整源代码,请随意访问我的Patreon
页面。
对使用语言模型和自然语言处理感兴趣的人也可以阅读其他文章:
GPT
模型:它是如何工作的?
16
位、8
位浮点格式:它是如何工作的?
一个周末
项目(第一部分):在
Raspberry
项目(第二部分):在
Raspberry
项目(第三部分):为视力受损人士制作视觉助手


