如果一行至少包含两个非 NaN 值,则将该行拆分为两个单独的行

Sla*_*wek 6 python dataframe pandas data-science

我正在尝试将 datafarame 转换为所需的输出格式,并满足下面提到的要求。


提供的要求:

  • 每行只能保留一个非 Nan 值(Trh1Trh2除外)
  • 出于性能原因,我想避免迭代每一行的方法。
  • 我只包含了四列,例如在实际场景中还有更多列可以分享

例子

输入

指数 模式 柱子 Trh1 Trh2 Trh3 Trh4
0 架构_1 列_1 0.01
1 架构_2 列_2 0.02 0.03
2 架构_3 列_3 0.03 0.04 0.05
3 架构_4 列_4 0.06 0.07

预期输出

指数 模式 柱子 Trh1 Trh2 Trh3 Trh4
0 架构_1 列_1 0.01
1 架构_2 列_2 0.02 0.03
2 架构_3 列_3 0.03 0.04
3 架构_3 列_3 0.05
4 架构_4 列_4 0.06
5 架构_4 列_4 0.07

我探索了以下方法:根据条件 pandas 将行分成 2 行。但是,此方法仅适用于两列中没有 Nan 值的情况下分割行。

moz*_*way 6

处理跳跃

cols = ['Index', 'Schema', 'Column', 'Trh1', 'Trh2']
special = ['Trh1', 'Trh2']
others = list(df.columns.difference(cols))


out = (df
   .assign(init=lambda d: d[others].isna().all(axis=1))
   [cols+['init']+others]
   .set_index(cols).stack().to_frame()
   .assign(n=lambda d: d.groupby(level=range(df.index.ndim)).cumcount())
   .set_index('n', append=True)[0]
   .unstack(-2)
   .reset_index()
)

out.loc[out['init'].isna(), special] = np.nan

out = out.drop(columns=['n', 'init'])

out = out.dropna(subset=special+others, how='all')
Run Code Online (Sandbox Code Playgroud)

输出:

 Index    Schema Column  Trh1  Trh2  Trh3  Trh4
0      0  schema_1  col_1   NaN  0.01   NaN   NaN
1      1  schema_2  col_2  0.02  0.03   NaN   NaN
2      2  schema_3  col_3  0.03  0.04   NaN   NaN
3      2  schema_3  col_3   NaN   NaN  0.05   NaN
5      3  schema_4  col_4   NaN   NaN  0.06   NaN
6      3  schema_4  col_4   NaN   NaN   NaN  0.07
Run Code Online (Sandbox Code Playgroud)

原答案

您可以将重塑与重复数据删除结合使用,使用stack/ unstack

cols = ['Index', 'Schema', 'Column', 'Trh1', 'Trh2']

out = (df
   # stack and remove NaNs
   .set_index(cols).stack().to_frame()
   # deduplicate
   .assign(n=lambda d: d.groupby(level=range(df.index.ndim)).cumcount())
   # reshape to original shape
   .set_index('n', append=True)[0]
   .unstack(-2)
   # cleanup
   .reset_index()
   .drop(columns='n')
)

# add rows that were dropped because having no value
out = pd.concat([df[df[df.columns.difference(cols)].isna().all(axis=1)], out],
                ignore_index=True).sort_values(by='Index') # optional

Run Code Online (Sandbox Code Playgroud)

注意。这不需要在初始cols.

或者使用melt,这可能会占用更多内存,但如果有重复项,也会更健壮:

cols = ['Index', 'Schema', 'Column', 'Trh1', 'Trh2']

out = (df.melt(cols)
       # drop NAs, except first row per group
       .loc[lambda d: d['value'].notna() | ~d[cols].duplicated()]
       # de-duplicate
       .assign(n=lambda d: d.groupby(cols, dropna=False).cumcount())
       # reshape
       .pivot(index=cols+['n'], columns='variable', values='value')
       # cleanup
       .reset_index().rename_axis(index=None, columns=None)
      )
Run Code Online (Sandbox Code Playgroud)

输出:

   Index    Schema Column  Trh1  Trh2  Trh3  Trh4
0      0  schema_1  col_1   NaN  0.01   NaN   NaN
1      1  schema_2  col_2  0.02  0.03   NaN   NaN
2      2  schema_3  col_3  0.03  0.04  0.05   NaN
3      3  schema_4  col_4   NaN   NaN  0.06   NaN
4      3  schema_4  col_4   NaN   NaN   NaN  0.07
Run Code Online (Sandbox Code Playgroud)