Cómo completar los valores faltantes de las solicitudes de fecha

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

En este artículo, se analiza cómo detectar y reabastecer los valores faltantes de una serie temporal en los datos que se muestran en la API de exportación de datos de Google Analytics.


Antes de comenzar

En el artículo, se supone que sabes cómo funciona la API de exportación de datos de Google Analytics. 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 se tratan las dimensiones de la API de exportación de datos de Google Analytics
  • Cómo estructurar tus consultas para agrupar resultados y detectar fechas faltantes
  • Cómo completar los valores faltantes con Java

Introducción

La comparación de datos a lo largo de un período proporciona contexto. Por ejemplo, indicar que un sitio web generó $1 millón de ingresos no significa mucho. Sin embargo, afirmar que un sitio web aumentó los ingresos 10 veces respecto del trimestre o año tras año es realmente impresionante. Con la API de Google Analytics, es fácil trazar datos a lo largo del 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 recopiló datos, la API de Google Analytics reabastecerá las fechas y los valores de 0 para las métricas.

ga:fechaga:sesiones
2010-03-01101
2010-03-020
2010-03-0369

Sin embargo, se vuelve difícil si consultas la fecha junto con otras dimensiones. Si una de las fechas no tiene datos, la API NO mostrará una entrada para esa fecha. Pasará a la siguiente fecha disponible que contenga datos.

ga:palabra clavega:fechaga:sesiones
silla2010-03-0155
silla2010-03-0348

Lo ideal es que los analistas deseen que las fechas faltantes para una palabra clave en particular se completen como en el primer ejemplo anterior.

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

Información general

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

  1. Google Analytics solo procesa los datos que se recopilan. Si nadie llegó a un sitio en un día en particular, entonces no hay datos para procesar, por lo que no se muestra ningún dato.
  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 tratar de definir un proceso que los guíe a todos, la API de Google Analytics deja el ejercicio de completar los datos de las consultas que tienen varias dimensiones hasta el desarrollador. Por suerte :)

Descripción general del programa

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

  1. Modifique la consulta para asegurarse de que las dimensiones se ordenen de manera oportunista.
  2. Determine las fechas previstas del período.
  3. Itera y reabastece las fechas que faltan.
  4. Complete los valores que faltan.

Modificar la consulta

Para reabastecer las fechas, debemos asegurarnos de que los datos que muestra la API tengan un formato que facilite la detección de fechas faltantes. A continuación, se muestra 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 y las métricas. Dado que no se usó ningún parámetro de orden, los resultados se muestran en un orden arbitrario.

ga:palabra clavega:fechaga:entradas
silla2010-03-0414
silla2010-03-0123
tabla2010-03-0418
tabla2010-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 en las dimensiones que se usan 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:palabra clavega:fechaga:entradas
silla2010-03-0123
silla2010-03-0313
silla2010-03-0414
tabla2010-03-0224
tabla2010-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 entre los límites de fecha (es decir, días, meses, años). Por lo tanto, si deseas reabastecer las fechas, asegúrate de que tu consulta use la dimensión ga:date en las dimensiones y los parámetros de búsqueda.

Una vez que se ejecute la consulta ordenada, las mismas páginas de destino se mostrarán 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, como están ordenadas, es mucho más fácil identificar las fechas que faltan.

Determina las fechas previstas

Para detectar las fechas faltantes, debemos comparar las fechas reales que muestra la API con las fechas previstas en cada serie temporal. Podemos determinar lo que se espera de la siguiente manera:

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

Ambos valores se pueden usar juntos a fin de determinar cada fecha prevista mediante el aumento de la fecha de inicio por 1 para cada día del período.

Cómo determinar la fecha de inicio prevista

Podemos usar el parámetro de búsqueda 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 búsqueda 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);
    }
  }

Cómo contar la cantidad de días esperados

Para obtener la cantidad de días en el período, el programa analiza las fechas de inicio y finalización en objetos Date de Java. Luego, usa un objeto Calendar para determinar 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.

Identifica 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, necesitamos encontrar el inicio de cada serie temporal y, luego, revisar cada fecha y completar los datos faltantes que la API no mostró.

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

Aquí está el código completo para manejar la respuesta. A continuación, se explica 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);
  }
}

Reabastezca las fechas faltantes

Para cada entrada de una serie, el programa almacena los valores de métricas (entradas) en un ArrayList llamado row. Cuando se detecta una nueva serie temporal, 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 fecha de 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ó fechas faltantes 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 (sin incluir) y agrega esa cantidad de ceros 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 reabasteció con datos y se pueden agregar los datos actuales. La fecha esperada se incrementa a un día después de la fecha encontrada con 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 "";
}

Complete los valores restantes

Una vez que los datos de la serie se procesaron en row, debemos verificar que no falten fechas al final de la serie.

El método forwardFillRow calcula la diferencia entre la cantidad de días de la consulta original en función del tamaño actual de la fila y agrega esa cantidad de ceros 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 de la serie temporal. Ahora que tenemos todos los datos, el programa imprime los valores de dimensiones y métricas como una lista separada por comas.

Conclusión

Con este ejemplo, 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 incluso pueden adaptar estas técnicas y aplicarlas para manejar varias dimensiones y varias métricas. Ahora, comenzar a realizar análisis avanzados de las series temporales que muestra la API de Google Analytics es todavía más fácil que nunca.