Tutoriel de recommandation utilisant l'analyse d'association (implémentation python)

À propos de cet article

J'ai écrit un article parce qu'il n'y avait pas beaucoup de tutoriels dans le monde qui ont été mis en œuvre à l'aide d'exemples de données concernant les recommandations.
Il existe des méthodes telles que l'apprentissage automatique pour créer des recommandations, mais cet article explique comment créer des recommandations à l'aide de méthodes basées sur des statistiques.
Je vais expliquer l'utilisation de python et d'un jeu de données ouvert.

Ceci est un article sur l'implémentation par python. Pour le concept de recommandations, consultez les articles suivants. Tutoriel de recommandation utilisant l'analyse d'association (concept)

Il sera mis en œuvre selon le flux de l'article de l'édition conceptuelle.

Si vous souhaitez essayer cette implémentation sans créer d'environnement

Il y a des frais, mais nous avons préparé un environnement d'exécution pour Google Colaboratory à ici.

** Importer les bibliothèques requises **

Importez les bibliothèques requises.

#Importer la bibliothèque
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

** Chargement du jeu de données **

Chargez l'ensemble de données utilisé dans ce didacticiel. Ceci charge un ensemble de données depuis le référentiel github.

#Charger le jeu de données
import urllib.request
from io import StringIO

url = "https://raw.githubusercontent.com/tachiken0210/dataset/master/dataset_cart.csv"

#Fonction de lecture de csv
def read_csv(url):
    res = urllib.request.urlopen(url)
    res = res.read().decode("utf-8")
    df = pd.read_csv(StringIO( res) )
    return df

#Courir
df_cart_ori = read_csv(url)

** Vérifiez le contenu de l'ensemble de données **

Vérifiez le contenu de l'ensemble de données utilisé cette fois.

df_cart_ori.head()
cart_id goods_id action create_at update_at last_update time
0 108750017 583266 UPD 2013-11-26 03:11:06 2013-11-26 03:11:06 2013-11-26 03:11:06 1385478215
1 108750017 662680 UPD 2013-11-26 03:11:06 2013-11-26 03:11:06 2013-11-26 03:11:06 1385478215
2 108750017 664077 UPD 2013-11-26 03:11:06 2013-11-26 03:11:06 2013-11-26 03:11:06 1385478215
3 108199875 661648 ADD 2013-11-26 03:11:10 2013-11-26 03:11:10 2013-11-26 03:11:10 1385478215
4 105031004 661231 ADD 2013-11-26 03:11:41 2013-11-26 03:11:41 2013-11-26 03:11:41 1385478215

** À propos de l'ensemble de données utilisé dans ce tutoriel **

Cet ensemble de données est les données du journal lorsqu'un client sur un certain site EC met un produit dans le panier.

** ・ card_id: identifiant de panier associé au client (il est acceptable de le considérer comme un identifiant de client, il sera donc appelé le client ci-dessous)
**** ・ goods_id: identifiant de produit
**** ・ action: Action du client (AJOUTER: Ajouter au panier, DEL: Supprimer du panier, etc.)
**** ・ create_at: Heure de création du journal
**** ・ update_at: Le journal est mis à jour Heure (non utilisée cette fois)
**** ・ last_update: Heure à laquelle le journal a été mis à jour (non utilisée cette fois)
**** ・ heure: Horodatage
**

En outre, les ensembles de données ouverts généraux, y compris l'ensemble de données utilisé cette fois, incluent rarement des noms spécifiques tels que les noms de produits dans l'ensemble de données et sont essentiellement exprimés par l'ID de produit. Par conséquent, il est difficile de savoir de quel type de produit il s'agit, mais veuillez noter que cela est dû au contenu des données.

Jetons ensuite un coup d'œil au contenu de ces données. Cet ensemble de données contient des données pour tous les clients sous forme de journal chronologique, alors concentrons-nous sur un seul client. Tout d'abord, extrayons le client avec le plus grand nombre de journaux dans les données.

df_cart_ori["cart_id"].value_counts().head()
#production
110728794    475
106932411    426
115973611    264
109269739    205
112332751    197
Name: cart_id, dtype: int64

Il existe 475 journaux pour 110728794 utilisateurs, ce qui semble être le plus enregistré. Extrayons le journal de ce client.

