Python pandas:根据每行的排序值分配标签,不包括NaN

时间:2017-09-03 10:48:35

标签: python pandas sorting numpy

我想根据每行的排序值列表中的位置(不包括NaN)来分配每个值的位置。我只是无法弄清楚如何用pandas以优雅的方式做到这一点。

我认为在一个例子中解释更容易:

                A         B        C           D
Date                                              
2002-02-28 -0.051272 -0.005851 -0.012669       NaN
2002-03-29  0.103416  0.050121  0.050203       0.5
2002-04-30 -0.090579 -0.042308  0.019293       0.03
2002-05-31  0.160239 -0.078983  0.047319       0.66

对于每一行,我想执行以下操作:

  • 排除NaNs
  • 计算该行中排序值列表中值的位置并指定此数字(位置1是最小(负)数字,位置N是最大正数)

结果将是:

                A         B        C         D
Date                                              
2002-02-28      1         3       2         NaN
2002-03-29      3         1       2          4
2002-04-30      1         2       3          4
2002-05-31      3         1       2          4

在第二步中,我想要每列制作一个3行“滚动”功能,检查当前行和一列之前的2行是否小于某个阈值X,如果是,然后显示这3个值的平均值,否则只需记下NaN。如果这3个值中的任何一个是NaN,那么只需记下NaN。只能从2002-04-30进行计算,因为需要至少3个值。对于列D,这将在行2002-04-30中产生“NaN”,因为事先只有两个数值。对于D列和2002-05-31行,它也会产生“NaN”,因为3个值是4,4和4,其中4大于阈值。

假设阈值X = 3。 (我遗漏了D栏,因为我的解释使数据变宽):

E.g:

                A           B                C         
Date                                              
2002-02-28      NaN        NaN              NaN        
2002-03-29      NaN        NaN              NaN        
2002-04-30   Avg(1,3,1)    Avg(3,1,2)    Avg(2+2+3)         
2002-05-31   Avg(3,1,3)    Avg(1,2,1)    Avg(2+3+2)        

修改: 我想我自己也做了两个步骤。你能否评价这是否正确和明智?:

import numpy as np
import pandas as pd
df = pd.DataFrame(data={'X': [0.1, 0.2, 0.3, 0.4], 'Y': [0.5, -0.2, np.NaN, -1], 'Z': [np.NaN, -0.21, -5, 10]})
df.apply(lambda row: [sorted([y for y in row if not np.isnan(y)]).index(x)+1 if not np.isnan(x) else np.NaN for x in row], axis=1)

df:
     X    Y      Z
0  0.1  0.5    NaN
1  0.2 -0.2  -0.21
2  0.3  NaN  -5.00
3  0.4 -1.0  10.00

After .apply:
     X    Y    Z
0  1.0  2.0  NaN
1  3.0  2.0  1.0
2  2.0  NaN  1.0
3  2.0  1.0  3.0


# Step 2 with new examplatory data and only one column
df = pd.DataFrame(data={'A': [1,2,3,np.NaN,3,1,3,4,3,np.NaN,2,2,1,2]})
threshold = 3
df['A_rolling'] = df['A'].rolling(window=3, min_periods=3).apply(lambda x: x.mean() if all([val <= threshold for val in x]) else np.NaN)

      A  A_rolling
0   1.0        NaN
1   2.0        NaN
2   3.0   2.000000
3   NaN        NaN
4   3.0        NaN
5   1.0        NaN
6   3.0   2.333333
7   4.0        NaN
8   3.0        NaN
9   NaN        NaN
10  2.0        NaN
11  2.0        NaN
12  1.0   1.666667
13  2.0   1.666667

所以现在只需要为所有列运行它:)

有什么想法吗? 感谢

1 个答案:

答案 0 :(得分:2)

对于第一步,您可以使用rank方法:

step1 = df.rank(axis=1)
             A    B    C    D
Date                          
2002-02-28  1.0  3.0  2.0  NaN
2002-03-29  3.0  1.0  2.0  4.0
2002-04-30  1.0  2.0  3.0  4.0
2002-05-31  3.0  1.0  2.0  4.0

对于第二步,用NaN替换大于阈值的所有值并运行滚动平均值可能不那么冗长:

threshold = 3
step1[step1 > threshold] = pd.np.NaN
step2 = step1.rolling(window=3, min_periods=3).mean()
                  A         B         C   D
Date                                        
2002-02-28       NaN       NaN       NaN NaN
2002-03-29       NaN       NaN       NaN NaN
2002-04-30  1.666667  2.000000  2.333333 NaN
2002-05-31  2.333333  1.333333  2.333333 NaN