Remplissage des valeurs manquantes dans les demandes de date

Nick Mihailovski, équipe API Google Analytics – Octobre 2009

Cet article explique comment détecter et remplir les valeurs de séries temporelles manquantes dans les données renvoyées par l' API d'exportation de données Google Analytics.


Avant de commencer

Dans cet article, nous partons du principe que vous connaissez le fonctionnement de l'API d'exportation de données Google Analytics. L'exemple de code est en Java, mais vous pouvez utiliser les concepts dans le langage de votre choix. Le code de cet article est fourni en Open Source et peut être téléchargé depuis l'hébergement du projet.

Après avoir lu cet article, vous découvrirez:

  • Comment l'API d'exportation de données Google Analytics traite les dimensions de date
  • Structurer vos requêtes pour regrouper les résultats et détecter les dates manquantes
  • Comment renseigner les valeurs manquantes à l'aide de Java

Présentation

La comparaison des données sur une période donnée fournit un contexte. Par exemple, indiquer qu'un site Web a généré 1 million de dollars de revenus ne veut pas dire grand-chose. Mais il est impressionnant d'affirmer que le chiffre d'affaires d'un site Web a été multiplié par 10 d'un trimestre à l'autre. Avec l'API Google Analytics, vous pouvez facilement tracer les données au fil du temps à l'aide des dimensions ga:date, ga:day et ga:month.

Si votre requête n'utilise qu'une dimension "Date" et que des jours de la plage de dates n'ont collecté aucune donnée, l'API Google Analytics remplira les dates et les valeurs 0 pour les métriques.

ga:datega:sessions
2010-03-01101
2010-03-020
2010-03-0369

Toutefois, cette opération peut s'avérer compliquée si vous interrogez la date avec d'autres variables. Si l'une des dates ne contient aucune donnée, l'API NE affichera PAS d'entrée pour cette date. Il passera simplement à la prochaine date disponible contenant des données.

ga:keywordga:datega:sessions
chaise2010-03-0155
chaise2010-03-0348

Idéalement, les analystes voudraient que les dates manquantes pour un mot clé particulier soient renseignées, comme dans le premier exemple ci-dessus

Cet article décrit certaines bonnes pratiques pour remplir les données de manière pragmatique.

Contexte

Voyons d'abord pourquoi ce problème existe. Il y a deux raisons.

  1. Google Analytics ne traite que les données collectées. Si personne n'a consulté un site un jour en particulier, il n'y a aucune donnée à traiter et aucune donnée n'est donc renvoyée.
  2. Il est très difficile de déterminer le nombre de dimensions supplémentaires et les valeurs à utiliser pour les dates sans données.

Ainsi, au lieu d'essayer de définir un processus pour toutes les règles, l'API Google Analytics laisse au développeur la possibilité de renseigner les données des requêtes comportant plusieurs dimensions. Vous avez de la chance :)

Présentation du programme

Voici les étapes à suivre pour remplir les données du graphique ci-dessus.

  1. Modifiez la requête pour vous assurer que les dimensions sont triées avec opportunisme.
  2. Déterminez les dates attendues dans la plage de dates.
  3. Itérez et remplissez les dates manquantes.
  4. Renseignez toutes les valeurs manquantes restantes.

Modifier la requête

Pour remplir les dates, nous devons nous assurer que le format des données renvoyées par l'API permet de détecter facilement les dates manquantes. Voici un exemple de requête permettant de récupérer ga:keyword et ga:date pendant les cinq premiers jours de mars:

DataQuery dataQuery = new DataQuery(new URL(BASE_URL));
dataQuery.setIds(TABLE_ID);
dataQuery.setStartDate("2010-03-01");
dataQuery.setEndDate("2010-03-05");
dataQuery.setDimensions("ga:keyword,ga:date");
dataQuery.setMetrics("ga:entrances");

Une fois la requête envoyée à l'API, les résultats contiendront une liste d'objets DataEntry. Chaque objet d'entrée représente une ligne de données et inclut des noms et des valeurs pour les dimensions/métriques. Comme aucun paramètre de tri n'a été utilisé, les résultats sont renvoyés dans un ordre arbitraire.

ga:keywordga:datega:entrances
chaise2010-03-0414
chaise2010-03-0123
table2010-03-0418
table2010-03-0224
chaise2010-03-0313

