我尝试在给定日期之前获取7天的天气数据并接近某些坐标(lat,lon)。像半径20公里的东西。如果有多个电台,我可能想要按天分组数据。
有没有办法直接使用BigQuery计算所有这些?为了测试,我计算了最小和最大坐标并创建了以下查询
SELECT
*
FROM
[bigquery-public-data:noaa_gsod.gsod2016] a
JOIN
[bigquery-public-data:noaa_gsod.stations] b
ON
a.stn=b.usaf
AND a.wban=b.wban
WHERE
(b.lat >= 46.248332
AND b.lat <= 47.147654)
AND (b.lon >= 5.689853
AND b.lon <= 7.001115)
AND a.mo='03'
我对查询还不是很满意
这就是我计算最小最大坐标的方法:
maxLat = lat + math.degrees(searchRadius / earthRadius)
minLat = lat - math.degrees(searchRadius / earthRadius)
maxLon = lon + math.degrees(searchRadius / earthRadius) / math.cos(math.radians(lat))
minLon = lon - math.degrees(searchRadius / earthRadius) / math.cos(math.radians(lat))
答案 0 :(得分:2)
这是我能提出的最佳解决方案:
#standardSQL
CREATE TEMP FUNCTION distance(lat1 FLOAT64, lat2 FLOAT64, lon1 FLOAT64, lon2 FLOAT64) AS((
WITH data AS(
SELECT POW(SIN((ACOS(-1) / 180 * (lat1 -lat2)) / 2), 2) + COS(ACOS(-1) / 180 * (lat1)) * COS(ACOS(-1) / 180 * (lat2)) * POW(SIN((ACOS(-1) / 180 * (lon1 -lon2)) / 2), 2) a
)
SELECT 6371 * 2 * ATAN2(SQRT((SELECT a FROM data)), SQRT(1 - (SELECT a FROM data)))
));
WITH temperature_data AS(
SELECT
CONCAT(year, mo, da) date,
temp,
b.lat lat,
b.lon lon
FROM `bigquery-public-data.noaa_gsod.gsod2016` a
JOIN `bigquery-public-data.noaa_gsod.stations` b
ON a.stn = b.usaf AND a.wban = b.wban
WHERE concat(year, mo, da) BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(PARSE_DATE('%Y%m%d', '20160725'), INTERVAL 7 DAY)) AND '20160725'
)
SELECT
date,
STRUCT(AVG(IF(distance(t.lat, 10.1, t.lon, 10.2) < 20, temp, NULL)) AS avg_temp, STDDEV_SAMP(IF(distance(t.lat, 10.1, t.lon, 10.2) < 20, temp, NULL)) AS std_temp) data_20km,
STRUCT(AVG(IF(distance(t.lat, 10.1, t.lon, 10.2) < 50, temp, NULL)) AS avg_temp, STDDEV_SAMP(IF(distance(t.lat, 10.1, t.lon, 10.2) < 50, temp, NULL)) AS std_temp) data_50km,
STRUCT(AVG(IF(distance(t.lat, 10.1, t.lon, 10.2) < 100, temp, NULL)) AS avg_temp, STDDEV_SAMP(IF(distance(t.lat, 10.1, t.lon, 10.2) < 100, temp, NULL)) AS std_temp) data_100km,
STRUCT(AVG(IF(distance(t.lat, 10.1, t.lon, 10.2) < 200, temp, NULL)) AS avg_temp, STDDEV_SAMP(IF(distance(t.lat, 10.1, t.lon, 10.2) < 200, temp, NULL)) AS std_temp) data_200km,
STRUCT(AVG(IF(distance(t.lat, 10.1, t.lon, 10.2) < 500, temp, NULL)) AS avg_temp, STDDEV_SAMP(IF(distance(t.lat, 10.1, t.lon, 10.2) < 500, temp, NULL)) AS std_temp) data_500km
FROM temperature_data t
WHERE
distance(t.lat, 10.1, t.lon, 10.2) < 2000
GROUP BY date
ORDER BY date
我会尝试解释你的问题:
如何在特定日期过后7天?
在查询temperature_data
内,注意WHERE
子句的条件是:
WHERE concat(year, mo, da) BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(PARSE_DATE('%Y%m%d', '20160725'), INTERVAL 7 DAY)) AND '20160725'
这是从给定日期开始选择过去7天的地方。您可以通过更改值'20160725'来选择要分析的日期。
可以通过查询直接计算max和min lat / lon吗?
是。我想你的意思是,如果可以选择给定范围内的空间点(例如20公里)。 一种方法是定义一个临时函数来计算所需点和站点之间的距离,这在查询中表示为:
CREATE TEMP FUNCTION distance(lat1 FLOAT64, lat2 FLOAT64, lon1 FLOAT64, lon2 FLOAT64) AS((
WITH data AS(
SELECT POW(SIN((ACOS(-1) / 180 * (lat1 -lat2)) / 2), 2) + COS(ACOS(-1) / 180 * (lat1)) * COS(ACOS(-1) / 180 * (lat2)) * POW(SIN((ACOS(-1) / 180 * (lon1 -lon2)) / 2), 2) a
)
SELECT 6371 * 2 * ATAN2(SQRT((SELECT a FROM data)), SQRT(1 - (SELECT a FROM data)))
));
你可以玩并测试这个功能,例如:
SELECT distance(50, 60, 30, 10) # result is ~ 1680km
此处使用此功能:
WHERE
distance(t.lat, 10.1, t.lon, 10.2) < 2000
过滤距离(10.1°,10.2°)更远2000公里以上的点。在您的查询中,您可以选择不同的输入值而不是(10.1°,10.2°)。
通常它找不到任何数据,因为很可能是半径 20公里太小,找不到车站。如何将查询修改为 如果在半径20公里范围内找不到它,找到最近的车站?
一种可能的解决方案是同时查询几个不同的距离:
SELECT
date,
STRUCT(AVG(IF(distance(t.lat, 10.1, t.lon, 10.2) < 20, temp, NULL)) AS avg_temp, STDDEV_SAMP(IF(distance(t.lat, 10.1, t.lon, 10.2) < 20, temp, NULL)) AS std_temp) data_20km,
STRUCT(AVG(IF(distance(t.lat, 10.1, t.lon, 10.2) < 50, temp, NULL)) AS avg_temp, STDDEV_SAMP(IF(distance(t.lat, 10.1, t.lon, 10.2) < 50, temp, NULL)) AS std_temp) data_50km,
STRUCT(AVG(IF(distance(t.lat, 10.1, t.lon, 10.2) < 100, temp, NULL)) AS avg_temp, STDDEV_SAMP(IF(distance(t.lat, 10.1, t.lon, 10.2) < 100, temp, NULL)) AS std_temp) data_100km,
STRUCT(AVG(IF(distance(t.lat, 10.1, t.lon, 10.2) < 200, temp, NULL)) AS avg_temp, STDDEV_SAMP(IF(distance(t.lat, 10.1, t.lon, 10.2) < 200, temp, NULL)) AS std_temp) data_200km,
STRUCT(AVG(IF(distance(t.lat, 10.1, t.lon, 10.2) < 500, temp, NULL)) AS avg_temp, STDDEV_SAMP(IF(distance(t.lat, 10.1, t.lon, 10.2) < 500, temp, NULL)) AS std_temp) data_500km
FROM temperature_data t
WHERE
distance(t.lat, 10.1, t.lon, 10.2) < 2000
GROUP BY date
请注意,此查询正在提取从输入点(10.1°,10.2°)到2000km之外的站点。然后应用滤波器来选择20km,50km,100km,200km和500km范围内的点。
您可以根据需要更改这些值。如果你想从另一个点获得平均温度,比如说(40°,30°),只需将值(10.1,10.2)改为(40,30),你就可以了。此外,如果您希望距离此点不同,则可以将表达式IF(distance(t.lat, 10.1, t.lon, 10.2) < 200
更改为更符合您需求的范围。
请注意WHERE
子句的条件为:
distance(t.lat, 10.1, t.lon, 10.2) < 2000
因此,这将过滤掉距离点(10.1,10.2)更远的所有站点超过2000公里。您也可以根据需要更改此值。
关于这一点的最后说明:我还带来了STDDEV_SAMP
standard deviation of a sampling。这可能对您有一定价值,并且它可以让您了解平均值在均值周围散布的程度(通过采样数据大小效果校正)。如果我们不知道我们与正确值的接近程度,那么平均值本身就没那么有价值。
我能获得更好,免费的历史天气数据吗?
不知道。希望这个公共数据集对您来说足够好。
答案 1 :(得分:0)
根据您提供的信息,我不确定您是否可以在查询中计算最大/最小数据。在Legacy SQL中工作我可能会尝试嵌套多个查询,或者加入计算它们的查询,或者两者兼而有之。
您可能还可以在必要时编写一些可以调整搜索查询的内容,但我还没有得到您已经足够的结构来撰写建议。
对于其他问题:
获得平均值 - 而不是使用*来调用您必须单独调用哪些列进行平均以及忽略或分组的所有内容。
选择特定日期的过去7天 - 非常不幸的是,它似乎不是时间戳列,因此您必须强制使用。
在LegacySQL中我会写这样的东西:
SELECT dte, avg_temp, avg_cnt_temp
FROM
(SELECT CAST(CONCAT(a.year, '-', a.mo, '-', a.da) AS timestamp) AS dte,
/* This is calling the separate year, month, and day strings as a
datetime funtion so I can use date_add later */
AVG(a.temp) AS avg_temp, AVG(a.count_temp) AS avg_cnt_temp /* You'll
want to include all of the data you're wanting to call here, I
only tested with these two */
FROM [bigquery-public-data:noaa_gsod.gsod2016] AS a
JOIN [bigquery-public-data:noaa_gsod.stations] AS b
ON a.stn=b.usaf AND a.wban=b.wban
GROUP BY dte, mo, da)
WHERE dte >= (DATE_ADD('2016-12-31 00:00:00', -7, "DAY")) AND dte <=
TIMESTAMP('2016-12-31 00:00:00') /* replace with your date */
我认为在标准SQL中你不会以同样的方式嵌套。
如果要跨站等组合数据,请不要呼叫电台标识符