Completa los valores faltantes a partir de solicitudes de fecha

Nick Mihailovski, equipo de la API de Google Analytics, octubre de 2009

En este artículo, se explica cómo detectar y reabastecer los valores de series temporales faltantes en los datos que muestra la API de Data Export de Google Analytics.


Antes de comenzar

En el artículo, se asume que sabes cómo funciona la API de Google Analytics Data Export. El código de muestra está en Java, pero puedes usar los conceptos en el lenguaje que prefieras. El código de este artículo se proporciona como código abierto y se puede descargar desde el hosting del proyecto.

Después de leer este artículo, aprenderás lo siguiente:

  • Cómo la API de Data Export de Google Analytics trata las dimensiones de fecha.
  • Cómo estructurar tus consultas para agrupar los resultados y detectar fechas faltantes
  • Cómo completar los valores faltantes con Java

Introducción

La comparación de datos en un período de tiempo proporciona contexto. Por ejemplo, decir que un sitio web generó USD 1 millón en ingresos no significa mucho. Sin embargo, indicar que un sitio web aumentó sus ingresos 10 veces de un trimestre a otro o de un año a otro es realmente impresionante. Con la API de Google Analytics, es fácil trazar datos en el tiempo mediante las dimensiones ga:date, ga:day y ga:month.

Si tu consulta solo usa una dimensión de fecha, si algún día del período no recopiló datos, la API de Google Analytics reabastecerá las fechas y los valores de 0 para las métricas.

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

Sin embargo, puede ser complicado consultar la fecha junto con otras dimensiones. Si una de las fechas no tiene datos, la API NO mostrará una entrada para esa fecha. Simplemente pasará a la siguiente fecha disponible que contenga datos.

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

Lo ideal es que los analistas quieran completar las fechas que faltan para una palabra clave específica, como en el primer ejemplo anterior

En este artículo, se describen algunas prácticas recomendadas para reabastecer datos de forma pragmática.

Información general

Veamos primero por qué existe este problema. Hay 2 razones.

  1. Google Analytics solo procesa los datos que se recopilan. Si nadie visitó un sitio en un día en particular, no hay datos para procesar, por lo que no se muestran datos.
  2. Es muy difícil determinar cuántas dimensiones adicionales y qué valores se deben usar para las fechas que no tienen datos.

Por lo tanto, en lugar de intentar definir un proceso para gobernarlos todos, la API de Google Analytics deja el ejercicio de completar datos para consultas que tienen varias dimensiones a cargo del desarrollador. ¡Qué suerte! :)

Descripción general del programa

Estos son los pasos para reabastecer los datos del gráfico anterior.

  1. Modifica la consulta para asegurarte de que las dimensiones se ordenen de forma oportuna.
  2. Determine las fechas esperadas a partir del período.
  3. Itera y reabastece las fechas que faltan.
  4. Completa los valores restantes que faltan.

Modifica la consulta

Para reabastecer las fechas, debemos asegurarnos de que los datos que muestra la API estén en un formato que facilite la detección de la falta de una fecha. A continuación, se incluye una consulta de ejemplo para recuperar ga:keyword y ga:date durante los primeros 5 días de marzo:

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");

Una vez que se envía la consulta a la API, los resultados contendrán una lista de objetos DataEntry. Cada objeto de entrada representa una fila de datos y, además, incluye nombres y valores para las dimensiones o métricas. Como no se usó ningún parámetro de clasificación, los resultados se muestran en un orden arbitrario.

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

Para facilitar la identificación de las fechas que faltan, primero debemos agrupar todas las dimensiones. Para ello, configura el parámetro de orden de la consulta con las dimensiones que se usaron en la consulta original.

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

Si agregas el parámetro de orden, la API mostrará los resultados en el orden deseado.

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

El segundo paso es asegurarse de que, para cada dimensión, todas las fechas se muestren en orden ascendente. Si bien la API de Google Analytics proporciona una serie de dimensiones de fecha, solo ga:date se puede ordenar con precisión en función de los límites de fecha (es decir, días, meses, años). Por lo tanto, si quieres reabastecer fechas, asegúrate de que tu consulta use la dimensión ga:date en las dimensiones y en los parámetros de consulta de orden.

