在pandas DataFrame的每一行中保留前n个非NaN单元格

时间:2017-11-27 11:44:15

标签: python pandas dataframe nan

我有一个Pandas Dataframe,每行至少有4个非NaN值, 但位于不同的栏目:

Index       Col1     Col2      Col3         Col4     Col5  Col6  Col7  Col8 
1991-12-31  100.000 100.000    100.000     89.123   NaN    NaN   NaN   NaN                     
1992-01-31  98.300  101.530    100.000     NaN      92.342 NaN   NaN   NaN                     
1992-02-29  NaN     100.230    98.713      97.602   NaN    NaN   NaN   NaN                     
1992-03-31  NaN     NaN        102.060     93.473   98.123 NaN   NaN   NaN                     
1992-04-30  NaN     102.205    107.755     94.529   94.529 NaN   NaN   NaN

(我只显示前8列)我想把它变成一个数据框,每行有4列。 这些行应仅包含该日期的前四个(从左到右读取)非NaN值。

编辑:

每一行的顺序都很重要。

2 个答案:

答案 0 :(得分:4)

如果订单不重要,您可以沿第一个轴拨打np.sort

df = df.set_index('Index')   # ignore if `Index` already is the index

pd.DataFrame(np.sort(df.values, axis=1)[:, :4], 
           columns=np.arange(1, 5)).add_prefix('Col')

     Col1     Col2     Col3     Col4
0  89.123  100.000  100.000  100.000
1  92.342   98.300  100.000  101.530
2  97.602   98.713  100.230      NaN
3  93.473   98.123  102.060      NaN
4  94.529   94.529  102.205  107.755

这比我的第二个解决方案快得多,所以如果可以的话,请务必考虑一下。

如果订单有问题,请致电sorted + apply并获取结果的前4列。

df.apply(sorted, key=np.isnan, axis=1).iloc[:, :4]

               Col1     Col2     Col3    Col4
Index                                        
1991-12-31  100.000  100.000  100.000  89.123
1992-01-31   98.300  101.530  100.000  92.342
1992-02-29  100.230   98.713   97.602     NaN
1992-03-31  102.060   93.473   98.123     NaN
1992-04-30  102.205  107.755   94.529  94.529

<强>计时
以下是 我的答案的时间 -

df = pd.concat([df] * 10000, ignore_index=1)

%timeit df.apply(sorted, key=np.isnan, axis=1).iloc[:, :4]
1 loop, best of 3: 8.45 s per loop

pd.DataFrame(np.sort(df.values, axis=1)[:, :4], 
           columns=np.arange(1, 5)).add_prefix('Col')    
100 loops, best of 3: 4.76 ms per loop

答案 1 :(得分:2)

您可以使用:

#if necessary
#df = df.set_index('Index')

df = df.apply(lambda x: pd.Series(x.dropna().values), axis=1).iloc[:, :4]
print (df)
                  0        1        2       3
Index                                        
1991-12-31  100.000  100.000  100.000  89.123
1992-01-31   98.300  101.530  100.000  92.342
1992-02-29  100.230   98.713   97.602     NaN
1992-03-31  102.060   93.473   98.123     NaN
1992-04-30  102.205  107.755   94.529  94.529

为了更好地使用性能numpy - 使用要求每行至少有4个非值:

a = df.values
df = pd.DataFrame(a[~np.isnan(a)].reshape(a.shape[0],-1)[:, :4], index=df.index)

<强>计时

        Index   Col1     Col2     Col3    Col4    Col5  Col6  Col7  Col8
0  1991-12-31  100.0  100.000  100.000  89.123     NaN   NaN   NaN   NaN
1  1992-01-31   98.3  101.530  100.000     NaN  92.342   NaN   NaN   NaN
2  1992-02-29    NaN  100.230   98.713  97.602     NaN   NaN   NaN   1.0
3  1992-03-31    NaN      NaN  102.060  93.473  98.123   NaN   NaN   1.0
4  1992-04-30    NaN  102.205  107.755  94.529  94.529   NaN   NaN   NaN

df = df.set_index('Index')

df = pd.concat([df] * 10000, ignore_index=1)

In [260]: %timeit pd.DataFrame(justify(df.values, invalid_val=np.nan, axis=1, side='left')[:,:4])
100 loops, best of 3: 6.78 ms per loop

In [261]: %%timeit a = df.values
     ...: pd.DataFrame(a[~np.isnan(a)].reshape(a.shape[0],-1)[:, :4], index=df.index)
     ...: 
100 loops, best of 3: 2.11 ms per loop

In [262]: %timeit pd.DataFrame(np.sort(df.values, axis=1)[:, :4], columns=np.arange(1, 5)).add_prefix('Col')
100 loops, best of 3: 5.28 ms per loop

In [263]: %timeit pd.DataFrame(mask_app(df.values)[:,:4])
100 loops, best of 3: 8.68 ms per loop