遍历DataFrame填充嵌套字典统计公司活跃实例数未达预期
问题排查与解决方案:统计公司每年活跃实例数
核心问题分析
你的代码输出不符合预期,主要源于三个关键错误:
- 嵌套字典初始化引用重复:若多个年份共用同一个公司字典对象,修改任意年份的计数会同步影响所有年份,导致各年份结果完全一致。
- 未实现逐年累计逻辑:现有代码仅计算单年份的增减量,没有基于上一年的活跃数累加当年变化,无法得到累计活跃实例数。
- 未处理
NaT终止日期:终止日期为空的记录未被跳过,可能引发错误或不正确的计数逻辑。
解决方案
方法一:基于Pandas的高效实现(推荐)
利用Pandas的事件分组与累计计算,避免嵌套循环的低效与错误:
import pandas as pd # 1. 加载并预处理数据 data = [ [106072, 84028, 'COMPANY A', '7/1/04', '4/9/69'], [106084, 84028, 'COMPANY A', '12/1/85', '8/20/69'], [106094, 84028, 'COMPANY A', '12/1/70', '10/3/69'], [106115, 84028, 'COMPANY B', '12/1/85', '1/7/70'], [106133, 91108, 'COMPANY B', '2/4/86', '3/6/70'], [106133, 91108, 'COMPANY C', pd.NaT, '3/6/91'], [106133, 91108, 'COMPANY C', pd.NaT, '3/6/91'], ] df = pd.DataFrame(data, columns=['ID', 'CO_Num', 'CO_Name', 'Termination_Date', 'Effective_Date']) # 转换日期列(处理两位年份) df['Effective_Date'] = pd.to_datetime(df['Effective_Date'], format='%m/%d/%y') df['Termination_Date'] = pd.to_datetime(df['Termination_Date'], format='%m/%d/%y', errors='coerce') # 2. 生成生效/终止事件 # 生效事件:+1 events_eff = df[['CO_Name', 'Effective_Date']].copy() events_eff['year'] = events_eff['Effective_Date'].dt.year events_eff['change'] = 1 # 终止事件:-1(仅处理非空终止日期) events_term = df[df['Termination_Date'].notna()][['CO_Name', 'Termination_Date']].copy() events_term['year'] = events_term['Termination_Date'].dt.year events_term['change'] = -1 # 3. 合并事件并计算年度变化量 all_events = pd.concat([events_eff, events_term]) yearly_changes = all_events.groupby(['CO_Name', 'year'])['change'].sum().unstack(fill_value=0) # 4. 重索引目标年份并计算累计活跃数 target_years = range(1969, 1972) yearly_changes = yearly_changes.reindex(columns=target_years, fill_value=0) active_counts = yearly_changes.cumsum(axis=1) # 输出结果 print(active_counts)
执行后将得到预期结果:
1969 1970 1971 CO_Name COMPANY A 3 2 2 COMPANY B 0 2 2 COMPANY C 0 0 0
方法二:修正嵌套循环逻辑
若坚持使用嵌套循环,需修正初始化与累计逻辑:
import pandas as pd # 预处理数据(同方法一) data = [ [106072, 84028, 'COMPANY A', '7/1/04', '4/9/69'], [106084, 84028, 'COMPANY A', '12/1/85', '8/20/69'], [106094, 84028, 'COMPANY A', '12/1/70', '10/3/69'], [106115, 84028, 'COMPANY B', '12/1/85', '1/7/70'], [106133, 91108, 'COMPANY B', '2/4/86', '3/6/70'], [106133, 91108, 'COMPANY C', pd.NaT, '3/6/91'], [106133, 91108, 'COMPANY C', pd.NaT, '3/6/91'], ] df = pd.DataFrame(data, columns=['ID', 'CO_Num', 'CO_Name', 'Termination_Date', 'Effective_Date']) df['Effective_Date'] = pd.to_datetime(df['Effective_Date'], format='%m/%d/%y') df['Termination_Date'] = pd.to_datetime(df['Termination_Date'], format='%m/%d/%y', errors='coerce') # 1. 正确初始化嵌套字典(每个年份的公司字典独立) companies = df['CO_Name'].unique().tolist() target_years = range(1969, 1972) nest_dict = {year: {comp: 0 for comp in companies} for year in target_years} # 2. 初始化累计计数器 current_counts = {comp: 0 for comp in companies} # 3. 逐年计算累计活跃数 for year in sorted(target_years): # 继承上一年的累计数 for comp in companies: nest_dict[year][comp] = current_counts[comp] # 处理当年的生效/终止事件 for _, row in df.iterrows(): comp = row['CO_Name'] # 生效事件 if row['Effective_Date'].dt.year == year: nest_dict[year][comp] += 1 current_counts[comp] += 1 # 终止事件(跳过NaT) if pd.notna(row['Termination_Date']) and row['Termination_Date'].dt.year == year: nest_dict[year][comp] -= 1 current_counts[comp] -= 1 # 转换为DataFrame result_df = pd.DataFrame.from_dict(nest_dict, orient='columns') print(result_df)
关键修正点说明
- 字典初始化:使用字典推导式为每个年份创建独立的公司字典,避免引用重复。
- 累计逻辑:维护
current_counts变量,逐年继承上一年的活跃数,再叠加当年的增减量。 - NaT处理:通过
pd.notna()跳过终止日期为空的记录,避免错误判断。
内容的提问来源于stack exchange,提问作者Stephen
相关产品推荐
相关产品推荐