Pour faciliter l'identification des dates manquantes, nous devons d'abord regrouper toutes les dimensions. Pour ce faire, définissez le paramètre de tri de la requête sur les dimensions utilisées dans la requête d'origine.

dataQuery.setSort("ga:keyword,ga:date");

L'ajout du paramètre de tri permet à l'API de renvoyer les résultats dans l'ordre souhaité.

ga:keywordga:datega:entrances
chaise2010-03-0123
chaise2010-03-0313
chaise2010-03-0414
table2010-03-0224
table2010-03-0418

La deuxième étape consiste à s'assurer que pour chaque dimension, toutes les dates sont renvoyées dans l'ordre croissant. Bien que l'API Google Analytics fournisse un certain nombre de dimensions de date, seul ga:date peut être trié avec précision au-delà des limites de date (jours, mois, années). Ainsi, si vous souhaitez remplacer les dates, assurez-vous que votre requête utilise la dimension ga:date à la fois dans les dimensions et dans les paramètres de requête de tri.

Une fois la requête triée exécutée, toutes les mêmes pages de destination sont renvoyées les unes à côté des autres, et les dates sont classées dans l'ordre séquentiel. La liste de dates d'une seule page de destination peut être considérée comme une série temporelle. Comme elles sont dans l'ordre, il est beaucoup plus facile d'identifier les dates manquantes.

Déterminer les dates prévues

Pour détecter les dates manquantes, nous devons comparer les dates réelles renvoyées par l'API aux dates attendues dans chaque série temporelle. Nous pouvons déterminer ce qui est attendu en:

  1. Déterminer la date de début attendue à partir de la requête API.
  2. Compter le nombre de jours prévus dans la plage de dates de la requête.

Les deux valeurs peuvent être utilisées ensemble pour déterminer chaque date prévue en incrémentant la date de début de 1 pour chaque jour de la plage de dates.

Déterminer la date de début prévue

Nous pouvons utiliser le paramètre de requête start-date comme date de début attendue de la série. Étant donné que le format de date renvoyé dans la réponse de l'API yyyyMMdd est différent de celui du paramètre de requête yyyy-MM-dd, nous devons d'abord convertir le format de date avant de pouvoir l'utiliser.

La méthode setExpectedStartDate convertit les formats des dates.

  private static SimpleDateFormat queryDateFormat = new SimpleDateFormat("yyyy-MM-dd");
  private static SimpleDateFormat resultDateFormat = new SimpleDateFormat("yyyyMMdd");

  public void setExpectedStartDate(String startDate) {
    try {
      calendar.setTime(queryDateFormat.parse(startDate));
      expectedStartDate = resultDateFormat.format(calendar.getTime());
    } catch (ParseException e) {
      handleException(e);
    }
  }

Compter le nombre de jours attendus

Pour obtenir le nombre de jours de la plage de dates, le programme analyse les dates de début et de fin dans des objets Java Date. Elle utilise ensuite un objet Calendar pour déterminer l'heure entre les deux dates. Un jour est ajouté à la différence de dates pour que ce nombre soit inclusif.

  private static final long millisInDay = 24 * 60 * 60 * 1000;

  public void setNumberOfDays(DataQuery dataQuery) {
    long startDay = 0;
    long endDay = 0;

    try {
      calendar.setTime(queryDateFormat.parse(dataQuery.getStartDate()));
      startDay = calendar.getTimeInMillis() / millisInDay;

      calendar.setTime(queryDateFormat.parse(dataQuery.getEndDate()));
      endDay = calendar.getTimeInMillis() / millisInDay;
    } catch (ParseException e) {
      handleException(e);
    }

    numberOfDays = (int) (endDay - startDay + 1);
  }

Nous disposons maintenant de toutes les données dont nous avons besoin pour déterminer les dates manquantes.

Identifier chaque série temporelle dans les résultats

Une fois la requête exécutée, le programme passe en revue chaque objet DataEntry de la réponse de l'API. Comme la requête a été triée initialement, la réponse contiendra une série temporelle partielle pour chaque mot clé. Nous devons donc trouver le début de chaque série temporelle, puis passer en revue chaque date et renseigner les données manquantes qui ne sont pas renvoyées par l'API.

Ce programme utilise les variables dimensionValue et tmpDimensionValue pour détecter le début de chaque série.

Voici l'intégralité du code permettant de gérer la réponse. Le remplissage des données manquantes est abordé ci-dessous.

