Skip to main content

Querry Xarray with SQL.

Project description

xarray-sql

Query Xarray with SQL

ci lint ci-build ci-rust

pip install xarray-sql

What is this?

This is an experiment to provide a SQL interface for array datasets. Succinctly, we "pivot" Xarray Datasets to treat them like tables so we can run SQL queries against them.

Quickstart

Open a Dataset, register it as a table with from_dataset, compute a climatology in SQL, then write the result back to Xarray and plot it:

Note: this example also needs pooch and a netCDF backend (for the tutorial download) and matplotlib (for the plot): pip install pooch netCDF4 matplotlib.

import xarray as xr
import xarray_sql as xql

# 4x-daily surface air temperature on a lat/lon grid, 2013-2014.
ds = xr.tutorial.open_dataset('air_temperature')

ctx = xql.XarrayContext()
ctx.from_dataset('air', ds, chunks=dict(time=100))

# A climatology — the mean annual cycle — computed in SQL: average air
# temperature for each month of the year, over all grid cells and years.
clim = ctx.sql('''
  SELECT
    CAST(date_part('month', "time") AS INTEGER) AS month,
    AVG("air") AS air
  FROM "air"
  GROUP BY CAST(date_part('month', "time") AS INTEGER)
  ORDER BY month
''')

# Write the SQL result back to an Xarray Dataset. `month` is a derived
# column, so name it as the dimension; the variable's units are recovered
# from the registered table. The result is one value per month: air(month).
clim_ds = clim.to_dataset(dims=["month"])

# Plot the annual cycle as a time series.
clim_ds["air"].plot()  # in a script, call matplotlib.pyplot.show() to display

That's the round trip — Xarray in, SQL in the middle, Xarray (and a plot) back out.

A bigger example: ARCO-ERA5

The same interface scales to cloud-native datasets with hundreds of variables, like ARCO-ERA5.

Note: reading from gs:// requires gcsfs (pip install gcsfs).

import xarray as xr
import xarray_sql as xql


# Open ARCO-ERA5 — a weather dataset with 273 variables since 1940. 
# Turning off dask means we don't have to wait to construct a task graph.
ds = xr.open_zarr(
  'gs://gcp-public-data-arco-era5/ar/full_37-1h-0p25deg-chunk-1.zarr-v3',
  chunks=None,  # Turn dask off
  storage_options={'token': 'anon'}  # Anonymous read from the public GCS bucket — no auth required.
)

ctx = xql.XarrayContext()
# Make sure to pass `chunks`!
ctx.from_dataset('era5', ds, chunks=dict(time=6), table_names={
    ('time', 'latitude', 'longitude'): 'surface',
    ('time', 'level', 'latitude', 'longitude'): 'atmosphere',
})
# Registration takes ~10s on my machine.

# Heads up: ARCO-ERA5 has 262 surface + 11 atmospheric variables. The library
# pushes column projection down to Zarr, so SELECT only fetches what you ask
# for — but `SELECT * FROM era5.surface` would try to pull every variable
# across the year (terabytes from GCS). 
#  ---> Always SELECT specific columns. <---

# Average 2m-temperature over NYC on the morning of 2020-01-01. The library
# pushes WHERE clauses on dimension columns down to partition pruning.
ctx.sql('''
  SELECT AVG("2m_temperature") - 273.15 AS avg_c
  FROM era5.surface
  WHERE time BETWEEN TIMESTAMP '2020-01-01'
                 AND TIMESTAMP '2020-01-01 05:00:00'
    AND latitude  BETWEEN 39 AND 40
    AND longitude BETWEEN 286 AND 287  -- ERA5 uses 0-360 longitudes
''').to_pandas()
#       avg_c
# 0  8.640069

# Average temperature per pressure level, globally. 
result = ctx.sql('''
  SELECT level, AVG(temperature) - 273.15 AS avg_c
  FROM era5.atmosphere
  WHERE time BETWEEN TIMESTAMP '2020-01-01'
                 AND TIMESTAMP '2020-01-01 05:00:00'
  GROUP BY level
  ORDER BY level DESC
''')
# DataFrame()
# +-------+----------------------+
# | level | avg_c                |
# +-------+----------------------+
# | 1000  | 6.6210120796502565   |
# | 975   | 5.185637919348153    |
# | 950   | 4.028428657263021    |
# | 925   | 3.0828117974912743   |
# | 900   | 2.2109172992531967   |
# | 875   | 1.395017610194202    |
# | 850   | 0.6342670572626616   |
# | 825   | -0.21037158786759846 |
# | 800   | -1.1810754318269687  |
# | 775   | -2.3064649711534457  |
# +-------+----------------------+

