PANDAS中类似SQL的窗口函数:Python Pandas Dataframe中的行编号

时间:2013-07-21 19:16:35

标签: python pandas numpy dataframe

我来自sql背景,我经常使用以下数据处理步骤:

  1. 按一个或多个字段对数据表进行分区
  2. 对于每个分区,在每个行中添加一个rownumber,将行排列一个或多个其他字段,分析人员指定升序或降序
  3. EX:

    df = pd.DataFrame({'key1' : ['a','a','a','b','a'],
               'data1' : [1,2,2,3,3],
               'data2' : [1,10,2,3,30]})
    df
         data1        data2     key1    
    0    1            1         a           
    1    2            10        a        
    2    2            2         a       
    3    3            3         b       
    4    3            30        a        
    

    我正在寻找如何使用与此sql窗口函数相当的PANDAS:

    RN = ROW_NUMBER() OVER (PARTITION BY Key1, Key2 ORDER BY Data1 ASC, Data2 DESC)
    
    
        data1        data2     key1    RN
    0    1            1         a       1    
    1    2            10        a       2 
    2    2            2         a       3
    3    3            3         b       1
    4    3            30        a       4
    

    我已经尝试了以下哪些我没有'分区'的地方工作:

    def row_number(frame,orderby_columns, orderby_direction,name):
        frame.sort_index(by = orderby_columns, ascending = orderby_direction, inplace = True)
        frame[name] = list(xrange(len(frame.index)))
    

    我试图扩展这个想法以使用分区(pandas中的组),但以下不起作用:

    df1 = df.groupby('key1').apply(lambda t: t.sort_index(by=['data1', 'data2'], ascending=[True, False], inplace = True)).reset_index()
    
    def nf(x):
        x['rn'] = list(xrange(len(x.index)))
    
    df1['rn1'] = df1.groupby('key1').apply(nf)
    

    但是当我这样做时,我只是得到了很多NaN。

    理想情况下,有一种简洁的方法可以复制sql的窗口功能(我已经找到了基于窗口的聚合...这是熊猫中的一个内容)...有人可以和我分享最多在PANDAS中对这样的行进行编号的惯用方法是什么?

5 个答案:

答案 0 :(得分:31)

您还可以使用sort_values()groupby(),最后使用cumcount() + 1

df['RN'] = df.sort_values(['data1','data2'], ascending=[True,False]) \
             .groupby(['key1']) \
             .cumcount() + 1
print(df)

的产率:

   data1  data2 key1  RN
0      1      1    a   1
1      2     10    a   2
2      2      2    a   3
3      3      3    b   1
4      3     30    a   4
用pandas 0.18

测试PS

答案 1 :(得分:13)

您可以使用groupby两次以及rank方法执行此操作:

In [11]: g = df.groupby('key1')

使用min方法参数为相同的RN提供共享相同数据的值:

In [12]: g['data1'].rank(method='min')
Out[12]:
0    1
1    2
2    2
3    1
4    4
dtype: float64

In [13]: df['RN'] = g['data1'].rank(method='min')

然后将这些结果分组并添加与data2相关的排名:

In [14]: g1 = df.groupby(['key1', 'RN'])

In [15]: g1['data2'].rank(ascending=False) - 1
Out[15]:
0    0
1    0
2    1
3    0
4    0
dtype: float64

In [16]: df['RN'] += g1['data2'].rank(ascending=False) - 1

In [17]: df
Out[17]:
   data1  data2 key1  RN
0      1      1    a   1
1      2     10    a   2
2      2      2    a   3
3      3      3    b   1
4      3     30    a   4

感觉应该有一种本地方式来做到这一点(可能会有......)。

答案 2 :(得分:3)

您可以同时使用transformRank这是一个示例

df = pd.DataFrame({'C1' : ['a','a','a','b','b'],
           'C2' : [1,2,3,4,5]})
df['Rank'] = df.groupby(by=['C1'])['C2'].transform(lambda x: x.rank())
df

enter image description here

了解更多信息,请查看Pandas Rank方法

答案 3 :(得分:1)

使用groupby.rank函数。 这是工作示例。

df = pd.DataFrame({'C1':['a', 'a', 'a', 'b', 'b'], 'C2': [1, 2, 3, 4, 5]})
df

C1 C2
a  1
a  2
a  3
b  4
b  5

df["RANK"] = df.groupby("C1")["C2"].rank(method="first", ascending=True)
df

C1 C2 RANK
a  1  1
a  2  2
a  3  3
b  4  1
b  5  2

答案 4 :(得分:-1)

pandas.lib.fast_zip()可以从数组列表中创建一个元组数组。您可以使用此函数创建元组系列,然后对其进行排名:

values = {'key1' : ['a','a','a','b','a','b'],
          'data1' : [1,2,2,3,3,3],
          'data2' : [1,10,2,3,30,20]}

df = pd.DataFrame(values, index=list("abcdef"))

def rank_multi_columns(df, cols, **kw):
    data = []
    for col in cols:
        if col.startswith("-"):
            flag = -1
            col = col[1:]
        else:
            flag = 1
        data.append(flag*df[col])
    values = pd.lib.fast_zip(data)
    s = pd.Series(values, index=df.index)
    return s.rank(**kw)

rank = df.groupby("key1").apply(lambda df:rank_multi_columns(df, ["data1", "-data2"]))

print rank

结果:

a    1
b    2
c    3
d    2
e    4
f    1
dtype: float64