チュートリアル

Kalev Leetaru 提供の小さなデータセットを使用して、Timeseries Insights API について説明します。 このデータセットは、世界のイベントとメディア カバレッジを追跡するグローバル データベース GDELT プロジェクトから派生しています。 このデータセットには、2019 年 4 月にニュースの URL で言及されているエンティティが含まれています。

目標

  • Timeseries Insights API のデータ形式について学習します。
  • データセットを作成、クエリ、更新、削除する方法を学習します。

料金

プレビューの費用はありません。

始める前に

スタートガイドに沿って Cloud プロジェクトを設定し、Timeseries Insights API を有効にします。

チュートリアル データセット

このデータセットには、場所、組織、人物などのエンティティのアノテーションが含まれます。

Timeseries Insights API は JSON 形式の入力を受け取ります。このデータセットのサンプル Event は次のとおりです。

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

各イベントには、イベント タイムスタンプの eventTime フィールドと、関連するイベントをマークするために長い groupId を設定する必要があります。イベント プロパティは dimensions として、それぞれ namestringValboolVallongValdoubleVal のいずれかを指定します。

注: Google Cloud APIs は、JSON フィールド名について、キャメルケース(camelCase など)とスネークケース(snake_case など)の両方を受け入れることができます。このドキュメントは主にキャメルケースとして記述されます。

注: JSON の長い値(数値)は実際には整数精度のみを持つ浮動小数点値であるため、JSON の場合、groupIdlongVal の両方が事実上、53 桁のバイナリ値に制限されます。には数字を指定します。int64 データを提供するには、JSON 値を文字列として引用符で囲む必要があります。 通常、groupId は数値 ID か、上述の制限を満たす決定論的ハッシュ関数を使用して生成されます。

注: name フィールドと stringVal フィールドは、'_' を含む英数字である必要があります。スペースなどの特殊文字は使用できません。

注: 静的な Google Cloud Storage データソースから読み取る場合、各 JSON イベントは次のように 1 行に相当します。

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

データセットのリスト表示

projects.datasets.list${PROJECT_ID} のすべてのデータセットを表示します。 gcurl はエイリアスであり、PROJECT_ID は両方ともスタートガイドで設定される環境変数です。

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

結果は次の JSON 文字列です。

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

結果には、現在プロジェクトの下にあるデータセットが表示されます。 state フィールドは、データセットを使用する準備ができているかどうかを示します。データセットの作成直後は、インデックス登録が完了するまで LOADING 状態になり、その後 LOADED 状態に移行します。作成およびインデックス登録中にエラーが発生した場合は、FAILED 状態になります。結果には、元の作成リクエストの完全なデータセット情報も含まれます。

データセットを作成

projects.datasets.create によって新しいデータセットがプロジェクトに追加されます。

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

create.json には以下が含まれます。

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

このリクエストは、GCS 形式のイベントデータを含む dataset_tutorial という名前のデータセットを GCS dataSources から作成します。dataNames にリストされているディメンションのみがインデックスに登録され、システムによって使用されます。streaming=true の場合、データセットは最初のインデックス登録が完了した後にストリーミング更新も受け入れます。ただし、ttl より古いストリーミング更新は無視されます。

API サーバーで承認されると、作成リクエストによって成功が返されます。インデックスが完了するまでデータセットは LOADING 状態になり、ステータスは LOADED になり、クエリと更新があればそれを受け入れます。

データセットのクエリ

projects.datasets.query は、異常検出クエリを実行します。

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

query.json には以下が含まれます。

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

dimensionNames で指定したディメンションに分割されたスライスの testedInterval 中に異常が起きたかどうかを検出する必要があります。スライスは、一部のディメンションで固定された値を使用して、データセット内のイベントのサブセットです。たとえば、{"name": "EntityLOCATION","stringVal": "Seine River"} はスライスです。すべてのサブセットdataNamesとして、dimensionNames API は、説明されていないディメンションを介してイベントを集計します。これは、SQL クエリで「count(*)」を使用する「グループ別」オペレーションに似ています。

各スライスのイベントは forecastParams.aggregatedDimension に基づいて集計されます。このフィールドが空の場合、単にスライスのすべてのイベントがカウントされます。空でない場合は、このフィールドでスライス内の有効な数値ディメンション名を想定します。数値を合計すると、時系列を形成します。

各スライスを分析して異常かどうかを判定します。

  1. testedInterval.startTime - forecastParams.forecastHistory から testedInterval.startTime + testedInterval.length までの時系列を形成する。各データポイントは長さ testedInterval.length の時間バケットであり、そのバケット内のイベントを集計することで値が設定されます。 aggregatedDimension パラメータで指定されているとおりです。時系列で(minDensity パラメータで指定された)データポイントが不十分な場合、分析が停止されます。
  2. スライスの時系列を計算したら、一般的な予測手法を使用して分析します。時系列の最初の (100 - holdout)% を使用して予測モデルをトレーニングし、最後の holdout% を使用してモデルの品質をテストします。エラー指標に基づいて、テストされた間隔の信頼区間を計算します。実際の値がこの範囲外の場合は、異常値としてマークします。実際の値の範囲外の境界を構成するには、maxPositiveRelativeChangemaxNegativeRelativeChangeforecastExtraWeight パラメータを確認します。

クエリ結果は次のようになります。

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

これには、同じ ForecastSlice 形式で異常に評価されたいくつかの異常と、必要に応じて評価されたスライスが含まれます。 result には、異常値、実際の値、予測値の範囲が表示されます。trainingErrorsholdoutErrors は、異常検出に使用される追加の指標を示します。

ストリーミング更新

作成リクエストで streaming: true が指定されている場合は、projects.datasets.appendEvents によって、ストリーミング レコードにイベントレコードが追加されます。

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

append.json には以下が含まれます。

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

ストリーミングされた更新はほぼリアルタイムでインデックス登録されるため、変更はクエリ結果で迅速に返すことができます。

データセットの削除

projects.datasets.delete は削除するデータセットをマークします。

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

リクエストはすぐに戻ります。データセットは追加のクエリと更新を受け入れません。データがサービスから完全に削除されるまでしばらく時間がかかる場合があります。それ以降は List データセットがこのデータセットを返しません。

次のステップ

その他の例は GDELT ウェブサイトをご覧ください。