Posts in "Data analysis"

Comment extraire les données de navigation depuis Piano Analytics ?

J’ai régulièrement à analyser des données de navigation à l’aide du module d’exploration de Piano Analytics. Toutefois, il est parfois compliqué d’extraire ces données pour les retravailler dans Excel ou les présenter dans PowerPoint.

Explication sur le contenu de l’extract

Pour faciliter ce processus, j’ai développé un petit code JavaScript permettant d’extraire l’ensemble des données de navigation affichées dans l’explorateur Piano. Ce script génère un fichier CSV contenant, pour chaque page, les informations suivantes :

  • Si la page est sélectionnée dans le chemin de navigation en cours
  • L’étape à laquelle appartient la page
  • La position de la page dans l’étape (triée en fonction du volume)
  • Le niveau 2 associé
  • Le nom de la page
  • Le nombre d’occurrences
  • Le taux de sortie (uniquement disponible dans l’analyse exploratoire « après une page »)

Attention : Si vous effectuez une extraction en mode concentration (« avant une page »), les numéros d’étapes sont inversés (1 pour la dernière étape, 2 pour l’avant-dernière étape, etc.).

Utilisation du code

Le script JavaScript lit le DOM de la page pour récupérer les informations nécessaires et générer un fichier CSV. Pour l’utiliser, vous avez deux options.

  • Ajouter le code en favori dans votre navigateur : Suivez les instructions ici pour installer un bookmarklet JavaScript, puis cliquez dessus lorsque vous êtes dans l’analyse de navigation
javascript:(function(){function extractDataAndExportToCSV(){const url=window.location.href;if(url.includes("https://analytics.piano.io/explorer/#/treeview/")){const stickers=document.querySelectorAll('.ats-treeview-container .treeview-sticker');const data=[];stickers.forEach(sticker=>{const isSelected=sticker.classList.contains('sticker-selected');const classList=Array.from(sticker.classList);const positionClass=classList.find(cls=>cls.startsWith('treeview-sticker-'));const x=parseInt(positionClass.split('-')[2]);const y=parseInt(positionClass.split('-')[3]);const level2=sticker.querySelector('.ats-b-treeview-sticker-subtitle')?.innerText||'';const pageName=sticker.querySelector('.ats-b-treeview-sticker-title')?.innerText||'';const occurrence=(sticker.querySelector('.ats-b-treeview-sticker-value')?.innerText||'').replace(/[\s,]/g,'');let exitRate=null;if(url.includes('/afterapage/')){const progressBarValue=sticker.querySelector('.ats-progress-bar-value')?.innerText||'';if(progressBarValue){exitRate=parseFloat(progressBarValue.replace('%','').replace(',','.'))/100;if(navigator.language==='fr-FR'){exitRate=exitRate.toString().replace('.',',');}}}data.push({Selected:isSelected,step:x+1,'order in step':y+1,'Level 2':level2,'Page Name':pageName,Occurrence:occurrence,'Exit Rate':exitRate});});const csv=convertToCSV(data);downloadCSV(csv);}}function convertToCSV(data){const header=Object.keys(data[0]).join(';');const rows=data.map(row=>Object.values(row).join(';'));return[header,...rows].join('\n');}function downloadCSV(csv){const blob=new Blob([csv],{type:'text/csv;charset=utf-8;'});const link=document.createElement("a");const url=URL.createObjectURL(blob);link.setAttribute("href",url);link.setAttribute("download","export.csv");link.style.visibility='hidden';document.body.appendChild(link);link.click();document.body.removeChild(link);}extractDataAndExportToCSV();})();
  • Exécuter le code suivant directement dans la console de votre navigateur
function extractDataAndExportToCSV() {
    const url = window.location.href;

    if (url.includes("https://analytics.piano.io/explorer/#/treeview/")) {
        const stickers = document.querySelectorAll('.ats-treeview-container .treeview-sticker');
        const data = [];

        stickers.forEach(sticker => {
            const isSelected = sticker.classList.contains('sticker-selected');
            const classList = Array.from(sticker.classList);
            const positionClass = classList.find(cls => cls.startsWith('treeview-sticker-'));
            const x = parseInt(positionClass.split('-')[2]);
            const y = parseInt(positionClass.split('-')[3]);
            const level2 = sticker.querySelector('.ats-b-treeview-sticker-subtitle')?.innerText || '';
            const pageName = sticker.querySelector('.ats-b-treeview-sticker-title')?.innerText || '';
            const occurrence = (sticker.querySelector('.ats-b-treeview-sticker-value')?.innerText || '').replace(/[\s,]/g, '');
            let exitRate = null;
            if (url.includes('/afterapage/')) {
                const progressBarValue = sticker.querySelector('.ats-progress-bar-value')?.innerText || '';
                if (progressBarValue) {
                    exitRate = parseFloat(progressBarValue.replace('%', '').replace(',', '.')) / 100;
                    if (navigator.language === 'fr-FR') {
                        exitRate = exitRate.toString().replace('.', ',');
                    }
                }
            }

            data.push({
                Selected: isSelected,
                step: x + 1,
                'order in step': y + 1,
                'Level 2': level2,
                'Page Name': pageName,
                Occurrence: occurrence,
                'Exit Rate': exitRate
            });
        });

        const csv = convertToCSV(data);
        downloadCSV(csv);
    }
}

function convertToCSV(data) {
    const header = Object.keys(data[0]).join(';');
    const rows = data.map(row => Object.values(row).join(';'));
    return [header, ...rows].join('\n');
}

function downloadCSV(csv) {
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
    const link = document.createElement("a");
    const url = URL.createObjectURL(blob);
    link.setAttribute("href", url);
    link.setAttribute("download", "export.csv");
    link.style.visibility = 'hidden';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
}
extractDataAndExportToCSV();

Conseils d’exploitation dans Excel

Pour recréer le tunnel sélectionné dans Piano, vous pouvez filtrer les données dans Excel en utilisant la colonne « Selected » = True :

Si vous souhaitez obtenir la distribution des pages pour une étape précise, il suffit de filtrer sur le numéro d’étape :

Note importante : Ce code se base sur la structure actuelle de la page de Piano Analytics et pourrait ne plus fonctionner en cas de modification de celle-ci. Je ferai de mon mieux pour maintenir le script à jour, mais n’hésitez pas à me signaler tout dysfonctionnement.

Comparer simplement vos tunnels de conversion Piano Analytics

Petit rappel sur la fonctionnalité Tunnel

Nous avions vu dans un article précédent comment la nouvelle fonctionnalité « Tunnel » de Piano Analytics permettait de renforcer les analyses de séquences déjà disponibles dans l’interface. En effet, celle-ci propose de visualiser les volumes, les taux de passages et le temps moyen pour des séquences de plusieurs évènements ou groupes d’évènements que vous allez pouvoir définir :

Comment comparer plusieurs tunnels, sur la métrique de son choix ?

Après plusieurs semaines d’utilisations, j’ai rencontré certains use cases que je ne parvenais pas à gérer à 100% juste via ce module. Les 2 principaux étant :

  • de pouvoir visualiser des métriques autres que uniquement le volume de visites/visiteurs/users (taux de passage, temps passé, taux de sortie)
  • de pouvoir comparer rapidement plusieurs tunnels, en faisant varier un élément.

Pour vous illustrer ces points, imaginez le cas suivant : j’ai 10 visuels d’auto-promotions différentes pour une campagne visant à générer des achats et, au-delà de connaître simplement leur taux/volume de conversions, je souhaite aussi pouvoir comparer leur performance à chacune des étapes de mon tunnel de conversion.

Remplacez maintenant « auto-promotions » par n’importe quel levier d’aide à la conversion comportant des variantes pour vous projeter dans cette problématique (landing pages, AB Test, campagnes de personnalisation…).

Il serait théoriquement possible de le faire, en réalisant manuellement les 10 tunnels, mais il faut bien avouer que cela est fastidieux et qu’un travail manuel de retraitement de la donnée sera de toute façon nécessaire. Et même en faisant cela, je n’aurais toujours pas le temps moyen passé entre chacune de mes étapes.

L’objectif de cet article

L’objectif de cet article va donc être de vous montrer comment exploiter l’API proposé par Tunnel pour automatiser cette tâche, et ainsi obtenir ce type d’information en très peu de temps. La démonstration se fera via Python, mais la logique est applicable au autres langages ou à l’outil de votre choix.

