Samouczek

Do zilustrowania działania interfejsu Timeseries Insights wykorzystujemy mały zbiór danych udostępniony przez Kalev Leetaru. Zbiór danych pochodzi z projektu GDELT, globalnej bazy danych służącej do śledzenia zdarzeń na świecie i korzystania z mediów. Ten zbiór danych zawiera wzmianki o jednostkach w adresach URL wiadomości w kwietniu 2019 r.

Cele

  • Poznaj format danych w interfejsie Timeseries Insights API.
  • Dowiedz się, jak tworzyć, aktualizować i usuwać zbiory danych.

Wady

Podgląd jest bezpłatny.

Zanim zaczniesz

Skonfiguruj projekt Cloud i włącz interfejs Timeseries Insights API po wprowadzeniu.

Zbiór danych samouczka

Zbiór danych zawiera między innymi adnotacje dotyczące lokalizacji, organizacji i osób.

Interfejs Timeseries Insights API pobiera dane wejściowe w formacie JSON. Przykładowy Zdarzenie dla tego zbioru danych to

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

Każde zdarzenie musi mieć pole eventTime na sygnaturę czasową wydarzenia i długookresową wartość groupId oznaczającą powiązane wydarzenia. Właściwości zdarzenia są podane jako dimensions, a każda z nich ma name i jedną z stringVal, boolVal, longVal lub doubleVal.

UWAGA: w przypadku interfejsów Google Cloud API nazwy pól JSON obsługują zarówno wielkość liter (np. camelCase), jak i wąż (np. snake_case). Dokumenty są napisane głównie jako wielbłądy.

UWAGA: długie wartości JSON (liczby) to w rzeczywistości liczby zmiennoprzecinkowe z dokładnością do liczb całkowitych, więc zarówno groupId, jak i longVal są efektywnie ograniczone do 53 cyfr binarnych, jeśli JSON używa liczb. Aby podać dane int64, wartość JSON należy podać w postaci ciągu. groupId to zwykle identyfikator numeryczny lub generowany za pomocą funkcji hashowej, która spełnia powyższe ograniczenie.

UWAGA: pola name i stringVal powinny być wartościami alfanumerycznymi, w tym '_'. Znaki specjalne zawierające spację nie są obsługiwane.

UWAGA: podczas czytania ze statycznego źródła danych Google Cloud Storage każde zdarzenie JSON powinno być w jednym wierszu:

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

Wyświetlanie listy zbiorów danych

projekty.datasets.list pokazuje wszystkie zbiory danych w ${PROJECT_ID}. Uwaga gcurl to alias, a PROJECT_ID to zmienna środowiskowa skonfigurowana w artykule Pierwsze kroki.

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

Wynikiem jest ciąg znaków JSON, na przykład

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

Wyniki pokazują zbiory danych, które są obecnie w projekcie. Pole state wskazuje, czy zbiór danych jest gotowy do użycia. Utworzony przed chwilą zbiór danych pozostaje w stanie LOADING do momentu zakończenia indeksowania, a następnie przechodzi do stanu LOADED. Jeśli podczas tworzenia i indeksowania wystąpią błędy, będzie ono miało stan FAILED. Wynik zawiera też pełne informacje ze zbioru danych z pierwotnego żądania utworzenia.

Utwórz zbiór danych

projekty.datasets.create dodaje nowy zbiór danych do projektu.

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

Gdzie create.json zawiera:

{
  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"}
  ]
}

To żądanie tworzy zbiór danych o nazwie dataset_tutorial z GCS dataSources, który zawiera dane zdarzenia w formacie JSON. Tylko wymiary wymienione w dataNames są indeksowane i używane przez system. Jeśli streaming=true, zbiór danych akceptuje też aktualizacje strumieniowe po zakończeniu wstępnego indeksowania. Aktualizacje przesyłane strumieniowo starsze niż ttl są jednak ignorowane.

