From 89c501d4ea0ba4d0e49cee62d45c7b8035425703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matt=C3=A9o=20Delabre?= Date: Mon, 25 Nov 2019 21:32:07 -0500 Subject: [PATCH] Pageviews: Lissage par noyau gaussien --- data/pageviews/pageviews.py | 62 ++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/data/pageviews/pageviews.py b/data/pageviews/pageviews.py index f4dc7f5..f16d321 100644 --- a/data/pageviews/pageviews.py +++ b/data/pageviews/pageviews.py @@ -5,8 +5,10 @@ from datetime import datetime import logging import math import matplotlib +import numpy from matplotlib import pyplot import requests +from scipy import stats import sys import mwclient @@ -29,19 +31,46 @@ wikimedia_pageviews_start = datetime(2015, 7, 1) # Objet pour afficher le journal d’exécution logger = logging.getLogger('pageviews') -def wrapping_move_mean(data, window): - """ - Calcule une moyenne par fenêtre circulaire sur un tableau de valeurs. +# Tableau contenant tous les jours de l’année de 1 à 365 +year_all_days = numpy.arange(1, 366) - :param data: Tableau de valeurs. - :param window: Taille de la fenêtre. - :return: Tableau moyenné avec une fenêtre de taille donnée. Le résultat - a les même dimensions que l’entrée. + +def year_date_distance(a, b): """ - down_half_window = math.floor(window / 2) - up_half_window = math.ceil(window / 2) - padded_data = data[-down_half_window:] + data + data[:up_half_window] - return bottleneck.move_mean(padded_data, window=window)[window - 1:] + Calcule la distance entre deux jours de l’année. + + :example: year_date_distance(10, 360) == 15 + :example: year_date_distance(numpy.array([10, 182, 355]), 182) + == [172, 0, 173] + :param a: Première valeur (peut être un tableau numpy). + :param b: Seconde valeur (peut être un tableau numpy). + :return: Valeur de la distance. + """ + return numpy.stack(( + numpy.mod(a - b, len(year_all_days)), + numpy.mod(b - a, len(year_all_days)) + )).min(axis=0) + + +def year_smooth_gaussian(data, scale): + """ + Fait un lissage de tableau de valeurs avec une valeur par jour de l’année + en utilisant un noyau gaussien. + + À la bordure de fin ou de début d’année, le lissage est fait avec l’autre + morceau de l’année. + + :param data: Données à lisser. + :param scale: Variance du noyau gaussien. + :return: Données lissées. + """ + ref_pdf = stats.norm.pdf(year_date_distance(year_all_days, 1), scale=scale) + pdf_matrix = numpy.stack([ + numpy.roll(ref_pdf, day - 1) + for day in year_all_days + ]) + return pdf_matrix.dot(data) + def wikimedia_page_views(site, article): """ @@ -79,6 +108,7 @@ def wikimedia_page_views(site, article): for record in data['items'] )) + def wikimedia_article_canonical_name(site, article): """ Obtient le nom canonique d’un article après avoir suivi les redirections. @@ -97,6 +127,7 @@ def wikimedia_article_canonical_name(site, article): return original_page.resolve_redirect().name + def wikimedia_article_views(site, article): """ Obtient le nombre de visites sur un article Wikipédia, incluant la page @@ -127,10 +158,11 @@ def wikimedia_article_views(site, article): # Somme le nombre de visites sur chacune des pages return sum( (wikimedia_page_views(site, page) - for page in redirects + [article]), + for page in redirects + [article]), start=collections.Counter() ) + def wikimedia_mean_article_views(site, article): """ Obtient des statistiques moyennes sur les vues d’un article Wikipédia par @@ -170,7 +202,8 @@ def wikimedia_mean_article_views(site, article): key=lambda x: x[0] )] - return wrapping_move_mean(days, window=60) + return days + def create_year_plot(): """ @@ -190,6 +223,7 @@ def create_year_plot(): ax.set_xticklabels(calendar.month_abbr[1:13]) return fig, ax + if __name__ == '__main__': logging.basicConfig(level=logging.INFO) @@ -225,7 +259,7 @@ dénombrées comme une visite sur la page canonique. ) data = wikimedia_mean_article_views(site, canonical_article) - ax.plot(data, label=canonical_article) + ax.plot(year_smooth_gaussian(data, 10), label=canonical_article) ax.set_ylabel('Vues par jour') fig.legend()