Python中DataFrame多条件自定义函数应用报错排查与优化
错误原因分析
1. Cannot convert type '<class 'list'>' to date.
创建德国假日对象时,holidays.DE(years=[])传入了空列表,holidays库无法识别空年份列表,导致后续判断date in german_holidays时无法正确转换日期类型,抛出类型转换错误。需要指定数据对应的年份(比如你的数据是2019年,应改为holidays.DE(years=2019))。
2. The truth value of a Series is ambiguous...
- 日期类型问题:如果
df['date']列是字符串类型而非datetime64类型,在进行date + pd.Timedelta(days=1)或日期判断时会隐式转换失败,导致返回Series而非单个布尔值,触发模糊真值错误。 - weekday调用错误:
date.weekday是方法而非属性,正确写法应为date.weekday(),直接使用属性会拿到方法对象,无法和数字(5、6等)做比较,导致逻辑判断异常。
修正后的基础实现
先解决上述错误,调整代码如下:
import pandas as pd import holidays # 1. 正确初始化德国假日,指定年份 german_holidays = holidays.DE(years=2019) # 2. 确保date列是datetime类型 df['date'] = pd.to_datetime(df['date']) def eighteen(date): if date in german_holidays: return 120 elif (date + pd.Timedelta(days=1)) in german_holidays: return 1320 elif date.weekday() == 5: # 改为调用weekday()方法 return 1080 elif date.weekday() == 6: return 120 else: return 1440 def fifteenone(date): if date in german_holidays: return 120 elif (date + pd.Timedelta(days=1)) in german_holidays: return 1320 elif date.weekday() == 4: return 1320 elif date.weekday() == 5: return 0 elif date.weekday() == 6: return 120 else: return 1440 def fifteentwo(date): if date in german_holidays: return 120 elif (date + pd.Timedelta(days=1)) in german_holidays: return 1320 elif date.weekday() == 4: return 1320 elif date.weekday() == 5: return 0 elif date.weekday() == 6: return 0 elif date.weekday() == 0: return 1080 else: return 1440 def twelve(date): if date in german_holidays: return 0 elif date.weekday() == 5: return 0 elif date.weekday() == 6: return 0 else: return 960 def WT(shift, date): if shift == "18-sh": return eighteen(date) elif shift == "15.1-sh": return fifteenone(date) elif shift == "15.2-sh": return fifteentwo(date) elif shift == "12-sh": return twelve(date) df['WT'] = df.apply(lambda row: WT(row['shift'], row['date']), axis=1)
更优的向量化实现方案
apply是逐行遍历,数据量大时效率极低,建议用向量化操作代替,步骤如下:
- 预处理衍生列:
df['date'] = pd.to_datetime(df['date']) # 标记当日是否为德国假日 df['is_holiday'] = df['date'].isin(german_holidays) # 标记次日是否为德国假日 df['next_day_holiday'] = df['date'].shift(-1).isin(german_holidays) # 获取星期几(0=周一,6=周日) df['weekday'] = df['date'].dt.weekday
- 针对不同班次定义规则,用
np.select批量计算:
import numpy as np # 定义18-sh的规则和结果 rules_18sh = [ df['is_holiday'], df['next_day_holiday'], df['weekday'] == 5, df['weekday'] == 6 ] results_18sh = [120, 1320, 1080, 120] # 定义15.1-sh的规则和结果 rules_151sh = [ df['is_holiday'], df['next_day_holiday'], df['weekday'] == 4, df['weekday'] == 5, df['weekday'] == 6 ] results_151sh = [120, 1320, 1320, 0, 120] # 定义15.2-sh的规则和结果 rules_152sh = [ df['is_holiday'], df['next_day_holiday'], df['weekday'] == 4, df['weekday'] == 5, df['weekday'] == 6, df['weekday'] == 0 ] results_152sh = [120, 1320, 1320, 0, 0, 1080] # 定义12-sh的规则和结果 rules_12sh = [ df['is_holiday'], df['weekday'] == 5, df['weekday'] == 6 ] results_12sh = [0, 0, 0] # 按班次批量计算WT df['WT'] = np.where(df['shift'] == '18-sh', np.select(rules_18sh, results_18sh, default=1440), np.where(df['shift'] == '15.1-sh', np.select(rules_151sh, results_151sh, default=1440), np.where(df['shift'] == '15.2-sh', np.select(rules_152sh, results_152sh, default=1440), np.select(rules_12sh, results_12sh, default=960))))
这种方式完全利用pandas和numpy的向量化运算,避免逐行遍历,效率提升显著,且规则更清晰易维护。
内容的提问来源于stack exchange,提问作者user20216792
相关产品推荐
相关产品推荐