df_cart_ori[df_cart_ori["cart_id"]==110728794].head(10)
cart_id goods_id action create_at update_at last_update time
32580 110728794 664457 ADD 2013-11-26 22:54:13 2013-11-26 22:54:13 2013-11-26 22:54:13 1385478215
32619 110728794 664885 ADD 2013-11-26 22:55:09 2013-11-26 22:55:09 2013-11-26 22:55:09 1385478215
33047 110728794 664937 ADD 2013-11-26 22:58:52 2013-11-26 22:58:52 2013-11-26 22:58:52 1385478215
33095 110728794 664701 ADD 2013-11-26 23:00:25 2013-11-26 23:00:25 2013-11-26 23:00:25 1385478215
34367 110728794 665050 ADD 2013-11-26 23:02:40 2013-11-26 23:02:40 2013-11-26 23:02:40 1385478215
34456 110728794 664989 ADD 2013-11-26 23:05:03 2013-11-26 23:05:03 2013-11-26 23:05:03 1385478215
34653 110728794 664995 ADD 2013-11-26 23:07:00 2013-11-26 23:07:00 2013-11-26 23:07:00 1385478215
34741 110728794 664961 ADD 2013-11-26 23:09:41 2013-11-26 23:09:41 2013-11-26 23:09:41 1385478215
296473 110728794 665412 DEL 2013-12-03 17:17:30 2013-12-03 17:17:30 2013-12-03 07:41:13 1386083014
296476 110728794 665480 DEL 2013-12-03 17:17:37 2013-12-03 17:17:37 2013-12-03 07:42:29 1386083014

Si vous regardez ce client, vous pouvez voir qu'il ajoute continuellement des paniers à ses produits tout au long de la journée. En analysant cela pour d'autres clients de la même manière, il semble qu'il est facile de voir que le produit y est susceptible d'être ajouté au panier à côté du produit x qui est généralement présent.
De cette manière, le but de ce temps est d'analyser le modèle selon lequel ** "Le produit y est susceptible d'être ajouté au panier à côté d'un certain produit x" à partir de cet ensemble de données. **

Nous traiterons en fait les données du suivant, mais gardez ce qui précède dans le coin de votre tête.

** Prétraitement des données **

À partir de là, nous prétraiterons les données. Tout d'abord, si vous regardez la colonne "action", il y a diverses choses telles que ADD et DEL. Pour l'instant, concentrons-nous sur le journal appelé ADD (ajouté au panier). Cela signifie se concentrer uniquement sur la demande de produit du client.

df = df_cart_ori.copy()
df = df[df["action"]=="ADD"]
df.head()
cart_id goods_id action create_at update_at last_update time
3 108199875 661648 ADD 2013-11-26 03:11:10 2013-11-26 03:11:10 2013-11-26 03:11:10 1385478215
4 105031004 661231 ADD 2013-11-26 03:11:41 2013-11-26 03:11:41 2013-11-26 03:11:41 1385478215
6 110388732 661534 ADD 2013-11-26 03:11:55 2013-11-26 03:11:55 2013-11-26 03:11:55 1385478215
7 110388740 662336 ADD 2013-11-26 03:11:58 2013-11-26 03:11:58 2013-11-26 03:11:58 1385478215
8 110293997 661648 ADD 2013-11-26 03:12:13 2013-11-26 03:12:13 2013-11-26 03:12:13 1385478215

Ensuite, triez l'ensemble de données pour classer par ordre chronologique les articles du panier pour chaque client.

df = df[["cart_id","goods_id","create_at"]]
df = df.sort_values(["cart_id","create_at"],ascending=[True,True])
df.head()
cart_id goods_id create_at
548166 78496306 661142 2013-11-15 23:07:02
517601 79100564 662760 2013-11-24 18:17:24
517404 79100564 661093 2013-11-24 18:25:29
23762 79100564 664856 2013-11-26 13:41:47
22308 79100564 562296 2013-11-26 13:44:20

Le processus d'ici formatera les données pour s'adapter à la bibliothèque d'analyse d'association.

Pour chaque client, nous ajouterons les articles du panier à la liste dans l'ordre.

df = df[["cart_id","goods_id"]]
df["goods_id"] = df["goods_id"].astype(int).astype(str)
df = df.groupby(["cart_id"])["goods_id"].apply(lambda x:list(x)).reset_index()
df.head()
 |   cart_id | goods_id                                                                                                                                     |

