Instructivo

Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

Usamos un pequeño conjunto de datos proporcionado por Kavle Leetaru para ilustrar la API de Timeseries Insights. El conjunto de datos se deriva del proyecto GDETX, una base de datos global que realiza un seguimiento de los eventos mundiales y la cobertura de los medios. Este conjunto de datos contiene las menciones de entidades en las URL de noticias de abril de 2019.

Objetivos

  • Conoce el formato de datos para la API de Timeseries Insights.
  • Aprende a crear, consultar, actualizar y borrar conjuntos de datos.

Costos

La vista previa no tiene costo.

Antes de comenzar

Configura un proyecto de Cloud y habilita la API de Series Series Insights en Primeros pasos.

Conjunto de datos del instructivo

El conjunto de datos incluye anotaciones de entidades sobre ubicaciones, organizaciones, personas, entre otras.

La API de Timeseries Insights toma entradas de formato JSON. Un Event de muestra para este conjunto de datos es

{
  "groupId":"-6180929807044612746",
  "dimensions":[{"name":"EntityORGANIZATION","stringVal":"Medina Gazette"}],
  "eventTime":"2019-04-05T08:00:00+00:00"
}

Cada evento debe tener un campo eventTime para la marca de tiempo del evento y un groupId con valor largo a fin de marcar eventos relacionados. Las propiedades de eventos se incluyen como dimensions, cada una de las cuales tiene un name y uno de stringVal, boolVal, longVal o doubleVal.

NOTA: Las API de Google Cloud aceptan mayúsculas y minúsculas (como camelCase) y caso de serpiente (como snake_case) para los nombres de campo JSON. Por lo general, las documentación se escriben como mayúsculas camellos.

NOTA: Debido a que los valores largos JSON (números) son valores flotantes con solo precisións de números enteros, groupId y longVal están efectivamente limitados a 53 dígitos binarios si JSON. utiliza números. Para proporcionar datos int64, el valor JSON debe estar entre comillas. Una groupId suele ser un ID numérico o se genera con una función hash determinista, lo que cumple con la restricción anterior.

NOTA: Se supone que los campos name y stringVal son valores alfanuméricos, incluido '_'. No se admiten los caracteres especiales, incluido el espacio.

NOTA: Cuando se lee desde una fuente de datos estática de Google Cloud Storage, se supone que cada evento JSON es una sola línea de la siguiente manera:

{"groupId":"-6180929807044612746","dimensions":[{"name":"EntityORGANIZATION","stringVal":"Medina Gazette"}],"eventTime":"2019-04-05T08:00:00+00:00"}

Mostrar lista de conjuntos de datos

projects.datasets.list muestra todos los conjuntos de datos en ${PROJECT_ID}. Ten en cuenta que gcurl es un alias y PROJECT_ID es una variable de entorno que se configura en Primeros pasos.

$ gcurl https://timeseriesinsights.googleapis.com/v1/projects/${PROJECT_ID}/datasets

El resultado es una string JSON como la siguiente:

{
  "datasets": [
    {
      "name": "example",
      "state": "LOADED",
      ...
    },
    {
      "name": "dataset_tutorial",
      "state": "LOADING",
      ...
    }
  ]
}

Los resultados muestran los conjuntos de datos que se encuentran actualmente en el proyecto. El campo state indica si el conjunto de datos está listo para usarse. Cuando se crea un conjunto de datos, está en el estado LOADING hasta que se completa la indexación y, luego, pasa al estado LOADED. Si se produce algún error durante la creación y la indexación, estará en estado FAILED. El resultado también incluye la información completa del conjunto de datos de la solicitud de creación original.

Crear un conjunto de datos

projects.datasets.create agrega un conjunto de datos nuevo al proyecto.

$ gcurl -X POST -d @create.json https://timeseriesinsights.googleapis.com/v1/projects/${PROJECT}/datasets

donde create.json contiene lo siguiente:

