pandasql传入DataFrame表名参数报错UFuncTypeError的解决咨询
问题分析与解决:pandasql传入DataFrame参数报错
问题背景
处理名为movies的DataFrame,genres列以逗号分隔多类型标签。原函数通过pandasql统计各类型电影数量可正常运行,但新增table参数支持数据子集分析后,调用时出现错误:
UFuncTypeError: ufunc 'add' did not contain a loop with signature matching types (dtype('<U37'), dtype('<U37')) -> dtype('<U37')
原正常代码:
from pandasql import sqldf pysqldf = lambda q: sqldf(q, globals()) def number_in_genre(genres_list): dataframes = [] for genre in genres_list: q = """SELECT COUNT(*) FROM movies WHERE genres LIKE '%""" + genre + """%'""" df = pysqldf(q) df['genre'] = genre dataframes.append(df) return pd.concat(dataframes)
修改后报错代码:
def number_in_genre(genres_list, table): dataframes = [] for genre in genres_list: q = """SELECT COUNT(*) FROM """ + table + """ WHERE genres LIKE '%""" + genre + """%'""" df = pysqldf(q) df['genre'] = genre dataframes.append(df) return pd.concat(dataframes)
错误原因
原函数中FROM后是字符串'movies',pysqldf通过globals()找到全局变量里对应的movies DataFrame。但修改后传入的table参数是DataFrame对象,而非字符串表名,直接将字符串与DataFrame对象拼接时,Python会尝试执行对象间的加法操作,而DataFrame不支持与字符串直接相加,因此触发了numpy的ufunc类型不匹配错误。
解决思路
方案1:将DataFrame注册为临时表名传入
通过给传入的DataFrame分配一个临时变量名,注册到全局命名空间,让pandasql能识别该表名:
from pandasql import sqldf import pandas as pd pysqldf = lambda q: sqldf(q, globals()) def number_in_genre(genres_list, table): # 创建临时表名并注册到全局空间 temp_table = "_temp_movie_subset" globals()[temp_table] = table dataframes = [] for genre in genres_list: # 使用f-string拼接SQL,更简洁安全 q = f"""SELECT COUNT(*) FROM {temp_table} WHERE genres LIKE '%{genre}%'""" df = pysqldf(q) df['genre'] = genre dataframes.append(df) # 清理临时变量,避免污染全局空间 del globals()[temp_table] return pd.concat(dataframes)
调用示例:
# 统计前30条数据中各类型数量 number_in_genre(["Horror", "Comedy"], movies.head(30)) # 统计低成本电影中各类型数量 low_budget_movies = movies.loc[movies['production_budget'] < 2000000] number_in_genre(["Drama", "Action"], low_budget_movies)
方案2:传入表名字符串并确保DataFrame在命名空间中
如果不想用临时表,可将DataFrame赋值给一个变量,传入变量名字符串:
def number_in_genre(genres_list, table_name): dataframes = [] for genre in genres_list: q = f"""SELECT COUNT(*) FROM {table_name} WHERE genres LIKE '%{genre}%'""" df = pysqldf(q) df['genre'] = genre dataframes.append(df) return pd.concat(dataframes)
调用示例:
low_budget_movies = movies.loc[movies['production_budget'] < 2000000] number_in_genre(["Drama", "Action"], "low_budget_movies")
额外优化:避免SQL注入风险(可选)
如果genres_list来自外部输入,直接拼接字符串可能存在SQL注入风险,可改用参数化查询:
def number_in_genre(genres_list, table): temp_table = "_temp_movie_subset" globals()[temp_table] = table dataframes = [] for genre in genres_list: # 使用?作为占位符,传入参数 q = f"""SELECT COUNT(*) FROM {temp_table} WHERE genres LIKE ?""" # 拼接通配符后作为参数传入 df = pysqldf(q, params=(f'%{genre}%',)) df['genre'] = genre dataframes.append(df) del globals()[temp_table] return pd.concat(dataframes)
内容的提问来源于stack exchange,提问作者Roline
相关产品推荐
相关产品推荐