ctx.sql('''
  SELECT latitude, longitude, AVG("2m_temperature") - 273.15 AS avg_c
  FROM era5.surface
  WHERE time BETWEEN TIMESTAMP '2020-01-01'
                 AND TIMESTAMP '2020-01-01 05:00:00'
  GROUP BY latitude, longitude
  ORDER BY latitude DESC, longitude
''').to_dataset(dims=['latitude', 'longitude'], template=ds)
# <xarray.Dataset> Size: 8MB
# Dimensions:    (latitude: 721, longitude: 1440)
# Coordinates:
#   * latitude   (latitude) float32 3kB 90.0 89.75 89.5 ... -89.5 -89.75 -90.0
#   * longitude  (longitude) float32 6kB 0.0 0.25 0.5 0.75 ... 359.2 359.5 359.8
# Data variables:
#     avg_c      (latitude, longitude) float64 8MB -26.84 -26.84 ... -27.38 -27.38
# Attributes:
#     last_updated:           2026-06-20 02:33:34.265980+00:00
#     valid_time_start:       1940-01-01
#     valid_time_stop:        2025-12-31
#     valid_time_stop_era5t:  2026-06-14

(A runnable version of this example lives at perf_tests/era5_temp_profile.py.)

Why build this?

A few reasons:

  • Even though SQL is the lingua franca of data, scientific datasets are often inaccessible to non-scientists (SQL users).
  • Joining tabular data with raster data is common yet difficult. It could be easy.
  • There are many cloud-native, Xarray-openable datasets, from Google Earth Engine to the Source Cooperative. Wouldn’t it be great if these were also SQL-accessible? How can the bridge be built with minimal effort?

This is a light-weight way to prove the value of the interface.

The larger goal is to explore the hypothesis that the Pangeo ecosystem is a scientific database. Here, xarray-sql can be thought of as a missing DB front end.

How does it work?

All chunks in a Xarray Dataset are transformed into a Dask DataFrame via from_map() and to_dataframe(). For SQL support, we just use dask-sql. That's it!

2025 update: This library now implements a Dask-like from_map interface in pure DataFusion and PyArrow, but works with the same principle!

2026 update: Instead of from_map(), we create a way to translate Xarray chunks into Arrow RecordBatches. We pass a Python callback into a DataFusion TableProvider that lets the DB engine translate the underlying Dataset arrays into DataFusion partitions. Ultimately, the initial insight of the pivot() function -- that any ndarray can be translated into a 2D table -- underlies this performant query mechanism.

Does it work?

