如何在Pandas DataFrame行中查找最后一个索引并按列反向计算差值
Pandas实现:按行计算连续有效块的反向列差值
先把你的需求用代码还原出来,方便大家理解:
原始DataFrame
import pandas as pd import numpy as np df = pd.DataFrame( [ [1, 1, 1, 1, np.nan, np.nan, np.nan], [1, 1, 1, 1, 1, 1, np.nan], [1, 1, 1, 1, np.nan, np.nan, np.nan], [1, 1, np.nan, np.nan, 1, 1, 1] ], index=[1,2,3,4], columns=[20,40,60,80,100,120,140] )
目标输出
20 40 60 80 100 120 140 1 40 20 0 NaN NaN NaN NaN 2 80 60 40 20 0 NaN NaN 3 60 40 20 0 NaN NaN NaN 4 20 0 NaN NaN 40 20 0
核心需求其实是:对每行的每个连续非NaN区域,找到该区域的最后一列的列名,然后区域内每个位置的值 = 最后列名 - 当前列名,非区域位置保持NaN。接下来一步步实现:
步骤1:标记每行的连续有效块
首先得把每行里的连续非NaN区域区分开,给每个块分配唯一ID:
# 先得到非NaN的布尔矩阵 not_nan = df.notna() # 按行计算连续块的ID:遇到NaN或者和前一列状态不同时,ID递增 group_ids = not_nan.cumsum(axis=1) * not_nan
group_ids里,同一个连续有效块的位置会有相同的数字ID,NaN位置则是0,这样我们就能精准定位每个独立的块。
步骤2:获取每个块的最后列名
接下来要针对每个块,找到它的最后一列的列名。这里用transform方法按行处理:
# 把列名转换成和DataFrame同形状的矩阵,方便按位置取值 col_names = np.tile(df.columns, (len(df), 1)) # 对每行的每个分组,取该块最后一列的列名 last_col = group_ids.transform(lambda x: col_names[x.index, x.idxmax()], axis=1)
x.idxmax()会返回当前分组在该行的最后一个非0位置(也就是块的最后一列索引),然后我们从col_names里取出对应的列名,这样每个块内的所有位置都会拿到该块的最后列名。
步骤3:计算差值并保留原始NaN
最后一步很简单,用每个块的最后列名减去当前列名,再用where把原始NaN的位置还原回去:
result = (last_col - df.columns).where(not_nan)
where(not_nan)会只保留原始非NaN位置的计算结果,NaN位置依然是NaN,完美匹配你的目标输出。
完整运行代码
把上面的步骤整合起来,直接运行就能得到结果:
import pandas as pd import numpy as np # 构建原始DataFrame df = pd.DataFrame( [ [1, 1, 1, 1, np.nan, np.nan, np.nan], [1, 1, 1, 1, 1, 1, np.nan], [1, 1, 1, 1, np.nan, np.nan, np.nan], [1, 1, np.nan, np.nan, 1, 1, 1] ], index=[1,2,3,4], columns=[20,40,60,80,100,120,140] ) # 1. 标记连续有效块 not_nan = df.notna() group_ids = not_nan.cumsum(axis=1) * not_nan # 2. 获取每个块的最后列名 col_names = np.tile(df.columns, (len(df), 1)) last_col = group_ids.transform(lambda x: col_names[x.index, x.idxmax()], axis=1) # 3. 计算差值并保留NaN result = (last_col - df.columns).where(not_nan) print(result)
内容的提问来源于stack exchange,提问作者Zmann3000
相关产品推荐
相关产品推荐

