Eu tenho dataframe com cada linha com um valor de lista.
id list_of_value
0 ['a','b','c']
1 ['d','b','c']
2 ['a','b','c']
3 ['a','b','c']
eu tenho que fazer um cálculo de uma pontuação com uma linha e contra todas as outras linhas
Por exemplo:
Step 1: Take value of id 0: ['a','b','c'],
Step 2: find the intersection between id 0 and id 1 ,
resultant = ['b','c']
Step 3: Score Calculation => resultant.size / id.size
repita a etapa 2,3 entre os ID 0 e 1,2,3, da mesma forma para todos os IDs.
e crie um quadro de dados N x N; tal como este:
- 0 1 2 3
0 1 0.6 1 1
1 1 1 1 1
2 1 1 1 1
3 1 1 1 1
No momento, meu código tem apenas um para loop:
def scoreCalc(x,queryTData):
#mathematical calculation
commonTData = np.intersect1d(np.array(x),queryTData)
return commonTData.size/queryTData.size
ids = list(df['feed_id'])
dfSim = pd.DataFrame()
for indexQFID in range(len(ids)):
queryTData = np.array(df.loc[df['id'] == ids[indexQFID]]['list_of_value'].values.tolist())
dfSim[segmentDfFeedIds[indexQFID]] = segmentDf['list_of_value'].apply(scoreCalc,args=(queryTData,))
Existe uma maneira melhor de fazer isso? Posso apenas escrever uma função de aplicar em vez de fazer uma iteração de loop for. posso torná-lo mais rápido?
list_of_value
?list_of_value
. Quero dizer no total, em todas as linhas.Respostas:
Se seus dados não forem muito grandes, você poderá
get_dummies
codificar os valores e fazer uma multiplicação de matrizes:Resultado:
Atualização : Aqui está uma breve explicação para o código. A idéia principal é transformar as listas fornecidas em um código quente:
Depois disso, o tamanho da interseção das duas linhas, digamos,
0
e1
é apenas o produto escalar, porque um caractere pertence às duas linhas se e somente se é representado por1
ambas.Com isso em mente, primeiro uso
para transformar cada célula em uma série e concatenar todas essas séries. Resultado:
Agora, usamos
pd.get_dummies
essa série para transformá-la em um dataframe com um código quente:Como você pode ver, cada valor tem sua própria linha. Como queremos combinar aqueles que pertencem à mesma linha original em uma linha, podemos apenas somar pelo índice original. portanto
fornece o quadro de dados codificado em binário que queremos. A próxima linha
é exatamente como a sua lógica:
s.dot(s.T)
calcula os produtos pontilhados por linhas e depois.div(s.sum(1))
divide as contagens por linhas.fonte
12k x 12k
dataframe. Deve ficar bom se você tiver algumas centenas de valores exclusivos.Tente isto
Resultado
Você também pode fazer o seguinte
fonte
Use a compreensão da lista aninhada na lista de conjuntos
s_list
. Na compreensão da lista, use aintersection
operação para verificar a sobreposição e obter o comprimento de cada resultado. Por fim, construa o quadro de dados e divida-o pelo comprimento de cada lista emdf.list_of_value
Caso haja valores duplicados em cada lista, você deve usar em
collections.Counter
vez deset
. Alterei os dados de amostra id = 0 para['a','a','c']
e id = 1 para['d','b','a']
fonte
Atualizada
Como existem muitas soluções candidatas propostas, parece uma boa ideia fazer uma análise de tempo. Gerei alguns dados aleatórios com 12 mil linhas, conforme solicitado pelo OP, mantendo os 3 elementos por conjunto, mas expandindo o tamanho do alfabeto disponível para preencher os conjuntos. Isso pode ser ajustado para corresponder aos dados reais.
Deixe-me saber se você tem uma solução que gostaria de testar ou atualizar.
Configuração
Vencedor Atual
Competidores
Postagem original com detalhes da solução
É possível fazer isso
pandas
com uma auto-junção.Como outras respostas apontaram, o primeiro passo é descompactar os dados em um formato mais longo.
Nesta tabela, é possível calcular as contagens por ID.
E então vem a auto-junção, o que acontece na
value
coluna. Isso associa os IDs uma vez para cada valor de interseção, para que os IDs emparelhados possam ser contados para obter os tamanhos de interseção.Esses dois podem ser mesclados e uma pontuação calculada.
Se você preferir a forma da matriz, isso é possível com a
pivot
. Essa será uma representação muito maior se os dados forem escassos.fonte
Esta solução irá trabalhar eficientemente com qualquer tamanho de dados e qualquer tipo de valores em sua
list
palavra suastr
ouint
ou de outra forma, também cuidar dos valores repetitivos se houver.Nesse caso, a compreensão da lista tem um desempenho melhor porque não precisa carregar o atributo de acréscimo da lista e chamá-lo como uma função a cada iteração. Em outras palavras, e em geral, as compreensões de lista são mais rápidas porque suspender e retomar o quadro de uma função ou várias funções em outros casos são mais lentas do que a criação de uma lista sob demanda.
Usar uma compreensão de lista no lugar de um loop que não cria uma lista, acumular absurdamente uma lista de valores sem sentido e depois jogá-la fora, geralmente é mais lento por causa da sobrecarga de criar e estender a lista.
Resultado:
Tempo de execução:
fonte
Você pode converter a lista em um conjunto e usar a função de interseção para verificar se há sobreposição:
(apenas uma função de aplicação é usada conforme solicitado :-))
fonte
Eu usaria
product
para obter todas as combinações. Então podemos verificar comnumpy.isin
enumpy.mean
:Amostra de tempo
fonte
Deve ser rápido, considere também a duplicata na lista
fonte
Sim! Estamos procurando um produto cartesiano aqui, que é fornecido nesta resposta. Isso pode ser obtido sem um loop for ou uma compreensão de lista
Vamos adicionar um novo valor repetido ao nosso quadro de dados
df
para que fique assim:Em seguida, mesclar-se
É assim que o quadro mesclado se parece:
Em seguida, aplicamos a função desejada a cada linha usando
axis=1
Remodelando isso para obter valores no formato desejado
Espero que isto ajude :)
fonte