Una vez que se ejecute la consulta ordenada, se mostrarán todas las mismas páginas de destino una al lado de la otra y las fechas estarán en orden secuencial. La lista de fechas de una sola página de destino se puede considerar como una serie temporal y, debido a que están en orden, es mucho más fácil identificar las fechas que faltan.

Determina las fechas previstas

Para detectar las fechas que faltan, debemos comparar las fechas reales que muestra la API con las que se esperan en cada serie temporal. Podemos determinar lo que se espera si hacemos lo siguiente:

  1. Determinar la fecha de inicio esperada de la consulta a la API
  2. Cuenta la cantidad de días esperados en el período de la consulta.

Ambos valores se pueden usar juntos para determinar cada fecha esperada aumentando la fecha de inicio en 1 para cada día del período.

Determinación de la fecha de inicio esperada

Podemos usar el parámetro de consulta start-date como la fecha de inicio esperada de la serie. Debido a que el formato de fecha que se muestra en la respuesta de la API yyyyMMdd es diferente del formato del parámetro de consulta yyyy-MM-dd, primero debemos convertir el formato de fecha para poder usarlo.

El método setExpectedStartDate convierte los formatos de las fechas.

  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);
    }
  }

Contar el número de días esperados

Para obtener la cantidad de días del período, el programa analiza las fechas de inicio y finalización en objetos Date de Java. Luego, usa un objeto Calendar para averiguar el tiempo entre ambas fechas. Se agrega un día a la diferencia en las fechas para que el recuento sea inclusivo.

  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);
  }

Ahora tenemos todos los datos que necesitamos para averiguar qué fechas faltan.

Identificar cada serie temporal en los resultados

Una vez que se ejecuta la consulta, el programa pasa por cada objeto DataEntry en la respuesta de la API. Debido a que la consulta se ordenó inicialmente, la respuesta tendrá una serie temporal parcial para cada palabra clave. Por lo tanto, debemos encontrar el inicio de cada serie temporal, luego revisar cada fecha y completar los datos faltantes que no muestra la API.

Este programa usa las variables dimensionValue y tmpDimensionValue para detectar el inicio de cada serie.

Este es el código completo para manejar la respuesta. A continuación, se analiza cómo completar los datos faltantes.

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);
  }
}

Reabastecimiento de las fechas faltantes

Para cada entrada de una serie, el programa almacena los valores de las métricas (entradas) en una ArrayList llamada row. Cuando se detecta una serie temporal nueva, se crea una fila nueva y la fecha esperada se establece en la fecha de inicio esperada.

Luego, para cada entrada, el programa verifica si el valor de la fecha en la entrada es igual a la fecha esperada. Si son iguales, la métrica de la entrada se agrega a la fila. De lo contrario, el programa detectó que faltan fechas que deben reabastecerse.

El método backfillRow controla el reabastecimiento de datos. Acepta como parámetros las fechas esperadas y encontradas, así como la fila actual. Luego, determina la cantidad de días entre las dos fechas (no incluidas) y agrega esos 0 a la fila.

  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);
      }
    }
  }

Cuando se completa el método, la fila se reabastece con datos y se pueden agregar los datos actuales. Luego, la fecha esperada se incrementa a un día después de la fecha encontrada mediante el método 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 "";
}

Completa los valores restantes

Una vez que los datos de la serie se hayan procesado en un row, debemos comprobar que no falten más fechas al final de la serie.

El método forwardFillRow simplemente calcula la diferencia entre la cantidad de días de la consulta original y el tamaño actual de la fila y agrega esos 0 al final de la fila.

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

En este punto, el programa completó los valores faltantes en la serie temporal. Ahora que tenemos todos los datos, el programa imprime los valores de la dimensión y la métrica como una lista separada por comas.

Conclusión

Con esta muestra, puedes reabastecer fácilmente los datos en fechas que la API no muestra. Como se mencionó anteriormente, esta solución se puede adaptar a cualquier lenguaje de programación. Los desarrolladores pueden incluso adaptar estas técnicas y aplicarlas para manejar múltiples dimensiones y métricas. Ahora es más fácil que nunca comenzar a realizar análisis avanzados de las series temporales que muestra la API de Google Analytics.