Partager la publication "Nettoyage du Spam dans Google Analytics avec Python"
Précédemment nous avions vu comment nettoyer le spam avec les segments directement dans Google Analytics et comment nettoyer ce spam avec R.
Cette fois nous allons voir comment le faire avec Python. Pour cela nous reprendrons la méthode d’importation des données de l’API de Google Analytics avec Python, que nous avions décrite précédemment. Démarrons tout de suite le code source.
Dans l’article précédent nous avions notamment importé les données de trafic de Networking Morbihan sur 7 ans 1/2 . Les données brutes présentaient des anomalies que nous allons nettoyer ici.
Code Source
N’hésitez pas à copier/coller les codes sources suivants. Comme nous vous l’indiquions précédemment nous faisons tourner le programme dans l’environnement de développement Spyder.
Attention le code source démarre ici à la récupération des données utiles. Pour voir comment se connecter à Google Analytics API reportez vous à notre article précédent sur l’importation.
Récupération des données pour nettoyage :
Afin de pouvoir filtrer le spam il sera nécessaire de récupérer des dimensions : hostname, browser, fullReferrer, sourceMedium, language, landingPagePath, pagePath dans Google Analytics.
########################################################################## # RECUPERATION DES DONNEES POUR FILTRAGE ########################################################################## #Pour mémoire Dimensions & Metrics Explorer #https://developers.google.com/analytics/devguides/reporting/core/dimsmets #Attention le nombre de dimensions est limité à 9 et de Metrics à 10. def get_gaPVAllYears(analytics): # Use the Analytics Service Object to query the Analytics Reporting API V4. return analytics.reports().batchGet( body={ 'reportRequests': [ { 'viewId': VIEW_ID, 'pageSize': 100000, #pour dépasser la limite de 1000 'dateRanges': [{'startDate': "2011-07-01", 'endDate': "2018-12-31"}], 'metrics': [{'expression': 'ga:pageviews'}], 'dimensions': [{'name': 'ga:date'}, {'name': 'ga:hostname'}, {'name': 'ga:browser'}, {'name': 'ga:fullReferrer'}, {'name': 'ga:sourceMedium'}, {'name': 'ga:language'}, {'name': 'ga:landingPagePath'}, {'name': 'ga:pagePath'}, {'name': 'ga:keyword'}], }] } ).execute() response = get_gaPVAllYears(analytics) gaPVAllYears = dataframe_response(response) gaPVAllYears.dtypes gaPVAllYears.count() #51717 enregistrements
Préparation des données :
Dans cette partie nous allons préparer les données pour être exploitables par la suite. Nous allons notamment créer une observation par page vue..
############################################################################### #Etape 1 préparation des données #changement des noms de variables pour manipuler les colonnes. gaPVAllYears = gaPVAllYears.rename(columns={'ga:browser': 'browser', 'ga:date': 'date', 'ga:fullReferrer': 'fullReferrer', 'ga:hostname': 'hostname', 'ga:keyword': 'keyword', 'ga:landingPagePath': 'landingPagePath', 'ga:language': 'language', 'ga:pagePath': 'pagePath', 'ga:pageviews': 'pageviews', 'ga:sourceMedium': 'sourceMedium'}) #creation de la variable Année à partir de ga:date gaPVAllYears['Année'] = gaPVAllYears['date'].astype(str).str[:4] #separation de la variable sourceMedium en source et medium gaPVAllYears['source'] = gaPVAllYears['sourceMedium'].str.split("/",1, expand = True)[0] gaPVAllYears['medium'] = gaPVAllYears['sourceMedium'].str.split("/",1, expand = True)[1] #transformation date string en datetime gaPVAllYears.date = pd.to_datetime(gaPVAllYears.date, format="%Y%m%d") #replication des lignes en fonction de la valeur de pageviews (dans R : uncount) #i.e. : une ligne par page vue gaPVAllYears = gaPVAllYears.reindex(gaPVAllYears.index.repeat(gaPVAllYears.pageviews)) gaPVAllYears = gaPVAllYears.reset_index(drop=True) #reindexation gaPVAllYears.pageviews = 1 #tous les pageviews à 1 maintenant #### Verifs gaPVAllYears[['date', 'pageviews']] gaPVAllYears.dtypes gaPVAllYears.count() #82559 enregistrements !!!! au 31/12/2018 comme dans R ############################################################################### # Sauvegarde en csv pour éviter de faire des appels à GA gaPVAllYears.to_csv("gaPVAllYears.csv", sep=";", index=False) #séparateur ; ###############################################################################
Nettoyage des langues suspectes :
Remarque préalable : pour ceux qui n’ont pas réussi à récupérer leurs données avec l’API nous vous proposons de récupérer nos données brutes en .zip ici : https://github.com/Anakeyn/CleanSpamGAwPython/blob/master/gaPVAllYears.zip, afin de tester le reste du programme.
Dans cette partie nous allons vérifier que les langues sont bien au format ISO « langue-pays » : xxx-xxx, par exemple : fr-FR, fr-BE, es ..
#Relecture ############ myDateToParse = ['date'] #pour parser la variable date en datetime sinon object gaPVAllYears = pd.read_csv("gaPVAllYears.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse) #verifs gaPVAllYears.dtypes gaPVAllYears.count() #82559 enregistrements gaPVAllYears.head(20) ############################################################################## ########################################################################## #Nettoyage des langues suspectes. ########################################################################## pattern = "^[a-zA-Z]{2,3}([-/][a-zA-Z]{2,3})?$" indexGoodlang = gaPVAllYears[(gaPVAllYears.language.str.contains(pat=pattern,regex=True)==True)].index gaPVAllYearsCleanLanguage=gaPVAllYears.iloc[indexGoodlang] gaPVAllYearsCleanLanguage.reset_index(inplace=True, drop=True) #reindexation. gaPVAllYearsCleanLanguage.dtypes gaPVAllYearsCleanLanguage.count() #76733 gaPVAllYearsCleanLanguage[['date', 'pageviews']] #creation de la dataframe daily_data par jour dfDatePV = gaPVAllYearsCleanLanguage[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues daily_data = dfDatePV.groupby(dfDatePV['date']).count() # #dans l'opération précédente la date est partie dans l'index et pageviews a pris le décompte daily_data['date'] = daily_data.index # daily_data['cnt_ma30'] = daily_data['pageviews'].rolling(window=30).mean() daily_data['Année'] = daily_data['date'].astype(str).str[:4] #Graphique pages vues par jour sns.set() #paramètres esthétiques ressemble à ggplot par défaut. fig, ax = plt.subplots() #un seul plot sns.lineplot(x='date', y='pageviews', hue='Année', data= daily_data, palette=sns.color_palette("husl",n_colors=8)) fig.suptitle("L'anomalie de fin 2016 a disparu ", fontsize=14, fontweight='bold') ax.set(xlabel='Année', ylabel='Nbre pages vues / jour', title='suite au nettoyage des langues.') fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 \n Données nettoyées variable langue", fontsize=9, ha="right") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) #plt.show() fig.savefig("PV-s2011-Clean-Lang.png", bbox_inches="tight", dpi=600) #Graphique Moyenne Mobile 30 jours. sns.set() #paramètres esthétiques ressemble à ggplot par défaut. fig, ax = plt.subplots() #un seul plot sns.lineplot(x='date', y='cnt_ma30', hue='Année', data= daily_data, palette=sns.color_palette("husl",n_colors=8)) fig.suptitle("L'anomalie de fin 2016 a disparu ", fontsize=14, fontweight='bold') ax.set(xlabel='Année', ylabel='Nbre pages vues / jour en moyenne mobile', title='suite au nettoyage des langues.') fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 en moy. mob. 30 j. \n Données nettoyées variable langue", fontsize=9, ha="right") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) #plt.show() fig.savefig("PV-s2011-Clean-Lang-mm30.png", bbox_inches="tight", dpi=600) ############################################################################### # Sauvegarde en csv gaPVAllYearsCleanLanguage.to_csv("gaPVAllYearsCL.csv", sep=";", index=False) #séparateur ; ###############################################################################
Graphique Pages VUES après nettoyage des langues suspectes
Nettoyage des Ghostnames :
Les Ghostnames sont des noms de domaine qui hébergent votre code de suivi de Google Analytics (sans votre consentement en général :-(). Ici nous n’allons conserver que les sites qui sont légitimes comme monsite.com ou encore des sites de cache comme celui de Google, par exemple :
webcache.googleusercontent.com.
On enlève aussi les sous-domaines que l’on ne veut pas garder, ici pour nous loc.networking-morbihan.com .
#Relecture ############ myDateToParse = ['date'] #pour parser la variable date en datetime sinon object gaPVAllYearsCleanLanguage = pd.read_csv("gaPVAllYearsCL.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse) #verifs gaPVAllYearsCleanLanguage.dtypes gaPVAllYearsCleanLanguage.count() #76733 enregistrements gaPVAllYearsCleanLanguage.head(20) ############################################################################## ########################################################################## #nettoyage des hostnames. ########################################################################## #Pour faciliter la lecture on va créer une liste de patterns #on garde ceux qui nous intéressent patternGoodHostname = ["networking-morbihan\.com", "translate\.googleusercontent\.com", "webcache\.googleusercontent\.com", "networking-morbihan\.com\.googleweblight\.com", "web\.archive\.org"] #on regroupe en une seule pattern pattern = '|'.join(patternGoodHostname) indexGoodHostname = gaPVAllYearsCleanLanguage[(gaPVAllYearsCleanLanguage.hostname.str.contains(pat=pattern,regex=True)==True)].index gaPVAllYearsCleanHost1 = gaPVAllYearsCleanLanguage.iloc[indexGoodHostname] gaPVAllYearsCleanHost1.reset_index(inplace=True, drop=True) #reindexation. gaPVAllYearsCleanHost1.dtypes gaPVAllYearsCleanHost1.count() #76170 #on vire loc.networking-morbihan.com qui restait patternBadHostname = "loc\.networking-morbihan\.com" #on garde ceux qui ne correspondent pas à la pattern attention ici ==False indexGoodHostname = gaPVAllYearsCleanHost1[(gaPVAllYearsCleanHost1.hostname.str.contains(pat=patternBadHostname,regex=True)==False)].index gaPVAllYearsCleanHost = gaPVAllYearsCleanHost1.iloc[indexGoodHostname] gaPVAllYearsCleanHost.reset_index(inplace=True, drop=True) #reindexation. gaPVAllYearsCleanHost.dtypes gaPVAllYearsCleanHost.count() #76159 #creation de la dataframe daily_data par jour dfDatePV = gaPVAllYearsCleanHost[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues daily_data = dfDatePV.groupby(dfDatePV['date']).count() # #dans l'opération précédente la date est partie dans l'index daily_data['date'] = daily_data.index # daily_data['cnt_ma30'] = daily_data['pageviews'].rolling(window=30).mean() daily_data['Année'] = daily_data['date'].astype(str).str[:4] #Graphique pages vues par jour sns.set() #paramètres esthétiques ressemble à ggplot par défaut. fig, ax = plt.subplots() #un seul plot sns.lineplot(x='date', y='pageviews', hue='Année', data= daily_data, palette=sns.color_palette("husl",n_colors=8)) fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold') ax.set(xlabel='Année', ylabel='Nbre pages vues / jour', title='suite au nettoyage des Hostnames par rapport au nettoyage des langues.') fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 \n Données nettoyées variable hostname", fontsize=9, ha="right") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) #plt.show() fig.savefig("PV-s2011-Clean-Host.png", bbox_inches="tight", dpi=600) #Graphique Moyenne Mobile 30 jours. sns.set() #paramètres esthétiques ressemble à ggplot par défaut. fig, ax = plt.subplots() #un seul plot sns.lineplot(x='date', y='cnt_ma30', hue='Année', data= daily_data, palette=sns.color_palette("husl",n_colors=8)) fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold') ax.set(xlabel='Année', ylabel='Nbre pages vues / jour en moyenne mobile', title='suite au nettoyage des Hostnames par rapport au nettoyage des langues.') fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 en moy. mob. 30 j. \n Données nettoyées variable hostname", fontsize=9, ha="right") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) #plt.show() fig.savefig("PV-s2011-Clean-Host-mm30.png", bbox_inches="tight", dpi=600) ############################################################################### # Sauvegarde en csv gaPVAllYearsCleanHost.to_csv("gaPVAllYearsCH.csv", sep=";", index=False) #séparateur ; ###############################################################################
Graphique Pages VUES après nettoyage des GHOSTNAMES
Nettoyage des browsers suspects
Avant de virer les browsers suspects il est nécessaire de vérifier à la main ce que vous avez dans la variable browser. Cela peut changer par rapport à nous .
#Relecture ############ myDateToParse = ['date'] #pour parser la variable date en datetime sinon object gaPVAllYearsCleanHost = pd.read_csv("gaPVAllYearsCH.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse) #verifs gaPVAllYearsCleanHost.dtypes gaPVAllYearsCleanHost.count() #76159 enregistrements gaPVAllYearsCleanHost.head(20) ############################################################################## ########################################################################## #nettoyage des browser suspects - peut contenir des robots crawlers ########################################################################## #voyons ce qu'il y a dedans gaPVAllYearsCleanHost['browser'].value_counts() #on vire les "curiosités" et les bots patternBadBrowser = ["not set","Google\\.com", "en-us", "GOOG", "PagePeeker\\.com", "bot"] #on regroupe en une seule pattern pattern = '|'.join(patternBadBrowser) #on garde ceux qui ne correspondent pas à la pattern attention ici ==False indexGoodBrowser = gaPVAllYearsCleanHost[(gaPVAllYearsCleanHost.browser.str.contains(pat=pattern,regex=True)==False)].index gaPVAllYearsCleanBrowser = gaPVAllYearsCleanHost.iloc[indexGoodBrowser] gaPVAllYearsCleanBrowser.reset_index(inplace=True, drop=True) #reindexation. gaPVAllYearsCleanBrowser.dtypes gaPVAllYearsCleanBrowser.count() #76126 #creation de la dataframe daily_data par jour dfDatePV = gaPVAllYearsCleanBrowser[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues daily_data = dfDatePV.groupby(dfDatePV['date']).count() # #dans l'opération précédente la date est partie dans l'index daily_data['date'] = daily_data.index # daily_data['cnt_ma30'] = daily_data['pageviews'].rolling(window=30).mean() daily_data['Année'] = daily_data['date'].astype(str).str[:4] #Graphique pages vues par jour sns.set() #paramètres esthétiques ressemble à ggplot par défaut. fig, ax = plt.subplots() #un seul plot sns.lineplot(x='date', y='pageviews', hue='Année', data= daily_data, palette=sns.color_palette("husl",n_colors=8)) fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold') ax.set(xlabel='Année', ylabel='Nbre pages vues / jour', title='suite au nettoyage des browsers suspects par rapport aux nettoyages précédents.') fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 \n Données net. variable browser", fontsize=9, ha="right") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) #plt.show() fig.savefig("PV-s2011-Clean-Browser.png", bbox_inches="tight", dpi=600) #Graphique Moyenne Mobile 30 jours. sns.set() #paramètres esthétiques ressemble à ggplot par défaut. fig, ax = plt.subplots() #un seul plot sns.lineplot(x='date', y='cnt_ma30', hue='Année', data= daily_data, palette=sns.color_palette("husl",n_colors=8)) fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold') ax.set(xlabel='Année', ylabel='Nbre pages vues / jour en moyenne mobile', title='suite au nettoyage des browsers suspects par rapport aux nettoyages précédents.') fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 en moy. mob. 30 j. \n Données net. variable browser", fontsize=9, ha="right") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) #plt.show() fig.savefig("PV-s2011-Clean-Host-mm30.png", bbox_inches="tight", dpi=600) ############################################################################### # Sauvegarde en csv gaPVAllYearsCleanBrowser.to_csv("gaPVAllYearsCB.csv", sep=";", index=False) #séparateur ; ###############################################################################
Graphique Pages VUES après nettoyage de la variable Browser
Nettoyage des Crawlers Spammers et sources de trafic non désirées dans la variable source.
Afin de réaliser cette opération nous aurons besoin de récupérer un fichier de sites blacklistés. Nous en avons créé un que vous pouvez récupérer dans cette archive sur notre Github. Toutefois il est possible que vous soyez obligé de le compléter au vu de ce que vous avez dans vos données.
#Relecture ############ myDateToParse = ['date'] #pour parser la variable date en datetime sinon object gaPVAllYearsCleanBrowser = pd.read_csv("gaPVAllYearsCB.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse) #verifs gaPVAllYearsCleanBrowser.dtypes gaPVAllYearsCleanBrowser.count() #76126 enregistrements gaPVAllYearsCleanBrowser.head(20) ############################################################################## ########################################################################## #nettoyage des Crawlers Spammers et autres sources de trafic non désirées #dans source ########################################################################## gaPVAllYearsCleanBrowser['source'].value_counts() gaPVAllYearsCleanSource = gaPVAllYearsCleanBrowser.copy() #=on fait une copie ici #la liste des sites et mots clés non désirés est dans un fichier que #nous avons créé. dfBlacklistSites = pd.read_csv("blacklist-source-sites.csv", sep=";") patternBadSource = dfBlacklistSites["blacksites"].tolist() #ça plante si on le fait en une fois, on va devoir diviser en paquets len(patternBadSource) step = 500 steps = list(range(0, len(patternBadSource), step)) j=0 for i in steps: if (i+step < len(patternBadSource) ) : imax=i+step else : imax = len(patternBadSource) print("i=",i) print("imax=",imax) patternBadSourcePack = '|'.join(patternBadSource[i:imax]) indexGoodSource = gaPVAllYearsCleanSource[(gaPVAllYearsCleanSource.source.str.contains(pat=patternBadSourcePack,regex=True)==False)].index print("indexGoodSource size =", indexGoodSource.size) gaPVAllYearsCleanSource = gaPVAllYearsCleanSource.iloc[indexGoodSource] gaPVAllYearsCleanSource.reset_index(inplace=True, drop=True) #on reindexe gaPVAllYearsCleanSource.reset_index(inplace=True, drop=True) #reindexation. pas sur que cela serve beaucoup ici ###################!!!!!!!!!!!!!!!!!!!!!!!!! gaPVAllYearsCleanSource.dtypes gaPVAllYearsCleanSource.count() #74275 #même chose qu'avec R !!!!!! 74275 #pour vérifier ce que l'on a dans la variable gaPVAllYearsCleanSource['source'].value_counts() # Sauvegarde en csv gaPVAllYearsCleanSource['source'].value_counts().to_csv("gaPVAllYearsCSSC.csv", sep=";") #séparateur ; ######### #creation de la dataframe daily_data par jour dfDatePV = gaPVAllYearsCleanSource[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues daily_data = dfDatePV.groupby(dfDatePV['date']).count() # #dans l'opération précédente la date est partie dans l'index daily_data['date'] = daily_data.index # daily_data['cnt_ma30'] = daily_data['pageviews'].rolling(window=30).mean() daily_data['Année'] = daily_data['date'].astype(str).str[:4] #Graphique pages vues par jour sns.set() #paramètres esthétiques ressemble à ggplot par défaut. fig, ax = plt.subplots() #un seul plot sns.lineplot(x='date', y='pageviews', hue='Année', data= daily_data, palette=sns.color_palette("husl",n_colors=8)) fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold') ax.set(xlabel='Année', ylabel='Nbre pages vues / jour', title='suite au nettoyage des referrers suspects par rapport aux nettoyages précédents.') fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 \n Données net. variable source", fontsize=9, ha="right") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) #plt.show() fig.savefig("PV-s2011-Clean-Source.png", bbox_inches="tight", dpi=600) #Graphique Moyenne Mobile 30 jours. sns.set() #paramètres esthétiques ressemble à ggplot par défaut. fig, ax = plt.subplots() #un seul plot sns.lineplot(x='date', y='cnt_ma30', hue='Année', data= daily_data, palette=sns.color_palette("husl",n_colors=8)) fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold') ax.set(xlabel='Année', ylabel='Nbre pages vues / jour en moyenne mobile', title='suite au nettoyage des referrers suspects par rapport aux nettoyages précédents.') fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 en moy. mob. 30 j. \n Données net. variable source", fontsize=9, ha="right") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) #plt.show() fig.savefig("PV-s2011-Clean-Source-mm30.png", bbox_inches="tight", dpi=600) ############################################################################### # Sauvegarde en csv gaPVAllYearsCleanSource.to_csv("gaPVAllYearsCS.csv", sep=";", index=False) #séparateur ; ###############################################################################
Graphique Pages VUES après nettoyage de la variable SOURCE
Nettoyage des fausses pages référentes dans la variable fullReferrer
Il s’agit ici de fausses pages référentes mais sur des sites légitimes. Nous avons aussi créé un fichier blacklist-fullReferrer-Page.csv que vous pouvez récupérer sur notre Github.
#Relecture ############ myDateToParse = ['date'] #pour parser la variable date en datetime sinon object gaPVAllYearsCleanSource = pd.read_csv("gaPVAllYearsCS.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse) #verifs gaPVAllYearsCleanSource.dtypes gaPVAllYearsCleanSource.count() #74275 enregistrements gaPVAllYearsCleanSource.head(20) ############################################################################## ########################################################################## #nettoyage des fausses pages référentes dans fullReferrer ########################################################################## gaPVAllYearsCleanFullReferrer = gaPVAllYearsCleanSource.copy() #=on fait une copie ici #la liste des pages non désirées est dans un fichier que #nous avons créé. dfBlacklistFullReferrers = pd.read_csv("blacklist-fullRefferer-Page.csv", sep=";") patternBadFullReferrer = dfBlacklistFullReferrers["Blackpages"].tolist() pattern = '|'.join(patternBadFullReferrer) indexGoodFullReferrer = gaPVAllYearsCleanFullReferrer[(gaPVAllYearsCleanFullReferrer.fullReferrer.str.contains(pat=pattern,regex=True)==False)].index gaPVAllYearsCleanFullReferrer = gaPVAllYearsCleanFullReferrer.iloc[indexGoodFullReferrer] gaPVAllYearsCleanFullReferrer.reset_index(inplace=True, drop=True) #on reindexe gaPVAllYearsCleanFullReferrer.count() #73829 #même chose qu'avec R !!!!!! #creation de la dataframe daily_data par jour dfDatePV = gaPVAllYearsCleanFullReferrer[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues daily_data = dfDatePV.groupby(dfDatePV['date']).count() # #dans l'opération précédente la date est partie dans l'index daily_data['date'] = daily_data.index # daily_data['cnt_ma30'] = daily_data['pageviews'].rolling(window=30).mean() daily_data['Année'] = daily_data['date'].astype(str).str[:4] #Graphique pages vues par jour sns.set() #paramètres esthétiques ressemble à ggplot par défaut. fig, ax = plt.subplots() #un seul plot sns.lineplot(x='date', y='pageviews', hue='Année', data= daily_data, palette=sns.color_palette("husl",n_colors=8)) fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold') ax.set(xlabel='Année', ylabel='Nbre pages vues / jour', title='suite au nettoyage des pages référentes suspectes par rapport aux nettoyages précédents.') fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 \n Données net. variable fullReferrer", fontsize=9, ha="right") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) #plt.show() fig.savefig("PV-s2011-Clean-FullReferrer.png", bbox_inches="tight", dpi=600) #Graphique Moyenne Mobile 30 jours. sns.set() #paramètres esthétiques ressemble à ggplot par défaut. fig, ax = plt.subplots() #un seul plot sns.lineplot(x='date', y='cnt_ma30', hue='Année', data= daily_data, palette=sns.color_palette("husl",n_colors=8)) fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold') ax.set(xlabel='Année', ylabel='Nbre pages vues / jour en moyenne mobile', title='suite au nettoyage des pages référentes suspectes par rapport aux nettoyages précédents.') fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 en moy. mob. 30 j. \n Données net. variable fullReferrer", fontsize=9, ha="right") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) #plt.show() fig.savefig("PV-s2011-Clean-FullReferrer-mm30.png", bbox_inches="tight", dpi=600) ############################################################################### # Sauvegarde en csv gaPVAllYearsCleanFullReferrer.to_csv("gaPVAllYearsCFR.csv", sep=";", index=False) #séparateur ; ###############################################################################
Graphique Pages VUES après nettoyage de la variable fullReFERRER
Nettoyage des pages d’administration
Il s’agit ici des pages d’administration de votre site Web et qui dépendent de votre CMS. Donc vous serez obligé de l’adapter. Pour nous il s’agit de WordPress.
#Relecture ############ myDateToParse = ['date'] #pour parser la variable date en datetime sinon object gaPVAllYearsFullReferrer = pd.read_csv("gaPVAllYearsCFR.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse) #verifs gaPVAllYearsCleanFullReferrer.dtypes gaPVAllYearsCleanFullReferrer.count() #73829 enregistrements gaPVAllYearsCleanFullReferrer.head(20) ############################################################################## ########################################################################## #nettoyage des pages d'administration dans pagePath ########################################################################## gaPVAllYearsCleanPagePath = gaPVAllYearsCleanFullReferrer.copy() #=on fait une copie ici #on vire les accès à l'administration et les pages vues depuis l'administration patternBadPagePath = ["/wp-login\\.php", "/wp-admin/", "/cron/", "/?p=\\d"] pattern = '|'.join(patternBadPagePath) indexGoodPagePath = gaPVAllYearsCleanPagePath[(gaPVAllYearsCleanPagePath.pagePath.str.contains(pat=pattern,regex=True)==False)].index gaPVAllYearsCleanPagePath = gaPVAllYearsCleanPagePath.iloc[indexGoodPagePath] gaPVAllYearsCleanPagePath.reset_index(inplace=True, drop=True) #on reindexe gaPVAllYearsCleanPagePath.count() #73301 #même chose qu'avec R !!!!!! #creation de la dataframe daily_data par jour dfDatePV = gaPVAllYearsCleanPagePath[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues daily_data = dfDatePV.groupby(dfDatePV['date']).count() # #dans l'opération précédente la date est partie dans l'index daily_data['date'] = daily_data.index # daily_data['cnt_ma30'] = daily_data['pageviews'].rolling(window=30).mean() daily_data['Année'] = daily_data['date'].astype(str).str[:4] #Graphique pages vues par jour sns.set() #paramètres esthétiques ressemble à ggplot par défaut. fig, ax = plt.subplots() #un seul plot sns.lineplot(x='date', y='pageviews', hue='Année', data= daily_data, palette=sns.color_palette("husl",n_colors=8)) fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold') ax.set(xlabel='Année', ylabel='Nbre pages vues / jour', title="suite au nettoyage des pages d'administration par rapport aux nettoyages précédents.") fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 \n Données net. variable pagePath", fontsize=9, ha="right") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) #plt.show() fig.savefig("PV-s2011-Clean-PagePath.png", bbox_inches="tight", dpi=600) #Graphique Moyenne Mobile 30 jours. sns.set() #paramètres esthétiques ressemble à ggplot par défaut. fig, ax = plt.subplots() #un seul plot sns.lineplot(x='date', y='cnt_ma30', hue='Année', data= daily_data, palette=sns.color_palette("husl",n_colors=8)) fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold') ax.set(xlabel='Année', ylabel='Nbre pages vues / jour en moyenne mobile', title="suite au nettoyage des pages d'administration par rapport aux nettoyages précédents.") fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 en moy. mob. 30 j. \n Données net. variable pagePath", fontsize=9, ha="right") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) #plt.show() fig.savefig("PV-s2011-Clean-PagePath-mm30.png", bbox_inches="tight", dpi=600) ############################################################################### # Sauvegarde en csv gaPVAllYearsCleanPagePath.to_csv("gaPVAllYearsCPP.csv", sep=";", index=False) #séparateur ; ###############################################################################
PAGE VUES APRES SUPPRESSION DES PAGES D’ADMINISTRATION
Nettoyage des pages dont l’entrée sur le site s’est faite via l’administration : variable landingPagePath.
Comme précédemment, la liste des pages dépend de votre CMS. pour nous il s’agit de WordPress.
#Relecture ############ myDateToParse = ['date'] #pour parser la variable date en datetime sinon object gaPVAllYearsCleanPagePath = pd.read_csv("gaPVAllYearsCPP.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse) #verifs gaPVAllYearsCleanPagePath.dtypes gaPVAllYearsCleanPagePath.count() #73301 enregistrements gaPVAllYearsCleanPagePath.head(20) ############################################################################## ########################################################################## #nettoyage des pages dont l'entrée sur le site s'est faite #via l'administration, variable landingPagePath ########################################################################## gaPVAllYearsCleanLandingPagePath = gaPVAllYearsCleanPagePath.copy() #=on fait une copie ici patternBadLandingPagePath = ["/wp-login\\.php", "/wp-admin/", "/cron/", "/?p=\\d"] pattern = '|'.join(patternBadLandingPagePath) indexGoodLandingPagePath = gaPVAllYearsCleanLandingPagePath[(gaPVAllYearsCleanLandingPagePath.landingPagePath.str.contains(pat=pattern,regex=True)==False)].index gaPVAllYearsCleanLandingPagePath = gaPVAllYearsCleanLandingPagePath.iloc[indexGoodLandingPagePath] gaPVAllYearsCleanLandingPagePath.reset_index(inplace=True, drop=True) #on reindexe gaPVAllYearsCleanLandingPagePath.count() #72822 #même chose qu'avec R !!!!!! #creation de la dataframe daily_data par jour dfDatePV = gaPVAllYearsCleanLandingPagePath[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues daily_data = dfDatePV.groupby(dfDatePV['date']).count() # #dans l'opération précédente la date est partie dans l'index daily_data['date'] = daily_data.index #recrée la colonne date. daily_data['cnt_ma30'] = daily_data['pageviews'].rolling(window=30).mean() daily_data['Année'] = daily_data['date'].astype(str).str[:4] #Graphique pages vues par jour sns.set() #paramètres esthétiques ressemble à ggplot par défaut. fig, ax = plt.subplots() #un seul plot sns.lineplot(x='date', y='pageviews', hue='Année', data= daily_data, palette=sns.color_palette("husl",n_colors=8)) fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold') ax.set(xlabel='Année', ylabel='Nbre pages vues / jour', title="suite au nettoyage des pages d'administration référentes par rapport aux nettoyages précédents.") fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 \n Données net. variable landingPagePath", fontsize=9, ha="right") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) #plt.show() fig.savefig("PV-s2011-Clean-LandingPagePath.png", bbox_inches="tight", dpi=600) #Graphique Moyenne Mobile 30 jours. sns.set() #paramètres esthétiques ressemble à ggplot par défaut. fig, ax = plt.subplots() #un seul plot sns.lineplot(x='date', y='cnt_ma30', hue='Année', data= daily_data, palette=sns.color_palette("husl",n_colors=8)) fig.suptitle("L'évolution du nombre de pages vues ne se voit pas à l'oeil nu ", fontsize=14, fontweight='bold') ax.set(xlabel='Année', ylabel='Nbre pages vues / jour en moyenne mobile', title="suite au nettoyage des pages d'administration référentes par rapport aux nettoyages précédents.") fig.text(.9,-.05,"Nombre de pages vues par jour depuis 2011 en moy. mob. 30 j. \n Données net. variable landingPagePath", fontsize=9, ha="right") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) #plt.show() fig.savefig("PV-s2011-Clean-LandingPagePath-mm30.png", bbox_inches="tight", dpi=600) ############################################################################### # Sauvegarde en csv gaPVAllYearsCleanLandingPagePath.to_csv("gaPVAllYearsCLPP.csv", sep=";", index=False) #séparateur ; ###############################################################################
PAGES VUES APRES NETTOYAGE DE la variable landingPAGEPATH
Il s’agissait de la dernière étape. Au début nous avions 82559 observations et à la fin nous trouvons 72821 enregistrements soit près de 10000 ou 15% de spam ce qui n’est pas négligeable.
Jeu de données nettoyé
Pour finir, comparons les différentes années sur le jeu de données nettoyé.
#Relecture ############ myDateToParse = ['date'] #pour parser la variable date en datetime sinon object gaPVAllYearsCleanLandingPagePath = pd.read_csv("gaPVAllYearsCLPP.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse) #verifs gaPVAllYearsCleanLandingPagePath.dtypes gaPVAllYearsCleanLandingPagePath.count() #72821 enregistrements gaPVAllYearsCleanLandingPagePath.head(20) ############################################################################## ########################################################################## # Jeu de données nettoyé ########################################################################## #nom de sauvegarde plus facile à retenir : dfPageViews = gaPVAllYearsCleanLandingPagePath.copy() #=on fait une copie ici ############################################################################### # Sauvegarde en csv dfPageViews.to_csv("dfPageViews.csv", sep=";", index=False) #séparateur ; ############################################################################### #Relecture ############ myDateToParse = ['date'] #pour parser la variable date en datetime sinon object dfPageViews = pd.read_csv("dfPageViews.csv", sep=";", dtype={'Année':object}, parse_dates=myDateToParse) #verifs dfPageViews.dtypes dfPageViews.count() #72822 enregistrements dfPageViews.head(20) ############################################################################## #creation de la dataframe daily_data par jour dfDatePV = dfPageViews[['date', 'pageviews']].copy() #nouveau dataframe avec que la date et le nombre de pages vues daily_data = dfDatePV.groupby(dfDatePV['date']).count() # #dans l'opération précédente la date est partie dans l'index daily_data['date'] = daily_data.index #recrée la colonne date. daily_data['cnt_ma30'] = daily_data['pageviews'].rolling(window=30).mean() daily_data['Année'] = daily_data['date'].astype(str).str[:4] daily_data['DayOfYear'] = daily_data['date'].dt.dayofyear #récupère la date du jour #Graphique Moyenne Mobile 30 jours. sns.set() #paramètres esthétiques ressemble à ggplot par défaut. fig, ax = plt.subplots() #un seul plot sns.lineplot(x='DayOfYear', y='cnt_ma30', hue='Année', data= daily_data, palette=sns.color_palette("husl",n_colors=8)) fig.suptitle("Les données présentent une saisonnalité : ", fontsize=14, fontweight='bold') ax.set(xlabel="Numéro de Jour dans l'année", ylabel='Nbre pages vues / jour en moyenne mobile', title="Le trafic baisse en général en été.") fig.text(.9,-.05,"Comparatif Nbre pages vues par jour par an moy. mob. 30 jours \n Données nettoyées", fontsize=9, ha="right") plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) #plt.show() fig.savefig("PV-Comparatif-mm30.png", bbox_inches="tight", dpi=600) # Sauvegarde en csv daily_data.to_csv("DailyDataCleanPython.csv", sep=";", index=False) #séparateur ; ########################################################################## # MERCI pour votre attention ! ########################################################################## #on reste dans l'IDE #if __name__ == '__main__': # main()
Graphique Comparatif des PAGES VUES EN MOYENNE MOBILE SUR 30 JOURS PAR ANNEES
Vous pouvez retrouver le code source en entier et les fichiers nécessaires dans notre Github à l’adresse https://github.com/Anakeyn/CleanSpamGAwPython
N’hésitez pas à laisser vos avis, conseils et remarques en commentaires,
A Bientôt,
Pierre