|---:|----------:|:---------------------------------------------------------------------------------------------------------------------------------------------| | 0 | 78496306 | ['661142'] | | 1 | 79100564 | ['662760', '661093', '664856', '562296', '663364', '664963', '664475', '583266'] | | 2 | 79455669 | ['663801', '664136', '664937', '663932', '538673', '663902', '667859'] | | 3 | 81390353 | ['663132', '661725', '664236', '663331', '659679', '663847', '662340', '662292', '664099', '664165', '663581', '665426', '663899', '663405'] | | 4 | 81932021 | ['662282', '664218']
Ensuite, vérifions le contenu du bloc de données ci-dessus.
Remarquez le client sur la deuxième ligne (79100564).
Tout d'abord, après l'ajout du premier produit 663801, 664136 est ajouté au panier. Autrement dit, X = "663801" et Y = "664136". < De plus, après 664136, 664937 est ajouté au panier. Pour ceux-ci, X = "664136" et Y = "664937". De cette manière, en disposant cette paire XY pour chaque client, il est possible d'effectuer une analyse d'association.

Formatez maintenant les données pour que ce XY soit apparié.

def get_combination(l):
    length = len(l)
    list_output = []
    list_pair = []
    for i in range(length-1):
        if l[i]==l[i+1]:
            pass
        else:
            list_pair =[l[i],l[i+1]]
            list_output.append(list_pair)
    return list_output

df["comb_goods_id"] = df["goods_id"].apply(lambda x:get_combination(x))

#Faire une liste des entrées d'association
dataset= []
for i,contents in enumerate(df["comb_goods_id"].values):
    for c in contents:
        dataset.append(c)

print("Le nombre de paires XY est",len(dataset))
print("Contenu de la paire XY",dataset[:5])
#production
Le nombre de paires XY est 141956
Contenu de la paire XY[['662760', '661093'], ['661093', '664856'], ['664856', '562296'], ['562296', '663364'], ['663364', '664963']]

** Effectuer une analyse d'association **

Ce qui précède est utilisé comme entrée pour l'analyse d'association. J'ai créé une méthode qui effectue une analyse d'association en tant que méthode d'association, je vais donc l'utiliser.

#Bibliothèque d'association
def association(dataset):
    df = pd.DataFrame(dataset,columns=["x","y"])
    num_dataset = df.shape[0]
    df["sum_count_xy"]=1
    print("calculating support....")
    df_a_support = (df.groupby("x").sum()/num_dataset).rename(columns={"sum_count_xy":"support_x"})
    df_b_support = (df.groupby("y").sum()/num_dataset).rename(columns={"sum_count_xy":"support_y"})
    df = df.groupby(["x","y"]).sum()
    df["support_xy"]=df["sum_count_xy"]/num_dataset
    df = df.reset_index()
    df = pd.merge(df,df_a_support,on="x")
    df = pd.merge(df,df_b_support,on="y")
    print("calculating confidence....")
    df_temp = df.groupby("x").sum()[["sum_count_xy"]].rename(columns={"sum_count_xy":"sum_count_x"})
    df = pd.merge(df,df_temp,on="x")
    df_temp = df.groupby("y").sum()[["sum_count_xy"]].rename(columns={"sum_count_xy":"sum_count_y"})
    df = pd.merge(df,df_temp,on="y")
    df["confidence"]=df["sum_count_xy"]/df["sum_count_x"]
    print("calculating lift....")
    df["lift"]=df["confidence"]/df["support_y"]
    df["sum_count"] = num_dataset
    df_output = df
    return df_output
#Appliquer le jeu de données à l'analyse d'association
df_output = association(dataset)
df_output.head()
x y sum_count_xy support_xy support_x support_y sum_count_x sum_count_y confidence lift sum_count
0 485836 662615 1 7.04444e-06 7.04444e-06 0.000147933 1 21 1 6759.81 141956
1 549376 662615 1 7.04444e-06 2.11333e-05 0.000147933 3 21 0.333333 2253.27 141956
2 654700 662615 1 7.04444e-06 0.000464933 0.000147933 66 21 0.0151515 102.421 141956
3 661475 662615 1 7.04444e-06 0.000965088 0.000147933 137 21 0.00729927 49.3417 141956
4 661558 662615 1 7.04444e-06 0.000408577 0.000147933 58 21 0.0172414 116.548 141956

J'ai eu le résultat. Le contenu de chaque colonne est le suivant ** ・ x: Condition partie X
・ y: Conclusion partie Y
・ sum_count_xy: Nombre de données applicables à XY
・ support_xy: support XY
・ support_x: support X
> ・ Support_y: Support Y
・ sum_count_x: Nombre de données applicables à X
・ sum_count_y: Nombre de données applicables à Y
・ confiance: Confiance
・ lift: Lift
**

** Post-traitement des résultats d'association **

