быстрый способ получить индекс элементов top-k каждого столбца в кадре данных pandas

У меня есть очень большой фрейм данных pandas примерно с 500 000 столбцов. Каждый столбец содержит около 500 элементов. Для каждого столбца мне нужно получить расположение (индекс, столбец) элементов top-k в столбце.

Итак, если бы k было равно 2, и это был бы мой фрейм данных:

  A  B  C  D
w 4  8  10 2
x 5  1  1  6 
y 9  22 25 7 
z 15 5  7  2

Я хотел бы вернуться:

[(A,y),(A,z),(B,w),(B,y),(C,w),(C,y),(D,x),(D,y)]

Имейте в виду, что у меня около 500 000 столбцов, поэтому скорость — моя главная задача. Есть ли разумный способ сделать это, который не займет целую неделю на моей машине? Каков самый быстрый способ - даже если он будет достаточно быстрым для того объема данных, который у меня есть?

Спасибо за помощь!


person A. Arpi    schedule 24.08.2015    source источник
comment
Проблема здесь в том, что вы действительно хотите вернуть, поскольку построение списка кортежей будет дорогим, например, df.apply(lambda x: x.sort(inplace=False, ascending=False)[:2]) вернет то, что вы хотите, но значения NaN немного громоздки и, вероятно, не то, что вы хотите   -  person EdChum    schedule 24.08.2015


Ответы (2)


Я думаю, что у numpy есть хорошее решение для этого, быстрое, и вы можете отформатировать вывод, как хотите.

In [2]: df = pd.DataFrame(data=np.random.randint(0, 1000, (200, 500000)), 
                      columns=range(500000), index=range(200))

In [3]: def top_k(x,k):
             ind=np.argpartition(x,-1*k)[-1*k:]
             return ind[np.argsort(x[ind])]

In [69]: %time np.apply_along_axis(lambda x: top_k(x,2),0,df.as_matrix())
CPU times: user 5.91 s, sys: 40.7 ms, total: 5.95 s
Wall time: 6 s

Out[69]:
array([[ 14,  54],
       [178, 141],
       [ 49, 111],
       ...,
       [ 24, 122],
       [ 55,  89],
       [  9, 175]])

Довольно быстро по сравнению с решением pandas (которое чище, IMO, но здесь мы стремимся к скорости):

In [41]: %time np.array([df[c].nlargest(2).index.values for c in df])
CPU times: user 3min 43s, sys: 6.58 s, total: 3min 49s
Wall time: 4min 8s

Out[41]:
array([[ 54,  14],
       [141, 178],
       [111,  49],
       ...,
       [122,  24],
       [ 89,  55],
       [175,   9]])

Списки расположены в обратном порядке друг друга (это можно легко исправить, обратив сортировку в версии numpy)

Обратите внимание, что в примере из-за случайной генерации int мы, вероятно, можем иметь более k значений, которые равны и максимальны, поэтому возвращаемые индексы могут не совпадать между всеми методами, но все дадут действительный результат (вы получите k индексов, которые соответствуют максимальным значениям в колонке)

person cwharland    schedule 24.08.2015
comment
Это очень быстрый подход, и я использовал его. К сожалению, он не предоставляет индексы панд, только номера индексов. Но было легко изменить код, чтобы решить эту проблему. Спасибо! - person A. Arpi; 25.08.2015
comment
Хорошая работа, не стесняйтесь редактировать ответ, чтобы включить сопоставление с индексами панд, если вы думаете, что это поможет другим. - person cwharland; 26.08.2015

В Pandas есть эффективная операция nlargest, которую вы можете использовать, и она работает быстрее, чем полная сортировка. Для применения в 500 000 столбцов все равно потребуется некоторое время.

In [1]: df = pd.DataFrame(data=np.random.randint(0, 100, (200, 500000)), 
                          columns=range(500000), index=range(200))

In [2]: %time np.array([df[c].nlargest(2).index.values for c in df])
Wall time: 2min 57s
Out[2]: 
array([[171,   1],
       [ 42,  78],

Как заметил @EdChum, вы, вероятно, не хотите хранить в виде кортежей, было бы намного эффективнее использовать два массива или какую-либо другую стратегию.

person chrisb    schedule 24.08.2015
comment
Это должно быть быстрее, чем временная сортировка +1, проблема здесь в том, что apply пытается вернуть df с той же формой, что и исходная df, чего нельзя достичь, если вы не возьмете необработанные значения и не вернете некоторые другие данные структура как в вашем ответе - person EdChum; 24.08.2015