使用df.apply与lambda函数为DataFrame批量加列报错求助
解决Pandas批量添加列时的ValueError问题
你的问题出在将apply返回的多值序列直接赋值给多列的方式不正确,Pandas无法自动将每个行返回的数组拆分到对应的列中,导致了ValueError: Must have equal len keys and value when setting with an iterable错误。下面是具体的解决方案和优化建议:
核心问题分析
当你用apply返回一个长度为7的数组(或列表)时,得到的是一个每个元素都是数组的Series。直接将这个Series赋值给7列的话,Pandas会尝试把每个数组当成对应列的单个值,这显然长度不匹配——每一行的数组长度是7,但列数也是7,需要把数组拆分成7个单独的值分别对应7列。
另外,你的get_player_stats函数在无数据时返回的是7个空字符串的元组,而成功时返回的是NumPy数组,类型不一致也可能引发潜在问题,建议统一返回列表类型。
修改后的完整代码
第一步:优化函数返回值和数据获取逻辑(增强健壮性)
import requests import numpy as np import pandas as pd def get_player_stats(player_name): print(player_name) # 请求球员ID resp = requests.get(player_id_api + player_name) if resp.status_code != 200: print('Error {}'.format(resp.status_code)) return ['']*7 result = resp.json() # 安全获取player_id,避免KeyError player_id = result.get('data', [{}])[0].get('pid', None) if not player_id: print('No player ID found') return ['']*7 # 请求球员数据 resp_data = requests.get(player_data_api + str(player_id)) if resp_data.status_code != 200: print('Error {}'.format(resp_data.status_code)) return ['']*7 result_data = resp_data.json() # 安全获取ODIs击球和投球数据 batting_odis = result_data.get('data', {}).get('batting', {}).get('ODIs', {}) bowling_odis = result_data.get('data', {}).get('bowling', {}).get('ODIs', {}) # 检查是否有有效数据 if batting_odis and bowling_odis: total_6s = batting_odis.get('6s', '') total_4s = batting_odis.get('4s', '') average = batting_odis.get('Ave', '') total_innings = batting_odis.get('Inns', '') total_catches = batting_odis.get('Ct', '') total_stumps = batting_odis.get('St', '') total_wickets = bowling_odis.get('Wkts', '') print(average, total_innings, total_4s, total_6s, total_catches, total_stumps, total_wickets) return [average, total_innings, total_4s, total_6s, total_catches, total_stumps, total_wickets] else: print('No data for player') return ['']*7
第二步:正确批量赋值多列
不需要提前循环初始化列,直接将apply的结果转换为DataFrame后赋值:
cols = ['Avg','tot_inns','tot_4s','tot_6s','tot_cts','tot_sts','tot_wkts'] # 方式1:对playerName列直接apply,转换为列表后生成DataFrame(更高效) players_available[cols] = pd.DataFrame( players_available['playerName'].apply(get_player_stats).tolist(), index=players_available.index ) # 方式2:整行apply后用pd.Series拆分 players_available[cols] = players_available.apply(lambda x: get_player_stats(x['playerName']), axis=1).apply(pd.Series)
为什么这样能解决问题?
- 方式1中,
tolist()会把每个行返回的列表转换成一个二维列表,pd.DataFrame会自动将二维列表的每一列映射到你指定的cols中。 - 方式2中,
apply(pd.Series)会把每个行的数组拆分成单独的列,形成一个新的DataFrame,直接赋值给原DataFrame的目标列即可。
额外优化建议
- 添加请求超时:在
requests.get中加入timeout参数,避免请求卡住:requests.get(url, timeout=10) - 缓存API结果:如果同一个球员名称会被多次调用,可以用字典缓存已获取的结果,减少重复请求,提升效率。
- 异步请求:如果数据量很大,考虑用
aiohttp进行异步请求,大幅缩短总耗时。
内容的提问来源于stack exchange,提问作者chink
相关产品推荐
相关产品推荐

