Below is a script to scrape data from the National Severe Storms Laboratory Probsevere dataset. Their data is available at a 2 minute interval and is only available for 24 hours before moving to the archives. The data is GeoJSON
which I convert to a MultiIndex
DataFrame
and save as a parquet
. The script runs once an hour to collect the previous hours data at a 10 min interval.
The script is running on a raspberry pi and writing the parquet
to an external HHD. I am collecting the data for a machine learning project. Eventually I will serve the data from the pi to my local network. I use shapely
to convert the geometry
into geometric shapes.
from datetime import datetime
from typing import Mapping
import pandas as pd
import numpy as np
import requests
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.interval import IntervalTrigger
NCEP_DATA = "https://mrms.ncep.noaa.gov/data"
scheduler = BlockingScheduler()
def name_to_datetime(names: pd.Series) -> pd.DatetimeIndex:
return pd.DatetimeIndex(names.str.replace("_", "T").str.extract(r"(\d*T\d*).json")[0]).rename("validTime")
def read_mrms(*args: str) -> pd.DataFrame:
url = "/".join([NCEP_DATA, *args]) + "/?C=M;O=D"
return pd.read_html(url)[0].dropna()
def read_probsevere() -> pd.DataFrame:
df = read_mrms("ProbSevere", "PROBSEVERE")
df.index = name_to_datetime(df.Name)
return (NCEP_DATA + "/ProbSevere/PROBSEVERE/" + df["Name"]).rename("url")
def get_last_hours_data():
s = read_probsevere()
last_hour = datetime.utcnow() - pd.to_timedelta(1, unit="h")
is_last_hour = (s.index.day == last_hour.day) & (s.index.hour == last_hour.hour)
is_10_min_interval = (s.index.minute % 10) == 0
return s[is_last_hour & is_10_min_interval]
def to_dataframe(mrms_files: Mapping[pd.Timestamp, str]) -> pd.DataFrame:
def generate():
for vt, url in mrms_files.items():
features = requests.get(url).json()["features"]
print(f"data collected for {vt}")
for feat in features:
props = feat["properties"]
props["validTime"] = vt
props["geometry"] = feat["geometry"]
yield props
ps = pd.DataFrame(generate()).set_index(["validTime", "ID"])
ps["AVG_BEAM_HGT"] = ps["AVG_BEAM_HGT"].str.replace(r"[A-Za-z]", "", regex=True).apply(pd.eval)
ps[["MAXRC_EMISS", "MAXRC_ICECF"]] = (
ps[["MAXRC_EMISS", "MAXRC_ICECF"]]
.stack()
.str.extract(r"(?:\()([a-z]*)(?:\))")
.replace({"weak": 1, "moderate": 2, "strong": 3})
.fillna(0)
.unstack(-1)
.droplevel(0, axis=1)
)
ps.loc[:, ps.columns != "geometry"] = ps.loc[:, ps.columns != "geometry"].astype(np.float32)
return ps
@scheduler.scheduled_job(IntervalTrigger(hours=1))
def on_hour():
template = "/media/external/data/{0}.parquet"
last = get_last_hours_data()
df = to_dataframe(last)
file_name = template.format(datetime.now().strftime("%Y-%m-%d.HR%H"))
df.to_parquet(file_name)
print(f"file saved as {file_name}")
if __name__ == "__main__":
on_hour()
scheduler.start()