Я хотел бы заменить значения метками столбцов в соответствии с самыми большими 3 значениями для каждой строки.Давайте предположим, что это входные данные:
p1 p2 p3 p4
0 0 9 1 4
1 0 2 3 4
2 1 3 10 7
3 1 5 3 1
4 2 3 7 10
Учитывая n = 3
, я ищу:
Top1 Top2 Top3
0 p2 p4 p3
1 p4 p3 p2
2 p3 p4 p2
3 p2 p3 p1
4 p4 p3 p2
Меня не волнуют дубликаты, например, для индекса 3
, Top3
может быть 'p1'
или 'p4'
.
Попытка 1
Моя первая попытка - полная сортировка с использованием np.ndarray.argsort
:
res = pd.DataFrame(df.columns[df.values.argsort(1)]).iloc[:, len(df.index): 0: -1]
Но на самом деле у меня более 4 столбцов, и это будет неэффективно.
Попытка 2
Далее я попробовал np.argpartition
.Но поскольку значения внутри каждого раздела не сортируются, для этого требуется последующая сортировка:
n = 3
parts = np.argpartition(-df.values, n, axis=1)[:, :-1]
args = (-df.values[np.arange(df.shape[0])[:, None], parts]).argsort(1)
res = pd.DataFrame(df.columns[parts[np.arange(df.shape[0])[:, None], args]],
columns=[f'Top{i}' for i in range(1, n+1)])
Это, на самом деле, работает на медленнее , чем первая попыткабольшие кадры данных.Есть ли более эффективный способ, который использует преимущества частичной сортировки?Вы можете использовать приведенный ниже код для целей тестирования.
Сравнительный анализ
# Python 3.6.0, NumPy 1.11.3, Pandas 0.19.2
import pandas as pd, numpy as np
df = pd.DataFrame({'p1': [0, 0, 1, 1, 2],
'p2': [9, 2, 3, 5, 3],
'p3': [1, 3, 10, 3, 7],
'p4': [4, 4, 7, 1, 10]})
def full_sort(df):
return pd.DataFrame(df.columns[df.values.argsort(1)]).iloc[:, len(df.index): 0: -1]
def partial_sort(df):
n = 3
parts = np.argpartition(-df.values, n, axis=1)[:, :-1]
args = (-df.values[np.arange(df.shape[0])[:, None], parts]).argsort(1)
return pd.DataFrame(df.columns[parts[np.arange(df.shape[0])[:, None], args]])
df = pd.concat([df]*10**5)
%timeit full_sort(df) # 86.3 ms per loop
%timeit partial_sort(df) # 158 ms per loop