用SQL对 Pandas进行查询,但不返回新的数据框
我正在尝试制作一个小应用程序,可以把CSV和 Parquet文件加载到pandas DataFrame,并让我能够用SQL查询它们并使用matplotlib绘制图表。然而每次查询时,它不会返回一个新的DataFrame。如果我把display_data函数中的if data is none语句删掉,我就会得到如下错误:
AttributeError: 'NoneType' object has no attribute 'columns'
def display_data(dataf):
if dataf is None:
return
tree.delete(*tree.get_children())
tree["columns"] = list(dataf.columns)
tree["show"] = "headings"
for col in dataf.columns:
tree.heading(col, text=col)
tree.column(col, width=150)
for _, row in dataf.iterrows():
tree.insert('', 'end', values=list(row))
def filter_data(sql_input):
global df
df2 = sqldf(sql_input)
display_data(df2)
return df2
entry_value = tk.Entry(frame_filter, width=20)
entry_value.grid(row=0, column=1, padx=5)
#entry_value.insert(0, "Value")
sql = entry_value.get()
btn_filter = tk.Button(frame_filter, text="Filter", command=filter_data(sql))
#btn_filter.pack(pady=10)
btn_filter.grid(row=0, column=2, padx=5)
解决方案
我没法测试你的代码,但我发现了一些可能导致问题的错误。
你在 command=filter_data(sql) 里有一个常见的错误。它需要的是不带 () 的函数名以及参数 - command=filter_data -,当你点击按钮时,它会使用 () 来执行它。最终你可以使用 lambda 来创建函数名 command=lambda:filter_data(sql),但这似乎没什么用,因为还有另一个错误。
第二个问题:sql = entry_value.get() 只在程序启动时获取数据——在用户把数据输入到 Entry 之前。你必须把 entry_value.get() 直接放在 def filter_data(): 里面,这样每次点击按钮时它都会执行——所以当 Entry 中已经有数据时。
还有第三个问题:分配给 command= 的函数在 mainloop() 的某处被执行,它不知道如何处理返回值 - return df2。如果你以后需要这个DataFrame,就必须把它赋值给全局变量。因此你需要 global df2,或者你得使用现有的 global df 并用 df = ... 来替代 df2 = ...。
不过如果你只需要它来运行 display_data(df),那么你就不需要 global。
此刻它只在程序启动时执行 filter_data(sql) 一次——并把该函数的返回结果赋值给 command=。
result = filter_data(sql)
btn_filter = tk.Button(..., command=result)
我不知道你使用的是什么模块 sqldf,但我找到了这个 sqldf ,它显示的是 sqldf.run(...) 而不是 sql(...)。
也许你的 sql(...) 给出了 None,你又有 df2 = None,于是这就产生了 display_data(None),从而导致与 AttributeError: 'NoneType' object has no attribute 'columns 相关的问题。
def filter_data():
global df
sql = entry_value.get() # <-- use directly in function
df = sqldf(sql)
display_data(df)
#return df # useless
# --- main ---
entry_value = tk.Entry(frame_filter, width=20)
entry_value.grid(row=0, column=1, padx=5)
# entry_value.insert(0, "Value")
btn_filter = tk.Button(frame_filter, text="Filter", command=filter_data)
btn_filter.grid(row=0, column=2, padx=5)
与此同时我做了自己的 minimal working code,并测试了模块:sqldf 和 pandasql。
看起来 sqldf.run(sql) 和 pandasql.sqldf(sql) 在 sql 是空字符串时会给出 None。而且因为你把 sql = entry_value.get() 放错位置,sql 总是空字符串,这就导致了 df2 = None。
模块
用于测试的最小可工作代码:
import tkinter as tk
import pandas as pd
import sqldf
#import pandasql
def filter_sql():
sql = filter_entry.get()
if sql:
# new_df = df.query(sql) # e.g. `A>13` (built-in function)
new_df = sqldf.run(sql) # e.g. `SELECT * FROM df WHERE A>13`
#new_df = pandasql.sqldf(sql) # e.g. `SELECT * FROM df WHERE A>13`
else:
new_df = df
output.insert("end", f"Query: {sql}\n")
output.insert("end", new_df.to_string())
output.insert("end", "\n---\n")
# --- main ---
df = pd.DataFrame(
{
"A": [11, 12, 13, 14, 15],
"B": [21, 22, 23, 24, 25],
"C": [31, 32, 33, 34, 35],
"D": [41, 42, 43, 44, 45],
}
)
root = tk.Tk()
filter_entry = tk.Entry(root)
filter_entry.pack()
button = tk.Button(root, text="Filter", command=filter_sql)
button.pack()
output = tk.Text()
output.pack()
root.mainloop()
