如何移除Pandas DataFrame每行最后两个非NaN列并单独存储
处理Pandas DataFrame每行最后两个非NaN列的高效方案
我有一个包含两千多行、近一千列的Pandas DataFrame,每行的有效数据列数不同,未使用的列填充为NaN。示例如下:
0 1 2 3 4 5 6 7 A Hip 22 Hop Hat 3 NaN NaN NaN B Gig 45 Get Gik 4 Goo 5 NaN C Lip 34 Lop 2 NaN NaN NaN NaN D Fip 56 Fup Fik Fap Fat Fot 6.0
需要移除每行最后两个非NaN列,并将这些列单独存储为一个DataFrame,处理后预期原DF如下:
0 1 2 3 4 5 6 7 A Hip 22 Hop NaN NaN NaN NaN NaN B Gig 45 Get Gik 4 NaN NaN NaN C Lip 34 NaN NaN NaN NaN NaN NaN D Fip 56 Fup Fik Fap Fat NaN NaN
之前尝试转为嵌套列表操作导致程序崩溃,原因是大尺寸数据转列表会占用过多内存,且循环操作效率极低。
高效向量化实现方案
利用Pandas的内置向量化操作,避免手动循环或转列表,大幅提升效率并降低内存占用:
import pandas as pd import numpy as np # 示例数据构造(可替换为你的实际数据) data = { 0: ['Hip', 'Gig', 'Lip', 'Fip'], 1: [22, 45, 34, 56], 2: ['Hop', 'Get', 'Lop', 'Fup'], 3: ['Hat', 'Gik', 2, 'Fik'], 4: [3, 4, np.nan, 'Fap'], 5: [np.nan, 'Goo', np.nan, 'Fat'], 6: [np.nan, 5, np.nan, 'Fot'], 7: [np.nan, np.nan, np.nan, 6.0] } df = pd.DataFrame(data, index=['A','B','C','D']) # 1. 从右往左标记非NaN位置并累积计数 rev_notna = df.notna().iloc[:, ::-1] # 反转列顺序,从右往左处理 rev_cum_count = rev_notna.cumsum(axis=1) # 每行从右往左累积非NaN数量 # 2. 生成掩码:标记每行最后两个非NaN的位置 mask_last_two = rev_cum_count <= 2 mask_last_two = mask_last_two.iloc[:, ::-1] # 反转回原列顺序 # 3. 提取最后两个非NaN列到新DataFrame extract_df = df.where(mask_last_two).stack().unstack() extract_df.columns = ['last_non_nan_1', 'last_non_nan_2'] # 重命名列,方便识别 # 4. 处理原始DataFrame:移除最后两个非NaN列(设为NaN) processed_df = df.where(~mask_last_two) # 输出结果 print("处理后的原始DataFrame:") print(processed_df) print("\n提取的最后两个非NaN列DataFrame:") print(extract_df)
代码说明
- 反转列顺序:将原DF列反转后,从右往左统计非NaN的累积数量,这样每行的最后一个非NaN对应计数1,倒数第二个对应计数2,以此类推。
- 掩码生成:通过
rev_cum_count <=2筛选出每行最后两个非NaN的位置,再反转回原列顺序得到对应掩码。 - 提取与处理:用
where方法快速提取目标值到新DF,同时将原DF中目标位置设为NaN,全程为Pandas内置向量化操作,效率远高于手动循环或转列表。
注意事项
- 如果某行非NaN值不足2个,
extract_df中对应行的列会自动填充NaN,无需额外处理。 - 该方案适用于大尺寸DataFrame(如2000行1000列),不会出现内存溢出问题。
内容的提问来源于stack exchange,提问作者Robert Tuttle
相关产品推荐
相关产品推荐

