Pandas/Numpy:观测时长转每日二进制标识及代码报错修复
问题:将观测持续天数转换为每日二进制状态标识
原始DataFrame(df_obs)以日期为索引,包含观测持续天数列:
Date duration 2012-01-01 3 2013-04-01 26 2014-05-01 14 2016-01-01 297
需求:转换为每日观测状态的二进制标识,示例输出:
Date Obs 2012-01-01 1 2012-01-02 1 2012-01-03 1 2012-01-04 0 2012-01-05 0
原代码及报错
原脚本:
Time = pd.DataFrame(pd.date_range(start='01/01/2012', end='31/12/2019')) obs = np.zeros() for d in df_obs.itertuples(): ilong = np.argwhere(Time.date == d.Index)[0][0] obs[ilong:ilong+d.duration] = 1
报错信息:
ValueError: Length of values (1) does not match length of index (2921)
错误原因
np.zeros()未指定长度,默认生成0维空数组,无法执行切片赋值操作Time是默认列名为0的DataFrame,不存在date属性,直接调用Time.date会引发错误- 循环中用
argwhere查找索引的方式效率低,且易出现日期匹配失败的情况
修复方案
方案1:修复原循环逻辑
import pandas as pd import numpy as np # 构造示例df_obs(实际使用时替换为你的数据) df_obs = pd.DataFrame( {'duration': [3, 26, 14, 297]}, index=pd.to_datetime(['2012-01-01', '2013-04-01', '2014-05-01', '2016-01-01']) ) # 创建完整日期序列的DataFrame,并重命名列便于操作 Time = pd.DataFrame(pd.date_range(start='2012-01-01', end='2019-12-31'), columns=['Date']) # 初始化与日期序列长度一致的全0数组 obs = np.zeros(len(Time), dtype=int) for d in df_obs.itertuples(): # 精准定位起始日期在Time中的索引 ilong = Time[Time['Date'] == d.Index].index[0] # 为对应日期范围赋值为1 obs[ilong:ilong + d.duration] = 1 # 将结果合并到Time中 Time['Obs'] = obs print(Time.head())
方案2:高效向量化实现(推荐)
避免循环查找索引,直接通过日期范围赋值,效率更高:
import pandas as pd import numpy as np df_obs = pd.DataFrame( {'duration': [3, 26, 14, 297]}, index=pd.to_datetime(['2012-01-01', '2013-04-01', '2014-05-01', '2016-01-01']) ) # 创建完整的日期序列作为索引 full_dates = pd.date_range(start='2012-01-01', end='2019-12-31', name='Date') # 初始化全0的观测状态Series obs_series = pd.Series(0, index=full_dates, name='Obs') for start_date, duration in df_obs['duration'].items(): # 计算观测结束日期(包含起始日,所以减1) end_date = start_date + pd.Timedelta(days=duration - 1) # 直接为日期范围内的状态赋值为1 obs_series.loc[start_date:end_date] = 1 # 转换为需求的DataFrame格式 result = obs_series.reset_index() print(result.head())
内容的提问来源于stack exchange,提问作者SHV_la
相关产品推荐
相关产品推荐