Żądanie utworzenia zwróci, jeśli zostanie zaakceptowane przez serwer interfejsu API. Zbiór danych będzie miał stan LOADING, dopóki indeksowanie nie zostanie ukończone, a stan zmieni się na LOADED i zacznie akceptować zapytania i aktualizacje, jeśli takie występują.

Zbiór danych zapytań

projekty.datasets.query wyszukują anomalie.

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

Gdzie query.json zawiera:

{
  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
}

Chcemy wykryć, czy w okresie testedInterval wystąpiły anomalie w przypadku wymiarów podzielonych między dimensionNames. Wycinek to podzbiór zdarzeń w zbiorze danych ze stałymi wartościami niektórych wymiarów. Na przykład {"name": "EntityLOCATION","stringVal": "Seine River"} to wycinek. Dowolny podzbiór dataNames z definicji zbioru danych może być użyty jako dimensionNames. Interfejs API będzie zbierać zdarzenia przez niewymienione wymiary. Przypomina to operację „grupuj według” z użyciem funkcji „count(*)” w zapytaniach SQL.

Zdarzenia w każdym wycinku są agregowane na podstawie forecastParams.aggregatedDimension. Jeśli to pole jest puste, policzymy wszystkie zdarzenia w wycinku. Jeśli nie jest ono puste, to pole powinno być prawidłową nazwą wymiaru liczbowego występującą w zdarzeniach w tym wycinku. Wartości liczbowe są sumowane w celu utworzenia serii czasowej.

Przeanalizujemy każdy wycinek, by sprawdzić, czy jest to anomalia:

  1. Utworzenie serii czasowej z zakresu od testedInterval.startTime - forecastParams.forecastHistory do testedInterval.startTime + testedInterval.length, w której każdy punkt danych jest przedziałem czasu testedInterval.length i którego wartość jest określona przez agregowanie zdarzeń w tym segmencie zgodnie z wartością aggregatedDimension. Jeśli seria czasowa nie ma wystarczającej liczby punktów danych (zgodnie z parametrem minDensity), przestaniemy ją analizować.
  2. Po obliczeniu serii czasowej dla danego fragmentu przeanalizujemy go, stosując typowe techniki prognozowania. Pierwsze (100 - holdout)% z serii czasowej jest używane do trenowania modelu prognozowania, a ostatnie holdout% do testowania jego jakości. Na podstawie danych o błędach obliczymy granice ufności dla testowanego interwału, a jeśli rzeczywista wartość wykracza poza te granice, oznaczymy je jako anomalię. Aby skonfigurować rzeczywistą wartość spoza zakresu, sprawdź parametry maxPositiveRelativeChange, maxNegativeRelativeChange i forecastExtraWeight.

Wynik zapytania wygląda następująco:

{
  "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": [
      ...
    ]
  }
}

Zawiera anomalie i (opcjonalnie) wycinki, które nie zostały oznaczone jako anomalie, w tym samym formacie Prognozowane wartości. result pokazuje godzinę anomalii, faktyczną wartość i zakres wartości prognozy. trainingErrors i holdoutErrors pokazują dodatkowe dane używane do wykrywania anomalii.

Aktualizacja strumieniowego przesyłania danych

projekty.datasets.appendEvent dodaje rekordy zdarzeń w formie strumieniowej, jeśli żądanie utworzenia zawiera wartość streaming: true.

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

Gdzie append.json zawiera:

{
  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"
    }
  ]
}

Aktualizacje przesyłane strumieniowo są indeksowane niemal w czasie rzeczywistym, dzięki czemu mogą szybko reagować w wynikach zapytań.

Usuń zbiór danych

projekty.datasets.delete oznacza zbiór danych do usunięcia.

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

Żądanie jest zwracane natychmiast, a zbiór danych nie akceptuje kolejnych zapytań ani aktualizacji. Może minąć trochę czasu, zanim dane zostaną całkowicie usunięte z usługi. Po tym czasie zbiór danych listy nie zwróci tego zbioru danych.

Co dalej?

Inne przykłady można znaleźć w witrynie GDELT.