SQL-подобные функции окна в PANDAS: Нумерация строк в Python Pandas Dataframe
Я пришел из sql фона и часто использую следующий шаг обработки данных:
- Разделить таблицу данных на одно или несколько полей
- Для каждого раздела добавьте число к каждой из его строк, которое ранжирует строку по одному или нескольким другим полям, где аналитик указывает восходящий или нисходящий
Пример:
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
Я ищу, как сделать PANDAS эквивалентной этой функции окна sql:
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)
Но я только что получил много NaNs, когда я это делаю.
В идеале, был бы краткий способ воспроизвести возможность оконной функции sql (я выяснил, что агрегаты на основе окон... что один вкладыш в пандах)... может кто-нибудь поделиться со мной самым идиоматичным способом количество строк, как это в PANDAS?
Ответы
Ответ 1
Вы можете сделать это, используя groupby
дважды вместе с методом rank
:
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')
И затем сгруппируйте эти результаты и добавьте ранг по данным2:
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
вы также можете использовать 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, протестированный с помощью pandas 0.18
Ответ 3
Вы можете использовать transform
и Rank
вместе. Вот пример
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 для получения дополнительной информации
Ответ 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
Ответ 5
Используйте функцию 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