{
  name: "dataset_tutorial",
  streaming: true,
  ttl: "8640000s",
  dataNames: [
    "EntityCONSUMER_GOOD",
    "EntityEVENT",
    "EntityLOCATION",
    "EntityORGANIZATION",
    "EntityOTHER",
    "EntityPERSON",
    "EntityUNKNOWN",
    "EntityWORK_OF_ART",
  ],
  dataSources: [
    {uri: "gs://data.gdeltproject.org/blog/2021-timeseries-insights-api/datasets/webnlp-201904.json"}
  ]
}

Esta solicitud crea un conjunto de datos llamado dataset_tutorial a partir de GCS dataSources, que contiene datos de evento en formato JSON. El sistema solo indexa y usa las dimensiones que aparecen en dataNames. Si es streaming=true, el conjunto de datos también acepta actualizaciones de transmisión después de que se completa la indexación inicial. Sin embargo, se ignoran las actualizaciones de transmisión que tienen más de ttl de antig.edad.

La solicitud de creación muestra éxito si el servidor de la API la acepta. El conjunto de datos estará en estado LOADING hasta que se complete la indexación; luego, el estado se convierte en LOADED y comienza a aceptar consultas y actualizaciones si hay alguna.

Conjunto de datos de consulta

projects.datasets.query realiza consultas de detección de anomalías.

$ gcurl -X POST -d @query.json https://timeseriesinsights.googleapis.com/v1/projects/${PROJECT}/datasets/dataset_tutorial:query

donde query.json contiene lo siguiente:

{
  dimensionNames: ["EntityLOCATION"],
  testedInterval: {
    startTime: "2019-04-15T00:00:00Z",
    length: "86400s"
  },
  forecastParams: {
    holdout: 10,
    minDensity: 0,
    forecastHistory: "1209600s",
    maxPositiveRelativeChange: 1,
    maxNegativeRelativeChange: 1,
    forecastExtraWeight: 0,
    seasonalityHint: "DAILY",
  },
  returnNonAnomalies: true
}

Queremos detectar si se produjo alguna anomalía durante el testedInterval para las divisiones en las dimensiones proporcionadas por dimensionNames. Un slice es un subconjunto de eventos en el conjunto de datos con valores fijos para algunas de sus dimensiones. Por ejemplo, {"name": "EntityLOCATION","stringVal": "Seine River"} es una porción. Cualquier subconjunto de dataNames de la definición del conjunto de datos se puede usar como dimensionNames, la API agregará el evento sobre las dimensiones no mencionadas. Esto es similar a una operación de "agrupar por" con "recuento(*)" en consultas de SQL.

Los eventos de cada una de las porciones se agregan en función de forecastParams.aggregatedDimension. Si este campo está vacío, simplemente registraremos todos los eventos en la porción. Si no está vacío, esperamos que este campo sea un nombre de dimensión numérico válido presente en los eventos de esta porción. Los valores numéricos se suman para formar la serie temporal.

Analizaremos cada porción para comprobar si es una anomalía por:

  1. Formar una serie temporal que va de testedInterval.startTime - forecastParams.forecastHistory a testedInterval.startTime + testedInterval.length, en la que cada dato es un depósito de tiempo de testedInterval.length y tiene su valor dado mediante la agregación de los eventos en ese depósito según lo especificado por el parámetro aggregatedDimension. Si la serie temporal no tiene suficientes datos (como se especifica en el parámetro minDensity), dejaremos de analizarla.
  2. Una vez que calculamos la serie temporal para la porción, la analizaremos mediante técnicas de previsión comunes. El primer (100 - holdout)% de la serie temporal se usa para entrenar un modelo de predicción y se usa el último holdout% a fin de probar la calidad del modelo. En función de las métricas de error, calcularemos los límites de confianza para el intervalo probado y, si el valor real no se encuentra dentro de estos límites por más de lo configurado, lo marcaremos de forma nominal. Para configurar la medida que el valor real puede estar fuera de los límites, verifica los parámetros maxPositiveRelativeChange, maxNegativeRelativeChange y forecastExtraWeight.

El resultado de la consulta se ve de la siguiente manera:

