Créer un moteur de recommandations de contenu avec #R et #GTM et booster vos temps de chargement


Suite à la conférence de Mark Edmondson au MeasureCamp du 5 Mars 2016, j’ai traduit son excellent article qui se trouve à cette adresse « http://code.markedmondson.me/predictClickOpenCPU/ » et ajouté une fonctionnalité qui permet d’accélérer les chargements de pages avec la balise prerender ( gain de 70% en moyenne sur Chrome )

Connecter R à Google Tag Manager ( GTM )
Il est possible de connecter R à Google Tag Manager avec un serveur OpenCPU et des fonctions R qui permettent de délivrer une API Rest.

Le concept est détaillé dans le schéma ci-dessous et l’objectif est de créer un moteur de recommandations de contenu basé sur ce qu’un utilisateur a vu durant sa session.

Voici l’architecture que Mark a mise en place pour sa présentation :

data dmpLe code source se trouve à cette adresse : https://github.com/MarkEdmondson1234/predictClickOpenCPU

Open CPU
OpenCPU est un système dédié au calcul scientifique et la recherche reproductible.
Le serveur OpenCPU permet :

  • d’avoir une API Rest pour faire de l’analyse de données basée sur R
  • de transformer votre code R en JSON
  • le support des librairies Javascript

Vous pouvez utiliser les serveurs publics ou héberger le vôtre.
Pour ce projet, il faut juste mettre en place un webhook Github vers OpenCPU et tout ce que vous allez pousser sur Github sera déployé sur votre serveur OpenCPU .

webhook github

Découvrez le fonctionnement de l’API OpenCPU
Le dépot Github est disponible sur OpenCPU à l’adresse suivante : https://MarkEdmondson1234.ocpu.io/predictClickOpenCPU/

Si vous avez besoin de documentation sur l’API OpenCPU : https://www.opencpu.org/api.html#api-ci

Testez le package R qui gère l’API
Vous pouvez tester facilement le fonctionnement de l’API sur un serveur de test.

Testez sur : https://public.opencpu.org/ocpu/test/
Créer une requête POST vers cette URL :
https://MarkEdmondson1234.ocpu.io/predictClickOpenCPU/R/predictMarkov/json

…avec les paramètres :
pageview_names
[« /example/96″, »/example/213″, »/example/107 »]


Si ça fonctionne, vous obtenez une réponse en statut 200 avec un JSON qui contient les résultats.

Code R pour générer le modèle prédictif
Voici le code R qui va générer ce modèle en trois étapes :

  • Analyser les données Google Analytics
## from https://github.com/MarkEdmondson1234/googleAnalyticsR_public
library(googleAnalyticsR)
ga_auth()

gaId <- xxxx # GA ViewId

## dimension3 contains userId in format:
## u={cid}&t={timestamp}
raw <- google_analytics(gaId,
 start = "2016-02-01",
 end = "2016-02-01",
 metrics = c("pageviews"),
 dimensions = c("dimension3","pagePath"),
 max_results = 20000)
  • Transformer les données en un format adapté pour le modèle
split_regex <- "u=(.+)&t=(.+)"
library(dplyr)
library(tidyr)
processed <- raw %>% tidyr::extract(dimension3,
 c("cid","timestamp"),
 split_regex)

## javascript to R timestamp
processed$timestamp <- as.POSIXct(as.numeric(processed$timestamp) / 1000,
 origin = "1970-01-01")

## find users with session length > 1
nonbounce <- processed %>% group_by(cid) %>%
 summarise(session_length = n()) %>% filter(session_length > 1) %>% ungroup()

processed <- nonbounce %>% left_join(processed)

processed <- processed %>% arrange(cid, timestamp)

## for each cid, make a string of pagePath in timestamp order
sequence <- processed %>% group_by(cid) %>%
 summarise(sequence = paste(aggregation, collapse = ","))

sequence <- paste(sequence$cid, sequence$sequence, sep=",")
  • Créer le modèle prédictif basé sur les chaines de Markov

Une chaine de Markov est une suite de variables aléatoires qui permet de modéliser l´évolution dynamique d’un système aléatoire.

La propriété fondamentale est que son évolution future ne dépend du passé qu’au travers de sa valeur actuelle.

Les applications sont très nombreuses :
– mathématiques financières
– gestion de stock
– algorithmes stochastiques d’optimisation

Voici une simple implémentation en R qui permet à partir des urls visitées de déterminer l’url suivante la plus probable.

library(clickstream)

# fitting a simple Markov chain and predicting the next click
clickstreams <- sequence
csf <- tempfile()
writeLines(clickstreams, csf)
cls <- readClickstreams(csf, header = TRUE)

## Make the model:

