前陣子為了把社群通報的震度資料和 USGS 的 event 資料對起來,我在一台 4 核 8G 的 VPS 上跑了一支 Python 小管線。資料量不大:一天大概兩三百筆 event、社群端每分鐘幾十則訊息,跑一週也才幾萬筆。理論上這種規模不該出問題,但它就是出了。
問題現象是:同一筆地震,USGS 那邊的 origin time 和社群通報的時間常常差 5 到 20 秒,我原本直接拿兩邊時間戳做 join,結果配對率低到大概六成,而且配錯的比配對的還難抓。
我的做法是先把時間統一成 UTC 的 epoch 秒,再做一層「容忍窗」配對。程式大概長這樣:
from datetime import timedelta
def pair_events(usgs, reports, window_sec=30):
usgs = sorted(usgs, key=lambda e: e["origin_epoch"])
out = []
for r in reports:
t = r["report_epoch"]
lo = t - window_sec
hi = t + window_sec
cand = [e for e in usgs if lo <= e["origin_epoch"] <= hi]
if len(cand) == 1:
out.append((r, cand[0]))
elif len(cand) > 1:
# 多筆候選就取時間最近的那筆,但要記錄下來
best = min(cand, key=lambda e: abs(e["origin_epoch"] - t))
out.append((r, best, "multi"))
return out
為什麼是 30 秒?因為我實際看了一週的資料,社群端從感覺到發訊息的延遲中位數大概十幾秒,而 USGS 的 origin time 是發震時間,不是發佈時間。兩邊根本不是同一個物理量,硬對就是會錯。用容忍窗的代價是:當兩筆地震在 30 秒內先後發生,配對就會曖昧,所以我留了 multi 標記,人工抽查。
會失效的地方很具體:第一,規模小的地震 USGS 常常晚很久才出,甚至不出,這時候容忍窗再大也配不到;第二,跨日界線時如果我一開始用本地時區做字串比對,就會整批偏移,我一開始就是這樣踩的;第三,社群端如果有重複轉發,同一則訊息會被配到不同 event,我去重是用 (report_id, event_id) 當 key,但 report_id 本身不保證唯一,這點我到現在還不確定該怎麼解。
另外一個實際的坑是時區。我一開始想說「反正都轉 UTC 就好」,結果某一批資料的來源欄位其實是本地時間卻沒標時區,整批早了 8 小時,我對了半天才發現是來源端的問題,不是我的程式。
現在我的做法是:配對結果一律保留原始時間戳和配對理由,出問題時可以回頭查,而不是只留一個配好的 pair。
你們在對不同來源的地震時間時,容忍窗是固定值還是會隨規模或距離調整?我目前是固定 30 秒,但總覺得偏鄉或海域事件應該放寬,卻沒有好方法決定放多寬。
0 条评论
还没有评论,写下第一条。