computar similaridade jaccard no dataframe

Nov 05 2020

autodidata em python, estou tentando melhorar então qualquer ajuda é muito bem-vinda, muito obrigado! Quero calcular uma similaridade de jaccard em uma coluna do meu dataframe combinando critérios em outra coluna. df se parece com isto:

name       bag number       item          quantity
sally         1             BANANA            3
sally         2             BREAD             1
franck        3             BANANA            2
franck        3             ORANGE            1
franck        3             BREAD             4
robert        4             ORANGE            3
jenny         5             BANANA            4
jenny         5             ORANGE            2

Com cerca de 80 itens categóricos, o número da sacola (amostra) é exclusivo para um comprador, mas eles podem ter mais de um e as quantidades variam de 0 a 4. Eu gostaria de iterar através do número da sacola para comparar o conteúdo com uma similaridade de jaccard ou distância de cada par de bolsa. Se possível com a opção de considerar a quantidade como peso de comparação. o resultado ideal seria um dataframe como aquela matriz Python Pandas Distance usando similaridade jaccard

Eu sinto que a solução está em algum lugar entre este> Como calcular a similaridade do jaccard a partir de um dataframe pandas e como aplicar uma função personalizada a grupos em um dataframe dask, usando várias colunas como entrada de função

Estou pensando que devo iterar por meio de uma máscara para configurar as duas variáveis ​​da função jaccard. Mas em todos os exemplos que vejo, os itens a comparar estão em colunas diferentes. Então, estou meio perdida aqui ... muito obrigado por ajudar! Felicidades

Respostas

EdgarRamírezMondragón Nov 05 2020 at 14:02

Resolver a versão mais fácil e não ponderada do problema pode ser feito com as seguintes etapas:

  1. crie uma tabela dinâmica com seu dataframe atual

    p = df.pivot_table(
        index='bag_number',
        columns='item',
        values='quantity',
    ).fillna(0)  # Convert NaN to 0
    
  2. siga o exemplo em sua pergunta vinculada para calcular a distância de Jaccard comscipy

    from scipy.spatial.distance import jaccard, pdist, squareform
    
    m = 1 - squareform(pdist(p.astype(bool), jaccard))
    sim = pd.DataFrame(m, index=p.index, columns=p.index)
    

Resultado:

bag_number         1         2         3         4         5
bag_number                                                  
1           1.000000  0.000000  0.333333  0.000000  0.500000
2           0.000000  1.000000  0.333333  0.000000  0.000000
3           0.333333  0.333333  1.000000  0.333333  0.666667
4           0.000000  0.000000  0.333333  1.000000  0.500000
5           0.500000  0.000000  0.666667  0.500000  1.000000

A versão ponderada é apenas um pouco mais complicada. A pdistfunção só oferece suporte a um vetor que será aplicado a todas as comparações, portanto, você precisará criar uma função de similaridade (ou distância) personalizada. De acordo com a Wikipedia , a versão ponderada pode ser calculada da seguinte forma:

import numpy as np

def weighted_jaccard_distance(x, y):
    arr = np.array([x, y])
    return 1 - arr.min(axis=0).sum() / arr.max(axis=0).sum()

Agora você pode calcular a similaridade ponderada

sim_weighted = pd.DataFrame(
    data=1 - squareform(pdist(p, weighted_jaccard_distance)),
    index=p.index,
    columns=p.index,
)

Resultado:

bag_number     1         2         3         4         5
bag_number                                              
1           1.00  0.000000  0.250000  0.000000  0.500000
2           0.00  1.000000  0.142857  0.000000  0.000000
3           0.25  0.142857  1.000000  0.111111  0.300000
4           0.00  0.000000  0.111111  1.000000  0.285714
5           0.50  0.000000  0.300000  0.285714  1.000000