Nous obtiendrons donc :

  • Un tableau récapitulatif des métriques associées à chaque variante de notre tunnels,
  • Pour la métrique de votre choix, un histogramme de comparaison pour chaque variante,
  • Pour la métrique de votre choix, une carte de chaleur composée des étapes et des variantes de votre tunnel.

Pour cet exercice, nous prendrons le cas d’un tunnel composé des étapes suivantes :

  • Le visiteur clique sur une autopromotion, pouvant être présente un peu partout sur le site. J’ai 10 autopromotions différentes, que je voudrais faire varier,
  • Puis le visiteur affiche une page produit,
  • Puis le visiteur ajoute un produit au panier,
  • Puis le visiteur affiche le panier,
  • Puis finalement, le visiteur réalise un achat.

Voici le tunnel dans l’interface, où j’ai filtré sur une des dix auto-promotions en première étape :

Ce cas nous servira de base pour le reste de l’article.

Les données obtenues

Maintenant que nous avons notre cas pratique défini et avant de rentrer dans le code, projetons-nous dans le résultat que nous pouvons obtenir.

Tableaux récapitulatifs des campagnes

Le code retourne tout d’abord un tableau récapitulatif des 10 campagnes d’auto-promotions ayant initié la séquence, avec les métriques proposées par l’API :

  • Visites (nombre de visites ayant été jusqu’au bout du tunnel),
  • Exits (nombre de visites abandonnistes),
  • visits_conversion_rate (taux de conversion global du tunnel),
  • visits_exit_rate (taux de sortie global du tunnel),
  • min_timespent (temps le plus court enregistré pour réaliser le tunnel, en secondes),
  • max_timespent (temps le plus long enregistré pour réaliser le tunnel, en secondes),
  • avg_timespent (temps moyen pour réaliser le tunnel, en secondes).

Cela permet d’avoir un premier aperçu des performances de chaque campagne, et de repérer les métriques intéressantes.

Carte de chaleurs des tunnels

On remarque dans mon exemple que les taux de conversion sont disparates, selon la campagne qui a initié le tunnel. Je souhaite donc pouvoir isoler cette métrique, et l’afficher dans un histogramme :

En plus de cela, je souhaite avoir le détail des taux de passage, entre chacune des étapes.

Voici le résultat, mise en forme via une matrice colorimétrique (plus le taux de passage est important, plus la valeur tend vers le rouge) :

N.B : la première étape a 0 pour taux de conversion, puisque celle-ci ne comporte pas d’étape précédente.

Tunnel de conversion détaillé

Si je souhaite finalement zoomer sur un des tunnels, pour mieux appréhender les différences de valeurs, une dernière fonction me permet de l’afficher de manière plus visuelle :

Pour que vous compreniez mieux le rendu, voici un tunnel, mais cette fois-ci avec la métrique « visits » :

Guide d’utilisation du fichier Python

Maintenant que nous avons vu l’intérêt d’un tel retraitement de données, nous allons voir ensemble, comment utiliser le fichier.

Récupération de l’appel API dans Tunnel

Nous allons d’abord devoir générer un appel API « modèle », qui nous permettra ensuite d’automatiser l’ensemble des requêtes.

Pour cela, configurez votre tunnel comme bon vous semble (nombre d’étapes, filtres, segments…). Une contrainte à respecter : l’étape que vous souhaitez faire varier doit comporter un filtre « est égal à {{la valeur de votre choix}} ». La valeur en elle-même n’est pas importante puisque le script Python va la dynamiser. Voici un exemple :

Une fois cela configuré, cliquez sur le bouton de partage :

Puis copiez la partie « Body », contenant tout le paramétrage API de votre tunnel, et mettez cela de côté dans un bloc-notes :

Récupération de la clé API

Pour requêter vos données Piano Analytics depuis l’extérieur de l’interface, vous allez devoir obtenir une clé API. Cette clé est liée à votre compte Piano Analytics, vous n’aurez donc pas la même que votre collègue. Vous aurez également accès au même périmètre que celui disponible dans votre interface (même liste de niveaux 1).

Une fois connecté à votre compte, allez dans votre profil puis dans l’onglet Api Keys.

 Générez une nouvelle clé et conservez bien l’Access key et surtout la secret key, qui ne vous sera plus donnée par la suite.

Configuration de votre script Python

Pour utiliser ce code, il vous faudra donc une instance Python disponible. Si vous n’en avez pas, vous pouvez utiliser Le service de notebook en ligne de Google.

Vous allez devoir paramétrer quelques variables afin de rendre le code fonctionnel, mais rien d’insurmontable :

  • key : votre clé API,
  • etape : numéro de l’étape qui doit être variabilisée (cela n’est pas obligatoirement la première étape),
  • propriete : nom de la propriété où vous avez placé le filtre « est égal » à variabiliser,
  • valeurs : liste des différentes valeurs que prendra le filtre « est égal ». Pas de limite dans le nombre de valeurs, retenez juste que chaque valeur va générer un appel API. Donc plus il y a de valeurs, plus le temps d’exécution du script sera important,
  • NomsEtapes : vous pouvez renommer les différentes étapes de votre tunnel, afin que cela soit plus parlant lors de la lecture des graphiques…Attention, le nombre de valeurs dans la liste doit exactement correspondre au nombre d’étapes de votre tunnel,
  • jsonFromDQ : reprendre exactement le Json que vous avez récupéré de Data Query à l’étape précédente.
