如何对多列DataFrame按分组行方向移位非NaN值(保留NaN)
问题:DataFrame按列跳过NaN向下移位n行(保留原NaN位置)
我有如下DataFrame:
| A1 | A2 | A3 | A4 | A5 | A6 |
|---|---|---|---|---|---|
| 1 | nan | 3 | 7 | nan | 8 |
| nan | 5 | nan | 11 | 9 | nan |
| 54 | 6 | 84 | 12 | 3 | nan |
| 10 | nan | nan | 16 | nan | 45 |
| 12 | 93 | 13 | 31 | 5 | 91 |
| 73 | nan | 45 | nan | nan | 9 |
希望将整个DataFrame向下移位n行(示例n=2),移位时跳过NaN值但保留原有NaN的位置,期望输出如下:
| A1 | A2 | A3 | A4 | A5 | A6 |
|---|---|---|---|---|---|
| nan | nan | nan | nan | nan | nan |
| nan | nan | nan | nan | nan | nan |
| nan | nan | nan | 7 | nan | nan |
| 1 | nan | nan | 11 | nan | nan |
| 54 | 5 | 3 | 12 | 9 | 8 |
| 10 | nan | 84 | nan | nan | 45 |
我尝试过以下代码,但仅适用于少量列:
df['dummy'] = df.apply(lambda x: 1 if pd.notnull(x[column]) else 0, axis=1) df['dummy2'] = df.groupby(['dummy'])[column].shift(n) df[column] = df.apply(lambda x: x['dummy2'] if x['dummy']==1 else x[column], axis=1)
还生成了dummy DataFrame:
dummy_df = df.applymap(lambda x: 1 if pd.notnull(x) else 0)
但不清楚后续操作。现在需要处理数千列的移位需求,希望用最少循环实现,能否借助dummy_df结合groupby完成?
解决方案
针对数千列的场景,最高效的方式是按列独立处理,利用apply对每列执行跳过NaN的移位逻辑,无需显式循环,也不需要dummy_df或groupby。核心思路是:对每一列,先提取非NaN值进行移位,再将移位后的值放回原列的非NaN位置,保留原始NaN的位置。
实现代码如下:
import pandas as pd import numpy as np # 定义处理单列的函数 def shift_non_nan(column, shift_n): # 提取列中的非NaN值并移位,前面补shift_n个NaN non_nan_vals = column.dropna() shifted_vals = non_nan_vals.shift(shift_n) # 将移位后的值映射回原列,原NaN位置保持NaN return column.where(column.isna(), shifted_vals) # 示例DataFrame data = { 'A1': [1, np.nan, 54, 10, 12, 73], 'A2': [np.nan, 5, 6, np.nan, 93, np.nan], 'A3': [3, np.nan, 84, np.nan, 13, 45], 'A4': [7, 11, 12, 16, 31, np.nan], 'A5': [np.nan, 9, 3, np.nan, 5, np.nan], 'A6': [8, np.nan, np.nan, 45, 91, 9] } df = pd.DataFrame(data) # 执行移位(n=2) n = 2 df_shifted = df.apply(lambda col: shift_non_nan(col, n), axis=0) print(df_shifted)
输出结果与预期一致:
A1 A2 A3 A4 A5 A6 0 NaN NaN NaN NaN NaN NaN 1 NaN NaN NaN NaN NaN NaN 2 NaN NaN NaN 7.0 NaN NaN 3 1.0 NaN NaN 11.0 NaN NaN 4 54.0 5.0 3.0 12.0 9.0 8.0 5 10.0 NaN 84.0 NaN NaN 45.0
为什么这个方法适合数千列?
apply(axis=0)会自动遍历每一列,无需手动写循环,代码简洁。- 每列的处理都是矢量化操作(
dropna、shift、where都是Pandas的高效内置方法),性能远高于逐元素循环。 - 逻辑清晰,直接针对“跳过NaN移位+保留原NaN位置”的需求,避免了dummy_df和groupby的复杂操作。
内容的提问来源于stack exchange,提问作者beginer101
相关产品推荐
相关产品推荐

