Панды: применить функцию к каждой паре столбцов с ограничениями

Как следует из названия, я пытаюсь применить функцию к каждой паре столбцов фрейма данных при некоторых условиях. Я попытаюсь проиллюстрировать это. Мой df имеет форму:

Code |  14  |  17  |  19  | ...
w1   |  0   |   5  |   3  | ...
w2   |  2   |   5  |   4  | ... 
w3   |  0   |   0  |   5  | ...

Код соответствует определенному месту в прямоугольной сетке, а ws — это разные слова. Я хотел бы применить меру косинусного сходства между каждой парой столбцов только (ОТРЕДАКТИРОВАНО!) если сумма элементов в одном из столбцов пары больше 5.

Желаемый результат будет примерно таким:

     | [14,17]  |  [14,19]  |  [14,...]  |  [17,19]  | ...
Sim  |cs(14,17) |cs(14,19)  |cs(14,...)  |cs(17,19)..| ...

cs является результатом косинусного сходства для каждой пары столбцов. Есть ли подходящий способ сделать это?

Любая помощь будет оценена :-)


person Gonzalo Donoso    schedule 19.07.2016    source источник
comment
Если я правильно понимаю, вам не нужны ни cs(14,17), ни cs(14,19) и т. д., потому что в столбце «14» нет элемента, который больше 5. А вы что-нибудь пробовали? Не могли бы вы предоставить код и примеры, которые не помогли?   -  person danielhadar    schedule 19.07.2016
comment
Привет, @danielhadar. На самом деле до сих пор я сделал несколько расчетов вручную. Я спрашиваю, есть ли способ применить функции (в данном случае косинусное сходство, но я буду применять больше функций) к каждой паре столбцов векторным способом, то есть без написания циклов по столбцам. Сборка последнего дф только для лучшей визуализации результата, но это не важно.   -  person Gonzalo Donoso    schedule 19.07.2016


Ответы (1)


Чтобы применить метрику косинуса к каждой паре из двух наборов входных данных, вы можете использовать scipy.spatial.distance.cdist. Это будет намного быстрее, чем использование двойного цикла Python.

Пусть одной коллекцией будут все столбцы df. Пусть в другой коллекции будут только те столбцы, в которых сумма больше 5:

import pandas as pd
df = pd.DataFrame({'14':[0,2,0], '17':[5,5,0], '19':[3,4,5]})
mask = df.sum(axis=0) > 5
df2 = df.loc[:, mask]

Тогда все сходства косинусов можно вычислить одним вызовом cdist:

import scipy.spatial.distance as SSD
values = SSD.cdist(df2.T, df.T, metric='cosine')
# array([[  2.92893219e-01,   1.11022302e-16,   3.00000000e-01],
#        [  4.34314575e-01,   3.00000000e-01,   1.11022302e-16]])

Значения могут быть завернуты в новый DataFrame и изменены:

result = pd.DataFrame(values, columns=df.columns, index=df2.columns)
result = result.stack()

import pandas as pd
import scipy.spatial.distance as SSD
df = pd.DataFrame({'14':[0,2,0], '17':[5,5,0], '19':[3,4,5]})
mask = df.sum(axis=0) > 5
df2 = df.loc[:, mask]
values = SSD.cdist(df2.T, df.T, metric='cosine')
result = pd.DataFrame(values, columns=df.columns, index=df2.columns)
result = result.stack()
mask = result.index.get_level_values(0) != result.index.get_level_values(1)
result = result.loc[mask]
print(result)

дает серию

17  14    0.292893
    19    0.300000
19  14    0.434315
    17    0.300000
person unutbu    schedule 19.07.2016