├── .binder └── environment.yml ├── .codecov.yml ├── .git-blame-ignore-revs ├── .git_archival.txt ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bugreport.yml │ ├── config.yml │ ├── misc.yml │ └── newfeature.yml ├── PULL_REQUEST_TEMPLATE.md ├── config.yml ├── dependabot.yml ├── labeler.yml ├── release.yml ├── stale.yml └── workflows │ ├── benchmarks-last-release.yml │ ├── benchmarks.yml │ ├── ci-additional.yaml │ ├── ci.yaml │ ├── configure-testpypi-version.py │ ├── hypothesis.yaml │ ├── label-prs.yml │ ├── nightly-wheels.yml │ ├── publish-test-results.yaml │ ├── pypi-release.yaml │ └── upstream-dev-ci.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── CITATION.cff ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CORE_TEAM_GUIDE.md ├── DATATREE_MIGRATION_GUIDE.md ├── HOW_TO_RELEASE.md ├── LICENSE ├── README.md ├── asv_bench ├── asv.conf.json └── benchmarks │ ├── README_CI.md │ ├── __init__.py │ ├── accessors.py │ ├── alignment.py │ ├── coding.py │ ├── combine.py │ ├── dataarray_missing.py │ ├── dataset.py │ ├── dataset_io.py │ ├── datatree.py │ ├── groupby.py │ ├── import.py │ ├── indexing.py │ ├── interp.py │ ├── merge.py │ ├── pandas.py │ ├── polyfit.py │ ├── reindexing.py │ ├── renaming.py │ ├── repr.py │ ├── rolling.py │ └── unstacking.py ├── ci ├── install-upstream-wheels.sh ├── minimum_versions.py ├── release_contributors.py └── requirements │ ├── all-but-dask.yml │ ├── all-but-numba.yml │ ├── bare-minimum.yml │ ├── doc.yml │ ├── environment-3.14.yml │ ├── environment-windows-3.14.yml │ ├── environment-windows.yml │ ├── environment.yml │ └── min-all-deps.yml ├── conftest.py ├── design_notes ├── flexible_indexes_notes.md ├── grouper_objects.md └── named_array_design_doc.md ├── doc ├── Makefile ├── README.rst ├── _static │ ├── .gitignore │ ├── advanced_selection_interpolation.svg │ ├── ci.png │ ├── dask-array.svg │ ├── dataset-diagram.png │ ├── index_api.svg │ ├── index_contribute.svg │ ├── index_getting_started.svg │ ├── index_user_guide.svg │ ├── logos │ │ ├── Xarray_Icon_Final.png │ │ ├── Xarray_Icon_Final.svg │ │ ├── Xarray_Logo_FullColor_InverseRGB_Final.png │ │ ├── Xarray_Logo_FullColor_InverseRGB_Final.svg │ │ ├── Xarray_Logo_RGB_Final.png │ │ └── Xarray_Logo_RGB_Final.svg │ ├── numfocus_logo.png │ ├── opendap-prism-tmax.png │ ├── style.css │ ├── thumbnails │ │ ├── ERA5-GRIB-example.png │ │ ├── ROMS_ocean_model.png │ │ ├── area_weighted_temperature.png │ │ ├── monthly-means.png │ │ ├── multidimensional-coords.png │ │ ├── toy-weather-data.png │ │ └── visualization_gallery.png │ └── view-docs.png ├── _templates │ └── autosummary │ │ ├── accessor.rst │ │ ├── accessor_attribute.rst │ │ ├── accessor_callable.rst │ │ └── accessor_method.rst ├── api-hidden.rst ├── api.rst ├── combined.json ├── conf.py ├── contribute │ ├── contributing.rst │ ├── developers-meeting.rst │ └── index.rst ├── examples │ ├── ERA5-GRIB-example.ipynb │ ├── ROMS_ocean_model.ipynb │ ├── _code │ │ └── accessor_example.py │ ├── apply_ufunc_vectorize_1d.ipynb │ ├── area_weighted_temperature.ipynb │ ├── blank_template.ipynb │ ├── monthly-means.ipynb │ ├── multidimensional-coords.ipynb │ ├── visualization_gallery.ipynb │ └── weather-data.ipynb ├── gallery.rst ├── gallery.yml ├── gallery │ ├── README.txt │ ├── plot_cartopy_facetgrid.py │ ├── plot_colorbar_center.py │ ├── plot_control_colorbar.py │ └── plot_lines_from_2d.py ├── get-help │ ├── faq.rst │ ├── help-diagram.rst │ ├── howdoi.rst │ └── socials.rst ├── getting-started-guide │ ├── index.rst │ ├── installing.rst │ ├── quick-overview.rst │ ├── tutorials-and-videos.rst │ └── why-xarray.rst ├── index.rst ├── internals │ ├── chunked-arrays.rst │ ├── duck-arrays-integration.rst │ ├── extending-xarray.rst │ ├── how-to-add-new-backend.rst │ ├── how-to-create-custom-index.rst │ ├── index.rst │ ├── internal-design.rst │ ├── interoperability.rst │ ├── time-coding.rst │ └── zarr-encoding-spec.rst ├── roadmap.rst ├── user-guide │ ├── combining.rst │ ├── complex-numbers.rst │ ├── computation.rst │ ├── dask.rst │ ├── data-structures.rst │ ├── duckarrays.rst │ ├── ecosystem.rst │ ├── groupby.rst │ ├── hierarchical-data.rst │ ├── index.rst │ ├── indexing.rst │ ├── interpolation.rst │ ├── io.rst │ ├── options.rst │ ├── pandas.rst │ ├── plotting.rst │ ├── reshaping.rst │ ├── terminology.rst │ ├── testing.rst │ ├── time-series.rst │ └── weather-climate.rst ├── videos.yml └── whats-new.rst ├── licenses ├── ANYTREE_LICENSE ├── DASK_LICENSE ├── ICOMOON_LICENSE ├── NUMPY_LICENSE ├── PANDAS_LICENSE ├── PYTHON_LICENSE ├── SCIKIT_LEARN_LICENSE └── SEABORN_LICENSE ├── properties ├── README.md ├── __init__.py ├── conftest.py ├── test_encode_decode.py ├── test_index_manipulation.py ├── test_pandas_roundtrip.py └── test_properties.py ├── pyproject.toml ├── setup.py └── xarray ├── __init__.py ├── backends ├── __init__.py ├── api.py ├── common.py ├── file_manager.py ├── h5netcdf_.py ├── locks.py ├── lru_cache.py ├── memory.py ├── netCDF4_.py ├── netcdf3.py ├── plugins.py ├── pydap_.py ├── scipy_.py ├── store.py └── zarr.py ├── coders.py ├── coding ├── __init__.py ├── calendar_ops.py ├── cftime_offsets.py ├── cftimeindex.py ├── common.py ├── frequencies.py ├── strings.py ├── times.py └── variables.py ├── compat ├── __init__.py ├── array_api_compat.py ├── dask_array_compat.py ├── dask_array_ops.py ├── npcompat.py ├── pdcompat.py └── toolzcompat.py ├── computation ├── __init__.py ├── apply_ufunc.py ├── arithmetic.py ├── computation.py ├── fit.py ├── nanops.py ├── ops.py ├── rolling.py ├── rolling_exp.py └── weighted.py ├── conventions.py ├── convert.py ├── core ├── __init__.py ├── _aggregations.py ├── _typed_ops.py ├── accessor_dt.py ├── accessor_str.py ├── common.py ├── coordinate_transform.py ├── coordinates.py ├── dataarray.py ├── dataset.py ├── dataset_utils.py ├── dataset_variables.py ├── datatree.py ├── datatree_io.py ├── datatree_mapping.py ├── datatree_render.py ├── dtypes.py ├── duck_array_ops.py ├── extension_array.py ├── extensions.py ├── formatting.py ├── formatting_html.py ├── groupby.py ├── indexes.py ├── indexing.py ├── missing.py ├── nputils.py ├── options.py ├── parallel.py ├── resample.py ├── resample_cftime.py ├── treenode.py ├── types.py ├── utils.py └── variable.py ├── groupers.py ├── indexes ├── __init__.py └── range_index.py ├── namedarray ├── __init__.py ├── _aggregations.py ├── _array_api.py ├── _typing.py ├── core.py ├── daskmanager.py ├── dtypes.py ├── parallelcompat.py ├── pycompat.py └── utils.py ├── plot ├── __init__.py ├── accessor.py ├── dataarray_plot.py ├── dataset_plot.py ├── facetgrid.py └── utils.py ├── py.typed ├── static ├── __init__.py ├── css │ ├── __init__.py │ └── style.css └── html │ ├── __init__.py │ └── icons-svg-inline.html ├── structure ├── __init__.py ├── alignment.py ├── chunks.py ├── combine.py ├── concat.py └── merge.py ├── testing ├── __init__.py ├── assertions.py └── strategies.py ├── tests ├── __init__.py ├── arrays.py ├── conftest.py ├── data │ ├── bears.nc │ ├── example.grib │ ├── example.ict │ ├── example.uamiv │ ├── example_1.nc │ └── example_1.nc.gz ├── namespace.py ├── test_accessor_dt.py ├── test_accessor_str.py ├── test_array_api.py ├── test_assertions.py ├── test_backends.py ├── test_backends_api.py ├── test_backends_common.py ├── test_backends_datatree.py ├── test_backends_file_manager.py ├── test_backends_locks.py ├── test_backends_lru_cache.py ├── test_calendar_ops.py ├── test_cftime_offsets.py ├── test_cftimeindex.py ├── test_cftimeindex_resample.py ├── test_coarsen.py ├── test_coding.py ├── test_coding_strings.py ├── test_coding_times.py ├── test_combine.py ├── test_computation.py ├── test_concat.py ├── test_conventions.py ├── test_coordinate_transform.py ├── test_coordinates.py ├── test_cupy.py ├── test_dask.py ├── test_dataarray.py ├── test_dataarray_typing.yml ├── test_dataset.py ├── test_dataset_typing.yml ├── test_datatree.py ├── test_datatree_mapping.py ├── test_datatree_typing.yml ├── test_deprecation_helpers.py ├── test_distributed.py ├── test_dtypes.py ├── test_duck_array_ops.py ├── test_duck_array_wrapping.py ├── test_error_messages.py ├── test_extensions.py ├── test_formatting.py ├── test_formatting_html.py ├── test_groupby.py ├── test_hashable.py ├── test_indexes.py ├── test_indexing.py ├── test_interp.py ├── test_merge.py ├── test_missing.py ├── test_namedarray.py ├── test_nputils.py ├── test_options.py ├── test_pandas_to_xarray.py ├── test_parallelcompat.py ├── test_plot.py ├── test_plugins.py ├── test_print_versions.py ├── test_range_index.py ├── test_rolling.py ├── test_sparse.py ├── test_strategies.py ├── test_treenode.py ├── test_tutorial.py ├── test_typed_ops.py ├── test_ufuncs.py ├── test_units.py ├── test_utils.py ├── test_variable.py └── test_weighted.py ├── tutorial.py ├── typing.py ├── ufuncs.py └── util ├── __init__.py ├── deprecation_helpers.py ├── generate_aggregations.py ├── generate_ops.py └── print_versions.py /.binder/environment.yml: -------------------------------------------------------------------------------- 1 | name: xarray-examples 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - python=3.10 6 | - boto3 7 | - bottleneck 8 | - cartopy 9 | - cfgrib 10 | - cftime 11 | - coveralls 12 | - dask 13 | - distributed 14 | - dask_labextension 15 | - h5netcdf 16 | - h5py 17 | - hdf5 18 | - iris 19 | - lxml # Optional dep of pydap 20 | - matplotlib 21 | - nc-time-axis 22 | - netcdf4 23 | - numba 24 | - numpy 25 | - packaging 26 | - pandas 27 | - pint>=0.22 28 | - pip 29 | - pooch 30 | - pydap 31 | - rasterio 32 | - scipy 33 | - seaborn 34 | - setuptools 35 | - sparse 36 | - toolz 37 | - xarray 38 | - zarr 39 | - numbagg 40 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: true 3 | 4 | coverage: 5 | status: 6 | project: 7 | default: 8 | # Require 1% coverage, i.e., always succeed 9 | target: 1% 10 | flags: 11 | - unittests 12 | paths: 13 | - "!xarray/tests/" 14 | unittests: 15 | target: 90% 16 | flags: 17 | - unittests 18 | paths: 19 | - "!xarray/tests/" 20 | mypy: 21 | target: 20% 22 | flags: 23 | - mypy 24 | patch: false 25 | changes: false 26 | 27 | comment: false 28 | 29 | flags: 30 | unittests: 31 | paths: 32 | - "xarray" 33 | - "!xarray/tests" 34 | carryforward: false 35 | mypy: 36 | paths: 37 | - "xarray" 38 | carryforward: false 39 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # black PR 3142 2 | d089df385e737f71067309ff7abae15994d581ec 3 | 4 | # isort PR 1924 5 | 0e73e240107caee3ffd1a1149f0150c390d43251 6 | -------------------------------------------------------------------------------- /.git_archival.txt: -------------------------------------------------------------------------------- 1 | node: 34efef2192a65e0f26a340ae305b0d3ed9e91b19 2 | node-date: 2025-05-30T15:24:28Z 3 | describe-name: v2025.04.0-52-g34efef2192a 4 | ref-names: HEAD -> main 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # reduce the number of merge conflicts 2 | doc/whats-new.rst merge=union 3 | # allow installing from git archives 4 | .git_archival.txt export-subst 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: numfocus 2 | custom: https://numfocus.org/donate-to-xarray 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bugreport.yml: -------------------------------------------------------------------------------- 1 | name: 🐛 Bug Report 2 | description: File a bug report to help us improve 3 | labels: [bug, "needs triage"] 4 | body: 5 | - type: textarea 6 | id: what-happened 7 | attributes: 8 | label: What happened? 9 | description: | 10 | Thanks for reporting a bug! Please describe what you were trying to get done. 11 | Tell us what happened, what went wrong. 12 | validations: 13 | required: true 14 | 15 | - type: textarea 16 | id: what-did-you-expect-to-happen 17 | attributes: 18 | label: What did you expect to happen? 19 | description: | 20 | Describe what you expected to happen. 21 | validations: 22 | required: false 23 | 24 | - type: textarea 25 | id: sample-code 26 | attributes: 27 | label: Minimal Complete Verifiable Example 28 | description: | 29 | Minimal, self-contained copy-pastable example that demonstrates the issue. This will be automatically formatted into code, so no need for markdown backticks. 30 | render: Python 31 | 32 | - type: checkboxes 33 | id: mvce-checkboxes 34 | attributes: 35 | label: MVCE confirmation 36 | description: | 37 | Please confirm that the bug report is in an excellent state, so we can understand & fix it quickly & efficiently. For more details, check out: 38 | 39 | - [Minimal Complete Verifiable Examples](https://stackoverflow.com/help/mcve) 40 | - [Craft Minimal Bug Reports](https://matthewrocklin.com/minimal-bug-reports) 41 | 42 | options: 43 | - label: Minimal example — the example is as focused as reasonably possible to demonstrate the underlying issue in xarray. 44 | - label: Complete example — the example is self-contained, including all data and the text of any traceback. 45 | - label: Verifiable example — the example copy & pastes into an IPython prompt or [Binder notebook](https://mybinder.org/v2/gh/pydata/xarray/main?urlpath=lab/tree/doc/examples/blank_template.ipynb), returning the result. 46 | - label: New issue — a search of GitHub Issues suggests this is not a duplicate. 47 | - label: Recent environment — the issue occurs with the latest version of xarray and its dependencies. 48 | 49 | - type: textarea 50 | id: log-output 51 | attributes: 52 | label: Relevant log output 53 | description: Please copy and paste any relevant output. This will be automatically formatted into code, so no need for markdown backticks. 54 | render: Python 55 | 56 | - type: textarea 57 | id: extra 58 | attributes: 59 | label: Anything else we need to know? 60 | description: | 61 | Please describe any other information you want to share. 62 | 63 | - type: textarea 64 | id: show-versions 65 | attributes: 66 | label: Environment 67 | description: | 68 | Paste the output of `xr.show_versions()` between the `
` tags, leaving an empty line following the opening tag. 69 | value: | 70 |
71 | 72 | 73 | 74 |
75 | validations: 76 | required: true 77 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: ❓ Usage question 4 | url: https://github.com/pydata/xarray/discussions 5 | about: | 6 | Ask questions and discuss with other community members here. 7 | If you have a question like "How do I concatenate a list of datasets?" then 8 | please include a self-contained reproducible example if possible. 9 | - name: 🗺️ Raster analysis usage question 10 | url: https://github.com/corteva/rioxarray/discussions 11 | about: | 12 | If you are using the rioxarray extension (engine='rasterio'), or have questions about 13 | raster analysis such as geospatial formats, coordinate reprojection, etc., 14 | please use the rioxarray discussion forum. 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/misc.yml: -------------------------------------------------------------------------------- 1 | name: 📝 Issue 2 | description: General issue, that's not a bug report. 3 | labels: ["needs triage"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Please describe your issue here. 9 | - type: textarea 10 | id: issue-description 11 | attributes: 12 | label: What is your issue? 13 | description: | 14 | Thank you for filing an issue! Please give us further information on how we can help you. 15 | placeholder: Please describe your issue. 16 | validations: 17 | required: true 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/newfeature.yml: -------------------------------------------------------------------------------- 1 | name: 💡 Feature Request 2 | description: Suggest an idea for xarray 3 | labels: [enhancement] 4 | body: 5 | - type: textarea 6 | id: description 7 | attributes: 8 | label: Is your feature request related to a problem? 9 | description: | 10 | Please do a quick search of existing issues to make sure that this has not been asked before. 11 | Please provide a clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: solution 16 | attributes: 17 | label: Describe the solution you'd like 18 | description: | 19 | A clear and concise description of what you want to happen. 20 | - type: textarea 21 | id: alternatives 22 | attributes: 23 | label: Describe alternatives you've considered 24 | description: | 25 | A clear and concise description of any alternative solutions or features you've considered. 26 | validations: 27 | required: false 28 | - type: textarea 29 | id: additional-context 30 | attributes: 31 | label: Additional context 32 | description: | 33 | Add any other context about the feature request here. 34 | validations: 35 | required: false 36 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | - [ ] Closes #xxxx 4 | - [ ] Tests added 5 | - [ ] User visible changes (including notable bug fixes) are documented in `whats-new.rst` 6 | - [ ] New functions/methods are listed in `api.rst` 7 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | # Comment to be posted to on first time issues 2 | newIssueWelcomeComment: > 3 | Thanks for opening your first issue here at xarray! Be sure to follow the issue template! 4 | 5 | If you have an idea for a solution, we would really welcome a Pull Request with proposed changes. 6 | 7 | See the [Contributing Guide](https://docs.xarray.dev/en/latest/contributing.html) for more. 8 | 9 | It may take us a while to respond here, but we really value your contribution. Contributors like you help make xarray better. 10 | 11 | Thank you! 12 | 13 | # Comment to be posted to on PRs from first time contributors in your repository 14 | newPRWelcomeComment: > 15 | Thank you for opening this pull request! It may take us a few days to respond here, so thank you for being patient. 16 | 17 | If you have questions, some answers may be found in our [contributing guidelines](https://docs.xarray.dev/en/stable/contributing.html). 18 | 19 | # Comment to be posted to on pull requests merged by a first time user 20 | firstPRMergeComment: > 21 | Congratulations on completing your first pull request! Welcome to Xarray! 22 | We are proud of you, and hope to see you again! 23 | ![celebration gif](https://media.giphy.com/media/umYMU8G2ixG5mJBDo5/giphy.gif) 24 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | # Check for updates once a week 7 | interval: "weekly" 8 | groups: 9 | actions: 10 | patterns: 11 | - "*" 12 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | Automation: 2 | - changed-files: 3 | - any-glob-to-any-file: 4 | - .github/** 5 | 6 | CI: 7 | - changed-files: 8 | - any-glob-to-any-file: 9 | - ci/** 10 | 11 | dependencies: 12 | - changed-files: 13 | - any-glob-to-any-file: 14 | - ci/requirements/* 15 | 16 | topic-arrays: 17 | - changed-files: 18 | - any-glob-to-any-file: 19 | - xarray/core/duck_array_ops.py 20 | 21 | topic-backends: 22 | - changed-files: 23 | - any-glob-to-any-file: 24 | - xarray/backends/** 25 | 26 | topic-cftime: 27 | - changed-files: 28 | - any-glob-to-any-file: 29 | - xarray/coding/*time* 30 | 31 | topic-CF conventions: 32 | - changed-files: 33 | - any-glob-to-any-file: 34 | - xarray/conventions.py 35 | 36 | topic-dask: 37 | - changed-files: 38 | - any-glob-to-any-file: 39 | - xarray/compat/dask* 40 | - xarray/core/parallel.py 41 | 42 | topic-DataTree: 43 | - changed-files: 44 | - any-glob-to-any-file: 45 | - xarray/core/datatree* 46 | 47 | topic-documentation: 48 | - changed-files: 49 | - any-glob-to-any-file: 50 | - doc/* 51 | - "!doc/whats-new.rst" 52 | - doc/**/* 53 | 54 | topic-groupby: 55 | - changed-files: 56 | - any-glob-to-any-file: 57 | - xarray/core/groupby.py 58 | 59 | topic-html-repr: 60 | - changed-files: 61 | - any-glob-to-any-file: 62 | - xarray/core/formatting_html.py 63 | 64 | topic-hypothesis: 65 | - changed-files: 66 | - any-glob-to-any-file: 67 | - properties/** 68 | - xarray/testing/strategies.py 69 | 70 | topic-indexing: 71 | - changed-files: 72 | - any-glob-to-any-file: 73 | - xarray/core/indexes.py 74 | - xarray/core/indexing.py 75 | 76 | topic-NamedArray: 77 | - changed-files: 78 | - any-glob-to-any-file: 79 | - xarray/namedarray/* 80 | 81 | topic-performance: 82 | - changed-files: 83 | - any-glob-to-any-file: 84 | - asv_bench/benchmarks/** 85 | 86 | topic-plotting: 87 | - changed-files: 88 | - any-glob-to-any-file: 89 | - xarray/plot/* 90 | - xarray/plot/**/* 91 | 92 | topic-rolling: 93 | - changed-files: 94 | - any-glob-to-any-file: 95 | - xarray/computation/rolling.py 96 | - xarray/computation/rolling_exp.py 97 | 98 | topic-testing: 99 | - changed-files: 100 | - any-glob-to-any-file: 101 | - conftest.py 102 | - xarray/testing/* 103 | 104 | topic-typing: 105 | - changed-files: 106 | - any-glob-to-any-file: 107 | - xarray/core/types.py 108 | 109 | topic-zarr: 110 | - changed-files: 111 | - any-glob-to-any-file: 112 | - xarray/backends/zarr.py 113 | 114 | io: 115 | - changed-files: 116 | - any-glob-to-any-file: 117 | - xarray/backends/** 118 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | authors: 4 | - dependabot 5 | - pre-commit-ci 6 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 600 # start with a large number and reduce shortly 5 | 6 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. 7 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. 8 | daysUntilClose: 30 9 | 10 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 11 | exemptLabels: 12 | - pinned 13 | - security 14 | - "[Status] Maybe Later" 15 | 16 | # Set to true to ignore issues in a project (defaults to false) 17 | exemptProjects: true 18 | 19 | # Set to true to ignore issues in a milestone (defaults to false) 20 | exemptMilestones: true 21 | 22 | # Set to true to ignore issues with an assignee (defaults to false) 23 | exemptAssignees: true 24 | 25 | # Label to use when marking as stale 26 | staleLabel: stale 27 | 28 | # Comment to post when marking as stale. Set to `false` to disable 29 | markComment: | 30 | In order to maintain a list of currently relevant issues, we mark issues as stale after a period of inactivity 31 | 32 | If this issue remains relevant, please comment here or remove the `stale` label; otherwise it will be marked as closed automatically 33 | 34 | closeComment: | 35 | The stalebot didn't hear anything for a while, so it closed this. Please reopen if this is still an issue. 36 | 37 | # Comment to post when removing the stale label. 38 | # unmarkComment: > 39 | # Your comment here. 40 | 41 | # Comment to post when closing a stale Issue or Pull Request. 42 | # closeComment: > 43 | # Your comment here. 44 | 45 | # Limit the number of actions per hour, from 1-30. Default is 30 46 | limitPerRun: 2 # start with a small number 47 | 48 | # Limit to only `issues` or `pulls` 49 | # only: issues 50 | 51 | # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': 52 | # pulls: 53 | # daysUntilStale: 30 54 | # markComment: > 55 | # This pull request has been automatically marked as stale because it has not had 56 | # recent activity. It will be closed if no further activity occurs. Thank you 57 | # for your contributions. 58 | 59 | # issues: 60 | # exemptLabels: 61 | # - confirmed 62 | -------------------------------------------------------------------------------- /.github/workflows/benchmarks-last-release.yml: -------------------------------------------------------------------------------- 1 | name: Benchmark compare last release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | benchmark: 11 | name: Linux 12 | runs-on: ubuntu-latest 13 | env: 14 | ASV_DIR: "./asv_bench" 15 | CONDA_ENV_FILE: ci/requirements/environment.yml 16 | 17 | steps: 18 | # We need the full repo to avoid this issue 19 | # https://github.com/actions/checkout/issues/23 20 | - uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | 24 | - name: Set up conda environment 25 | uses: mamba-org/setup-micromamba@v2 26 | with: 27 | micromamba-version: "1.5.10-0" 28 | environment-file: ${{env.CONDA_ENV_FILE}} 29 | environment-name: xarray-tests 30 | cache-environment: true 31 | cache-environment-key: "${{runner.os}}-${{runner.arch}}-py${{env.PYTHON_VERSION}}-${{env.TODAY}}-${{hashFiles(env.CONDA_ENV_FILE)}}-benchmark" 32 | create-args: >- 33 | asv 34 | 35 | - name: "Get Previous tag" 36 | id: previoustag 37 | uses: "WyriHaximus/github-action-get-previous-tag@v1" 38 | # with: 39 | # fallback: 1.0.0 # Optional fallback tag to use when no tag can be found 40 | 41 | - name: Run benchmarks 42 | shell: bash -l {0} 43 | id: benchmark 44 | env: 45 | OPENBLAS_NUM_THREADS: 1 46 | MKL_NUM_THREADS: 1 47 | OMP_NUM_THREADS: 1 48 | ASV_FACTOR: 1.5 49 | ASV_SKIP_SLOW: 1 50 | run: | 51 | set -x 52 | # ID this runner 53 | asv machine --yes 54 | echo "Baseline: ${{ steps.previoustag.outputs.tag }} " 55 | echo "Contender: ${{ github.sha }}" 56 | # Use mamba for env creation 57 | # export CONDA_EXE=$(which mamba) 58 | export CONDA_EXE=$(which conda) 59 | # Run benchmarks for current commit against base 60 | ASV_OPTIONS="--split --show-stderr --factor $ASV_FACTOR" 61 | asv continuous $ASV_OPTIONS ${{ steps.previoustag.outputs.tag }} ${{ github.sha }} \ 62 | | sed "/Traceback \|failed$\|PERFORMANCE DECREASED/ s/^/::error::/" \ 63 | | tee benchmarks.log 64 | # Report and export results for subsequent steps 65 | if grep "Traceback \|failed\|PERFORMANCE DECREASED" benchmarks.log > /dev/null ; then 66 | exit 1 67 | fi 68 | working-directory: ${{ env.ASV_DIR }} 69 | 70 | - name: Add instructions to artifact 71 | if: always() 72 | run: | 73 | cp benchmarks/README_CI.md benchmarks.log .asv/results/ 74 | working-directory: ${{ env.ASV_DIR }} 75 | 76 | - uses: actions/upload-artifact@v4 77 | if: always() 78 | with: 79 | name: asv-benchmark-results-${{ runner.os }} 80 | path: ${{ env.ASV_DIR }}/.asv/results 81 | -------------------------------------------------------------------------------- /.github/workflows/benchmarks.yml: -------------------------------------------------------------------------------- 1 | name: Benchmark 2 | 3 | on: 4 | pull_request: 5 | types: [opened, reopened, synchronize, labeled] 6 | workflow_dispatch: 7 | 8 | env: 9 | PR_HEAD_LABEL: ${{ github.event.pull_request.head.label }} 10 | 11 | jobs: 12 | benchmark: 13 | if: ${{ contains( github.event.pull_request.labels.*.name, 'run-benchmark') && github.event_name == 'pull_request' || contains( github.event.pull_request.labels.*.name, 'topic-performance') && github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' }} 14 | name: Linux 15 | runs-on: ubuntu-latest 16 | env: 17 | ASV_DIR: "./asv_bench" 18 | CONDA_ENV_FILE: ci/requirements/environment.yml 19 | 20 | steps: 21 | # We need the full repo to avoid this issue 22 | # https://github.com/actions/checkout/issues/23 23 | - uses: actions/checkout@v4 24 | with: 25 | fetch-depth: 0 26 | 27 | - name: Set up conda environment 28 | uses: mamba-org/setup-micromamba@v2 29 | with: 30 | micromamba-version: "1.5.10-0" 31 | environment-file: ${{env.CONDA_ENV_FILE}} 32 | environment-name: xarray-tests 33 | cache-environment: true 34 | cache-environment-key: "${{runner.os}}-${{runner.arch}}-py${{env.PYTHON_VERSION}}-${{env.TODAY}}-${{hashFiles(env.CONDA_ENV_FILE)}}-benchmark" 35 | # add "build" because of https://github.com/airspeed-velocity/asv/issues/1385 36 | create-args: >- 37 | asv 38 | python-build 39 | mamba<=1.5.10 40 | 41 | - name: Run benchmarks 42 | shell: bash -l {0} 43 | id: benchmark 44 | env: 45 | OPENBLAS_NUM_THREADS: 1 46 | MKL_NUM_THREADS: 1 47 | OMP_NUM_THREADS: 1 48 | ASV_FACTOR: 1.5 49 | ASV_SKIP_SLOW: 1 50 | run: | 51 | set -x 52 | # ID this runner 53 | asv machine --yes 54 | echo "Baseline: ${{ github.event.pull_request.base.sha }} (${{ github.event.pull_request.base.label }})" 55 | echo "Contender: ${GITHUB_SHA} ($PR_HEAD_LABEL)" 56 | # Run benchmarks for current commit against base 57 | ASV_OPTIONS="--split --show-stderr --factor $ASV_FACTOR" 58 | asv continuous $ASV_OPTIONS ${{ github.event.pull_request.base.sha }} ${GITHUB_SHA} \ 59 | | sed "/Traceback \|failed$\|PERFORMANCE DECREASED/ s/^/::error::/" \ 60 | | tee benchmarks.log 61 | # Report and export results for subsequent steps 62 | if grep "Traceback \|failed\|PERFORMANCE DECREASED" benchmarks.log > /dev/null ; then 63 | exit 1 64 | fi 65 | working-directory: ${{ env.ASV_DIR }} 66 | 67 | - name: Add instructions to artifact 68 | if: always() 69 | run: | 70 | cp benchmarks/README_CI.md benchmarks.log .asv/results/ 71 | working-directory: ${{ env.ASV_DIR }} 72 | 73 | - uses: actions/upload-artifact@v4 74 | if: always() 75 | with: 76 | name: asv-benchmark-results-${{ runner.os }} 77 | path: ${{ env.ASV_DIR }}/.asv/results 78 | -------------------------------------------------------------------------------- /.github/workflows/configure-testpypi-version.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import copy 3 | import pathlib 4 | 5 | import tomli 6 | import tomli_w 7 | 8 | 9 | def split_path(path, sep="/"): 10 | if isinstance(path, str): 11 | return [part for part in path.split(sep) if part] 12 | else: 13 | return path 14 | 15 | 16 | def extract(mapping, path, sep="/"): 17 | parts = split_path(path, sep=sep) 18 | cur = mapping 19 | for part in parts: 20 | cur = cur[part] 21 | 22 | return cur 23 | 24 | 25 | def update(mapping, path, value, sep="/"): 26 | new = copy.deepcopy(mapping) 27 | 28 | parts = split_path(path, sep=sep) 29 | parent = extract(new, parts[:-1]) 30 | parent[parts[-1]] = value 31 | 32 | return new 33 | 34 | 35 | parser = argparse.ArgumentParser() 36 | parser.add_argument("path", type=pathlib.Path) 37 | args = parser.parse_args() 38 | 39 | content = args.path.read_text() 40 | decoded = tomli.loads(content) 41 | with_local_scheme = update( 42 | decoded, "tool.setuptools_scm.local_scheme", "no-local-version", sep="." 43 | ) 44 | # work around a bug in setuptools / setuptools-scm 45 | with_setuptools_pin = copy.deepcopy(with_local_scheme) 46 | requires = extract(with_setuptools_pin, "build-system.requires", sep=".") 47 | requires[0] = "setuptools>=42,<60" 48 | 49 | new_content = tomli_w.dumps(with_setuptools_pin) 50 | args.path.write_text(new_content) 51 | -------------------------------------------------------------------------------- /.github/workflows/label-prs.yml: -------------------------------------------------------------------------------- 1 | name: "PR Labeler" 2 | on: 3 | - pull_request_target 4 | 5 | jobs: 6 | label: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/labeler@v5 10 | with: 11 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 12 | sync-labels: false 13 | -------------------------------------------------------------------------------- /.github/workflows/nightly-wheels.yml: -------------------------------------------------------------------------------- 1 | name: Upload nightly wheels 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: "0 0 * * *" 6 | jobs: 7 | cron: 8 | runs-on: ubuntu-latest 9 | if: github.repository == 'pydata/xarray' 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | fetch-depth: 0 14 | - uses: actions/setup-python@v5 15 | with: 16 | python-version: "3.12" 17 | 18 | - name: Install dependencies 19 | run: | 20 | python -m pip install --upgrade pip 21 | python -m pip install build twine 22 | 23 | - name: Build tarball and wheels 24 | run: | 25 | git clean -xdf 26 | git restore -SW . 27 | python -m build 28 | 29 | - name: Check built artifacts 30 | run: | 31 | python -m twine check --strict dist/* 32 | pwd 33 | if [ -f dist/xarray-0.0.0.tar.gz ]; then 34 | echo "❌ INVALID VERSION NUMBER" 35 | exit 1 36 | else 37 | echo "✅ Looks good" 38 | fi 39 | 40 | - name: Upload wheel 41 | uses: scientific-python/upload-nightly-action@b36e8c0c10dbcfd2e05bf95f17ef8c14fd708dbf # 0.6.2 42 | with: 43 | anaconda_nightly_upload_token: ${{ secrets.ANACONDA_NIGHTLY }} 44 | artifacts_path: dist 45 | -------------------------------------------------------------------------------- /.github/workflows/publish-test-results.yaml: -------------------------------------------------------------------------------- 1 | # Copied from https://github.com/EnricoMi/publish-unit-test-result-action/blob/v1.23/README.md#support-fork-repositories-and-dependabot-branches 2 | 3 | name: Publish test results 4 | 5 | on: 6 | workflow_run: 7 | workflows: ["CI"] 8 | types: 9 | - completed 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.ref }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | publish-test-results: 17 | name: Publish test results 18 | runs-on: ubuntu-latest 19 | if: github.event.workflow_run.conclusion != 'skipped' 20 | 21 | steps: 22 | - name: Download and extract artifacts 23 | env: 24 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 25 | run: | 26 | mkdir artifacts && cd artifacts 27 | 28 | artifacts_url=${{ github.event.workflow_run.artifacts_url }} 29 | 30 | gh api "$artifacts_url" -q '.artifacts[] | [.name, .archive_download_url] | @tsv' | while read artifact 31 | do 32 | IFS=$'\t' read name url <<< "$artifact" 33 | gh api $url > "$name.zip" 34 | unzip -d "$name" "$name.zip" 35 | done 36 | 37 | - name: Publish Unit Test Results 38 | uses: EnricoMi/publish-unit-test-result-action@v2 39 | with: 40 | commit: ${{ github.event.workflow_run.head_sha }} 41 | event_file: artifacts/Event File/event.json 42 | event_name: ${{ github.event.workflow_run.event }} 43 | files: "artifacts/**/*.xml" 44 | comment_mode: off 45 | -------------------------------------------------------------------------------- /.github/workflows/pypi-release.yaml: -------------------------------------------------------------------------------- 1 | name: Build and Upload xarray to PyPI 2 | on: 3 | release: 4 | types: 5 | - published 6 | push: 7 | tags: 8 | - "v*" 9 | 10 | jobs: 11 | build-artifacts: 12 | runs-on: ubuntu-latest 13 | if: github.repository == 'pydata/xarray' 14 | steps: 15 | - uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | - uses: actions/setup-python@v5 19 | name: Install Python 20 | with: 21 | python-version: "3.12" 22 | 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | python -m pip install build twine 27 | 28 | - name: Build tarball and wheels 29 | run: | 30 | git clean -xdf 31 | git restore -SW . 32 | python -m build 33 | 34 | - name: Check built artifacts 35 | run: | 36 | python -m twine check --strict dist/* 37 | pwd 38 | if [ -f dist/xarray-0.0.0.tar.gz ]; then 39 | echo "❌ INVALID VERSION NUMBER" 40 | exit 1 41 | else 42 | echo "✅ Looks good" 43 | fi 44 | - uses: actions/upload-artifact@v4 45 | with: 46 | name: releases 47 | path: dist 48 | 49 | test-built-dist: 50 | needs: build-artifacts 51 | runs-on: ubuntu-latest 52 | steps: 53 | - uses: actions/setup-python@v5 54 | name: Install Python 55 | with: 56 | python-version: "3.12" 57 | - uses: actions/download-artifact@v4 58 | with: 59 | name: releases 60 | path: dist 61 | - name: List contents of built dist 62 | run: | 63 | ls -ltrh 64 | ls -ltrh dist 65 | 66 | - name: Verify the built dist/wheel is valid 67 | if: github.event_name == 'push' 68 | run: | 69 | python -m pip install --upgrade pip 70 | python -m pip install dist/xarray*.whl 71 | python -m xarray.util.print_versions 72 | 73 | upload-to-test-pypi: 74 | needs: test-built-dist 75 | if: github.event_name == 'push' 76 | runs-on: ubuntu-latest 77 | 78 | environment: 79 | name: pypi 80 | url: https://test.pypi.org/p/xarray 81 | permissions: 82 | id-token: write 83 | 84 | steps: 85 | - uses: actions/download-artifact@v4 86 | with: 87 | name: releases 88 | path: dist 89 | - name: Publish package to TestPyPI 90 | if: github.event_name == 'push' 91 | uses: pypa/gh-action-pypi-publish@v1.12.4 92 | with: 93 | repository_url: https://test.pypi.org/legacy/ 94 | verbose: true 95 | 96 | upload-to-pypi: 97 | needs: test-built-dist 98 | if: github.event_name == 'release' 99 | runs-on: ubuntu-latest 100 | 101 | environment: 102 | name: pypi 103 | url: https://pypi.org/p/xarray 104 | permissions: 105 | id-token: write 106 | 107 | steps: 108 | - uses: actions/download-artifact@v4 109 | with: 110 | name: releases 111 | path: dist 112 | - name: Publish package to PyPI 113 | uses: pypa/gh-action-pypi-publish@v1.12.4 114 | with: 115 | verbose: true 116 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | __pycache__ 3 | .env 4 | .venv 5 | 6 | # example caches from Hypothesis 7 | .hypothesis/ 8 | 9 | # temp files from docs build 10 | doc/*.nc 11 | doc/auto_gallery 12 | doc/rasm.zarr 13 | doc/savefig 14 | 15 | # C extensions 16 | *.so 17 | 18 | # Packages 19 | *.egg 20 | *.egg-info 21 | .eggs 22 | dist 23 | build 24 | eggs 25 | parts 26 | bin 27 | var 28 | sdist 29 | develop-eggs 30 | .installed.cfg 31 | lib 32 | lib64 33 | 34 | # Installer logs 35 | pip-log.txt 36 | 37 | # Unit test / coverage reports 38 | .coverage 39 | .coverage.* 40 | .tox 41 | nosetests.xml 42 | .cache 43 | .prettier_cache 44 | .dmypy.json 45 | .mypy_cache 46 | .ropeproject/ 47 | .tags* 48 | .testmon* 49 | .tmontmp/ 50 | .pytest_cache 51 | dask-worker-space/ 52 | 53 | # asv environments 54 | asv_bench/.asv 55 | asv_bench/pkgs 56 | 57 | # Translations 58 | *.mo 59 | 60 | # Mr Developer 61 | .mr.developer.cfg 62 | .project 63 | .pydevproject 64 | 65 | # IDEs 66 | .idea 67 | *.swp 68 | .DS_Store 69 | .vscode/ 70 | 71 | # xarray specific 72 | doc/_build 73 | doc/generated/ 74 | xarray/tests/data/*.grib.*.idx 75 | 76 | # Sync tools 77 | Icon* 78 | 79 | .ipynb_checkpoints 80 | doc/team-panel.txt 81 | doc/external-examples-gallery.txt 82 | doc/notebooks-examples-gallery.txt 83 | doc/videos-gallery.txt 84 | 85 | # Until we support this properly, excluding from gitignore. (adding it to 86 | # gitignore to make it _easier_ to work with `uv`, not as an indication that I 87 | # think we shouldn't...) 88 | uv.lock 89 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # https://pre-commit.com/ 2 | ci: 3 | autoupdate_schedule: monthly 4 | autoupdate_commit_msg: "Update pre-commit hooks" 5 | repos: 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v5.0.0 8 | hooks: 9 | - id: trailing-whitespace 10 | - id: end-of-file-fixer 11 | - id: check-yaml 12 | - id: debug-statements 13 | - id: mixed-line-ending 14 | - repo: https://github.com/pre-commit/pygrep-hooks 15 | rev: v1.10.0 16 | hooks: 17 | # - id: python-check-blanket-noqa # checked by ruff 18 | # - id: python-check-blanket-type-ignore # checked by ruff 19 | # - id: python-check-mock-methods # checked by ruff 20 | - id: python-no-log-warn 21 | # - id: python-use-type-annotations # too many false positives 22 | - id: rst-backticks 23 | - id: rst-directive-colons 24 | - id: rst-inline-touching-normal 25 | - id: text-unicode-replacement-char 26 | - repo: https://github.com/astral-sh/ruff-pre-commit 27 | # Ruff version. 28 | rev: v0.11.8 29 | hooks: 30 | - id: ruff-format 31 | - id: ruff 32 | args: ["--fix", "--show-fixes"] 33 | - repo: https://github.com/keewis/blackdoc 34 | rev: v0.3.9 35 | hooks: 36 | - id: blackdoc 37 | exclude: "generate_aggregations.py" 38 | additional_dependencies: ["black==24.8.0"] 39 | - repo: https://github.com/rbubley/mirrors-prettier 40 | rev: v3.5.3 41 | hooks: 42 | - id: prettier 43 | args: [--cache-location=.prettier_cache/cache] 44 | - repo: https://github.com/pre-commit/mirrors-mypy 45 | rev: v1.15.0 46 | hooks: 47 | - id: mypy 48 | # Copied from setup.cfg 49 | exclude: "properties|asv_bench" 50 | # This is slow and so we take it out of the fast-path; requires passing 51 | # `--hook-stage manual` to pre-commit 52 | stages: [manual] 53 | additional_dependencies: [ 54 | # Type stubs 55 | types-python-dateutil, 56 | types-setuptools, 57 | types-PyYAML, 58 | types-pytz, 59 | typing-extensions>=4.1.0, 60 | numpy, 61 | ] 62 | - repo: https://github.com/citation-file-format/cff-converter-python 63 | rev: ebf0b5e44d67f8beaa1cd13a0d0393ea04c6058d 64 | hooks: 65 | - id: validate-cff 66 | - repo: https://github.com/ComPWA/taplo-pre-commit 67 | rev: v0.9.3 68 | hooks: 69 | - id: taplo-format 70 | args: ["--option", "array_auto_collapse=false"] 71 | - repo: https://github.com/abravalheri/validate-pyproject 72 | rev: v0.24.1 73 | hooks: 74 | - id: validate-pyproject 75 | additional_dependencies: ["validate-pyproject-schema-store[all]"] 76 | - repo: https://github.com/crate-ci/typos 77 | rev: v1 78 | hooks: 79 | - id: typos 80 | # https://github.com/crate-ci/typos/issues/347 81 | pass_filenames: false 82 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | sphinx: 4 | configuration: doc/conf.py 5 | fail_on_warning: true 6 | 7 | build: 8 | os: ubuntu-lts-latest 9 | tools: 10 | python: mambaforge-latest 11 | jobs: 12 | post_checkout: 13 | - (git --no-pager log --pretty="tformat:%s" -1 | grep -vqF "[skip-rtd]") || exit 183 14 | - git fetch --unshallow || true 15 | pre_install: 16 | - git update-index --assume-unchanged doc/conf.py ci/requirements/doc.yml 17 | 18 | conda: 19 | environment: ci/requirements/doc.yml 20 | 21 | formats: [] 22 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | - Trolling, insulting/derogatory comments, and personal or political attacks 21 | - Public or private harassment 22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | - Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at xarray-core-team@googlegroups.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: https://contributor-covenant.org 46 | [version]: https://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Xarray's contributor guidelines [can be found in our online documentation](https://docs.xarray.dev/en/stable/contribute/contributing.html) 2 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/__init__.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | import os 3 | 4 | import numpy as np 5 | 6 | _counter = itertools.count() 7 | 8 | 9 | def parameterized(names, params): 10 | def decorator(func): 11 | func.param_names = names 12 | func.params = params 13 | return func 14 | 15 | return decorator 16 | 17 | 18 | def requires_dask(): 19 | try: 20 | import dask # noqa: F401 21 | except ImportError as err: 22 | raise NotImplementedError() from err 23 | 24 | 25 | def requires_sparse(): 26 | try: 27 | import sparse # noqa: F401 28 | except ImportError as err: 29 | raise NotImplementedError() from err 30 | 31 | 32 | def randn(shape, frac_nan=None, chunks=None, seed=0): 33 | rng = np.random.default_rng(seed) 34 | if chunks is None: 35 | x = rng.standard_normal(shape) 36 | else: 37 | import dask.array as da 38 | 39 | rng = da.random.default_rng(seed) 40 | x = rng.standard_normal(shape, chunks=chunks) 41 | 42 | if frac_nan is not None: 43 | inds = rng.choice(range(x.size), int(x.size * frac_nan)) 44 | x.flat[inds] = np.nan 45 | 46 | return x 47 | 48 | 49 | def randint(low, high=None, size=None, frac_minus=None, seed=0): 50 | rng = np.random.default_rng(seed) 51 | x = rng.integers(low, high, size) 52 | if frac_minus is not None: 53 | inds = rng.choice(range(x.size), int(x.size * frac_minus)) 54 | x.flat[inds] = -1 55 | 56 | return x 57 | 58 | 59 | def _skip_slow(): 60 | """ 61 | Use this function to skip slow or highly demanding tests. 62 | 63 | Use it as a `Class.setup` method or a `function.setup` attribute. 64 | 65 | Examples 66 | -------- 67 | >>> from . import _skip_slow 68 | >>> def time_something_slow(): 69 | ... pass 70 | ... 71 | >>> time_something.setup = _skip_slow 72 | """ 73 | if os.environ.get("ASV_SKIP_SLOW", "0") == "1": 74 | raise NotImplementedError("Skipping this test...") 75 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/accessors.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | import xarray as xr 4 | 5 | from . import parameterized 6 | 7 | NTIME = 365 * 30 8 | 9 | 10 | @parameterized(["calendar"], [("standard", "noleap")]) 11 | class DateTimeAccessor: 12 | def setup(self, calendar): 13 | np.random.randn(NTIME) 14 | time = xr.date_range("2000", periods=30 * 365, calendar=calendar) 15 | data = np.ones((NTIME,)) 16 | self.da = xr.DataArray(data, dims="time", coords={"time": time}) 17 | 18 | def time_dayofyear(self, calendar): 19 | _ = self.da.time.dt.dayofyear 20 | 21 | def time_year(self, calendar): 22 | _ = self.da.time.dt.year 23 | 24 | def time_floor(self, calendar): 25 | _ = self.da.time.dt.floor("D") 26 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/alignment.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | import xarray as xr 4 | 5 | from . import parameterized, requires_dask 6 | 7 | ntime = 365 * 30 8 | nx = 50 9 | ny = 50 10 | 11 | rng = np.random.default_rng(0) 12 | 13 | 14 | class Align: 15 | def setup(self, *args, **kwargs): 16 | data = rng.standard_normal((ntime, nx, ny)) 17 | self.ds = xr.Dataset( 18 | {"temperature": (("time", "x", "y"), data)}, 19 | coords={ 20 | "time": xr.date_range("2000", periods=ntime), 21 | "x": np.arange(nx), 22 | "y": np.arange(ny), 23 | }, 24 | ) 25 | self.year = self.ds.time.dt.year 26 | self.idx = np.unique(rng.integers(low=0, high=ntime, size=ntime // 2)) 27 | self.year_subset = self.year.isel(time=self.idx) 28 | 29 | @parameterized(["join"], [("outer", "inner", "left", "right", "exact", "override")]) 30 | def time_already_aligned(self, join): 31 | xr.align(self.ds, self.year, join=join) 32 | 33 | @parameterized(["join"], [("outer", "inner", "left", "right")]) 34 | def time_not_aligned(self, join): 35 | xr.align(self.ds, self.year[-100:], join=join) 36 | 37 | @parameterized(["join"], [("outer", "inner", "left", "right")]) 38 | def time_not_aligned_random_integers(self, join): 39 | xr.align(self.ds, self.year_subset, join=join) 40 | 41 | 42 | class AlignCFTime(Align): 43 | def setup(self, *args, **kwargs): 44 | super().setup() 45 | self.ds["time"] = xr.date_range("2000", periods=ntime, calendar="noleap") 46 | self.year = self.ds.time.dt.year 47 | self.year_subset = self.year.isel(time=self.idx) 48 | 49 | 50 | class AlignDask(Align): 51 | def setup(self, *args, **kwargs): 52 | requires_dask() 53 | super().setup() 54 | self.ds = self.ds.chunk({"time": 100}) 55 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/coding.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | import xarray as xr 4 | 5 | from . import parameterized 6 | 7 | 8 | @parameterized(["calendar"], [("standard", "noleap")]) 9 | class EncodeCFDatetime: 10 | def setup(self, calendar): 11 | self.units = "days since 2000-01-01" 12 | self.dtype = np.dtype("int64") 13 | self.times = xr.date_range( 14 | "2000", freq="D", periods=10000, calendar=calendar 15 | ).values 16 | 17 | def time_encode_cf_datetime(self, calendar): 18 | xr.coding.times.encode_cf_datetime(self.times, self.units, calendar, self.dtype) 19 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/combine.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | import xarray as xr 4 | 5 | from . import requires_dask 6 | 7 | 8 | class Combine1d: 9 | """Benchmark concatenating and merging large datasets""" 10 | 11 | def setup(self) -> None: 12 | """Create 2 datasets with two different variables""" 13 | 14 | t_size = 8000 15 | t = np.arange(t_size) 16 | data = np.random.randn(t_size) 17 | 18 | self.dsA0 = xr.Dataset({"A": xr.DataArray(data, coords={"T": t}, dims=("T"))}) 19 | self.dsA1 = xr.Dataset( 20 | {"A": xr.DataArray(data, coords={"T": t + t_size}, dims=("T"))} 21 | ) 22 | 23 | def time_combine_by_coords(self) -> None: 24 | """Also has to load and arrange t coordinate""" 25 | datasets = [self.dsA0, self.dsA1] 26 | 27 | xr.combine_by_coords(datasets) 28 | 29 | 30 | class Combine1dDask(Combine1d): 31 | """Benchmark concatenating and merging large datasets""" 32 | 33 | def setup(self) -> None: 34 | """Create 2 datasets with two different variables""" 35 | requires_dask() 36 | 37 | t_size = 8000 38 | t = np.arange(t_size) 39 | var = xr.Variable(dims=("T",), data=np.random.randn(t_size)).chunk() 40 | 41 | data_vars = {f"long_name_{v}": ("T", var) for v in range(500)} 42 | 43 | self.dsA0 = xr.Dataset(data_vars, coords={"T": t}) 44 | self.dsA1 = xr.Dataset(data_vars, coords={"T": t + t_size}) 45 | 46 | 47 | class Combine3d: 48 | """Benchmark concatenating and merging large datasets""" 49 | 50 | def setup(self): 51 | """Create 4 datasets with two different variables""" 52 | 53 | t_size, x_size, y_size = 50, 450, 400 54 | t = np.arange(t_size) 55 | data = np.random.randn(t_size, x_size, y_size) 56 | 57 | self.dsA0 = xr.Dataset( 58 | {"A": xr.DataArray(data, coords={"T": t}, dims=("T", "X", "Y"))} 59 | ) 60 | self.dsA1 = xr.Dataset( 61 | {"A": xr.DataArray(data, coords={"T": t + t_size}, dims=("T", "X", "Y"))} 62 | ) 63 | self.dsB0 = xr.Dataset( 64 | {"B": xr.DataArray(data, coords={"T": t}, dims=("T", "X", "Y"))} 65 | ) 66 | self.dsB1 = xr.Dataset( 67 | {"B": xr.DataArray(data, coords={"T": t + t_size}, dims=("T", "X", "Y"))} 68 | ) 69 | 70 | def time_combine_nested(self): 71 | datasets = [[self.dsA0, self.dsA1], [self.dsB0, self.dsB1]] 72 | 73 | xr.combine_nested(datasets, concat_dim=[None, "T"]) 74 | 75 | def time_combine_by_coords(self): 76 | """Also has to load and arrange t coordinate""" 77 | datasets = [self.dsA0, self.dsA1, self.dsB0, self.dsB1] 78 | 79 | xr.combine_by_coords(datasets) 80 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/dataarray_missing.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | import xarray as xr 4 | 5 | from . import parameterized, randn, requires_dask 6 | 7 | 8 | def make_bench_data(shape, frac_nan, chunks): 9 | vals = randn(shape, frac_nan) 10 | coords = {"time": pd.date_range("2000-01-01", freq="D", periods=shape[0])} 11 | da = xr.DataArray(vals, dims=("time", "x", "y"), coords=coords) 12 | 13 | if chunks is not None: 14 | da = da.chunk(chunks) 15 | 16 | return da 17 | 18 | 19 | class DataArrayMissingInterpolateNA: 20 | def setup(self, shape, chunks, limit): 21 | if chunks is not None: 22 | requires_dask() 23 | self.da = make_bench_data(shape, 0.1, chunks) 24 | 25 | @parameterized( 26 | ["shape", "chunks", "limit"], 27 | ( 28 | [(365, 75, 75)], 29 | [None, {"x": 25, "y": 25}], 30 | [None, 3], 31 | ), 32 | ) 33 | def time_interpolate_na(self, shape, chunks, limit): 34 | actual = self.da.interpolate_na(dim="time", method="linear", limit=limit) 35 | 36 | if chunks is not None: 37 | actual = actual.compute() 38 | 39 | 40 | class DataArrayMissingBottleneck: 41 | def setup(self, shape, chunks, limit): 42 | if chunks is not None: 43 | requires_dask() 44 | self.da = make_bench_data(shape, 0.1, chunks) 45 | 46 | @parameterized( 47 | ["shape", "chunks", "limit"], 48 | ( 49 | [(365, 75, 75)], 50 | [None, {"x": 25, "y": 25}], 51 | [None, 3], 52 | ), 53 | ) 54 | def time_ffill(self, shape, chunks, limit): 55 | actual = self.da.ffill(dim="time", limit=limit) 56 | 57 | if chunks is not None: 58 | actual = actual.compute() 59 | 60 | @parameterized( 61 | ["shape", "chunks", "limit"], 62 | ( 63 | [(365, 75, 75)], 64 | [None, {"x": 25, "y": 25}], 65 | [None, 3], 66 | ), 67 | ) 68 | def time_bfill(self, shape, chunks, limit): 69 | actual = self.da.bfill(dim="time", limit=limit) 70 | 71 | if chunks is not None: 72 | actual = actual.compute() 73 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/dataset.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from xarray import Dataset 4 | 5 | from . import requires_dask 6 | 7 | 8 | class DatasetBinaryOp: 9 | def setup(self): 10 | self.ds = Dataset( 11 | { 12 | "a": (("x", "y"), np.ones((300, 400))), 13 | "b": (("x", "y"), np.ones((300, 400))), 14 | } 15 | ) 16 | self.mean = self.ds.mean() 17 | self.std = self.ds.std() 18 | 19 | def time_normalize(self): 20 | (self.ds - self.mean) / self.std 21 | 22 | 23 | class DatasetChunk: 24 | def setup(self): 25 | requires_dask() 26 | self.ds = Dataset() 27 | array = np.ones(1000) 28 | for i in range(250): 29 | self.ds[f"var{i}"] = ("x", array) 30 | 31 | def time_chunk(self): 32 | self.ds.chunk(x=(1,) * 1000) 33 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/datatree.py: -------------------------------------------------------------------------------- 1 | import xarray as xr 2 | from xarray.core.datatree import DataTree 3 | 4 | 5 | class Datatree: 6 | def setup(self): 7 | run1 = DataTree.from_dict({"run1": xr.Dataset({"a": 1})}) 8 | self.d_few = {"run1": run1} 9 | self.d_many = {f"run{i}": xr.Dataset({"a": 1}) for i in range(100)} 10 | 11 | def time_from_dict_few(self): 12 | DataTree.from_dict(self.d_few) 13 | 14 | def time_from_dict_many(self): 15 | DataTree.from_dict(self.d_many) 16 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/import.py: -------------------------------------------------------------------------------- 1 | class Import: 2 | """Benchmark importing xarray""" 3 | 4 | def timeraw_import_xarray(self): 5 | return "import xarray" 6 | 7 | def timeraw_import_xarray_plot(self): 8 | return "import xarray.plot" 9 | 10 | def timeraw_import_xarray_backends(self): 11 | return """ 12 | from xarray.backends import list_engines 13 | list_engines() 14 | """ 15 | 16 | def timeraw_import_xarray_only(self): 17 | # import numpy and pandas in the setup stage 18 | return "import xarray", "import numpy, pandas" 19 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/interp.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | 4 | import xarray as xr 5 | 6 | from . import parameterized, randn, requires_dask 7 | 8 | nx = 1500 9 | ny = 1000 10 | nt = 500 11 | 12 | randn_xy = randn((nx, ny), frac_nan=0.1) 13 | randn_xt = randn((nx, nt)) 14 | randn_t = randn((nt,)) 15 | 16 | new_x_short = np.linspace(0.3 * nx, 0.7 * nx, 100) 17 | new_x_long = np.linspace(0.3 * nx, 0.7 * nx, 500) 18 | new_y_long = np.linspace(0.1, 0.9, 500) 19 | 20 | 21 | class Interpolation: 22 | def setup(self, *args, **kwargs): 23 | self.ds = xr.Dataset( 24 | { 25 | "var1": (("x", "y"), randn_xy), 26 | "var2": (("x", "t"), randn_xt), 27 | "var3": (("t",), randn_t), 28 | }, 29 | coords={ 30 | "x": np.arange(nx), 31 | "y": np.linspace(0, 1, ny), 32 | "t": pd.date_range("1970-01-01", periods=nt, freq="D"), 33 | "x_coords": ("x", np.linspace(1.1, 2.1, nx)), 34 | }, 35 | ) 36 | 37 | @parameterized(["method", "is_short"], (["linear", "cubic"], [True, False])) 38 | def time_interpolation(self, method, is_short): 39 | new_x = new_x_short if is_short else new_x_long 40 | self.ds.interp(x=new_x, method=method).load() 41 | 42 | @parameterized(["method"], (["linear", "nearest"])) 43 | def time_interpolation_2d(self, method): 44 | self.ds.interp(x=new_x_long, y=new_y_long, method=method).load() 45 | 46 | 47 | class InterpolationDask(Interpolation): 48 | def setup(self, *args, **kwargs): 49 | requires_dask() 50 | super().setup(**kwargs) 51 | self.ds = self.ds.chunk({"t": 50}) 52 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/merge.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | import xarray as xr 4 | 5 | 6 | class DatasetAddVariable: 7 | param_names = ["existing_elements"] 8 | params = [[0, 10, 100, 1000]] 9 | 10 | def setup(self, existing_elements): 11 | self.datasets = {} 12 | # Dictionary insertion is fast(er) than xarray.Dataset insertion 13 | d = {} 14 | for i in range(existing_elements): 15 | d[f"var{i}"] = i 16 | self.dataset = xr.merge([d]) 17 | 18 | d = {f"set_2_{i}": i for i in range(existing_elements)} 19 | self.dataset2 = xr.merge([d]) 20 | 21 | def time_variable_insertion(self, existing_elements): 22 | dataset = self.dataset 23 | dataset["new_var"] = 0 24 | 25 | def time_merge_two_datasets(self, existing_elements): 26 | xr.merge([self.dataset, self.dataset2]) 27 | 28 | 29 | class DatasetCreation: 30 | # The idea here is to time how long it takes to go from numpy 31 | # and python data types, to a full dataset 32 | # See discussion 33 | # https://github.com/pydata/xarray/issues/7224#issuecomment-1292216344 34 | param_names = ["strategy", "count"] 35 | params = [ 36 | ["dict_of_DataArrays", "dict_of_Variables", "dict_of_Tuples"], 37 | [0, 1, 10, 100, 1000], 38 | ] 39 | 40 | def setup(self, strategy, count): 41 | data = np.array(["0", "b"], dtype=str) 42 | self.dataset_coords = dict(time=np.array([0, 1])) 43 | self.dataset_attrs = dict(description="Test data") 44 | attrs = dict(units="Celsius") 45 | if strategy == "dict_of_DataArrays": 46 | 47 | def create_data_vars(): 48 | return { 49 | f"long_variable_name_{i}": xr.DataArray( 50 | data=data, dims=("time"), attrs=attrs 51 | ) 52 | for i in range(count) 53 | } 54 | 55 | elif strategy == "dict_of_Variables": 56 | 57 | def create_data_vars(): 58 | return { 59 | f"long_variable_name_{i}": xr.Variable("time", data, attrs=attrs) 60 | for i in range(count) 61 | } 62 | 63 | elif strategy == "dict_of_Tuples": 64 | 65 | def create_data_vars(): 66 | return { 67 | f"long_variable_name_{i}": ("time", data, attrs) 68 | for i in range(count) 69 | } 70 | 71 | self.create_data_vars = create_data_vars 72 | 73 | def time_dataset_creation(self, strategy, count): 74 | data_vars = self.create_data_vars() 75 | xr.Dataset( 76 | data_vars=data_vars, coords=self.dataset_coords, attrs=self.dataset_attrs 77 | ) 78 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/pandas.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | 4 | import xarray as xr 5 | 6 | from . import parameterized, requires_dask 7 | 8 | 9 | class MultiIndexSeries: 10 | def setup(self, dtype, subset): 11 | data = np.random.rand(100000).astype(dtype) 12 | index = pd.MultiIndex.from_product( 13 | [ 14 | list("abcdefhijk"), 15 | list("abcdefhijk"), 16 | pd.date_range(start="2000-01-01", periods=1000, freq="D"), 17 | ] 18 | ) 19 | series = pd.Series(data, index) 20 | if subset: 21 | series = series[::3] 22 | self.series = series 23 | 24 | @parameterized(["dtype", "subset"], ([int, float], [True, False])) 25 | def time_from_series(self, dtype, subset): 26 | xr.DataArray.from_series(self.series) 27 | 28 | 29 | class ToDataFrame: 30 | def setup(self, *args, **kwargs): 31 | xp = kwargs.get("xp", np) 32 | nvars = kwargs.get("nvars", 1) 33 | random_kws = kwargs.get("random_kws", {}) 34 | method = kwargs.get("method", "to_dataframe") 35 | 36 | dim1 = 10_000 37 | dim2 = 10_000 38 | 39 | var = xr.Variable( 40 | dims=("dim1", "dim2"), data=xp.random.random((dim1, dim2), **random_kws) 41 | ) 42 | data_vars = {f"long_name_{v}": (("dim1", "dim2"), var) for v in range(nvars)} 43 | 44 | ds = xr.Dataset( 45 | data_vars, coords={"dim1": np.arange(0, dim1), "dim2": np.arange(0, dim2)} 46 | ) 47 | self.to_frame = getattr(ds, method) 48 | 49 | def time_to_dataframe(self): 50 | self.to_frame() 51 | 52 | def peakmem_to_dataframe(self): 53 | self.to_frame() 54 | 55 | 56 | class ToDataFrameDask(ToDataFrame): 57 | def setup(self, *args, **kwargs): 58 | requires_dask() 59 | 60 | import dask.array as da 61 | 62 | super().setup( 63 | xp=da, random_kws=dict(chunks=5000), method="to_dask_dataframe", nvars=500 64 | ) 65 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/polyfit.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | import xarray as xr 4 | 5 | from . import parameterized, randn, requires_dask 6 | 7 | NDEGS = (2, 5, 20) 8 | NX = (10**2, 10**6) 9 | 10 | 11 | class Polyval: 12 | def setup(self, *args, **kwargs): 13 | self.xs = {nx: xr.DataArray(randn((nx,)), dims="x", name="x") for nx in NX} 14 | self.coeffs = { 15 | ndeg: xr.DataArray( 16 | randn((ndeg,)), dims="degree", coords={"degree": np.arange(ndeg)} 17 | ) 18 | for ndeg in NDEGS 19 | } 20 | 21 | @parameterized(["nx", "ndeg"], [NX, NDEGS]) 22 | def time_polyval(self, nx, ndeg): 23 | x = self.xs[nx] 24 | c = self.coeffs[ndeg] 25 | xr.polyval(x, c).compute() 26 | 27 | @parameterized(["nx", "ndeg"], [NX, NDEGS]) 28 | def peakmem_polyval(self, nx, ndeg): 29 | x = self.xs[nx] 30 | c = self.coeffs[ndeg] 31 | xr.polyval(x, c).compute() 32 | 33 | 34 | class PolyvalDask(Polyval): 35 | def setup(self, *args, **kwargs): 36 | requires_dask() 37 | super().setup(*args, **kwargs) 38 | self.xs = {k: v.chunk({"x": 10000}) for k, v in self.xs.items()} 39 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/reindexing.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | import xarray as xr 4 | 5 | from . import requires_dask 6 | 7 | ntime = 500 8 | nx = 50 9 | ny = 50 10 | 11 | 12 | class Reindex: 13 | def setup(self): 14 | data = np.random.default_rng(0).random((ntime, nx, ny)) 15 | self.ds = xr.Dataset( 16 | {"temperature": (("time", "x", "y"), data)}, 17 | coords={"time": np.arange(ntime), "x": np.arange(nx), "y": np.arange(ny)}, 18 | ) 19 | 20 | def time_1d_coarse(self): 21 | self.ds.reindex(time=np.arange(0, ntime, 5)).load() 22 | 23 | def time_1d_fine_all_found(self): 24 | self.ds.reindex(time=np.arange(0, ntime, 0.5), method="nearest").load() 25 | 26 | def time_1d_fine_some_missing(self): 27 | self.ds.reindex( 28 | time=np.arange(0, ntime, 0.5), method="nearest", tolerance=0.1 29 | ).load() 30 | 31 | def time_2d_coarse(self): 32 | self.ds.reindex(x=np.arange(0, nx, 2), y=np.arange(0, ny, 2)).load() 33 | 34 | def time_2d_fine_all_found(self): 35 | self.ds.reindex( 36 | x=np.arange(0, nx, 0.5), y=np.arange(0, ny, 0.5), method="nearest" 37 | ).load() 38 | 39 | def time_2d_fine_some_missing(self): 40 | self.ds.reindex( 41 | x=np.arange(0, nx, 0.5), 42 | y=np.arange(0, ny, 0.5), 43 | method="nearest", 44 | tolerance=0.1, 45 | ).load() 46 | 47 | 48 | class ReindexDask(Reindex): 49 | def setup(self): 50 | requires_dask() 51 | super().setup() 52 | self.ds = self.ds.chunk({"time": 100}) 53 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/renaming.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | import xarray as xr 4 | 5 | 6 | class SwapDims: 7 | param_names = ["size"] 8 | params = [[int(1e3), int(1e5), int(1e7)]] 9 | 10 | def setup(self, size: int) -> None: 11 | self.ds = xr.Dataset( 12 | {"a": (("x", "t"), np.ones((size, 2)))}, 13 | coords={ 14 | "x": np.arange(size), 15 | "y": np.arange(size), 16 | "z": np.arange(size), 17 | "x2": ("x", np.arange(size)), 18 | "y2": ("y", np.arange(size)), 19 | "z2": ("z", np.arange(size)), 20 | }, 21 | ) 22 | 23 | def time_swap_dims(self, size: int) -> None: 24 | self.ds.swap_dims({"x": "xn", "y": "yn", "z": "zn"}) 25 | 26 | def time_swap_dims_newindex(self, size: int) -> None: 27 | self.ds.swap_dims({"x": "x2", "y": "y2", "z": "z2"}) 28 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/repr.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | 4 | import xarray as xr 5 | 6 | 7 | class Repr: 8 | def setup(self): 9 | a = np.arange(0, 100) 10 | data_vars = dict() 11 | for i in a: 12 | data_vars[f"long_variable_name_{i}"] = xr.DataArray( 13 | name=f"long_variable_name_{i}", 14 | data=np.arange(0, 20), 15 | dims=[f"long_coord_name_{i}_x"], 16 | coords={f"long_coord_name_{i}_x": np.arange(0, 20) * 2}, 17 | ) 18 | self.ds = xr.Dataset(data_vars) 19 | self.ds.attrs = {f"attr_{k}": 2 for k in a} 20 | 21 | def time_repr(self): 22 | repr(self.ds) 23 | 24 | def time_repr_html(self): 25 | self.ds._repr_html_() 26 | 27 | 28 | class ReprDataTree: 29 | def setup(self): 30 | # construct a datatree with 500 nodes 31 | number_of_files = 20 32 | number_of_groups = 25 33 | tree_dict = {} 34 | for f in range(number_of_files): 35 | for g in range(number_of_groups): 36 | tree_dict[f"file_{f}/group_{g}"] = xr.Dataset({"g": f * g}) 37 | 38 | self.dt = xr.DataTree.from_dict(tree_dict) 39 | 40 | def time_repr(self): 41 | repr(self.dt) 42 | 43 | def time_repr_html(self): 44 | self.dt._repr_html_() 45 | 46 | 47 | class ReprMultiIndex: 48 | def setup(self): 49 | index = pd.MultiIndex.from_product( 50 | [range(1000), range(1000)], names=("level_0", "level_1") 51 | ) 52 | series = pd.Series(range(1000 * 1000), index=index) 53 | self.da = xr.DataArray(series) 54 | 55 | def time_repr(self): 56 | repr(self.da) 57 | 58 | def time_repr_html(self): 59 | self.da._repr_html_() 60 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/unstacking.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | 4 | import xarray as xr 5 | 6 | from . import requires_dask, requires_sparse 7 | 8 | 9 | class Unstacking: 10 | def setup(self): 11 | data = np.random.default_rng(0).random((250, 500)) 12 | self.da_full = xr.DataArray(data, dims=list("ab")).stack(flat_dim=[...]) 13 | self.da_missing = self.da_full[:-1] 14 | self.df_missing = self.da_missing.to_pandas() 15 | 16 | def time_unstack_fast(self): 17 | self.da_full.unstack("flat_dim") 18 | 19 | def time_unstack_slow(self): 20 | self.da_missing.unstack("flat_dim") 21 | 22 | def time_unstack_pandas_slow(self): 23 | self.df_missing.unstack() 24 | 25 | 26 | class UnstackingDask(Unstacking): 27 | def setup(self, *args, **kwargs): 28 | requires_dask() 29 | super().setup(**kwargs) 30 | self.da_full = self.da_full.chunk({"flat_dim": 25}) 31 | 32 | 33 | class UnstackingSparse(Unstacking): 34 | def setup(self, *args, **kwargs): 35 | requires_sparse() 36 | 37 | import sparse 38 | 39 | data = sparse.random((500, 1000), random_state=0, fill_value=0) 40 | self.da_full = xr.DataArray(data, dims=list("ab")).stack(flat_dim=[...]) 41 | self.da_missing = self.da_full[:-1] 42 | 43 | mindex = pd.MultiIndex.from_arrays([np.arange(100), np.arange(100)]) 44 | self.da_eye_2d = xr.DataArray(np.ones((100,)), dims="z", coords={"z": mindex}) 45 | self.da_eye_3d = xr.DataArray( 46 | np.ones((100, 50)), 47 | dims=("z", "foo"), 48 | coords={"z": mindex, "foo": np.arange(50)}, 49 | ) 50 | 51 | def time_unstack_to_sparse_2d(self): 52 | self.da_eye_2d.unstack(sparse=True) 53 | 54 | def time_unstack_to_sparse_3d(self): 55 | self.da_eye_3d.unstack(sparse=True) 56 | 57 | def peakmem_unstack_to_sparse_2d(self): 58 | self.da_eye_2d.unstack(sparse=True) 59 | 60 | def peakmem_unstack_to_sparse_3d(self): 61 | self.da_eye_3d.unstack(sparse=True) 62 | 63 | def time_unstack_pandas_slow(self): 64 | pass 65 | -------------------------------------------------------------------------------- /ci/install-upstream-wheels.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if which micromamba >/dev/null; then 4 | conda=micromamba 5 | elif which mamba >/dev/null; then 6 | conda=mamba 7 | else 8 | conda=conda 9 | fi 10 | 11 | # temporarily (?) remove numbagg and numba 12 | $conda remove -y numba numbagg sparse 13 | # temporarily remove numexpr 14 | $conda remove -y numexpr 15 | # forcibly remove packages to avoid artifacts 16 | $conda remove -y --force \ 17 | numpy \ 18 | scipy \ 19 | pandas \ 20 | distributed \ 21 | fsspec \ 22 | zarr \ 23 | cftime \ 24 | packaging \ 25 | bottleneck \ 26 | flox 27 | # pint 28 | 29 | # to limit the runtime of Upstream CI 30 | python -m pip install \ 31 | -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple \ 32 | --no-deps \ 33 | --pre \ 34 | --upgrade \ 35 | numpy \ 36 | scipy \ 37 | matplotlib \ 38 | pandas 39 | # for some reason pandas depends on pyarrow already. 40 | # Remove once a `pyarrow` version compiled with `numpy>=2.0` is on `conda-forge` 41 | python -m pip install \ 42 | -i https://pypi.fury.io/arrow-nightlies/ \ 43 | --prefer-binary \ 44 | --no-deps \ 45 | --pre \ 46 | --upgrade \ 47 | pyarrow 48 | # manually install `pint`, `donfig`, and `crc32c` to pull in new dependencies 49 | python -m pip install --upgrade pint donfig crc32c 50 | python -m pip install \ 51 | --no-deps \ 52 | --upgrade \ 53 | git+https://github.com/dask/dask \ 54 | git+https://github.com/dask/dask-expr \ 55 | git+https://github.com/dask/distributed \ 56 | git+https://github.com/zarr-developers/zarr-python \ 57 | git+https://github.com/Unidata/cftime \ 58 | git+https://github.com/pypa/packaging \ 59 | git+https://github.com/hgrecco/pint \ 60 | git+https://github.com/pydata/bottleneck \ 61 | git+https://github.com/intake/filesystem_spec \ 62 | git+https://github.com/SciTools/nc-time-axis \ 63 | git+https://github.com/xarray-contrib/flox \ 64 | git+https://github.com/h5netcdf/h5netcdf \ 65 | git+https://github.com/dgasmith/opt_einsum 66 | # git+https://github.com/pydata/sparse 67 | -------------------------------------------------------------------------------- /ci/release_contributors.py: -------------------------------------------------------------------------------- 1 | import re 2 | import textwrap 3 | 4 | import git 5 | from tlz.itertoolz import last, unique 6 | 7 | co_author_re = re.compile(r"Co-authored-by: (?P[^<]+?) <(?P.+)>") 8 | 9 | 10 | def main(): 11 | repo = git.Repo(".") 12 | 13 | most_recent_release = last(repo.tags) 14 | 15 | # extract information from commits 16 | contributors = {} 17 | for commit in repo.iter_commits(f"{most_recent_release.name}.."): 18 | matches = co_author_re.findall(commit.message) 19 | if matches: 20 | contributors.update({email: name for name, email in matches}) 21 | contributors[commit.author.email] = commit.author.name 22 | 23 | # deduplicate and ignore 24 | # TODO: extract ignores from .github/release.yml 25 | ignored = ["dependabot", "pre-commit-ci"] 26 | unique_contributors = unique( 27 | contributor 28 | for contributor in contributors.values() 29 | if contributor.removesuffix("[bot]") not in ignored 30 | ) 31 | 32 | sorted_ = sorted(unique_contributors) 33 | if len(sorted_) > 1: 34 | names = f"{', '.join(sorted_[:-1])} and {sorted_[-1]}" 35 | else: 36 | names = "".join(sorted_) 37 | 38 | statement = textwrap.dedent( 39 | f"""\ 40 | Thanks to the {len(sorted_)} contributors to this release: 41 | {names} 42 | """.rstrip() 43 | ) 44 | 45 | print(statement) 46 | 47 | 48 | if __name__ == "__main__": 49 | main() 50 | -------------------------------------------------------------------------------- /ci/requirements/all-but-dask.yml: -------------------------------------------------------------------------------- 1 | name: xarray-tests 2 | channels: 3 | - conda-forge 4 | - nodefaults 5 | dependencies: 6 | - aiobotocore 7 | - array-api-strict 8 | - boto3 9 | - bottleneck 10 | - cartopy 11 | - cftime 12 | - coveralls 13 | - flox 14 | - h5netcdf 15 | - h5py 16 | - hdf5 17 | - hypothesis 18 | - lxml # Optional dep of pydap 19 | - matplotlib-base 20 | - nc-time-axis 21 | - netcdf4 22 | - numba 23 | - numbagg 24 | - numpy 25 | - packaging 26 | - pandas 27 | - pint>=0.22 28 | - pip 29 | - pydap 30 | - pytest 31 | - pytest-cov 32 | - pytest-env 33 | - pytest-mypy-plugins 34 | - pytest-timeout 35 | - pytest-xdist 36 | - rasterio 37 | - scipy 38 | - seaborn 39 | - sparse 40 | - toolz 41 | - typing_extensions 42 | - zarr 43 | -------------------------------------------------------------------------------- /ci/requirements/all-but-numba.yml: -------------------------------------------------------------------------------- 1 | name: xarray-tests 2 | channels: 3 | - conda-forge 4 | - nodefaults 5 | dependencies: 6 | # Pin a "very new numpy" (updated Sept 24, 2024) 7 | - numpy>=2.1.1 8 | - aiobotocore 9 | - array-api-strict 10 | - boto3 11 | - bottleneck 12 | - cartopy 13 | - cftime 14 | - dask-core 15 | - distributed 16 | - flox 17 | - fsspec 18 | - h5netcdf 19 | - h5py 20 | - hdf5 21 | - hypothesis 22 | - iris 23 | - lxml # Optional dep of pydap 24 | - matplotlib-base 25 | - nc-time-axis 26 | - netcdf4 27 | # numba, sparse, numbagg, numexpr often conflicts with newer versions of numpy. 28 | # This environment helps us test xarray with the latest versions 29 | # of numpy 30 | # - numba 31 | # - numbagg 32 | # - numexpr 33 | # - sparse 34 | - opt_einsum 35 | - packaging 36 | - pandas 37 | # - pint>=0.22 38 | - pip 39 | - pooch 40 | - pre-commit 41 | - pyarrow # pandas raises a deprecation warning without this, breaking doctests 42 | - pydap 43 | - pytest 44 | - pytest-cov 45 | - pytest-env 46 | - pytest-mypy-plugins 47 | - pytest-timeout 48 | - pytest-xdist 49 | - rasterio 50 | - scipy 51 | - seaborn 52 | - toolz 53 | - typing_extensions 54 | - zarr 55 | -------------------------------------------------------------------------------- /ci/requirements/bare-minimum.yml: -------------------------------------------------------------------------------- 1 | name: xarray-tests 2 | channels: 3 | - conda-forge 4 | - nodefaults 5 | dependencies: 6 | - python=3.10 7 | - coveralls 8 | - pip 9 | - pytest 10 | - pytest-cov 11 | - pytest-env 12 | - pytest-mypy-plugins 13 | - pytest-timeout 14 | - pytest-xdist 15 | - numpy=1.24 16 | - packaging=23.1 17 | - pandas=2.1 18 | -------------------------------------------------------------------------------- /ci/requirements/doc.yml: -------------------------------------------------------------------------------- 1 | name: xarray-docs 2 | channels: 3 | # Don't change to pkgs/main, as it causes random timeouts in readthedocs 4 | - conda-forge 5 | - nodefaults 6 | dependencies: 7 | - python 8 | - bottleneck 9 | - cartopy 10 | - cfgrib 11 | - kerchunk 12 | - dask-core 13 | - hypothesis 14 | - h5netcdf 15 | - ipykernel 16 | - ipywidgets # silence nbsphinx warning 17 | - ipython 18 | - iris 19 | - jupyter_client 20 | - jupyter_sphinx 21 | - matplotlib-base 22 | - nbsphinx 23 | - ncdata 24 | - netcdf4 25 | - numba 26 | - numpy>=2 27 | - packaging 28 | - pandas 29 | - pooch 30 | - pip 31 | - pre-commit 32 | - pyarrow 33 | - pydata-sphinx-theme 34 | - pyproj 35 | - rich # for Zarr tree() 36 | - scipy 37 | - seaborn 38 | - setuptools 39 | - sparse 40 | - sphinx-autosummary-accessors 41 | - sphinx-copybutton 42 | - sphinx-design 43 | - sphinx-inline-tabs 44 | - sphinx>=6,<8 45 | - sphinxcontrib-mermaid 46 | - sphinxcontrib-srclinks 47 | - sphinx-remove-toctrees 48 | - sphinxext-opengraph 49 | - sphinxext-rediraffe 50 | - zarr 51 | - pip: 52 | # relative to this file. Needs to be editable to be accepted. 53 | - -e ../.. 54 | -------------------------------------------------------------------------------- /ci/requirements/environment-3.14.yml: -------------------------------------------------------------------------------- 1 | name: xarray-tests 2 | channels: 3 | - conda-forge 4 | - nodefaults 5 | dependencies: 6 | - aiobotocore 7 | - array-api-strict 8 | - boto3 9 | - bottleneck 10 | - cartopy 11 | - cftime 12 | - dask-core 13 | - distributed 14 | - flox 15 | - fsspec 16 | - h5netcdf 17 | - h5py 18 | - hdf5 19 | - hypothesis 20 | - iris 21 | - lxml # Optional dep of pydap 22 | - matplotlib-base 23 | - nc-time-axis 24 | - netcdf4 25 | # - numba 26 | # - numbagg 27 | - numexpr 28 | - numpy 29 | - opt_einsum 30 | - packaging 31 | - pandas 32 | - pandas-stubs<=2.2.3.241126 # https://github.com/pydata/xarray/issues/10110 33 | # - pint>=0.22 34 | - pip 35 | - pooch 36 | - pre-commit 37 | - pyarrow # pandas raises a deprecation warning without this, breaking doctests 38 | - pydap 39 | - pytest 40 | - pytest-cov 41 | - pytest-env 42 | - pytest-mypy-plugins 43 | - pytest-timeout 44 | - pytest-xdist 45 | - rasterio 46 | - scipy 47 | - seaborn 48 | # - sparse 49 | - toolz 50 | - types-colorama 51 | - types-docutils 52 | - types-psutil 53 | - types-Pygments 54 | - types-python-dateutil 55 | - types-pytz 56 | - types-PyYAML 57 | - types-setuptools 58 | - typing_extensions 59 | - zarr 60 | - pip: 61 | - jax # no way to get cpu-only jaxlib from conda if gpu is present 62 | - types-defusedxml 63 | - types-pexpect 64 | -------------------------------------------------------------------------------- /ci/requirements/environment-windows-3.14.yml: -------------------------------------------------------------------------------- 1 | name: xarray-tests 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - array-api-strict 6 | - boto3 7 | - bottleneck 8 | - cartopy 9 | - cftime 10 | - dask-core 11 | - distributed 12 | - flox 13 | - fsspec 14 | - h5netcdf 15 | - h5py 16 | - hdf5 17 | - hypothesis 18 | - iris 19 | - lxml # Optional dep of pydap 20 | - matplotlib-base 21 | - nc-time-axis 22 | - netcdf4 23 | # - numba 24 | # - numbagg 25 | - numpy 26 | - packaging 27 | - pandas 28 | - pandas-stubs<=2.2.3.241126 # https://github.com/pydata/xarray/issues/10110 29 | # - pint>=0.22 30 | - pip 31 | - pre-commit 32 | - pyarrow # importing dask.dataframe raises an ImportError without this 33 | - pydap 34 | - pytest 35 | - pytest-cov 36 | - pytest-env 37 | - pytest-mypy-plugins 38 | - pytest-timeout 39 | - pytest-xdist 40 | - rasterio 41 | - scipy 42 | - seaborn 43 | # - sparse 44 | - toolz 45 | - types-colorama 46 | - types-docutils 47 | - types-psutil 48 | - types-Pygments 49 | - types-python-dateutil 50 | - types-pytz 51 | - types-PyYAML 52 | - types-setuptools 53 | - typing_extensions 54 | - zarr 55 | - pip: 56 | - types-defusedxml 57 | - types-pexpect 58 | -------------------------------------------------------------------------------- /ci/requirements/environment-windows.yml: -------------------------------------------------------------------------------- 1 | name: xarray-tests 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - array-api-strict 6 | - boto3 7 | - bottleneck 8 | - cartopy 9 | - cftime 10 | - dask-core 11 | - distributed 12 | - flox 13 | - fsspec 14 | - h5netcdf 15 | - h5py 16 | - hdf5 17 | - hypothesis 18 | - iris 19 | - lxml # Optional dep of pydap 20 | - matplotlib-base 21 | - nc-time-axis 22 | - netcdf4 23 | - numba 24 | - numbagg 25 | - numpy 26 | - packaging 27 | - pandas 28 | - pandas-stubs<=2.2.3.241126 # https://github.com/pydata/xarray/issues/10110 29 | # - pint>=0.22 30 | - pip 31 | - pre-commit 32 | - pyarrow # importing dask.dataframe raises an ImportError without this 33 | - pydap 34 | - pytest 35 | - pytest-cov 36 | - pytest-env 37 | - pytest-mypy-plugins 38 | - pytest-timeout 39 | - pytest-xdist 40 | - rasterio 41 | - scipy 42 | - seaborn 43 | - sparse 44 | - toolz 45 | - types-colorama 46 | - types-docutils 47 | - types-psutil 48 | - types-Pygments 49 | - types-python-dateutil 50 | - types-pytz 51 | - types-PyYAML 52 | - types-setuptools 53 | - typing_extensions 54 | - zarr 55 | - pip: 56 | - types-defusedxml 57 | - types-pexpect 58 | -------------------------------------------------------------------------------- /ci/requirements/environment.yml: -------------------------------------------------------------------------------- 1 | name: xarray-tests 2 | channels: 3 | - conda-forge 4 | - nodefaults 5 | dependencies: 6 | - aiobotocore 7 | - array-api-strict 8 | - boto3 9 | - bottleneck 10 | - cartopy 11 | - cftime 12 | - dask-core 13 | - distributed 14 | - flox 15 | - fsspec 16 | - h5netcdf 17 | - h5py 18 | - hdf5 19 | - hypothesis 20 | - iris 21 | - lxml # Optional dep of pydap 22 | - matplotlib-base 23 | - mypy==1.15 # mypy 1.16 breaks CI 24 | - nc-time-axis 25 | - netcdf4 26 | - numba 27 | - numbagg 28 | - numexpr 29 | - numpy>=2 30 | - opt_einsum 31 | - packaging 32 | - pandas 33 | - pandas-stubs<=2.2.3.241126 # https://github.com/pydata/xarray/issues/10110 34 | # - pint>=0.22 35 | - pip 36 | - pooch 37 | - pre-commit 38 | - pyarrow # pandas raises a deprecation warning without this, breaking doctests 39 | - pydap 40 | - pydap-server 41 | - pytest 42 | - pytest-cov 43 | - pytest-env 44 | - pytest-mypy-plugins 45 | - pytest-timeout 46 | - pytest-xdist 47 | - rasterio 48 | - scipy 49 | - seaborn 50 | - sparse 51 | - toolz 52 | - types-colorama 53 | - types-docutils 54 | - types-psutil 55 | - types-Pygments 56 | - types-python-dateutil 57 | - types-pytz 58 | - types-PyYAML 59 | - types-setuptools 60 | - types-openpyxl 61 | - typing_extensions 62 | - zarr 63 | - pip: 64 | - jax # no way to get cpu-only jaxlib from conda if gpu is present 65 | - types-defusedxml 66 | - types-pexpect 67 | -------------------------------------------------------------------------------- /ci/requirements/min-all-deps.yml: -------------------------------------------------------------------------------- 1 | name: xarray-tests 2 | channels: 3 | - conda-forge 4 | - nodefaults 5 | dependencies: 6 | # MINIMUM VERSIONS POLICY: see doc/user-guide/installing.rst 7 | # Run ci/min_deps_check.py to verify that this file respects the policy. 8 | # When upgrading python, numpy, or pandas, must also change 9 | # doc/user-guide/installing.rst, doc/user-guide/plotting.rst and setup.py. 10 | - python=3.10 11 | - array-api-strict=1.0 # dependency for testing the array api compat 12 | - boto3=1.29 13 | - bottleneck=1.3 14 | - cartopy=0.22 15 | - cftime=1.6 16 | - coveralls 17 | - dask-core=2023.11 18 | - distributed=2023.11 19 | # Flox > 0.8 has a bug with numbagg versions 20 | # It will require numbagg > 0.6 21 | # so we should just skip that series eventually 22 | # or keep flox pinned for longer than necessary 23 | - flox=0.7 24 | - h5netcdf=1.3 25 | # h5py and hdf5 tend to cause conflicts 26 | # for e.g. hdf5 1.12 conflicts with h5py=3.1 27 | # prioritize bumping other packages instead 28 | - h5py=3.8 29 | - hdf5=1.12 30 | - hypothesis 31 | - iris=3.7 32 | - lxml=4.9 # Optional dep of pydap 33 | - matplotlib-base=3.8 34 | - nc-time-axis=1.4 35 | # netcdf follows a 1.major.minor[.patch] convention 36 | # (see https://github.com/Unidata/netcdf4-python/issues/1090) 37 | - netcdf4=1.6.0 38 | - numba=0.57 39 | - numbagg=0.6 40 | - numpy=1.24 41 | - packaging=23.2 42 | - pandas=2.1 43 | - pint=0.22 44 | - pip 45 | - pydap=3.5 46 | - pytest 47 | - pytest-cov 48 | - pytest-env 49 | - pytest-mypy-plugins 50 | - pytest-timeout 51 | - pytest-xdist 52 | - rasterio=1.3 53 | - scipy=1.11 54 | - seaborn=0.13 55 | - sparse=0.14 56 | - toolz=0.12 57 | - typing_extensions=4.8 58 | - zarr=2.16 59 | -------------------------------------------------------------------------------- /conftest.py: -------------------------------------------------------------------------------- 1 | """Configuration for pytest.""" 2 | 3 | import pytest 4 | 5 | 6 | def pytest_addoption(parser: pytest.Parser): 7 | """Add command-line flags for pytest.""" 8 | parser.addoption("--run-flaky", action="store_true", help="runs flaky tests") 9 | parser.addoption( 10 | "--run-network-tests", 11 | action="store_true", 12 | help="runs tests requiring a network connection", 13 | ) 14 | parser.addoption("--run-mypy", action="store_true", help="runs mypy tests") 15 | 16 | 17 | def pytest_runtest_setup(item): 18 | # based on https://stackoverflow.com/questions/47559524 19 | if "flaky" in item.keywords and not item.config.getoption("--run-flaky"): 20 | pytest.skip("set --run-flaky option to run flaky tests") 21 | if "network" in item.keywords and not item.config.getoption("--run-network-tests"): 22 | pytest.skip( 23 | "set --run-network-tests to run test requiring an internet connection" 24 | ) 25 | if any("mypy" in m.name for m in item.own_markers) and not item.config.getoption( 26 | "--run-mypy" 27 | ): 28 | pytest.skip("set --run-mypy option to run mypy tests") 29 | 30 | 31 | # See https://docs.pytest.org/en/stable/example/markers.html#automatically-adding-markers-based-on-test-names 32 | def pytest_collection_modifyitems(items): 33 | for item in items: 34 | if "mypy" in item.nodeid: 35 | # IMPORTANT: mypy type annotation tests leverage the pytest-mypy-plugins 36 | # plugin, and are thus written in test_*.yml files. As such, there are 37 | # no explicit test functions on which we can apply a pytest.mark.mypy 38 | # decorator. Therefore, we mark them via this name-based, automatic 39 | # marking approach, meaning that each test case must contain "mypy" in the 40 | # name. 41 | item.add_marker(pytest.mark.mypy) 42 | 43 | 44 | @pytest.fixture(autouse=True) 45 | def add_standard_imports(doctest_namespace, tmpdir): 46 | import numpy as np 47 | import pandas as pd 48 | 49 | import xarray as xr 50 | 51 | doctest_namespace["np"] = np 52 | doctest_namespace["pd"] = pd 53 | doctest_namespace["xr"] = xr 54 | 55 | # always seed numpy.random to make the examples deterministic 56 | np.random.seed(0) 57 | 58 | # always switch to the temporary directory, so files get written there 59 | tmpdir.chdir() 60 | 61 | # Avoid the dask deprecation warning, can remove if CI passes without this. 62 | try: 63 | import dask 64 | except ImportError: 65 | pass 66 | else: 67 | dask.config.set({"dataframe.query-planning": True}) 68 | -------------------------------------------------------------------------------- /doc/README.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | xarray 4 | ------ 5 | 6 | You can find information about building the docs at our `Contributing page `_. 7 | -------------------------------------------------------------------------------- /doc/_static/.gitignore: -------------------------------------------------------------------------------- 1 | examples*.png 2 | *.log 3 | *.pdf 4 | *.fbd_latexmk 5 | *.aux 6 | -------------------------------------------------------------------------------- /doc/_static/ci.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/doc/_static/ci.png -------------------------------------------------------------------------------- /doc/_static/dataset-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/doc/_static/dataset-diagram.png -------------------------------------------------------------------------------- /doc/_static/index_api.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 63 | 68 | 73 | 76 | 82 | 88 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /doc/_static/index_contribute.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 63 | 69 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /doc/_static/logos/Xarray_Icon_Final.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/doc/_static/logos/Xarray_Icon_Final.png -------------------------------------------------------------------------------- /doc/_static/logos/Xarray_Icon_Final.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /doc/_static/logos/Xarray_Logo_FullColor_InverseRGB_Final.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/doc/_static/logos/Xarray_Logo_FullColor_InverseRGB_Final.png -------------------------------------------------------------------------------- /doc/_static/logos/Xarray_Logo_FullColor_InverseRGB_Final.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 39 | 41 | 43 | 48 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /doc/_static/logos/Xarray_Logo_RGB_Final.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/doc/_static/logos/Xarray_Logo_RGB_Final.png -------------------------------------------------------------------------------- /doc/_static/logos/Xarray_Logo_RGB_Final.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 39 | 41 | 43 | 48 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /doc/_static/numfocus_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/doc/_static/numfocus_logo.png -------------------------------------------------------------------------------- /doc/_static/opendap-prism-tmax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/doc/_static/opendap-prism-tmax.png -------------------------------------------------------------------------------- /doc/_static/style.css: -------------------------------------------------------------------------------- 1 | /* Override some aspects of the pydata-sphinx-theme */ 2 | 3 | /* Xarray Branding Guide: 4 | Primary Color palette (Hex): #17afb4 #e28126 #59c7d6 #0e4666 #4a4a4a 5 | Secondary Color Palette (Hex): #f58154 #e7b72d #b3dfe5 #8e8d99 #767985 6 | Primary Typeface: Acumin Variable Concept - Semicondensed Medium 7 | */ 8 | 9 | /* Increase Xarray logo size in upper left corner */ 10 | .navbar-brand img { 11 | height: 75px; 12 | } 13 | .navbar-brand { 14 | height: 75px; 15 | } 16 | 17 | /* Adjust index page overview cards, borrowed from Pandas & Numpy */ 18 | /* Override SVG icon color */ 19 | html[data-theme="dark"] .sd-card img[src*=".svg"] { 20 | filter: invert(0.82) brightness(0.8) contrast(1.2); 21 | } 22 | /* https://github.com/executablebooks/sphinx-design/blob/main/style/_cards.scss */ 23 | /* More space around image */ 24 | .intro-card { 25 | padding: 30px 1px 1px 1px; 26 | } 27 | /* More prominent card borders */ 28 | .intro-card .sd-card { 29 | border: 2px solid var(--pst-color-border); 30 | overflow: hidden; 31 | } 32 | /* Shrink SVG icons */ 33 | .intro-card .sd-card-img-top { 34 | margin: 1px; 35 | height: 100px; 36 | background-color: transparent !important; 37 | } 38 | /* Color titles like links */ 39 | .intro-card .sd-card-title { 40 | color: var(--pst-color-primary); 41 | font-size: var(--pst-font-size-h5); 42 | } 43 | /* Don't have 'raised' color background for card interiors in dark mode */ 44 | .bd-content .sd-card .sd-card-body { 45 | background-color: unset !important; 46 | } 47 | 48 | /* workaround Pydata Sphinx theme using light colors for widget cell outputs in dark-mode */ 49 | /* works for many widgets but not for Xarray html reprs */ 50 | /* https://github.com/pydata/pydata-sphinx-theme/issues/2189 */ 51 | html[data-theme="dark"] div.cell_output .text_html:has(div.xr-wrap) { 52 | background-color: var(--pst-color-on-background) !important; 53 | color: var(--pst-color-text-base) !important; 54 | } 55 | -------------------------------------------------------------------------------- /doc/_static/thumbnails/ERA5-GRIB-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/doc/_static/thumbnails/ERA5-GRIB-example.png -------------------------------------------------------------------------------- /doc/_static/thumbnails/ROMS_ocean_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/doc/_static/thumbnails/ROMS_ocean_model.png -------------------------------------------------------------------------------- /doc/_static/thumbnails/area_weighted_temperature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/doc/_static/thumbnails/area_weighted_temperature.png -------------------------------------------------------------------------------- /doc/_static/thumbnails/monthly-means.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/doc/_static/thumbnails/monthly-means.png -------------------------------------------------------------------------------- /doc/_static/thumbnails/multidimensional-coords.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/doc/_static/thumbnails/multidimensional-coords.png -------------------------------------------------------------------------------- /doc/_static/thumbnails/toy-weather-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/doc/_static/thumbnails/toy-weather-data.png -------------------------------------------------------------------------------- /doc/_static/thumbnails/visualization_gallery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/doc/_static/thumbnails/visualization_gallery.png -------------------------------------------------------------------------------- /doc/_static/view-docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/doc/_static/view-docs.png -------------------------------------------------------------------------------- /doc/_templates/autosummary/accessor.rst: -------------------------------------------------------------------------------- 1 | {{ fullname }} 2 | {{ underline }} 3 | 4 | .. currentmodule:: {{ module.split('.')[0] }} 5 | 6 | .. autoaccessor:: {{ (module.split('.')[1:] + [objname]) | join('.') }} 7 | -------------------------------------------------------------------------------- /doc/_templates/autosummary/accessor_attribute.rst: -------------------------------------------------------------------------------- 1 | {{ fullname }} 2 | {{ underline }} 3 | 4 | .. currentmodule:: {{ module.split('.')[0] }} 5 | 6 | .. autoaccessorattribute:: {{ (module.split('.')[1:] + [objname]) | join('.') }} 7 | -------------------------------------------------------------------------------- /doc/_templates/autosummary/accessor_callable.rst: -------------------------------------------------------------------------------- 1 | {{ fullname }} 2 | {{ underline }} 3 | 4 | .. currentmodule:: {{ module.split('.')[0] }} 5 | 6 | .. autoaccessorcallable:: {{ (module.split('.')[1:] + [objname]) | join('.') }}.__call__ 7 | -------------------------------------------------------------------------------- /doc/_templates/autosummary/accessor_method.rst: -------------------------------------------------------------------------------- 1 | {{ fullname }} 2 | {{ underline }} 3 | 4 | .. currentmodule:: {{ module.split('.')[0] }} 5 | 6 | .. autoaccessormethod:: {{ (module.split('.')[1:] + [objname]) | join('.') }} 7 | -------------------------------------------------------------------------------- /doc/combined.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "refs": { 4 | ".zgroup": "{\"zarr_format\":2}", 5 | "foo/.zarray": "{\"chunks\":[4,5],\"compressor\":null,\"dtype\":\"`__. 9 | 10 | Find the `notes for the meeting here `__. 11 | 12 | There is a :issue:`GitHub issue for changes to the meeting<4001>`. 13 | 14 | You can subscribe to this calendar to be notified of changes: 15 | 16 | * `Google Calendar `__ 17 | * `iCal `__ 18 | 19 | .. raw:: html 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /doc/contribute/index.rst: -------------------------------------------------------------------------------- 1 | ################## 2 | Contributors Guide 3 | ################## 4 | 5 | We welcome your skills and enthusiasm at the Xarray project! There are numerous opportunities to 6 | contribute beyond just writing code. 7 | All contributions, including bug reports, bug fixes, documentation improvements, enhancement suggestions, 8 | and other ideas are welcome. 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | :hidden: 13 | 14 | contributing 15 | ../internals/index 16 | ../roadmap 17 | ../whats-new 18 | developers-meeting 19 | Team 20 | -------------------------------------------------------------------------------- /doc/examples/ERA5-GRIB-example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# GRIB Data Example " 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "GRIB format is commonly used to disseminate atmospheric model data. With xarray and the cfgrib engine, GRIB data can easily be analyzed and visualized." 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "import xarray as xr\n", 24 | "import matplotlib.pyplot as plt\n", 25 | "%matplotlib inline" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "To read GRIB data, you can use `xarray.load_dataset`. The only extra code you need is to specify the engine as `cfgrib`." 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "ds = xr.tutorial.load_dataset(\"era5-2mt-2019-03-uk.grib\", engine=\"cfgrib\")" 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "Let's create a simple plot of 2-m air temperature in degrees Celsius:" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "metadata": {}, 55 | "outputs": [], 56 | "source": [ 57 | "ds = ds - 273.15\n", 58 | "ds.t2m[0].plot(cmap=plt.cm.coolwarm)" 59 | ] 60 | }, 61 | { 62 | "cell_type": "markdown", 63 | "metadata": {}, 64 | "source": [ 65 | "With CartoPy, we can create a more detailed plot, using built-in shapefiles to help provide geographic context:" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "import cartopy.crs as ccrs\n", 75 | "import cartopy\n", 76 | "\n", 77 | "fig = plt.figure(figsize=(10, 10))\n", 78 | "ax = plt.axes(projection=ccrs.Robinson())\n", 79 | "ax.coastlines(resolution=\"10m\")\n", 80 | "plot = ds.t2m[0].plot(\n", 81 | " cmap=plt.cm.coolwarm, transform=ccrs.PlateCarree(), cbar_kwargs={\"shrink\": 0.6}\n", 82 | ")\n", 83 | "plt.title(\"ERA5 - 2m temperature British Isles March 2019\")" 84 | ] 85 | }, 86 | { 87 | "cell_type": "markdown", 88 | "metadata": {}, 89 | "source": [ 90 | "Finally, we can also pull out a time series for a given location easily:" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "ds.t2m.sel(longitude=0, latitude=51.5).plot()\n", 100 | "plt.title(\"ERA5 - London 2m temperature March 2019\")" 101 | ] 102 | } 103 | ], 104 | "metadata": { 105 | "kernelspec": { 106 | "display_name": "Python 3", 107 | "language": "python", 108 | "name": "python3" 109 | }, 110 | "language_info": { 111 | "codemirror_mode": { 112 | "name": "ipython", 113 | "version": 3 114 | }, 115 | "file_extension": ".py", 116 | "mimetype": "text/x-python", 117 | "name": "python", 118 | "nbconvert_exporter": "python", 119 | "pygments_lexer": "ipython3", 120 | "version": "3.7.3" 121 | } 122 | }, 123 | "nbformat": 4, 124 | "nbformat_minor": 4 125 | } 126 | -------------------------------------------------------------------------------- /doc/examples/_code/accessor_example.py: -------------------------------------------------------------------------------- 1 | import xarray as xr 2 | 3 | 4 | @xr.register_dataset_accessor("geo") 5 | class GeoAccessor: 6 | def __init__(self, xarray_obj): 7 | self._obj = xarray_obj 8 | self._center = None 9 | 10 | @property 11 | def center(self): 12 | """Return the geographic center point of this dataset.""" 13 | if self._center is None: 14 | # we can use a cache on our accessor objects, because accessors 15 | # themselves are cached on instances that access them. 16 | lon = self._obj.latitude 17 | lat = self._obj.longitude 18 | self._center = (float(lon.mean()), float(lat.mean())) 19 | return self._center 20 | 21 | def plot(self): 22 | """Plot data on a map.""" 23 | return "plotting!" 24 | -------------------------------------------------------------------------------- /doc/examples/blank_template.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "d8f54f6a", 6 | "metadata": {}, 7 | "source": [ 8 | "# Blank template\n", 9 | "\n", 10 | "Use this notebook from Binder to test an issue or reproduce a bug report" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "id": "41b90ede", 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "import xarray as xr\n", 21 | "import numpy as np\n", 22 | "import pandas as pd\n", 23 | "\n", 24 | "ds = xr.tutorial.load_dataset(\"air_temperature\")\n", 25 | "da = ds[\"air\"]" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "id": "effd9aeb", 32 | "metadata": {}, 33 | "outputs": [], 34 | "source": [] 35 | } 36 | ], 37 | "metadata": { 38 | "kernelspec": { 39 | "display_name": "Python 3", 40 | "language": "python", 41 | "name": "python3" 42 | }, 43 | "language_info": { 44 | "codemirror_mode": { 45 | "name": "ipython", 46 | "version": 3 47 | }, 48 | "file_extension": ".py", 49 | "mimetype": "text/x-python", 50 | "name": "python", 51 | "nbconvert_exporter": "python", 52 | "pygments_lexer": "ipython3", 53 | "version": "3.8.10" 54 | } 55 | }, 56 | "nbformat": 4, 57 | "nbformat_minor": 5 58 | } 59 | -------------------------------------------------------------------------------- /doc/gallery.rst: -------------------------------------------------------------------------------- 1 | Gallery 2 | ======= 3 | 4 | Here's a list of examples on how to use xarray. We will be adding more examples soon. 5 | Contributions are highly welcomed and appreciated. So, if you are interested in contributing, please consult the 6 | :ref:`contributing` guide. 7 | 8 | 9 | 10 | Notebook Examples 11 | ----------------- 12 | 13 | .. include:: notebooks-examples-gallery.txt 14 | 15 | 16 | .. toctree:: 17 | :maxdepth: 1 18 | :hidden: 19 | 20 | examples/weather-data 21 | examples/monthly-means 22 | examples/area_weighted_temperature 23 | examples/multidimensional-coords 24 | examples/visualization_gallery 25 | examples/ROMS_ocean_model 26 | examples/ERA5-GRIB-example 27 | examples/apply_ufunc_vectorize_1d 28 | examples/blank_template 29 | 30 | 31 | External Examples 32 | ----------------- 33 | 34 | 35 | .. include:: external-examples-gallery.txt 36 | -------------------------------------------------------------------------------- /doc/gallery.yml: -------------------------------------------------------------------------------- 1 | notebooks-examples: 2 | - title: Toy weather data 3 | path: examples/weather-data.html 4 | thumbnail: _static/thumbnails/toy-weather-data.png 5 | 6 | - title: Calculating Seasonal Averages from Timeseries of Monthly Means 7 | path: examples/monthly-means.html 8 | thumbnail: _static/thumbnails/monthly-means.png 9 | 10 | - title: Compare weighted and unweighted mean temperature 11 | path: examples/area_weighted_temperature.html 12 | thumbnail: _static/thumbnails/area_weighted_temperature.png 13 | 14 | - title: Working with Multidimensional Coordinates 15 | path: examples/multidimensional-coords.html 16 | thumbnail: _static/thumbnails/multidimensional-coords.png 17 | 18 | - title: Visualization Gallery 19 | path: examples/visualization_gallery.html 20 | thumbnail: _static/thumbnails/visualization_gallery.png 21 | 22 | - title: GRIB Data Example 23 | path: examples/ERA5-GRIB-example.html 24 | thumbnail: _static/thumbnails/ERA5-GRIB-example.png 25 | 26 | - title: Applying unvectorized functions with apply_ufunc 27 | path: examples/apply_ufunc_vectorize_1d.html 28 | thumbnail: _static/logos/Xarray_Logo_RGB_Final.svg 29 | 30 | external-examples: 31 | - title: Managing raster data with rioxarray 32 | path: https://corteva.github.io/rioxarray/stable/examples/examples.html 33 | thumbnail: _static/logos/Xarray_Logo_RGB_Final.svg 34 | 35 | - title: Xarray and dask on the cloud with Pangeo 36 | path: https://gallery.pangeo.io/ 37 | thumbnail: https://avatars.githubusercontent.com/u/60833341?s=200&v=4 38 | 39 | - title: Xarray with Dask Arrays 40 | path: https://examples.dask.org/xarray.html_ 41 | thumbnail: _static/logos/Xarray_Logo_RGB_Final.svg 42 | 43 | - title: Project Pythia Foundations Book 44 | path: https://foundations.projectpythia.org/core/xarray.html 45 | thumbnail: https://raw.githubusercontent.com/ProjectPythia/projectpythia.github.io/main/portal/_static/images/logos/pythia_logo-blue-btext-twocolor.svg 46 | -------------------------------------------------------------------------------- /doc/gallery/README.txt: -------------------------------------------------------------------------------- 1 | .. _recipes: 2 | 3 | Gallery 4 | ======= 5 | -------------------------------------------------------------------------------- /doc/gallery/plot_cartopy_facetgrid.py: -------------------------------------------------------------------------------- 1 | """ 2 | ================================== 3 | Multiple plots and map projections 4 | ================================== 5 | 6 | Control the map projection parameters on multiple axes 7 | 8 | This example illustrates how to plot multiple maps and control their extent 9 | and aspect ratio. 10 | 11 | For more details see `this discussion`_ on github. 12 | 13 | .. _this discussion: https://github.com/pydata/xarray/issues/1397#issuecomment-299190567 14 | """ 15 | 16 | import cartopy.crs as ccrs 17 | import matplotlib.pyplot as plt 18 | 19 | import xarray as xr 20 | 21 | # Load the data 22 | ds = xr.tutorial.load_dataset("air_temperature") 23 | air = ds.air.isel(time=[0, 724]) - 273.15 24 | 25 | # This is the map projection we want to plot *onto* 26 | map_proj = ccrs.LambertConformal(central_longitude=-95, central_latitude=45) 27 | 28 | p = air.plot( 29 | transform=ccrs.PlateCarree(), # the data's projection 30 | col="time", 31 | col_wrap=1, # multiplot settings 32 | aspect=ds.sizes["lon"] / ds.sizes["lat"], # for a sensible figsize 33 | subplot_kws={"projection": map_proj}, # the plot's projection 34 | ) 35 | 36 | # We have to set the map's options on all four axes 37 | for ax in p.axes.flat: 38 | ax.coastlines() 39 | ax.set_extent([-160, -30, 5, 75]) 40 | # Without this aspect attributes the maps will look chaotic and the 41 | # "extent" attribute above will be ignored 42 | ax.set_aspect("equal") 43 | 44 | plt.show() 45 | -------------------------------------------------------------------------------- /doc/gallery/plot_colorbar_center.py: -------------------------------------------------------------------------------- 1 | """ 2 | ================== 3 | Centered colormaps 4 | ================== 5 | 6 | xarray's automatic colormaps choice 7 | 8 | """ 9 | 10 | import matplotlib.pyplot as plt 11 | 12 | import xarray as xr 13 | 14 | # Load the data 15 | ds = xr.tutorial.load_dataset("air_temperature") 16 | air = ds.air.isel(time=0) 17 | 18 | f, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(8, 6)) 19 | 20 | # The first plot (in kelvins) chooses "viridis" and uses the data's min/max 21 | air.plot(ax=ax1, cbar_kwargs={"label": "K"}) 22 | ax1.set_title("Kelvins: default") 23 | ax2.set_xlabel("") 24 | 25 | # The second plot (in celsius) now chooses "BuRd" and centers min/max around 0 26 | airc = air - 273.15 27 | airc.plot(ax=ax2, cbar_kwargs={"label": "°C"}) 28 | ax2.set_title("Celsius: default") 29 | ax2.set_xlabel("") 30 | ax2.set_ylabel("") 31 | 32 | # The center doesn't have to be 0 33 | air.plot(ax=ax3, center=273.15, cbar_kwargs={"label": "K"}) 34 | ax3.set_title("Kelvins: center=273.15") 35 | 36 | # Or it can be ignored 37 | airc.plot(ax=ax4, center=False, cbar_kwargs={"label": "°C"}) 38 | ax4.set_title("Celsius: center=False") 39 | ax4.set_ylabel("") 40 | 41 | # Make it nice 42 | plt.tight_layout() 43 | plt.show() 44 | -------------------------------------------------------------------------------- /doc/gallery/plot_control_colorbar.py: -------------------------------------------------------------------------------- 1 | """ 2 | =========================== 3 | Control the plot's colorbar 4 | =========================== 5 | 6 | Use ``cbar_kwargs`` keyword to specify the number of ticks. 7 | The ``spacing`` kwarg can be used to draw proportional ticks. 8 | """ 9 | 10 | import matplotlib.pyplot as plt 11 | 12 | import xarray as xr 13 | 14 | # Load the data 15 | air_temp = xr.tutorial.load_dataset("air_temperature") 16 | air2d = air_temp.air.isel(time=500) 17 | 18 | # Prepare the figure 19 | f, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(14, 4)) 20 | 21 | # Irregular levels to illustrate the use of a proportional colorbar 22 | levels = [245, 250, 255, 260, 265, 270, 275, 280, 285, 290, 310, 340] 23 | 24 | # Plot data 25 | air2d.plot(ax=ax1, levels=levels) 26 | air2d.plot(ax=ax2, levels=levels, cbar_kwargs={"ticks": levels}) 27 | air2d.plot( 28 | ax=ax3, levels=levels, cbar_kwargs={"ticks": levels, "spacing": "proportional"} 29 | ) 30 | 31 | # Show plots 32 | plt.tight_layout() 33 | plt.show() 34 | -------------------------------------------------------------------------------- /doc/gallery/plot_lines_from_2d.py: -------------------------------------------------------------------------------- 1 | """ 2 | ================================== 3 | Multiple lines from a 2d DataArray 4 | ================================== 5 | 6 | 7 | Use :py:func:`xarray.plot.line` on a 2d DataArray to plot selections as 8 | multiple lines. 9 | 10 | See :ref:`plotting.multiplelines` for more details. 11 | 12 | """ 13 | 14 | import matplotlib.pyplot as plt 15 | 16 | import xarray as xr 17 | 18 | # Load the data 19 | ds = xr.tutorial.load_dataset("air_temperature") 20 | air = ds.air - 273.15 # to celsius 21 | 22 | # Prepare the figure 23 | f, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4), sharey=True) 24 | 25 | # Selected latitude indices 26 | isel_lats = [10, 15, 20] 27 | 28 | # Temperature vs longitude plot - illustrates the "hue" kwarg 29 | air.isel(time=0, lat=isel_lats).plot.line(ax=ax1, hue="lat") 30 | ax1.set_ylabel("°C") 31 | 32 | # Temperature vs time plot - illustrates the "x" and "add_legend" kwargs 33 | air.isel(lon=30, lat=isel_lats).plot.line(ax=ax2, x="time", add_legend=False) 34 | ax2.set_ylabel("") 35 | 36 | # Show 37 | plt.tight_layout() 38 | plt.show() 39 | -------------------------------------------------------------------------------- /doc/get-help/socials.rst: -------------------------------------------------------------------------------- 1 | .. _socials: 2 | 3 | Social Media 4 | ============ 5 | 6 | Xarray is active on several social media platforms. We use these platforms to share updates and connect with the user community. 7 | 8 | - `Discord `__ 9 | - `Bluesky `__ 10 | - `Twitter(X) `__ 11 | -------------------------------------------------------------------------------- /doc/getting-started-guide/index.rst: -------------------------------------------------------------------------------- 1 | ################ 2 | Getting Started 3 | ################ 4 | 5 | The getting started guide aims to get you using Xarray productively as quickly as possible. 6 | It is designed as an entry point for new users, and it provided an introduction to Xarray's main concepts. 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | 11 | why-xarray 12 | installing 13 | quick-overview 14 | tutorials-and-videos 15 | -------------------------------------------------------------------------------- /doc/getting-started-guide/tutorials-and-videos.rst: -------------------------------------------------------------------------------- 1 | 2 | Tutorials and Videos 3 | ==================== 4 | 5 | There are an abundance of tutorials and videos available for learning how to use *xarray*. 6 | Often, these tutorials are taught to workshop attendees at conferences or other events. 7 | We highlight a number of these resources below, but this is by no means an exhaustive list! 8 | 9 | Tutorials 10 | ---------- 11 | 12 | - `Xarray's Tutorials`_ repository 13 | - The `UW eScience Institute's Geohackweek`_ tutorial on xarray for geospatial data scientists. 14 | - `Nicolas Fauchereau's 2015 tutorial`_ on xarray for netCDF users. 15 | 16 | 17 | Videos 18 | ------- 19 | 20 | .. include:: ../videos-gallery.txt 21 | 22 | 23 | Books, Chapters and Articles 24 | ----------------------------- 25 | 26 | - Stephan Hoyer and Joe Hamman's `Journal of Open Research Software paper`_ describing the xarray project. 27 | 28 | 29 | .. _Xarray's Tutorials: https://xarray-contrib.github.io/xarray-tutorial/ 30 | .. _Journal of Open Research Software paper: https://doi.org/10.5334/jors.148 31 | .. _UW eScience Institute's Geohackweek : https://geohackweek.github.io/nDarrays/ 32 | .. _tutorial: https://github.com/Unidata/unidata-users-workshop/blob/master/notebooks/xray-tutorial.ipynb 33 | .. _with answers: https://github.com/Unidata/unidata-users-workshop/blob/master/notebooks/xray-tutorial-with-answers.ipynb 34 | .. _Nicolas Fauchereau's 2015 tutorial: https://nbviewer.iPython.org/github/nicolasfauchereau/metocean/blob/master/notebooks/xray.ipynb 35 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | :html_theme.sidebar_secondary.remove: true 2 | 3 | .. module:: xarray 4 | 5 | Xarray documentation 6 | ==================== 7 | 8 | Xarray makes working with labelled multi-dimensional arrays in Python simple, 9 | efficient, and fun! 10 | 11 | **Version**: |version| - :ref:`whats-new` 12 | 13 | **Useful links**: 14 | `Home `__ | 15 | `Code Repository `__ | 16 | `Issues `__ | 17 | `Discussions `__ | 18 | `Releases `__ | 19 | `Tutorial `__ | 20 | `Stack Overflow `__ | 21 | `Blog `__ | 22 | 23 | .. grid:: 1 1 2 2 24 | :gutter: 2 25 | 26 | .. grid-item-card:: Get started! 27 | :img-top: _static/index_getting_started.svg 28 | :class-card: intro-card 29 | :link: getting-started-guide/index 30 | :link-type: doc 31 | 32 | *New to Xarray?* 33 | Start here with our installation instructions and a brief overview of Xarray. 34 | 35 | .. grid-item-card:: User guide 36 | :img-top: _static/index_user_guide.svg 37 | :class-card: intro-card 38 | :link: user-guide/index 39 | :link-type: doc 40 | 41 | *Ready to deepen your understanding of Xarray?* 42 | Visit the user guide for detailed explanations of the data model, common computational patterns, and more. 43 | 44 | .. grid-item-card:: API reference 45 | :img-top: _static/index_api.svg 46 | :class-card: intro-card 47 | :link: api 48 | :link-type: doc 49 | 50 | *Need to learn more about a specific Xarray function?* 51 | Go here to review the documentation of all public functions and classes in Xarray. 52 | 53 | .. grid-item-card:: Contribute 54 | :img-top: _static/index_contribute.svg 55 | :class-card: intro-card 56 | :link: contribute/contributing 57 | :link-type: doc 58 | 59 | *Saw a typo in the documentation? Want to improve existing functionalities?* 60 | Please review our guide on improving Xarray. 61 | 62 | .. toctree:: 63 | :maxdepth: 2 64 | :hidden: 65 | :caption: For users 66 | 67 | Get Started 68 | User Guide 69 | Tutorial 70 | Gallery 71 | API Reference 72 | Get Help 73 | Contribute 74 | Release Notes 75 | -------------------------------------------------------------------------------- /doc/internals/duck-arrays-integration.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _internals.duckarrays: 3 | 4 | Integrating with duck arrays 5 | ============================= 6 | 7 | .. warning:: 8 | 9 | This is an experimental feature. Please report any bugs or other difficulties on `xarray's issue tracker `_. 10 | 11 | Xarray can wrap custom numpy-like arrays (":term:`duck array`\s") - see the :ref:`user guide documentation `. 12 | This page is intended for developers who are interested in wrapping a new custom array type with xarray. 13 | 14 | .. _internals.duckarrays.requirements: 15 | 16 | Duck array requirements 17 | ~~~~~~~~~~~~~~~~~~~~~~~ 18 | 19 | Xarray does not explicitly check that required methods are defined by the underlying duck array object before 20 | attempting to wrap the given array. However, a wrapped array type should at a minimum define these attributes: 21 | 22 | * ``shape`` property, 23 | * ``dtype`` property, 24 | * ``ndim`` property, 25 | * ``__array__`` method, 26 | * ``__array_ufunc__`` method, 27 | * ``__array_function__`` method. 28 | 29 | These need to be defined consistently with :py:class:`numpy.ndarray`, for example the array ``shape`` 30 | property needs to obey `numpy's broadcasting rules `_ 31 | (see also the `Python Array API standard's explanation `_ 32 | of these same rules). 33 | 34 | .. _internals.duckarrays.array_api_standard: 35 | 36 | Python Array API standard support 37 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 38 | 39 | As an integration library xarray benefits greatly from the standardization of duck-array libraries' APIs, and so is a 40 | big supporter of the `Python Array API Standard `_. 41 | 42 | We aim to support any array libraries that follow the Array API standard out-of-the-box. However, xarray does occasionally 43 | call some numpy functions which are not (yet) part of the standard (e.g. :py:meth:`xarray.DataArray.pad` calls :py:func:`numpy.pad`). 44 | See `xarray issue #7848 `_ for a list of such functions. We can still support dispatching on these functions through 45 | the array protocols above, it just means that if you exclusively implement the methods in the Python Array API standard 46 | then some features in xarray will not work. 47 | 48 | Custom inline reprs 49 | ~~~~~~~~~~~~~~~~~~~ 50 | 51 | In certain situations (e.g. when printing the collapsed preview of 52 | variables of a ``Dataset``), xarray will display the repr of a :term:`duck array` 53 | in a single line, truncating it to a certain number of characters. If that 54 | would drop too much information, the :term:`duck array` may define a 55 | ``_repr_inline_`` method that takes ``max_width`` (number of characters) as an 56 | argument 57 | 58 | .. code:: python 59 | 60 | class MyDuckArray: 61 | ... 62 | 63 | def _repr_inline_(self, max_width): 64 | """format to a single line with at most max_width characters""" 65 | ... 66 | 67 | ... 68 | 69 | To avoid duplicated information, this method must omit information about the shape and 70 | :term:`dtype`. For example, the string representation of a ``dask`` array or a 71 | ``sparse`` matrix would be: 72 | 73 | .. ipython:: python 74 | 75 | import dask.array as da 76 | import xarray as xr 77 | import sparse 78 | 79 | a = da.linspace(0, 1, 20, chunks=2) 80 | a 81 | 82 | b = np.eye(10) 83 | b[[5, 7, 3, 0], [6, 8, 2, 9]] = 2 84 | b = sparse.COO.from_numpy(b) 85 | b 86 | 87 | xr.Dataset(dict(a=("x", a), b=(("y", "z"), b))) 88 | -------------------------------------------------------------------------------- /doc/internals/index.rst: -------------------------------------------------------------------------------- 1 | .. _internals: 2 | 3 | Xarray Internals 4 | ================ 5 | 6 | Xarray builds upon two of the foundational libraries of the scientific Python 7 | stack, NumPy and pandas. It is written in pure Python (no C or Cython 8 | extensions), which makes it easy to develop and extend. Instead, we push 9 | compiled code to :ref:`optional dependencies`. 10 | 11 | The pages in this section are intended for: 12 | 13 | * Contributors to xarray who wish to better understand some of the internals, 14 | * Developers from other fields who wish to extend xarray with domain-specific logic, perhaps to support a new scientific community of users, 15 | * Developers of other packages who wish to interface xarray with their existing tools, e.g. by creating a backend for reading a new file format, or wrapping a custom array type. 16 | 17 | .. toctree:: 18 | :maxdepth: 2 19 | :hidden: 20 | 21 | internal-design 22 | interoperability 23 | duck-arrays-integration 24 | chunked-arrays 25 | extending-xarray 26 | how-to-add-new-backend 27 | how-to-create-custom-index 28 | zarr-encoding-spec 29 | time-coding 30 | -------------------------------------------------------------------------------- /doc/internals/interoperability.rst: -------------------------------------------------------------------------------- 1 | .. _interoperability: 2 | 3 | Interoperability of Xarray 4 | ========================== 5 | 6 | Xarray is designed to be extremely interoperable, in many orthogonal ways. 7 | Making xarray as flexible as possible is the common theme of most of the goals on our :ref:`roadmap`. 8 | 9 | This interoperability comes via a set of flexible abstractions into which the user can plug in. The current full list is: 10 | 11 | - :ref:`Custom file backends ` via the :py:class:`~xarray.backends.BackendEntrypoint` system, 12 | - Numpy-like :ref:`"duck" array wrapping `, which supports the `Python Array API Standard `_, 13 | - :ref:`Chunked distributed array computation ` via the :py:class:`~xarray.namedarray.parallelcompat.ChunkManagerEntrypoint` system, 14 | - Custom :py:class:`~xarray.Index` objects for :ref:`flexible label-based lookups `, 15 | - Extending xarray objects with domain-specific methods via :ref:`custom accessors `. 16 | 17 | .. warning:: 18 | 19 | One obvious way in which xarray could be more flexible is that whilst subclassing xarray objects is possible, we 20 | currently don't support it in most transformations, instead recommending composition over inheritance. See the 21 | :ref:`internal design page ` for the rationale and look at the corresponding `GH issue `_ 22 | if you're interested in improving support for subclassing! 23 | 24 | .. note:: 25 | 26 | If you think there is another way in which xarray could become more generically flexible then please 27 | tell us your ideas by `raising an issue to request the feature `_! 28 | 29 | 30 | Whilst xarray was originally designed specifically to open ``netCDF4`` files as :py:class:`numpy.ndarray` objects labelled by :py:class:`pandas.Index` objects, 31 | it is entirely possible today to: 32 | 33 | - lazily open an xarray object directly from a custom binary file format (e.g. using ``xarray.open_dataset(path, engine='my_custom_format')``, 34 | - handle the data as any API-compliant numpy-like array type (e.g. sparse or GPU-backed), 35 | - distribute out-of-core computation across that array type in parallel (e.g. via :ref:`dask`), 36 | - track the physical units of the data through computations (e.g via `pint-xarray `_), 37 | - query the data via custom index logic optimized for specific applications (e.g. an :py:class:`~xarray.Index` object backed by a KDTree structure), 38 | - attach domain-specific logic via accessor methods (e.g. to understand geographic Coordinate Reference System metadata), 39 | - organize hierarchical groups of xarray data in a :py:class:`xarray.DataTree` (e.g. to treat heterogeneous simulation and observational data together during analysis). 40 | 41 | All of these features can be provided simultaneously, using libraries compatible with the rest of the scientific python ecosystem. 42 | In this situation xarray would be essentially a thin wrapper acting as pure-python framework, providing a common interface and 43 | separation of concerns via various domain-agnostic abstractions. 44 | 45 | Most of the remaining pages in the documentation of xarray's internals describe these various types of interoperability in more detail. 46 | -------------------------------------------------------------------------------- /doc/internals/zarr-encoding-spec.rst: -------------------------------------------------------------------------------- 1 | .. currentmodule:: xarray 2 | 3 | .. _zarr_encoding: 4 | 5 | Zarr Encoding Specification 6 | ============================ 7 | 8 | In implementing support for the `Zarr `_ storage 9 | format, Xarray developers made some *ad hoc* choices about how to store 10 | NetCDF data in Zarr. 11 | Future versions of the Zarr spec will likely include a more formal convention 12 | for the storage of the NetCDF data model in Zarr; see 13 | `Zarr spec repo `_ for ongoing 14 | discussion. 15 | 16 | First, Xarray can only read and write Zarr groups. There is currently no support 17 | for reading / writing individual Zarr arrays. Zarr groups are mapped to 18 | Xarray ``Dataset`` objects. 19 | 20 | Second, from Xarray's point of view, the key difference between 21 | NetCDF and Zarr is that all NetCDF arrays have *dimension names* while Zarr 22 | arrays do not. Therefore, in order to store NetCDF data in Zarr, Xarray must 23 | somehow encode and decode the name of each array's dimensions. 24 | 25 | To accomplish this, Xarray developers decided to define a special Zarr array 26 | attribute: ``_ARRAY_DIMENSIONS``. The value of this attribute is a list of 27 | dimension names (strings), for example ``["time", "lon", "lat"]``. When writing 28 | data to Zarr, Xarray sets this attribute on all variables based on the variable 29 | dimensions. When reading a Zarr group, Xarray looks for this attribute on all 30 | arrays, raising an error if it can't be found. The attribute is used to define 31 | the variable dimension names and then removed from the attributes dictionary 32 | returned to the user. 33 | 34 | Because of these choices, Xarray cannot read arbitrary array data, but only 35 | Zarr data with valid ``_ARRAY_DIMENSIONS`` or 36 | `NCZarr `_ attributes 37 | on each array (NCZarr dimension names are defined in the ``.zarray`` file). 38 | 39 | After decoding the ``_ARRAY_DIMENSIONS`` or NCZarr attribute and assigning the variable 40 | dimensions, Xarray proceeds to [optionally] decode each variable using its 41 | standard CF decoding machinery used for NetCDF data (see :py:func:`decode_cf`). 42 | 43 | Finally, it's worth noting that Xarray writes (and attempts to read) 44 | "consolidated metadata" by default (the ``.zmetadata`` file), which is another 45 | non-standard Zarr extension, albeit one implemented upstream in Zarr-Python. 46 | You do not need to write consolidated metadata to make Zarr stores readable in 47 | Xarray, but because Xarray can open these stores much faster, users will see a 48 | warning about poor performance when reading non-consolidated stores unless they 49 | explicitly set ``consolidated=False``. See :ref:`io.zarr.consolidated_metadata` 50 | for more details. 51 | 52 | As a concrete example, here we write a tutorial dataset to Zarr and then 53 | re-open it directly with Zarr: 54 | 55 | .. ipython:: python 56 | :okwarning: 57 | 58 | import os 59 | import xarray as xr 60 | import zarr 61 | 62 | ds = xr.tutorial.load_dataset("rasm") 63 | ds.to_zarr("rasm.zarr", mode="w") 64 | 65 | zgroup = zarr.open("rasm.zarr") 66 | print(os.listdir("rasm.zarr")) 67 | print(zgroup.tree()) 68 | dict(zgroup["Tair"].attrs) 69 | 70 | .. ipython:: python 71 | :suppress: 72 | 73 | import shutil 74 | 75 | shutil.rmtree("rasm.zarr") 76 | -------------------------------------------------------------------------------- /doc/user-guide/index.rst: -------------------------------------------------------------------------------- 1 | ########### 2 | User Guide 3 | ########### 4 | 5 | In this user guide, you will find detailed descriptions and 6 | examples that describe many common tasks that you can accomplish with Xarray. 7 | 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :caption: Data model 12 | 13 | terminology 14 | data-structures 15 | hierarchical-data 16 | dask 17 | 18 | 19 | .. toctree:: 20 | :maxdepth: 2 21 | :caption: Core operations 22 | 23 | indexing 24 | combining 25 | reshaping 26 | computation 27 | groupby 28 | interpolation 29 | 30 | .. toctree:: 31 | :maxdepth: 2 32 | :caption: I/O 33 | 34 | io 35 | complex-numbers 36 | 37 | .. toctree:: 38 | :maxdepth: 2 39 | :caption: Visualization 40 | 41 | plotting 42 | 43 | 44 | .. toctree:: 45 | :maxdepth: 2 46 | :caption: Interoperability 47 | 48 | pandas 49 | duckarrays 50 | ecosystem 51 | 52 | 53 | .. toctree:: 54 | :maxdepth: 2 55 | :caption: Domain-specific workflows 56 | 57 | time-series 58 | weather-climate 59 | 60 | .. toctree:: 61 | :maxdepth: 2 62 | :caption: Options and Testing 63 | 64 | options 65 | testing 66 | -------------------------------------------------------------------------------- /doc/user-guide/options.rst: -------------------------------------------------------------------------------- 1 | .. currentmodule:: xarray 2 | 3 | .. _options: 4 | 5 | Configuration 6 | ============= 7 | 8 | Xarray offers a small number of configuration options through :py:func:`set_options`. With these, you can 9 | 10 | 1. Control the ``repr``: 11 | 12 | - ``display_expand_attrs`` 13 | - ``display_expand_coords`` 14 | - ``display_expand_data`` 15 | - ``display_expand_data_vars`` 16 | - ``display_max_rows`` 17 | - ``display_style`` 18 | 19 | 2. Control behaviour during operations: ``arithmetic_join``, ``keep_attrs``, ``use_bottleneck``. 20 | 3. Control colormaps for plots:``cmap_divergent``, ``cmap_sequential``. 21 | 4. Aspects of file reading: ``file_cache_maxsize``, ``warn_on_unclosed_files``. 22 | 23 | 24 | You can set these options either globally 25 | 26 | :: 27 | 28 | xr.set_options(arithmetic_join="exact") 29 | 30 | or locally as a context manager: 31 | 32 | :: 33 | 34 | with xr.set_options(arithmetic_join="exact"): 35 | # do operation here 36 | pass 37 | -------------------------------------------------------------------------------- /doc/videos.yml: -------------------------------------------------------------------------------- 1 | - title: "Xdev Python Tutorial Seminar Series 2022 Thinking with Xarray : High-level computation patterns" 2 | src: '' 3 | authors: 4 | - Deepak Cherian 5 | - title: "Xdev Python Tutorial Seminar Series 2021 seminar introducing xarray (2 of 2)" 6 | src: '' 7 | authors: 8 | - Anderson Banihirwe 9 | 10 | - title: "Xdev Python Tutorial Seminar Series 2021 seminar introducing xarray (1 of 2)" 11 | src: '' 12 | authors: 13 | - Anderson Banihirwe 14 | 15 | - title: "Xarray's 2020 virtual tutorial" 16 | src: '' 17 | authors: 18 | - Anderson Banihirwe 19 | - Deepak Cherian 20 | - Martin Durant 21 | 22 | - title: "Xarray's Tutorial presented at the 2020 SciPy Conference" 23 | src: ' ' 24 | authors: 25 | - Joe Hamman 26 | - Deepak Cherian 27 | - Ryan Abernathey 28 | - Stephan Hoyer 29 | 30 | - title: "Scipy 2015 talk introducing xarray to a general audience" 31 | src: '' 32 | authors: 33 | - Stephan Hoyer 34 | 35 | - title: " 2015 Unidata Users Workshop talk and tutorial with (`with answers`_) introducing xarray to users familiar with netCDF" 36 | src: '' 37 | authors: 38 | - Stephan Hoyer 39 | -------------------------------------------------------------------------------- /licenses/DASK_LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2018, Anaconda, Inc. and contributors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | Neither the name of Anaconda nor the names of any contributors may be used to 15 | endorse or promote products derived from this software without specific prior 16 | written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 28 | THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /licenses/NUMPY_LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2011, NumPy Developers. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of the NumPy Developers nor the names of any 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /licenses/PANDAS_LICENSE: -------------------------------------------------------------------------------- 1 | pandas license 2 | ============== 3 | 4 | Copyright (c) 2011-2012, Lambda Foundry, Inc. and PyData Development Team 5 | All rights reserved. 6 | 7 | Copyright (c) 2008-2011 AQR Capital Management, LLC 8 | All rights reserved. 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions are 12 | met: 13 | 14 | * Redistributions of source code must retain the above copyright 15 | notice, this list of conditions and the following disclaimer. 16 | 17 | * Redistributions in binary form must reproduce the above 18 | copyright notice, this list of conditions and the following 19 | disclaimer in the documentation and/or other materials provided 20 | with the distribution. 21 | 22 | * Neither the name of the copyright holder nor the names of any 23 | contributors may be used to endorse or promote products derived 24 | from this software without specific prior written permission. 25 | 26 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS 27 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 29 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 30 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 31 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 32 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 36 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | -------------------------------------------------------------------------------- /licenses/SCIKIT_LEARN_LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2007-2021 The scikit-learn developers. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE 30 | -------------------------------------------------------------------------------- /licenses/SEABORN_LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2013, Michael L. Waskom 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /properties/README.md: -------------------------------------------------------------------------------- 1 | # Property-based tests using Hypothesis 2 | 3 | This directory contains property-based tests using a library 4 | called [Hypothesis](https://github.com/HypothesisWorks/hypothesis-python). 5 | 6 | The property tests for xarray are a work in progress - more are always welcome. 7 | They are stored in a separate directory because they tend to run more examples 8 | and thus take longer, and so that local development can run a test suite 9 | without needing to `pip install hypothesis`. 10 | 11 | ## Hang on, "property-based" tests? 12 | 13 | Instead of making assertions about operations on a particular piece of 14 | data, you use Hypothesis to describe a _kind_ of data, then make assertions 15 | that should hold for _any_ example of this kind. 16 | 17 | For example: "given a 2d ndarray of dtype uint8 `arr`, 18 | `xr.DataArray(arr).plot.imshow()` never raises an exception". 19 | 20 | Hypothesis will then try many random examples, and report a minimised 21 | failing input for each error it finds. 22 | [See the docs for more info.](https://hypothesis.readthedocs.io/en/master/) 23 | -------------------------------------------------------------------------------- /properties/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/properties/__init__.py -------------------------------------------------------------------------------- /properties/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | def pytest_addoption(parser): 5 | parser.addoption( 6 | "--run-slow-hypothesis", 7 | action="store_true", 8 | default=False, 9 | help="run slow hypothesis tests", 10 | ) 11 | 12 | 13 | def pytest_collection_modifyitems(config, items): 14 | if config.getoption("--run-slow-hypothesis"): 15 | return 16 | skip_slow_hyp = pytest.mark.skip(reason="need --run-slow-hypothesis option to run") 17 | for item in items: 18 | if "slow_hypothesis" in item.keywords: 19 | item.add_marker(skip_slow_hyp) 20 | 21 | 22 | try: 23 | from hypothesis import settings 24 | except ImportError: 25 | pass 26 | else: 27 | # Run for a while - arrays are a bigger search space than usual 28 | settings.register_profile("ci", deadline=None, print_blob=True) 29 | settings.load_profile("ci") 30 | -------------------------------------------------------------------------------- /properties/test_encode_decode.py: -------------------------------------------------------------------------------- 1 | """ 2 | Property-based tests for encoding/decoding methods. 3 | 4 | These ones pass, just as you'd hope! 5 | 6 | """ 7 | 8 | import warnings 9 | 10 | import pytest 11 | 12 | pytest.importorskip("hypothesis") 13 | # isort: split 14 | 15 | import hypothesis.extra.numpy as npst 16 | import hypothesis.strategies as st 17 | import numpy as np 18 | from hypothesis import given 19 | 20 | import xarray as xr 21 | from xarray.coding.times import _parse_iso8601 22 | from xarray.testing.strategies import CFTimeStrategyISO8601, variables 23 | from xarray.tests import requires_cftime 24 | 25 | 26 | @pytest.mark.slow 27 | @given(original=variables()) 28 | def test_CFMask_coder_roundtrip(original) -> None: 29 | coder = xr.coding.variables.CFMaskCoder() 30 | roundtripped = coder.decode(coder.encode(original)) 31 | xr.testing.assert_identical(original, roundtripped) 32 | 33 | 34 | @pytest.mark.xfail 35 | @pytest.mark.slow 36 | @given(var=variables(dtype=npst.floating_dtypes())) 37 | def test_CFMask_coder_decode(var) -> None: 38 | var[0] = -99 39 | var.attrs["_FillValue"] = -99 40 | coder = xr.coding.variables.CFMaskCoder() 41 | decoded = coder.decode(var) 42 | assert np.isnan(decoded[0]) 43 | 44 | 45 | @pytest.mark.slow 46 | @given(original=variables()) 47 | def test_CFScaleOffset_coder_roundtrip(original) -> None: 48 | coder = xr.coding.variables.CFScaleOffsetCoder() 49 | roundtripped = coder.decode(coder.encode(original)) 50 | xr.testing.assert_identical(original, roundtripped) 51 | 52 | 53 | @requires_cftime 54 | @given(dt=st.datetimes() | CFTimeStrategyISO8601()) 55 | def test_iso8601_decode(dt): 56 | iso = dt.isoformat() 57 | with warnings.catch_warnings(): 58 | warnings.filterwarnings("ignore", message=".*date/calendar/year zero.*") 59 | parsed, _ = _parse_iso8601(type(dt), iso) 60 | assert dt == parsed 61 | -------------------------------------------------------------------------------- /properties/test_properties.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | 3 | import pytest 4 | 5 | pytest.importorskip("hypothesis") 6 | 7 | import hypothesis.strategies as st 8 | from hypothesis import given, note 9 | 10 | import xarray as xr 11 | import xarray.testing.strategies as xrst 12 | from xarray.groupers import find_independent_seasons, season_to_month_tuple 13 | 14 | 15 | @given(attrs=xrst.simple_attrs) 16 | def test_assert_identical(attrs): 17 | v = xr.Variable(dims=(), data=0, attrs=attrs) 18 | xr.testing.assert_identical(v, v.copy(deep=True)) 19 | 20 | ds = xr.Dataset(attrs=attrs) 21 | xr.testing.assert_identical(ds, ds.copy(deep=True)) 22 | 23 | 24 | @given( 25 | roll=st.integers(min_value=0, max_value=12), 26 | breaks=st.lists( 27 | st.integers(min_value=0, max_value=11), min_size=1, max_size=12, unique=True 28 | ), 29 | ) 30 | def test_property_season_month_tuple(roll, breaks): 31 | chars = list("JFMAMJJASOND") 32 | months = tuple(range(1, 13)) 33 | 34 | rolled_chars = chars[roll:] + chars[:roll] 35 | rolled_months = months[roll:] + months[:roll] 36 | breaks = sorted(breaks) 37 | if breaks[0] != 0: 38 | breaks = [0] + breaks 39 | if breaks[-1] != 12: 40 | breaks = breaks + [12] 41 | seasons = tuple( 42 | "".join(rolled_chars[start:stop]) for start, stop in itertools.pairwise(breaks) 43 | ) 44 | actual = season_to_month_tuple(seasons) 45 | expected = tuple( 46 | rolled_months[start:stop] for start, stop in itertools.pairwise(breaks) 47 | ) 48 | assert expected == actual 49 | 50 | 51 | @given(data=st.data(), nmonths=st.integers(min_value=1, max_value=11)) 52 | def test_property_find_independent_seasons(data, nmonths): 53 | chars = "JFMAMJJASOND" 54 | # if stride > nmonths, then we can't infer season order 55 | stride = data.draw(st.integers(min_value=1, max_value=nmonths)) 56 | chars = chars + chars[:nmonths] 57 | seasons = [list(chars[i : i + nmonths]) for i in range(0, 12, stride)] 58 | note(seasons) 59 | groups = find_independent_seasons(seasons) 60 | for group in groups: 61 | inds = tuple(itertools.chain(*group.inds)) 62 | assert len(inds) == len(set(inds)) 63 | assert len(group.codes) == len(set(group.codes)) 64 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup 3 | 4 | setup(use_scm_version={"fallback_version": "9999"}) 5 | -------------------------------------------------------------------------------- /xarray/backends/__init__.py: -------------------------------------------------------------------------------- 1 | """Backend objects for saving and loading data 2 | 3 | DataStores provide a uniform interface for saving and loading data in different 4 | formats. They should not be used directly, but rather through Dataset objects. 5 | """ 6 | 7 | from xarray.backends.common import AbstractDataStore, BackendArray, BackendEntrypoint 8 | from xarray.backends.file_manager import ( 9 | CachingFileManager, 10 | DummyFileManager, 11 | FileManager, 12 | ) 13 | from xarray.backends.h5netcdf_ import H5netcdfBackendEntrypoint, H5NetCDFStore 14 | from xarray.backends.memory import InMemoryDataStore 15 | from xarray.backends.netCDF4_ import NetCDF4BackendEntrypoint, NetCDF4DataStore 16 | from xarray.backends.plugins import list_engines, refresh_engines 17 | from xarray.backends.pydap_ import PydapBackendEntrypoint, PydapDataStore 18 | from xarray.backends.scipy_ import ScipyBackendEntrypoint, ScipyDataStore 19 | from xarray.backends.store import StoreBackendEntrypoint 20 | from xarray.backends.zarr import ZarrBackendEntrypoint, ZarrStore 21 | 22 | __all__ = [ 23 | "AbstractDataStore", 24 | "BackendArray", 25 | "BackendEntrypoint", 26 | "CachingFileManager", 27 | "DummyFileManager", 28 | "FileManager", 29 | "H5NetCDFStore", 30 | "H5netcdfBackendEntrypoint", 31 | "InMemoryDataStore", 32 | "NetCDF4BackendEntrypoint", 33 | "NetCDF4DataStore", 34 | "PydapBackendEntrypoint", 35 | "PydapDataStore", 36 | "ScipyBackendEntrypoint", 37 | "ScipyDataStore", 38 | "StoreBackendEntrypoint", 39 | "ZarrBackendEntrypoint", 40 | "ZarrStore", 41 | "list_engines", 42 | "refresh_engines", 43 | ] 44 | -------------------------------------------------------------------------------- /xarray/backends/lru_cache.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import threading 4 | from collections import OrderedDict 5 | from collections.abc import Callable, Iterator, MutableMapping 6 | from typing import Any, TypeVar 7 | 8 | K = TypeVar("K") 9 | V = TypeVar("V") 10 | 11 | 12 | class LRUCache(MutableMapping[K, V]): 13 | """Thread-safe LRUCache based on an OrderedDict. 14 | 15 | All dict operations (__getitem__, __setitem__, __contains__) update the 16 | priority of the relevant key and take O(1) time. The dict is iterated over 17 | in order from the oldest to newest key, which means that a complete pass 18 | over the dict should not affect the order of any entries. 19 | 20 | When a new item is set and the maximum size of the cache is exceeded, the 21 | oldest item is dropped and called with ``on_evict(key, value)``. 22 | 23 | The ``maxsize`` property can be used to view or adjust the capacity of 24 | the cache, e.g., ``cache.maxsize = new_size``. 25 | """ 26 | 27 | _cache: OrderedDict[K, V] 28 | _maxsize: int 29 | _lock: threading.RLock 30 | _on_evict: Callable[[K, V], Any] | None 31 | 32 | __slots__ = ("_cache", "_lock", "_maxsize", "_on_evict") 33 | 34 | def __init__(self, maxsize: int, on_evict: Callable[[K, V], Any] | None = None): 35 | """ 36 | Parameters 37 | ---------- 38 | maxsize : int 39 | Integer maximum number of items to hold in the cache. 40 | on_evict : callable, optional 41 | Function to call like ``on_evict(key, value)`` when items are 42 | evicted. 43 | """ 44 | if not isinstance(maxsize, int): 45 | raise TypeError("maxsize must be an integer") 46 | if maxsize < 0: 47 | raise ValueError("maxsize must be non-negative") 48 | self._maxsize = maxsize 49 | self._cache = OrderedDict() 50 | self._lock = threading.RLock() 51 | self._on_evict = on_evict 52 | 53 | def __getitem__(self, key: K) -> V: 54 | # record recent use of the key by moving it to the front of the list 55 | with self._lock: 56 | value = self._cache[key] 57 | self._cache.move_to_end(key) 58 | return value 59 | 60 | def _enforce_size_limit(self, capacity: int) -> None: 61 | """Shrink the cache if necessary, evicting the oldest items.""" 62 | while len(self._cache) > capacity: 63 | key, value = self._cache.popitem(last=False) 64 | if self._on_evict is not None: 65 | self._on_evict(key, value) 66 | 67 | def __setitem__(self, key: K, value: V) -> None: 68 | with self._lock: 69 | if key in self._cache: 70 | # insert the new value at the end 71 | del self._cache[key] 72 | self._cache[key] = value 73 | elif self._maxsize: 74 | # make room if necessary 75 | self._enforce_size_limit(self._maxsize - 1) 76 | self._cache[key] = value 77 | elif self._on_evict is not None: 78 | # not saving, immediately evict 79 | self._on_evict(key, value) 80 | 81 | def __delitem__(self, key: K) -> None: 82 | del self._cache[key] 83 | 84 | def __iter__(self) -> Iterator[K]: 85 | # create a list, so accessing the cache during iteration cannot change 86 | # the iteration order 87 | return iter(list(self._cache)) 88 | 89 | def __len__(self) -> int: 90 | return len(self._cache) 91 | 92 | @property 93 | def maxsize(self) -> int: 94 | """Maximum number of items can be held in the cache.""" 95 | return self._maxsize 96 | 97 | @maxsize.setter 98 | def maxsize(self, size: int) -> None: 99 | """Resize the cache, evicting the oldest items if necessary.""" 100 | if size < 0: 101 | raise ValueError("maxsize must be non-negative") 102 | with self._lock: 103 | self._enforce_size_limit(size) 104 | self._maxsize = size 105 | -------------------------------------------------------------------------------- /xarray/backends/memory.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import copy 4 | 5 | import numpy as np 6 | 7 | from xarray.backends.common import AbstractWritableDataStore 8 | from xarray.core.variable import Variable 9 | 10 | 11 | class InMemoryDataStore(AbstractWritableDataStore): 12 | """ 13 | Stores dimensions, variables and attributes in ordered dictionaries, making 14 | this store fast compared to stores which save to disk. 15 | 16 | This store exists purely for internal testing purposes. 17 | """ 18 | 19 | def __init__(self, variables=None, attributes=None): 20 | self._variables = {} if variables is None else variables 21 | self._attributes = {} if attributes is None else attributes 22 | 23 | def get_attrs(self): 24 | return self._attributes 25 | 26 | def get_variables(self): 27 | return self._variables 28 | 29 | def get_dimensions(self): 30 | return {d: s for v in self._variables.values() for d, s in v.dims.items()} 31 | 32 | def prepare_variable(self, k, v, *args, **kwargs): 33 | new_var = Variable(v.dims, np.empty_like(v), v.attrs) 34 | self._variables[k] = new_var 35 | return new_var, v.data 36 | 37 | def set_attribute(self, k, v): 38 | # copy to imitate writing to disk. 39 | self._attributes[k] = copy.deepcopy(v) 40 | 41 | def set_dimension(self, dim, length, unlimited_dims=None): 42 | # in this model, dimensions are accounted for in the variables 43 | pass 44 | -------------------------------------------------------------------------------- /xarray/backends/store.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from collections.abc import Iterable 4 | from typing import TYPE_CHECKING, Any 5 | 6 | from xarray import conventions 7 | from xarray.backends.common import ( 8 | BACKEND_ENTRYPOINTS, 9 | AbstractDataStore, 10 | BackendEntrypoint, 11 | ) 12 | from xarray.core.dataset import Dataset 13 | 14 | if TYPE_CHECKING: 15 | import os 16 | 17 | from xarray.core.types import ReadBuffer 18 | 19 | 20 | class StoreBackendEntrypoint(BackendEntrypoint): 21 | description = "Open AbstractDataStore instances in Xarray" 22 | url = "https://docs.xarray.dev/en/stable/generated/xarray.backends.StoreBackendEntrypoint.html" 23 | 24 | def guess_can_open( 25 | self, 26 | filename_or_obj: str | os.PathLike[Any] | ReadBuffer | AbstractDataStore, 27 | ) -> bool: 28 | return isinstance(filename_or_obj, AbstractDataStore) 29 | 30 | def open_dataset( 31 | self, 32 | filename_or_obj: str | os.PathLike[Any] | ReadBuffer | AbstractDataStore, 33 | *, 34 | mask_and_scale=True, 35 | decode_times=True, 36 | concat_characters=True, 37 | decode_coords=True, 38 | drop_variables: str | Iterable[str] | None = None, 39 | use_cftime=None, 40 | decode_timedelta=None, 41 | ) -> Dataset: 42 | assert isinstance(filename_or_obj, AbstractDataStore) 43 | 44 | vars, attrs = filename_or_obj.load() 45 | encoding = filename_or_obj.get_encoding() 46 | 47 | vars, attrs, coord_names = conventions.decode_cf_variables( 48 | vars, 49 | attrs, 50 | mask_and_scale=mask_and_scale, 51 | decode_times=decode_times, 52 | concat_characters=concat_characters, 53 | decode_coords=decode_coords, 54 | drop_variables=drop_variables, 55 | use_cftime=use_cftime, 56 | decode_timedelta=decode_timedelta, 57 | ) 58 | 59 | ds = Dataset(vars, attrs=attrs) 60 | ds = ds.set_coords(coord_names.intersection(vars)) 61 | ds.set_close(filename_or_obj.close) 62 | ds.encoding = encoding 63 | 64 | return ds 65 | 66 | 67 | BACKEND_ENTRYPOINTS["store"] = (None, StoreBackendEntrypoint) 68 | -------------------------------------------------------------------------------- /xarray/coders.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module provides coder objects that encapsulate the 3 | "encoding/decoding" process. 4 | """ 5 | 6 | from xarray.coding.times import CFDatetimeCoder, CFTimedeltaCoder 7 | 8 | __all__ = ["CFDatetimeCoder", "CFTimedeltaCoder"] 9 | -------------------------------------------------------------------------------- /xarray/coding/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/coding/__init__.py -------------------------------------------------------------------------------- /xarray/compat/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/compat/__init__.py -------------------------------------------------------------------------------- /xarray/compat/array_api_compat.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from xarray.namedarray.pycompat import array_type 4 | 5 | 6 | def is_weak_scalar_type(t): 7 | return isinstance(t, bool | int | float | complex | str | bytes) 8 | 9 | 10 | def _future_array_api_result_type(*arrays_and_dtypes, xp): 11 | # fallback implementation for `xp.result_type` with python scalars. Can be removed once a 12 | # version of the Array API that includes https://github.com/data-apis/array-api/issues/805 13 | # can be required 14 | strongly_dtyped = [t for t in arrays_and_dtypes if not is_weak_scalar_type(t)] 15 | weakly_dtyped = [t for t in arrays_and_dtypes if is_weak_scalar_type(t)] 16 | 17 | if not strongly_dtyped: 18 | strongly_dtyped = [ 19 | xp.asarray(x) if not isinstance(x, type) else x for x in weakly_dtyped 20 | ] 21 | weakly_dtyped = [] 22 | 23 | dtype = xp.result_type(*strongly_dtyped) 24 | if not weakly_dtyped: 25 | return dtype 26 | 27 | possible_dtypes = { 28 | complex: "complex64", 29 | float: "float32", 30 | int: "int8", 31 | bool: "bool", 32 | str: "str", 33 | bytes: "bytes", 34 | } 35 | dtypes = [possible_dtypes.get(type(x), "object") for x in weakly_dtyped] 36 | 37 | return xp.result_type(dtype, *dtypes) 38 | 39 | 40 | def result_type(*arrays_and_dtypes, xp) -> np.dtype: 41 | if xp is np or any( 42 | isinstance(getattr(t, "dtype", t), np.dtype) for t in arrays_and_dtypes 43 | ): 44 | return xp.result_type(*arrays_and_dtypes) 45 | else: 46 | return _future_array_api_result_type(*arrays_and_dtypes, xp=xp) 47 | 48 | 49 | def get_array_namespace(*values): 50 | def _get_single_namespace(x): 51 | if hasattr(x, "__array_namespace__"): 52 | return x.__array_namespace__() 53 | elif isinstance(x, array_type("cupy")): 54 | # cupy is fully compliant from xarray's perspective, but will not expose 55 | # __array_namespace__ until at least v14. Special case it for now 56 | import cupy as cp 57 | 58 | return cp 59 | else: 60 | return np 61 | 62 | namespaces = {_get_single_namespace(t) for t in values} 63 | non_numpy = namespaces - {np} 64 | 65 | if len(non_numpy) > 1: 66 | names = [module.__name__ for module in non_numpy] 67 | raise TypeError(f"Mixed array types {names} are not supported.") 68 | elif non_numpy: 69 | [xp] = non_numpy 70 | else: 71 | xp = np 72 | 73 | return xp 74 | 75 | 76 | def to_like_array(array, like): 77 | # Mostly for cupy compatibility, because cupy binary ops require all cupy arrays 78 | xp = get_array_namespace(like) 79 | if xp is not np: 80 | return xp.asarray(array) 81 | # avoid casting things like pint quantities to numpy arrays 82 | return array 83 | -------------------------------------------------------------------------------- /xarray/compat/dask_array_compat.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from xarray.namedarray.utils import module_available 4 | 5 | 6 | def reshape_blockwise( 7 | x: Any, 8 | shape: int | tuple[int, ...], 9 | chunks: tuple[tuple[int, ...], ...] | None = None, 10 | ): 11 | if module_available("dask", "2024.08.2"): 12 | from dask.array import reshape_blockwise 13 | 14 | return reshape_blockwise(x, shape=shape, chunks=chunks) 15 | else: 16 | return x.reshape(shape) 17 | 18 | 19 | def sliding_window_view( 20 | x, window_shape, axis=None, *, automatic_rechunk=True, **kwargs 21 | ): 22 | # Backcompat for handling `automatic_rechunk`, delete when dask>=2024.11.0 23 | # Note that subok, writeable are unsupported by dask, so we ignore those in kwargs 24 | from dask.array.lib.stride_tricks import sliding_window_view 25 | 26 | if module_available("dask", "2024.11.0"): 27 | return sliding_window_view( 28 | x, window_shape=window_shape, axis=axis, automatic_rechunk=automatic_rechunk 29 | ) 30 | else: 31 | # automatic_rechunk is not supported 32 | return sliding_window_view(x, window_shape=window_shape, axis=axis) 33 | -------------------------------------------------------------------------------- /xarray/compat/npcompat.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005-2011, NumPy Developers. 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | 11 | # * Redistributions in binary form must reproduce the above 12 | # copyright notice, this list of conditions and the following 13 | # disclaimer in the documentation and/or other materials provided 14 | # with the distribution. 15 | 16 | # * Neither the name of the NumPy Developers nor the names of any 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from __future__ import annotations 32 | 33 | from typing import Any 34 | 35 | try: 36 | # requires numpy>=2.0 37 | from numpy import isdtype # type: ignore[attr-defined,unused-ignore] 38 | 39 | HAS_STRING_DTYPE = True 40 | except ImportError: 41 | import numpy as np 42 | from numpy.typing import DTypeLike 43 | 44 | kind_mapping = { 45 | "bool": np.bool_, 46 | "signed integer": np.signedinteger, 47 | "unsigned integer": np.unsignedinteger, 48 | "integral": np.integer, 49 | "real floating": np.floating, 50 | "complex floating": np.complexfloating, 51 | "numeric": np.number, 52 | } 53 | 54 | def isdtype( 55 | dtype: np.dtype[Any] | type[Any], kind: DTypeLike | tuple[DTypeLike, ...] 56 | ) -> bool: 57 | kinds = kind if isinstance(kind, tuple) else (kind,) 58 | str_kinds = {k for k in kinds if isinstance(k, str)} 59 | type_kinds = {k.type for k in kinds if isinstance(k, np.dtype)} 60 | 61 | if unknown_kind_types := set(kinds) - str_kinds - type_kinds: 62 | raise TypeError( 63 | f"kind must be str, np.dtype or a tuple of these, got {unknown_kind_types}" 64 | ) 65 | if unknown_kinds := {k for k in str_kinds if k not in kind_mapping}: 66 | raise ValueError( 67 | f"unknown kind: {unknown_kinds}, must be a np.dtype or one of {list(kind_mapping)}" 68 | ) 69 | 70 | # verified the dtypes already, no need to check again 71 | translated_kinds = {kind_mapping[k] for k in str_kinds} | type_kinds 72 | if isinstance(dtype, np.generic): 73 | return isinstance(dtype, translated_kinds) 74 | else: 75 | return any(np.issubdtype(dtype, k) for k in translated_kinds) 76 | 77 | HAS_STRING_DTYPE = False 78 | -------------------------------------------------------------------------------- /xarray/compat/pdcompat.py: -------------------------------------------------------------------------------- 1 | # For reference, here is a copy of the pandas copyright notice: 2 | 3 | # BSD 3-Clause License 4 | 5 | # Copyright (c) 2008-2011, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team 6 | # All rights reserved. 7 | 8 | # Copyright (c) 2011-2025, Open source contributors. 9 | 10 | # Redistribution and use in source and binary forms, with or without 11 | # modification, are permitted provided that the following conditions are met: 12 | 13 | # * Redistributions of source code must retain the above copyright notice, this 14 | # list of conditions and the following disclaimer. 15 | 16 | # * Redistributions in binary form must reproduce the above copyright notice, 17 | # this list of conditions and the following disclaimer in the documentation 18 | # and/or other materials provided with the distribution. 19 | 20 | # * Neither the name of the copyright holder nor the names of its 21 | # contributors may be used to endorse or promote products derived from 22 | # this software without specific prior written permission. 23 | 24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | 35 | from __future__ import annotations 36 | 37 | from enum import Enum 38 | from typing import Literal 39 | 40 | import pandas as pd 41 | 42 | from xarray.core.types import PDDatetimeUnitOptions 43 | 44 | 45 | def count_not_none(*args) -> int: 46 | """Compute the number of non-None arguments. 47 | 48 | Copied from pandas.core.common.count_not_none (not part of the public API) 49 | """ 50 | return sum(arg is not None for arg in args) 51 | 52 | 53 | class _NoDefault(Enum): 54 | """Used by pandas to specify a default value for a deprecated argument. 55 | Copied from pandas._libs.lib._NoDefault. 56 | 57 | See also: 58 | - pandas-dev/pandas#30788 59 | - pandas-dev/pandas#40684 60 | - pandas-dev/pandas#40715 61 | - pandas-dev/pandas#47045 62 | """ 63 | 64 | no_default = "NO_DEFAULT" 65 | 66 | def __repr__(self) -> str: 67 | return "" 68 | 69 | 70 | no_default = ( 71 | _NoDefault.no_default 72 | ) # Sentinel indicating the default value following pandas 73 | NoDefault = Literal[_NoDefault.no_default] # For typing following pandas 74 | 75 | 76 | def timestamp_as_unit(date: pd.Timestamp, unit: PDDatetimeUnitOptions) -> pd.Timestamp: 77 | """Convert the underlying int64 representation to the given unit. 78 | 79 | Compatibility function for pandas issue where "as_unit" is not defined 80 | for pandas.Timestamp in pandas versions < 2.2. Can be removed minimum 81 | pandas version is >= 2.2. 82 | """ 83 | if hasattr(date, "as_unit"): 84 | date = date.as_unit(unit) 85 | elif hasattr(date, "_as_unit"): 86 | date = date._as_unit(unit) 87 | return date 88 | 89 | 90 | def default_precision_timestamp(*args, **kwargs) -> pd.Timestamp: 91 | """Return a Timestamp object with the default precision. 92 | 93 | Xarray default is "ns". 94 | """ 95 | dt = pd.Timestamp(*args, **kwargs) 96 | if dt.unit != "ns": 97 | dt = timestamp_as_unit(dt, "ns") 98 | return dt 99 | -------------------------------------------------------------------------------- /xarray/compat/toolzcompat.py: -------------------------------------------------------------------------------- 1 | # This file contains functions copied from the toolz library in accordance 2 | # with its license. The original copyright notice is duplicated below. 3 | 4 | # Copyright (c) 2013 Matthew Rocklin 5 | 6 | # All rights reserved. 7 | 8 | # Redistribution and use in source and binary forms, with or without 9 | # modification, are permitted provided that the following conditions are met: 10 | 11 | # a. Redistributions of source code must retain the above copyright notice, 12 | # this list of conditions and the following disclaimer. 13 | # b. Redistributions in binary form must reproduce the above copyright 14 | # notice, this list of conditions and the following disclaimer in the 15 | # documentation and/or other materials provided with the distribution. 16 | # c. Neither the name of toolz nor the names of its contributors 17 | # may be used to endorse or promote products derived from this software 18 | # without specific prior written permission. 19 | 20 | 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR 25 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 31 | # DAMAGE. 32 | 33 | 34 | def sliding_window(n, seq): 35 | """A sequence of overlapping subsequences 36 | 37 | >>> list(sliding_window(2, [1, 2, 3, 4])) 38 | [(1, 2), (2, 3), (3, 4)] 39 | 40 | This function creates a sliding window suitable for transformations like 41 | sliding means / smoothing 42 | 43 | >>> mean = lambda seq: float(sum(seq)) / len(seq) 44 | >>> list(map(mean, sliding_window(2, [1, 2, 3, 4]))) 45 | [1.5, 2.5, 3.5] 46 | """ 47 | import collections 48 | import itertools 49 | 50 | return zip( 51 | *( 52 | collections.deque(itertools.islice(it, i), 0) or it 53 | for i, it in enumerate(itertools.tee(seq, n)) 54 | ), 55 | strict=False, 56 | ) 57 | -------------------------------------------------------------------------------- /xarray/computation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/computation/__init__.py -------------------------------------------------------------------------------- /xarray/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/core/__init__.py -------------------------------------------------------------------------------- /xarray/core/coordinate_transform.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from collections.abc import Hashable, Iterable, Mapping 4 | from typing import Any, overload 5 | 6 | import numpy as np 7 | 8 | 9 | class CoordinateTransform: 10 | """Abstract coordinate transform with dimension & coordinate names. 11 | 12 | EXPERIMENTAL (not ready for public use yet). 13 | 14 | """ 15 | 16 | coord_names: tuple[Hashable, ...] 17 | dims: tuple[str, ...] 18 | dim_size: dict[str, int] 19 | dtype: Any 20 | 21 | def __init__( 22 | self, 23 | coord_names: Iterable[Hashable], 24 | dim_size: Mapping[str, int], 25 | dtype: Any = None, 26 | ): 27 | self.coord_names = tuple(coord_names) 28 | self.dims = tuple(dim_size) 29 | self.dim_size = dict(dim_size) 30 | 31 | if dtype is None: 32 | dtype = np.dtype(np.float64) 33 | self.dtype = dtype 34 | 35 | def forward(self, dim_positions: dict[str, Any]) -> dict[Hashable, Any]: 36 | """Perform grid -> world coordinate transformation. 37 | 38 | Parameters 39 | ---------- 40 | dim_positions : dict 41 | Grid location(s) along each dimension (axis). 42 | 43 | Returns 44 | ------- 45 | coord_labels : dict 46 | World coordinate labels. 47 | 48 | """ 49 | # TODO: cache the results in order to avoid re-computing 50 | # all labels when accessing the values of each coordinate one at a time 51 | raise NotImplementedError 52 | 53 | def reverse(self, coord_labels: dict[Hashable, Any]) -> dict[str, Any]: 54 | """Perform world -> grid coordinate reverse transformation. 55 | 56 | Parameters 57 | ---------- 58 | labels : dict 59 | World coordinate labels. 60 | 61 | Returns 62 | ------- 63 | dim_positions : dict 64 | Grid relative location(s) along each dimension (axis). 65 | 66 | """ 67 | raise NotImplementedError 68 | 69 | @overload 70 | def equals(self, other: CoordinateTransform) -> bool: ... 71 | 72 | @overload 73 | def equals( 74 | self, other: CoordinateTransform, *, exclude: frozenset[Hashable] | None = None 75 | ) -> bool: ... 76 | 77 | def equals(self, other: CoordinateTransform, **kwargs) -> bool: 78 | """Check equality with another CoordinateTransform of the same kind. 79 | 80 | Parameters 81 | ---------- 82 | other : CoordinateTransform 83 | The other Index object to compare with this object. 84 | exclude : frozenset of hashable, optional 85 | Dimensions excluded from checking. It is None by default, (i.e., 86 | when this method is not called in the context of alignment). For a 87 | n-dimensional transform this option allows a CoordinateTransform to 88 | optionally ignore any dimension in ``exclude`` when comparing 89 | ``self`` with ``other``. For a 1-dimensional transform this kwarg 90 | can be safely ignored, as this method is not called when all of the 91 | transform's dimensions are also excluded from alignment. 92 | """ 93 | raise NotImplementedError 94 | 95 | def generate_coords( 96 | self, dims: tuple[str, ...] | None = None 97 | ) -> dict[Hashable, Any]: 98 | """Compute all coordinate labels at once.""" 99 | if dims is None: 100 | dims = self.dims 101 | 102 | positions = np.meshgrid( 103 | *[np.arange(self.dim_size[d]) for d in dims], 104 | indexing="ij", 105 | ) 106 | dim_positions = {dim: positions[i] for i, dim in enumerate(dims)} 107 | 108 | return self.forward(dim_positions) 109 | -------------------------------------------------------------------------------- /xarray/core/dataset_utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import typing 4 | from collections.abc import Hashable, Mapping 5 | from typing import Any, Generic 6 | 7 | import pandas as pd 8 | 9 | from xarray.core import utils 10 | from xarray.core.common import _contains_datetime_like_objects 11 | from xarray.core.indexing import map_index_queries 12 | from xarray.core.types import T_Dataset 13 | from xarray.core.variable import IndexVariable, Variable 14 | 15 | if typing.TYPE_CHECKING: 16 | from xarray.core.dataset import Dataset 17 | 18 | 19 | class _LocIndexer(Generic[T_Dataset]): 20 | __slots__ = ("dataset",) 21 | 22 | def __init__(self, dataset: T_Dataset): 23 | self.dataset = dataset 24 | 25 | def __getitem__(self, key: Mapping[Any, Any]) -> T_Dataset: 26 | if not utils.is_dict_like(key): 27 | raise TypeError("can only lookup dictionaries from Dataset.loc") 28 | return self.dataset.sel(key) 29 | 30 | def __setitem__(self, key, value) -> None: 31 | if not utils.is_dict_like(key): 32 | raise TypeError( 33 | "can only set locations defined by dictionaries from Dataset.loc." 34 | f" Got: {key}" 35 | ) 36 | 37 | # set new values 38 | dim_indexers = map_index_queries(self.dataset, key).dim_indexers 39 | self.dataset[dim_indexers] = value 40 | 41 | 42 | def as_dataset(obj: Any) -> Dataset: 43 | """Cast the given object to a Dataset. 44 | 45 | Handles Datasets, DataArrays and dictionaries of variables. A new Dataset 46 | object is only created if the provided object is not already one. 47 | """ 48 | from xarray.core.dataset import Dataset 49 | 50 | if hasattr(obj, "to_dataset"): 51 | obj = obj.to_dataset() 52 | if not isinstance(obj, Dataset): 53 | obj = Dataset(obj) 54 | return obj 55 | 56 | 57 | def _get_virtual_variable( 58 | variables, key: Hashable, dim_sizes: Mapping | None = None 59 | ) -> tuple[Hashable, Hashable, Variable]: 60 | """Get a virtual variable (e.g., 'time.year') from a dict of xarray.Variable 61 | objects (if possible) 62 | 63 | """ 64 | from xarray.core.dataarray import DataArray 65 | 66 | if dim_sizes is None: 67 | dim_sizes = {} 68 | 69 | if key in dim_sizes: 70 | data = pd.Index(range(dim_sizes[key]), name=key) 71 | variable = IndexVariable((key,), data) 72 | return key, key, variable 73 | 74 | if not isinstance(key, str): 75 | raise KeyError(key) 76 | 77 | split_key = key.split(".", 1) 78 | if len(split_key) != 2: 79 | raise KeyError(key) 80 | 81 | ref_name, var_name = split_key 82 | ref_var = variables[ref_name] 83 | 84 | if _contains_datetime_like_objects(ref_var): 85 | ref_var = DataArray(ref_var) 86 | data = getattr(ref_var.dt, var_name).data 87 | else: 88 | data = getattr(ref_var, var_name).data 89 | virtual_var = Variable(ref_var.dims, data) 90 | 91 | return ref_name, var_name, virtual_var 92 | -------------------------------------------------------------------------------- /xarray/core/dataset_variables.py: -------------------------------------------------------------------------------- 1 | import typing 2 | from collections.abc import Hashable, Iterator, Mapping 3 | from typing import Any 4 | 5 | import numpy as np 6 | 7 | from xarray.core import formatting 8 | from xarray.core.utils import Frozen 9 | from xarray.core.variable import Variable 10 | 11 | if typing.TYPE_CHECKING: 12 | from xarray.core.dataarray import DataArray 13 | from xarray.core.dataset import Dataset 14 | 15 | 16 | class DataVariables(Mapping[Any, "DataArray"]): 17 | __slots__ = ("_dataset",) 18 | 19 | def __init__(self, dataset: "Dataset"): 20 | self._dataset = dataset 21 | 22 | def __iter__(self) -> Iterator[Hashable]: 23 | return ( 24 | key 25 | for key in self._dataset._variables 26 | if key not in self._dataset._coord_names 27 | ) 28 | 29 | def __len__(self) -> int: 30 | length = len(self._dataset._variables) - len(self._dataset._coord_names) 31 | assert length >= 0, "something is wrong with Dataset._coord_names" 32 | return length 33 | 34 | def __contains__(self, key: Hashable) -> bool: 35 | return key in self._dataset._variables and key not in self._dataset._coord_names 36 | 37 | def __getitem__(self, key: Hashable) -> "DataArray": 38 | if key not in self._dataset._coord_names: 39 | return self._dataset[key] 40 | raise KeyError(key) 41 | 42 | def __repr__(self) -> str: 43 | return formatting.data_vars_repr(self) 44 | 45 | @property 46 | def variables(self) -> Mapping[Hashable, Variable]: 47 | all_variables = self._dataset.variables 48 | return Frozen({k: all_variables[k] for k in self}) 49 | 50 | @property 51 | def dtypes(self) -> Frozen[Hashable, np.dtype]: 52 | """Mapping from data variable names to dtypes. 53 | 54 | Cannot be modified directly, but is updated when adding new variables. 55 | 56 | See Also 57 | -------- 58 | Dataset.dtype 59 | """ 60 | return self._dataset.dtypes 61 | 62 | def _ipython_key_completions_(self): 63 | """Provide method for the key-autocompletions in IPython.""" 64 | return [ 65 | key 66 | for key in self._dataset._ipython_key_completions_() 67 | if key not in self._dataset._coord_names 68 | ] 69 | -------------------------------------------------------------------------------- /xarray/indexes/__init__.py: -------------------------------------------------------------------------------- 1 | """Xarray index objects for label-based selection and alignment of Dataset / 2 | DataArray objects. 3 | 4 | """ 5 | 6 | from xarray.core.indexes import ( 7 | Index, 8 | PandasIndex, 9 | PandasMultiIndex, 10 | ) 11 | from xarray.indexes.range_index import RangeIndex 12 | 13 | __all__ = ["Index", "PandasIndex", "PandasMultiIndex", "RangeIndex"] 14 | -------------------------------------------------------------------------------- /xarray/namedarray/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/namedarray/__init__.py -------------------------------------------------------------------------------- /xarray/plot/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Use this module directly: 3 | import xarray.plot as xplt 4 | 5 | Or use the methods on a DataArray or Dataset: 6 | DataArray.plot._____ 7 | Dataset.plot._____ 8 | """ 9 | 10 | from xarray.plot.dataarray_plot import ( 11 | contour, 12 | contourf, 13 | hist, 14 | imshow, 15 | line, 16 | pcolormesh, 17 | plot, 18 | step, 19 | surface, 20 | ) 21 | from xarray.plot.dataset_plot import scatter 22 | from xarray.plot.facetgrid import FacetGrid 23 | 24 | __all__ = [ 25 | "FacetGrid", 26 | "contour", 27 | "contourf", 28 | "hist", 29 | "imshow", 30 | "line", 31 | "pcolormesh", 32 | "plot", 33 | "scatter", 34 | "step", 35 | "surface", 36 | ] 37 | -------------------------------------------------------------------------------- /xarray/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/py.typed -------------------------------------------------------------------------------- /xarray/static/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/static/__init__.py -------------------------------------------------------------------------------- /xarray/static/css/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/static/css/__init__.py -------------------------------------------------------------------------------- /xarray/static/html/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/static/html/__init__.py -------------------------------------------------------------------------------- /xarray/static/html/icons-svg-inline.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /xarray/structure/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/structure/__init__.py -------------------------------------------------------------------------------- /xarray/testing/__init__.py: -------------------------------------------------------------------------------- 1 | from xarray.testing.assertions import ( # noqa: F401 2 | _assert_dataarray_invariants, 3 | _assert_dataset_invariants, 4 | _assert_indexes_invariants_checks, 5 | _assert_internal_invariants, 6 | _assert_variable_invariants, 7 | _data_allclose_or_equiv, 8 | assert_allclose, 9 | assert_chunks_equal, 10 | assert_duckarray_allclose, 11 | assert_duckarray_equal, 12 | assert_equal, 13 | assert_identical, 14 | assert_isomorphic, 15 | ) 16 | 17 | __all__ = [ 18 | "assert_allclose", 19 | "assert_chunks_equal", 20 | "assert_duckarray_allclose", 21 | "assert_duckarray_equal", 22 | "assert_equal", 23 | "assert_identical", 24 | "assert_isomorphic", 25 | ] 26 | -------------------------------------------------------------------------------- /xarray/tests/data/bears.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/tests/data/bears.nc -------------------------------------------------------------------------------- /xarray/tests/data/example.grib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/tests/data/example.grib -------------------------------------------------------------------------------- /xarray/tests/data/example.ict: -------------------------------------------------------------------------------- 1 | 29, 1001 2 | Henderson, Barron 3 | U.S. EPA 4 | Example file with artificial data 5 | JUST_A_TEST 6 | 1, 1 7 | 2018, 04, 27 2018, 04, 27 8 | 0 9 | Start_UTC 10 | 5 11 | 1, 1, 1, 1, 1 12 | -9999, -9999, -9999, -9999, -9999 13 | lat, degrees_north 14 | lon, degrees_east 15 | elev, meters 16 | TEST_ppbv, ppbv 17 | TESTM_ppbv, ppbv 18 | 0 19 | 9 20 | INDEPENDENT_VARIABLE_DEFINITION: Start_UTC 21 | INDEPENDENT_VARIABLE_UNITS: Start_UTC 22 | ULOD_FLAG: -7777 23 | ULOD_VALUE: N/A 24 | LLOD_FLAG: -8888 25 | LLOD_VALUE: N/A, N/A, N/A, N/A, 0.025 26 | OTHER_COMMENTS: www-air.larc.nasa.gov/missions/etc/IcarttDataFormat.htm 27 | REVISION: R0 28 | R0: No comments for this revision. 29 | Start_UTC, lat, lon, elev, TEST_ppbv, TESTM_ppbv 30 | 43200, 41.00000, -71.00000, 5, 1.2345, 2.220 31 | 46800, 42.00000, -72.00000, 15, 2.3456, -9999 32 | 50400, 42.00000, -73.00000, 20, 3.4567, -7777 33 | 50400, 42.00000, -74.00000, 25, 4.5678, -8888 34 | -------------------------------------------------------------------------------- /xarray/tests/data/example.uamiv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/tests/data/example.uamiv -------------------------------------------------------------------------------- /xarray/tests/data/example_1.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/tests/data/example_1.nc -------------------------------------------------------------------------------- /xarray/tests/data/example_1.nc.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/tests/data/example_1.nc.gz -------------------------------------------------------------------------------- /xarray/tests/namespace.py: -------------------------------------------------------------------------------- 1 | from xarray.core import duck_array_ops 2 | 3 | 4 | def reshape(array, shape, **kwargs): 5 | return type(array)(duck_array_ops.reshape(array.array, shape=shape, **kwargs)) 6 | -------------------------------------------------------------------------------- /xarray/tests/test_backends_common.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import numpy as np 4 | import pytest 5 | 6 | from xarray.backends.common import _infer_dtype, robust_getitem 7 | 8 | 9 | class DummyFailure(Exception): 10 | pass 11 | 12 | 13 | class DummyArray: 14 | def __init__(self, failures): 15 | self.failures = failures 16 | 17 | def __getitem__(self, key): 18 | if self.failures: 19 | self.failures -= 1 20 | raise DummyFailure 21 | return "success" 22 | 23 | 24 | def test_robust_getitem() -> None: 25 | array = DummyArray(failures=2) 26 | with pytest.raises(DummyFailure): 27 | array[...] 28 | result = robust_getitem(array, ..., catch=DummyFailure, initial_delay=1) 29 | assert result == "success" 30 | 31 | array = DummyArray(failures=3) 32 | with pytest.raises(DummyFailure): 33 | robust_getitem(array, ..., catch=DummyFailure, initial_delay=1, max_retries=2) 34 | 35 | 36 | @pytest.mark.parametrize( 37 | "data", 38 | [ 39 | np.array([["ab", "cdef", b"X"], [1, 2, "c"]], dtype=object), 40 | np.array([["x", 1], ["y", 2]], dtype="object"), 41 | ], 42 | ) 43 | def test_infer_dtype_error_on_mixed_types(data): 44 | with pytest.raises(ValueError, match="unable to infer dtype on variable"): 45 | _infer_dtype(data, "test") 46 | -------------------------------------------------------------------------------- /xarray/tests/test_backends_locks.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import threading 4 | 5 | from xarray.backends import locks 6 | 7 | 8 | def test_threaded_lock() -> None: 9 | lock1 = locks._get_threaded_lock("foo") 10 | assert isinstance(lock1, type(threading.Lock())) 11 | lock2 = locks._get_threaded_lock("foo") 12 | assert lock1 is lock2 13 | 14 | lock3 = locks._get_threaded_lock("bar") 15 | assert lock1 is not lock3 16 | -------------------------------------------------------------------------------- /xarray/tests/test_backends_lru_cache.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import Any 4 | from unittest import mock 5 | 6 | import pytest 7 | 8 | from xarray.backends.lru_cache import LRUCache 9 | 10 | 11 | def test_simple() -> None: 12 | cache: LRUCache[Any, Any] = LRUCache(maxsize=2) 13 | cache["x"] = 1 14 | cache["y"] = 2 15 | 16 | assert cache["x"] == 1 17 | assert cache["y"] == 2 18 | assert len(cache) == 2 19 | assert dict(cache) == {"x": 1, "y": 2} 20 | assert list(cache.keys()) == ["x", "y"] 21 | assert list(cache.items()) == [("x", 1), ("y", 2)] 22 | 23 | cache["z"] = 3 24 | assert len(cache) == 2 25 | assert list(cache.items()) == [("y", 2), ("z", 3)] 26 | 27 | 28 | def test_trivial() -> None: 29 | cache: LRUCache[Any, Any] = LRUCache(maxsize=0) 30 | cache["x"] = 1 31 | assert len(cache) == 0 32 | 33 | 34 | def test_invalid() -> None: 35 | with pytest.raises(TypeError): 36 | LRUCache(maxsize=None) # type: ignore[arg-type] 37 | with pytest.raises(ValueError): 38 | LRUCache(maxsize=-1) 39 | 40 | 41 | def test_update_priority() -> None: 42 | cache: LRUCache[Any, Any] = LRUCache(maxsize=2) 43 | cache["x"] = 1 44 | cache["y"] = 2 45 | assert list(cache) == ["x", "y"] 46 | assert "x" in cache # contains 47 | assert list(cache) == ["y", "x"] 48 | assert cache["y"] == 2 # getitem 49 | assert list(cache) == ["x", "y"] 50 | cache["x"] = 3 # setitem 51 | assert list(cache.items()) == [("y", 2), ("x", 3)] 52 | 53 | 54 | def test_del() -> None: 55 | cache: LRUCache[Any, Any] = LRUCache(maxsize=2) 56 | cache["x"] = 1 57 | cache["y"] = 2 58 | del cache["x"] 59 | assert dict(cache) == {"y": 2} 60 | 61 | 62 | def test_on_evict() -> None: 63 | on_evict = mock.Mock() 64 | cache = LRUCache(maxsize=1, on_evict=on_evict) 65 | cache["x"] = 1 66 | cache["y"] = 2 67 | on_evict.assert_called_once_with("x", 1) 68 | 69 | 70 | def test_on_evict_trivial() -> None: 71 | on_evict = mock.Mock() 72 | cache = LRUCache(maxsize=0, on_evict=on_evict) 73 | cache["x"] = 1 74 | on_evict.assert_called_once_with("x", 1) 75 | 76 | 77 | def test_resize() -> None: 78 | cache: LRUCache[Any, Any] = LRUCache(maxsize=2) 79 | assert cache.maxsize == 2 80 | cache["w"] = 0 81 | cache["x"] = 1 82 | cache["y"] = 2 83 | assert list(cache.items()) == [("x", 1), ("y", 2)] 84 | cache.maxsize = 10 85 | cache["z"] = 3 86 | assert list(cache.items()) == [("x", 1), ("y", 2), ("z", 3)] 87 | cache.maxsize = 1 88 | assert list(cache.items()) == [("z", 3)] 89 | 90 | with pytest.raises(ValueError): 91 | cache.maxsize = -1 92 | -------------------------------------------------------------------------------- /xarray/tests/test_cupy.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import numpy as np 4 | import pandas as pd 5 | import pytest 6 | 7 | import xarray as xr 8 | 9 | cp = pytest.importorskip("cupy") 10 | 11 | 12 | @pytest.fixture 13 | def toy_weather_data(): 14 | """Construct the example DataSet from the Toy weather data example. 15 | 16 | https://docs.xarray.dev/en/stable/examples/weather-data.html 17 | 18 | Here we construct the DataSet exactly as shown in the example and then 19 | convert the numpy arrays to cupy. 20 | 21 | """ 22 | np.random.seed(123) 23 | times = pd.date_range("2000-01-01", "2001-12-31", name="time") 24 | annual_cycle = np.sin(2 * np.pi * (times.dayofyear.values / 365.25 - 0.28)) 25 | 26 | base = 10 + 15 * annual_cycle.reshape(-1, 1) 27 | tmin_values = base + 3 * np.random.randn(annual_cycle.size, 3) 28 | tmax_values = base + 10 + 3 * np.random.randn(annual_cycle.size, 3) 29 | 30 | ds = xr.Dataset( 31 | { 32 | "tmin": (("time", "location"), tmin_values), 33 | "tmax": (("time", "location"), tmax_values), 34 | }, 35 | {"time": times, "location": ["IA", "IN", "IL"]}, 36 | ) 37 | 38 | ds.tmax.data = cp.asarray(ds.tmax.data) 39 | ds.tmin.data = cp.asarray(ds.tmin.data) 40 | 41 | return ds 42 | 43 | 44 | def test_cupy_import() -> None: 45 | """Check the import worked.""" 46 | assert cp 47 | 48 | 49 | def test_check_data_stays_on_gpu(toy_weather_data) -> None: 50 | """Perform some operations and check the data stays on the GPU.""" 51 | freeze = (toy_weather_data["tmin"] <= 0).groupby("time.month").mean("time") 52 | assert isinstance(freeze.data, cp.ndarray) 53 | 54 | 55 | def test_where() -> None: 56 | from xarray.core.duck_array_ops import where 57 | 58 | data = cp.zeros(10) 59 | 60 | output = where(data < 1, 1, data).all() 61 | assert output 62 | assert isinstance(output, cp.ndarray) 63 | -------------------------------------------------------------------------------- /xarray/tests/test_error_messages.py: -------------------------------------------------------------------------------- 1 | """ 2 | This new file is intended to test the quality & friendliness of error messages that are 3 | raised by xarray. It's currently separate from the standard tests, which are more 4 | focused on the functions working (though we could consider integrating them.). 5 | """ 6 | 7 | import pytest 8 | 9 | 10 | def test_no_var_in_dataset(ds): 11 | with pytest.raises( 12 | KeyError, 13 | match=( 14 | r"No variable named 'foo'. Variables on the dataset include \['z1', 'z2', 'x', 'time', 'c', 'y'\]" 15 | ), 16 | ): 17 | ds["foo"] 18 | -------------------------------------------------------------------------------- /xarray/tests/test_extensions.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pickle 4 | 5 | import pytest 6 | 7 | import xarray as xr 8 | from xarray.core.extensions import register_datatree_accessor 9 | from xarray.tests import assert_identical 10 | 11 | 12 | @register_datatree_accessor("example_accessor") 13 | @xr.register_dataset_accessor("example_accessor") 14 | @xr.register_dataarray_accessor("example_accessor") 15 | class ExampleAccessor: 16 | """For the pickling tests below.""" 17 | 18 | def __init__(self, xarray_obj): 19 | self.obj = xarray_obj 20 | 21 | 22 | class TestAccessor: 23 | def test_register(self) -> None: 24 | @register_datatree_accessor("demo") 25 | @xr.register_dataset_accessor("demo") 26 | @xr.register_dataarray_accessor("demo") 27 | class DemoAccessor: 28 | """Demo accessor.""" 29 | 30 | def __init__(self, xarray_obj): 31 | self._obj = xarray_obj 32 | 33 | @property 34 | def foo(self): 35 | return "bar" 36 | 37 | dt: xr.DataTree = xr.DataTree() 38 | assert dt.demo.foo == "bar" 39 | 40 | ds = xr.Dataset() 41 | assert ds.demo.foo == "bar" 42 | 43 | da = xr.DataArray(0) 44 | assert da.demo.foo == "bar" 45 | # accessor is cached 46 | assert ds.demo is ds.demo 47 | 48 | # check descriptor 49 | assert ds.demo.__doc__ == "Demo accessor." 50 | # TODO: typing doesn't seem to work with accessors 51 | assert xr.Dataset.demo.__doc__ == "Demo accessor." # type: ignore[attr-defined] 52 | assert isinstance(ds.demo, DemoAccessor) 53 | assert xr.Dataset.demo is DemoAccessor # type: ignore[attr-defined] 54 | 55 | # ensure we can remove it 56 | del xr.Dataset.demo # type: ignore[attr-defined] 57 | assert not hasattr(xr.Dataset, "demo") 58 | 59 | with pytest.warns(Warning, match="overriding a preexisting attribute"): 60 | 61 | @xr.register_dataarray_accessor("demo") 62 | class Foo: 63 | pass 64 | 65 | # it didn't get registered again 66 | assert not hasattr(xr.Dataset, "demo") 67 | 68 | def test_pickle_dataset(self) -> None: 69 | ds = xr.Dataset() 70 | ds_restored = pickle.loads(pickle.dumps(ds)) 71 | assert_identical(ds, ds_restored) 72 | 73 | # state save on the accessor is restored 74 | assert ds.example_accessor is ds.example_accessor 75 | ds.example_accessor.value = "foo" 76 | ds_restored = pickle.loads(pickle.dumps(ds)) 77 | assert_identical(ds, ds_restored) 78 | assert ds_restored.example_accessor.value == "foo" 79 | 80 | def test_pickle_dataarray(self) -> None: 81 | array = xr.Dataset() 82 | assert array.example_accessor is array.example_accessor 83 | array_restored = pickle.loads(pickle.dumps(array)) 84 | assert_identical(array, array_restored) 85 | 86 | def test_broken_accessor(self) -> None: 87 | # regression test for GH933 88 | 89 | @xr.register_dataset_accessor("stupid_accessor") 90 | class BrokenAccessor: 91 | def __init__(self, xarray_obj): 92 | raise AttributeError("broken") 93 | 94 | with pytest.raises(RuntimeError, match=r"error initializing"): 95 | _ = xr.Dataset().stupid_accessor 96 | -------------------------------------------------------------------------------- /xarray/tests/test_hashable.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from enum import Enum 4 | from typing import TYPE_CHECKING, Union 5 | 6 | import pytest 7 | 8 | from xarray import DataArray, Dataset, Variable 9 | 10 | if TYPE_CHECKING: 11 | from xarray.core.types import TypeAlias 12 | 13 | DimT: TypeAlias = Union[int, tuple, "DEnum", "CustomHashable"] 14 | 15 | 16 | class DEnum(Enum): 17 | dim = "dim" 18 | 19 | 20 | class CustomHashable: 21 | def __init__(self, a: int) -> None: 22 | self.a = a 23 | 24 | def __hash__(self) -> int: 25 | return self.a 26 | 27 | 28 | parametrize_dim = pytest.mark.parametrize( 29 | "dim", 30 | [ 31 | pytest.param(5, id="int"), 32 | pytest.param(("a", "b"), id="tuple"), 33 | pytest.param(DEnum.dim, id="enum"), 34 | pytest.param(CustomHashable(3), id="HashableObject"), 35 | ], 36 | ) 37 | 38 | 39 | @parametrize_dim 40 | def test_hashable_dims(dim: DimT) -> None: 41 | v = Variable([dim], [1, 2, 3]) 42 | da = DataArray([1, 2, 3], dims=[dim]) 43 | Dataset({"a": ([dim], [1, 2, 3])}) 44 | 45 | # alternative constructors 46 | DataArray(v) 47 | Dataset({"a": v}) 48 | Dataset({"a": da}) 49 | 50 | 51 | @parametrize_dim 52 | def test_dataset_variable_hashable_names(dim: DimT) -> None: 53 | Dataset({dim: ("x", [1, 2, 3])}) 54 | -------------------------------------------------------------------------------- /xarray/tests/test_nputils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import numpy as np 4 | from numpy.testing import assert_array_equal 5 | 6 | from xarray.core.nputils import NumpyVIndexAdapter, _is_contiguous 7 | 8 | 9 | def test_is_contiguous() -> None: 10 | assert _is_contiguous([1]) 11 | assert _is_contiguous([1, 2, 3]) 12 | assert not _is_contiguous([1, 3]) 13 | 14 | 15 | def test_vindex() -> None: 16 | x = np.arange(3 * 4 * 5).reshape((3, 4, 5)) 17 | vindex = NumpyVIndexAdapter(x) 18 | 19 | # getitem 20 | assert_array_equal(vindex[0], x[0]) 21 | assert_array_equal(vindex[[1, 2], [1, 2]], x[[1, 2], [1, 2]]) 22 | assert vindex[[0, 1], [0, 1], :].shape == (2, 5) 23 | assert vindex[[0, 1], :, [0, 1]].shape == (2, 4) 24 | assert vindex[:, [0, 1], [0, 1]].shape == (2, 3) 25 | 26 | # setitem 27 | vindex[:] = 0 28 | assert_array_equal(x, np.zeros_like(x)) 29 | # assignment should not raise 30 | vindex[[0, 1], [0, 1], :] = vindex[[0, 1], [0, 1], :] 31 | vindex[[0, 1], :, [0, 1]] = vindex[[0, 1], :, [0, 1]] 32 | vindex[:, [0, 1], [0, 1]] = vindex[:, [0, 1], [0, 1]] 33 | -------------------------------------------------------------------------------- /xarray/tests/test_print_versions.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import io 4 | 5 | import xarray 6 | 7 | 8 | def test_show_versions() -> None: 9 | f = io.StringIO() 10 | xarray.show_versions(file=f) 11 | assert "INSTALLED VERSIONS" in f.getvalue() 12 | -------------------------------------------------------------------------------- /xarray/tests/test_tutorial.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from xarray import DataArray, DataTree, tutorial 4 | from xarray.testing import assert_identical 5 | from xarray.tests import network 6 | 7 | 8 | @network 9 | class TestLoadDataset: 10 | def test_download_from_github(self, tmp_path) -> None: 11 | cache_dir = tmp_path / tutorial._default_cache_dir_name 12 | ds = tutorial.open_dataset("tiny", cache_dir=cache_dir).load() 13 | tiny = DataArray(range(5), name="tiny").to_dataset() 14 | assert_identical(ds, tiny) 15 | 16 | def test_download_from_github_load_without_cache( 17 | self, tmp_path, monkeypatch 18 | ) -> None: 19 | cache_dir = tmp_path / tutorial._default_cache_dir_name 20 | 21 | ds_nocache = tutorial.open_dataset( 22 | "tiny", cache=False, cache_dir=cache_dir 23 | ).load() 24 | ds_cache = tutorial.open_dataset("tiny", cache_dir=cache_dir).load() 25 | assert_identical(ds_cache, ds_nocache) 26 | 27 | 28 | @network 29 | class TestLoadDataTree: 30 | def test_download_from_github(self, tmp_path) -> None: 31 | cache_dir = tmp_path / tutorial._default_cache_dir_name 32 | ds = tutorial.open_datatree("tiny", cache_dir=cache_dir).load() 33 | tiny = DataTree.from_dict({"/": DataArray(range(5), name="tiny").to_dataset()}) 34 | assert_identical(ds, tiny) 35 | 36 | def test_download_from_github_load_without_cache( 37 | self, tmp_path, monkeypatch 38 | ) -> None: 39 | cache_dir = tmp_path / tutorial._default_cache_dir_name 40 | 41 | ds_nocache = tutorial.open_datatree( 42 | "tiny", cache=False, cache_dir=cache_dir 43 | ).load() 44 | ds_cache = tutorial.open_datatree("tiny", cache_dir=cache_dir).load() 45 | assert_identical(ds_cache, ds_nocache) 46 | -------------------------------------------------------------------------------- /xarray/typing.py: -------------------------------------------------------------------------------- 1 | """ 2 | Public typing utilities for use by external libraries. 3 | """ 4 | 5 | from xarray.computation.rolling import ( 6 | DataArrayCoarsen, 7 | DataArrayRolling, 8 | DatasetRolling, 9 | ) 10 | from xarray.computation.weighted import DataArrayWeighted, DatasetWeighted, Weighted 11 | from xarray.core.groupby import DataArrayGroupBy 12 | from xarray.core.resample import DataArrayResample 13 | 14 | __all__ = [ 15 | "DataArrayCoarsen", 16 | "DataArrayGroupBy", 17 | "DataArrayResample", 18 | "DataArrayRolling", 19 | "DataArrayWeighted", 20 | "DatasetRolling", 21 | "DatasetWeighted", 22 | "Weighted", 23 | ] 24 | -------------------------------------------------------------------------------- /xarray/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pydata/xarray/34efef2192a65e0f26a340ae305b0d3ed9e91b19/xarray/util/__init__.py --------------------------------------------------------------------------------