Dictionary of Applied Machine Learning · transformer
Numerical companion to the entry transformer: it recomputes what the entry states and prints one line per check
Downloads the INCA 1-hour precipitation analysis (GeoSphere Austria) for a fixed timestamp, resamples it onto a 96 x 96 pixel image of 1 km resolution centered on Krems an der Donau, and cuts the image into 16 x 16-pixel patches. Each patch is one token of the image data point; its pixel values form the feature vector of the token, and arranging these feature vectors as rows yields the input matrix of a transformer. The course of the Danube (extracted once from OpenStreetMap, embedded below) and the location of Krems are drawn for orientation.
Run it without installing anything:uv run https://dictionaryofml.org/terms/transformer.py
uv downloads this script and the pinned NumPy and Matplotlib it needs, then runs it; the script fetches any input file it uses. To keep the output files, download transformer.py into a folder and run uv run transformer.py there. With NumPy and Matplotlib already installed, python3 transformer.py, from any directory — it writes its output files into the current directory. Fixed seeds, so the printed numbers reproduce exactly. Download transformer.py · Notebook · Open in Colab
One cell per block of the script: the code, and what that code printed when it last ran here
"""Radar-based precipitation image around Krems, cut into patches (tokens).
Downloads the INCA 1-hour precipitation analysis (GeoSphere Austria) for a
fixed timestamp, resamples it onto a 96 x 96 pixel image of 1 km resolution
centered on Krems an der Donau, and cuts the image into 16 x 16-pixel
patches. Each patch is one token of the image data point; its pixel values
form the feature vector of the token, and arranging these feature vectors
as rows yields the input matrix of a transformer. The course of the Danube
(extracted once from OpenStreetMap, embedded below) and the location of
Krems are drawn for orientation.
The timestamp is pinned, so re-running the script downloads the same
historical reading and reproduces the committed CSVs byte for byte
(network access required).
Blocks:
[B-fetch] download the INCA precipitation grid around Krems
[B-grid] resample onto the 96 x 96 pixel image; write transformer_radar.csv
[B-river] write the Danube course in pixel coordinates; transformer_danube.csv
[B-preview] plot the image cut into patches; save transformer.png
"""
import json
import urllib.request
from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt
HERE = Path(__file__).parent
# pinned reading: a convective-rain hour over Krems an der Donau, Austria
TIMESTAMP = "2026-08-20T17:00"
KREMS_LAT, KREMS_LON = 48.4167, 15.6167
NPIX = 96 # image is NPIX x NPIX pixels of 1 km
PATCH = 16 # patches are PATCH x PATCH pixels
# course of the Danube near Krems as (km east, km north) offsets;
# extracted from OpenStreetMap (waterway=river, name=Donau) on 2026-09-03
DANUBE_KM = [
(-49.9, -20.7), (-49.3, -21.7), (-48.8, -22.9), (-48.0, -23.8), (-46.8, -24.3),
(-45.6, -24.5), (-44.3, -24.7), (-42.8, -24.4), (-41.6, -24.3), (-40.5, -24.7),
(-39.5, -25.8), (-38.6, -26.9), (-37.3, -27.1), (-36.8, -25.9), (-37.4, -24.7),
(-36.8, -23.4), (-35.7, -22.8), (-34.4, -22.6), (-33.0, -22.4), (-31.7, -22.2),
(-30.3, -22.3), (-28.9, -22.3), (-27.7, -21.9), (-26.5, -21.5), (-25.1, -21.3),
(-23.8, -21.2), (-22.3, -20.9), (-21.0, -19.8), (-19.9, -19.2), (-18.7, -18.2),
(-17.6, -16.4), (-16.5, -15.3), (-15.7, -14.3), (-15.4, -12.9), (-15.3, -11.7),
(-15.2, -10.5), (-15.5, -9.1), (-15.4, -7.7), (-15.0, -6.4), (-13.8, -5.5),
(-12.6, -4.9), (-11.7, -4.0), (-11.0, -3.0), (-10.2, -2.0), (-9.3, -1.2),
(-8.0, -1.1), (-7.5, -2.1), (-6.9, -3.3), (-5.5, -3.5), (-4.2, -2.8),
(-3.1, -2.1), (-1.7, -1.7), (-0.1, -1.5), (1.3, -1.6), (3.1, -3.3),
(4.2, -3.9), (5.9, -3.2), (7.4, -3.3), (8.4, -4.1), (9.4, -4.9),
(10.9, -4.8), (12.5, -4.1), (14.5, -4.1), (16.0, -3.9), (17.3, -4.1),
(18.5, -4.8), (19.6, -6.1), (21.7, -7.4), (22.9, -7.8), (24.0, -8.5),
(26.1, -8.5), (28.0, -8.2), (29.5, -8.8), (32.7, -9.0), (35.9, -9.2),
(39.5, -8.7), (42.8, -7.8), (44.4, -7.3), (45.7, -7.2), (46.9, -7.1),
]
KM_PER_DEG_LAT = 110.57
KM_PER_DEG_LON = 111.32 * np.cos(np.deg2rad(KREMS_LAT))
half_lat = (NPIX / 2 + 2) / KM_PER_DEG_LAT
half_lon = (NPIX / 2 + 2) / KM_PER_DEG_LON
url = (
"https://dataset.api.hub.geosphere.at/v1/grid/historical/inca-v1-1h-1km"
f"?parameters=RR&start={TIMESTAMP}&end={TIMESTAMP}"
f"&bbox={KREMS_LAT - half_lat:.4f},{KREMS_LON - half_lon:.4f},"
f"{KREMS_LAT + half_lat:.4f},{KREMS_LON + half_lon:.4f}"
"&output_format=geojson"
)
with urllib.request.urlopen(url, timeout=120) as resp:
payload = json.load(resp)
points = np.array(
[
f["geometry"]["coordinates"]
+ [f["properties"]["parameters"]["RR"]["data"][0]]
for f in payload["features"]
]
)
print(f"[B-fetch] {len(points)} grid cells around Krems at {TIMESTAMP} UTC")
[B-fetch] 10712 grid cells around Krems at 2026-08-20T17:00 UTC
# pixel (px, py) is centered (px - 47.5, py - 47.5) km east/north of Krems
east_km = (points[:, 0] - KREMS_LON) * KM_PER_DEG_LON
north_km = (points[:, 1] - KREMS_LAT) * KM_PER_DEG_LAT
image = np.full((NPIX, NPIX), np.nan)
counts = np.zeros((NPIX, NPIX))
px = np.rint(east_km + (NPIX - 1) / 2).astype(int)
py = np.rint(north_km + (NPIX - 1) / 2).astype(int)
inside = (px >= 0) & (px < NPIX) & (py >= 0) & (py < NPIX)
for x, y, val in zip(px[inside], py[inside], points[inside, 2]):
image[y, x] = (0.0 if counts[y, x] == 0 else image[y, x]) + val
counts[y, x] += 1
image[counts > 0] /= counts[counts > 0]
# fill pixels that received no grid cell from the closest filled pixel
missing = np.argwhere(counts == 0)
filled = np.argwhere(counts > 0)
for y, x in missing:
nearest = filled[np.argmin(((filled - [y, x]) ** 2).sum(axis=1))]
image[y, x] = image[nearest[0], nearest[1]]
rows = [
f"{x},{y},{image[y, x]:.3f}" for y in range(NPIX) for x in range(NPIX)
]
csv_path = HERE / "transformer_radar.csv"
csv_path.write_text("px,py,rr\n" + "\n".join(rows) + "\n")
print(
f"[B-grid] wrote {csv_path.name}: {NPIX}x{NPIX} pixels, "
f"{len(missing)} empty pixels filled from the closest filled pixel, "
f"max {np.nanmax(image):.2f} mm"
)
[B-grid] wrote transformer_radar.csv: 96x96 pixels, 20 empty pixels filled from the closest filled pixel, max 20.19 mm
offset = (NPIX - 1) / 2
river_rows = [
f"{e + offset:.1f},{n + offset:.1f}"
for e, n in DANUBE_KM
if abs(e) <= NPIX / 2 + 0.5 and abs(n) <= NPIX / 2 + 0.5
]
river_path = HERE / "transformer_danube.csv"
river_path.write_text("px,py\n" + "\n".join(river_rows) + "\n")
print(f"[B-river] wrote {river_path.name}: {len(river_rows)} points")
[B-river] wrote transformer_danube.csv: 77 points
fig, ax = plt.subplots(figsize=(6.4, 4.8))
half = NPIX / 2
im = ax.imshow(
image,
origin="lower",
cmap="gray_r",
vmin=0.0,
extent=(-half, half, -half, half),
)
for line_km in range(-int(half) + PATCH, int(half), PATCH):
ax.axhline(line_km, color="0.55", linewidth=0.5)
ax.axvline(line_km, color="0.55", linewidth=0.5)
river = np.array(DANUBE_KM)
ax.plot(river[:, 0], river[:, 1], color="white", linewidth=2.4)
ax.plot(river[:, 0], river[:, 1], color="black", linewidth=1.0)
ax.plot(0, 0, marker="o", color="black", markersize=5,
markeredgecolor="white")
ax.annotate("Krems", (0, 0), xytext=(2, 3), fontsize=9,
bbox=dict(facecolor="white", edgecolor="none", pad=0.5))
ax.annotate("Danube", (-32, -22.3), xytext=(-30, -19), fontsize=9,
bbox=dict(facecolor="white", edgecolor="none", pad=0.5))
ax.set_xlim(-half, half)
ax.set_ylim(-half, half)
ax.set_xlabel("km east of Krems")
ax.set_ylabel("km north of Krems")
ax.set_title(
f"1-hour precipitation around Krems, {TIMESTAMP} UTC\n"
"(INCA, GeoSphere Austria); each 16 x 16-pixel patch is one token"
)
fig.colorbar(im, ax=ax, label="precipitation (mm)")
fig.tight_layout()
png_path = HERE / "transformer.png"
fig.savefig(png_path, dpi=150)
print(f"[B-preview] saved {png_path.name}")
[B-preview] saved transformer.png

B-preview writes when the script runs