{
  "name": "projects/timeseries-staging/datasets/dataset_tutorial",
  "anomalyDetectionResult": {
    "anomalies": [
      {
        "dimensions": [
          {
            "name": "EntityLOCATION",
            "stringVal": "Ile de la Cite"
          }
        ],
        "result": {
          "holdoutErrors": {},
          "trainingErrors": {
            "mdape": 1,
            "rmd": 1
          },
          "forecastStats": {
            "density": "23",
            "numAnomalies": 1
          },
          "testedIntervalActual": 440,
          "testedIntervalForecastLowerBound": -1,
          "testedIntervalForecastUpperBound": 1
        },
        "status": {}
      },
      {
        "dimensions": [
          {
            "name": "EntityLOCATION",
            "stringVal": "Seine"
          }
        ],
        "result": {
          "holdoutErrors": {
            "mdape": 0.1428571428571429,
            "rmd": 0.1428571428571429
          },
          "trainingErrors": {
            "mdape": 0.84615384615384626,
            "rmd": 0.62459546925566334
          },
          "forecastStats": {
            "density": "85",
            "numAnomalies": 1
          },
          "testedIntervalActual": 586,
          "testedIntervalForecast": 9.3333333333333339,
          "testedIntervalForecastLowerBound": 8,
          "testedIntervalForecastUpperBound": 10.666666666666668
        },
        "status": {}
      },
      {
        "dimensions": [
          {
            "name": "EntityLOCATION",
            "stringVal": "Notre Dame"
          }
        ],
        "result": {
          "holdoutErrors": {
            "mdape": 0.42857142857142855,
            "rmd": 0.42857142857142855
          },
          "trainingErrors": {
            "mdape": 0.19999999999999996,
            "rmd": 0.65055762081784374
          },
          "forecastStats": {
            "density": "100",
            "numAnomalies": 1
          },
          "testedIntervalActual": 790,
          "testedIntervalForecast": 7,
          "testedIntervalForecastLowerBound": 4,
          "testedIntervalForecastUpperBound": 10
        },
        "status": {}
      },
      ...
    ],
    "nonAnomalies": [
      ...
    ]
  }
}

Contiene anomalías y, de manera opcional, evalúa las porciones que no se marcaron como anomalías, en el mismo formato de ForeSlicelice. result muestra la hora de la anomalía, el valor real y el rango de valores de previsión. trainingErrors y holdoutErrors muestran métricas adicionales que se usan para la detección de anomalías.

Actualización de la transmisión

projects.datasets.appendEvents agrega registros de evento de forma de transmisión si la solicitud de creación especifica streaming: true.

$ gcurl -X POST -d @append.json https://timeseriesinsights.googleapis.com/v1/projects/${PROJECT}/datasets/dataset_tutorial:appendEvents

donde append.json contiene lo siguiente:

{
  events: [
    {
      "groupId":"-5379487492185488040",
      "dimensions":[{"name":"EntityPERSON","stringVal":"Jason Marsalis"}],
      "eventTime":"2021-06-01T15:45:00+00:00"
    },{
      "groupId":"1324354349507023708",
      "dimensions":[{"name":"EntityORGANIZATION","stringVal":"WAFA"}],
      "eventTime":"2021-06-02T04:00:00+00:00"
    }
  ]
}

Las actualizaciones transmitidas se indexan casi en tiempo real para que los cambios puedan responder con rapidez en los resultados de la consulta.

Borrar conjunto de datos

projects.datasets.delete marca el conjunto de datos para la eliminación.

$ gcurl -X DELETE https://timeseriesinsights.googleapis.com/v1/projects/${PROJECT}/datasets/dataset_tutorial

La solicitud se muestra de inmediato y el conjunto de datos no aceptará consultas ni actualizaciones adicionales. Puede tomar un tiempo hasta que los datos se quiten completamente del servicio, después de lo cual los conjuntos de datos de List no mostrarán este conjunto de datos.

¿Qué sigue?

Puedes encontrar otros ejemplos en el sitio web de GDETX.