j'ai un dataframe avec chaque ligne ayant une valeur de liste.
id list_of_value
0 ['a','b','c']
1 ['d','b','c']
2 ['a','b','c']
3 ['a','b','c']
je dois faire un calcul d'un score avec une ligne et contre toutes les autres lignes
Par exemple:
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
répétez l'étape 2,3 entre id 0 et id 1,2,3, de même pour tous les id.
et créer une trame de données N x N; tel que cela:
- 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
À l'heure actuelle, mon code n'en a qu'une pour la boucle:
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,))
Y a-t-il une meilleure manière de faire cela? puis-je simplement écrire une fonction d'application au lieu de faire une itération for-loop. puis-je faire plus vite?
list_of_value
?list_of_value
. Je veux dire au total, sur toutes les lignes.Réponses:
Si vos données ne sont pas trop grandes, vous pouvez utiliser
get_dummies
pour encoder les valeurs et faire une multiplication matricielle:Production:
Mise à jour : voici une brève explication du code. L'idée principale est de transformer les listes données en un codage à chaud:
Une fois que nous avons que la taille d'intersection des deux lignes, disons,
0
et1
est juste leur produit scalaire, car un personnage appartient aux deux lignes si et seulement si elle est représentée par1
les deux.Dans cet esprit, première utilisation
pour transformer chaque cellule en une série et concaténer toutes ces séries. Production:
Maintenant, nous utilisons
pd.get_dummies
sur cette série pour la transformer en une trame de données codée à chaud:Comme vous pouvez le voir, chaque valeur a sa propre ligne. Puisque nous voulons combiner ceux qui appartiennent à la même ligne d'origine à une ligne, nous pouvons simplement les additionner par l'index d'origine. Donc
donne la trame de données codée binaire que nous voulons. La ligne suivante
est tout comme votre logique:
s.dot(s.T)
calcule les produits scalaires par lignes, puis.div(s.sum(1))
divise les nombres par lignes.la source
12k x 12k
dataframe. Ça devrait aller si vous avez environ quelques centaines de valeurs uniques.Essaye ça
Production
Vous pouvez également le faire comme suit
la source
Utilisez la compréhension des listes imbriquées sur la liste des ensembles
s_list
. Dans la compréhension de la liste, utilisez l'intersection
opération pour vérifier le chevauchement et obtenir la longueur de chaque résultat. Enfin, construisez la trame de données et divisez-la par la longueur de chaque listedf.list_of_value
Dans le cas où il y a des valeurs en double dans chaque liste, vous devez utiliser à la
collections.Counter
place deset
. J'ai changé les données d'exemple id = 0 en['a','a','c']
et id = 1 en['d','b','a']
la source
Mise à jour
Puisqu'il y a beaucoup de solutions proposées, il semble que ce soit une bonne idée de faire une analyse temporelle. J'ai généré des données aléatoires avec 12k lignes comme demandé par l'OP, en gardant les 3 éléments par ensemble mais en augmentant la taille de l'alphabet disponible pour remplir les ensembles. Cela peut être ajusté pour correspondre aux données réelles.
Faites-moi savoir si vous avez une solution que vous aimeriez tester ou mettre à jour.
Installer
Gagnant actuel
Concurrents
Message d'origine avec détails de la solution
Il est possible de le faire
pandas
avec une auto-jointure.Comme d'autres réponses l'ont souligné, la première étape consiste à décompresser les données sous une forme plus longue.
À partir de ce tableau, il est possible de calculer les nombres par ID.
Et puis vient l'auto-jointure, qui se produit sur la
value
colonne. Cela associe les ID une fois pour chaque valeur d'intersection, de sorte que les ID appariés peuvent être comptés pour obtenir les tailles d'intersection.Ces deux peuvent ensuite être fusionnés et un score calculé.
Si vous préférez la forme matricielle, c'est possible avec a
pivot
. Ce sera une représentation beaucoup plus grande si les données sont rares.la source
Cette solution fonctionnera efficacement avec toute taille des données et tout type de valeurs dans votre
list
exemple sonstr
ouint
ou autrement, en prenant soin des valeurs répétitives le cas échéant.Dans ce cas, la compréhension de la liste fonctionne mieux car elle n'a pas besoin de charger l'attribut append de la liste et de l'appeler en tant que fonction à chaque itération. En d'autres termes et en général, les compréhensions de liste fonctionnent plus rapidement car la suspension et la reprise du cadre d'une fonction, ou plusieurs fonctions dans d'autres cas, sont plus lentes que la création d'une liste à la demande.
Utiliser une compréhension de liste à la place d'une boucle qui ne crée pas de liste, accumuler de manière absurde une liste de valeurs dénuées de sens, puis jeter la liste, est souvent plus lent en raison de la surcharge de création et d'extension de la liste.
Résultat:
Temps d'exécution:
la source
Vous pouvez convertir la liste en un ensemble et utiliser la fonction d'intersection pour vérifier le chevauchement:
(une seule fonction d'application est utilisée comme vous l'avez demandé :-))
la source
J'utiliserais
product
pour obtenir toutes les combinaisons. Ensuite, nous pouvons vérifier avecnumpy.isin
etnumpy.mean
:Échantillon de temps
la source
Devrait être rapide, considérez également le doublon dans la liste
la source
Oui! Nous recherchons ici un produit cartésien, qui est donné dans cette réponse. Ceci peut être réalisé sans boucle for ou compréhension de liste
Ajoutons une nouvelle valeur répétée à notre bloc de données
df
pour qu'elle ressemble à ceci:Fusionner ensuite avec lui-même
Voici à quoi ressemble le cadre fusionné:
Ensuite, nous appliquons la fonction souhaitée à chaque ligne en utilisant
axis=1
Remodeler cela pour obtenir des valeurs au format souhaité
J'espère que cela t'aides :)
la source