key = "votre_cle_API"
etape = 1
propriete = 'onsitead_format'
valeurs = ["Campagne 1", "Campagne 2", "Campagne 3", "Campagne 4", "Campagne 5", "Campagne 6", "Campagne 7", "Campagne 8", "Campagne 9", "Campagne 10"]
nomsEtapes = ['clic autopromo','page_produit','ajout_panier','page_panier','achat']
jsonFromDQ = {
  "funnel": {
    "scope": "visit_id",
    "steps": [...

Exécutez maintenant le script principal, qui va de manière schématique boucler sur des appels API générés à partir de votre liste de valeurs, puis consolider le tout dans un grand tableau (dataframe Pandas).

Visualisation des données

Comme nous l’avons vu, plusieurs méthodes et fonctions sont à votre disposition pour récupérer l’information. Parcourons-les ensemble.

Les tableaux récapitulatifs

Pour afficher les tableaux récapitulatif des tunnels, il faut cibler la variable globalData, qui va contenir 2 tableaux.

globalData[1] va contenir les chiffres globaux pour chaque tunnel :

globalData[0] va contenir le tableau « brut » de tous les tunnels (si vous souhaitez retravailler les données d’une autre manière) :

Les graphiques

La fonction displaygraph() va afficher 2 graphiques :

  • Un histogramme de comparaison des tunnels sur la métrique de votre choix (trié par valeur décroissante),
  • Une matrice colorimétrique, qui affiche la métrique pour chaque tunnel et chaque étape de votre tunnel.

Vous pouvez personnaliser le 2ième argument de la fonction avec le nom de votre métrique parmi celles disponibles.

Finalement, la fonction detailFunnel() vous permet d’avoir le détail d’un des tunnels, sur la métrique de votre choix. Il y a ici 2 arguments personnalisables :

  • le 2ième, permettant de sélectionner le tunnel de votre choix,
  • le 3ième, permettant de sélectionner la métrique de votre choix.

Conclusion

En conclusion, Tunnel est un outil très utile pour l’analyse de séquences d’événements, mais il peut être limité en termes de métriques disponibles et de comparaison. Cet article a montré comment exploiter l’API proposée par Tunnel pour automatiser cette tâche et obtenir rapidement des informations telles que des tableaux récapitulatifs des métriques associées à chaque variante de Tunnel, des histogrammes de comparaison pour chaque variante, et des cartes de chaleur. Bien que le code présenté ici utilise Python, la logique peut être appliquée à d’autres langages et outils. Cette démonstration montre donc que Tunnel n’est qu’un point de départ vers d’autres analyses plus poussées.

Code source

Voici le code source permettant de récupérer les données de votre scénario d’analyse et d’afficher les graphiques de votre choix :

import pandas as pd
pd.options.mode.chained_assignment = None 
import requests
import json
import numpy as np
import datetime
import time
import re
import plotly.express as px

######## Partie configuration

key = "123ABC" # Votre clé API
etape = 1 #Le numéro de l'étape qui va être variabilisé
propriete = 'onsitead_format' # Nom de la propriété où vous avez placé le filtre "est égal" à variabiliser
valeurs = ["Campagne 1", "Campagne 2", "Campagne 3"] # liste de valeurs qui seront placées dans les filtres de l'étape
nomsEtapes = ['clic autopromo','page_produit','ajout_panier','page_panier','achat'] #vous pouvez renommer les différentes étapes de votre tunnel
jsonFromDQ = { #reprendre exactement le Json que vous avez récupéré dans Data Query
  "funnel": {
    "scope": "visit_id",
    "steps": []
      }
    }

######## Partie déclaration et excecution des données
def apiCall(apikey, url):
  headers = {
    'x-api-key': apikey
  }
  call = requests.get(url, headers=headers)
  data = json.loads(call.content)
  return data

def jsontoTab(json):
  steps = {k: v for k, v in json['DataFeed']['Rows'][0].items() if k.startswith('m_step')}
  tab = []
  ModelingStep = {}
  for step in steps:
    splitKey = step.split('_')
    ModelingStep = {
        '_'.join(splitKey[2:len(splitKey)]) : steps[step],
        'step': int(splitKey[1].split('step')[1])
    }
    tab.append(ModelingStep)
  finalTab = pd.DataFrame(data=tab).groupby(['step']).sum().reset_index()
  return finalTab

def jsontoTabGlobal(json):
  steps = {k: v for k, v in json['DataFeed']['Rows'][0].items() if k.startswith('m_global')}
  newDict = {}
  for step in steps:
    newDict[step.split('m_global_')[1]] = steps[step]
  final = pd.DataFrame(data=newDict,index=[0])
  return final

def urlBuilder(url,step,prop,filter):
    NewDict = url
    NewDict['funnel']['steps'][step-1]['condition']['filter']['$and'][1][prop]['$eq'] = filter
    toStr = json.dumps(NewDict).replace(" ", "")
    urlFinal = 'https://api.atinternet.io/v3/data/getData?param='+toStr
    return urlFinal

def builder(initialUrl, stepToChange,propToChange,ListValues,stepNames):
  listTable = []
  listTableGlobal = []
  for value in ListValues:
    changedUrl = urlBuilder(initialUrl,stepToChange,propToChange,value)
    jsonRaw = apiCall(key,changedUrl)
    tableGlobal = jsontoTabGlobal(jsonRaw)
    tableGlobal['funnel_version'] = value
    tableGlobal['visits'] = ((tableGlobal['exits']/tableGlobal['visits_exit_rate'])-tableGlobal['exits']).round()
    tableGlobal= tableGlobal[['funnel_version','visits','exits','visits_conversion_rate','visits_exit_rate','min_timespent','max_timespent','avg_timespent']]
    listTableGlobal.append(tableGlobal)

    table = jsontoTab(jsonRaw)
    table['funnel_version'] = value
    table['step_name'] = ''
    for i in range(len(stepNames)):
      table['step_name'][table['step'] == i+1] = stepNames[i]
    table = table[['funnel_version','step','step_name','visits','min_timespent','max_timespent','avg_timespent','visits_exits','visits_conversion_rate','visits_exit_rate']]
    listTable.append(table)
  return [pd.concat(listTable),pd.concat(listTableGlobal)]

def displaygraph(data,metric,values):
    fig1 = px.bar(data[1].sort_values(by=[metric], ascending=False), x='funnel_version', y=metric)
    fig1.show()

    globalvalues = []
    for value in values:
      valueList = list(data[0][data[0]['funnel_version'] == value][metric])
      globalvalues.append(valueList)
    fig2 = px.imshow(globalvalues,
                labels=dict(x="Etapes", y="Variante", color=metric),
                color_continuous_scale='RdBu_r',
                x=nomsEtapes,
                y=values,
               text_auto=True,
               width=1000, 
               height=650)
    fig2.update_xaxes(side="top")
    fig2.show()
  
def detailFunnel(data,funnel,metric):
  fig = px.funnel(data[data['funnel_version'] == funnel], x='step_name', y=metric)
  fig.show()
  
globalData = builder(jsonFromDQ,etape,propriete,valeurs,nomsEtapes)

####################### partie visualisation

globalData[0] #Va contenir le tableau "brut" de tous les tunnels
globalData[1] #Va contenir les chiffres globaux pour chaque tunnel
displaygraph(globalData,'visits_conversion_rate',valeurs) #affiche l'hisgramme récap et la matrice colorimétrique, en fonction de la métrique de votre choix
detailFunnel(globalData[0],'Campagne 8','visits') #Donne le détail d'un des tunnels, en fonction de la métrique de votre choix

Exploiter la nouvelle fonctionnalité « tunnel » de Piano Analytics

Piano analytics a mis en ligne il y a quelques semaines une nouvelle fonctionnalité, appelée sobrement « funnel », qui vient compléter le toolkit des analyses séquentielles déjà disponibles. Celle-ci propose de visualiser les volumes, les taux de passages et le temps moyen pour des séquences de plusieurs évènements que vous allez pouvoir définir.

C’est l’occasion de vous expliquer le fonctionnement de cette nouvelle feature, ses principales différences avec les outils de séquences déjà disponibles (l’analyse « Navigation » et la segmentation séquentielle) et finalement quelques use cases afin que vous puissiez vous projeter dans son utilisation. A noter qu’il s’agit à l’heure actuelle d’un MVP, et que de nouvelles fonctionnalités sont à venir très prochainement (enregistrement des templates, partages, breakdown).

Présentation de l’interface

La fonctionnalité est disponible depuis Data Query, en allant dans « Nouveau » > « Créer un rapport tunnel » :

Dans le menu supérieur, vous retrouvez le même menu que Data Query, avec :

  • La sélection de votre/vos site(s) de niveau 1 (et oui, vous pourrez créer un tunnel multi-sites) (1)
  • La selection de la période d’analyse (2)
  • La barre de segment (et oui, vous pourrez segmenter un tunnel) (3)

La zone de gauche va vous proposer de choisir le premier évènement que vous souhaitez cibler dans votre tunnel (4), ou le segment que vous souhaiteriez éventuellement appliquer (5)

Finalement, la bande « configuration » vous propose de choisir la granularité de votre tunnel (visites, visiteurs, users). (6)

Création de votre premier tunnel

Maintenant que l’interface est présentée, créons ensemble notre premier tunnel ! Voici le cas que nous allons traiter : sur un site e-commerce, nous allons recréer un tunnel complet de notre parcours d’achat, en y incluant à la fois les évènements Sales Insights, mais aussi les évènements de connexion au compte (trackés en pages vues).

La première étape pour vous va être ici de déterminer les évènements représentant chaque étape. Dans mon cas les voici :

  • cart.display (affichage du panier)
  • page.display (page de connexion)
  • cart.delivery (choix mode de livraison)
  • cart.payment (choix mode de paiement)
  • transaction.confirmation (confirmation de l’achat)

Nous allons donc placer ces évènements sur la timeline du tunnel :

La deuxième étape sera de définir si des filtres doivent être placés sur certains évènements. Dans mon cas je vais devoir préciser que l’évènement page.display concernant la page de connexion au compte client. Je vais ainsi cliquer sur l’évènement « page.display » et ajouter le filtre « connexion_tunnel_achat » sur la propriété « page » :

Les autres évènements symbolisent déjà l’avancée dans le tunnel d’achat, je n’ai donc pas besoin d’ajouter de filtres sur d’autres étapes. Je souhaite cependant me concentrer sur les paniers importants, je filtre donc ceux ayant au moins 100€ de marchandise :

Vous pouvez également segmenter votre tunnel, pour par exemple ne visualiser que les parcours effectués sur mobile, ou ceux ayant été exposés à une auto-promotion :

Notre tunnel est prêt, il ne reste plus qu’à lancer le calcul, comme un Template Data Query classique !

Astuce 1 : les étapes avec filtres apparaissent avec un petit entonnoir à gauche du nom de d’évènement.

Astuce 2 : vous n’êtes pas obligé de spécifier un évènement dans une étape, vous pouvez uniquement y appliquer des filtres. Ainsi tout évènement correspondant aux filtres sera inclus dans l’étape. Pour cela il suffit d’éditer l’évènement et de sélectionner « any event » dans le menu déroulant.

Analyse d’un tunnel

Votre tunnel maintenant affiché, vous allez pouvoir analyser sur la partie graphique :

  • Le volume de visites à chaque étape (1)
  • Le volume de déperdition à chaque étape (2)
  • Le taux de passage et le taux de sortie, en survolant une des étapes (3)

Sur la partie KPi’s, vous allez retrouver :

  • Les visites (combien de visites étaient présentes à la première étape de mon tunnel ?) (4)
  • La durée moyenne (combien de temps s’est écoulé entre la première et la dernière étape ?) (5)
  • Le taux de conversion (volume de la dernière étape divisé par le volume de la première) (6)
  • Sorties (nombre de visites ayant quitté le tunnel sur une des étapes) (7)

Libre à vous maintenant de modifier votre tunnel en ajoutant une étape, en modifiant votre segment ou encore en supprimant un filtre présent sur une étape !

Les calculs derrière la fonctionnalité

Pour bien utiliser cette fonctionnalité, il convient de comprendre les calculs qui sont effectués en coulisse.

Les étapes des séquences que vous définissez ne sont pas obligatoirement consécutives. Ainsi, d’autres évènements peuvent se glisser entre 2 étapes, ce qui n’empêchera pas la prise en compte de la visite dans le tunnel. Contre intuitif de prime abord, cela me semble un très bon choix, puisque cela vous permettra, par exemple, d’éviter les évènements parasites se glissant dans votre tunnel (par exemple des évènements de clics entre 2 pages car vous avez marqué votre menu principal avec des évènements click.navigation).

Ainsi, si je définis la séquence « chargement page A » > « lecture video B » > « chargement page C », voici les visites qui seront retenues :

Gardez donc en tête qu’une visite réalisant la séquence complète de votre tunnel pourra potentiellement avoir fait plusieurs aller-retours sur le site avant de convertir.

Cette logique est également valable pour les granularités visiteurs et utilisateurs, où seul l’ordre des évènements compte et non leur enchainement.

Les principales différences avec les autres fonctionnalités de séquençage

Comme indiqué en introduction, d’autres outils permettaient déjà de faire du séquençage :

  • la segmentation séquentielle
  • l’analyse navigation

Voyons rapidement les principales différences entre ces outils et quels sont leurs avantages/inconvénients.

La segmentation séquentielle

La segmentation séquentielle va avoir le même mode de calcul que l’analyse tunnel, à savoir la comptabilisation des visites/visiteurs/users ayant suivi un enchainement d’évènements dans un certain ordre, mais pas obligatoirement consécutifs (celui-ci est disponible dans le module de segmentation avancée, via le bouton « puis » ou « then »). Exemple ici :

Il serait donc en théorie possible de recréer un tunnel, en créant plusieurs métriques calculées basées sur des segments séquentiels, et vous obtiendriez le même résultat, mais cela serait bien sûr chronophage. L’avantage est en revanche que vous pouvez placer un taux de passage dans un dashboard ou un template Data Query, ce qui n’est pas possible avec tunnel. Il permet également d’être utilisé comme segment dans l’ensemble de vos analyses Explorer.

L’analyse navigation

L’analyse navigation va, elle, avoir une autre logique de calcul. Celle-ci dispose uniquement d’une granularité « visites », centrée sur les évènements page.display() et la propriété page_full_name (le nom de votre page, concaténé avec ses chapitres). Exemple ici avec le sunburst :

source : support.piano.io

L’analyse va également être concentrée sur les chargements de pages consécutifs, on peut ainsi parler ici de séquences directes (enchainement des évènements les uns derrières les autres), ce qui fait une grosse différence avec tunnel. Je vous place ici le lien vers la documentation Piano, qui explique les détails du mode de calcul.

Finalement, la navigation va plus avoir un rôle d’analyse exploratoire, puisque que vous vous verrez proposer à chaque étape sélectionnée, les étapes suivantes, OU, les étapes précédentes. Cela n’a donc pas le même objectif qu’un tunnel.

source : support.piano.io

Je vous place ci-dessous un tableau récapitulatif des spécificités et usages de chaque fonctionnalité :

Les nouveaux use cases possibles avec l’analyse tunnel

Maintenant que vous avez acquis toutes les subtilités de cette nouvelle analyse, il est maintenant temps de se projeter dans des exemples d’analyses, que vous n’auriez pas pu faire auparavant.

Créer un tunnel avec plusieurs type d’évènements

C’est le fameux cas que j’ai donné comme exemple en introduction. Comme l’analyse navigation ne traite que les évènements de pages et que le tunnel disponible dans Sales Insights ne gère que ses propres évènements, il n’était auparavant pas possible de créer un tunnel mixant plusieurs typologies d’évènements.

Le cas du tunnel d’achat marqué avec des évènements Sales Insights, mais comprenant une étape de connexion marquée avec un évènement page.display() devient maintenant réalisable.

Rassembler plusieurs évènements en une seule étape

Définir les étapes d’un tunnel ne veut pas nécessairement dire qu’elles ne doivent contenir qu’un seul évènement. Reprenons notre exemple précédent avec le tunnel d’achat. Imaginons que je veuille avoir une étape supplémentaire concernant la connexion, à savoir :

  • ceux qui ont été exposés au formulaire de connexion
  • ceux qui ont été au bout du processus de connexion (connexion au compte ou création de compte)

Problème : la connexion et la création d’un comptes sont 2 évènements page.display() différents, avec leur propre label (« connexion_succes » et « validation_creation_compte »).

Je peux cependant définir dans mon tunnel une étape « validation connexion » qui aura un filtre « page = connexion_succes ou validation_creation_compte« , permettant de prendre en compte les 2 cas de figures.

Créer des entonnoirs de conversion

Avec l’exemple précèdent, nous voyons qu’il est possible, en jouant avec les filtres, de rassembler plusieurs évènements au sein d’une étape. Nous pouvons appliquer ce principe en créant un entonnoir de conversion, qui pourra comprendre des milliers d’évènements pour chacune des étapes. Restons sur notre site e-commerce, où nous voudrions connaitre la déperdition de trafic entre la home page et la commande. Nous aurons donc comme étapes :

  • La home page, qui est en soi une page unique
  • Les listes produits, qui sont des centaines de pages
  • les pages produits, qui sont des milliers de pages
  • le panier et la confirmation d’achat, qui sont des évènements uniques

Faire un suivi des conversions multi-visites

Dès que nous sortons d’une conversion à scope visites, les analyses vont devenir plus complexes. En effet, même si l’interface dispose des outils nécessaires pour suivre un visiteur ou un user, la plupart des analyses et des métriques se rapportent à la visite (sources, temps passé, conversions…). Ce module tunnel va nous permettre de visualiser plus facilement des parcours multi-visites, et surtout d’estimer le temps nécessaire pour réaliser une conversion longue.

Imaginons que vous êtes le web analyste de Voyageurs du Monde, une société vous proposant de réaliser des voyages sur mesure. Très clairement, le temps entre la première prise de contact et la validation d’une proposition de voyage va être de l’ordre de plusieurs jours, à plusieurs semaines.

Notre objectif va donc être de suivre ce processus, de la création du compte jusqu’à l’achat et d’en déterminer la déperdition ainsi que le temps moyen. Voici nos étapes clés :

  • La création d’un nouveau compte
  • L’envoi d’une première liste de souhaits
  • la consultation de la proposition de Voyageur du monde (sachant qu’il peut y en avoir plusieurs, en fonction des retours du client)
  • La validation de la proposition (synonyme de paiement)

Nous allons analyser cela sur une période de 6 mois, afin de laisser le temps aux plus indécis d’éventuellement terminer leur parcours.

Cependant nous allons ici avoir un problème : un utilisateur peut aussi bien créer son compte le premier mois que le dernier mois de l’analyse. Ainsi, celui ayant débuté le parcours ciblé le sixième mois aura eu beaucoup moins de temps pour réaliser l’ensemble, impactant donc négativement le taux de conversion global.

Afin d’annuler ce comportement, nous allons appliquer un segment à portée Utilisateurs qui ciblera ceux ayant créé leur compte lors du premier mois de notre période d’analyse. Ainsi, uniquement ces utilisateurs seront suivis en terme de déperdition dans le reste du parcours.

Créer un tunnel omnicanal

Vous ne l’aviez peut-être pas en tête, mais il est possible d’envoyer des évènements offline vers Piano analytics. Retrait d’un produit en magasin, rendez-vous physique, appel call center… sont des évènements offline, que vous pouvez rattacher au reste du parcours online d’un utilisateur. Exemple ici avec un event click&collect, qui sera rattaché à l’utilisateur ayant fait la commande en ligne, tout en ne générant pas de visite dans vos données.

https://logx.xiti.com/event?s=12345&events=
[{"name":"confirmation_retrait_clickandcollect",
"data":{
"user_id":"12345678",
"transaction_id":"T8787878887"
}}]

Pensez bien à déclarer votre évènement personnalisé comme offsite, au risque de générer des visites parasites dans vos données :

Maintenant que ce concept est acquis, mettons nous dans la peau d’un site de génération de leads. Le process est le suivant :
  • Le visiteur arrive depuis une landing page
  • Il consulte l’offre et remplit un formulaire pour une demande de rappel par un conseiller
  • le conseiller parvient à joindre le visiteur au téléphone et lui fait parvenir un contrat (event offline)
  • Le client consulte son espace en ligne, puis signe le contrat envoyé

Bien entendu, il faudra préalablement mettre en place un système de requêtes server to server, afin que le serveur de la centrale d’appels envoie l’information de l’appel réussi en temps réel au serveur Piano Analytics.

A noter que seule la granularité « User » sera disponible pour ce type de tunnel, puisque les evènements offline ne peuvent être rattachés qu’au user_id.

Conclusion

Le module est amené à évoluer à très court terme, aussi cet article sera mis à jour au fur et à mesure des évolutions de tunnel. Comme vous avez pu le voir à travers ces exemples, il permet de répondre dès maintenant à des problématiques qui n’étaient pas réalisables auparavant. il a donc toute sa place dans vos habitudes d’analyses à partir de maintenant.

Piano Analytics et Power Bi/Excel : niveau avancé

Nous avions vus dans un article précédent comment le module Power Query, commun aux logiciels Excel et Power Bi, permettait de réaliser des appels API de la solution Piano Analytics et ainsi récupérer ses données analytics dans ses rapports/dashboards. 

Je vous conseille de faire un tour sur cet article si vous n’êtes pas à l’aise avec Power Query  ou l’API Piano avant de continuer cette lecture.

La limite des 10 000 lignes

Nous savons comment récupérer les données via l’API, mais nous pouvons être confrontés à un problème lorsque nos appels sont détaillés : atteindre la limite des 10 000 lignes récupérables dans un appel.

En effet l’API Piano analytics fonctionne avec un système de pagination, où vous définissez le nombre de résultats souhaités dans l’appel (10 000 maximum), et quelle page de résultats vous souhaitez récupérer.

Par exemple, si j’indique un nombre de résultats à 50 et la page de résultat à 2 (« max-results »:50, »page-num »:2), j’obtiendrais les lignes de résultats 51 à 100.

Comme la limite est de 10 000 lignes, si une requête me retourne 30 000 résultats, je devrais alors réaliser 3 appels API sur les pages 1,2 et 3 pour récupérer l’ensemble de mes données. Or, dans une source de données Power Query classique, je ne peux effectuer qu’un seul appel à la fois. 

Nous pourrions alors tout à fait imaginer créer 3 sources de données et simplement faire varier le numéro de page pour avoir l’ensemble des données : 

  • « max-results »:10000, »page-num »:1
  • « max-results »:10000, »page-num »:2
  • « max-results »:10000, »page-num »:3

Il suffirait ensuite d’utiliser la fonction « ajouter des requêtes » de Power Query pour ne créer qu’un seul tableau regroupant ces 3 requêtes.

Cette possibilité n’est cependant pas optimale car elle va générer quelques désagréments : 

  • Cela n’est pas extensible à l’infinie, si vous devez par exemple récupérer 200 000 lignes (maximum autorisé) vous devrez faire 20 appels différents…
  • Cela va « polluer » votre vue dans power Query, puisque vous aurez potentiellement des dizaines d’appels
  • Cela n’est pas dynamique. Si sur avril la requête vous retournait 30 000 résultats, vous n’êtes pas à l’abris qu’elle passe à 50 000 sur le mois de mai

Pour ces raisons, nous allons essayer de développer une technique permettant de requêter dynamiquement autant de pages de résultats nécessaires en fonction du volume de données à récupérer sur la période. Et quand je parle de « technique » je parle en réalité d’une fonction personnalisée Power Query que nous allons écrire ensemble.

Pour ceux souhaitant simplement récupérer le code de la fonction mais sans en comprendre le fonctionnement, vous pouvez vous rendre directement à la fin de l’article.

Objectifs de la fonction personnalisée

Avant de nous lancer dans l’écriture de la fonction, nous devons définir ce qu’elle doit faire. Voici une liste des étapes à réaliser : 

  • La fonction va devoir en premier lieu déterminer le nombre de lignes de résultats présentes dans notre requête API (l’API Piano Analytics dispose de fonctionnalités pour récupérer cette information, nous verrons cela par la suite)
  • A partir de ce nombre de lignes, en déduire ne nombre d’appels qu’il faudra réaliser, sachant que le nombre maximum de lignes récupérables est de 10 000
  • Générer dynamiquement autant d’appels API qu’il y a de pages de résultats et en récupérer les données
  • Fusionner les résultats de l’ensemble des requêtes dans un tableau

Ecriture de la fonction

Comprendre le fonctionnement de l’API « getRowCount »

Quand on se trouve dans Data Query, l’interface nous indique bien le nombre de résultats d’une requête (dans mon cas 62 452) : 

Une image contenant texte

Description générée automatiquement

Vous ne trouverez en revanche pas cette information lors d’un appel API classique (Bouton « partage » > «API GET ») :

https://api.atinternet.io/v3/data/getData?param={"columns":["date","src","page","m_visits"],"sort":["-m_visits"],"space":{"s":[592201]},"period":{"p1":[{"type":"D","start":"2022-01-01","end":"2022-03-31"}]},"max-results":50,"page-num":1,"options":{}}

Une image contenant texte

Description générée automatiquement

Fort heureusement, la documentation API nous propose une méthode pour obtenir cette information : le getRowCount

Il suffit alors de modifier le terme getData par getRowCount dans l’url de l’API : 

https://api.atinternet.io/v3/data/getRowCount?param={"columns":["date","src","page","m_visits"],"sort":["-m_visits"],"space":{"s":[592201]},"period":{"p1":[{"type":"D","start":"2022-01-01","end":"2022-03-31"}]},"max-results":50,"page-num":1,"options":{}}

On va cependant obtenir ce message d’erreur si nous n’effectuons que cette modification :

Une image contenant texte

Description générée automatiquement

En effet le getRowcount va refuser une partie des informations contenues dans notre appel de base, car ne faisant pas sens dans ce contexte : 

  • Sort
  • Page-num
  • Max-results

Nous le supprimons donc de notre appel pour ne conserver que les informations liées aux données (columns, space, période, segment, filter et options) :

https://api.atinternet.io/v3/data/getRowCount?param={"columns":["date","src","page","m_visits"],"space":{"s":[592201]},"period":{"p1":[{"type":"D","start":"2022-01-01","end":"2022-03-31"}]} ,"options":{}}

Une image contenant texte

Description générée automatiquement

Modification d’un appel classique en appel rowcount dans Power Query

Nous savons maintenant comment récupérer notre rowCount ! Notons nous maintenant ce que Power Query devra faire à partir de l’url API donnée par Data Query : 

  • Changer le terme getData par getRowCount dans l’url de l’API 
  • Ne conserver que les propriétés columns, space, période, segment, filter et options 
  • Lancer l’appel Api et récupérer la valeur présente dans le json.

Isoler la partie paramètres d’un appel

Avant de créer directement une fonction personnalisée, nous allons nous entrainer en ne traitant qu’une seule requête fixe. Je vous invite donc à générer également de votre côté un appel API retournant plus de 10 000 lignes pour suivre les instructions. 

Dans Power Query, créez une nouvelle source vide (accueil > nouvelles source > requête vide). Collez maintenant votre URL API dans le champ d’édition de l’étape par défaut « source » :

Une image contenant texte

Description générée automatiquement

Nous allons maintenant essentiellement travailler dans l’éditeur avancé afin de construire les retraitements souhaités. On va créer 2 variables via la méthode « split() » : une contenant la racine de l’url API, l’autre contenant le json des paramètres de requête, cela nous permettra de retravailler les informations séparément. On va retourner dans une liste pour vérifier le résultat :

Une image contenant texte

Description générée automatiquement

Récupérer les paramètres nécessaires au getRowCount

Notre objectif va être maintenant de récupérer les propriétés dont nous avons besoin pour l’appel rowCount :

  • columns
  • space
  • period
  • filter
  • options

Comme cette partie de l’url est du JSON, nous allons pouvoir utiliser la méthode Json.document pour mieux manipuler et isoler les informations : 

Une image contenant texte

Description générée automatiquement

Une image contenant table

Description générée automatiquement

Il est maintenant très facile de récupérer une propriété de cet objet, en ajoutant son nom à la fin de l’expression (ici « columns » ):

Power Query a par contre transformer notre json en un format non exploitable (value), nous allons donc devoir reconvertir la valeur en JSON (via json.fromValue), puis le JSON en string (text.FromBinary) :

Nous avons presque nous ce qu’il nous faut ! il suffit maintenant d’ajouter le nom initiale de la propriété afin que le format soit prêt à être réinjecter dans un appels API :

Nous savons maintenant comment récupérer la propriété de notre choix depuis un appel API ! Nous allons donc appliquer cette logique sur l’ensemble des paramètres.

Nous allons donc créer des variables contenant chacune des propriétés dont nous avons besoin, afin de les reconcaténer ensuite dans notre appel rowcount :

Une image contenant texte

Description générée automatiquement

Gérer les paramètres optionnels

Dans notre appel d’exemple, nous n’avions pas de filtre et de segment car ce sont des propriétés optionnelles. Il faut cependant quand même gérer le cas ou elles seraient présentes dans un de nos appels. Si on tente de les récupérer avec la même méthode alors qu’elles ne sont pas présentes, la variable tombe en erreur :

Une image contenant texte

Description générée automatiquement

Nous allons donc devoir vérifier leur présence en amont. Pour cela nous allons transformer à la volée notre liste de paramètres en tableau, puis d’utiliser la méthode List.Contains sur la valeur recherchée, qui nous retournera un booléen.

Exemple avec la propriété filter :

Une image contenant texte

Description générée automatiquement

A partir de là, il suffit décrire une condition demandant à retourner une valeur null lorsque notre test retourne FALSE ou de nous retourner le filtre s’il est présent (true). On applique cette logique à nos paramètres optionnels. Au moment de créer la liste des propriétés dans la variable results, on applique finalement la méthode RemoveNulls, qui va supprimer les éléments vides.

Une image contenant table

Description générée automatiquement

Grace à la fonction text.combine, nous allons pouvoir concaténer toutes nos propriétés dans une string, en ajoutant « , » comme caractère séparateur. Nous n’avons plus qu’à réutiliser cette méthode pour construire notre appel rowCount :

Une image contenant texte

Description générée automatiquement

Requêter l’API getRowcount et déterminer le nombre d’appels à réaliser

La prochaine étape va maintenant être de requêter cette API (web.Contents) et de parcourir le JSON (json.document) pour récupérer la valeur qui nous intéresse.

L’API nous retourne un objet « Rowcounts », contenant une liste à 1 valeur, contenant elle-même une propriété RowCount. Nous allons donc sélectionner cette propriété et récupérer la première valeur associée :

Une image contenant texte

Description générée automatiquement

Dans mon cas, j’ai donc plus de 60 000 résultats. Si je veux déterminer automatiquement le nombre de pages de 10 000 de résultats que je devrais récupérer, il me suffit de diviser cette valeur par 10 00 et d’arrondir à l’entier supérieur :

 Voilà pour cette partie, qui a finalement été assez complexe ! 

La prochaine étape visera à lancer ces 7 appels API, pour chacune des pages de données à récupérer ! 

Mais avant cela, nous avons besoin de gérer le cas ou la requête ne retourne aucun résultat. En effet la suite de la procédure va partir du principe qu’il y a au moins une page de résultats à récupérer, il faut donc s’assurer que la page 1 soit à minima requêtée. Nous allons donc construire une condition très simple pour cela :

Récupération des pages de résultats

Construction des appels API à réaliser

La première étape va être de créer une liste de 1 à 7 (dans mon cas), pour reconstruire un appel api complet pour chaque page de résultats. Pour cela nous allons pouvoir utiliser la méthode list.Generate, qui va prendre 3 paramètres : 

  • La valeur de départ de la liste (ici 1)
  • La valeur maximale (notre rowCountAdjusted)
  • L’incrément qu’on souhaite faire à chaque nouvelle valeur de la liste (+1)

Une image contenant texte

Description générée automatiquement

Nous allons ensuite placer notre numéro dans l’expression complète de l’objet page-num de l’API. La méthode list.transform permet d’appliquer des retraitements sur chaque valeur de la liste et la méthode text.combine permet de faire des concaténations :

Une image contenant texte

Description générée automatiquement

Une image contenant texte

Description générée automatiquement

Toujours à l’aide de list.transform, on remplace le page-num de l’appel d’origine (stocké dans source) par notre liste.

Une image contenant texte

Description générée automatiquement

Une image contenant texte

Description générée automatiquement

Nos URLs sont finalement prêtes à être appelées ! 

Déterminer les colonnes qui seront présentes dans le tableau final

Mais (et oui, encore un mais), Pour préparer au mieux la future création du tableau de données, nous allons avoir besoin de savoir dès maintenant les colonnes qui y sont présentes. Fort heureusement, l’API retourne un objet « columns » nous donnant une multitude d’informations, dont 2 qui nous intéressent  : 

  • Name, qui indique la property key de la colonne dans l’appel API
  • Label, qui indique le nom de la colonne (celui présenté dans l’interface)

Une image contenant texte

Description générée automatiquement

On requête donc l’API d’origine et on isole l’objet columns :

Une image contenant texte

Description générée automatiquement

Chaque colonne est représentée par un objet « Record » qui contient les informations souhaitées :

Une image contenant texte

Description générée automatiquement

Une image contenant table

Description générée automatiquement

On réutilise la méthode list.transform pour extraire Name et Label :

Une image contenant texte

Description générée automatiquement

Réaliser l’appel de données

On peut enfin appeler nos données, en repartant de notre liste d’URL API et en y appliquant la méthode web.contents. l’objet de l’API contenant les lignes de données est l’objet « Rows » :

Une image contenant texte

Description générée automatiquement

Le format « list » n’est plus adaptée à partir de cette étape, nous allons donc la convertir en tableau de données grâce à la méthode table.FromList :

Une image contenant texte

Description générée automatiquement

Une image contenant table

Description générée automatiquement

Power Query a automatiquement créé un tableau à une colonne « column1 ». Si vous rentrez dans une des listes, vous constaterez qu’elle contient une multitude d’objets « Record », symbolisant chaque ligne de notre futur tableau. Nous allons donc simplifier tout cela en supprimant un niveau d’imbrication. La méthode exandListColumn va permettre de faire remonter les objets « Record » directement dans la colonne « Column1 » :

Une image contenant texte

Description générée automatiquement

Une image contenant table

Description générée automatiquement

Il ne nous reste plus qu’à déployer nos objets records en tant que colonnes, via la méthode expandRecordColumn. Cette méthode prend 4 arguments : 

  • La table à modifier (expandTable)
  • La colonne contenant les records (column1)
  • Les noms des colonnes à extraire (nous les avons déjà récupérés tout à l’heure dans la variable columnsName
  • Le nom qui sera donné à nos colonnes (nous allons reprendre ceux affichés dans l’interface, que nous avons dans la variable columnsLabel)

Une image contenant texte

Description générée automatiquement

Nous venons donc récupérer plus de 60 000 lignes de données en une seule requête Power Query ! 

Construction de la fonction finale

La dernière étape consiste à transformer l’ensemble de notre côté en une fonction, que nous pourrons réutiliser pour autant d’appels que nous souhaitons. Plusieurs choses à faire pour cela : 

  • Mettre au format « fonction fléchée » notre expression
  • Ajouter un paramètre « requestedAPI » qui symbolisera l’API indiqué par l’utilisateur

Une image contenant texte

Description générée automatiquement

Vous devirez obtenir quelques choses de ce type :

Une image contenant texte

Description générée automatiquement

Placez maintenant l’appel API de votre choix dans le champ « requestedAPI » et cliquez sur appeler. Votre tableau de données va se créer automatiquement dans une nouvelle requête :

Une image contenant table

Description générée automatiquement

Conclusion

Une fois la fonction créée, elle peut être utilisée à l’infini et partagée à d’autres utilisateurs sans avoir besoin de connaissance technique particulière. Si vous avez suivi l’ensemble de cet démonstration vous avez maintenant une très bonne connaissance de l’API Piano Analytics ainsi qu’une bonne compréhension des possibilités offertes par le code « m » de power Query

Le fichier d’exemple contant la fonction finalisée :

Récupérer vos données Piano Analytics dans Power Bi et Excel : le guide complet

L’API Piano Analytics permet très facilement d’extraire une importante quantité d’informations stockées dans la solution.

On peut vouloir importer les données Piano Analytics dans Excel pour réaliser une analyse ponctuelle, réaliser des tableaux de bord ou encore nourrir un reporting Power Point.

Dans un autre contexte, on peut également utiliser Power Bi pour créer des rapports décisionnels avec ses données analytics et toute autre source de données.

Nous allons voir à travers cet article que la solution est la même dans les 2 cas : Power Query.

En effet cet outil est disponible aussi bien dans Excel que dans Power Bi, et permet (entre autres) le requêtage de toute API externe pour importer les données et les remanipuler dans exploitation via un langage de programmation (le mashup).

source : Microsoft

Lancer Power Query dans Power BI ou dans Excel

Les présentations étant maintenant faites, rentrons dans le vif du sujet ! Pour suivre ce guide, vous pouvez utiliser de manière indifférente Power Bi ou Excel

Dans power Bi, Power Query est disponible dans l’onglet Accueil > Transformer les données :

Dans Excel, l’outil est disponible dans l’onglet Données > Obtenir des données > Lancer l’éditeur Power Query :

Création de la clé API

Pour requêter vos données Piano Analytics depuis l’extérieur de l’interface, vous allez devoir obtenir une clé API. Cette clé est liée à votre compte Piano Analytics, vous n’aurez donc pas la même que votre collègue. Vous aurez également accès au même périmètre que celui disponible dans votre interface (même liste de niveaux 1).

Une fois connecté à votre compte, allez dans votre profil puis dans l’onglet Api Keys.

 Générez une nouvelle clé et conservez bien l’Access key et surtout la secret key, qui ne vous sera plus donnée par la suite.

Récupération de l’appel API

A partir de la documentation API, il est tout à fait possible de créer votre appel de zéro. Il est cependant plus confortable de partir de l’outil Data Query, qui sera capable de vous restituer un appel API clé en main.

Rendez-vous donc dans l’interface Data Query, où je vous invite à créer l’appel de votre choix, peu importe la période (nous verrons cette question plus tard). Dans mon cas je vais simplement prendre le nombre de pages vues et de visites pour mes pages avec aux moins 10 pages vues :

Pour récupérer l’appel API correspondant, ouvrez l’onglet de partage et sélectionnez « Copier l’URL API (GET) » (nous reviendrons sur la notion d’API Post dans un autre article).

L’url est maintenant présente dans votre presse papier. Si vous la coller dans un bloc-notes, vous verrez que celle-ci est encodée. Pour y voir plus clair, je vous conseille de la désencoder via l’outil de votre choix. De mon côté je vais utiliser l’outil du site Meyerweb. On obtient donc ce résultat :

https://api.atinternet.io/v3/data/getData?param={"columns":["page","m_visits","m_page_loads"],"sort":["-m_visits"],"space":{"s":[592201]},"period":{"p1":[{"type":"D","start":"2022-04-12","end":"2022-04-18"}]},"max-results":50,"page-num":1,"options":{}}

Cet article n’est pas là pour apprendre à décortiquer la structure de cet appel (pour rappel, la documentation est ici), retenez simplement que le paramètre « param » de l’url est au format Json et est composé de plusieurs propriétés (columns, sort, period, max-results…).

Test de l’API Piano Analytics

On peut maintenant tester si notre clé API fonctionne ! Pour cela collez l’url dans votre navigateur. Un popup va s’afficher et vous demander un nom d’utilisateur et un mot de passe. Mettez votre Access key en tant que Nom d’utilisateur et la secret Key en mot de passe :

Vous obtiendrez normalement un json, que vous pourrez rendre plus lisible avec une petite extension chrome du type Json Viewer. On retrouve bien dans la propriété « rows » nos pages avec leurs données :

Créer son appel Power Query

Ouvrez Power Query et créez une nouvelle source de données avec le type « web », qui correspond au requêtage des API et des pages web :

Placez votre requête API dans le champ et sélectionnez le mode avancé :

Nous allons maintenant gérer l’authentification de l’API, qui se fait via le header de la requête.

On ajoute pour cela un paramètre « x-api-key », avec pour valeur la concaténation de votre access key et de votre secret key séparées d’un « _ » : accessKey_secretKey

Power Query vous demandera alors le type de connexion que vous souhaitez mettre en place. Comme le système d’authentification est déjà présent dans l’appel en lui-même via x-api-key, on peut rester sur le mode anonyme :

Power Query vous affiche maintenant les données qu’il a récupérées, et nous allons tâcher de les déployer afin de pouvoir les utiliser. Procédez comme ceci :

  • Cliquez sur la valeur « Record » à gauche de « DataFeed » (Non nécéssaire pour Power Bi)
  • Cliquez sur la valeur « List » associée à « Rows »
  • Dans l’onglet « Transformer », sélectionnez « convertir vers la table » puis « OK » dans la popin « vers la table » qui s’affiche
  • A droite de l’entête de colonne « Column1 » cliquez sur le petit icône avec les 2 flèches qui s’opposent et sélectionnez les colonnes que vous souhaitez extraire.

Vous avez maintenant vos données qui s’affichent dans Power Query. Vous pouvez renommer les colonnes qui ont toutes le préfixe « columns1 », pour plus de clarté. Vous pouvez maintenant cliquer sur « Fermer et charger » (« fermer et appliquer » pour Power Bi) pour que les données soient chargées dans le document :

Libre à vous de créer maintenant autant d’appels que vous le souhaitez en suivant cette méthode !

Dynamiser les périodes d’appel de vos API

Vous l’aurez peut-être remarqué mais la période donnée par Data Query dans l’API est toujours une période dite « fixe », c’est-à-dire avec une date de début et une date de fin, et cela même si vous sélectionnez une période relative (hier, la semaine dernière, le mois en cours) dans l’interface :

period »:{« p1 »:[{« type »:"D","start":"2022-04-12","end":"2022-04-18"}]}

Pour changer la période d’appel, il faudrait donc théoriquement éditer notre requête dans Power Query et changer manuellement la date, ce qui n’est pas très pratique (notamment si vous avez des dizaines d’appels) :

Fort heureusement la documentation nous donne les outils pour récréer une période relative dans l’appel API.

Si on reprend notre json de base gérant la date « period »:{« p1 »:[{« type »: »D », »start »: »2022-04-12″, »end »: »2022-04-18″}]} » voici ce que cela deviendrait pour des périodes relatives :

Période relativeJson correspondant
Aujourd’hui« period »:{« p1 »:[{« type »: « R », »granularity »: « D », »startOffset »: 0, »endOffset »: 0}]}
hier« period »:{« p1 »:[{« type »: « R », »granularity »: « D », »startOffset »: -1, »endOffset »: -1}]}
La semaine en cours« period »:{« p1 »:[{« type »: « R », »granularity »: « W », »startOffset »: 0, »endOffset »: 0}]}
Le mois dernier« period »:{« p1 »:[{« type »: « R », »granularity »: « M », »startOffset »: -1, »endOffset »: -1}]}
L’année en cours« period »:{« p1 »:[{« type »: « R », »granularity »: « Y », »startOffset »: 0, »endOffset »: 0}]}

Comme vous le voyez, le patern se répète et il n’y a en réalité que 3 éléments à changer :

  • Granularity, qui indique la granularité de la période (Days, Week, Month, Quarter, Year)
  • StartOffset, qui indique la période à laquelle il faut faire remonter l’appel. 0 correspond à la période en cours, -1 la période précédente, -2 la période encore précédente…
  • EndOffset, qui indique la période à laquelle il faut arrêter l’appel. Le format est le même que pour StartOffset

Ainsi, « granularity »: « W », »startOffset »: -2, »endOffset »: -1 » veut dire « sélectionne moi les 2 dernières semaines par rapport à la date actuelle ». Ou encore « granularity »: « D », »startOffset »: -366, »endOffset »: -1 » veut dire « sélectionne moi les 365 derniers jours par rapport à la date actuelle ».

Vous pouvez ainsi imaginer une multitude de combinaisons pour vos appels en mixant ces 3 paramètres.

Si on souhaite maintenant modifier la période de départ de notre appel pour la rendre dynamique par rapport à la date actuelle, par exemple en sélectionnant la semaine précédente :

  • Ouvrez Power Query et sélectionnez la requête à modifier
  • Dans « étapes appliquées », double cliquez sur « Source » pour afficher à nouveau le champ contenant l’appel API
  • Remplacez la propriété « period » avec la période relative.

Validez vos changements en cliquant sur « OK » :

Votre appel s’adaptera donc bien maintenant à la date à laquelle vous actualisez votre document !

Bonus 1 : modifiez la période de tous vos appels API en une seule fois

Comme vous l’aurez compris, pour modifier la période de vos appels API, il faudra alors le faire manuellement (pour passer de la période « semaine -1 » à « semaine -2 » par exemple). Si vous devez régulièrement changer les périodes d’appels, le travail peut vite devenir fastidieux.

Une solution pour contourner ce problème est de créer une valeur unique, qui sera automatiquement prise en compte comme période dans tous les appels.

Nous allons utiliser pour cela les paramètres power query :

  • Allez dans accueil > Gérer les paramètres > Nouveau Paramètre
  • Nommez-la avec le nom de votre choix (« periode_analyse » dans mon cas) 
  • placez le json de la période souhaitée dans le champ « valeur actuelle ». dans mon cas je souhaite le mois précédent, donc {« p1 »:[{« type »: « R », »granularity »: « M », »startOffset »: –1, »endOffset »: -1}]}

Maintenant que notre paramètre existe, nous allons remplacer la période présente dans les appels par notre paramètre. Pour cela :

  • Sélectionnez la requête à modifier et placez-vous sur l’étape « Source »
  • Dépliez via la flèche le code m correspondant à cette étape et repérez la zone où est écrite la période
  • Supprimez la période présente et concaténez le début de l’appel API avec notre paramètre période_analyse, puis concaténez à nouveau avec la fin de l’API via le format début_requete’&période_analyse&’fin_requete (cf. capture)

Effectuez cette opération sur l’ensemble des appels à dynamiser. Et voilà ! Vous pouvez maintenant changer à loisir votre période d’analyse, l’ensemble des appels seront mis à jour. A noter que vous pouvez effectuer exactement la même démarche pour une éventuelle période de comparaison.

Bonus 2 : Dynamiser la période à partir de dates fixes

Le format relatif des dates est très pratique, mais très spécifique à Piano Analytics. Si d’autres sources que vos données analytics sont appelées, vous aurez potentiellement besoin d’utiliser le même format de date partout afin d’avoir la même période dans toutes vos tables.

L’idéal serait donc de pouvoir repasser dans un format de dates plus conventionnelles (AAAA-MM-JJ par exemple) mais tout en gardant le système de dynamisation.

C’est ce que nous allons faire ici ensemble, via l’écriture d’un peu de mashup ! Pour rappel, Power Query n’est « qu’un » générateur de code en langage m (ou aussi appelé Mashup), dont vous pouvez voir l’appercu dans Power Query > Accueil > Editeur avancé. Si on en comprend la nomenclature il est possible d’écrire son propre code sans passer par l’interface.

Notre objectif va être simple : Recréer dynamiquement au format AAAA-MM-JJ les dates de début et de fin du mois précédent afin de pouvoir les placer dans mes appels (à noter que la même logique pourra être appliquée pour les autres types de périodes). Si je suis par exemple le 3 mai 2022, le code devra alors me donner 2022-04-01 et 2022-04-30.

Construction de la requête

La première étape va être pour nous de créer une requête vite. Pour cela allez dans Accueil > Nouvelle Source > Requête vide.

Allez maintenant dans Accueil > Editeur avancé pour afficher le code. Vous devriez avoir quelque chose comme ceci :

La première étape pour nous va être de récupérer la date du jour, afin d’en déduire le mois précédent. Ainsi, à chaque mise à jour du fichier, la date du jour sera automatiquement updatée. Nous allons pouvoir utiliser la fonction DateTime.LocalNow() pour cela. On va également renommer l’étape en « today » afin que cela soit plus explicite.

Vous devriez avoir un code comme ceci, et obtenir la date du jour dans l’éditeur :

On ajoute maintenant une étape, permettant d’enlever 1 mois à la date du jour via la méthode Date.AddMonths et en y plaçant la valeur -1. Nous sommes maintenant dans le bon mois :

Notre objectif va maintenant être de construire les dates de début et de fin, à partir de la variable lastMonth en extrayant l’année et le mois puis en créant le jour. Commençons par la date de début.

Pour extraire l’année d’une date, on va pouvoir utiliser la méthode Date.year. On va également convertir la valeur au format string via la méthode Number.toText car nous en aurons besoin plus tard :

On va appliquer la même logique pour récupérer le mois, avec la méthode date.Month :

On va ici avoir une difficulté qui n’était pas présente sur l’année : on ne souhaite pas récupérer « 3 » comme valeur mais « 03 », sinon la date ne sera pas au bon format (AAAA-MM-JJ). Pour corriger cela il suffirait de concaténer la valeur récupérée avec « 0 » afin d’obtenir le bon format :

startDateMonth = « 0 »& Number.ToText(Date.Month(LastMonth))

Cependant cette méthode ne fonctionnera pas pour les mois contenant 2 chiffres (octobre, novembre, décembre) puisque cela va nous générer des valeurs du type « 010 ».

Pour prendre en compte des mois particuliers, on va demander à tronquer la valeur pour ne conserver que les 2 derniers caractères, via la méthode text.end :

startDateMonth = Text.End(« 0″& Number.ToText(Date.Month(LastMonth)),2)

Finalement la valeur du jour sera toujours la même : « 01 » pas besoin de code spécifique pour cela !

Il ne reste plus qu’à concaténer l’ensemble dans une variable :

startDate = startDateYear& »-« &startDateMonth& »-01″

Construire la date de fin

Récupérer l’année et le mois de la date de fin ne comporte aucune nouveauté, vous pourrez donc récupérer les valeurs de la même manière que la date de début.

Le jour va en revanche nous poser un peu plus de problème. En effet cette valeur doit représenter le dernier jour du mois, il peut donc être égal à 28,29,30 ou 31.

Dans un premier temps nous appliquons la méthode Date.EndOfMonth pour obtenir la date du dernier jour du mois :

    endDateMonth = Date.EndOfMonth(LastMonth)

On extrait maintenant le jour de cette date via la méthode date.Day et on transforme la valeur en texte :

 endDateMonth = Number.ToText(Date.Day(Date.EndOfMonth(LastMonth)))

Il n’y a plus qu’à concaténer l’ensemble des valeurs pour créer la date de fin (je vais le faire ici en un seul bloc :

endDate = Number.ToText(Date.Year(LastMonth))& »-« &Text.End(« 0″ & Number.ToText(Date.Month(LastMonth)),2)& »-« &Number.ToText(Date.Day(Date.EndOfMonth(LastMonth)))

Retourner les 2 valeurs sous la forme d’une liste et les injecter dans nos appels

Il ne reste plus qu’à retourner les 2 dates sous la forme d’une liste pour en finir avec notre code :

Nous allons maintenant pourvoir intjecter les 2 dates crées dans toutes les sources qui nous intéressent et pas uniquement l’API Piano Analytics.

Pour cela, on édite l’appel en question et on va placer chaque item de notre liste au bon emplacement via la sélection {0} et {1} :

Conclusion

Nous avons vu à travers cet article comment effectuer nos appels API Piano Analytics et comment dynamiser les dates d’appels. Ayez bien en tête que n’importe quelle période est dynamisable et si le cas dont vous aviez besoin n’a pas été vu dans cet article, je vous renvoie vers l’ensemble des méthodes M autour des dates.