You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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的目标列即可。

额外优化建议

  1. 添加请求超时:在requests.get中加入timeout参数,避免请求卡住:requests.get(url, timeout=10)
  2. 缓存API结果:如果同一个球员名称会被多次调用,可以用字典缓存已获取的结果,减少重复请求,提升效率。
  3. 异步请求:如果数据量很大,考虑用aiohttp进行异步请求,大幅缩短总耗时。

内容的提问来源于stack exchange,提问作者chink

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:10:36