Eh bien, l'exécution de l'association est terminée, mais il y a une mise en garde.
** Fondamentalement, une valeur de portance élevée semble être très pertinente, mais si le nombre de données est petit, on soupçonne que cela peut être arrivé par accident. **
Prenez la ligne du haut, par exemple. C'est la partie de x = "485836", y = "662615".
Cela a une valeur de portance très élevée de 6760, mais cela ne s'est produit qu'une seule fois (= support_xy). En supposant que le nombre de fois soit suffisamment de données, il est fort possible que cela se soit produit par accident.
Alors, comment dériver le seuil support_xy qui détermine s'il est accidentel ou non?

.. .. .. En réalité, il est difficile de déterminer ce seuil. (Il n'y a pas de bonne réponse)

Ici, vérifions une fois l'histogramme de support_xy.

fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.hist(df_output["sum_count_xy"], bins=100)
ax.set_title('')
ax.set_xlabel('xy')
ax.set_ylabel('freq')
#C'est difficile à voir, alors limitez la portée de l'axe y
ax.set_ylim(0,500)
fig.show()

Vous pouvez voir que la plupart des données ont un support_xy sur l'axe des x concentré près de 0.
Dans ce tutoriel, toutes les données proches de 0 sont considérées comme une coïncidence et seront supprimées.
Lors de la définition du seuil, la fraction est utilisée et le seuil est défini de manière à ce que les données des 98% inférieurs puissent être considérées comme une coïncidence.

df = df_output.copy()
df = df[df["support_xy"]>=df["support_xy"].quantile(0.98)]
df.head()
x y sum_count_xy support_xy support_x support_y sum_count_x sum_count_y confidence lift sum_count
58 662193 667129 8 5.63555e-05 0.0036138 0.00281073 513 399 0.0155945 5.54822 141956
59 665672 667129 12 8.45332e-05 0.00395193 0.00281073 561 399 0.0213904 7.61026 141956
60 666435 667129 30 0.000211333 0.0082279 0.00281073 1168 399 0.0256849 9.13817 141956
62 666590 667129 7 4.93111e-05 0.00421257 0.00281073 598 399 0.0117057 4.16464 141956
63 666856 667129 8 5.63555e-05 0.00390966 0.00281073 555 399 0.0144144 5.12835 141956

Cela m'a sauvé les données de la paire xy que je pensais être arrivées par accident.
Puis le dernier processus, ne laissant que y, qui est pertinent pour x. Comme expliqué précédemment, cela signifie que seules les paires xy avec des valeurs de portance élevées doivent être conservées.
Ici, nous ne laisserons que les paires avec une élévation de 2.0 ou plus.

** À propos, il s'agit d'une image de la signification de l'ascenseur (valeur de l'ascenseur), mais c'est une valeur qui montre à quel point il est facile d'ajouter y produits au panier lorsque x produits sont ajoutés au panier par rapport à d'autres produits. **

Par exemple, dans le contenu du bloc de données ci-dessus, lorsque le produit "662193" (x) est ajouté au panier, le produit "667129" (y) est ** 5,5 fois plus facile à ajouter au panier * * Ça veut dire.

df = df[df["lift"]>=2.0]
df_recommendation = df.copy()
df_recommendation.head()
x y sum_count_xy support_xy support_x support_y sum_count_x sum_count_y confidence lift sum_count
58 662193 667129 8 5.63555e-05 0.0036138 0.00281073 513 399 0.0155945 5.54822 141956
59 665672 667129 12 8.45332e-05 0.00395193 0.00281073 561 399 0.0213904 7.61026 141956
60 666435 667129 30 0.000211333 0.0082279 0.00281073 1168 399 0.0256849 9.13817 141956
62 666590 667129 7 4.93111e-05 0.00421257 0.00281073 598 399 0.0117057 4.16464 141956
63 666856 667129 8 5.63555e-05 0.00390966 0.00281073 555 399 0.0144144 5.12835 141956

** Comment utiliser les résultats de l'association (= données de recommandation) **

Ceci termine la génération des données de recommandation de base.
L'utilisation spécifique de ceci est aussi simple que la promotion du produit "667129" auprès de la personne qui a ajouté le produit "662193" au panier, par exemple.
En fait, les données de cette recommandation sont stockées dans la base de données afin que y soit sorti lorsque x est entré.

Problèmes avec cette recommandation

Maintenant que j'ai créé les données de recommandation, je vais vous montrer les problèmes avec cette recommandation (sans la cacher).

Autrement dit ** il n'est pas possible de cibler tous les produits d'entrée (x). **

Vérifions ça.
Tout d'abord, vérifions le nombre de types de x dans les données de transaction d'origine.