## 1612 users - 285 seconds
model <- fitMarkovChain(cls, verbose=TRUE)

### Using the model:

## get the likely pages a user starts from
likely_start <- as.data.frame(model@start)
likely_start <- likely_start[order(likely_start$Freq, decreasing = TRUE),]

## List of pages in the model
states(model)

## Prediction:
startPattern <- new("Pattern", sequence = c("/example/96","/example/213"))
predict(model, startPattern)

## pages that absorb (e.g. are last in session)
last_pages <- absorbingStates(model)

## model is saved so it can be uploaded to the R package for the predictions:
save(model, file="./data/model.RData")
  • Afficher les chaines de Markov

chainemarkov

Code GTM qui appelle OpenCPU

  • Créer un cookie qui enregistre toutes les urls visitées

Ce code est simple à déployer sur votre site avec GTM.
GTM sessionUrlscookie sessionUrl

 
//Tag : Write SessionUrls to Cookie
str = {{Page Path}};

 // parsing URL to fit model
 index = str.indexOf('.html');
 newUrl = str.substring(str.lastIndexOf("/predictClickOpenCPU/"),str.lastIndexOf("."), index);

 // if existing cookie append pageURL, else record this one
 if({{sessionUrls}}){
  sessionUrls = {{sessionUrls}} + "," + newUrl;
 } else {
  sessionUrls = newUrl;
 }

 //write session cookie
 document.cookie = 'sessionUrls=' + sessionUrls;
  • Créer un appel au serveur OpenCPU qui affiche le résultat avec l’url suivante la plus probable

GTM call opencpu

//Tag Read from OpenCPU
//code.jquery.com/jquery-1.10.2.min.js
//www.opencpu.org/js/archive/opencpu-0.4.js

//set CORS to call "predictClickOpenCPU" package on public server
ocpu.seturl("//MarkEdmondson1234.ocpu.io/predictClickOpenCPU/R")

//split character into array
var mydata = {{sessionUrls}}.split(",");

console.log(mydata);

//call R function: predictClickOpenCPU::predictMarkov(pageview_names=mydata)
var req = ocpu.rpc("predictMarkov", {
 pageview_names : mydata
 }, function(output){
 dataLayer.push({
 'event' : 'openCPUcall',
 'prediction': output.page[0],
 'probability': output.probability[0]
 });
 console.log("R returned: " + output);
});

//optional
req.fail(function(){
 console.log("R returned an error: " + req.responseText);
 });

Bonus : Gestion de la balise prerender pour précharger la page suivante la plus probable

  • Comment fonctionne cette balise ?
    – L’évenement prerender permet de faire gagner du temps sur le chargement de la page suivante.
    – Ce chargement s’effectue via un onglet caché dans Google Chrome et compte pour une page vue.
    – Il faut indiquer l’url de la page suivante dans la balise head du html.

    • Chrome : <link rel=”prerender” href=”http://www.example.com/page”>
    • Firefox : <link rel=”prerender prefetch” href=”http://www.example.com/page”>

    – Pour identifier la page suivante la plus probable, nous allons utiliser l’API OpenCPU.

prerender

 

  • Quelles sont les limites ?
    • Si la connexion internet est mauvaise, l’internaute devra attendre encore plus longtemps
      • La solution idéale serait d’utiliser cette balise en fonction de la connexion de l’internaute
    • Les pages avec des éléments médias en HTML5 ne peuvent pas être en prerender.
    • Les évènements alert() et print() désactivent le prerender
  • Code source Js à placer sur votre site
//set CORS to call "predictClickOpenCPU" package on public server
ocpu.seturl("//MarkEdmondson1234.ocpu.io/predictClickOpenCPU/R")

//split character into array
var mydata = {{sessionUrls}}.split(",");

//call R function: predictClickOpenCPU::predictMarkov(pageview_names=mydata)
var req = ocpu.rpc("predictMarkov", {
 pageview_names : mydata
 }, function(output){

 var relPreRender = document.createElement ("link");
 relPreRender.setAttribute("rel", "prerender");
 relPreRender.setAttribute("href",output.page[0]);
 document.getElementsByTagName("head")[0].appendChild(relPreRender);
});

Conclusion

Si je me base sur cette étude, les temps de chargement sont réduits de plus de 70% sur Chrome et et 20% sur Firefox qui acceptent cette balise.
resultat-temps-chargement

Les chaines de Markov sont très utilisées :

  • Modélisation des files d’attentes
  • Reconnaissance de formes
  • Le PageRank est défini comme un chaine de Markov
  • Les systèmes de Bonus/Malus des assurances

A vous de tester pour trouver de nouveaux cas d’usage ( Optimisation du tunnel de conversion, des menus à facette, .. )

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *