NRT data#
import time
import matplotlib.pyplot as plt
import pandas as pd
import uscrn
Recent hourly data#
With uscrn.get_nrt_data(), we can load recent data (near-real-time) from USCRN
by specifying the period we want and from which dataset.
Here, we request the 6 most recent files.
now = pd.Timestamp.now("UTC")
print(now)
df = uscrn.get_nrt_data(
(-6, None),
"hourly",
n_jobs=2,
)
df
| wban | utc_time | lst_time | crx_vn | longitude | latitude | t_calc | t_hr_avg | t_max | t_min | ... | soil_moisture_5 | soil_moisture_10 | soil_moisture_20 | soil_moisture_50 | soil_moisture_100 | soil_temp_5 | soil_temp_10 | soil_temp_20 | soil_temp_50 | soil_temp_100 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 03047 | 2026-07-10 06:00:00 | 2026-07-10 00:00:00 | 2.622 | -102.809998 | 31.620001 | 27.000000 | 27.400000 | 27.900000 | 27.000000 | ... | 0.041 | 0.033 | 0.036 | 0.037 | 0.028 | 32.0 | 34.000000 | 33.700001 | 34.400002 | 31.500000 |
| 1 | 03048 | 2026-07-10 06:00:00 | 2026-07-09 23:00:00 | 2.622 | -106.889999 | 34.360001 | 27.600000 | 28.200001 | 28.600000 | 27.600000 | ... | 0.052 | 0.091 | 0.081 | 0.061 | 0.058 | 33.0 | 35.000000 | 34.500000 | 31.299999 | 29.299999 |
| 2 | 03054 | 2026-07-10 06:00:00 | 2026-07-10 00:00:00 | 2.622 | -102.769997 | 33.959999 | 24.799999 | 25.299999 | 26.299999 | 24.799999 | ... | 0.102 | 0.112 | 0.123 | 0.114 | 0.139 | 31.1 | 32.599998 | 31.400000 | 28.400000 | 24.799999 |
| 3 | 03055 | 2026-07-10 06:00:00 | 2026-07-10 00:00:00 | 2.622 | -101.589996 | 36.599998 | 22.000000 | 21.799999 | 22.200001 | 21.400000 | ... | 0.103 | 0.185 | 0.179 | 0.243 | 0.163 | 28.0 | 28.200001 | 27.100000 | 24.900000 | 24.299999 |
| 4 | 03060 | 2026-07-10 06:00:00 | 2026-07-09 23:00:00 | 2.622 | -107.690002 | 38.540001 | 16.900000 | 17.600000 | 18.900000 | 16.900000 | ... | 0.042 | 0.116 | 0.175 | 0.221 | 0.184 | 25.0 | 25.799999 | 24.600000 | 20.500000 | 17.000000 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 907 | 96404 | 2026-07-10 11:00:00 | 2026-07-10 02:00:00 | 2.514 | -141.210007 | 62.740002 | 10.700000 | 10.700000 | 10.700000 | 10.700000 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 908 | 96405 | 2026-07-10 11:00:00 | 2026-07-10 02:00:00 | 2.514 | -145.350006 | 60.470001 | 4.700000 | 5.000000 | 6.000000 | 4.600000 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 909 | 96407 | 2026-07-10 11:00:00 | 2026-07-10 02:00:00 | 2.515 | -159.000000 | 66.559998 | 12.300000 | 12.600000 | 13.100000 | 12.300000 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 910 | 96408 | 2026-07-10 11:00:00 | 2026-07-10 02:00:00 | 2.515 | -150.869995 | 63.450001 | 10.500000 | 10.600000 | 10.700000 | 10.500000 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 911 | 96409 | 2026-07-10 11:00:00 | 2026-07-10 02:00:00 | 2.514 | -149.399994 | 68.650002 | 9.300000 | 9.500000 | 11.800000 | 8.300000 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
912 rows × 36 columns
Sometimes, a few sites can have times an hour earlier than the others.
See the notes in uscrn.get_nrt_data() for more details.
def func(x):
nx = len(x)
if nx == 0:
return ""
elif nx < 10:
return sorted(x.unique())
else:
return f"..."
(
df.utc_time
.value_counts()
.sort_index()
.to_frame()
.assign(
wbans=df.groupby("utc_time")["wban"].apply(func),
)
)
| count | wbans | |
|---|---|---|
| utc_time | ||
| 2026-07-10 06:00:00 | 152 | ... |
| 2026-07-10 07:00:00 | 152 | ... |
| 2026-07-10 08:00:00 | 152 | ... |
| 2026-07-10 09:00:00 | 152 | ... |
| 2026-07-10 10:00:00 | 152 | ... |
| 2026-07-10 11:00:00 | 152 | ... |
In these files, for example, site WBAN 13301 has data for multiple earlier days included.
https://www.ncei.noaa.gov/pub/data/uscrn/products/hourly02/updates/2024/CRN60H0203-202402212000.txt
https://www.ncei.noaa.gov/pub/data/uscrn/products/hourly02/updates/2024/CRN60H0203-202402222000.txt
Plot temperature change time series#
fig, ax = plt.subplots(figsize=(7, 4))
df_ = df.copy()
tmax = df_.utc_time.max()
df_ = df_[df_.utc_time.between(tmax - pd.Timedelta("7h"), tmax)]
df_["utc_time_mid"] = df_["utc_time"] + pd.Timedelta("30min")
df_["t_hr_avg_k"] = df_["t_hr_avg"].add(273.15)
df_["dt_hr_avg"] = df_["t_hr_avg_k"].sub(df_.groupby("wban")["t_hr_avg_k"].transform("mean"))
df_[["utc_time", "dt_hr_avg"]].groupby("utc_time").mean().plot(
color="0.3",
linewidth=3,
zorder=10,
legend=False,
ax=ax,
)
df_.groupby("wban").plot(
x="utc_time",
y="dt_hr_avg",
color="0.5",
linewidth=1,
alpha=0.4,
legend=False,
xlabel="Time (UTC)",
ylabel="NRT temperature anomaly $\Delta T$ (°C)",
ax=ax,
)
ax.set_title(df.attrs["title"], loc="left", size=8);
Plot current temperature#
fig, ax = plt.subplots(figsize=(7, 4.5))
ds = uscrn.to_xarray(df)
ds.isel(time=-1).plot.scatter(x="longitude", y="latitude", hue="t_hr_avg", ax=ax);
Specific period of hourly data#
Date selection works by file, not by the data inside the file.
In general, the data are an hour behind the file date/time.
See the notes in uscrn.get_nrt_data() for more details.
def get_nrt_hourly_period(period):
a, b = period
ap1 = pd.to_datetime(a) + pd.Timedelta(hours=1)
bp1 = pd.to_datetime(b) + pd.Timedelta(hours=1)
df = uscrn.get_nrt_data((ap1, bp1))
time.sleep(0.5) # for prints
in_period = df.utc_time.between(a, b)
print(
f"Got {in_period.sum()}/{len(df)} ({in_period.sum() / len(df):.1%}) records "
f"in desired period {a} to {b}"
)
outside = df.loc[~in_period, "utc_time"].value_counts()
print(
"Outside counts:",
", ".join(f"{time:%Y-%m-%d %H} ({count})" for time, count in outside.items())
)
dupe = df.duplicated(["wban", "utc_time"], keep=False)
print(f"Got {dupe.sum()} ({dupe.sum() / len(df):.1%}) duplicates")
return (
df[in_period]
.drop_duplicates(["wban", "utc_time"], keep="last")
.reset_index(drop=True)
)
df = get_nrt_hourly_period(("2024-02-09 16", "2024-02-09 20"))
df
| wban | utc_time | lst_time | crx_vn | longitude | latitude | t_calc | t_hr_avg | t_max | t_min | ... | soil_moisture_5 | soil_moisture_10 | soil_moisture_20 | soil_moisture_50 | soil_moisture_100 | soil_temp_5 | soil_temp_10 | soil_temp_20 | soil_temp_50 | soil_temp_100 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 03047 | 2024-02-09 16:00:00 | 2024-02-09 10:00:00 | 2.622 | -102.809998 | 31.620001 | 11.300000 | 10.0 | 11.300000 | 8.300000 | ... | 0.048 | 0.038 | 0.056 | 0.045 | 0.035 | 9.3 | 9.1 | 9.5 | 12.1 | 13.3 |
| 1 | 03048 | 2024-02-09 16:00:00 | 2024-02-09 09:00:00 | 2.622 | -106.889999 | 34.360001 | 3.000000 | 2.3 | 3.100000 | 0.900000 | ... | 0.101 | 0.140 | 0.109 | 0.056 | 0.057 | 3.1 | 4.4 | 6.2 | 8.2 | 9.2 |
| 2 | 03054 | 2024-02-09 16:00:00 | 2024-02-09 10:00:00 | 2.622 | -102.769997 | 33.959999 | 6.700000 | 6.5 | 6.700000 | 5.900000 | ... | 0.132 | 0.160 | 0.164 | 0.105 | 0.125 | 5.0 | 5.8 | 6.9 | 9.0 | 10.3 |
| 3 | 03055 | 2024-02-09 16:00:00 | 2024-02-09 10:00:00 | 2.622 | -101.589996 | 36.599998 | 8.700000 | 6.7 | 8.700000 | 3.900000 | ... | 0.250 | 0.306 | 0.324 | 0.412 | 0.217 | 4.5 | 5.0 | 5.6 | 6.4 | 6.6 |
| 4 | 03060 | 2024-02-09 16:00:00 | 2024-02-09 09:00:00 | 2.622 | -107.690002 | 38.540001 | -7.400000 | -8.4 | -7.400000 | -10.400000 | ... | NaN | NaN | NaN | 0.173 | 0.152 | -0.3 | -0.3 | -0.1 | 0.7 | 2.0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 761 | 94996 | 2024-02-09 20:00:00 | 2024-02-09 14:00:00 | 2.622 | -96.849998 | 40.700001 | 8.200000 | 7.8 | 8.400000 | 7.000000 | ... | 0.290 | 0.330 | 0.287 | 0.260 | 0.322 | 5.1 | 4.4 | 4.8 | 5.2 | 5.5 |
| 762 | 96404 | 2024-02-09 20:00:00 | 2024-02-09 11:00:00 | 2.514 | -141.210007 | 62.740002 | -21.700001 | -22.9 | -21.700001 | -24.200001 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 763 | 96405 | 2024-02-09 20:00:00 | 2024-02-09 11:00:00 | 2.514 | -145.350006 | 60.470001 | 3.000000 | 2.9 | 3.200000 | 2.600000 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 764 | 96407 | 2024-02-09 20:00:00 | 2024-02-09 11:00:00 | 2.515 | -159.000000 | 66.559998 | -9.700000 | -9.3 | -8.700000 | -11.100000 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 765 | 96409 | 2024-02-09 20:00:00 | 2024-02-09 11:00:00 | 2.514 | -149.399994 | 68.650002 | -10.800000 | -9.6 | -8.000000 | -11.100000 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
766 rows × 36 columns
Recent daily data#
Here, we load the most recent daily data file.
df = uscrn.get_nrt_data(-1, "daily")
df
| wban | lst_date | crx_vn | longitude | latitude | t_daily_max | t_daily_min | t_daily_mean | t_daily_avg | p_daily_calc | ... | soil_moisture_5_daily | soil_moisture_10_daily | soil_moisture_20_daily | soil_moisture_50_daily | soil_moisture_100_daily | soil_temp_5_daily | soil_temp_10_daily | soil_temp_20_daily | soil_temp_50_daily | soil_temp_100_daily | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 03047 | 2026-07-09 | 2.622 | -102.809998 | 31.620001 | 36.799999 | 23.500000 | 30.200001 | 29.900000 | 0.0 | ... | 0.051 | 0.043 | 0.038 | 0.037 | 0.028 | 35.000000 | 33.700001 | 33.299999 | 32.900002 | 31.299999 |
| 1 | 03048 | 2026-07-09 | 2.622 | -106.889999 | 34.360001 | 37.299999 | 20.799999 | 29.000000 | 29.500000 | 0.0 | ... | 0.055 | 0.094 | 0.082 | 0.061 | 0.206 | 36.200001 | 34.599998 | 33.000000 | 30.900000 | 29.400000 |
| 2 | 03054 | 2026-07-09 | 2.622 | -102.769997 | 33.959999 | 36.400002 | 18.700001 | 27.600000 | 28.700001 | 0.0 | ... | 0.106 | 0.115 | 0.122 | 0.113 | 0.138 | 32.000000 | 31.200001 | 29.900000 | 28.200001 | 24.799999 |
| 3 | 03055 | 2026-07-09 | 2.622 | -101.589996 | 36.599998 | 40.099998 | 18.600000 | 29.299999 | 29.200001 | 15.1 | ... | 0.106 | 0.184 | 0.179 | 0.244 | 0.163 | 28.600000 | 28.000000 | 26.500000 | 24.799999 | 24.200001 |
| 4 | 03060 | 2026-07-09 | 2.622 | -107.690002 | 38.540001 | 28.900000 | 14.700000 | 21.799999 | 21.200001 | 0.0 | ... | 0.045 | 0.116 | 0.173 | 0.221 | 0.184 | 27.100000 | 25.000000 | 23.200001 | 20.299999 | 17.000000 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 147 | 96404 | 2026-07-09 | 2.514 | -141.210007 | 62.740002 | 13.000000 | 7.100000 | 10.000000 | 10.200000 | 7.0 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 148 | 96405 | 2026-07-09 | 2.514 | -145.350006 | 60.470001 | 19.299999 | 5.400000 | 12.400000 | 12.400000 | 0.0 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 149 | 96407 | 2026-07-09 | 2.515 | -159.000000 | 66.559998 | 21.000000 | 14.200000 | 17.600000 | 17.600000 | 0.0 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 150 | 96408 | 2026-07-09 | 2.515 | -150.869995 | 63.450001 | 11.200000 | 8.600000 | 9.900000 | 9.800000 | 3.7 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 151 | 96409 | 2026-07-09 | 2.514 | -149.399994 | 68.650002 | 20.700001 | 7.400000 | 14.000000 | 15.700000 | 0.0 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
152 rows × 28 columns
fig, ax = plt.subplots(figsize=(7, 4.5))
ds = uscrn.to_xarray(df).squeeze()
ds.plot.scatter(x="longitude", y="latitude", hue="t_daily_max", ax=ax);