2017-09-03 17 views
1

我想根据每行值(不包括NaN)在排序值列表中的位置来分配每个值的位置。我无法弄清楚如何用大熊猫的优雅方式来做到这一点。Python熊猫:根据每行排序值(不包括NaN)分配标签

我觉得它更容易在一个例子来解释:

   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 

的每一行,我要做到以下几点:

  • 排除NaN的
  • 计算中值的位置在该行中排序值的列表并分配该数字(位置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。如果这三个值中的任何一个都是NaN,那么只记下NaN。只能从2002-04-30进行计算,因为需要至少3个值。对于D列,这将在2002-04-30行中产生“NaN”,因为事先只有两个数值。对于D列和2002-05-31行,它也会产生“NaN”,因为3个值分别是4,4和4,其中4个大于阈值。

假设阈值X = 3。 (我离开了列d,因为我的解释使数据以宽):

例如为:

   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 

因此,只有总得来说它现在所有列:)

任何想法? 感谢

回答

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 s到取代大于阈值的所有值和运行滚动平均值:

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 
+0

真的很好的解决方案。如果您在步骤2中删除最后一个'D'列,会很好。 – Dark

+0

@Bharath谢蒂:不需要,因为这只是试验;我想避免在上面的数据框中显示线条:)我会稍后看看你的例子,但现在看起来非常好,非常清晰,比我更优雅:) – tim

+0

很好,谢谢那真棒解决方案! – tim