我们使用 Kalev Letaru 提供的小型数据集来说明 Timeseries Insights API。 该数据集派生自 GDELT 项目,后者是一个全局数据库跟踪世界事件和媒体覆盖范围。 此数据集包含 2019 年 4 月的新闻网址中提及的实体。
目标
- 了解 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
,每个属性都具有 name
以及 stringVal
、boolVal
、longVal
或 doubleVal
之一。
注意:Google Cloud API 接受采用 JSON 字段名称的 camel 峰式大小写形式(例如 camelCase
)和蛇形大小写(例如 snake_case
)。文档主要以 camel 峰式大小写形式编写。
注意:由于 JSON 长值(数字)实际上是具有仅精度精度的浮点值,groupId
和longVal
如果 JSON 使用数字,则实际上相当于 53 个二进制数字。为了提供 int64 数据,JSON 值应作为字符串进行引用。 groupId
通常是数字 ID 或使用确定性哈希函数生成,用于满足上述限制。
注意:name
和 stringVal
字段应当为字母数字值,包括 '_'
。不支持包含空格的特殊字符。
注意:从静态 Google Cloud Storage 数据源读取数据时,每个 JSON 事件应该为一行,如下所示:
{"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 dataSources
创建一个名为 dataset_tutorial
的数据集,其中包含 JSON 格式的事件数据。只有 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
}
我们想要检测testedInterval
划分维度时,dimensionNames
中下载 Google 健身应用。切片是数据集中事件的子集,具有一些维度的固定值。例如,{"name": "EntityLOCATION","stringVal": "Seine River"}
是一个切片。数据集定义中任何 dataNames
子集都可以用作 dimensionNames
,该 API 将对未提及的维度进行聚合。这类似于 SQL 查询中带有“count(*)”的“分组依据”操作。
每个切片中的事件根据 forecastParams.aggregatedDimension
进行汇总。如果此字段为空,我们将仅计算切片中的所有事件。如果不是空,则此字段将是在此切片的事件中有效的数字维度名称。系统会将这些数值相加以形成时间序列。
我们将通过以下方式分析每个切片,以检查是否存在异常:
- 创建一个从
testedInterval.startTime - forecastParams.forecastHistory
到testedInterval.startTime + testedInterval.length
的时间序列,其中每个数据点都是长度为testedInterval.length
的时区,并通过汇总该存储分区中的事件来获取其值如aggregatedDimension
参数所指定。如果时间序列没有足够的数据点(由minDensity
参数指定),我们将停止分析该数据点。 - 计算切片的时间序列后,我们将使用常用的预测技术对其进行分析。时间序列的第一个
(100 - holdout)%
用于训练预测模型,最后一个holdout%
用于测试模型的质量。根据错误指标,我们将计算已测试间隔的置信区间,如果实际值超出配置的范围,我们会将其标记为异常。如需配置实际值在边界值外的数量,请检查maxPositiveRelativeChange
、maxNegativeRelativeChange
和forecastExtraWeight
参数。
查询结果如下所示:
{
"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
显示异常值、实际值和预测值的范围。trainingErrors
和 holdoutErrors
显示用于异常检测的其他指标。
流式更新
如果创建请求指定 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 网站中找到一些其他示例。