Yes. The recurring worry is that the SQL interface is a toy — fine for SELECTs, but not for the operations geoscience actually runs. So we wrote a suite that takes the staples of geospatial and climate analysis — the ones we assume need an array library — and expresses each one in SQL, then checks the SQL answer against an xarray/array reference to floating-point tolerance:

  • Spectral indices (NDVI) — column arithmetic over a real Sentinel-2 scene.
  • Climatology, anomalies, zonal meansGROUP BY and self-JOIN against the 0.25° ARCO-ERA5 archive registered as a lazy table. Each query is bounded to a small window (a few days over a region) and reads only that slice — the point is that you can aim a query at a multi-decade archive and pay only for the data it asks for, not that the query scans the whole record.
  • Forecast skill — scoring the Pangu-Weather and GraphCast ML models against ERA5 (WeatherBench 2) as a JOIN on valid_time = init + lead; it reproduces the published result that GraphCast beats Pangu at every lead.
  • Raster × vector zonal stats — a range JOIN of the ERA5 grid against a table of regions.
  • Reprojection and regridding — a scalar PROJ UDF (validated against Earth Engine's own geodesy via Xee) and a sparse-weight-table JOIN (regridding real SRTM terrain).

Every case matches its array reference. The headline finding: these operations are not really "array" operations at all — they are GROUP BY, JOIN, window functions, and CASE in disguise, and a query engine runs them at scale. See benchmarks/geospatial/ and the write-up, Geospatial operations are relational operations.

Why does this work?

Underneath Xarray, Dask, and Pandas, there are NumPy arrays. These are paged in chunks and represented contiguously in memory. It is only a matter of metadata that breaks them up into ndarrays. pivot(), which uses to_dataframe(), just changes this metadata (via a ravel()/reshape()), back into a column amenable to a DataFrame. We take advantage of this light weight metadata change to make chunked information scannable by a DB engine (DataFusion).

What are the current limitations?

TBD, DataFusion provides a whole new world! Currently, we're looking for early users – "tire kickers", if you will. We'd love your input to shape the direction of this project! Please, give this a try and file issues as you see fit. Check out our contributing guide, too 😉.

What would a deeper integration look like?

I have a few ideas so far. One approach involves applying operations directly on Xarray Datasets. This approach is being pursued here, as xql.

Deeper still: I was thinking we could make a virtual filesystem for parquet that would internally map to Zarr. Raster-backed virtual parquet would open up integrations to numerous tools like dask, pyarrow, duckdb, and BigQuery. More thoughts on this in #4.

2025 update: Something like this is being built across a few projects! The ones I know about are:

2026 update: A colleague and I are experimenting with native Zarr RDBMS engines. Check out:

Roadmap

  • Lazy evaluation via the pyarrow Dataset interface #93. Implemented in #100
  • Support proper parallelism via proper partition handling on the rust/datafusion side. #106
  • Support core datafusion optimizations to scan less data, like 104, ...
  • Translate a single Zarr to a collection of tables #85.
  • Distributed beyond a single node through the DataFusion integration with Ray Datasets #68 or Apache Ballista #98.
  • Demo: calculate Sea Surface Temperature from 1940 - Present in SQL #36.
  • Provide an option to integrate DataFusion directly to Zarr via Rust #4.
  • (To be formally announced eventually): The 100 Trillion Row Challenge #34.

Sponsors & Contributors

I want to give a special thanks to the following folks and institutions:

  • Pramod Gupta and the Anthromet Team at Google Research for the problem formation and design inspiration.
  • Jake Wall and AI2/Ecoscope for compute resources and key use cases.
  • Charles Stern, Stephan Hoyer, Alexander Kmoch, Wei Ji, and Qiusheng Wu for the early review and discussion of this project.
  • Tom Nichols, Kyle Barron, Tom White, and Maxime Dion for the Array Working Group and DataFusion-specific collaboration.
  • The gracious volunteer data science students at UCSD's DS3 org, who are working to make this library better.
  • Andrew Huang for the sense of taste he brings to the project and consummate code changes.
  • Aman Kumar for spending a considerable amount of his GSoC internship contributing to this project.

License

Copyright 2024 Alexander Merose

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

All vendored code has proper license attribution.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

xarray_sql-0.3.1.tar.gz (142.3 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

xarray_sql-0.3.1-cp310-abi3-win_amd64.whl (16.1 MB view details)

Uploaded CPython 3.10+Windows x86-64

xarray_sql-0.3.1-cp310-abi3-manylinux_2_34_x86_64.whl (18.9 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.34+ x86-64

xarray_sql-0.3.1-cp310-abi3-manylinux_2_28_x86_64.whl (16.6 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.28+ x86-64

xarray_sql-0.3.1-cp310-abi3-manylinux_2_28_aarch64.whl (15.3 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.28+ ARM64

xarray_sql-0.3.1-cp310-abi3-macosx_11_0_arm64.whl (14.4 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

xarray_sql-0.3.1-cp310-abi3-macosx_10_12_x86_64.whl (15.2 MB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

File details

Details for the file xarray_sql-0.3.1.tar.gz.

File metadata

  • Download URL: xarray_sql-0.3.1.tar.gz
  • Upload date:
  • Size: 142.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for xarray_sql-0.3.1.tar.gz
Algorithm Hash digest
SHA256 fa45c8143ab6723696c44e66c7dddcdbaa6221d108e8d948e64b11f1d07b320e
MD5 978e26ad4422a46c2ea30ee8a8d2ad6a
BLAKE2b-256 ed2683de0529de79aeab2c05000d18859b9579e4a6ed7276f22053c00caf221d

See more details on using hashes here.

File details

Details for the file xarray_sql-0.3.1-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: xarray_sql-0.3.1-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 16.1 MB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for xarray_sql-0.3.1-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 37260b0c1957c264eca365caecbb86c03b2f9ede3164bfd74c34f49305414020
MD5 2ffe80ebfabdef756fc37457493411c2
BLAKE2b-256 cc83580774b2fd3c18a9bafdb3d9b6f46ad733fe50c2c49ee578283f13685e03

See more details on using hashes here.

File details

Details for the file xarray_sql-0.3.1-cp310-abi3-manylinux_2_34_x86_64.whl.

File metadata

  • Download URL: xarray_sql-0.3.1-cp310-abi3-manylinux_2_34_x86_64.whl
  • Upload date:
  • Size: 18.9 MB
  • Tags: CPython 3.10+, manylinux: glibc 2.34+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for xarray_sql-0.3.1-cp310-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 b6d56545c3c92de6a0916a17462edde78bd53efc0d3e60731306915d97db456c
MD5 a99f8bd396db92bc3524a1c66a7f0e96
BLAKE2b-256 4debfe2a3fff8ba0b2076a095106700ae0abc808731cf64ebcbb79085bb15764

See more details on using hashes here.

File details

Details for the file xarray_sql-0.3.1-cp310-abi3-manylinux_2_28_x86_64.whl.

File metadata

  • Download URL: xarray_sql-0.3.1-cp310-abi3-manylinux_2_28_x86_64.whl
  • Upload date:
  • Size: 16.6 MB
  • Tags: CPython 3.10+, manylinux: glibc 2.28+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for xarray_sql-0.3.1-cp310-abi3-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 6f1ea45e4a51ff905a3587819f04a83375a9db955f1b40deedb22ebc35f7df7a
MD5 72c8e00b3e40afb3a90de845c093e16e
BLAKE2b-256 dc487eee14d7cc1053dca012920ded6cad105c72443123f7b0d834b335f00740

See more details on using hashes here.

File details

Details for the file xarray_sql-0.3.1-cp310-abi3-manylinux_2_28_aarch64.whl.

File metadata

  • Download URL: xarray_sql-0.3.1-cp310-abi3-manylinux_2_28_aarch64.whl
  • Upload date:
  • Size: 15.3 MB
  • Tags: CPython 3.10+, manylinux: glibc 2.28+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for xarray_sql-0.3.1-cp310-abi3-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 355c54f5887c09e079317329090e0bc95b8196379b62eada6dbc3366ca5475dc
MD5 28097b7904adede3b5643bfcb14dba45
BLAKE2b-256 fc7758133074e1ad84315074d7a85cfe02467f3709a744f37f6cdeded5beee0b

See more details on using hashes here.

File details

Details for the file xarray_sql-0.3.1-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

  • Download URL: xarray_sql-0.3.1-cp310-abi3-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 14.4 MB
  • Tags: CPython 3.10+, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for xarray_sql-0.3.1-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 05e8e1c806268ae80ab1b2b803c478ff5b33f3cdefce7559f5da3133b811824f
MD5 24dc8c5ed0c948eed69009b43db96860
BLAKE2b-256 6b14f7442c47c0915ce7cb65b137d93c9f5639cbf7a4ca19c31a62406b92a4b9

See more details on using hashes here.

File details

Details for the file xarray_sql-0.3.1-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

  • Download URL: xarray_sql-0.3.1-cp310-abi3-macosx_10_12_x86_64.whl
  • Upload date:
  • Size: 15.2 MB
  • Tags: CPython 3.10+, macOS 10.12+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for xarray_sql-0.3.1-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 703d11417853882e08441c798ab53ce4024dd31dafc912f19a733fe826c58b72
MD5 c323af66da42624da97e6b74b78551ab
BLAKE2b-256 15257c6c8d7bb4c3270fea9e22d7bea16e2ed8aca6c3f7ba0d8990be5aa7ee9c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page