如何正确使用np.where处理DataFrame列,修复END DATE格式异常
问题背景
原始DataFrame
| ID | State | TYPE | START DATE | END DATE |
|---|---|---|---|---|
| 863 | MI | Hire | 9/01/23 | |
| 224 | WI | Retire | 9/01/23 | |
| 567 | FL | Transfer | 10/01/23 | 10/31/23 |
| 345 | NC | |||
| 432 | NY |
编写的代码
import datetime import numpy as np import pandas as pd c1 = df["END DATE"].isnull() c2 = df["TYPE"] == "Hire" c3 = df["TYPE"] == "Retire" df = df.copy(deep = True) x = datetime.datetime.strptime("2023" + "-" + "12" + "-" + "31", '%Y-%m-%d').date() df["END DATE"] = np.where(((c2 | c3) & c1) , x, df["END DATE"])
执行后的异常输出
| ID | State | TYPE | START DATE | END DATE | |
|---|---|---|---|---|---|
| 0 | 863 | MI | Hire | 2023-09-01 | 2023-12-31 |
| 1 | 224 | WI | Retire | 2023-09-01 | 2023-12-31 |
| 2 | 567 | FL | Transfer | 2023-10-01 | 1698710400000000000 |
| 3 | 345 | NC | NaN | NaT | None |
| 4 | 432 | NY | NaN | NaT | None |
可以看到Transfer类型的END DATE值出现异常,原本期望保持不变。现提出两个问题:
- 是否可以修复该问题并继续使用np.where,使不满足条件的行保留原END DATE值?
- 如果无法实现,有什么高效的替代方法可以完成需求:当TYPE为Retire或Hire且END DATE为空时,设置任意指定结束日期?
解答
问题1:修复np.where的用法
可以修复。问题根源是类型不匹配:你创建的x是Python原生date对象,而原DataFrame的END DATE列是pandas的datetime64类型,np.where会强制将整列转换为与x兼容的类型,导致原有正常日期被转成时间戳数值。
修复方式:把x转换成pandas的datetime类型,保持和原列类型一致:
# 替换x的创建代码,用pandas Timestamp保持类型匹配 x = pd.Timestamp("2023-12-31")
修改后的完整代码:
import datetime import numpy as np import pandas as pd c1 = df["END DATE"].isnull() c2 = df["TYPE"] == "Hire" c3 = df["TYPE"] == "Retire" df = df.copy(deep=True) x = pd.Timestamp("2023-12-31") df["END DATE"] = np.where(((c2 | c3) & c1), x, df["END DATE"])
执行后,Transfer类型的END DATE会保留原有的2023-10-31日期值,不会被转成时间戳。
问题2:高效替代方法
如果不想用np.where,可以用pandas原生的条件赋值写法,更贴合pandas风格,效率也很高:
方法1:使用loc定位赋值
直接定位符合条件的行进行修改,精准且高效:
df = df.copy(deep=True) # 组合条件:TYPE是Hire/Retire 且 END DATE为空 mask = df["TYPE"].isin(["Hire", "Retire"]) & df["END DATE"].isnull() df.loc[mask, "END DATE"] = pd.Timestamp("2023-12-31")
方法2:使用fillna结合条件
通过生成条件填充列,再对目标行进行填充:
df = df.copy(deep=True) fill_values = pd.Series([pd.Timestamp("2023-12-31")]*len(df), index=df.index) # 仅对符合条件的行填充指定日期 df["END DATE"] = df["END DATE"].fillna(fill_values.where(df["TYPE"].isin(["Hire", "Retire"])))
这两种方法都能精准修改目标行,不会影响其他行的原有值,性能和np.where相当,适合处理大型数据集。
内容的提问来源于stack exchange,提问作者user12715151
相关产品推荐
相关产品推荐

