Tutorial

Utilizamos un pequeño conjunto de datos proporcionado por Kale Leetaru para ilustrar la API Insights. El conjunto de datos se deriva de The GDELT Project, una plataforma mundial de monitorización de bases de datos que realiza un seguimiento de los eventos internacionales y la cobertura de medios. Este conjunto de datos contiene las menciones a las entidades que aparecen en las URL de noticias en abril del 2019.

Objetivos

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

Costes

La vista previa es gratuita.

Antes de empezar

Configura un proyecto de Cloud y habilita la API Timeseries Insights en la introducción.

Conjunto de datos del tutorial

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

La API Insights de TimeSeries utiliza datos de formato JSON. Un Event de muestra de 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 de valor largo para marcar los eventos relacionados. Las propiedades de evento se incluyen como dimensions, cada una de las cuales tiene un valor de name y uno de stringVal, boolVal, longVal o doubleVal.

NOTA: Las API de Google Cloud aceptan mayúsculas y minúsculas (como camelCase) y serpiente (como snake_case) para los nombres de campos JSON. La documentación se compone principalmente de un camello.

NOTA: Dado que los valores largos de JSON (números) son en realidad valores decimales con solo una precisión de números enteros, tanto groupId como longVal están limitados a 53 dígitos binarios si JSON. utiliza números. Para proporcionar datos int64, el valor JSON se debe citar como una cadena. Por lo general, un groupId es un ID numérico o generado con una función hash determinista, respetando la restricción anterior.

NOTA: Los campos name y stringVal suelen ser valores alfanuméricos, incluidos '_'. No se admiten caracteres especiales, incluido el espacio.

NOTA: Al leer desde una fuente de datos estática de Google Cloud Storage, se supone que cada evento JSON debe ser una sola línea, tal como se indica a continuación:

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

Mostrar conjuntos de datos

projects.Arrays.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 la introducción.

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

El resultado es una cadena JSON como

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

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

Crear conjunto de datos

projects.Arrays.create añade un nuevo conjunto de datos al proyecto.

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

donde create.json contiene:

{
  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 desde dataSources de GCS, que contiene datos de eventos en formato JSON. El sistema solo indexa y utiliza las dimensiones que aparecen en dataNames. Si el valor es streaming=true, el conjunto de datos también acepta actualizaciones de transmisión después de que finalice la indexación inicial. Sin embargo, las actualizaciones en streaming anteriores a ttl se ignoran.

La solicitud de creación devuelve un resultado correcto si el servidor de la API la acepta. El conjunto de datos tendrá el estado LOADING hasta que finalice la indexación, el estado pasa a ser LOADED y empezará a aceptar consultas y actualizaciones, si las hay.

Conjunto de datos de consultas

projects.databases.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:

{
  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 ha producido alguna anomalía durante la testedInterval para las divisiones divididas en las dimensiones proporcionadas por dimensionNames. slice es un subconjunto de eventos del 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á eventos en las dimensiones no mencionadas. Esta operación es similar a una operación "agrupar por" con "count(*)" en las consultas SQL.

Los eventos de cada una de las porciones se agregan en función de forecastParams.aggregatedDimension. Si este campo está vacío, simplemente contaremos todos los eventos de 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 parte. Los valores numéricos se suman para formar la serie temporal.

Analizaremos cada fragmento para verificar si se trata de una anomalía:

  1. Formar una serie temporal que va del testedInterval.startTime - forecastParams.forecastHistory al testedInterval.startTime + testedInterval.length, en la que cada punto de datos es un intervalo de tiempo de testedInterval.length y tiene un valor asignado a los eventos de dicho segmento tal y como especifica el parámetro aggregatedDimension. Si la serie temporal no tiene suficientes datos (especificados mediante el parámetro minDensity), dejaremos de analizarla.
  2. Una vez que hayamos calculado la serie temporal de la porción, la analizaremos mediante técnicas de previsión comunes. La primera (100 - holdout)% de la serie temporal se utiliza para preparar un modelo de predicción y se utiliza el último objeto holdout% para probar la calidad del modelo. En función de las métricas de error, calculamos los límites de confianza del intervalo probado y, si el valor real está fuera de estos límites más allá de la configuración, lo marcamos como un anómalo. Para configurar qué valor puede estar realmente fuera de los límites, consulta los parámetros maxPositiveRelativeChange, maxNegativeRelativeChange y forecastExtraWeight.

El resultado de la consulta sería el siguiente:

{
  "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, opcionalmente, fragmentos evaluados que no se han marcado como anomalías, en el mismo formato de ForecastSlice. result muestra la hora de la anomalía, el valor real y el intervalo de valores de previsión. trainingErrors y holdoutErrors muestran métricas adicionales que se utilizan para la detección de anomalías.

Actualización de streaming

projects.conjuntos de datos.appendEvents añade registros de eventos de una transmisión 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:

{
  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 se transmiten casi en tiempo real para que los cambios puedan responder rápidamente en los resultados de las consultas.

Eliminar conjunto de datos

projects.conjuntos de datos.delete marca el conjunto de datos para eliminarlo.

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

La solicitud devuelve inmediatamente y el conjunto de datos no aceptará consultas o actualizaciones adicionales. Es posible que los datos tarden un tiempo en eliminarse completamente del servicio. Después, los conjuntos de datos de la lista no mostrarán este conjunto de datos.

Siguientes pasos

Puedes ver otros ejemplos en el sitio web de GDELT.