如何在Pandas中无循环按条件复制拆分数据行?
Pandas高效拆分日期区间行(无循环实现)
原始数据集
trade_id start_date end_date factset_entity_id product_id \ 0 100000191 2017 NaT 0711GY-E 99996362 1 100000193 2017 2018 0711GY-E 99996413 2 100000193 2018 2022 0711GY-E 99996413 3 100000193 2022 NaT 0711GY-E 99996413 4 100000231 2016 2018 05NC3S-E 59979529 product_name l6_id multi_assign_flag 0 Fan Milk - FanYogo 5.015152e+11 0.0 1 Fan Milk - FanDango/Frozen FanDango 5.015151e+11 0.0 2 Fan Milk - FanDango 5.015151e+11 0.0 3 Fan Milk - FanDango 5.015151e+11 0.0 4 Ci:z Holdings - Dr. Ci:Labo 5.020102e+11 1.0
需求说明
对start_date与end_date均不为空且不相等的行,拆分成两行:分别将start_date和end_date作为单独的年份值,其余字段保持不变,最终移除原start_date和end_date列,将年份列放在首位。
例如原行:
1 100000193 2017 2018 0711GY-E 99996413 Fan Milk - FanDango/Frozen FanDango 5.015151e+11 0.0
需转换为:
2017 100000193 0711GY-E 99996413 Fan Milk - FanDango/Frozen FanDango 5.015151e+11 0.0 2018 100000193 0711GY-E 99996413 Fan Milk - FanDango/Frozen FanDango 5.015151e+11 0.0
无循环实现方案
方案一:concat拆分法(性能最优,适合大数据集)
通过concat分别处理需要拆分和不需要拆分的行,全程采用矢量化操作,避免循环:
import pandas as pd # 构造示例数据(实际使用时替换为你的数据读取逻辑) data = { 'trade_id': [100000191, 100000193, 100000193, 100000193, 100000231], 'start_date': [2017, 2017, 2018, 2022, 2016], 'end_date': [pd.NaT, 2018, 2022, pd.NaT, 2018], 'factset_entity_id': ['0711GY-E', '0711GY-E', '0711GY-E', '0711GY-E', '05NC3S-E'], 'product_id': [99996362, 99996413, 99996413, 99996413, 59979529], 'product_name': ['Fan Milk - FanYogo', 'Fan Milk - FanDango/Frozen FanDango', 'Fan Milk - FanDango', 'Fan Milk - FanDango', 'Ci:z Holdings - Dr. Ci:Labo'], 'l6_id': [5.015152e+11, 5.015151e+11, 5.015151e+11, 5.015151e+11, 5.020102e+11], 'multi_assign_flag': [0.0, 0.0, 0.0, 0.0, 1.0] } df = pd.DataFrame(data) # 筛选需要拆分的行:end_date不为空且与start_date不等 mask = df['end_date'].notna() & (df['start_date'] != df['end_date']) # 拆分需要处理的行:复制两次,分别将year设为start_date和end_date split_part = pd.concat([ df[mask].assign(year=df['start_date']), df[mask].assign(year=df['end_date']) ]) # 处理不需要拆分的行:直接将year设为start_date non_split_part = df[~mask].assign(year=df['start_date']) # 合并两部分数据,重置索引并调整列顺序 result = pd.concat([split_part, non_split_part]).reset_index(drop=True) result = result[['year', 'trade_id', 'factset_entity_id', 'product_id', 'product_name', 'l6_id', 'multi_assign_flag']] # 输出结果 print(result)
方案二:explode展开法(代码简洁,易理解)
通过为每行生成年份列表,再用explode展开列表实现拆分:
import pandas as pd # 构造示例数据(同上) df = pd.DataFrame(data) # 标记需要拆分的行 mask = df['end_date'].notna() & (df['start_date'] != df['end_date']) # 为每行生成对应的年份列表:需要拆分的行生成[start, end],否则生成[start] df['year'] = df.apply( lambda x: [x['start_date'], x['end_date']] if mask.loc[x.name] else [x['start_date']], axis=1 ) # 展开年份列表,重置索引并调整列顺序 result = df.explode('year').reset_index(drop=True) result = result[['year', 'trade_id', 'factset_entity_id', 'product_id', 'product_name', 'l6_id', 'multi_assign_flag']] # 输出结果 print(result)
最终结果
两种方案都会得到如下格式的输出:
year trade_id factset_entity_id product_id \ 0 2017 100000193 0711GY-E 99996413 1 2018 100000193 0711GY-E 99996413 2 2018 100000193 0711GY-E 99996413 3 2022 100000193 0711GY-E 99996413 4 2016 100000231 05NC3S-E 59979529 5 2018 100000231 05NC3S-E 59979529 6 2017 100000191 0711GY-E 99996362 7 2022 100000193 0711GY-E 99996413 product_name l6_id multi_assign_flag 0 Fan Milk - FanDango/Frozen FanDango 5.015151e+11 0.0 1 Fan Milk - FanDango/Frozen FanDango 5.015151e+11 0.0 2 Fan Milk - FanDango 5.015151e+11 0.0 3 Fan Milk - FanDango 5.015151e+11 0.0 4 Ci:z Holdings - Dr. Ci:Labo 5.020102e+11 1.0 5 Ci:z Holdings - Dr. Ci:Labo 5.020102e+11 1.0 6 Fan Milk - FanYogo 5.015152e+11 0.0 7 Fan Milk - FanDango 5.015151e+11 0.0
内容的提问来源于stack exchange,提问作者R__
相关产品推荐
相关产品推荐