#x est la marchandise des données originales_Puisqu'il s'agit d'un identifiant, il agrège le numéro unique de celui-ci.
df_cart_ori["goods_id"].nunique()
#production
8398

À l'origine, il existe 8398 types de x.
En d'autres termes, nous devrions être en mesure de recommander des produits hautement pertinents (y) pour ces 8398 types d'entrées (x).

Voyons maintenant à quelle quantité d'entrée (x) correspond la sortie précédente.

#Regroupez le nombre unique de x dans le résultat de la recommandation.
df_recommendation["x"].nunique()
#production
463

L'entrée (x) prise en charge par les données de recommandation ** ci-dessus ne prend en charge que 463 types. ** (463/8392, donc environ 5%)

C'est l'une des faiblesses des recommandations basées sur le comportement des utilisateurs. Peut-être, en réalité, faudra-t-il résoudre ce problème.
Les éléments suivants peuvent être considérés comme des contre-mesures.

Résumé

Pour les recommandations qui utilisent des associations, il est relativement facile de créer des données de recommandation si les données de transaction sont accumulées. Si vous essayez de créer un moteur de recommandation dans votre travail réel, essayez-le!

Recommended Posts

Tutoriel de recommandation utilisant l'analyse d'association (implémentation python)
Tutoriel de recommandation utilisant l'analyse d'association (concept)
Analyse d'association en Python
Analyse de données à l'aide de pandas python
Analyse des ondes cérébrales avec Python: tutoriel Python MNE
Implémentation des notifications de bureau à l'aide de Python
Recommandation d'analyse des données à l'aide de MessagePack
Tutoriel Python
[Didacticiel d'analyse Python dans la base de données avec SQL Server 2017] Étape 6: Utilisation du modèle
[Didacticiel d'analyse Python en base de données avec SQL Server 2017]
Python: analyse négative / positive: analyse négative / positive de Twitter à l'aide de RNN-Partie 1
Analyse de la variation temporelle des trous noirs en utilisant Python
Tutoriel Python Django (5)
Tutoriel Python Django (2)
Résumé du didacticiel Python
Analyse de données python
Commencez à utiliser Python
Effectuer une analyse d'entité à l'aide de spaCy / GiNZA en Python
Tutoriel Python Django (8)
Tutoriel Python Django (6)
PRML Chapitre 12 Mise en œuvre de l'analyse principale bayésienne Python
[Construction de l'environnement] Analyse des dépendances à l'aide de CaboCha avec Python 2.7
[Didacticiel d'analyse Python dans la base de données avec SQL Server 2017] Étape 2: importer des données dans SQL Server à l'aide de PowerShell
Tutoriel Python Django (7)
Tutoriel Python Django (1)
Tutoriel du didacticiel Python Django
Scraping à l'aide de Python
Tutoriel Python Django (3)
Tutoriel Python Django (4)
[Didacticiel d'analyse Python dans la base de données avec SQL Server 2017] Étape 4: Extraction de fonctionnalités de données à l'aide de T-SQL
[Python] Implémentation du clustering à l'aide d'un modèle gaussien mixte
Explication du concept d'analyse de régression à l'aide de python Partie 2
[Python] [Word] [python-docx] Analyse simple des données de diff en utilisant python
Fusion de la mise en œuvre du tri / analyse du montant du calcul et de l'expérimentation en Python
Explication du concept d'analyse de régression à l'aide de Python Partie 1
Analyse des composants principaux à l'aide de python de nim avec nimpy
Explication du concept d'analyse de régression à l'aide de Python Extra 1
[Livre technique] Introduction à l'analyse de données avec Python -1 Chapitre Introduction-
Analyse de données avec python 2
Python: analyse des séries chronologiques
Analyse des données à l'aide de xarray
Tutoriel [Docker] (Python + php)
Manipuler Redmine à l'aide de Python Redmine
Implémentation RNN en python
Séquence de Fibonacci utilisant Python
Implémentation ValueObject en Python
Présentation de l'analyse de données python
Analyse vocale par python
Nettoyage des données à l'aide de Python
Utilisation des packages Python #external
Câblage Communication Pi-SPI avec Python
Calcul de l'âge à l'aide de python
Modèle d'analyse de données Python
Mémo du didacticiel Python OpenCV
Rechercher sur Twitter avec Python
[Tutoriel Python] Structure des données
Analyse orthologue à l'aide d'OrthoFinder
Identification de nom à l'aide de python
Notes sur l'utilisation de sous-processus Python
Tutoriel Cloud Run (python)