public void printBackfilledResults(DataFeed dataFeed) {
  String expectedDate = "";
  String dimensionValue = "";
  List<Integer> row = null;

  for (DataEntry entry : dataFeed.getEntries()) {
    String tmpDimValue = entry.getDimensions().get(0).getValue();

    // Detect beginning of a series.
    if (!tmpDimValue.equals(dimensionValue)) {
      if (row != null) {
        forwardFillRow(row);
        printRow(dimensionValue, row);
      }

      // Create a new row.
      row = new ArrayList<Integer>(numberOfDays);
      dimensionValue = tmpDimValue;
      expectedDate = expectedStartDate;
    }

    // Backfill row.
    String foundDate = entry.getDimension("ga:date").getValue();
    if (!foundDate.equals(expectedDate)) {
      backFillRow(expectedDate, foundDate, row);
    }

    // Handle the data.
    Metric metric = entry.getMetrics().get(0);
    row.add(new Integer(metric.getValue()));
    expectedDate = getNextDate(foundDate);
  }

  // Handle the last row.
  if (row != null) {
    forwardFillRow(row);
    printRow(dimensionValue, row);
  }
}

Remplir toutes les dates manquantes

Pour chaque entrée d'une série, le programme stocke les valeurs des métriques (entrées) dans un ArrayList appelé row. Lorsqu'une nouvelle série temporelle est détectée, une ligne est créée et la date attendue est définie sur la date de début prévue.

Ensuite, pour chaque entrée, le programme vérifie si la valeur de date de l'entrée correspond à la date prévue. Si elles sont égales, la métrique de l'entrée est ajoutée à la ligne. Sinon, le programme a détecté des dates manquantes qui doivent être renseignées.

La méthode backfillRow gère le remplissage des données. Il accepte comme paramètres les dates attendues et trouvées, ainsi que la ligne actuelle. Il détermine ensuite le nombre de jours entre les deux dates (non inclus) et ajoute autant de zéros à la ligne.

  public void backFillRow(String startDate, String endDate, List<Integer> row) {
    long d1 = 0;
    long d2 = 0;

    try {
      calendar.setTime(resultDateFormat.parse(startDate));
      d1 = calendar.getTimeInMillis() / millisInDay;

      calendar.setTime(resultDateFormat.parse(endDate));
      d2 = calendar.getTimeInMillis() / millisInDay;

    } catch (ParseException e) {
      handleException(e);
    }

    long differenceInDays = d2 - d1;
    if (differenceInDays > 0) {
      for (int i = 0; i < differenceInDays; i++) {
        row.add(0);
      }
    }
  }

Une fois la méthode terminée, la ligne a été remplie de données et les données actuelles peuvent être ajoutées. La date attendue est ensuite incrémentée d'un jour après la date trouvée à l'aide de la méthode getNextDate.

public String getNextDate(String initialDate) {
  try {
    calendar.setTime(resultDateFormat.parse(initialDate));
    calendar.add(Calendar.DATE, 1);
    return resultDateFormat.format(calendar.getTime());

  } catch (ParseException e) {
    handleException(e);
  }
  return "";
}

Renseignez toutes les valeurs restantes

Une fois les données de la série traitées dans une row, nous devons vérifier qu'il ne manque plus de dates à la fin de la série.

La méthode forwardFillRow calcule simplement la différence entre le nombre de jours de la requête d'origine et la taille actuelle de la ligne, et ajoute ces zéros à la fin de la ligne.

public void forwardFillRow(List<Integer> row) {
  int remainingElements = numberOfDays - row.size();
  if (remainingElements > 0) {
    for (int i = 0; i < remainingElements; i++) {
      row.add(0);
    }
  }
}

À ce stade, le programme a renseigné toutes les valeurs manquantes dans la série temporelle. Maintenant que nous disposons de toutes les données, le programme imprime les valeurs des dimensions et des métriques sous forme de liste d'éléments séparés par une virgule.

Conclusion

Cet exemple vous permet de remplir facilement des données sur des dates qui ne sont pas renvoyées par l'API. Comme indiqué ci-dessus, cette solution peut être adaptée à n'importe quel langage de programmation. Les développeurs peuvent même adapter ces techniques et les appliquer pour gérer plusieurs dimensions et métriques. Il est désormais encore plus facile que jamais d'effectuer des analyses avancées sur les séries temporelles renvoyées par l'API Google Analytics.