@@ -19,52 +19,97 @@ This is an experiment to provide a SQL interface for array datasets.
1919import xarray as xr
2020import xarray_sql as xql
2121
22- ds = xr.tutorial.open_dataset(' air_temperature' )
2322
24- # The same as a dask-sql Context; i.e. an Apache DataFusion Context.
23+ # Open a year of ARCO-ERA5 — all 273 variables. Selecting a year up front
24+ # keeps Dask's partition setup cheap before any chunks are read from GCS.
25+ ds = (
26+ xr.open_zarr(' gs://gcp-public-data-arco-era5/ar/full_37-1h-0p25deg-chunk-1.zarr-v3' ,
27+ chunks = dict (time = 1 ),
28+ storage_options = {' token' : ' anon' }) # Anonymous read from the public GCS bucket — no auth required.
29+ .sel(time = ' 2020' )
30+ )
31+
2532ctx = xql.XarrayContext()
26- ctx.from_dataset(' air' , ds, chunks = dict (time = 24 )) # the dataset needs to be chunked!
27- # data is only materialized when we make a query.
28-
29- result = ctx.sql('''
30- SELECT
31- "lat", "lon", AVG("air") as air_avg
32- FROM
33- "air"
34- GROUP BY
35- "lat", "lon"
36- ''' )
37- # DataFrame()
38- # +------+-------+--------------------+
39- # | lat | lon | air_avg |
40- # +------+-------+--------------------+
41- # | 75.0 | 205.0 | 259.88662671232834 |
42- # | 75.0 | 207.5 | 259.48268150684896 |
43- # | 75.0 | 230.0 | 258.9192123287667 |
44- # | 75.0 | 275.0 | 257.07574315068456 |
45- # | 75.0 | 322.5 | 250.11792123287654 |
46- # | 75.0 | 325.0 | 250.81590068493134 |
47- # | 72.5 | 205.0 | 262.74933904109537 |
48- # | 72.5 | 207.5 | 262.5384315068488 |
49- # | 72.5 | 230.0 | 260.82879452054743 |
50- # | 72.5 | 275.0 | 257.3063321917804 |
51- # +------+-------+--------------------+
52- # Data truncated.
53-
54- # The full query is only made when we call `collect()`, or, in this case,
55- # `to_pandas()`.
56- df = result.to_pandas()
57- df.head()
58- # lat lon air_avg
59- # 0 75.0 232.5 258.836188
60- # 1 75.0 247.5 257.716171
61- # 2 75.0 262.5 257.347959
62- # 3 75.0 277.5 257.671308
63- # 4 72.5 232.5 260.654401
33+ ctx.from_dataset(' era5' , ds, table_names = {
34+ (' time' , ' latitude' , ' longitude' ): ' surface' ,
35+ (' time' , ' level' , ' latitude' , ' longitude' ): ' atmosphere' ,
36+ })
37+ # Registration: ~0.5s for a full year of hourly ERA5, all variables.
38+
39+
40+ # Heads up: ARCO-ERA5 has 262 surface + 11 atmospheric variables. The library
41+ # pushes column projection down to Zarr, so SELECT only fetches what you ask
42+ # for — but `SELECT * FROM era5.surface` would try to pull every variable
43+ # across the year (terabytes from GCS).
44+ # ---> Always SELECT specific columns. <---
45+
46+ # Average 2m-temperature over NYC on the morning of 2020-01-01. The library
47+ # pushes WHERE clauses on dimension columns down to partition pruning.
48+ ctx.sql('''
49+ SELECT AVG("2m_temperature") - 273.15 AS avg_c
50+ FROM era5.surface
51+ WHERE time BETWEEN TIMESTAMP '2020-01-01'
52+ AND TIMESTAMP '2020-01-01 05:00:00'
53+ AND latitude BETWEEN 39 AND 40
54+ AND longitude BETWEEN 286 AND 287 -- ERA5 uses 0-360 longitudes
55+ ''' ).to_pandas()
56+ # avg_c
57+ # 0 8.640069
58+
59+ # Average temperature per pressure level, globally.
60+ ctx.sql('''
61+ SELECT level, AVG(temperature) - 273.15 AS avg_c
62+ FROM era5.atmosphere
63+ WHERE time BETWEEN TIMESTAMP '2020-01-01'
64+ AND TIMESTAMP '2020-01-01 05:00:00'
65+ GROUP BY level
66+ ORDER BY level DESC
67+ ''' ).to_pandas()
68+ # level avg_c
69+ # 0 1000 6.621012 ← surface
70+ # 1 975 5.185638
71+ # 2 950 4.028429
72+ # 3 925 3.082812
73+ # 4 900 2.210917
74+ # 5 875 1.395018
75+ # 6 850 0.634267
76+ # 7 825 -0.210372
77+ # 8 800 -1.181075
78+ # 9 775 -2.306465
79+ # 10 750 -3.535534
80+ # 11 700 -6.241685
81+ # 12 650 -9.236364
82+ # 13 600 -12.580938
83+ # 14 550 -16.335386
84+ # 15 500 -20.643604
85+ # 16 450 -25.573401
86+ # 17 400 -31.156920
87+ # 18 350 -37.400552
88+ # 19 300 -43.852607
89+ # 20 250 -49.322132
90+ # 21 225 -51.569113
91+ # 22 200 -53.693248
92+ # 23 175 -55.890484
93+ # 24 150 -58.382290
94+ # 25 125 -61.091916
95+ # 26 100 -63.624885 ← tropopause
96+ # 27 70 -63.182300
97+ # 28 50 -60.124845
98+ # 29 30 -55.986327
99+ # 30 20 -52.433089
100+ # 31 10 -44.140750
101+ # 32 7 -38.707350
102+ # 33 5 -32.621999
103+ # 34 3 -21.509175
104+ # 35 2 -13.355764
105+ # 36 1 -9.020513 ← top of atmosphere
64106```
65107
66- Succinctly, we "pivot" Xarray Datasets (with consistent dimensions) to treat them like tables so we can run
67- SQL queries against them.
108+ _ (A runnable version of this example lives at
109+ [ ` perf_tests/era5_temp_profile.py ` ] ( perf_tests/era5_temp_profile.py ) .)_
110+
111+ Succinctly, we "pivot" Xarray Datasets to treat them like tables so we can run
112+ SQL queries against them.
68113
69114## Round-tripping back to Xarray
70115
@@ -190,14 +235,14 @@ _2025 update_: Something like this is being built across a few projects! The one
190235_ 2026 update_ : A colleague and I are experimenting with native Zarr RDBMS engines. Check out:
191236
192237- [ Zarr-Datafusion] ( https://lib.rs/crates/zarr-datafusion )
193- - [ DuckDB-Zarr] ( https://github.com/hobbes-bot /duckdb-zarr )
238+ - [ DuckDB-Zarr] ( https://github.com/alxmrs /duckdb-zarr )
194239
195240## Roadmap
196241
197242- [x] ~ Lazy evaluation via the pyarrow Dataset interface [ #93 ] ( https://github.com/alxmrs/xarray-sql/issues/93 ) .~ _ Implemented in [ #100 ] ( https://github.com/alxmrs/xarray-sql/pull/100 ) _
198243- [x] Support proper parallelism via proper partition handling on the rust/datafusion side. [ #106 ] ( https://github.com/alxmrs/xarray-sql/issues/106 )
199244- [x] Support core datafusion optimizations to scan less data, like [ 104] ( https://github.com/alxmrs/xarray-sql/issues/104 ) , ...
200- - [ ] Translate a single Zarr to a collection of tables [ #85 ] ( https://github.com/alxmrs/xarray-sql/issues/85 ) .
245+ - [x ] Translate a single Zarr to a collection of tables [ #85 ] ( https://github.com/alxmrs/xarray-sql/issues/85 ) .
201246- [ ] Distributed beyond a single node through the DataFusion integration with Ray Datasets [ #68 ] ( https://github.com/alxmrs/xarray-sql/issues/68 ) or Apache Ballista [ #98 ] ( https://github.com/alxmrs/xarray-sql/issues/98 ) .
202247- [ ] Demo: calculate Sea Surface Temperature from 1940 - Present in SQL [ #36 ] ( https://github.com/alxmrs/xarray-sql/issues/36 ) .
203248- [ ] Provide an option to integrate DataFusion directly to Zarr via Rust [ #4 ] ( https://github.com/alxmrs/xarray-sql/issues/4 ) .
0 commit comments