PANDAS 中类 SQL 窗口函数: Python PANDAS 数据框架中的行编号

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

  1. 按一个或多个字段划分数据表
  2. 对于每个分区,向每个行添加一个行号,该行号按照一个或多个其他字段对该行进行排序,分析人员在该字段中指定升序或降序

例如:

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 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)))

我试图将这个想法扩展到分区(大熊猫中的群体) ,但下面的方法行不通:

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 的 window 函数功能(我已经算出了基于窗口的聚合... ... 这是熊猫中的一行) ... ... 有人能和我分享一下在 PANDAS 中给这样的行编号的最惯用的方法吗?

101202 次浏览

可以通过使用 groupbyrank方法两次来实现这一点:

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

使用 min 方法参数给出共享相同数据的值1和 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

感觉好像应该有一种本地化的方法来做这件事(很可能有! ...)。

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

你也可以使用 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

熊猫 PS 测试0.18

你可以同时使用 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

更多信息请看熊猫排名法

使用 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