├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── how_to.md └── workflows │ ├── ci.yml │ ├── formatting.yml │ ├── pull-request-linting.yml │ └── release_package.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yml ├── CITATION.cff ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── cliff.toml ├── docs ├── Makefile ├── __init__.py ├── _static │ └── custom.css ├── _templates │ ├── custom-class-template.rst │ └── custom-module-template.rst ├── conf.py ├── content │ ├── api_reference │ │ ├── _autosummary │ │ │ ├── xeofs.RotatorFactory.rst │ │ │ ├── xeofs.cross.CCA.rst │ │ │ ├── xeofs.cross.CPCCA.rst │ │ │ ├── xeofs.cross.CPCCARotator.rst │ │ │ ├── xeofs.cross.ComplexCCA.rst │ │ │ ├── xeofs.cross.ComplexCPCCA.rst │ │ │ ├── xeofs.cross.ComplexCPCCARotator.rst │ │ │ ├── xeofs.cross.ComplexMCA.rst │ │ │ ├── xeofs.cross.ComplexMCARotator.rst │ │ │ ├── xeofs.cross.ComplexRDA.rst │ │ │ ├── xeofs.cross.HilbertCCA.rst │ │ │ ├── xeofs.cross.HilbertCPCCA.rst │ │ │ ├── xeofs.cross.HilbertCPCCARotator.rst │ │ │ ├── xeofs.cross.HilbertMCA.rst │ │ │ ├── xeofs.cross.HilbertMCARotator.rst │ │ │ ├── xeofs.cross.HilbertRDA.rst │ │ │ ├── xeofs.cross.MCA.rst │ │ │ ├── xeofs.cross.MCARotator.rst │ │ │ ├── xeofs.cross.RDA.rst │ │ │ ├── xeofs.multi.CCA.rst │ │ │ ├── xeofs.single.ComplexEOF.rst │ │ │ ├── xeofs.single.ComplexEOFRotator.rst │ │ │ ├── xeofs.single.EOF.rst │ │ │ ├── xeofs.single.EOFRotator.rst │ │ │ ├── xeofs.single.ExtendedEOF.rst │ │ │ ├── xeofs.single.GWPCA.rst │ │ │ ├── xeofs.single.HilbertEOF.rst │ │ │ ├── xeofs.single.HilbertEOFRotator.rst │ │ │ ├── xeofs.single.OPA.rst │ │ │ ├── xeofs.single.POP.rst │ │ │ ├── xeofs.single.SparsePCA.rst │ │ │ └── xeofs.validation.EOFBootstrapper.rst │ │ ├── cross_set_analysis.rst │ │ ├── index.rst │ │ ├── model_evaluation.rst │ │ ├── multi_set_analysis.rst │ │ ├── single_set_analysis.rst │ │ └── utilities.rst │ ├── contributing.rst │ ├── user_guide │ │ ├── auto_examples │ │ │ ├── 1single │ │ │ │ ├── images │ │ │ │ │ ├── sphx_glr_plot_complex_eof_001.png │ │ │ │ │ ├── sphx_glr_plot_complex_eof_002.png │ │ │ │ │ ├── sphx_glr_plot_eeof_001.png │ │ │ │ │ ├── sphx_glr_plot_eeof_002.png │ │ │ │ │ ├── sphx_glr_plot_eeof_003.png │ │ │ │ │ ├── sphx_glr_plot_eeof_trend_001.png │ │ │ │ │ ├── sphx_glr_plot_eeof_trend_002.png │ │ │ │ │ ├── sphx_glr_plot_eeof_trend_003.png │ │ │ │ │ ├── sphx_glr_plot_eof-smode_001.png │ │ │ │ │ ├── sphx_glr_plot_eof-tmode_001.png │ │ │ │ │ ├── sphx_glr_plot_gwpca_001.png │ │ │ │ │ ├── sphx_glr_plot_gwpca_002.png │ │ │ │ │ ├── sphx_glr_plot_hilbert_eof_001.png │ │ │ │ │ ├── sphx_glr_plot_hilbert_eof_002.png │ │ │ │ │ ├── sphx_glr_plot_hilbert_eof_003.png │ │ │ │ │ ├── sphx_glr_plot_hilbert_eof_004.png │ │ │ │ │ ├── sphx_glr_plot_mreof_001.png │ │ │ │ │ ├── sphx_glr_plot_multivariate-eof_001.png │ │ │ │ │ ├── sphx_glr_plot_rotated_eof_001.png │ │ │ │ │ ├── sphx_glr_plot_weighted-eof_001.png │ │ │ │ │ └── thumb │ │ │ │ │ │ ├── sphx_glr_plot_complex_eof_thumb.png │ │ │ │ │ │ ├── sphx_glr_plot_eeof_thumb.png │ │ │ │ │ │ ├── sphx_glr_plot_eeof_trend_thumb.png │ │ │ │ │ │ ├── sphx_glr_plot_eof-smode_thumb.png │ │ │ │ │ │ ├── sphx_glr_plot_eof-tmode_thumb.png │ │ │ │ │ │ ├── sphx_glr_plot_gwpca_thumb.png │ │ │ │ │ │ ├── sphx_glr_plot_hilbert_eof_thumb.png │ │ │ │ │ │ ├── sphx_glr_plot_mreof_thumb.png │ │ │ │ │ │ ├── sphx_glr_plot_multivariate-eof_thumb.png │ │ │ │ │ │ ├── sphx_glr_plot_rotated_eof_thumb.png │ │ │ │ │ │ └── sphx_glr_plot_weighted-eof_thumb.png │ │ │ │ ├── index.rst │ │ │ │ ├── plot_complex_eof.ipynb │ │ │ │ ├── plot_complex_eof.py │ │ │ │ ├── plot_complex_eof.py.md5 │ │ │ │ ├── plot_complex_eof.rst │ │ │ │ ├── plot_complex_eof_codeobj.pickle │ │ │ │ ├── plot_eeof.ipynb │ │ │ │ ├── plot_eeof.py │ │ │ │ ├── plot_eeof.py.md5 │ │ │ │ ├── plot_eeof.rst │ │ │ │ ├── plot_eeof_codeobj.pickle │ │ │ │ ├── plot_eeof_trend.ipynb │ │ │ │ ├── plot_eeof_trend.py │ │ │ │ ├── plot_eeof_trend.py.md5 │ │ │ │ ├── plot_eeof_trend.rst │ │ │ │ ├── plot_eeof_trend_codeobj.pickle │ │ │ │ ├── plot_eof-smode.ipynb │ │ │ │ ├── plot_eof-smode.py │ │ │ │ ├── plot_eof-smode.py.md5 │ │ │ │ ├── plot_eof-smode.rst │ │ │ │ ├── plot_eof-smode_codeobj.pickle │ │ │ │ ├── plot_eof-tmode.ipynb │ │ │ │ ├── plot_eof-tmode.py │ │ │ │ ├── plot_eof-tmode.py.md5 │ │ │ │ ├── plot_eof-tmode.rst │ │ │ │ ├── plot_eof-tmode_codeobj.pickle │ │ │ │ ├── plot_gwpca.ipynb │ │ │ │ ├── plot_gwpca.py │ │ │ │ ├── plot_gwpca.py.md5 │ │ │ │ ├── plot_gwpca.rst │ │ │ │ ├── plot_gwpca_codeobj.pickle │ │ │ │ ├── plot_hilbert_eof.ipynb │ │ │ │ ├── plot_hilbert_eof.py │ │ │ │ ├── plot_hilbert_eof.py.md5 │ │ │ │ ├── plot_hilbert_eof.rst │ │ │ │ ├── plot_hilbert_eof_codeobj.pickle │ │ │ │ ├── plot_mreof.ipynb │ │ │ │ ├── plot_mreof.py │ │ │ │ ├── plot_mreof.py.md5 │ │ │ │ ├── plot_mreof.rst │ │ │ │ ├── plot_mreof_codeobj.pickle │ │ │ │ ├── plot_multivariate-eof.ipynb │ │ │ │ ├── plot_multivariate-eof.py │ │ │ │ ├── plot_multivariate-eof.py.md5 │ │ │ │ ├── plot_multivariate-eof.rst │ │ │ │ ├── plot_multivariate-eof_codeobj.pickle │ │ │ │ ├── plot_rotated_eof.ipynb │ │ │ │ ├── plot_rotated_eof.py │ │ │ │ ├── plot_rotated_eof.py.md5 │ │ │ │ ├── plot_rotated_eof.rst │ │ │ │ ├── plot_rotated_eof_codeobj.pickle │ │ │ │ ├── plot_weighted-eof.ipynb │ │ │ │ ├── plot_weighted-eof.py │ │ │ │ ├── plot_weighted-eof.py.md5 │ │ │ │ ├── plot_weighted-eof.rst │ │ │ │ ├── plot_weighted-eof_codeobj.pickle │ │ │ │ └── sg_execution_times.rst │ │ │ ├── 2cross │ │ │ │ ├── images │ │ │ │ │ ├── sphx_glr_plot_mca_001.png │ │ │ │ │ ├── sphx_glr_plot_rotated_mca_001.png │ │ │ │ │ └── thumb │ │ │ │ │ │ ├── sphx_glr_plot_mca_thumb.png │ │ │ │ │ │ └── sphx_glr_plot_rotated_mca_thumb.png │ │ │ │ ├── index.rst │ │ │ │ ├── plot_mca.ipynb │ │ │ │ ├── plot_mca.py │ │ │ │ ├── plot_mca.py.md5 │ │ │ │ ├── plot_mca.rst │ │ │ │ ├── plot_mca_codeobj.pickle │ │ │ │ ├── plot_rotated_mca.ipynb │ │ │ │ ├── plot_rotated_mca.py │ │ │ │ ├── plot_rotated_mca.py.md5 │ │ │ │ ├── plot_rotated_mca.rst │ │ │ │ ├── plot_rotated_mca_codeobj.pickle │ │ │ │ └── sg_execution_times.rst │ │ │ ├── 3multi │ │ │ │ ├── images │ │ │ │ │ ├── sphx_glr_plot_cca_001.png │ │ │ │ │ ├── sphx_glr_plot_cca_002.png │ │ │ │ │ └── thumb │ │ │ │ │ │ └── sphx_glr_plot_cca_thumb.png │ │ │ │ ├── index.rst │ │ │ │ ├── plot_cca.ipynb │ │ │ │ ├── plot_cca.py │ │ │ │ ├── plot_cca.py.md5 │ │ │ │ ├── plot_cca.rst │ │ │ │ ├── plot_cca_codeobj.pickle │ │ │ │ └── sg_execution_times.rst │ │ │ ├── 4validation │ │ │ │ ├── images │ │ │ │ │ ├── sphx_glr_plot_bootstrap_001.png │ │ │ │ │ └── thumb │ │ │ │ │ │ └── sphx_glr_plot_bootstrap_thumb.png │ │ │ │ ├── index.rst │ │ │ │ ├── plot_bootstrap.ipynb │ │ │ │ ├── plot_bootstrap.py │ │ │ │ ├── plot_bootstrap.py.md5 │ │ │ │ ├── plot_bootstrap.rst │ │ │ │ ├── plot_bootstrap_codeobj.pickle │ │ │ │ └── sg_execution_times.rst │ │ │ ├── auto_examples_jupyter.zip │ │ │ ├── auto_examples_python.zip │ │ │ └── index.rst │ │ ├── core_functionalities │ │ │ ├── dask_support.rst │ │ │ ├── efficient.rst │ │ │ ├── index.rst │ │ │ ├── labeled_data.rst │ │ │ ├── missing_values.rst │ │ │ ├── model_evaluation.rst │ │ │ └── model_serialization.rst │ │ ├── examples │ │ │ ├── 1single │ │ │ │ ├── README.rst │ │ │ │ ├── eof-smode.jpg │ │ │ │ ├── eof-tmode.jpg │ │ │ │ ├── mreof-analysis.jpg │ │ │ │ ├── multivariate-eof-analysis.jpg │ │ │ │ ├── plot_complex_eof.py │ │ │ │ ├── plot_eeof.png │ │ │ │ ├── plot_eeof.py │ │ │ │ ├── plot_eeof_trend.py │ │ │ │ ├── plot_eof-smode.py │ │ │ │ ├── plot_eof-tmode.py │ │ │ │ ├── plot_gwpca.py │ │ │ │ ├── plot_hilbert_eof.py │ │ │ │ ├── plot_mreof.py │ │ │ │ ├── plot_multivariate-eof.py │ │ │ │ ├── plot_rotated_eof.py │ │ │ │ ├── plot_weighted-eof.py │ │ │ │ ├── rotated_eof.jpg │ │ │ │ ├── sparse_pca.jpg │ │ │ │ └── weighted_eof.jpg │ │ │ ├── 2cross │ │ │ │ ├── README.rst │ │ │ │ ├── mca.jpg │ │ │ │ ├── plot_mca.py │ │ │ │ ├── plot_rotated_mca.py │ │ │ │ └── rotated_mca.jpg │ │ │ ├── 3multi │ │ │ │ ├── README.rst │ │ │ │ └── plot_cca.py │ │ │ ├── 4validation │ │ │ │ ├── README.rst │ │ │ │ ├── bootstrap.jpg │ │ │ │ └── plot_bootstrap.py │ │ │ └── README.rst │ │ ├── how_to_cite.rst │ │ ├── index.rst │ │ ├── installation.rst │ │ ├── migration_v3.rst │ │ ├── model_implementation.rst │ │ ├── quickstart.ipynb │ │ ├── should_i_use_this.rst │ │ └── why.rst │ └── whats_new │ │ └── CHANGELOG.md ├── environment.yml ├── img │ ├── example_sst_rotated_pca_dark.png │ ├── example_sst_rotated_pca_light.png │ ├── timings_dark.png │ └── timings_light.png ├── index.rst ├── logos │ ├── favicon.ico │ ├── xeofs_logo_dark.png │ ├── xeofs_logo_dark_aspect21.png │ ├── xeofs_logo_icon.png │ └── xeofs_logo_light.png ├── make.bat └── perf │ ├── figure_timings.py │ ├── timings.nc │ ├── timings_dark.png │ ├── timings_light.png │ └── xeofs_timings.py ├── pyproject.toml ├── tests ├── __init__.py ├── conftest.py ├── data │ └── sample_data.nc ├── data_container │ └── __init__.py ├── linalg │ ├── __init__.py │ └── test_decomposer.py ├── models │ ├── __init__.py │ ├── cross │ │ ├── __init__.py │ │ ├── test_cca.py │ │ ├── test_cpcca.py │ │ ├── test_cpcca_complex_rotator.py │ │ ├── test_cpcca_rotator.py │ │ ├── test_hilbert_cpcca.py │ │ ├── test_hilbert_mca.py │ │ ├── test_hilbert_mca_rotator.py │ │ ├── test_mca.py │ │ ├── test_mca_rotator.py │ │ └── test_rda.py │ ├── multi │ │ ├── __init__.py │ │ └── test_cca.py │ ├── single │ │ ├── __init__.py │ │ ├── test_eeof.py │ │ ├── test_eof.py │ │ ├── test_eof_rotator.py │ │ ├── test_gwpca.py │ │ ├── test_hilbert_eof.py │ │ ├── test_hilbert_eof_rotator.py │ │ ├── test_opa.py │ │ ├── test_pop.py │ │ └── test_sparse_pca.py │ └── test_rotator_factory.py ├── preprocessing │ ├── __init__.py │ ├── test_multiindex_converter_dataarray.py │ ├── test_multiindex_converter_dataset.py │ ├── test_pca.py │ ├── test_preprocessor_dataarray.py │ ├── test_preprocessor_datalist.py │ ├── test_preprocessor_dataset.py │ ├── test_renamer_dataarray.py │ ├── test_renamer_dataset.py │ ├── test_sanitizer.py │ ├── test_scaler_dataarray.py │ ├── test_scaler_dataset.py │ ├── test_stacker_dataarray.py │ ├── test_stacker_dataset.py │ └── test_whitener.py ├── utilities.py ├── utils │ ├── __init__.py │ ├── test_dimension_renamer.py │ └── test_total_variance.py └── validation │ ├── __init__.py │ └── test_eof_bootstrapper.py └── xeofs ├── __init__.py ├── _version.py ├── base_model.py ├── cross ├── __init__.py ├── base_model_cross_set.py ├── cca.py ├── cpcca.py ├── cpcca_rotator.py ├── mca.py ├── mca_rotator.py └── rda.py ├── data_container ├── __init__.py └── data_container.py ├── linalg ├── __init__.py ├── _numpy │ ├── __init__.py │ ├── _rotation.py │ ├── _svd.py │ └── _utils.py ├── decomposer.py ├── rotation.py ├── svd.py └── utils.py ├── multi ├── __init__.py └── cca.py ├── preprocessing ├── __init__.py ├── concatenator.py ├── dimension_renamer.py ├── list_processor.py ├── multi_index_converter.py ├── pca.py ├── preprocessor.py ├── sanitizer.py ├── scaler.py ├── stacker.py ├── transformer.py └── whitener.py ├── rotator_factory.py ├── single ├── __init__.py ├── _numpy │ ├── __init__.py │ └── _sparse_pca.py ├── base_model_single_set.py ├── eeof.py ├── eof.py ├── eof_rotator.py ├── gwpca.py ├── opa.py ├── pop.py └── sparse_pca.py ├── utils ├── __init__.py ├── constants.py ├── data_types.py ├── dimension_renamer.py ├── hilbert_transform.py ├── io.py ├── optional │ ├── __init__.py │ ├── distance_metrics.py │ ├── kernels.py │ ├── numba_utils.py │ └── statistics.py ├── sanity_checks.py └── xarray_utils.py └── validation ├── __init__.py ├── _base_bootstrapper.py └── bootstrapper.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: You think you found a glitch in the code? Please let us know! 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Reproducible Minimal Working Example** 14 | Provide a concise Python code snippet that demonstrates the issue. To display the code clearly, use [GitHub Flavored Markdown](http://github.github.com/github-flavored-markdown/) for formatting: 15 | 16 | ```python 17 | import xeofs as xe 18 | 19 | model = xe.single.EOF() 20 | ... 21 | ``` 22 | 23 | **Expected behavior** 24 | A clear and concise description of what you expected to happen. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Ubuntu] 28 | - `xeofs` version [e.g. 1.2.0] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Have an idea to improve xeofs? Share it with us! 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/how_to.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: How To...? 3 | about: Need help using xeofs? Get guidance here! 4 | title: '' 5 | labels: documentation 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the model you want to implement** 11 | A clear and concise description of your goal. 12 | 13 | **Reproducible Minimal Working Example** 14 | Provide a concise Python code snippet that demonstrates your approach. To display the code clearly, use [GitHub Flavored Markdown](http://github.github.com/github-flavored-markdown/) for formatting: 15 | 16 | ```python 17 | import xeofs as xe 18 | 19 | model = xe.single.EOF() 20 | ... 21 | ``` 22 | 23 | **Your problem** 24 | A clear and concise description of what your problem is. 25 | 26 | **Declaration** 27 | - [ ] I have consulted the [documentation](https://xeofs.readthedocs.io/en/latest/) but could not find a solution. 28 | 29 | 30 | **Desktop (please complete the following information):** 31 | - OS: [e.g. Ubuntu] 32 | - `xeofs` version [e.g. 1.2.0] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # Basic workflow for building and testing the pip installation 2 | 3 | name: Continuous Integration 4 | 5 | on: 6 | pull_request: 7 | branches: [ main, develop ] 8 | types: [opened, synchronize, reopened, edited] 9 | workflow_dispatch: # Allows you to run this workflow manually from the Actions tab 10 | 11 | 12 | jobs: 13 | test: 14 | name: py${{ matrix.versions.python-version }} ${{ matrix.versions.resolution }} ${{ matrix.deps.name}} 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | versions: 19 | - python-version: '3.10' 20 | resolution: lowest-direct 21 | - python-version: '3.11' 22 | resolution: highest 23 | - python-version: '3.12' 24 | resolution: highest 25 | deps: 26 | - name: minimal 27 | value: '[dev]' 28 | doctest: '' # doctest runs MCA and requires statsmodels 29 | - name: complete 30 | value: '[dev,complete]' 31 | doctest: '--doctest-glob=README.md' 32 | 33 | steps: 34 | - uses: actions/checkout@v4 35 | 36 | - name: Setup Python ${{ matrix.versions.python-version }} 37 | uses: actions/setup-python@v5 38 | with: 39 | python-version: ${{ matrix.versions.python-version }} 40 | 41 | - name: Install dependencies 42 | run: | 43 | pip install uv 44 | uv pip install .${{ matrix.deps.value }} -r pyproject.toml \ 45 | --system --resolution ${{ matrix.versions.resolution }} 46 | 47 | - name: Execute Tests 48 | run: | 49 | coverage run -m pytest -n auto ${{ matrix.deps.doctest }} 50 | coverage report -m 51 | coverage xml 52 | 53 | - name: Upload Coverage Report to Codecov 54 | uses: codecov/codecov-action@v3 55 | with: 56 | files: ./coverage.xml 57 | -------------------------------------------------------------------------------- /.github/workflows/formatting.yml: -------------------------------------------------------------------------------- 1 | name: Formatting 2 | 3 | on: 4 | pull_request: 5 | branches: [main, develop] 6 | types: [opened, synchronize, reopened, edited] 7 | workflow_dispatch: # Allows you to run this workflow manually from the Actions tab 8 | 9 | 10 | jobs: 11 | ruff: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Install Python 16 | uses: actions/setup-python@v5 17 | with: 18 | python-version: "3.11" 19 | - name: Install dependencies 20 | run: | 21 | python -m pip install --upgrade pip 22 | pip install ruff 23 | # Update output format to enable automatic inline annotations. 24 | - name: Run Ruff 25 | run: | 26 | ruff check 27 | ruff format --check 28 | -------------------------------------------------------------------------------- /.github/workflows/pull-request-linting.yml: -------------------------------------------------------------------------------- 1 | # Start action: 2 | # ---------------- 3 | # semantic-pull-request 4 | 5 | # Aim: ensure that PR title matches the concentional commits spec 6 | # More info: https://github.com/marketplace/actions/semantic-pull-request 7 | 8 | name: "PR Linting" 9 | 10 | on: 11 | pull_request_target: 12 | types: [opened, edited, synchronize] 13 | 14 | jobs: 15 | validate: 16 | name: validate conventional commit in title 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Run Semantic Pull Request Linting 20 | uses: amannn/action-semantic-pull-request@v5 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/release_package.yml: -------------------------------------------------------------------------------- 1 | name: Release Package After Merge 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: # Allows you to run this workflow manually from the Actions tab 8 | 9 | 10 | jobs: 11 | Release_Package: 12 | runs-on: ubuntu-latest 13 | concurrency: release 14 | permissions: 15 | id-token: write 16 | contents: write 17 | 18 | steps: 19 | - name: Checkout Repository 20 | uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | 24 | 25 | # This action uses Python Semantic Release v8 26 | - name: Python Semantic Release 27 | id: release 28 | uses: python-semantic-release/python-semantic-release@master 29 | with: 30 | github_token: ${{ secrets.GITHUB_TOKEN }} 31 | changelog: false 32 | 33 | - name: Publish package distributions to PyPI 34 | uses: pypa/gh-action-pypi-publish@release/v1 35 | # NOTE: DO NOT wrap the conditional in ${{ }} as it will always evaluate to true. 36 | # See https://github.com/actions/runner/issues/1173 37 | if: steps.release.outputs.released == 'true' 38 | with: 39 | password: ${{ secrets.PYPI_TOKEN }} 40 | 41 | - name: Publish package distributions to GitHub Releases 42 | uses: python-semantic-release/upload-to-gh-release@main 43 | if: steps.release.outputs.released == 'true' 44 | with: 45 | github_token: ${{ secrets.GITHUB_TOKEN }} 46 | 47 | # Update Changelog; this must be done after the release 48 | - name: Generate a changelog 49 | uses: orhun/git-cliff-action@v3 50 | id: git-cliff 51 | with: 52 | config: cliff.toml 53 | args: --verbose 54 | env: 55 | OUTPUT: docs/content/whats_new/CHANGELOG.md 56 | GITHUB_REPO: ${{ github.repository }} 57 | 58 | - name: Commit Changelog 59 | run: | 60 | git config user.name 'github-actions[bot]' 61 | git config user.email 'github-actions[bot]@users.noreply.github.com' 62 | set +e 63 | git add docs/content/whats_new/CHANGELOG.md 64 | git commit -m "docs(changelog): update changelog" 65 | git push https://${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git main 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Personal 2 | .vscode/ 3 | # Test related to CCA 4 | tests/**/test_cca_solution.py 5 | 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .nox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | *.py,cover 55 | .hypothesis/ 56 | .pytest_cache/ 57 | cover/ 58 | 59 | # Translations 60 | *.mo 61 | *.pot 62 | 63 | # Django stuff: 64 | *.log 65 | local_settings.py 66 | db.sqlite3 67 | db.sqlite3-journal 68 | 69 | # Flask stuff: 70 | instance/ 71 | .webassets-cache 72 | 73 | # Scrapy stuff: 74 | .scrapy 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | .pybuilder/ 81 | target/ 82 | 83 | # Jupyter Notebook 84 | .ipynb_checkpoints 85 | 86 | # IPython 87 | profile_default/ 88 | ipython_config.py 89 | 90 | # pyenv 91 | # For a library or package, you might want to ignore these files since the code is 92 | # intended to run in multiple environments; otherwise, check them in: 93 | # .python-version 94 | 95 | # pipenv 96 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 97 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 98 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 99 | # install all needed dependencies. 100 | #Pipfile.lock 101 | 102 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 103 | __pypackages__/ 104 | 105 | # Celery stuff 106 | celerybeat-schedule 107 | celerybeat.pid 108 | 109 | # SageMath parsed files 110 | *.sage.py 111 | 112 | # Environments 113 | .env 114 | .venv 115 | env/ 116 | venv/ 117 | ENV/ 118 | env.bak/ 119 | venv.bak/ 120 | 121 | # Spyder project settings 122 | .spyderproject 123 | .spyproject 124 | 125 | # Rope project settings 126 | .ropeproject 127 | 128 | # mkdocs documentation 129 | /site 130 | 131 | # mypy 132 | .mypy_cache/ 133 | .dmypy.json 134 | dmypy.json 135 | 136 | # Pyre type checker 137 | .pyre/ 138 | 139 | # pytype static type analyzer 140 | .pytype/ 141 | 142 | # Cython debug symbols 143 | cython_debug/ 144 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | 3 | # Conventional Commits 4 | - repo: https://github.com/compilerla/conventional-pre-commit 5 | rev: v1.2.0 6 | hooks: 7 | - id: conventional-pre-commit 8 | stages: [commit-msg] 9 | args: [] # optional: list of Conventional Commits types to allow 10 | # Lint and format with ruff 11 | - repo: https://github.com/astral-sh/ruff-pre-commit 12 | rev: v0.3.4 13 | hooks: 14 | - id: ruff 15 | args: [ --fix ] 16 | - id: ruff-format 17 | # Strip output from Jupyter Notebooks 18 | - repo: https://github.com/kynan/nbstripout 19 | rev: 0.6.1 20 | hooks: 21 | - id: nbstripout -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Use mamba instead of conda 9 | build: 10 | os: "ubuntu-20.04" 11 | tools: 12 | python: "mambaforge-4.10" 13 | 14 | 15 | # Build documentation in the docs/ directory with Sphinx 16 | sphinx: 17 | configuration: docs/conf.py 18 | 19 | # Optionally build your docs in additional formats such as PDF 20 | #formats: 21 | # - pdf 22 | 23 | # Optionally set the version of Python and requirements required to build your docs 24 | conda: 25 | environment: docs/environment.yml 26 | 27 | # python: 28 | # version: 3.7 29 | # install: 30 | # - requirements: requirements.txt 31 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: "Rieger" 5 | given-names: "Niclas" 6 | orcid: "https://orcid.org/0000-0003-3357-1742" 7 | - family-names: "Levang" 8 | given-names: "Samuel J." 9 | title: "xeofs" 10 | abstract: "Comprehensive EOF analysis in Python with xarray." 11 | license: MIT 12 | doi: 10.5281/zenodo.6323011 13 | url: "https://xeofs.readthedocs.io/en/latest/" 14 | repository-code: "https://github.com/xarray-contrib/xeofs" 15 | preferred-citation: 16 | authors: 17 | - family-names: "Rieger" 18 | given-names: "Niclas" 19 | orcid: "https://orcid.org/0000-0003-3357-1742" 20 | - family-names: "Levang" 21 | given-names: "Samuel J." 22 | date-published: "2024-01-02" 23 | doi: 10.21105/joss.06060 24 | issn: 2475-9066 25 | issue: 93 26 | journal: "Journal of Open Source Software" 27 | publisher: 28 | name: "Open Journals" 29 | start: 6060 30 | title: "xeofs: Comprehensive EOF analysis in Python with xarray" 31 | type: article 32 | url: "https://joss.theoj.org/papers/10.21105/joss.06060" 33 | volume: 9 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Niclas Rieger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/__init__.py -------------------------------------------------------------------------------- /docs/_static/custom.css: -------------------------------------------------------------------------------- 1 | 2 | html[data-theme="light"] { 3 | --pst-color-primary: #ea7317; 4 | --pst-color-secondary: #ea7217b2; 5 | --pst-color-info: #F9D264; 6 | --pst-color-warning: #F9D264; 7 | } 8 | 9 | html[data-theme="dark"] { 10 | --pst-color-primary: #ea7317; 11 | --pst-color-secondary: #ea7217b2; 12 | --pst-color-accent: #ea7217b2; 13 | --pst-color-info: #F9D264; 14 | --pst-color-target: #F9D264; 15 | --pst-color-warning: #F9D264; 16 | } 17 | 18 | 19 | ::-moz-selection { /* Code for Firefox */ 20 | color: #000000; 21 | background: #ea7317; 22 | } 23 | ::selection { 24 | color: #000000; 25 | background: #ea7317; 26 | } 27 | 28 | 29 | /* INFO admonition */ 30 | div.admonition.info { 31 | border-color: #286279; 32 | } 33 | div.admonition.info > .admonition-title:before { 34 | background-color: #286279; 35 | } 36 | div.admonition.info > .admonition-title:after { 37 | color: #286279; 38 | } 39 | 40 | /* Class for centering elements */ 41 | .center h1 { text-align: center; } 42 | .main-container .navbar-brand { 43 | justify-content: center; 44 | flex: 1; 45 | } -------------------------------------------------------------------------------- /docs/_templates/custom-class-template.rst: -------------------------------------------------------------------------------- 1 | {{ fullname | escape | underline}} 2 | 3 | .. currentmodule:: {{ module }} 4 | 5 | .. autoclass:: {{ objname }} 6 | :members: 7 | :inherited-members: 8 | 9 | {% block methods %} 10 | .. automethod:: __init__ 11 | 12 | {% if methods %} 13 | .. rubric:: {{ _('Methods') }} 14 | 15 | .. autosummary:: 16 | {% for item in methods %} 17 | ~{{ name }}.{{ item }} 18 | {%- endfor %} 19 | {% endif %} 20 | {% endblock %} 21 | 22 | {% block attributes %} 23 | {% if attributes %} 24 | .. rubric:: {{ _('Attributes') }} 25 | 26 | .. autosummary:: 27 | {% for item in attributes %} 28 | ~{{ name }}.{{ item }} 29 | {%- endfor %} 30 | {% endif %} 31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /docs/_templates/custom-module-template.rst: -------------------------------------------------------------------------------- 1 | {{ fullname | escape | underline}} 2 | 3 | .. automodule:: {{ fullname }} 4 | 5 | {% block attributes %} 6 | {% if attributes %} 7 | .. rubric:: Module Attributes 8 | 9 | .. autosummary:: 10 | :toctree: 11 | {% for item in attributes %} 12 | {{ item }} 13 | {%- endfor %} 14 | {% endif %} 15 | {% endblock %} 16 | 17 | {% block functions %} 18 | {% if functions %} 19 | .. rubric:: {{ _('Functions') }} 20 | 21 | .. autosummary:: 22 | :toctree: 23 | {% for item in functions %} 24 | {{ item }} 25 | {%- endfor %} 26 | {% endif %} 27 | {% endblock %} 28 | 29 | {% block classes %} 30 | {% if classes %} 31 | .. rubric:: {{ _('Classes') }} 32 | 33 | .. autosummary:: 34 | :toctree: 35 | :template: custom-class-template.rst 36 | {% for item in classes %} 37 | {{ item }} 38 | {%- endfor %} 39 | {% endif %} 40 | {% endblock %} 41 | 42 | {% block exceptions %} 43 | {% if exceptions %} 44 | .. rubric:: {{ _('Exceptions') }} 45 | 46 | .. autosummary:: 47 | :toctree: 48 | {% for item in exceptions %} 49 | {{ item }} 50 | {%- endfor %} 51 | {% endif %} 52 | {% endblock %} 53 | 54 | {% block modules %} 55 | {% if modules %} 56 | .. rubric:: Modules 57 | 58 | .. autosummary:: 59 | :toctree: 60 | :template: custom-module-template.rst 61 | :recursive: 62 | {% for item in modules %} 63 | {{ item }} 64 | {%- endfor %} 65 | {% endif %} 66 | {% endblock %} 67 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.RotatorFactory.rst: -------------------------------------------------------------------------------- 1 | RotatorFactory 2 | ============== 3 | 4 | .. currentmodule:: xeofs 5 | 6 | .. autoclass:: RotatorFactory 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~RotatorFactory.__init__ 19 | ~RotatorFactory.create_rotator 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.CCA.rst: -------------------------------------------------------------------------------- 1 | CCA 2 | === 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: CCA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~CCA.__init__ 19 | ~CCA.components 20 | ~CCA.compute 21 | ~CCA.correlation_coefficients_X 22 | ~CCA.correlation_coefficients_Y 23 | ~CCA.cross_correlation_coefficients 24 | ~CCA.deserialize 25 | ~CCA.fit 26 | ~CCA.fraction_variance_X_explained_by_X 27 | ~CCA.fraction_variance_Y_explained_by_X 28 | ~CCA.fraction_variance_Y_explained_by_Y 29 | ~CCA.get_params 30 | ~CCA.get_serialization_attrs 31 | ~CCA.heterogeneous_patterns 32 | ~CCA.homogeneous_patterns 33 | ~CCA.inverse_transform 34 | ~CCA.load 35 | ~CCA.predict 36 | ~CCA.save 37 | ~CCA.scores 38 | ~CCA.serialize 39 | ~CCA.squared_covariance_fraction 40 | ~CCA.transform 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.CPCCA.rst: -------------------------------------------------------------------------------- 1 | CPCCA 2 | ===== 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: CPCCA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~CPCCA.__init__ 19 | ~CPCCA.components 20 | ~CPCCA.compute 21 | ~CPCCA.correlation_coefficients_X 22 | ~CPCCA.correlation_coefficients_Y 23 | ~CPCCA.cross_correlation_coefficients 24 | ~CPCCA.deserialize 25 | ~CPCCA.fit 26 | ~CPCCA.fraction_variance_X_explained_by_X 27 | ~CPCCA.fraction_variance_Y_explained_by_X 28 | ~CPCCA.fraction_variance_Y_explained_by_Y 29 | ~CPCCA.get_params 30 | ~CPCCA.get_serialization_attrs 31 | ~CPCCA.heterogeneous_patterns 32 | ~CPCCA.homogeneous_patterns 33 | ~CPCCA.inverse_transform 34 | ~CPCCA.load 35 | ~CPCCA.predict 36 | ~CPCCA.save 37 | ~CPCCA.scores 38 | ~CPCCA.serialize 39 | ~CPCCA.squared_covariance_fraction 40 | ~CPCCA.transform 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.CPCCARotator.rst: -------------------------------------------------------------------------------- 1 | CPCCARotator 2 | ============ 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: CPCCARotator 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~CPCCARotator.__init__ 19 | ~CPCCARotator.components 20 | ~CPCCARotator.compute 21 | ~CPCCARotator.correlation_coefficients_X 22 | ~CPCCARotator.correlation_coefficients_Y 23 | ~CPCCARotator.cross_correlation_coefficients 24 | ~CPCCARotator.deserialize 25 | ~CPCCARotator.fit 26 | ~CPCCARotator.fraction_variance_X_explained_by_X 27 | ~CPCCARotator.fraction_variance_Y_explained_by_X 28 | ~CPCCARotator.fraction_variance_Y_explained_by_Y 29 | ~CPCCARotator.get_params 30 | ~CPCCARotator.get_serialization_attrs 31 | ~CPCCARotator.heterogeneous_patterns 32 | ~CPCCARotator.homogeneous_patterns 33 | ~CPCCARotator.inverse_transform 34 | ~CPCCARotator.load 35 | ~CPCCARotator.predict 36 | ~CPCCARotator.save 37 | ~CPCCARotator.scores 38 | ~CPCCARotator.serialize 39 | ~CPCCARotator.squared_covariance_fraction 40 | ~CPCCARotator.transform 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.ComplexCCA.rst: -------------------------------------------------------------------------------- 1 | ComplexCCA 2 | ========== 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: ComplexCCA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~ComplexCCA.__init__ 19 | ~ComplexCCA.components 20 | ~ComplexCCA.components_amplitude 21 | ~ComplexCCA.components_phase 22 | ~ComplexCCA.compute 23 | ~ComplexCCA.correlation_coefficients_X 24 | ~ComplexCCA.correlation_coefficients_Y 25 | ~ComplexCCA.cross_correlation_coefficients 26 | ~ComplexCCA.deserialize 27 | ~ComplexCCA.fit 28 | ~ComplexCCA.fraction_variance_X_explained_by_X 29 | ~ComplexCCA.fraction_variance_Y_explained_by_X 30 | ~ComplexCCA.fraction_variance_Y_explained_by_Y 31 | ~ComplexCCA.get_params 32 | ~ComplexCCA.get_serialization_attrs 33 | ~ComplexCCA.heterogeneous_patterns 34 | ~ComplexCCA.homogeneous_patterns 35 | ~ComplexCCA.inverse_transform 36 | ~ComplexCCA.load 37 | ~ComplexCCA.predict 38 | ~ComplexCCA.save 39 | ~ComplexCCA.scores 40 | ~ComplexCCA.scores_amplitude 41 | ~ComplexCCA.scores_phase 42 | ~ComplexCCA.serialize 43 | ~ComplexCCA.squared_covariance_fraction 44 | ~ComplexCCA.transform 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.ComplexCPCCA.rst: -------------------------------------------------------------------------------- 1 | ComplexCPCCA 2 | ============ 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: ComplexCPCCA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~ComplexCPCCA.__init__ 19 | ~ComplexCPCCA.components 20 | ~ComplexCPCCA.components_amplitude 21 | ~ComplexCPCCA.components_phase 22 | ~ComplexCPCCA.compute 23 | ~ComplexCPCCA.correlation_coefficients_X 24 | ~ComplexCPCCA.correlation_coefficients_Y 25 | ~ComplexCPCCA.cross_correlation_coefficients 26 | ~ComplexCPCCA.deserialize 27 | ~ComplexCPCCA.fit 28 | ~ComplexCPCCA.fraction_variance_X_explained_by_X 29 | ~ComplexCPCCA.fraction_variance_Y_explained_by_X 30 | ~ComplexCPCCA.fraction_variance_Y_explained_by_Y 31 | ~ComplexCPCCA.get_params 32 | ~ComplexCPCCA.get_serialization_attrs 33 | ~ComplexCPCCA.heterogeneous_patterns 34 | ~ComplexCPCCA.homogeneous_patterns 35 | ~ComplexCPCCA.inverse_transform 36 | ~ComplexCPCCA.load 37 | ~ComplexCPCCA.predict 38 | ~ComplexCPCCA.save 39 | ~ComplexCPCCA.scores 40 | ~ComplexCPCCA.scores_amplitude 41 | ~ComplexCPCCA.scores_phase 42 | ~ComplexCPCCA.serialize 43 | ~ComplexCPCCA.squared_covariance_fraction 44 | ~ComplexCPCCA.transform 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.ComplexCPCCARotator.rst: -------------------------------------------------------------------------------- 1 | ComplexCPCCARotator 2 | =================== 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: ComplexCPCCARotator 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~ComplexCPCCARotator.__init__ 19 | ~ComplexCPCCARotator.components 20 | ~ComplexCPCCARotator.components_amplitude 21 | ~ComplexCPCCARotator.components_phase 22 | ~ComplexCPCCARotator.compute 23 | ~ComplexCPCCARotator.correlation_coefficients_X 24 | ~ComplexCPCCARotator.correlation_coefficients_Y 25 | ~ComplexCPCCARotator.cross_correlation_coefficients 26 | ~ComplexCPCCARotator.deserialize 27 | ~ComplexCPCCARotator.fit 28 | ~ComplexCPCCARotator.fraction_variance_X_explained_by_X 29 | ~ComplexCPCCARotator.fraction_variance_Y_explained_by_X 30 | ~ComplexCPCCARotator.fraction_variance_Y_explained_by_Y 31 | ~ComplexCPCCARotator.get_params 32 | ~ComplexCPCCARotator.get_serialization_attrs 33 | ~ComplexCPCCARotator.heterogeneous_patterns 34 | ~ComplexCPCCARotator.homogeneous_patterns 35 | ~ComplexCPCCARotator.inverse_transform 36 | ~ComplexCPCCARotator.load 37 | ~ComplexCPCCARotator.predict 38 | ~ComplexCPCCARotator.save 39 | ~ComplexCPCCARotator.scores 40 | ~ComplexCPCCARotator.scores_amplitude 41 | ~ComplexCPCCARotator.scores_phase 42 | ~ComplexCPCCARotator.serialize 43 | ~ComplexCPCCARotator.squared_covariance_fraction 44 | ~ComplexCPCCARotator.transform 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.ComplexMCA.rst: -------------------------------------------------------------------------------- 1 | ComplexMCA 2 | ========== 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: ComplexMCA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~ComplexMCA.__init__ 19 | ~ComplexMCA.components 20 | ~ComplexMCA.components_amplitude 21 | ~ComplexMCA.components_phase 22 | ~ComplexMCA.compute 23 | ~ComplexMCA.correlation_coefficients_X 24 | ~ComplexMCA.correlation_coefficients_Y 25 | ~ComplexMCA.covariance_fraction_CD95 26 | ~ComplexMCA.cross_correlation_coefficients 27 | ~ComplexMCA.deserialize 28 | ~ComplexMCA.fit 29 | ~ComplexMCA.fraction_variance_X_explained_by_X 30 | ~ComplexMCA.fraction_variance_Y_explained_by_X 31 | ~ComplexMCA.fraction_variance_Y_explained_by_Y 32 | ~ComplexMCA.get_params 33 | ~ComplexMCA.get_serialization_attrs 34 | ~ComplexMCA.heterogeneous_patterns 35 | ~ComplexMCA.homogeneous_patterns 36 | ~ComplexMCA.inverse_transform 37 | ~ComplexMCA.load 38 | ~ComplexMCA.predict 39 | ~ComplexMCA.save 40 | ~ComplexMCA.scores 41 | ~ComplexMCA.scores_amplitude 42 | ~ComplexMCA.scores_phase 43 | ~ComplexMCA.serialize 44 | ~ComplexMCA.squared_covariance_fraction 45 | ~ComplexMCA.transform 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.ComplexMCARotator.rst: -------------------------------------------------------------------------------- 1 | ComplexMCARotator 2 | ================= 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: ComplexMCARotator 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~ComplexMCARotator.__init__ 19 | ~ComplexMCARotator.components 20 | ~ComplexMCARotator.components_amplitude 21 | ~ComplexMCARotator.components_phase 22 | ~ComplexMCARotator.compute 23 | ~ComplexMCARotator.correlation_coefficients_X 24 | ~ComplexMCARotator.correlation_coefficients_Y 25 | ~ComplexMCARotator.covariance_fraction_CD95 26 | ~ComplexMCARotator.cross_correlation_coefficients 27 | ~ComplexMCARotator.deserialize 28 | ~ComplexMCARotator.fit 29 | ~ComplexMCARotator.fraction_variance_X_explained_by_X 30 | ~ComplexMCARotator.fraction_variance_Y_explained_by_X 31 | ~ComplexMCARotator.fraction_variance_Y_explained_by_Y 32 | ~ComplexMCARotator.get_params 33 | ~ComplexMCARotator.get_serialization_attrs 34 | ~ComplexMCARotator.heterogeneous_patterns 35 | ~ComplexMCARotator.homogeneous_patterns 36 | ~ComplexMCARotator.inverse_transform 37 | ~ComplexMCARotator.load 38 | ~ComplexMCARotator.predict 39 | ~ComplexMCARotator.save 40 | ~ComplexMCARotator.scores 41 | ~ComplexMCARotator.scores_amplitude 42 | ~ComplexMCARotator.scores_phase 43 | ~ComplexMCARotator.serialize 44 | ~ComplexMCARotator.squared_covariance_fraction 45 | ~ComplexMCARotator.transform 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.ComplexRDA.rst: -------------------------------------------------------------------------------- 1 | ComplexRDA 2 | ========== 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: ComplexRDA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~ComplexRDA.__init__ 19 | ~ComplexRDA.components 20 | ~ComplexRDA.components_amplitude 21 | ~ComplexRDA.components_phase 22 | ~ComplexRDA.compute 23 | ~ComplexRDA.correlation_coefficients_X 24 | ~ComplexRDA.correlation_coefficients_Y 25 | ~ComplexRDA.cross_correlation_coefficients 26 | ~ComplexRDA.deserialize 27 | ~ComplexRDA.fit 28 | ~ComplexRDA.fraction_variance_X_explained_by_X 29 | ~ComplexRDA.fraction_variance_Y_explained_by_X 30 | ~ComplexRDA.fraction_variance_Y_explained_by_Y 31 | ~ComplexRDA.get_params 32 | ~ComplexRDA.get_serialization_attrs 33 | ~ComplexRDA.heterogeneous_patterns 34 | ~ComplexRDA.homogeneous_patterns 35 | ~ComplexRDA.inverse_transform 36 | ~ComplexRDA.load 37 | ~ComplexRDA.predict 38 | ~ComplexRDA.save 39 | ~ComplexRDA.scores 40 | ~ComplexRDA.scores_amplitude 41 | ~ComplexRDA.scores_phase 42 | ~ComplexRDA.serialize 43 | ~ComplexRDA.squared_covariance_fraction 44 | ~ComplexRDA.transform 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.HilbertCCA.rst: -------------------------------------------------------------------------------- 1 | HilbertCCA 2 | ========== 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: HilbertCCA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~HilbertCCA.__init__ 19 | ~HilbertCCA.components 20 | ~HilbertCCA.components_amplitude 21 | ~HilbertCCA.components_phase 22 | ~HilbertCCA.compute 23 | ~HilbertCCA.correlation_coefficients_X 24 | ~HilbertCCA.correlation_coefficients_Y 25 | ~HilbertCCA.cross_correlation_coefficients 26 | ~HilbertCCA.deserialize 27 | ~HilbertCCA.fit 28 | ~HilbertCCA.fraction_variance_X_explained_by_X 29 | ~HilbertCCA.fraction_variance_Y_explained_by_X 30 | ~HilbertCCA.fraction_variance_Y_explained_by_Y 31 | ~HilbertCCA.get_params 32 | ~HilbertCCA.get_serialization_attrs 33 | ~HilbertCCA.heterogeneous_patterns 34 | ~HilbertCCA.homogeneous_patterns 35 | ~HilbertCCA.inverse_transform 36 | ~HilbertCCA.load 37 | ~HilbertCCA.predict 38 | ~HilbertCCA.save 39 | ~HilbertCCA.scores 40 | ~HilbertCCA.scores_amplitude 41 | ~HilbertCCA.scores_phase 42 | ~HilbertCCA.serialize 43 | ~HilbertCCA.squared_covariance_fraction 44 | ~HilbertCCA.transform 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.HilbertCPCCA.rst: -------------------------------------------------------------------------------- 1 | HilbertCPCCA 2 | ============ 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: HilbertCPCCA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~HilbertCPCCA.__init__ 19 | ~HilbertCPCCA.components 20 | ~HilbertCPCCA.components_amplitude 21 | ~HilbertCPCCA.components_phase 22 | ~HilbertCPCCA.compute 23 | ~HilbertCPCCA.correlation_coefficients_X 24 | ~HilbertCPCCA.correlation_coefficients_Y 25 | ~HilbertCPCCA.cross_correlation_coefficients 26 | ~HilbertCPCCA.deserialize 27 | ~HilbertCPCCA.fit 28 | ~HilbertCPCCA.fraction_variance_X_explained_by_X 29 | ~HilbertCPCCA.fraction_variance_Y_explained_by_X 30 | ~HilbertCPCCA.fraction_variance_Y_explained_by_Y 31 | ~HilbertCPCCA.get_params 32 | ~HilbertCPCCA.get_serialization_attrs 33 | ~HilbertCPCCA.heterogeneous_patterns 34 | ~HilbertCPCCA.homogeneous_patterns 35 | ~HilbertCPCCA.inverse_transform 36 | ~HilbertCPCCA.load 37 | ~HilbertCPCCA.predict 38 | ~HilbertCPCCA.save 39 | ~HilbertCPCCA.scores 40 | ~HilbertCPCCA.scores_amplitude 41 | ~HilbertCPCCA.scores_phase 42 | ~HilbertCPCCA.serialize 43 | ~HilbertCPCCA.squared_covariance_fraction 44 | ~HilbertCPCCA.transform 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.HilbertCPCCARotator.rst: -------------------------------------------------------------------------------- 1 | HilbertCPCCARotator 2 | =================== 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: HilbertCPCCARotator 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~HilbertCPCCARotator.__init__ 19 | ~HilbertCPCCARotator.components 20 | ~HilbertCPCCARotator.components_amplitude 21 | ~HilbertCPCCARotator.components_phase 22 | ~HilbertCPCCARotator.compute 23 | ~HilbertCPCCARotator.correlation_coefficients_X 24 | ~HilbertCPCCARotator.correlation_coefficients_Y 25 | ~HilbertCPCCARotator.cross_correlation_coefficients 26 | ~HilbertCPCCARotator.deserialize 27 | ~HilbertCPCCARotator.fit 28 | ~HilbertCPCCARotator.fraction_variance_X_explained_by_X 29 | ~HilbertCPCCARotator.fraction_variance_Y_explained_by_X 30 | ~HilbertCPCCARotator.fraction_variance_Y_explained_by_Y 31 | ~HilbertCPCCARotator.get_params 32 | ~HilbertCPCCARotator.get_serialization_attrs 33 | ~HilbertCPCCARotator.heterogeneous_patterns 34 | ~HilbertCPCCARotator.homogeneous_patterns 35 | ~HilbertCPCCARotator.inverse_transform 36 | ~HilbertCPCCARotator.load 37 | ~HilbertCPCCARotator.predict 38 | ~HilbertCPCCARotator.save 39 | ~HilbertCPCCARotator.scores 40 | ~HilbertCPCCARotator.scores_amplitude 41 | ~HilbertCPCCARotator.scores_phase 42 | ~HilbertCPCCARotator.serialize 43 | ~HilbertCPCCARotator.squared_covariance_fraction 44 | ~HilbertCPCCARotator.transform 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.HilbertMCA.rst: -------------------------------------------------------------------------------- 1 | HilbertMCA 2 | ========== 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: HilbertMCA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~HilbertMCA.__init__ 19 | ~HilbertMCA.components 20 | ~HilbertMCA.components_amplitude 21 | ~HilbertMCA.components_phase 22 | ~HilbertMCA.compute 23 | ~HilbertMCA.correlation_coefficients_X 24 | ~HilbertMCA.correlation_coefficients_Y 25 | ~HilbertMCA.covariance_fraction_CD95 26 | ~HilbertMCA.cross_correlation_coefficients 27 | ~HilbertMCA.deserialize 28 | ~HilbertMCA.fit 29 | ~HilbertMCA.fraction_variance_X_explained_by_X 30 | ~HilbertMCA.fraction_variance_Y_explained_by_X 31 | ~HilbertMCA.fraction_variance_Y_explained_by_Y 32 | ~HilbertMCA.get_params 33 | ~HilbertMCA.get_serialization_attrs 34 | ~HilbertMCA.heterogeneous_patterns 35 | ~HilbertMCA.homogeneous_patterns 36 | ~HilbertMCA.inverse_transform 37 | ~HilbertMCA.load 38 | ~HilbertMCA.predict 39 | ~HilbertMCA.save 40 | ~HilbertMCA.scores 41 | ~HilbertMCA.scores_amplitude 42 | ~HilbertMCA.scores_phase 43 | ~HilbertMCA.serialize 44 | ~HilbertMCA.squared_covariance_fraction 45 | ~HilbertMCA.transform 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.HilbertMCARotator.rst: -------------------------------------------------------------------------------- 1 | HilbertMCARotator 2 | ================= 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: HilbertMCARotator 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~HilbertMCARotator.__init__ 19 | ~HilbertMCARotator.components 20 | ~HilbertMCARotator.components_amplitude 21 | ~HilbertMCARotator.components_phase 22 | ~HilbertMCARotator.compute 23 | ~HilbertMCARotator.correlation_coefficients_X 24 | ~HilbertMCARotator.correlation_coefficients_Y 25 | ~HilbertMCARotator.covariance_fraction_CD95 26 | ~HilbertMCARotator.cross_correlation_coefficients 27 | ~HilbertMCARotator.deserialize 28 | ~HilbertMCARotator.fit 29 | ~HilbertMCARotator.fraction_variance_X_explained_by_X 30 | ~HilbertMCARotator.fraction_variance_Y_explained_by_X 31 | ~HilbertMCARotator.fraction_variance_Y_explained_by_Y 32 | ~HilbertMCARotator.get_params 33 | ~HilbertMCARotator.get_serialization_attrs 34 | ~HilbertMCARotator.heterogeneous_patterns 35 | ~HilbertMCARotator.homogeneous_patterns 36 | ~HilbertMCARotator.inverse_transform 37 | ~HilbertMCARotator.load 38 | ~HilbertMCARotator.predict 39 | ~HilbertMCARotator.save 40 | ~HilbertMCARotator.scores 41 | ~HilbertMCARotator.scores_amplitude 42 | ~HilbertMCARotator.scores_phase 43 | ~HilbertMCARotator.serialize 44 | ~HilbertMCARotator.squared_covariance_fraction 45 | ~HilbertMCARotator.transform 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.HilbertRDA.rst: -------------------------------------------------------------------------------- 1 | HilbertRDA 2 | ========== 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: HilbertRDA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~HilbertRDA.__init__ 19 | ~HilbertRDA.components 20 | ~HilbertRDA.components_amplitude 21 | ~HilbertRDA.components_phase 22 | ~HilbertRDA.compute 23 | ~HilbertRDA.correlation_coefficients_X 24 | ~HilbertRDA.correlation_coefficients_Y 25 | ~HilbertRDA.cross_correlation_coefficients 26 | ~HilbertRDA.deserialize 27 | ~HilbertRDA.fit 28 | ~HilbertRDA.fraction_variance_X_explained_by_X 29 | ~HilbertRDA.fraction_variance_Y_explained_by_X 30 | ~HilbertRDA.fraction_variance_Y_explained_by_Y 31 | ~HilbertRDA.get_params 32 | ~HilbertRDA.get_serialization_attrs 33 | ~HilbertRDA.heterogeneous_patterns 34 | ~HilbertRDA.homogeneous_patterns 35 | ~HilbertRDA.inverse_transform 36 | ~HilbertRDA.load 37 | ~HilbertRDA.predict 38 | ~HilbertRDA.save 39 | ~HilbertRDA.scores 40 | ~HilbertRDA.scores_amplitude 41 | ~HilbertRDA.scores_phase 42 | ~HilbertRDA.serialize 43 | ~HilbertRDA.squared_covariance_fraction 44 | ~HilbertRDA.transform 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.MCA.rst: -------------------------------------------------------------------------------- 1 | MCA 2 | === 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: MCA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~MCA.__init__ 19 | ~MCA.components 20 | ~MCA.compute 21 | ~MCA.correlation_coefficients_X 22 | ~MCA.correlation_coefficients_Y 23 | ~MCA.covariance_fraction_CD95 24 | ~MCA.cross_correlation_coefficients 25 | ~MCA.deserialize 26 | ~MCA.fit 27 | ~MCA.fraction_variance_X_explained_by_X 28 | ~MCA.fraction_variance_Y_explained_by_X 29 | ~MCA.fraction_variance_Y_explained_by_Y 30 | ~MCA.get_params 31 | ~MCA.get_serialization_attrs 32 | ~MCA.heterogeneous_patterns 33 | ~MCA.homogeneous_patterns 34 | ~MCA.inverse_transform 35 | ~MCA.load 36 | ~MCA.predict 37 | ~MCA.save 38 | ~MCA.scores 39 | ~MCA.serialize 40 | ~MCA.squared_covariance_fraction 41 | ~MCA.transform 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.MCARotator.rst: -------------------------------------------------------------------------------- 1 | MCARotator 2 | ========== 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: MCARotator 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~MCARotator.__init__ 19 | ~MCARotator.components 20 | ~MCARotator.compute 21 | ~MCARotator.correlation_coefficients_X 22 | ~MCARotator.correlation_coefficients_Y 23 | ~MCARotator.covariance_fraction_CD95 24 | ~MCARotator.cross_correlation_coefficients 25 | ~MCARotator.deserialize 26 | ~MCARotator.fit 27 | ~MCARotator.fraction_variance_X_explained_by_X 28 | ~MCARotator.fraction_variance_Y_explained_by_X 29 | ~MCARotator.fraction_variance_Y_explained_by_Y 30 | ~MCARotator.get_params 31 | ~MCARotator.get_serialization_attrs 32 | ~MCARotator.heterogeneous_patterns 33 | ~MCARotator.homogeneous_patterns 34 | ~MCARotator.inverse_transform 35 | ~MCARotator.load 36 | ~MCARotator.predict 37 | ~MCARotator.save 38 | ~MCARotator.scores 39 | ~MCARotator.serialize 40 | ~MCARotator.squared_covariance_fraction 41 | ~MCARotator.transform 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.cross.RDA.rst: -------------------------------------------------------------------------------- 1 | RDA 2 | === 3 | 4 | .. currentmodule:: xeofs.cross 5 | 6 | .. autoclass:: RDA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~RDA.__init__ 19 | ~RDA.components 20 | ~RDA.compute 21 | ~RDA.correlation_coefficients_X 22 | ~RDA.correlation_coefficients_Y 23 | ~RDA.cross_correlation_coefficients 24 | ~RDA.deserialize 25 | ~RDA.fit 26 | ~RDA.fraction_variance_X_explained_by_X 27 | ~RDA.fraction_variance_Y_explained_by_X 28 | ~RDA.fraction_variance_Y_explained_by_Y 29 | ~RDA.get_params 30 | ~RDA.get_serialization_attrs 31 | ~RDA.heterogeneous_patterns 32 | ~RDA.homogeneous_patterns 33 | ~RDA.inverse_transform 34 | ~RDA.load 35 | ~RDA.predict 36 | ~RDA.save 37 | ~RDA.scores 38 | ~RDA.serialize 39 | ~RDA.squared_covariance_fraction 40 | ~RDA.transform 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.multi.CCA.rst: -------------------------------------------------------------------------------- 1 | CCA 2 | === 3 | 4 | .. currentmodule:: xeofs.multi 5 | 6 | .. autoclass:: CCA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~CCA.__init__ 19 | ~CCA.components 20 | ~CCA.explained_covariance 21 | ~CCA.explained_covariance_ratio 22 | ~CCA.explained_variance 23 | ~CCA.explained_variance_ratio 24 | ~CCA.fit 25 | ~CCA.get_metadata_routing 26 | ~CCA.get_params 27 | ~CCA.scores 28 | ~CCA.set_fit_request 29 | ~CCA.set_params 30 | ~CCA.set_transform_request 31 | ~CCA.transform 32 | ~CCA.weights 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.single.ComplexEOF.rst: -------------------------------------------------------------------------------- 1 | ComplexEOF 2 | ========== 3 | 4 | .. currentmodule:: xeofs.single 5 | 6 | .. autoclass:: ComplexEOF 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~ComplexEOF.__init__ 19 | ~ComplexEOF.components 20 | ~ComplexEOF.components_amplitude 21 | ~ComplexEOF.components_phase 22 | ~ComplexEOF.compute 23 | ~ComplexEOF.deserialize 24 | ~ComplexEOF.explained_variance 25 | ~ComplexEOF.explained_variance_ratio 26 | ~ComplexEOF.fit 27 | ~ComplexEOF.fit_transform 28 | ~ComplexEOF.get_params 29 | ~ComplexEOF.get_serialization_attrs 30 | ~ComplexEOF.inverse_transform 31 | ~ComplexEOF.load 32 | ~ComplexEOF.save 33 | ~ComplexEOF.scores 34 | ~ComplexEOF.scores_amplitude 35 | ~ComplexEOF.scores_phase 36 | ~ComplexEOF.serialize 37 | ~ComplexEOF.singular_values 38 | ~ComplexEOF.transform 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.single.ComplexEOFRotator.rst: -------------------------------------------------------------------------------- 1 | ComplexEOFRotator 2 | ================= 3 | 4 | .. currentmodule:: xeofs.single 5 | 6 | .. autoclass:: ComplexEOFRotator 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~ComplexEOFRotator.__init__ 19 | ~ComplexEOFRotator.components 20 | ~ComplexEOFRotator.components_amplitude 21 | ~ComplexEOFRotator.components_phase 22 | ~ComplexEOFRotator.compute 23 | ~ComplexEOFRotator.deserialize 24 | ~ComplexEOFRotator.explained_variance 25 | ~ComplexEOFRotator.explained_variance_ratio 26 | ~ComplexEOFRotator.fit 27 | ~ComplexEOFRotator.fit_transform 28 | ~ComplexEOFRotator.get_params 29 | ~ComplexEOFRotator.get_serialization_attrs 30 | ~ComplexEOFRotator.inverse_transform 31 | ~ComplexEOFRotator.load 32 | ~ComplexEOFRotator.save 33 | ~ComplexEOFRotator.scores 34 | ~ComplexEOFRotator.scores_amplitude 35 | ~ComplexEOFRotator.scores_phase 36 | ~ComplexEOFRotator.serialize 37 | ~ComplexEOFRotator.singular_values 38 | ~ComplexEOFRotator.transform 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.single.EOF.rst: -------------------------------------------------------------------------------- 1 | EOF 2 | === 3 | 4 | .. currentmodule:: xeofs.single 5 | 6 | .. autoclass:: EOF 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~EOF.__init__ 19 | ~EOF.components 20 | ~EOF.compute 21 | ~EOF.deserialize 22 | ~EOF.explained_variance 23 | ~EOF.explained_variance_ratio 24 | ~EOF.fit 25 | ~EOF.fit_transform 26 | ~EOF.get_params 27 | ~EOF.get_serialization_attrs 28 | ~EOF.inverse_transform 29 | ~EOF.load 30 | ~EOF.save 31 | ~EOF.scores 32 | ~EOF.serialize 33 | ~EOF.singular_values 34 | ~EOF.transform 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.single.EOFRotator.rst: -------------------------------------------------------------------------------- 1 | EOFRotator 2 | ========== 3 | 4 | .. currentmodule:: xeofs.single 5 | 6 | .. autoclass:: EOFRotator 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~EOFRotator.__init__ 19 | ~EOFRotator.components 20 | ~EOFRotator.compute 21 | ~EOFRotator.deserialize 22 | ~EOFRotator.explained_variance 23 | ~EOFRotator.explained_variance_ratio 24 | ~EOFRotator.fit 25 | ~EOFRotator.fit_transform 26 | ~EOFRotator.get_params 27 | ~EOFRotator.get_serialization_attrs 28 | ~EOFRotator.inverse_transform 29 | ~EOFRotator.load 30 | ~EOFRotator.save 31 | ~EOFRotator.scores 32 | ~EOFRotator.serialize 33 | ~EOFRotator.singular_values 34 | ~EOFRotator.transform 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.single.ExtendedEOF.rst: -------------------------------------------------------------------------------- 1 | ExtendedEOF 2 | =========== 3 | 4 | .. currentmodule:: xeofs.single 5 | 6 | .. autoclass:: ExtendedEOF 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~ExtendedEOF.__init__ 19 | ~ExtendedEOF.components 20 | ~ExtendedEOF.compute 21 | ~ExtendedEOF.deserialize 22 | ~ExtendedEOF.explained_variance 23 | ~ExtendedEOF.explained_variance_ratio 24 | ~ExtendedEOF.fit 25 | ~ExtendedEOF.fit_transform 26 | ~ExtendedEOF.get_params 27 | ~ExtendedEOF.get_serialization_attrs 28 | ~ExtendedEOF.inverse_transform 29 | ~ExtendedEOF.load 30 | ~ExtendedEOF.save 31 | ~ExtendedEOF.scores 32 | ~ExtendedEOF.serialize 33 | ~ExtendedEOF.singular_values 34 | ~ExtendedEOF.transform 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.single.GWPCA.rst: -------------------------------------------------------------------------------- 1 | GWPCA 2 | ===== 3 | 4 | .. currentmodule:: xeofs.single 5 | 6 | .. autoclass:: GWPCA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~GWPCA.__init__ 19 | ~GWPCA.components 20 | ~GWPCA.compute 21 | ~GWPCA.deserialize 22 | ~GWPCA.explained_variance 23 | ~GWPCA.explained_variance_ratio 24 | ~GWPCA.fit 25 | ~GWPCA.fit_transform 26 | ~GWPCA.get_params 27 | ~GWPCA.get_serialization_attrs 28 | ~GWPCA.inverse_transform 29 | ~GWPCA.largest_locally_weighted_components 30 | ~GWPCA.load 31 | ~GWPCA.save 32 | ~GWPCA.scores 33 | ~GWPCA.serialize 34 | ~GWPCA.transform 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.single.HilbertEOF.rst: -------------------------------------------------------------------------------- 1 | HilbertEOF 2 | ========== 3 | 4 | .. currentmodule:: xeofs.single 5 | 6 | .. autoclass:: HilbertEOF 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~HilbertEOF.__init__ 19 | ~HilbertEOF.components 20 | ~HilbertEOF.components_amplitude 21 | ~HilbertEOF.components_phase 22 | ~HilbertEOF.compute 23 | ~HilbertEOF.deserialize 24 | ~HilbertEOF.explained_variance 25 | ~HilbertEOF.explained_variance_ratio 26 | ~HilbertEOF.fit 27 | ~HilbertEOF.fit_transform 28 | ~HilbertEOF.get_params 29 | ~HilbertEOF.get_serialization_attrs 30 | ~HilbertEOF.inverse_transform 31 | ~HilbertEOF.load 32 | ~HilbertEOF.save 33 | ~HilbertEOF.scores 34 | ~HilbertEOF.scores_amplitude 35 | ~HilbertEOF.scores_phase 36 | ~HilbertEOF.serialize 37 | ~HilbertEOF.singular_values 38 | ~HilbertEOF.transform 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.single.HilbertEOFRotator.rst: -------------------------------------------------------------------------------- 1 | HilbertEOFRotator 2 | ================= 3 | 4 | .. currentmodule:: xeofs.single 5 | 6 | .. autoclass:: HilbertEOFRotator 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~HilbertEOFRotator.__init__ 19 | ~HilbertEOFRotator.components 20 | ~HilbertEOFRotator.components_amplitude 21 | ~HilbertEOFRotator.components_phase 22 | ~HilbertEOFRotator.compute 23 | ~HilbertEOFRotator.deserialize 24 | ~HilbertEOFRotator.explained_variance 25 | ~HilbertEOFRotator.explained_variance_ratio 26 | ~HilbertEOFRotator.fit 27 | ~HilbertEOFRotator.fit_transform 28 | ~HilbertEOFRotator.get_params 29 | ~HilbertEOFRotator.get_serialization_attrs 30 | ~HilbertEOFRotator.inverse_transform 31 | ~HilbertEOFRotator.load 32 | ~HilbertEOFRotator.save 33 | ~HilbertEOFRotator.scores 34 | ~HilbertEOFRotator.scores_amplitude 35 | ~HilbertEOFRotator.scores_phase 36 | ~HilbertEOFRotator.serialize 37 | ~HilbertEOFRotator.singular_values 38 | ~HilbertEOFRotator.transform 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.single.OPA.rst: -------------------------------------------------------------------------------- 1 | OPA 2 | === 3 | 4 | .. currentmodule:: xeofs.single 5 | 6 | .. autoclass:: OPA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~OPA.__init__ 19 | ~OPA.components 20 | ~OPA.compute 21 | ~OPA.decorrelation_time 22 | ~OPA.deserialize 23 | ~OPA.filter_patterns 24 | ~OPA.fit 25 | ~OPA.fit_transform 26 | ~OPA.get_params 27 | ~OPA.get_serialization_attrs 28 | ~OPA.inverse_transform 29 | ~OPA.load 30 | ~OPA.save 31 | ~OPA.scores 32 | ~OPA.serialize 33 | ~OPA.transform 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.single.POP.rst: -------------------------------------------------------------------------------- 1 | POP 2 | === 3 | 4 | .. currentmodule:: xeofs.single 5 | 6 | .. autoclass:: POP 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~POP.__init__ 19 | ~POP.components 20 | ~POP.components_amplitude 21 | ~POP.components_phase 22 | ~POP.compute 23 | ~POP.damping_times 24 | ~POP.deserialize 25 | ~POP.eigenvalues 26 | ~POP.fit 27 | ~POP.fit_transform 28 | ~POP.get_params 29 | ~POP.get_serialization_attrs 30 | ~POP.inverse_transform 31 | ~POP.load 32 | ~POP.periods 33 | ~POP.save 34 | ~POP.scores 35 | ~POP.scores_amplitude 36 | ~POP.scores_phase 37 | ~POP.serialize 38 | ~POP.transform 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.single.SparsePCA.rst: -------------------------------------------------------------------------------- 1 | SparsePCA 2 | ========= 3 | 4 | .. currentmodule:: xeofs.single 5 | 6 | .. autoclass:: SparsePCA 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~SparsePCA.__init__ 19 | ~SparsePCA.components 20 | ~SparsePCA.compute 21 | ~SparsePCA.deserialize 22 | ~SparsePCA.explained_variance 23 | ~SparsePCA.explained_variance_ratio 24 | ~SparsePCA.fit 25 | ~SparsePCA.fit_transform 26 | ~SparsePCA.get_params 27 | ~SparsePCA.get_serialization_attrs 28 | ~SparsePCA.inverse_transform 29 | ~SparsePCA.load 30 | ~SparsePCA.save 31 | ~SparsePCA.scores 32 | ~SparsePCA.serialize 33 | ~SparsePCA.transform 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /docs/content/api_reference/_autosummary/xeofs.validation.EOFBootstrapper.rst: -------------------------------------------------------------------------------- 1 | EOFBootstrapper 2 | =============== 3 | 4 | .. currentmodule:: xeofs.validation 5 | 6 | .. autoclass:: EOFBootstrapper 7 | :members: 8 | :inherited-members: 9 | 10 | 11 | .. automethod:: __init__ 12 | 13 | 14 | .. rubric:: Methods 15 | 16 | .. autosummary:: 17 | 18 | ~EOFBootstrapper.__init__ 19 | ~EOFBootstrapper.components 20 | ~EOFBootstrapper.compute 21 | ~EOFBootstrapper.deserialize 22 | ~EOFBootstrapper.explained_variance 23 | ~EOFBootstrapper.explained_variance_ratio 24 | ~EOFBootstrapper.fit 25 | ~EOFBootstrapper.fit_transform 26 | ~EOFBootstrapper.get_params 27 | ~EOFBootstrapper.get_serialization_attrs 28 | ~EOFBootstrapper.inverse_transform 29 | ~EOFBootstrapper.load 30 | ~EOFBootstrapper.save 31 | ~EOFBootstrapper.scores 32 | ~EOFBootstrapper.serialize 33 | ~EOFBootstrapper.singular_values 34 | ~EOFBootstrapper.transform 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /docs/content/api_reference/cross_set_analysis.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | ``xeofs.cross`` 3 | =============== 4 | Methods that investigate relationships or patterns between variables **across two** distinct datasets. 5 | 6 | .. autosummary:: 7 | :toctree: _autosummary 8 | :template: custom-class-template.rst 9 | :recursive: 10 | 11 | ~xeofs.cross.CCA 12 | ~xeofs.cross.MCA 13 | ~xeofs.cross.RDA 14 | ~xeofs.cross.CPCCA 15 | ~xeofs.cross.ComplexCCA 16 | ~xeofs.cross.ComplexMCA 17 | ~xeofs.cross.ComplexRDA 18 | ~xeofs.cross.ComplexCPCCA 19 | ~xeofs.cross.HilbertCCA 20 | ~xeofs.cross.HilbertMCA 21 | ~xeofs.cross.HilbertRDA 22 | ~xeofs.cross.HilbertCPCCA 23 | 24 | ------------------------------ 25 | Sparse Solutions via Rotation 26 | ------------------------------ 27 | 28 | .. autosummary:: 29 | :toctree: _autosummary 30 | :template: custom-class-template.rst 31 | :recursive: 32 | 33 | ~xeofs.cross.MCARotator 34 | ~xeofs.cross.CPCCARotator 35 | ~xeofs.cross.ComplexMCARotator 36 | ~xeofs.cross.ComplexCPCCARotator 37 | ~xeofs.cross.HilbertMCARotator 38 | ~xeofs.cross.HilbertCPCCARotator 39 | -------------------------------------------------------------------------------- /docs/content/api_reference/index.rst: -------------------------------------------------------------------------------- 1 | .. _api_reference: 2 | 3 | ====================== 4 | API Reference 5 | ====================== 6 | 7 | .. warning:: 8 | 9 | The package is under development, and its API may change. 10 | 11 | The xeofs package focuses on eigenmethods for dimensionality reduction in climate science. It is organized into methods that examine relationships between variables 12 | 13 | 1. within a **single dataset** (``xeofs.single``), 14 | 2. across **two datasets** (``xeofs.cross``) and 15 | 3. across **more than two datasets** (``xeofs.multi``). 16 | 17 | -------------------- 18 | Single-Set Analysis 19 | -------------------- 20 | 21 | A classic example of :doc:`single-set analysis ` is Principal Component Analysis (PCA/EOF analysis), used to extract the dominant patterns of variability within a single dataset. While PCA can be applied to multiple (standardized) datasets simultaneously, it treats all datasets as one large dataset, maximizing overall variability without considering inter-dataset relationships. Consequently, the most important variables may come from only one dataset, ignoring others. 22 | 23 | ---------------------------- 24 | Cross and Multi-Set Analysis 25 | ---------------------------- 26 | 27 | Classic examples of :doc:`cross ` or :doc:`multi`-set analysis methods include Canonical Correlation Analysis (CCA), Maximum Covariance Analysis (MCA) and Redundancy Analyis (RDA). These techniques identify shared patterns of variability between two distinct datasets, focusing on common patterns rather than those unique to each dataset. 28 | 29 | 30 | Additionally, xeofs offers tools for :doc:`model evaluation `, though these are still in early development stages. 31 | 32 | 33 | 34 | .. toctree:: 35 | :maxdepth: 3 36 | :hidden: 37 | :caption: Methods 38 | 39 | single_set_analysis 40 | cross_set_analysis 41 | multi_set_analysis 42 | utilities 43 | 44 | .. toctree:: 45 | :maxdepth: 3 46 | :hidden: 47 | :caption: Significance Testing 48 | 49 | model_evaluation 50 | 51 | -------------------------------------------------------------------------------- /docs/content/api_reference/model_evaluation.rst: -------------------------------------------------------------------------------- 1 | ==================== 2 | ``xeofs.validation`` 3 | ==================== 4 | Tools to assess the significane of your model. 5 | 6 | .. autosummary:: 7 | :toctree: _autosummary 8 | :template: custom-class-template.rst 9 | :recursive: 10 | 11 | ~xeofs.validation.EOFBootstrapper 12 | -------------------------------------------------------------------------------- /docs/content/api_reference/multi_set_analysis.rst: -------------------------------------------------------------------------------- 1 | ================ 2 | ``xeofs.multi`` 3 | ================ 4 | Methods that investigate relationships or patterns between variables across **more than two** distinct datasets. 5 | 6 | .. autosummary:: 7 | :toctree: _autosummary 8 | :template: custom-class-template.rst 9 | :recursive: 10 | 11 | ~xeofs.multi.CCA 12 | 13 | ------------------------------ 14 | Sparse Solutions via Rotation 15 | ------------------------------ 16 | 17 | .. autosummary:: 18 | :toctree: _autosummary 19 | :template: custom-class-template.rst 20 | :recursive: 21 | 22 | -------------------------------------------------------------------------------- /docs/content/api_reference/single_set_analysis.rst: -------------------------------------------------------------------------------- 1 | ================ 2 | ``xeofs.single`` 3 | ================ 4 | 5 | Methods that investigate relationships or patterns between variables within a single dataset. 6 | 7 | .. autosummary:: 8 | :toctree: _autosummary 9 | :template: custom-class-template.rst 10 | :recursive: 11 | 12 | ~xeofs.single.EOF 13 | ~xeofs.single.ComplexEOF 14 | ~xeofs.single.HilbertEOF 15 | ~xeofs.single.ExtendedEOF 16 | ~xeofs.single.POP 17 | ~xeofs.single.OPA 18 | ~xeofs.single.GWPCA 19 | ~xeofs.single.SparsePCA 20 | 21 | 22 | ------------------------------ 23 | Sparse Solutions via Rotation 24 | ------------------------------ 25 | 26 | .. autosummary:: 27 | :toctree: _autosummary 28 | :template: custom-class-template.rst 29 | :recursive: 30 | 31 | ~xeofs.single.EOFRotator 32 | ~xeofs.single.ComplexEOFRotator 33 | ~xeofs.single.HilbertEOFRotator -------------------------------------------------------------------------------- /docs/content/api_reference/utilities.rst: -------------------------------------------------------------------------------- 1 | ========= 2 | Utilities 3 | ========= 4 | Tools that may be helpful 5 | 6 | .. autosummary:: 7 | :toctree: _autosummary 8 | :template: custom-class-template.rst 9 | :recursive: 10 | 11 | ~xeofs.RotatorFactory 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_complex_eof_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_complex_eof_001.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_complex_eof_002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_complex_eof_002.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eeof_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eeof_001.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eeof_002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eeof_002.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eeof_003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eeof_003.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eeof_trend_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eeof_trend_001.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eeof_trend_002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eeof_trend_002.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eeof_trend_003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eeof_trend_003.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eof-smode_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eof-smode_001.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eof-tmode_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_eof-tmode_001.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_gwpca_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_gwpca_001.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_gwpca_002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_gwpca_002.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_hilbert_eof_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_hilbert_eof_001.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_hilbert_eof_002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_hilbert_eof_002.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_hilbert_eof_003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_hilbert_eof_003.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_hilbert_eof_004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_hilbert_eof_004.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_mreof_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_mreof_001.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_multivariate-eof_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_multivariate-eof_001.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_rotated_eof_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_rotated_eof_001.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_weighted-eof_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/sphx_glr_plot_weighted-eof_001.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_complex_eof_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_complex_eof_thumb.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_eeof_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_eeof_thumb.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_eeof_trend_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_eeof_trend_thumb.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_eof-smode_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_eof-smode_thumb.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_eof-tmode_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_eof-tmode_thumb.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_gwpca_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_gwpca_thumb.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_hilbert_eof_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_hilbert_eof_thumb.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_mreof_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_mreof_thumb.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_multivariate-eof_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_multivariate-eof_thumb.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_rotated_eof_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_rotated_eof_thumb.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_weighted-eof_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/images/thumb/sphx_glr_plot_weighted-eof_thumb.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_complex_eof.py: -------------------------------------------------------------------------------- 1 | """ 2 | Complex EOF analysis 3 | ============================================ 4 | 5 | In this tutorial, we'll walk through how to perform a Complex EOF analysis on 6 | the zonal and meridional wind components. 7 | 8 | Let's start by importing the necessary packages and loading the data: 9 | """ 10 | 11 | # %% 12 | import matplotlib.pyplot as plt 13 | import xarray as xr 14 | 15 | import xeofs as xe 16 | 17 | xr.set_options(display_expand_attrs=False) 18 | 19 | # %% 20 | # For this example, we'll use the ERA-Interim tutorial dataset ``eraint_uvz``: 21 | 22 | uvz = xr.tutorial.open_dataset("eraint_uvz") 23 | uvz 24 | 25 | # %% 26 | # This dataset contains the zonal, meridional, and vertical wind components at 27 | # three different atmospheric levels. Note that the data only covers two months, 28 | # so we have just two time steps (samples). While this isn't enough for a robust 29 | # EOF analysis, we'll proceed for demonstration purposes. Now, let's combine the 30 | # zonal (``u``) and meridional (``v``) wind components into a complex-valued 31 | # dataset: 32 | 33 | Z = uvz["u"] + 1j * uvz["v"] 34 | 35 | # %% 36 | # Next, we'll initialize and fit the ``ComplexEOF`` model to our data. The 37 | # ``xeofs`` package makes this easy by allowing us to specify the sample 38 | # dimension (``month``), automatically performing the Complex EOF analysis 39 | # across all three atmospheric levels. As a standard practice, we'll also weigh 40 | # each grid cell by the square root of the cosine of the latitude 41 | # (``use_coslat=True``). 42 | 43 | model = xe.single.ComplexEOF(n_modes=1, use_coslat=True, random_state=7) 44 | model.fit(Z, dim="month") 45 | 46 | # %% 47 | # Instead of just extracting the complex-valued components, we can also get the 48 | # amplitude and phase of these components. Let's start by looking at the 49 | # amplitude of the first mode: 50 | 51 | 52 | spatial_ampltiudes = model.components_amplitude() 53 | spatial_phases = model.components_phase() 54 | 55 | spatial_ampltiudes.sel(mode=1).plot(col="level") 56 | plt.show() 57 | 58 | # %% 59 | # It looks like the first mode picks up a pattern resembling the location of the 60 | # subtropical jet stream around ±30º latitude, particularly strong in the upper 61 | # troposphere at 200 hPa and weaker toward the surface. We can also plot the 62 | # phase of the first mode. To keep the plot clear, we'll only show the phase 63 | # where the amplitude is above a certain threshold (e.g., 0.004): 64 | 65 | relevant_phases = spatial_phases.where(spatial_ampltiudes > 0.004) 66 | relevant_phases.sel(mode=1).plot(col="level", cmap="twilight") 67 | plt.show() 68 | 69 | # %% 70 | -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_complex_eof.py.md5: -------------------------------------------------------------------------------- 1 | 00a4013c7fa42189d7980ec2a54d0eae -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_complex_eof_codeobj.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/plot_complex_eof_codeobj.pickle -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_eeof.py.md5: -------------------------------------------------------------------------------- 1 | dad4ad8d30e4903c94110f68dfed3398 -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_eeof_codeobj.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/plot_eeof_codeobj.pickle -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_eeof_trend.py.md5: -------------------------------------------------------------------------------- 1 | e84d0364f470ed5e3eae38a735ab52e0 -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_eeof_trend_codeobj.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/plot_eeof_trend_codeobj.pickle -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_eof-smode.py: -------------------------------------------------------------------------------- 1 | """ 2 | Sparse PCA 3 | ======================== 4 | 5 | This example demonstrates the application of sparse PCA [1]_ to sea surface temperature data. Sparse PCA is an alternative to rotated PCA, where the components are sparse, often providing a more interpretable solution. 6 | 7 | We replicate the analysis from the original paper [1]_, which identifies the ENSO (El Niño-Southern Oscillation) as the fourth mode, representing about 1% of the total variance. The original study focused on weekly sea surface temperatures from satellite data, whereas we use monthly data from ERSSTv5. Consequently, our results may not match exactly, but they should be quite similar. 8 | 9 | References 10 | ---------- 11 | .. [1] Erichson, N. B. et al. Sparse Principal Component Analysis via Variable Projection. SIAM J. Appl. Math. 80, 977-1002 (2020). 12 | 13 | """ 14 | 15 | # Load packages and data: 16 | import matplotlib.pyplot as plt 17 | import xarray as xr 18 | from cartopy.crs import EqualEarth, PlateCarree 19 | from matplotlib.gridspec import GridSpec 20 | 21 | import xeofs as xe 22 | 23 | # %% 24 | # We use sea surface temperature data from 1990 to 2017, consistent with the original paper. 25 | 26 | sst = xr.tutorial.open_dataset("ersstv5")["sst"] 27 | sst = sst.sel(time=slice("1990", "2017")) 28 | 29 | # %% 30 | # We perform sparse PCA using the `alpha` and `beta` parameters, which define the sparsity imposed by the elastic net (refer to Table 1 in the paper). In our analysis, we set `alpha` to 1e-5, as specified by the authors. Although the authors do not specify a value for `beta`, it appears that the results are not highly sensitive to this parameter. Therefore, we use the default `beta` value of 1e-4. 31 | 32 | model = xe.single.SparsePCA(n_modes=4, alpha=1e-5) 33 | model.fit(sst, dim="time") 34 | expvar = model.explained_variance() 35 | expvar_ratio = model.explained_variance_ratio() 36 | components = model.components() 37 | scores = model.scores() 38 | 39 | # %% 40 | # The explained variance fraction confirms that the fourth mode explains about 1% of the total variance, which is consistent with the original paper. 41 | 42 | print("Explained variance: ", expvar.round(0).values) 43 | print("Relative: ", (expvar_ratio * 100).round(1).values) 44 | 45 | # %% 46 | # Examining the first four modes, we clearly identify ENSO as the fourth mode. 47 | 48 | proj = EqualEarth(central_longitude=180) 49 | kwargs = {"cmap": "RdBu", "vmin": -0.05, "vmax": 0.05, "transform": PlateCarree()} 50 | 51 | fig = plt.figure(figsize=(10, 12)) 52 | gs = GridSpec(4, 2, width_ratios=[1, 2]) 53 | ax0 = [fig.add_subplot(gs[i, 0]) for i in range(4)] 54 | ax1 = [fig.add_subplot(gs[i, 1], projection=proj) for i in range(4)] 55 | 56 | for i, (a0, a1) in enumerate(zip(ax0, ax1)): 57 | scores.sel(mode=i + 1).plot(ax=a0) 58 | a1.coastlines(color=".5") 59 | components.sel(mode=i + 1).plot(ax=a1, **kwargs) 60 | 61 | a0.set_xlabel("") 62 | 63 | plt.tight_layout() 64 | plt.savefig("sparse_pca.jpg") 65 | 66 | # %% 67 | -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_eof-smode.py.md5: -------------------------------------------------------------------------------- 1 | 6d0f12994146614921c2bbc9e655d938 -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_eof-smode_codeobj.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/plot_eof-smode_codeobj.pickle -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_eof-tmode.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\n", 8 | "# EOF analysis (T-mode)\n", 9 | "\n", 10 | "EOF analysis in T-mode maximises the spatial variance.\n", 11 | "\n", 12 | "Load packages and data:\n" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "import matplotlib.pyplot as plt\n", 22 | "import xarray as xr\n", 23 | "from cartopy.crs import EqualEarth, PlateCarree\n", 24 | "from matplotlib.gridspec import GridSpec\n", 25 | "\n", 26 | "import xeofs as xe\n", 27 | "\n", 28 | "sst = xr.tutorial.open_dataset(\"ersstv5\")[\"sst\"]" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "Perform the actual analysis\n", 36 | "\n" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "model = xe.single.EOF(n_modes=5)\n", 46 | "model.fit(sst, dim=(\"lat\", \"lon\"))\n", 47 | "expvar = model.explained_variance_ratio()\n", 48 | "components = model.components()\n", 49 | "scores = model.scores()" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "Create figure showing the first two modes\n", 57 | "\n" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "proj = EqualEarth(central_longitude=180)\n", 67 | "kwargs = {\"cmap\": \"RdBu\", \"transform\": PlateCarree()}\n", 68 | "\n", 69 | "fig = plt.figure(figsize=(10, 8))\n", 70 | "gs = GridSpec(3, 2, width_ratios=[2, 1])\n", 71 | "ax0 = [fig.add_subplot(gs[i, 0], projection=proj) for i in range(3)]\n", 72 | "ax1 = [fig.add_subplot(gs[i, 1]) for i in range(3)]\n", 73 | "\n", 74 | "for i, (a0, a1) in enumerate(zip(ax0, ax1)):\n", 75 | " scores.sel(mode=i + 1).plot(ax=a0, **kwargs)\n", 76 | " a0.coastlines(color=\".5\")\n", 77 | " components.sel(mode=i + 1).plot(ax=a1)\n", 78 | "\n", 79 | " a0.set_xlabel(\"\")\n", 80 | "\n", 81 | "plt.tight_layout()\n", 82 | "plt.savefig(\"eof-tmode.jpg\")" 83 | ] 84 | } 85 | ], 86 | "metadata": { 87 | "kernelspec": { 88 | "display_name": "Python 3", 89 | "language": "python", 90 | "name": "python3" 91 | }, 92 | "language_info": { 93 | "codemirror_mode": { 94 | "name": "ipython", 95 | "version": 3 96 | }, 97 | "file_extension": ".py", 98 | "mimetype": "text/x-python", 99 | "name": "python", 100 | "nbconvert_exporter": "python", 101 | "pygments_lexer": "ipython3", 102 | "version": "3.11.4" 103 | } 104 | }, 105 | "nbformat": 4, 106 | "nbformat_minor": 0 107 | } 108 | -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_eof-tmode.py: -------------------------------------------------------------------------------- 1 | """ 2 | EOF analysis (T-mode) 3 | ======================== 4 | 5 | EOF analysis in T-mode maximises the spatial variance. 6 | 7 | Load packages and data: 8 | """ 9 | 10 | import matplotlib.pyplot as plt 11 | import xarray as xr 12 | from cartopy.crs import EqualEarth, PlateCarree 13 | from matplotlib.gridspec import GridSpec 14 | 15 | import xeofs as xe 16 | 17 | sst = xr.tutorial.open_dataset("ersstv5")["sst"] 18 | 19 | # %% 20 | # Perform the actual analysis 21 | 22 | model = xe.single.EOF(n_modes=5) 23 | model.fit(sst, dim=("lat", "lon")) 24 | expvar = model.explained_variance_ratio() 25 | components = model.components() 26 | scores = model.scores() 27 | 28 | # %% 29 | # Create figure showing the first two modes 30 | 31 | proj = EqualEarth(central_longitude=180) 32 | kwargs = {"cmap": "RdBu", "transform": PlateCarree()} 33 | 34 | fig = plt.figure(figsize=(10, 8)) 35 | gs = GridSpec(3, 2, width_ratios=[2, 1]) 36 | ax0 = [fig.add_subplot(gs[i, 0], projection=proj) for i in range(3)] 37 | ax1 = [fig.add_subplot(gs[i, 1]) for i in range(3)] 38 | 39 | for i, (a0, a1) in enumerate(zip(ax0, ax1)): 40 | scores.sel(mode=i + 1).plot(ax=a0, **kwargs) 41 | a0.coastlines(color=".5") 42 | components.sel(mode=i + 1).plot(ax=a1) 43 | 44 | a0.set_xlabel("") 45 | 46 | plt.tight_layout() 47 | plt.savefig("eof-tmode.jpg") 48 | -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_eof-tmode.py.md5: -------------------------------------------------------------------------------- 1 | af1f1248f081012ef3cb40381d6bbe73 -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_eof-tmode_codeobj.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/plot_eof-tmode_codeobj.pickle -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_gwpca.py.md5: -------------------------------------------------------------------------------- 1 | ae1e8af4b123c765c1cfd4d63d9386c2 -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_gwpca_codeobj.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/plot_gwpca_codeobj.pickle -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_hilbert_eof.py.md5: -------------------------------------------------------------------------------- 1 | 28f29c6a357ea6a325769ce0c7554634 -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_hilbert_eof_codeobj.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/plot_hilbert_eof_codeobj.pickle -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_mreof.py: -------------------------------------------------------------------------------- 1 | """ 2 | Varimax-rotated Multivariate EOF analysis 3 | ============================================ 4 | 5 | Multivariate EOF analysis with additional Varimax rotation. 6 | """ 7 | 8 | # Load packages and data: 9 | import matplotlib.pyplot as plt 10 | import xarray as xr 11 | from cartopy.crs import PlateCarree 12 | from matplotlib.gridspec import GridSpec 13 | 14 | import xeofs as xe 15 | 16 | # %% 17 | # Create four different dataarrayss 18 | sst = xr.tutorial.open_dataset("ersstv5")["sst"] 19 | subset1 = sst.isel(lon=slice(0, 45)) 20 | subset2 = sst.isel(lon=slice(46, 90)) 21 | subset3 = sst.isel(lon=slice(91, 135)) 22 | subset4 = sst.isel(lon=slice(136, None)) 23 | 24 | # %% 25 | # Perform the actual analysis 26 | 27 | multivariate_data = [subset1, subset2, subset3, subset4] 28 | mpca = xe.single.EOF(n_modes=100, standardize=False, use_coslat=True) 29 | mpca.fit(multivariate_data, dim="time") 30 | rotator = xe.single.EOFRotator(n_modes=20) 31 | rotator.fit(mpca) 32 | rcomponents = rotator.components() 33 | rscores = rotator.scores() 34 | 35 | # %% 36 | # Plot mode 1 37 | 38 | mode = 5 39 | proj = PlateCarree() 40 | kwargs = { 41 | "cmap": "RdBu", 42 | "vmin": -0.05, 43 | "vmax": 0.05, 44 | "transform": proj, 45 | "add_colorbar": False, 46 | } 47 | 48 | fig = plt.figure(figsize=(7.3, 6)) 49 | fig.subplots_adjust(wspace=0) 50 | gs = GridSpec(2, 4, figure=fig, width_ratios=[1, 1, 1, 1]) 51 | ax = [fig.add_subplot(gs[0, i], projection=proj) for i in range(4)] 52 | ax_pc = fig.add_subplot(gs[1, :]) 53 | 54 | # PC 55 | rscores.sel(mode=mode).plot(ax=ax_pc) 56 | ax_pc.set_xlabel("") 57 | ax_pc.set_title("") 58 | 59 | # EOFs 60 | for i, (a, comps) in enumerate(zip(ax, rcomponents)): 61 | a.coastlines(color=".5") 62 | comps.sel(mode=mode).plot(ax=a, **kwargs) 63 | a.set_xticks([], []) 64 | a.set_yticks([], []) 65 | a.set_xlabel("") 66 | a.set_ylabel("") 67 | a.set_title("Subset {:}".format(i + 1)) 68 | ax[0].set_ylabel("EOFs") 69 | fig.suptitle("Mode {:}".format(mode)) 70 | plt.savefig("mreof-analysis.jpg") 71 | -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_mreof.py.md5: -------------------------------------------------------------------------------- 1 | d7e99506275146b34aa2607581ecbd1e -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_mreof_codeobj.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/plot_mreof_codeobj.pickle -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_multivariate-eof.py: -------------------------------------------------------------------------------- 1 | """ 2 | Multivariate EOF analysis 3 | ============================================ 4 | 5 | Multivariate EOF analysis. 6 | """ 7 | 8 | # Load packages and data: 9 | import matplotlib.pyplot as plt 10 | import xarray as xr 11 | from cartopy.crs import PlateCarree 12 | from matplotlib.gridspec import GridSpec 13 | 14 | import xeofs as xe 15 | 16 | # Create four different dataarrayss 17 | sst = xr.tutorial.open_dataset("ersstv5")["sst"] 18 | subset1 = sst.isel(lon=slice(0, 45)) 19 | subset2 = sst.isel(lon=slice(46, 90)) 20 | subset3 = sst.isel(lon=slice(91, 135)) 21 | subset4 = sst.isel(lon=slice(136, None)) 22 | multivariate_data = [subset1, subset2, subset3, subset4] 23 | 24 | # %% 25 | # Perform the actual analysis 26 | 27 | pca = xe.single.EOF(n_modes=10, standardize=False, use_coslat=True) 28 | pca.fit(multivariate_data, dim="time") 29 | components = pca.components() 30 | scores = pca.scores() 31 | 32 | # %% 33 | # Plot mode 1 34 | 35 | mode = 5 36 | proj = PlateCarree() 37 | kwargs = { 38 | "cmap": "RdBu", 39 | "vmin": -0.05, 40 | "vmax": 0.05, 41 | "transform": proj, 42 | "add_colorbar": False, 43 | } 44 | 45 | fig = plt.figure(figsize=(7.3, 6)) 46 | fig.subplots_adjust(wspace=0) 47 | gs = GridSpec(2, 4, figure=fig, width_ratios=[1, 1, 1, 1]) 48 | ax = [fig.add_subplot(gs[0, i], projection=proj) for i in range(4)] 49 | ax_pc = fig.add_subplot(gs[1, :]) 50 | 51 | # PC 52 | scores.sel(mode=mode).plot(ax=ax_pc) 53 | ax_pc.set_xlabel("") 54 | ax_pc.set_title("") 55 | 56 | # EOFs 57 | for i, (a, comps) in enumerate(zip(ax, components)): 58 | a.coastlines(color=".5") 59 | comps.sel(mode=mode).plot(ax=a, **kwargs) 60 | a.set_xticks([], []) 61 | a.set_yticks([], []) 62 | a.set_xlabel("") 63 | a.set_ylabel("") 64 | a.set_title("Subset {:}".format(i + 1)) 65 | ax[0].set_ylabel("EOFs") 66 | fig.suptitle("Mode {:}".format(mode)) 67 | plt.savefig("multivariate-eof-analysis.jpg") 68 | -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_multivariate-eof.py.md5: -------------------------------------------------------------------------------- 1 | 58257a2d0b4d2b974cf43e80e195be40 -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_multivariate-eof_codeobj.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/plot_multivariate-eof_codeobj.pickle -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_rotated_eof.py.md5: -------------------------------------------------------------------------------- 1 | 669ef8691b2403f69184b4309b0f154b -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_rotated_eof_codeobj.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/plot_rotated_eof_codeobj.pickle -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_weighted-eof.py: -------------------------------------------------------------------------------- 1 | """ 2 | Weighted EOF analysis 3 | ======================== 4 | 5 | Weighted EOF analysis (in S-mode) maximises the temporal variance 6 | considering each gridpoint with a different weight. We compare the 7 | results for an EOF analysis based on (1) the covariance matrix, (2) area 8 | weighting based on latitude (coslat weighting), (3) the correlation matrix 9 | and finally (4) correlation matrix + coslat weighting. 10 | 11 | Load packages and data: 12 | """ 13 | 14 | import matplotlib.pyplot as plt 15 | import seaborn as sns 16 | import xarray as xr 17 | from cartopy.crs import Orthographic, PlateCarree 18 | from matplotlib.gridspec import GridSpec 19 | 20 | import xeofs as xe 21 | 22 | sns.set_context("paper") 23 | 24 | t2m = xr.tutorial.load_dataset("air_temperature")["air"] 25 | 26 | # %% 27 | # Perform the actual analysis 28 | 29 | components = [] 30 | scores = [] 31 | # (1) Based on covariance matrix 32 | model_cov = xe.single.EOF(n_modes=5, standardize=False, use_coslat=False) 33 | model_cov.fit(t2m, "time") 34 | components.append(model_cov.components()) 35 | scores.append(model_cov.scores()) 36 | # (2) Based on coslat weighted covariance matrix 37 | model_lat = xe.single.EOF(n_modes=5, standardize=False, use_coslat=True) 38 | model_lat.fit(t2m, "time") 39 | components.append(model_lat.components()) 40 | scores.append(model_lat.scores()) 41 | # (3) Based on correlation matrix 42 | model_cor = xe.single.EOF(n_modes=5, standardize=True, use_coslat=False) 43 | model_cor.fit(t2m, "time") 44 | components.append(model_cor.components()) 45 | scores.append(model_cor.scores()) 46 | # (4) Based on coslat weighted correlation matrix 47 | model_cor_lat = xe.single.EOF(n_modes=5, standardize=True, use_coslat=True) 48 | model_cor_lat.fit(t2m, "time") 49 | components.append(model_cor_lat.components()) 50 | scores.append(model_cor_lat.scores()) 51 | 52 | 53 | # %% 54 | # Create figure showing the first mode for all 4 cases 55 | 56 | proj = Orthographic(central_latitude=30, central_longitude=-80) 57 | kwargs = { 58 | "cmap": "mako", 59 | "transform": PlateCarree(), 60 | "vmin": 0, 61 | } 62 | titles = [ 63 | "(1) Covariances", 64 | "(2) Covariances + coslat", 65 | "(3) Correlation", 66 | "(4) Correlation + coslat", 67 | ] 68 | fig = plt.figure(figsize=(10, 12)) 69 | gs = GridSpec(4, 2) 70 | ax_pcs = [fig.add_subplot(gs[i, 0]) for i in range(4)] 71 | ax_eofs = [fig.add_subplot(gs[i, 1], projection=proj) for i in range(4)] 72 | 73 | for i, (a1, a2) in enumerate(zip(ax_eofs, ax_pcs)): 74 | a1.coastlines(color=".5") 75 | components[i].sel(mode=1).plot(ax=a1, **kwargs) 76 | scores[i].sel(mode=1).plot(ax=a2, color="darkred") 77 | a2.set_xlabel("") 78 | a1.set_title("", loc="center") 79 | a2.set_title("", loc="center") 80 | a2.set_title(titles[i], loc="left", weight="bold") 81 | if i < 3: 82 | a2.set_xticks([], []) 83 | sns.despine(ax=a2, trim=True, bottom=True) 84 | else: 85 | sns.despine(ax=a2, trim=True, bottom=False) 86 | 87 | plt.tight_layout() 88 | plt.savefig("weighted_eof.jpg", dpi=200) 89 | -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_weighted-eof.py.md5: -------------------------------------------------------------------------------- 1 | 7047ce79c7b692b5a3beb8b39381ff16 -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/1single/plot_weighted-eof_codeobj.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/1single/plot_weighted-eof_codeobj.pickle -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/2cross/images/sphx_glr_plot_mca_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/2cross/images/sphx_glr_plot_mca_001.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/2cross/images/sphx_glr_plot_rotated_mca_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/2cross/images/sphx_glr_plot_rotated_mca_001.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/2cross/images/thumb/sphx_glr_plot_mca_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/2cross/images/thumb/sphx_glr_plot_mca_thumb.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/2cross/images/thumb/sphx_glr_plot_rotated_mca_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/2cross/images/thumb/sphx_glr_plot_rotated_mca_thumb.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/2cross/index.rst: -------------------------------------------------------------------------------- 1 | 2 | 3 | .. _sphx_glr_content_user_guide_auto_examples_2cross: 4 | 5 | 2 | Cross-Set Analysis 6 | ======================== 7 | 8 | 9 | 10 | .. raw:: html 11 | 12 |
13 | 14 | 15 | .. raw:: html 16 | 17 |
18 | 19 | .. only:: html 20 | 21 | .. image:: /content/user_guide/auto_examples/2cross/images/thumb/sphx_glr_plot_mca_thumb.png 22 | :alt: 23 | 24 | :ref:`sphx_glr_content_user_guide_auto_examples_2cross_plot_mca.py` 25 | 26 | .. raw:: html 27 | 28 |
Maximum Covariance Analysis
29 |
30 | 31 | 32 | .. raw:: html 33 | 34 |
35 | 36 | .. only:: html 37 | 38 | .. image:: /content/user_guide/auto_examples/2cross/images/thumb/sphx_glr_plot_rotated_mca_thumb.png 39 | :alt: 40 | 41 | :ref:`sphx_glr_content_user_guide_auto_examples_2cross_plot_rotated_mca.py` 42 | 43 | .. raw:: html 44 | 45 |
Rotated Maximum Covariance Analysis
46 |
47 | 48 | 49 | .. raw:: html 50 | 51 |
52 | 53 | 54 | .. toctree:: 55 | :hidden: 56 | 57 | /content/user_guide/auto_examples/2cross/plot_mca 58 | /content/user_guide/auto_examples/2cross/plot_rotated_mca 59 | 60 | -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/2cross/plot_mca.py.md5: -------------------------------------------------------------------------------- 1 | e696fb777ef84ec447201ca9c01d1dfe -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/2cross/plot_mca_codeobj.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/2cross/plot_mca_codeobj.pickle -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/2cross/plot_rotated_mca.py.md5: -------------------------------------------------------------------------------- 1 | 00a21b73b61a542faad2eabd27cfcdf8 -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/2cross/plot_rotated_mca_codeobj.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/2cross/plot_rotated_mca_codeobj.pickle -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/2cross/sg_execution_times.rst: -------------------------------------------------------------------------------- 1 | 2 | :orphan: 3 | 4 | .. _sphx_glr_content_user_guide_auto_examples_2cross_sg_execution_times: 5 | 6 | 7 | Computation times 8 | ================= 9 | **00:10.643** total execution time for **content_user_guide_auto_examples_2cross** files: 10 | 11 | +-------------------------------------------------------------------------------------------------------+-----------+--------+ 12 | | :ref:`sphx_glr_content_user_guide_auto_examples_2cross_plot_mca.py` (``plot_mca.py``) | 00:06.057 | 0.0 MB | 13 | +-------------------------------------------------------------------------------------------------------+-----------+--------+ 14 | | :ref:`sphx_glr_content_user_guide_auto_examples_2cross_plot_rotated_mca.py` (``plot_rotated_mca.py``) | 00:04.585 | 0.0 MB | 15 | +-------------------------------------------------------------------------------------------------------+-----------+--------+ 16 | -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/3multi/images/sphx_glr_plot_cca_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/3multi/images/sphx_glr_plot_cca_001.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/3multi/images/sphx_glr_plot_cca_002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/3multi/images/sphx_glr_plot_cca_002.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/3multi/images/thumb/sphx_glr_plot_cca_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/3multi/images/thumb/sphx_glr_plot_cca_thumb.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/3multi/index.rst: -------------------------------------------------------------------------------- 1 | 2 | 3 | .. _sphx_glr_content_user_guide_auto_examples_3multi: 4 | 5 | 3 | Multi-Set Analysis 6 | ======================== 7 | 8 | 9 | 10 | .. raw:: html 11 | 12 |
13 | 14 | 15 | .. raw:: html 16 | 17 |
18 | 19 | .. only:: html 20 | 21 | .. image:: /content/user_guide/auto_examples/3multi/images/thumb/sphx_glr_plot_cca_thumb.png 22 | :alt: 23 | 24 | :ref:`sphx_glr_content_user_guide_auto_examples_3multi_plot_cca.py` 25 | 26 | .. raw:: html 27 | 28 |
Canonical Correlation Analysis
29 |
30 | 31 | 32 | .. raw:: html 33 | 34 |
35 | 36 | 37 | .. toctree:: 38 | :hidden: 39 | 40 | /content/user_guide/auto_examples/3multi/plot_cca 41 | 42 | -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/3multi/plot_cca.py.md5: -------------------------------------------------------------------------------- 1 | ccd6cf17e9ee3ea88ba470a3180fe224 -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/3multi/plot_cca_codeobj.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/3multi/plot_cca_codeobj.pickle -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/3multi/sg_execution_times.rst: -------------------------------------------------------------------------------- 1 | 2 | :orphan: 3 | 4 | .. _sphx_glr_content_user_guide_auto_examples_3multi_sg_execution_times: 5 | 6 | 7 | Computation times 8 | ================= 9 | **00:03.436** total execution time for **content_user_guide_auto_examples_3multi** files: 10 | 11 | +---------------------------------------------------------------------------------------+-----------+--------+ 12 | | :ref:`sphx_glr_content_user_guide_auto_examples_3multi_plot_cca.py` (``plot_cca.py``) | 00:03.436 | 0.0 MB | 13 | +---------------------------------------------------------------------------------------+-----------+--------+ 14 | -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/4validation/images/sphx_glr_plot_bootstrap_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/4validation/images/sphx_glr_plot_bootstrap_001.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/4validation/images/thumb/sphx_glr_plot_bootstrap_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/4validation/images/thumb/sphx_glr_plot_bootstrap_thumb.png -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/4validation/index.rst: -------------------------------------------------------------------------------- 1 | 2 | 3 | .. _sphx_glr_content_user_guide_auto_examples_4validation: 4 | 5 | 4 | Validation 6 | =============== 7 | 8 | 9 | .. raw:: html 10 | 11 |
12 | 13 | 14 | .. raw:: html 15 | 16 |
17 | 18 | .. only:: html 19 | 20 | .. image:: /content/user_guide/auto_examples/4validation/images/thumb/sphx_glr_plot_bootstrap_thumb.png 21 | :alt: 22 | 23 | :ref:`sphx_glr_content_user_guide_auto_examples_4validation_plot_bootstrap.py` 24 | 25 | .. raw:: html 26 | 27 |
Significance testing of EOF analysis via bootstrap
28 |
29 | 30 | 31 | .. raw:: html 32 | 33 |
34 | 35 | 36 | .. toctree:: 37 | :hidden: 38 | 39 | /content/user_guide/auto_examples/4validation/plot_bootstrap 40 | 41 | -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/4validation/plot_bootstrap.py.md5: -------------------------------------------------------------------------------- 1 | dcdf2653c5cb47f1d8828948c6fdda17 -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/4validation/plot_bootstrap_codeobj.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/4validation/plot_bootstrap_codeobj.pickle -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/4validation/sg_execution_times.rst: -------------------------------------------------------------------------------- 1 | 2 | :orphan: 3 | 4 | .. _sphx_glr_content_user_guide_auto_examples_4validation_sg_execution_times: 5 | 6 | 7 | Computation times 8 | ================= 9 | **00:29.972** total execution time for **content_user_guide_auto_examples_4validation** files: 10 | 11 | +--------------------------------------------------------------------------------------------------------+-----------+--------+ 12 | | :ref:`sphx_glr_content_user_guide_auto_examples_4validation_plot_bootstrap.py` (``plot_bootstrap.py``) | 00:29.972 | 0.0 MB | 13 | +--------------------------------------------------------------------------------------------------------+-----------+--------+ 14 | -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/auto_examples_jupyter.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/auto_examples_jupyter.zip -------------------------------------------------------------------------------- /docs/content/user_guide/auto_examples/auto_examples_python.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/auto_examples/auto_examples_python.zip -------------------------------------------------------------------------------- /docs/content/user_guide/core_functionalities/efficient.rst: -------------------------------------------------------------------------------- 1 | 2 | ============================================= 3 | Computationally Efficient 4 | ============================================= 5 | 6 | Whether you're working with in-memory or out-of-memory data, xeofs ensures computational efficiency. This is achieved using randomized SVD, which is typically faster for large matrices than a full SVD. For more details, refer to the sklearn documentation_ on PCA. 7 | 8 | A comparative analysis demonstrates the performance of xeofs against eofs_ on a standard laptop using a 3D dataset with time, longitude, and latitude dimensions. Results indicate that xeofs computes moderate datasets (10,000 samples by 100,000 features) in under a minute. While eofs is faster for smaller datasets, xeofs excels with larger datasets, offering significant speed advantages. The dashed line marks datasets with about 3 MiB; xeofs outpaces eofs above this size, whereas eofs is preferable for smaller datasets. 9 | 10 | Comparison of computational times between xeofs and eofs for data sets of varying sizes 11 | 12 | 13 | .. image:: ../../../perf/timings_dark.png 14 | :height: 300px 15 | :width: 750px 16 | :alt: Comparison of computational times between xeofs and eofs for data sets of varying sizes 17 | :align: center 18 | :class: only-dark 19 | 20 | .. image:: ../../../perf/timings_light.png 21 | :height: 300px 22 | :width: 750px 23 | :alt: Comparison of computational times between xeofs and eofs for data sets of varying sizes 24 | :align: center 25 | :class: only-light 26 | 27 | 28 | .. note:: 29 | 30 | You can find the script to run the performance tests here_. 31 | 32 | 33 | .. _eofs: https://ajdawson.github.io/eofs/ 34 | .. _here: https://github.com/xarray-contrib/xeofs/tree/main/docs/perf 35 | .. _documentation: https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html 36 | -------------------------------------------------------------------------------- /docs/content/user_guide/core_functionalities/index.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | Core Functionalities 3 | ============================== 4 | 5 | .. grid:: 3 6 | 7 | .. grid-item-card:: 8 | :octicon:`package;10em` 9 | :link: labeled_data 10 | :link-type: doc 11 | :text-align: center 12 | 13 | **Multi-Dimensional & Labeled Data** 14 | 15 | Perform dimensionality reduction within your familiar xarray ecosystem. 16 | 17 | .. grid-item-card:: 18 | :octicon:`globe;10em` 19 | :link: dask_support 20 | :link-type: doc 21 | :text-align: center 22 | 23 | **Dask Support** 24 | 25 | Process large datasets efficiently with dask xarray objects. 26 | 27 | 28 | 29 | .. grid-item-card:: 30 | :octicon:`rocket;10em` 31 | :link: efficient 32 | :link-type: doc 33 | :text-align: center 34 | 35 | **Computationally Efficient** 36 | 37 | Handle millions of features swiftly using randomized linear algebra. 38 | 39 | 40 | .. grid:: 3 41 | 42 | .. grid-item-card:: 43 | :octicon:`sync;10em` 44 | :link: model_serialization 45 | :link-type: doc 46 | :text-align: center 47 | 48 | **Model Serialization** 49 | 50 | Save and load models for future analysis. 51 | 52 | .. grid-item-card:: 53 | :octicon:`verified;10em` 54 | :link: model_evaluation 55 | :link-type: doc 56 | :text-align: center 57 | 58 | **Significance Testing** 59 | 60 | Assess the significance of your results with bootstrapping. 61 | 62 | .. grid-item-card:: 63 | :octicon:`key-asterisk;10em` 64 | :link: missing_values 65 | :link-type: doc 66 | :text-align: center 67 | 68 | **Dealing with missing values** 69 | 70 | Manage missing data flexibly within your analysis. 71 | 72 | 73 | .. toctree:: 74 | :maxdepth: 3 75 | :hidden: 76 | 77 | labeled_data 78 | dask_support 79 | efficient 80 | model_serialization 81 | model_evaluation 82 | missing_values -------------------------------------------------------------------------------- /docs/content/user_guide/core_functionalities/labeled_data.rst: -------------------------------------------------------------------------------- 1 | ============================================= 2 | Labeled ND Data 3 | ============================================= 4 | 5 | xeofs is specifically designed for xarray_ objects. This design choice allows you to apply dimensionality reduction techniques to multi-dimensional data while 6 | maintaining data labels. The only requirement for the user is to specify the *sample* dimensions. By default, xeofs assumes all other dimensions present in the 7 | data are *feature* dimensions. For example: 8 | 9 | .. code-block:: python 10 | 11 | model.fit(data, dim="time") 12 | 13 | This will assume that the data object has a *sample* dimension named ``time``, while all other dimensions are *feature* dimensions to be expressed as latent variables. 14 | 15 | .. note:: 16 | 17 | In this context, *sample* refers to the dimension representing the number of observations. In contrast, *feature* dimensions denote the variables. The main goal of dimensionality reduction techniques is to minimize the number of feature dimensions without altering the sample dimensions. 18 | 19 | .. note:: 20 | 21 | Multiple *sample* dimensions can be specified as long as at least one *feature* dimension remains. 22 | 23 | --------------------------------------------- 24 | Input Data Compatibility 25 | --------------------------------------------- 26 | 27 | xeofs is tailored to function harmoniously with xarray objects. Currently, it supports: 28 | 29 | 1. Single instances of ``xarray.DataArray`` or ``xarray.Dataset`` 30 | 2. Lists comprising ``xarray.DataArray`` or ``xarray.Dataset`` 31 | 32 | An intelligent feature of xeofs is its ability to deliver the appropriate output based on the input type. For instance, executing PCA on a 33 | single ``xarray.DataArray`` will yield a single ``xarray.DataArray`` for the PC components. Conversely, if a list of ``xarray.DataArray`` is inputted, 34 | xeofs will return a list of ``xarray.DataArray`` as PC components. 35 | 36 | .. warning:: 37 | 38 | A mixed list containing both xr.DataArray and xr.Dataset objects is not currently supported. 39 | 40 | .. warning:: 41 | 42 | Some methods like PCA/EOF analysis can also handle complex-valued input data. However, due to a limitation in the underlying dask_ library, complex-valued data is not supported when using dask. 43 | 44 | 45 | 46 | .. _limitation: https://github.com/dask/dask/issues/7639 47 | .. _xarray: https://docs.xarray.dev/en/stable/index.html 48 | .. _dask: https://dask.org/ 49 | 50 | -------------------------------------------------------------------------------- /docs/content/user_guide/core_functionalities/missing_values.rst: -------------------------------------------------------------------------------- 1 | 2 | ============================================= 3 | Handling Missing Values 4 | ============================================= 5 | 6 | Conventional SVD algorithms aren't typically designed to manage missing values. To address this, xeofs handles missing values (``NaNs``) within your data. There are two primary types of missing values: 7 | 8 | 1. Full-dimensional: ``NaNs`` spanning all samples for a specific feature or vice versa. 9 | 2. Isolated: Occasional or sporadic ``NaNs`` within the dataset. 10 | 11 | For example, in a 3D dataset with dimensions (time, lon, lat), a full-dimensional ``NaN`` might represent a grid point (lon, lat) exhibiting ``NaNs`` across all time steps. Conversely, an isolated ``NaN`` might indicate a grid point (lon, lat) displaying ``NaNs`` for only certain time steps. 12 | 13 | xeofs is adept at handling full-dimensional ``NaNs``. However, it cannot manage isolated ``NaNs``. Users need to decide how to fill or remove features or samples containing isolated ``NaNs``. 14 | 15 | .. note:: 16 | 17 | xeofs provides an optional runtime check ``check_nans``, enabled by default, which raises an error if isolated ``NaNs`` are detected. 18 | 19 | -------------------------------------------------------------------------------- /docs/content/user_guide/core_functionalities/model_evaluation.rst: -------------------------------------------------------------------------------- 1 | ==================== 2 | Significance Testing 3 | ==================== 4 | 5 | xeofs is dedicated to providing a user-friendly interface for model evaluations using bootstrapping. Currently, it supports bootstrapping for PCA/EOF analysis. 6 | For a practical example, see the :doc:`validation example <../auto_examples/4validation/index>`. -------------------------------------------------------------------------------- /docs/content/user_guide/core_functionalities/model_serialization.rst: -------------------------------------------------------------------------------- 1 | ============================================= 2 | Model Serialization 3 | ============================================= 4 | 5 | xeofs models offer convenient ``save()`` and ``load()`` methods for serializing 6 | fitted models to a portable format. 7 | 8 | .. code-block:: python 9 | 10 | from xeofs.single import EOF 11 | 12 | model = EOF() 13 | model.fit(data, dim="time") 14 | model.save("my_model.zarr") 15 | 16 | # Later, you can load the model 17 | loaded_model = EOF.load("my_model.zarr") 18 | 19 | 20 | -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/README.rst: -------------------------------------------------------------------------------- 1 | 1 | Single-Set Analysis 2 | ======================= 3 | -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/eof-smode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/examples/1single/eof-smode.jpg -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/eof-tmode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/examples/1single/eof-tmode.jpg -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/mreof-analysis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/examples/1single/mreof-analysis.jpg -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/multivariate-eof-analysis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/examples/1single/multivariate-eof-analysis.jpg -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/plot_complex_eof.py: -------------------------------------------------------------------------------- 1 | """ 2 | Complex EOF analysis 3 | ============================================ 4 | 5 | In this tutorial, we'll walk through how to perform a Complex EOF analysis on 6 | the zonal and meridional wind components. 7 | 8 | Let's start by importing the necessary packages and loading the data: 9 | """ 10 | 11 | # %% 12 | import matplotlib.pyplot as plt 13 | import xarray as xr 14 | 15 | import xeofs as xe 16 | 17 | xr.set_options(display_expand_attrs=False) 18 | 19 | # %% 20 | # For this example, we'll use the ERA-Interim tutorial dataset ``eraint_uvz``: 21 | 22 | uvz = xr.tutorial.open_dataset("eraint_uvz") 23 | uvz 24 | 25 | # %% 26 | # This dataset contains the zonal, meridional, and vertical wind components at 27 | # three different atmospheric levels. Note that the data only covers two months, 28 | # so we have just two time steps (samples). While this isn't enough for a robust 29 | # EOF analysis, we'll proceed for demonstration purposes. Now, let's combine the 30 | # zonal (``u``) and meridional (``v``) wind components into a complex-valued 31 | # dataset: 32 | 33 | Z = uvz["u"] + 1j * uvz["v"] 34 | 35 | # %% 36 | # Next, we'll initialize and fit the ``ComplexEOF`` model to our data. The 37 | # ``xeofs`` package makes this easy by allowing us to specify the sample 38 | # dimension (``month``), automatically performing the Complex EOF analysis 39 | # across all three atmospheric levels. As a standard practice, we'll also weigh 40 | # each grid cell by the square root of the cosine of the latitude 41 | # (``use_coslat=True``). 42 | 43 | model = xe.single.ComplexEOF(n_modes=1, use_coslat=True, random_state=7) 44 | model.fit(Z, dim="month") 45 | 46 | # %% 47 | # Instead of just extracting the complex-valued components, we can also get the 48 | # amplitude and phase of these components. Let's start by looking at the 49 | # amplitude of the first mode: 50 | 51 | 52 | spatial_ampltiudes = model.components_amplitude() 53 | spatial_phases = model.components_phase() 54 | 55 | spatial_ampltiudes.sel(mode=1).plot(col="level") 56 | plt.show() 57 | 58 | # %% 59 | # It looks like the first mode picks up a pattern resembling the location of the 60 | # subtropical jet stream around ±30º latitude, particularly strong in the upper 61 | # troposphere at 200 hPa and weaker toward the surface. We can also plot the 62 | # phase of the first mode. To keep the plot clear, we'll only show the phase 63 | # where the amplitude is above a certain threshold (e.g., 0.004): 64 | 65 | relevant_phases = spatial_phases.where(spatial_ampltiudes > 0.004) 66 | relevant_phases.sel(mode=1).plot(col="level", cmap="twilight") 67 | plt.show() 68 | 69 | # %% 70 | -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/plot_eeof.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/examples/1single/plot_eeof.png -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/plot_eeof_trend.py: -------------------------------------------------------------------------------- 1 | """ 2 | Removing nonlinear trends with EEOF analysis 3 | ============================================ 4 | 5 | This tutorial illustrates the application of Extended EOF (EEOF) analysis 6 | to isolate and remove nonlinear trends within a dataset. 7 | 8 | Let's begin by setting up the required packages and fetching the data. 9 | """ 10 | 11 | import matplotlib.pyplot as plt 12 | import xarray as xr 13 | 14 | import xeofs as xe 15 | 16 | xr.set_options(display_expand_data=False) 17 | 18 | # %% 19 | # We load the sea surface temperature (SST) data from the xarray tutorial. 20 | # The dataset consists of monthly averages from 1970 to 2021. To ensure the seasonal 21 | # cycle doesn't overshadow the analysis, we remove the monthly climatologies. 22 | 23 | sst = xr.tutorial.open_dataset("ersstv5").sst 24 | sst = sst.groupby("time.month") - sst.groupby("time.month").mean("time") 25 | 26 | 27 | # %% 28 | # We start by performing a standard EOF analysis on the dataset. 29 | 30 | eof = xe.single.EOF(n_modes=10) 31 | eof.fit(sst, dim="time") 32 | scores = eof.scores() 33 | components = eof.components() 34 | 35 | # %% 36 | # We immediately see that the first mode represents the global warming trend. 37 | # Yet, the signal is somewhat muddled by short-term and year-to-year variations. 38 | # Note the pronounced spikes around 1998 and 2016, hinting at the leakage of 39 | # ENSO signatures into this mode. 40 | 41 | fig, ax = plt.subplots(1, 2, figsize=(10, 5)) 42 | scores.sel(mode=1).plot(ax=ax[0]) 43 | components.sel(mode=1).plot(ax=ax[1]) 44 | 45 | 46 | # %% 47 | # Now, let's try to identify this trend more cleanly. To this end, we perform an 48 | # EEOF analysis on the same data with a suitably large embedding dimension. 49 | # We choose an embedding dimensioncorresponding to 120 months which is large enough 50 | # to capture long-term trends. To speed up computation, we apply the EEOF analysis 51 | # to the extended (lag) covariance matrix derived from the first 50 PCs. 52 | 53 | eeof = xe.single.ExtendedEOF(n_modes=5, tau=1, embedding=120, n_pca_modes=50) 54 | eeof.fit(sst, dim="time") 55 | components_ext = eeof.components() 56 | scores_ext = eeof.scores() 57 | 58 | # %% 59 | # The first mode now represents the global warming trend much more clearly. 60 | 61 | fig, ax = plt.subplots(1, 2, figsize=(10, 5)) 62 | scores_ext.sel(mode=1).plot(ax=ax[0]) 63 | components_ext.sel(mode=1, embedding=0).plot(ax=ax[1]) 64 | 65 | # %% 66 | # We can use this to the first mode to remove this nonlinear trend from our original dataset. 67 | 68 | sst_trends = eeof.inverse_transform(scores_ext.sel(mode=1)) 69 | sst_detrended = sst - sst_trends 70 | 71 | 72 | # %% 73 | # Reapplying the standard EOF analysis on our now detrended dataset: 74 | 75 | eof_model_detrended = xe.single.EOF(n_modes=5) 76 | eof_model_detrended.fit(sst_detrended, dim="time") 77 | scores_detrended = eof_model_detrended.scores() 78 | components_detrended = eof_model_detrended.components() 79 | 80 | 81 | # %% 82 | # The first mode now represents ENSO without any trend component. 83 | 84 | fig, ax = plt.subplots(1, 2, figsize=(10, 5)) 85 | scores_detrended.sel(mode=1).plot(ax=ax[0]) 86 | components_detrended.sel(mode=1).plot(ax=ax[1]) 87 | 88 | 89 | # %% 90 | -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/plot_eof-smode.py: -------------------------------------------------------------------------------- 1 | """ 2 | Sparse PCA 3 | ======================== 4 | 5 | This example demonstrates the application of sparse PCA [1]_ to sea surface temperature data. Sparse PCA is an alternative to rotated PCA, where the components are sparse, often providing a more interpretable solution. 6 | 7 | We replicate the analysis from the original paper [1]_, which identifies the ENSO (El Niño-Southern Oscillation) as the fourth mode, representing about 1% of the total variance. The original study focused on weekly sea surface temperatures from satellite data, whereas we use monthly data from ERSSTv5. Consequently, our results may not match exactly, but they should be quite similar. 8 | 9 | References 10 | ---------- 11 | .. [1] Erichson, N. B. et al. Sparse Principal Component Analysis via Variable Projection. SIAM J. Appl. Math. 80, 977-1002 (2020). 12 | 13 | """ 14 | 15 | # Load packages and data: 16 | import matplotlib.pyplot as plt 17 | import xarray as xr 18 | from cartopy.crs import EqualEarth, PlateCarree 19 | from matplotlib.gridspec import GridSpec 20 | 21 | import xeofs as xe 22 | 23 | # %% 24 | # We use sea surface temperature data from 1990 to 2017, consistent with the original paper. 25 | 26 | sst = xr.tutorial.open_dataset("ersstv5")["sst"] 27 | sst = sst.sel(time=slice("1990", "2017")) 28 | 29 | # %% 30 | # We perform sparse PCA using the `alpha` and `beta` parameters, which define the sparsity imposed by the elastic net (refer to Table 1 in the paper). In our analysis, we set `alpha` to 1e-5, as specified by the authors. Although the authors do not specify a value for `beta`, it appears that the results are not highly sensitive to this parameter. Therefore, we use the default `beta` value of 1e-4. 31 | 32 | model = xe.single.SparsePCA(n_modes=4, alpha=1e-5) 33 | model.fit(sst, dim="time") 34 | expvar = model.explained_variance() 35 | expvar_ratio = model.explained_variance_ratio() 36 | components = model.components() 37 | scores = model.scores() 38 | 39 | # %% 40 | # The explained variance fraction confirms that the fourth mode explains about 1% of the total variance, which is consistent with the original paper. 41 | 42 | print("Explained variance: ", expvar.round(0).values) 43 | print("Relative: ", (expvar_ratio * 100).round(1).values) 44 | 45 | # %% 46 | # Examining the first four modes, we clearly identify ENSO as the fourth mode. 47 | 48 | proj = EqualEarth(central_longitude=180) 49 | kwargs = {"cmap": "RdBu", "vmin": -0.05, "vmax": 0.05, "transform": PlateCarree()} 50 | 51 | fig = plt.figure(figsize=(10, 12)) 52 | gs = GridSpec(4, 2, width_ratios=[1, 2]) 53 | ax0 = [fig.add_subplot(gs[i, 0]) for i in range(4)] 54 | ax1 = [fig.add_subplot(gs[i, 1], projection=proj) for i in range(4)] 55 | 56 | for i, (a0, a1) in enumerate(zip(ax0, ax1)): 57 | scores.sel(mode=i + 1).plot(ax=a0) 58 | a1.coastlines(color=".5") 59 | components.sel(mode=i + 1).plot(ax=a1, **kwargs) 60 | 61 | a0.set_xlabel("") 62 | 63 | plt.tight_layout() 64 | plt.savefig("sparse_pca.jpg") 65 | 66 | # %% 67 | -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/plot_eof-tmode.py: -------------------------------------------------------------------------------- 1 | """ 2 | EOF analysis (T-mode) 3 | ======================== 4 | 5 | EOF analysis in T-mode maximises the spatial variance. 6 | 7 | Load packages and data: 8 | """ 9 | 10 | import matplotlib.pyplot as plt 11 | import xarray as xr 12 | from cartopy.crs import EqualEarth, PlateCarree 13 | from matplotlib.gridspec import GridSpec 14 | 15 | import xeofs as xe 16 | 17 | sst = xr.tutorial.open_dataset("ersstv5")["sst"] 18 | 19 | # %% 20 | # Perform the actual analysis 21 | 22 | model = xe.single.EOF(n_modes=5) 23 | model.fit(sst, dim=("lat", "lon")) 24 | expvar = model.explained_variance_ratio() 25 | components = model.components() 26 | scores = model.scores() 27 | 28 | # %% 29 | # Create figure showing the first two modes 30 | 31 | proj = EqualEarth(central_longitude=180) 32 | kwargs = {"cmap": "RdBu", "transform": PlateCarree()} 33 | 34 | fig = plt.figure(figsize=(10, 8)) 35 | gs = GridSpec(3, 2, width_ratios=[2, 1]) 36 | ax0 = [fig.add_subplot(gs[i, 0], projection=proj) for i in range(3)] 37 | ax1 = [fig.add_subplot(gs[i, 1]) for i in range(3)] 38 | 39 | for i, (a0, a1) in enumerate(zip(ax0, ax1)): 40 | scores.sel(mode=i + 1).plot(ax=a0, **kwargs) 41 | a0.coastlines(color=".5") 42 | components.sel(mode=i + 1).plot(ax=a1) 43 | 44 | a0.set_xlabel("") 45 | 46 | plt.tight_layout() 47 | plt.savefig("eof-tmode.jpg") 48 | -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/plot_mreof.py: -------------------------------------------------------------------------------- 1 | """ 2 | Varimax-rotated Multivariate EOF analysis 3 | ============================================ 4 | 5 | Multivariate EOF analysis with additional Varimax rotation. 6 | """ 7 | 8 | # Load packages and data: 9 | import matplotlib.pyplot as plt 10 | import xarray as xr 11 | from cartopy.crs import PlateCarree 12 | from matplotlib.gridspec import GridSpec 13 | 14 | import xeofs as xe 15 | 16 | # %% 17 | # Create four different dataarrayss 18 | sst = xr.tutorial.open_dataset("ersstv5")["sst"] 19 | subset1 = sst.isel(lon=slice(0, 45)) 20 | subset2 = sst.isel(lon=slice(46, 90)) 21 | subset3 = sst.isel(lon=slice(91, 135)) 22 | subset4 = sst.isel(lon=slice(136, None)) 23 | 24 | # %% 25 | # Perform the actual analysis 26 | 27 | multivariate_data = [subset1, subset2, subset3, subset4] 28 | mpca = xe.single.EOF(n_modes=100, standardize=False, use_coslat=True) 29 | mpca.fit(multivariate_data, dim="time") 30 | rotator = xe.single.EOFRotator(n_modes=20) 31 | rotator.fit(mpca) 32 | rcomponents = rotator.components() 33 | rscores = rotator.scores() 34 | 35 | # %% 36 | # Plot mode 1 37 | 38 | mode = 5 39 | proj = PlateCarree() 40 | kwargs = { 41 | "cmap": "RdBu", 42 | "vmin": -0.05, 43 | "vmax": 0.05, 44 | "transform": proj, 45 | "add_colorbar": False, 46 | } 47 | 48 | fig = plt.figure(figsize=(7.3, 6)) 49 | fig.subplots_adjust(wspace=0) 50 | gs = GridSpec(2, 4, figure=fig, width_ratios=[1, 1, 1, 1]) 51 | ax = [fig.add_subplot(gs[0, i], projection=proj) for i in range(4)] 52 | ax_pc = fig.add_subplot(gs[1, :]) 53 | 54 | # PC 55 | rscores.sel(mode=mode).plot(ax=ax_pc) 56 | ax_pc.set_xlabel("") 57 | ax_pc.set_title("") 58 | 59 | # EOFs 60 | for i, (a, comps) in enumerate(zip(ax, rcomponents)): 61 | a.coastlines(color=".5") 62 | comps.sel(mode=mode).plot(ax=a, **kwargs) 63 | a.set_xticks([], []) 64 | a.set_yticks([], []) 65 | a.set_xlabel("") 66 | a.set_ylabel("") 67 | a.set_title("Subset {:}".format(i + 1)) 68 | ax[0].set_ylabel("EOFs") 69 | fig.suptitle("Mode {:}".format(mode)) 70 | plt.savefig("mreof-analysis.jpg") 71 | -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/plot_multivariate-eof.py: -------------------------------------------------------------------------------- 1 | """ 2 | Multivariate EOF analysis 3 | ============================================ 4 | 5 | Multivariate EOF analysis. 6 | """ 7 | 8 | # Load packages and data: 9 | import matplotlib.pyplot as plt 10 | import xarray as xr 11 | from cartopy.crs import PlateCarree 12 | from matplotlib.gridspec import GridSpec 13 | 14 | import xeofs as xe 15 | 16 | # Create four different dataarrayss 17 | sst = xr.tutorial.open_dataset("ersstv5")["sst"] 18 | subset1 = sst.isel(lon=slice(0, 45)) 19 | subset2 = sst.isel(lon=slice(46, 90)) 20 | subset3 = sst.isel(lon=slice(91, 135)) 21 | subset4 = sst.isel(lon=slice(136, None)) 22 | multivariate_data = [subset1, subset2, subset3, subset4] 23 | 24 | # %% 25 | # Perform the actual analysis 26 | 27 | pca = xe.single.EOF(n_modes=10, standardize=False, use_coslat=True) 28 | pca.fit(multivariate_data, dim="time") 29 | components = pca.components() 30 | scores = pca.scores() 31 | 32 | # %% 33 | # Plot mode 1 34 | 35 | mode = 5 36 | proj = PlateCarree() 37 | kwargs = { 38 | "cmap": "RdBu", 39 | "vmin": -0.05, 40 | "vmax": 0.05, 41 | "transform": proj, 42 | "add_colorbar": False, 43 | } 44 | 45 | fig = plt.figure(figsize=(7.3, 6)) 46 | fig.subplots_adjust(wspace=0) 47 | gs = GridSpec(2, 4, figure=fig, width_ratios=[1, 1, 1, 1]) 48 | ax = [fig.add_subplot(gs[0, i], projection=proj) for i in range(4)] 49 | ax_pc = fig.add_subplot(gs[1, :]) 50 | 51 | # PC 52 | scores.sel(mode=mode).plot(ax=ax_pc) 53 | ax_pc.set_xlabel("") 54 | ax_pc.set_title("") 55 | 56 | # EOFs 57 | for i, (a, comps) in enumerate(zip(ax, components)): 58 | a.coastlines(color=".5") 59 | comps.sel(mode=mode).plot(ax=a, **kwargs) 60 | a.set_xticks([], []) 61 | a.set_yticks([], []) 62 | a.set_xlabel("") 63 | a.set_ylabel("") 64 | a.set_title("Subset {:}".format(i + 1)) 65 | ax[0].set_ylabel("EOFs") 66 | fig.suptitle("Mode {:}".format(mode)) 67 | plt.savefig("multivariate-eof-analysis.jpg") 68 | -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/plot_weighted-eof.py: -------------------------------------------------------------------------------- 1 | """ 2 | Weighted EOF analysis 3 | ======================== 4 | 5 | Weighted EOF analysis (in S-mode) maximises the temporal variance 6 | considering each gridpoint with a different weight. We compare the 7 | results for an EOF analysis based on (1) the covariance matrix, (2) area 8 | weighting based on latitude (coslat weighting), (3) the correlation matrix 9 | and finally (4) correlation matrix + coslat weighting. 10 | 11 | Load packages and data: 12 | """ 13 | 14 | import matplotlib.pyplot as plt 15 | import seaborn as sns 16 | import xarray as xr 17 | from cartopy.crs import Orthographic, PlateCarree 18 | from matplotlib.gridspec import GridSpec 19 | 20 | import xeofs as xe 21 | 22 | sns.set_context("paper") 23 | 24 | t2m = xr.tutorial.load_dataset("air_temperature")["air"] 25 | 26 | # %% 27 | # Perform the actual analysis 28 | 29 | components = [] 30 | scores = [] 31 | # (1) Based on covariance matrix 32 | model_cov = xe.single.EOF(n_modes=5, standardize=False, use_coslat=False) 33 | model_cov.fit(t2m, "time") 34 | components.append(model_cov.components()) 35 | scores.append(model_cov.scores()) 36 | # (2) Based on coslat weighted covariance matrix 37 | model_lat = xe.single.EOF(n_modes=5, standardize=False, use_coslat=True) 38 | model_lat.fit(t2m, "time") 39 | components.append(model_lat.components()) 40 | scores.append(model_lat.scores()) 41 | # (3) Based on correlation matrix 42 | model_cor = xe.single.EOF(n_modes=5, standardize=True, use_coslat=False) 43 | model_cor.fit(t2m, "time") 44 | components.append(model_cor.components()) 45 | scores.append(model_cor.scores()) 46 | # (4) Based on coslat weighted correlation matrix 47 | model_cor_lat = xe.single.EOF(n_modes=5, standardize=True, use_coslat=True) 48 | model_cor_lat.fit(t2m, "time") 49 | components.append(model_cor_lat.components()) 50 | scores.append(model_cor_lat.scores()) 51 | 52 | 53 | # %% 54 | # Create figure showing the first mode for all 4 cases 55 | 56 | proj = Orthographic(central_latitude=30, central_longitude=-80) 57 | kwargs = { 58 | "cmap": "mako", 59 | "transform": PlateCarree(), 60 | "vmin": 0, 61 | } 62 | titles = [ 63 | "(1) Covariances", 64 | "(2) Covariances + coslat", 65 | "(3) Correlation", 66 | "(4) Correlation + coslat", 67 | ] 68 | fig = plt.figure(figsize=(10, 12)) 69 | gs = GridSpec(4, 2) 70 | ax_pcs = [fig.add_subplot(gs[i, 0]) for i in range(4)] 71 | ax_eofs = [fig.add_subplot(gs[i, 1], projection=proj) for i in range(4)] 72 | 73 | for i, (a1, a2) in enumerate(zip(ax_eofs, ax_pcs)): 74 | a1.coastlines(color=".5") 75 | components[i].sel(mode=1).plot(ax=a1, **kwargs) 76 | scores[i].sel(mode=1).plot(ax=a2, color="darkred") 77 | a2.set_xlabel("") 78 | a1.set_title("", loc="center") 79 | a2.set_title("", loc="center") 80 | a2.set_title(titles[i], loc="left", weight="bold") 81 | if i < 3: 82 | a2.set_xticks([], []) 83 | sns.despine(ax=a2, trim=True, bottom=True) 84 | else: 85 | sns.despine(ax=a2, trim=True, bottom=False) 86 | 87 | plt.tight_layout() 88 | plt.savefig("weighted_eof.jpg", dpi=200) 89 | -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/rotated_eof.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/examples/1single/rotated_eof.jpg -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/sparse_pca.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/examples/1single/sparse_pca.jpg -------------------------------------------------------------------------------- /docs/content/user_guide/examples/1single/weighted_eof.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/examples/1single/weighted_eof.jpg -------------------------------------------------------------------------------- /docs/content/user_guide/examples/2cross/README.rst: -------------------------------------------------------------------------------- 1 | 2 | Cross-Set Analysis 2 | ======================== 3 | -------------------------------------------------------------------------------- /docs/content/user_guide/examples/2cross/mca.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/examples/2cross/mca.jpg -------------------------------------------------------------------------------- /docs/content/user_guide/examples/2cross/rotated_mca.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/examples/2cross/rotated_mca.jpg -------------------------------------------------------------------------------- /docs/content/user_guide/examples/3multi/README.rst: -------------------------------------------------------------------------------- 1 | 3 | Multi-Set Analysis 2 | ======================== 3 | -------------------------------------------------------------------------------- /docs/content/user_guide/examples/4validation/README.rst: -------------------------------------------------------------------------------- 1 | 4 | Validation 2 | =============== -------------------------------------------------------------------------------- /docs/content/user_guide/examples/4validation/bootstrap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/content/user_guide/examples/4validation/bootstrap.jpg -------------------------------------------------------------------------------- /docs/content/user_guide/examples/README.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | Examples 3 | ================== 4 | 5 | Here you can find some examples of how to use the library. -------------------------------------------------------------------------------- /docs/content/user_guide/how_to_cite.rst: -------------------------------------------------------------------------------- 1 | ============================================= 2 | How should I cite xeofs? 3 | ============================================= 4 | 5 | .. note:: 6 | 7 | Always cite the original publication/reference for the methods you use. xeofs is an easy-to-use interface but should not replace the acknowledgment of the original research. 8 | 9 | 10 | We appreciate your willingness to reference xeofs in your academic work. If you're using xeofs, we recommend citing both the following: 11 | 12 | 13 | 1. The xeofs overview article published in the Journal of Open Research Software: 14 | 15 | Rieger, N. & Levang, S. J. (2024). xeofs: Comprehensive EOF analysis in Python with xarray. Journal of Open Source Software, 9(93), 6060. DOI: https://doi.org/10.21105/joss.06060 16 | 17 | Here's an example of a BibTeX entry:: 18 | 19 | @article{xeofs_development_team, 20 | title = {xeofs: Comprehensive EOF analysis in Python with xarray}, 21 | author = {Rieger, N. and Levang, S. J.}, 22 | journal = {Journal of Open Source Software}, 23 | volume = {9}, 24 | number = {93}, 25 | year = {2024}, 26 | publisher = {The Open Journal}, 27 | doi = {10.21105/joss.06060}, 28 | url = {https://doi.org/10.21105/joss.06060} 29 | } 30 | 31 | 32 | 33 | 2. Additionally, you may cite the specific version of the xeofs package you're using. You can obtain a Zenodo citation and DOI for this purpose here: 34 | 35 | .. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.6323012.svg 36 | :target: https://doi.org/10.5281/zenodo.10897729 37 | 38 | Here's an example of a BibTeX entry:: 39 | 40 | @misc{xeofs_v2_3_2, 41 | author = {Rieger, N. and Levang, S. J. and others}, 42 | title = {xeofs: v2.3.2}, 43 | month = mar, 44 | year = 2024, 45 | doi = {10.5281/zenodo.10897729}, 46 | url = {https://doi.org/10.5281/zenodo.10897729} 47 | } -------------------------------------------------------------------------------- /docs/content/user_guide/index.rst: -------------------------------------------------------------------------------- 1 | .. image:: ../..//img/example_sst_rotated_pca_dark.png 2 | :class: only-dark 3 | 4 | .. image:: ../../img/example_sst_rotated_pca_light.png 5 | :class: only-light 6 | 7 | 8 | ============== 9 | User Guide 10 | ============== 11 | 12 | If you are new to xeofs, this is the place to start. Here, you will learn :doc:`why xeofs` was created, what it :doc:`can do ` for you, and how to :doc:`get started`. 13 | 14 | Advantages of using xeofs: 15 | 16 | - :doc:`Multi-Dimensional & Labeled Data `: Designed for xarray_ objects, xeofs applies dimensionality reduction to multi-dimensional data while maintaining data labels. It works with both DataArray and Dataset objects, providing output that matches the type of input, whether single or a list of xr.DataArray or xr.Dataset. 17 | - :doc:`Dask-Integrated `: Supports large datasets via dask_ xarray objects. 18 | - :doc:`Efficient `: Ensures computational efficiency, especially with large datasets, through randomized SVD. 19 | - **Extensive Methods:** Offers various dimensionality reduction techniques. For more details, see the :doc:`API reference<../api_reference/index>`. 20 | - :doc:`Handling Missing Values `: Can manage common cases where some features are masked out by NaN values, such as masked ocean or land cells. 21 | - :doc:`Bootstrapping `: Provides a user-friendly interface for model evaluation using bootstrapping. 22 | - :doc:`Modular `: Allows users to implement and incorporate new dimensionality reduction methods. 23 | 24 | If you're eager to see it in action, check out the :doc:`basic example` to get started quickly. For more comprehensive demonstrations, explore our :doc:`example gallery`. 25 | 26 | Finally, if you're using xeofs in your academic work, please consider citing the software. For more information, see :doc:`how to cite`. 27 | 28 | .. toctree:: 29 | :maxdepth: 3 30 | :hidden: 31 | :caption: Getting Started 32 | 33 | why 34 | should_i_use_this 35 | installation 36 | quickstart 37 | 38 | .. toctree:: 39 | :maxdepth: 3 40 | :hidden: 41 | :caption: Learning More 42 | 43 | core_functionalities/index 44 | model_implementation 45 | auto_examples/index 46 | 47 | .. toctree:: 48 | :maxdepth: 3 49 | :hidden: 50 | :caption: FAQs 51 | 52 | migration_v3 53 | how_to_cite 54 | 55 | 56 | .. _xarray: https://docs.xarray.dev/en/stable/index.html 57 | .. _dask: https://dask.org/ 58 | 59 | -------------------------------------------------------------------------------- /docs/content/user_guide/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ------------ 3 | 4 | Dependencies 5 | ~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | The following packages are dependencies of ``xeofs``: 8 | 9 | **Core Dependencies (Required)** 10 | 11 | * Python (3.10 or higher) 12 | * `numpy `__ 13 | * `pandas `__ 14 | * `xarray `__ 15 | * `dask `__ 16 | * `scikit-learn `__ 17 | * `typing-extensions `__ 18 | * `tqdm `__ 19 | 20 | **For Specialized Models (Optional)** 21 | 22 | * `numba `__ 23 | * `statsmodels `__ 24 | 25 | **For I/O (Optional)** 26 | 27 | * `h5netcdf `__ 28 | * `netCDF4 `__ 29 | * `zarr `__ 30 | 31 | 32 | Instructions 33 | ~~~~~~~~~~~~ 34 | 35 | The ``xeofs`` package can be installed using either the `conda `__ 36 | package manager 37 | 38 | .. code-block:: bash 39 | 40 | conda install -c conda-forge xeofs 41 | 42 | or the Python package installer `pip `__ 43 | 44 | .. code-block:: bash 45 | 46 | pip install xeofs 47 | 48 | Several optional dependencies are required for certain functionality and are not installed by default: 49 | 50 | * ``zarr``, ``h5netcdf``, or ``netcdf4`` are necessary for saving and loading models to disk 51 | * ``statsmodels`` is required for all models that inherit from ``CPCCA`` including ``CCA``, ``MCA`` and ``RDA`` 52 | * ``numba`` is required for the ``GWPCA`` model 53 | 54 | These extras can be automatically included when installing with pip: 55 | 56 | .. code-block:: bash 57 | 58 | pip install xeofs[complete] 59 | # or using individual groups 60 | pip install xeofs[io,etc] 61 | -------------------------------------------------------------------------------- /docs/content/user_guide/why.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | Why xeofs? 3 | ========== 4 | 5 | Dimensionality reduction and pattern recognition techniques rest on a fundamental idea: data can be represented as a compilation of observations (or samples) of 6 | certain variables (or features). This forms a 2D matrix. Principal Component Analysis (PCA) is a key example of this, known in climate science as Empirical Orthogonal 7 | Functions (EOF) analysis, which often is based on a Singular Value Decomposition (SVD) of a 2D matrix. 8 | 9 | When examining Earth observations, two characteristics of the data stand out: 10 | 11 | 1. **Multi-dimensional Nature:** The data spans multiple dimensions, including spatial coordinates (longitude, latitude, height), time metrics (time, steps, forecast lead times), and other categories (variable types, sensors). 12 | 2. **Large Volume:** These datasets are often enormous, frequently exceeding the memory capacity of a single computer. 13 | 14 | For handling multi-dimensional data in Python, the xarray_ library is exceptional. It manages large datasets efficiently, especially when used with dask_. 15 | 16 | However, applying dimensionality reduction techniques poses a challenge. Typically, this requires preprocessing the data into a 2D matrix format using *xarray*, 17 | which is then fed into packages like `scikit-learn`_ or *dask*. This process often results in the loss of dimension labels, forcing users to manually track 18 | dimensions -- a cumbersome, error-prone, and time-consuming task. While manageable for large projects, it becomes a significant burden for smaller tasks. 19 | 20 | *xeofs* is designed to tackle these challenges. It provides a collection of widely-used dimensionality reduction techniques, focusing on methods prevalent in climate sciences. 21 | It complements robust libraries like *scikit-learn* but stands out due to its seamless compatibility with both *xarray* and *dask*, ensuring a streamlined and efficient user experience. 22 | 23 | .. _xarray: https://docs.xarray.dev/en/stable/index.html 24 | .. _dask: https://dask.org/ 25 | .. _scikit-learn: https://scikit-learn.org/stable/index.html -------------------------------------------------------------------------------- /docs/environment.yml: -------------------------------------------------------------------------------- 1 | name: xeofs 2 | channels: 3 | - conda-forge 4 | - nodefaults # https://github.com/readthedocs/readthedocs.org/issues/9527 5 | dependencies: 6 | - python=3.11 7 | - rpy2 8 | - pandoc 9 | - pip 10 | - pip: 11 | - -e ../.[complete,docs] 12 | -------------------------------------------------------------------------------- /docs/img/example_sst_rotated_pca_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/img/example_sst_rotated_pca_dark.png -------------------------------------------------------------------------------- /docs/img/example_sst_rotated_pca_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/img/example_sst_rotated_pca_light.png -------------------------------------------------------------------------------- /docs/img/timings_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/img/timings_dark.png -------------------------------------------------------------------------------- /docs/img/timings_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/img/timings_light.png -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | 2 | .. image:: logos/xeofs_logo_dark.png 3 | :class: only-dark 4 | :align: center 5 | :width: 1000 6 | :alt: logo of xeofs 7 | 8 | 9 | .. image:: logos/xeofs_logo_light.png 10 | :class: only-light 11 | :align: center 12 | :width: 1000 13 | :alt: logo of xeofs 14 | 15 | 16 | 17 | .. rst-class:: center 18 | 19 | ===================================================== 20 | Extracting Patterns from Climate Data 21 | ===================================================== 22 | 23 | .. toctree:: 24 | :maxdepth: 3 25 | :hidden: 26 | 27 | content/user_guide/index 28 | content/user_guide/installation 29 | content/api_reference/index 30 | content/whats_new/CHANGELOG 31 | content/contributing 32 | 33 | 34 | 35 | xeofs is a specialized Python package designed for dimensionality reduction in climate science, aimed at extracting meaningful patterns from large datasets. It provides eigenmethods such as Principal Component Analysis (EOF analysis) and several related variants. Seamlessly integrated with xarray_ and Dask_, xeofs enables efficient handling and scalable computation of large, multi-dimensional datasets. This integration makes advanced climate data analysis both accessible and efficient. 36 | 37 | .. grid:: 2 38 | 39 | .. grid-item-card:: 40 | :octicon:`repo;10em` 41 | :link: content/user_guide/index 42 | :link-type: doc 43 | :text-align: center 44 | 45 | **User Guide** 46 | 47 | Learn more about the package and its features. 48 | 49 | .. grid-item-card:: 50 | :octicon:`gear;10em` 51 | :link: content/user_guide/installation 52 | :link-type: doc 53 | :text-align: center 54 | 55 | **Installation** 56 | 57 | Get started with xeofs in a few simple steps. 58 | 59 | 60 | .. grid:: 2 61 | 62 | .. grid-item-card:: 63 | :octicon:`search;10em` 64 | :link: content/api_reference/index 65 | :link-type: doc 66 | :text-align: center 67 | 68 | **API Reference** 69 | 70 | Explore the available functions and classes. 71 | 72 | .. grid-item-card:: 73 | :octicon:`people;10em` 74 | :link: content/contributing 75 | :link-type: doc 76 | :text-align: center 77 | 78 | **Contributing Guide** 79 | 80 | Join the community and contribute to the project. 81 | 82 | 83 | .. note:: 84 | 85 | xeofs is currently in the early stages of development. 86 | We welcome contributions and feedback from the community. 87 | 88 | .. note:: 89 | 90 | Version 3 has been released! Check out the :doc:`CHANGELOG` for more details. For users of previous versions, see the :doc:`migration guide`. 91 | 92 | .. _xarray: https://docs.xarray.dev/en/stable/index.html 93 | .. _Dask: https://dask.org/ 94 | 95 | 96 | -------------------------------------------------------------------------------- /docs/logos/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/logos/favicon.ico -------------------------------------------------------------------------------- /docs/logos/xeofs_logo_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/logos/xeofs_logo_dark.png -------------------------------------------------------------------------------- /docs/logos/xeofs_logo_dark_aspect21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/logos/xeofs_logo_dark_aspect21.png -------------------------------------------------------------------------------- /docs/logos/xeofs_logo_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/logos/xeofs_logo_icon.png -------------------------------------------------------------------------------- /docs/logos/xeofs_logo_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/logos/xeofs_logo_light.png -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.https://www.sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/perf/timings.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/perf/timings.nc -------------------------------------------------------------------------------- /docs/perf/timings_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/perf/timings_dark.png -------------------------------------------------------------------------------- /docs/perf/timings_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/docs/perf/timings_light.png -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "xeofs" 3 | version = "3.0.4" 4 | description = "Comprehensive EOF analysis in Python with xarray: A versatile, multidimensional, and scalable tool for advanced climate data analysis" 5 | authors = [ 6 | {name = "Niclas Rieger", email = "niclasrieger@gmail.com"}, 7 | {name = "Samuel J. Levang", email = "slevang@gmail.com"}, 8 | ] 9 | license = {text = "MIT"} 10 | readme = "README.md" 11 | requires-python = ">=3.10" 12 | 13 | dependencies = [ 14 | "numpy>=1.24", 15 | "pandas>=2", 16 | "xarray>=2024.10.0", 17 | "scikit-learn>=1.0.2", 18 | "tqdm>=4.64.0", 19 | "dask>=2023.0.1", 20 | "typing-extensions>=4.8.0", 21 | ] 22 | 23 | [project.optional-dependencies] 24 | complete = ["xeofs[etc,io]"] 25 | dev = [ 26 | "build>=1.0.0", 27 | "ruff>=0.3", 28 | "pytest>=7", 29 | "pytest-xdist>=3", 30 | "coverage>=6", 31 | "pre-commit>=3", 32 | "nbstripout>=0.6", 33 | "pooch>=1.6.0", 34 | ] 35 | docs = [ 36 | "rpy2>=3.5", 37 | "sphinx-gallery>=0.14", 38 | "sphinx-design>=0.6", 39 | "sphinx-copybutton>=0.5", 40 | "nbsphinx>=0.9", 41 | "pydata-sphinx-theme>=0.15", 42 | "sphinx>=8", 43 | "nbconvert>=7.9", 44 | "myst-parser>=3.0", 45 | "matplotlib>=3.4", 46 | "seaborn>=0.11", 47 | "cartopy>=0.22", 48 | "pooch>=1.6.0", 49 | "ipython>=8.14", 50 | "ipykernel>=6.23", 51 | ] 52 | etc = [ 53 | "numba>=0.57", 54 | "statsmodels>=0.14.0", 55 | ] 56 | io = [ 57 | "h5netcdf>=1.0.0", 58 | "netcdf4>=1.5.8", 59 | "zarr>=2.14.0", 60 | ] 61 | 62 | [project.urls] 63 | homepage = "https://github.com/xarray-contrib/xeofs" 64 | repository = "https://github.com/xarray-contrib/xeofs" 65 | documentation = "https://xeofs.readthedocs.io/en/latest/" 66 | 67 | [build-system] 68 | build-backend = "setuptools.build_meta" 69 | requires = [ 70 | "setuptools>=42", 71 | "setuptools-scm>=7", 72 | ] 73 | 74 | [tool.ruff] 75 | target-version = "py311" 76 | 77 | [tool.semantic_release] 78 | version_toml = [ "pyproject.toml:project.version" ] 79 | version_source = "tag" 80 | commit_message = "chore(release): v{version}" 81 | build_command = """ 82 | python -m pip install build~=0.10.0 83 | python -m build . 84 | """ 85 | branch = "main" 86 | 87 | [tool.semantic_release.commit_parser_options] 88 | allowed_tags = [ 89 | "build", 90 | "chore", 91 | "ci", 92 | "docs", 93 | "feat", 94 | "fix", 95 | "perf", 96 | "style", 97 | "refactor", 98 | "test", 99 | ] 100 | minor_tags = ["feat"] 101 | patch_tags = ["fix", "perf"] 102 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/tests/__init__.py -------------------------------------------------------------------------------- /tests/data/sample_data.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/tests/data/sample_data.nc -------------------------------------------------------------------------------- /tests/data_container/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/tests/data_container/__init__.py -------------------------------------------------------------------------------- /tests/linalg/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/tests/linalg/__init__.py -------------------------------------------------------------------------------- /tests/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/tests/models/__init__.py -------------------------------------------------------------------------------- /tests/models/cross/__init__.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | pytest.importorskip("statsmodels") 4 | -------------------------------------------------------------------------------- /tests/models/cross/test_cpcca_complex_rotator.py: -------------------------------------------------------------------------------- 1 | import dask.array as da 2 | import numpy as np 3 | import pytest 4 | import xarray as xr 5 | 6 | from xeofs.cross import HilbertCPCCA, HilbertCPCCARotator 7 | 8 | 9 | def generate_random_data(shape, lazy=False, seed=142): 10 | rng = np.random.default_rng(seed) 11 | if lazy: 12 | return xr.DataArray( 13 | da.random.random(shape, chunks=(5, 5)), # type: ignore 14 | dims=["sample", "feature"], 15 | coords={"sample": np.arange(shape[0]), "feature": np.arange(shape[1])}, 16 | ) 17 | else: 18 | return xr.DataArray( 19 | rng.random(shape), 20 | dims=["sample", "feature"], 21 | coords={"sample": np.arange(shape[0]), "feature": np.arange(shape[1])}, 22 | ) 23 | 24 | 25 | def test_transform_raises(): 26 | X = generate_random_data((200, 10), seed=123) 27 | Y = generate_random_data((200, 20), seed=321) 28 | 29 | cpcca = HilbertCPCCA(n_modes=10, alpha=1, use_pca=False) 30 | cpcca.fit(X, Y, "sample") 31 | 32 | rotator = HilbertCPCCARotator(n_modes=4) 33 | rotator.fit(cpcca) 34 | 35 | with pytest.raises(NotImplementedError): 36 | rotator.transform() 37 | 38 | 39 | @pytest.mark.parametrize( 40 | "alpha,use_pca", 41 | [ 42 | (1.0, False), 43 | (0.5, False), 44 | (0.0, False), 45 | (1.0, True), 46 | (0.5, True), 47 | (0.0, True), 48 | ], 49 | ) 50 | def test_squared_covariance_fraction_conserved(alpha, use_pca): 51 | X = generate_random_data((200, 10), seed=123) 52 | Y = generate_random_data((200, 20), seed=321) 53 | 54 | cpcca = HilbertCPCCA(n_modes=10, alpha=alpha, use_pca=use_pca, n_pca_modes="all") 55 | cpcca.fit(X, Y, "sample") 56 | 57 | n_rot_modes = 5 58 | rotator = HilbertCPCCARotator(n_modes=n_rot_modes, power=1) 59 | rotator.fit(cpcca) 60 | 61 | scf = rotator.squared_covariance_fraction() 62 | scf_rot = rotator.squared_covariance_fraction() 63 | 64 | scf_sum = scf.sel(mode=slice(1, n_rot_modes)).sum() 65 | scf_rot_sum = scf_rot.sel(mode=slice(1, n_rot_modes)).sum() 66 | 67 | xr.testing.assert_allclose(scf_sum, scf_rot_sum) 68 | -------------------------------------------------------------------------------- /tests/models/multi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/tests/models/multi/__init__.py -------------------------------------------------------------------------------- /tests/models/multi/test_cca.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from xeofs.multi import CCA 4 | 5 | 6 | @pytest.mark.parametrize( 7 | "dim", 8 | [ 9 | (("time",)), 10 | (("lat", "lon")), 11 | (("lon", "lat")), 12 | ], 13 | ) 14 | def test_fit(dim, mock_data_array_list): 15 | """Tests the fit method of the CCA class""" 16 | 17 | cca = CCA() 18 | cca.fit(mock_data_array_list, dim) 19 | 20 | # Assert the required attributes have been set 21 | assert hasattr(cca, "preprocessors") 22 | assert hasattr(cca, "data") 23 | 24 | 25 | @pytest.mark.parametrize( 26 | "dim", 27 | [ 28 | (("time",)), 29 | (("lat", "lon")), 30 | (("lon", "lat")), 31 | ], 32 | ) 33 | def test_components(dim, mock_data_array_list): 34 | """Tests the components method of the CCA class""" 35 | 36 | cca = CCA() 37 | cca.fit(mock_data_array_list, dim) 38 | 39 | comps = cca.components() 40 | assert isinstance(comps, list) 41 | 42 | 43 | @pytest.mark.parametrize( 44 | "dim", 45 | [ 46 | (("time",)), 47 | (("lat", "lon")), 48 | (("lon", "lat")), 49 | ], 50 | ) 51 | def test_scores(dim, mock_data_array_list): 52 | """Tests the components method of the CCA class""" 53 | 54 | cca = CCA() 55 | cca.fit(mock_data_array_list, dim) 56 | 57 | scores = cca.scores() 58 | assert isinstance(scores, list) 59 | -------------------------------------------------------------------------------- /tests/models/single/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/tests/models/single/__init__.py -------------------------------------------------------------------------------- /tests/models/single/test_gwpca.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import xeofs as xe 4 | 5 | pytest.importorskip("numba") 6 | 7 | # ============================================================================= 8 | # GENERALLY VALID TEST CASES 9 | # ============================================================================= 10 | N_ARRAYS = [1, 2] 11 | N_SAMPLE_DIMS = [1, 2] 12 | N_FEATURE_DIMS = [1, 2] 13 | INDEX_POLICY = ["index"] 14 | NAN_POLICY = ["no_nan", "fulldim"] 15 | DASK_POLICY = ["no_dask"] 16 | SEED = [0] 17 | 18 | VALID_TEST_DATA = [ 19 | (na, ns, nf, index, nan, dask) 20 | for na in N_ARRAYS 21 | for ns in N_SAMPLE_DIMS 22 | for nf in N_FEATURE_DIMS 23 | for index in INDEX_POLICY 24 | for nan in NAN_POLICY 25 | for dask in DASK_POLICY 26 | ] 27 | 28 | 29 | # TESTS 30 | # ============================================================================= 31 | @pytest.mark.parametrize( 32 | "kernel", 33 | [("bisquare"), ("gaussian"), ("exponential")], 34 | ) 35 | def test_fit(mock_data_array, kernel): 36 | gwpca = xe.single.GWPCA( 37 | n_modes=2, metric="haversine", kernel=kernel, bandwidth=5000 38 | ) 39 | gwpca.fit(mock_data_array, dim=("lat", "lon")) 40 | gwpca.components() 41 | gwpca.largest_locally_weighted_components() 42 | 43 | 44 | @pytest.mark.parametrize( 45 | "metric, kernel, bandwidth", 46 | [ 47 | ("haversine", "invalid_kernel", 5000), 48 | ("invalid_metric", "gaussian", 5000), 49 | ("haversine", "exponential", 0), 50 | ], 51 | ) 52 | def test_fit_invalid(mock_data_array, metric, kernel, bandwidth): 53 | with pytest.raises(ValueError): 54 | xe.single.GWPCA(n_modes=2, metric=metric, kernel=kernel, bandwidth=bandwidth) 55 | -------------------------------------------------------------------------------- /tests/models/test_rotator_factory.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from xeofs.cross import MCA, HilbertMCA, HilbertMCARotator, MCARotator 4 | from xeofs.rotator_factory import RotatorFactory 5 | from xeofs.single import EOF, EOFRotator, HilbertEOF, HilbertEOFRotator 6 | 7 | # RotatorFactory should be imported from its module 8 | # from module import RotatorFactory 9 | 10 | 11 | def test_rotator_factory_init(): 12 | factory = RotatorFactory(n_modes=3, power=2, max_iter=1000, rtol=1e-8) 13 | assert factory.params == {"n_modes": 3, "power": 2, "max_iter": 1000, "rtol": 1e-8} 14 | assert factory._valid_types == (EOF, HilbertEOF, MCA, HilbertMCA) 15 | 16 | 17 | def test_create_rotator_EOF(): 18 | factory = RotatorFactory(n_modes=3, power=2, max_iter=1000, rtol=1e-8) 19 | EOF_instance = EOF() 20 | rotator = factory.create_rotator(EOF_instance) 21 | assert isinstance(rotator, EOFRotator) 22 | 23 | 24 | def test_create_rotator_HilbertEOF(): 25 | factory = RotatorFactory(n_modes=3, power=2, max_iter=1000, rtol=1e-8) 26 | HilbertEOF_instance = HilbertEOF() # creating instance of the mock class 27 | rotator = factory.create_rotator(HilbertEOF_instance) 28 | assert isinstance(rotator, HilbertEOFRotator) 29 | 30 | 31 | def test_create_rotator_MCA(): 32 | pytest.importorskip("statsmodels") 33 | factory = RotatorFactory(n_modes=3, power=2, max_iter=1000, rtol=1e-8) 34 | MCA_instance = MCA() 35 | rotator = factory.create_rotator(MCA_instance) 36 | assert isinstance(rotator, MCARotator) 37 | 38 | 39 | def test_create_rotator_HilbertMCA(): 40 | pytest.importorskip("statsmodels") 41 | factory = RotatorFactory(n_modes=3, power=2, max_iter=1000, rtol=1e-8) 42 | HilbertMCA_instance = HilbertMCA() 43 | rotator = factory.create_rotator(HilbertMCA_instance) 44 | assert isinstance(rotator, HilbertMCARotator) 45 | 46 | 47 | def test_create_rotator_invalid_model(): 48 | factory = RotatorFactory(n_modes=3, power=2, max_iter=1000, rtol=1e-8) 49 | with pytest.raises(TypeError): 50 | factory.create_rotator("InvalidType") # type: ignore 51 | -------------------------------------------------------------------------------- /tests/preprocessing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/tests/preprocessing/__init__.py -------------------------------------------------------------------------------- /tests/preprocessing/test_multiindex_converter_dataarray.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from xeofs.preprocessing.multi_index_converter import ( 4 | MultiIndexConverter, 5 | ) 6 | from ..utilities import data_is_dask, data_has_multiindex 7 | 8 | # ============================================================================= 9 | # GENERALLY VALID TEST CASES 10 | # ============================================================================= 11 | N_SAMPLE_DIMS = [1, 2] 12 | N_FEATURE_DIMS = [1, 2] 13 | INDEX_POLICY = ["index", "multiindex"] 14 | NAN_POLICY = ["no_nan"] 15 | DASK_POLICY = ["no_dask", "dask"] 16 | SEED = [0] 17 | 18 | VALID_TEST_DATA = [ 19 | (ns, nf, index, nan, dask) 20 | for ns in N_SAMPLE_DIMS 21 | for nf in N_FEATURE_DIMS 22 | for index in INDEX_POLICY 23 | for nan in NAN_POLICY 24 | for dask in DASK_POLICY 25 | ] 26 | 27 | 28 | @pytest.mark.parametrize( 29 | "synthetic_dataarray", 30 | VALID_TEST_DATA, 31 | indirect=["synthetic_dataarray"], 32 | ) 33 | def test_transform(synthetic_dataarray): 34 | converter = MultiIndexConverter() 35 | converter.fit(synthetic_dataarray) 36 | transformed_data = converter.transform(synthetic_dataarray) 37 | 38 | is_dask_before = data_is_dask(synthetic_dataarray) 39 | is_dask_after = data_is_dask(transformed_data) 40 | 41 | # Transforming doesn't change the dask-ness of the data 42 | assert is_dask_before == is_dask_after 43 | 44 | # Transforming removes MultiIndex 45 | assert data_has_multiindex(transformed_data) is False 46 | 47 | # Result is robust to calling the method multiple times 48 | transformed_data = converter.transform(synthetic_dataarray) 49 | assert data_has_multiindex(transformed_data) is False 50 | 51 | # Transforming data twice won't change the data 52 | transformed_data2 = converter.transform(transformed_data) 53 | assert data_has_multiindex(transformed_data2) is False 54 | assert transformed_data.identical(transformed_data2) 55 | 56 | 57 | @pytest.mark.parametrize( 58 | "synthetic_dataarray", 59 | VALID_TEST_DATA, 60 | indirect=["synthetic_dataarray"], 61 | ) 62 | def test_inverse_transform_data(synthetic_dataarray): 63 | converter = MultiIndexConverter() 64 | converter.fit(synthetic_dataarray) 65 | transformed_data = converter.transform(synthetic_dataarray) 66 | inverse_transformed_data = converter.inverse_transform_data(transformed_data) 67 | 68 | is_dask_before = data_is_dask(synthetic_dataarray) 69 | is_dask_after = data_is_dask(transformed_data) 70 | 71 | # Transforming doesn't change the dask-ness of the data 72 | assert is_dask_before == is_dask_after 73 | 74 | has_multiindex_before = data_has_multiindex(synthetic_dataarray) 75 | has_multiindex_after = data_has_multiindex(inverse_transformed_data) 76 | 77 | assert inverse_transformed_data.identical(synthetic_dataarray) 78 | assert has_multiindex_before == has_multiindex_after 79 | -------------------------------------------------------------------------------- /tests/preprocessing/test_multiindex_converter_dataset.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from xeofs.preprocessing.multi_index_converter import ( 4 | MultiIndexConverter, 5 | ) 6 | from ..utilities import assert_expected_dims, data_is_dask, data_has_multiindex 7 | 8 | # ============================================================================= 9 | # GENERALLY VALID TEST CASES 10 | # ============================================================================= 11 | N_VARIABLES = [1, 2] 12 | N_SAMPLE_DIMS = [1, 2] 13 | N_FEATURE_DIMS = [1, 2] 14 | INDEX_POLICY = ["index"] 15 | NAN_POLICY = ["no_nan"] 16 | DASK_POLICY = ["no_dask", "dask"] 17 | SEED = [0] 18 | 19 | VALID_TEST_DATA = [ 20 | (nv, ns, nf, index, nan, dask) 21 | for nv in N_VARIABLES 22 | for ns in N_SAMPLE_DIMS 23 | for nf in N_FEATURE_DIMS 24 | for index in INDEX_POLICY 25 | for nan in NAN_POLICY 26 | for dask in DASK_POLICY 27 | ] 28 | 29 | 30 | # TESTS 31 | # ============================================================================= 32 | @pytest.mark.parametrize( 33 | "synthetic_dataset", 34 | VALID_TEST_DATA, 35 | indirect=["synthetic_dataset"], 36 | ) 37 | def test_transform(synthetic_dataset): 38 | converter = MultiIndexConverter() 39 | converter.fit(synthetic_dataset) 40 | transformed_data = converter.transform(synthetic_dataset) 41 | 42 | is_dask_before = data_is_dask(synthetic_dataset) 43 | is_dask_after = data_is_dask(transformed_data) 44 | 45 | # Transforming does not affect dimensions 46 | assert_expected_dims(transformed_data, synthetic_dataset, policy="all") 47 | 48 | # Transforming doesn't change the dask-ness of the data 49 | assert is_dask_before == is_dask_after 50 | 51 | # Transforming removes MultiIndex 52 | assert data_has_multiindex(transformed_data) is False 53 | 54 | # Result is robust to calling the method multiple times 55 | transformed_data = converter.transform(synthetic_dataset) 56 | assert data_has_multiindex(transformed_data) is False 57 | 58 | # Transforming data twice won't change the data 59 | transformed_data2 = converter.transform(transformed_data) 60 | assert data_has_multiindex(transformed_data2) is False 61 | assert transformed_data.identical(transformed_data2) 62 | 63 | 64 | @pytest.mark.parametrize( 65 | "synthetic_dataset", 66 | VALID_TEST_DATA, 67 | indirect=["synthetic_dataset"], 68 | ) 69 | def test_inverse_transform(synthetic_dataset): 70 | converter = MultiIndexConverter() 71 | converter.fit(synthetic_dataset) 72 | transformed_data = converter.transform(synthetic_dataset) 73 | inverse_transformed_data = converter.inverse_transform_data(transformed_data) 74 | 75 | is_dask_before = data_is_dask(synthetic_dataset) 76 | is_dask_after = data_is_dask(transformed_data) 77 | 78 | # Transforming doesn't change the dask-ness of the data 79 | assert is_dask_before == is_dask_after 80 | 81 | has_multiindex_before = data_has_multiindex(synthetic_dataset) 82 | has_multiindex_after = data_has_multiindex(inverse_transformed_data) 83 | 84 | assert inverse_transformed_data.identical(synthetic_dataset) 85 | assert has_multiindex_before == has_multiindex_after 86 | -------------------------------------------------------------------------------- /tests/preprocessing/test_renamer_dataarray.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from xeofs.preprocessing.dimension_renamer import DimensionRenamer 4 | from ..utilities import ( 5 | data_is_dask, 6 | get_dims_from_data, 7 | ) 8 | 9 | # ============================================================================= 10 | # GENERALLY VALID TEST CASES 11 | # ============================================================================= 12 | N_SAMPLE_DIMS = [1, 2] 13 | N_FEATURE_DIMS = [1, 2] 14 | INDEX_POLICY = ["index", "multiindex"] 15 | NAN_POLICY = ["no_nan"] 16 | DASK_POLICY = ["no_dask", "dask"] 17 | SEED = [0] 18 | 19 | VALID_TEST_DATA = [ 20 | (ns, nf, index, nan, dask) 21 | for ns in N_SAMPLE_DIMS 22 | for nf in N_FEATURE_DIMS 23 | for index in INDEX_POLICY 24 | for nan in NAN_POLICY 25 | for dask in DASK_POLICY 26 | ] 27 | 28 | 29 | @pytest.mark.parametrize( 30 | "synthetic_dataarray", 31 | VALID_TEST_DATA, 32 | indirect=["synthetic_dataarray"], 33 | ) 34 | def test_transform(synthetic_dataarray): 35 | all_dims, sample_dims, feature_dims = get_dims_from_data(synthetic_dataarray) 36 | 37 | n_dims = len(all_dims) 38 | 39 | base = "new" 40 | start = 10 41 | expected_dims = set(base + str(i) for i in range(start, start + n_dims)) 42 | 43 | renamer = DimensionRenamer(base=base, start=start) 44 | renamer.fit(synthetic_dataarray, sample_dims, feature_dims) 45 | transformed_data = renamer.transform(synthetic_dataarray) 46 | 47 | is_dask_before = data_is_dask(synthetic_dataarray) 48 | is_dask_after = data_is_dask(transformed_data) 49 | 50 | # Transforming doesn't change the dask-ness of the data 51 | assert is_dask_before == is_dask_after 52 | 53 | # Transforming converts dimension names 54 | given_dims = set(transformed_data.dims) 55 | assert given_dims == expected_dims 56 | 57 | # Result is robust to calling the method multiple times 58 | transformed_data = renamer.transform(synthetic_dataarray) 59 | given_dims = set(transformed_data.dims) 60 | assert given_dims == expected_dims 61 | 62 | 63 | @pytest.mark.parametrize( 64 | "synthetic_dataarray", 65 | VALID_TEST_DATA, 66 | indirect=["synthetic_dataarray"], 67 | ) 68 | def test_inverse_transform_data(synthetic_dataarray): 69 | all_dims, sample_dims, feature_dims = get_dims_from_data(synthetic_dataarray) 70 | 71 | base = "new" 72 | start = 10 73 | 74 | renamer = DimensionRenamer(base=base, start=start) 75 | renamer.fit(synthetic_dataarray, sample_dims, feature_dims) 76 | transformed_data = renamer.transform(synthetic_dataarray) 77 | inverse_transformed_data = renamer.inverse_transform_data(transformed_data) 78 | 79 | is_dask_before = data_is_dask(synthetic_dataarray) 80 | is_dask_after = data_is_dask(transformed_data) 81 | 82 | # Transforming doesn't change the dask-ness of the data 83 | assert is_dask_before == is_dask_after 84 | 85 | assert inverse_transformed_data.identical(synthetic_dataarray) 86 | assert set(inverse_transformed_data.dims) == set(synthetic_dataarray.dims) 87 | -------------------------------------------------------------------------------- /tests/preprocessing/test_renamer_dataset.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from xeofs.preprocessing.dimension_renamer import DimensionRenamer 4 | from ..utilities import ( 5 | data_is_dask, 6 | get_dims_from_data, 7 | ) 8 | 9 | # ============================================================================= 10 | # GENERALLY VALID TEST CASES 11 | # ============================================================================= 12 | N_VARIABLES = [1, 2] 13 | N_SAMPLE_DIMS = [1, 2] 14 | N_FEATURE_DIMS = [1, 2] 15 | INDEX_POLICY = ["index", "multiindex"] 16 | NAN_POLICY = ["no_nan", "fulldim"] 17 | DASK_POLICY = ["no_dask", "dask"] 18 | SEED = [0] 19 | 20 | VALID_TEST_DATA = [ 21 | (nv, ns, nf, index, nan, dask) 22 | for nv in N_VARIABLES 23 | for ns in N_SAMPLE_DIMS 24 | for nf in N_FEATURE_DIMS 25 | for index in INDEX_POLICY 26 | for nan in NAN_POLICY 27 | for dask in DASK_POLICY 28 | ] 29 | 30 | 31 | # TESTS 32 | # ============================================================================= 33 | @pytest.mark.parametrize( 34 | "synthetic_dataset", 35 | VALID_TEST_DATA, 36 | indirect=["synthetic_dataset"], 37 | ) 38 | def test_transform(synthetic_dataset): 39 | all_dims, sample_dims, feature_dims = get_dims_from_data(synthetic_dataset) 40 | 41 | n_dims = len(all_dims) 42 | 43 | base = "new" 44 | start = 10 45 | expected_dims = set(base + str(i) for i in range(start, start + n_dims)) 46 | 47 | renamer = DimensionRenamer(base=base, start=start) 48 | renamer.fit(synthetic_dataset, sample_dims, feature_dims) 49 | transformed_data = renamer.transform(synthetic_dataset) 50 | 51 | is_dask_before = data_is_dask(synthetic_dataset) 52 | is_dask_after = data_is_dask(transformed_data) 53 | 54 | # Transforming doesn't change the dask-ness of the data 55 | assert is_dask_before == is_dask_after 56 | 57 | # Transforming converts dimension names 58 | given_dims = set(transformed_data.dims) 59 | assert given_dims == expected_dims 60 | 61 | # Result is robust to calling the method multiple times 62 | transformed_data = renamer.transform(synthetic_dataset) 63 | given_dims = set(transformed_data.dims) 64 | assert given_dims == expected_dims 65 | 66 | 67 | @pytest.mark.parametrize( 68 | "synthetic_dataset", 69 | VALID_TEST_DATA, 70 | indirect=["synthetic_dataset"], 71 | ) 72 | def test_inverse_transform_data(synthetic_dataset): 73 | all_dims, sample_dims, feature_dims = get_dims_from_data(synthetic_dataset) 74 | 75 | base = "new" 76 | start = 10 77 | 78 | renamer = DimensionRenamer(base=base, start=start) 79 | renamer.fit(synthetic_dataset, sample_dims, feature_dims) 80 | transformed_data = renamer.transform(synthetic_dataset) 81 | inverse_transformed_data = renamer.inverse_transform_data(transformed_data) 82 | 83 | is_dask_before = data_is_dask(synthetic_dataset) 84 | is_dask_after = data_is_dask(transformed_data) 85 | 86 | # Transforming doesn't change the dask-ness of the data 87 | assert is_dask_before == is_dask_after 88 | 89 | assert inverse_transformed_data.identical(synthetic_dataset) 90 | assert set(inverse_transformed_data.dims) == set(synthetic_dataset.dims) 91 | -------------------------------------------------------------------------------- /tests/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/tests/utils/__init__.py -------------------------------------------------------------------------------- /tests/utils/test_dimension_renamer.py: -------------------------------------------------------------------------------- 1 | import xarray as xr 2 | import pytest 3 | 4 | from xeofs.utils.dimension_renamer import DimensionRenamer 5 | 6 | import pandas as pd 7 | 8 | 9 | @pytest.fixture 10 | def da_simple(): 11 | # Basic DataArray with single dimension 12 | return xr.DataArray([1, 2, 3], coords={"dim0": ["a", "b", "c"]}, dims=["dim0"]) 13 | 14 | 15 | @pytest.fixture 16 | def da_multi(): 17 | # DataArray with MultiIndex 18 | arrays = [ 19 | pd.Index(["a", "b", "c"], name="dim1a"), 20 | pd.Index([1, 2, 3], name="dim1b"), 21 | ] 22 | multi_index = pd.MultiIndex.from_arrays(arrays) 23 | return xr.DataArray([1, 2, 3], coords={"dim1": multi_index}, dims=["dim1"]) 24 | 25 | 26 | def test_simple_dim_rename(da_simple): 27 | renamer = DimensionRenamer("dim0", "_suffix") 28 | da_new = renamer.fit_transform(da_simple) 29 | assert "dim0_suffix" in da_new.dims 30 | 31 | # Inverse transform 32 | da_orig = renamer.inverse_transform(da_new) 33 | assert "dim0" in da_orig.dims 34 | 35 | 36 | def test_multiindex_dim_rename(da_multi): 37 | renamer = DimensionRenamer("dim1", "_suffix") 38 | da_new = renamer.fit_transform(da_multi) 39 | assert "dim1_suffix" in da_new.dims 40 | assert "dim1a_suffix" in da_new.coords["dim1_suffix"].coords.keys() 41 | assert "dim1b_suffix" in da_new.coords["dim1_suffix"].coords.keys() 42 | 43 | # Inverse transform 44 | da_orig = renamer.inverse_transform(da_new) 45 | assert "dim1" in da_orig.dims 46 | assert "dim1a" in da_orig.coords["dim1"].coords.keys() 47 | assert "dim1b" in da_orig.coords["dim1"].coords.keys() 48 | 49 | 50 | def test_fit_without_transform(da_simple): 51 | renamer = DimensionRenamer("dim0", "_suffix") 52 | renamer.fit(da_simple) 53 | assert hasattr(renamer, "dims_mapping") 54 | assert renamer.dims_mapping == {"dim0": "dim0_suffix"} 55 | 56 | 57 | def test_incorrect_dim_name(da_simple): 58 | with pytest.raises(KeyError): 59 | renamer = DimensionRenamer("nonexistent_dim", "_suffix") 60 | renamer.fit_transform(da_simple) 61 | 62 | 63 | def test_empty_suffix(da_simple): 64 | renamer = DimensionRenamer("dim0", "") 65 | da_new = renamer.fit_transform(da_simple) 66 | assert "dim0" in da_new.dims 67 | -------------------------------------------------------------------------------- /tests/utils/test_total_variance.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import xarray as xr 3 | 4 | from xeofs.utils.xarray_utils import total_variance 5 | 6 | 7 | def test_total_variance(mock_data_array): 8 | """Test the total_variance function.""" 9 | arr = mock_data_array.copy().values 10 | # Compute total variance using numpy by hand 11 | tot_var_ref = np.sum(np.var(arr, axis=0, ddof=1)) 12 | 13 | da = mock_data_array.stack(sample=("time",), feature=("lat", "lon")) 14 | da -= da.mean(dim="sample") 15 | tot_var = total_variance(da, dim="sample") 16 | assert isinstance(tot_var, xr.DataArray) 17 | assert tot_var.dims == () 18 | assert tot_var.shape == () 19 | assert tot_var.dtype == np.float64 20 | assert np.allclose( 21 | tot_var, tot_var_ref 22 | ), "Total variance computed by hand does not match." 23 | -------------------------------------------------------------------------------- /tests/validation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/tests/validation/__init__.py -------------------------------------------------------------------------------- /xeofs/__init__.py: -------------------------------------------------------------------------------- 1 | from xeofs import cross, multi, single, validation 2 | from xeofs._version import __version__ 3 | from xeofs.rotator_factory import RotatorFactory 4 | 5 | __all__ = ["single", "cross", "multi", "RotatorFactory", "validation", "__version__"] 6 | -------------------------------------------------------------------------------- /xeofs/_version.py: -------------------------------------------------------------------------------- 1 | from importlib.metadata import version 2 | 3 | __version__ = version("xeofs") 4 | -------------------------------------------------------------------------------- /xeofs/cross/__init__.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | 3 | from .cca import CCA, ComplexCCA, HilbertCCA 4 | from .cpcca import CPCCA, ComplexCPCCA, HilbertCPCCA 5 | from .cpcca_rotator import ComplexCPCCARotator, CPCCARotator, HilbertCPCCARotator 6 | from .mca import MCA, ComplexMCA, HilbertMCA 7 | from .mca_rotator import ComplexMCARotator, HilbertMCARotator, MCARotator 8 | from .rda import RDA, ComplexRDA, HilbertRDA 9 | 10 | __all__ = [ 11 | "CCA", 12 | "MCA", 13 | "RDA", 14 | "CPCCA", 15 | "ComplexCCA", 16 | "ComplexMCA", 17 | "ComplexRDA", 18 | "ComplexCPCCA", 19 | "HilbertCCA", 20 | "HilbertMCA", 21 | "HilbertRDA", 22 | "HilbertCPCCA", 23 | "MCARotator", 24 | "CPCCARotator", 25 | "ComplexMCARotator", 26 | "ComplexCPCCARotator", 27 | "HilbertMCARotator", 28 | "HilbertCPCCARotator", 29 | ] 30 | 31 | 32 | DEPRECATED_NAMES = [ 33 | # ("OldClass", "NewClass"), 34 | ] 35 | 36 | 37 | def __dir__(): 38 | return sorted(__all__ + [names[0] for names in DEPRECATED_NAMES]) 39 | 40 | 41 | def __getattr__(name): 42 | for old_name, new_name in DEPRECATED_NAMES: 43 | if name == old_name: 44 | msg = ( 45 | f"Class '{old_name}' is deprecated and will be renamed to '{new_name}' in the next major release. " 46 | f"In that release, '{old_name}' will refer to a different class. " 47 | f"Please switch to '{new_name}' to maintain compatibility." 48 | ) 49 | warnings.warn(msg, DeprecationWarning, stacklevel=2) 50 | return globals()[new_name] 51 | raise AttributeError(f"module {__name__} has no attribute {name}") 52 | -------------------------------------------------------------------------------- /xeofs/data_container/__init__.py: -------------------------------------------------------------------------------- 1 | from .data_container import DataContainer 2 | 3 | __all__ = ["DataContainer"] 4 | -------------------------------------------------------------------------------- /xeofs/data_container/data_container.py: -------------------------------------------------------------------------------- 1 | import dask 2 | import xarray as xr 3 | from typing_extensions import Self 4 | 5 | from ..utils.data_types import DataArray 6 | 7 | 8 | class DataContainer(dict): 9 | def __init__(self, *args, **kwargs): 10 | super().__init__(*args, **kwargs) 11 | self._allow_compute = dict({k: True for k in self.keys()}) 12 | 13 | def add(self, data: DataArray, name: str, allow_compute: bool = True) -> None: 14 | data.name = name 15 | super().__setitem__(name, data) 16 | self._allow_compute[name] = True if allow_compute else False 17 | 18 | def __setitem__(self, __key: str, __value: DataArray) -> None: 19 | super().__setitem__(__key, __value) 20 | self._allow_compute[__key] = self._allow_compute.get(__key, True) 21 | 22 | def __getitem__(self, __key: str) -> DataArray: 23 | try: 24 | return super().__getitem__(__key) 25 | except KeyError: 26 | raise KeyError( 27 | f"Cannot find data '{__key}'. Please fit the model first by calling .fit()." 28 | ) 29 | 30 | def serialize(self) -> xr.DataTree: 31 | dt = xr.DataTree(name="data") 32 | for key, data in self.items(): 33 | if not data.name: 34 | data.name = key 35 | dt[key] = xr.DataTree(data.to_dataset()) 36 | dt[key].attrs = {key: "_is_node", "allow_compute": self._allow_compute[key]} 37 | 38 | return dt 39 | 40 | @classmethod 41 | def deserialize(cls, dt: xr.DataTree) -> Self: 42 | container = cls() 43 | for key, node in dt.items(): 44 | container[key] = node[key] 45 | container._allow_compute[key] = node.attrs["allow_compute"] 46 | return container 47 | 48 | def compute(self, **kwargs): 49 | computed_data = {k: v for k, v in self.items() if self._allow_compute[k]} 50 | (computed_data,) = dask.compute(computed_data, **kwargs) 51 | for k, v in computed_data.items(): 52 | self[k] = v 53 | 54 | def _validate_attrs_values(self, value): 55 | """Convert any boolean and None values to strings""" 56 | if isinstance(value, bool): 57 | return str(value) 58 | elif value is None: 59 | return "None" 60 | else: 61 | return value 62 | 63 | def _validate_attrs(self, attrs: dict) -> dict: 64 | """Convert any boolean and None values to strings""" 65 | for key, value in attrs.items(): 66 | if isinstance(value, bool): 67 | attrs[key] = str(value) 68 | elif value is None: 69 | attrs[key] = "None" 70 | elif isinstance(value, list): 71 | attrs[key] = [self._validate_attrs_values(v) for v in value] 72 | 73 | return attrs 74 | 75 | def set_attrs(self, attrs: dict): 76 | attrs = self._validate_attrs(attrs) 77 | for key in self.keys(): 78 | self[key].attrs = attrs 79 | -------------------------------------------------------------------------------- /xeofs/linalg/__init__.py: -------------------------------------------------------------------------------- 1 | from .utils import total_variance 2 | 3 | __all__ = ["total_variance"] 4 | -------------------------------------------------------------------------------- /xeofs/linalg/_numpy/__init__.py: -------------------------------------------------------------------------------- 1 | from ._rotation import _promax 2 | from ._svd import _SVD 3 | from ._utils import _fractional_matrix_power 4 | 5 | __all__ = ["_fractional_matrix_power", "_promax", "_SVD"] 6 | -------------------------------------------------------------------------------- /xeofs/linalg/_numpy/_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from ._svd import _SVD 4 | 5 | 6 | def _fractional_matrix_power(C, power, **kwargs): 7 | """Compute the fractional matrix power of a symmetric matrix using SVD. 8 | 9 | Note: This function is a simplified version of the fractional_matrix_power 10 | function from the scipy library. However, the scipy function does not 11 | support dask arrays due to the use of np.asarray. 12 | """ 13 | if C.shape[0] != C.shape[1]: 14 | raise ValueError("Matrix must be square.") 15 | 16 | svd = _SVD(n_modes="all", **kwargs) 17 | _, s, V = svd.fit_transform(C) 18 | 19 | # cut off small singular values 20 | is_above_zero = s > np.finfo(s.dtype).eps 21 | V = V[:, is_above_zero] 22 | s = s[is_above_zero] 23 | 24 | # TODO: use hermitian=True for numpy>=2.0 25 | # V, s, _ = np.linalg.svd(C, hermitian=True) 26 | C_scaled = V @ np.diag(s**power) @ V.conj().T 27 | 28 | # Even if the input matrix is real and symmetric, the output matrix might 29 | # be complex due to numerical errors. In this case, we return the real part 30 | if np.iscomplexobj(C): 31 | return C_scaled 32 | else: 33 | return C_scaled.real 34 | -------------------------------------------------------------------------------- /xeofs/linalg/rotation.py: -------------------------------------------------------------------------------- 1 | import xarray as xr 2 | 3 | from ..utils.data_types import DataArray 4 | from ._numpy._rotation import _promax 5 | 6 | 7 | def promax(loadings: DataArray, feature_dim, **kwargs): 8 | rotated, rot_mat, phi_mat = xr.apply_ufunc( 9 | _promax, 10 | loadings, 11 | input_core_dims=[[feature_dim, "mode"]], 12 | output_core_dims=[ 13 | [feature_dim, "mode"], 14 | ["mode_m", "mode_n"], 15 | ["mode_m", "mode_n"], 16 | ], 17 | kwargs=kwargs, 18 | dask="allowed", 19 | ) 20 | 21 | return rotated, rot_mat, phi_mat 22 | -------------------------------------------------------------------------------- /xeofs/linalg/svd.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import xarray as xr 3 | from dask.base import compute as dask_compute 4 | 5 | from ..utils.data_types import DataArray 6 | from ._numpy import _SVD 7 | 8 | 9 | class SVD: 10 | def __init__( 11 | self, 12 | n_modes: int | float | str, 13 | is_complex: bool | str = "auto", 14 | init_rank_reduction: float = 0.3, 15 | flip_signs: bool = True, 16 | solver: str = "auto", 17 | compute: bool = True, 18 | random_state: np.random.Generator | int | None = None, 19 | solver_kwargs: dict = {}, 20 | sample_name: str = "sample", 21 | feature_name: str = "feature", 22 | ): 23 | self.n_modes = n_modes 24 | self.is_complex = is_complex 25 | self.init_rank_reduction = init_rank_reduction 26 | self.flip_signs = flip_signs 27 | self.solver = solver 28 | self.random_state = random_state 29 | self.solver_kwargs = solver_kwargs 30 | self.compute_svd = compute 31 | 32 | self.sample_name = sample_name 33 | self.feature_name = feature_name 34 | 35 | def fit_transform(self, X: DataArray) -> tuple[DataArray, DataArray, DataArray]: 36 | """Decomposes the data object. 37 | 38 | Parameters 39 | ---------- 40 | X : DataArray 41 | A 2-dimensional data object to be decomposed. 42 | 43 | Returns 44 | ------- 45 | U : DataArray 46 | The left singular vectors of the decomposition. 47 | s : DataArray 48 | The singular values of the decomposition. 49 | V : DataArray 50 | The right singular vectors of the decomposition. 51 | 52 | """ 53 | svd = _SVD( 54 | n_modes=self.n_modes, 55 | init_rank_reduction=self.init_rank_reduction, 56 | flip_signs=self.flip_signs, 57 | solver=self.solver, 58 | random_state=self.random_state, 59 | is_complex=self.is_complex, 60 | **self.solver_kwargs, 61 | ) 62 | U, s, V = xr.apply_ufunc( 63 | svd.fit_transform, 64 | X, 65 | input_core_dims=[[self.sample_name, self.feature_name]], 66 | output_core_dims=[ 67 | [self.sample_name, "mode"], 68 | ["mode"], 69 | [self.feature_name, "mode"], 70 | ], 71 | dask="allowed", 72 | ) 73 | mode_coords = np.arange(1, U.mode.size + 1) 74 | s = s.assign_coords(mode=mode_coords) 75 | U = U.assign_coords(mode=mode_coords) 76 | V = V.assign_coords(mode=mode_coords) 77 | 78 | if self.compute_svd: 79 | U, s, V = dask_compute(U, s, V) 80 | 81 | return U, s, V 82 | -------------------------------------------------------------------------------- /xeofs/linalg/utils.py: -------------------------------------------------------------------------------- 1 | from ..utils.data_types import DataArray 2 | 3 | 4 | def total_variance(X: DataArray, dim: str) -> DataArray: 5 | """Compute the total variance of the centered data.""" 6 | return (X * X.conj()).sum() / (X[dim].size - 1) 7 | -------------------------------------------------------------------------------- /xeofs/multi/__init__.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | 3 | from .cca import CCA 4 | 5 | __all__ = ["CCA"] 6 | 7 | 8 | DEPRECATED_NAMES = [ 9 | # ("OldClass", "NewClass"), 10 | ] 11 | 12 | 13 | def __dir__(): 14 | return sorted(__all__ + [names[0] for names in DEPRECATED_NAMES]) 15 | 16 | 17 | def __getattr__(name): 18 | for old_name, new_name in DEPRECATED_NAMES: 19 | if name == old_name: 20 | msg = ( 21 | f"Class '{old_name}' is deprecated and will be renamed to '{new_name}' in the next major release. " 22 | f"In that release, '{old_name}' will refer to a different class. " 23 | f"Please switch to '{new_name}' to maintain compatibility." 24 | ) 25 | warnings.warn(msg, DeprecationWarning, stacklevel=2) 26 | return globals()[new_name] 27 | raise AttributeError(f"module {__name__} has no attribute {name}") 28 | -------------------------------------------------------------------------------- /xeofs/preprocessing/__init__.py: -------------------------------------------------------------------------------- 1 | from .concatenator import Concatenator 2 | from .dimension_renamer import DimensionRenamer 3 | from .multi_index_converter import MultiIndexConverter 4 | from .pca import PCA 5 | from .preprocessor import Preprocessor 6 | from .sanitizer import Sanitizer 7 | from .scaler import Scaler 8 | from .stacker import Stacker 9 | from .whitener import Whitener 10 | 11 | __all__ = [ 12 | "Concatenator", 13 | "DimensionRenamer", 14 | "MultiIndexConverter", 15 | "Preprocessor", 16 | "Sanitizer", 17 | "Scaler", 18 | "Stacker", 19 | "Whitener", 20 | "PCA", 21 | ] 22 | -------------------------------------------------------------------------------- /xeofs/preprocessing/dimension_renamer.py: -------------------------------------------------------------------------------- 1 | from typing_extensions import Self 2 | 3 | from ..utils.data_types import Data, DataArray, DataVarBound, Dims 4 | from .transformer import Transformer 5 | 6 | 7 | class DimensionRenamer(Transformer): 8 | """Rename dimensions of a DataArray or Dataset. 9 | 10 | Parameters 11 | ---------- 12 | base: str 13 | Base string for the new dimension names. 14 | start: int 15 | Start index for the new dimension names. 16 | 17 | """ 18 | 19 | def __init__(self, base="dim", start=0): 20 | super().__init__() 21 | self.base = base 22 | self.start = start 23 | self.dim_mapping = {} 24 | 25 | def get_serialization_attrs(self) -> dict: 26 | return dict( 27 | dim_mapping=self.dim_mapping, 28 | ) 29 | 30 | def fit(self, X: Data, sample_dims: Dims, feature_dims: Dims, **kwargs) -> Self: 31 | self.sample_dims_before = sample_dims 32 | self.feature_dims_before = feature_dims 33 | 34 | self.dim_mapping = { 35 | dim: f"{self.base}{i}" for i, dim in enumerate(X.dims, start=self.start) 36 | } 37 | 38 | self.sample_dims_after: Dims = tuple( 39 | [self.dim_mapping[dim] for dim in self.sample_dims_before] 40 | ) 41 | self.feature_dims_after: Dims = tuple( 42 | [self.dim_mapping[dim] for dim in self.feature_dims_before] 43 | ) 44 | 45 | return self 46 | 47 | def transform(self, X: DataVarBound) -> DataVarBound: 48 | try: 49 | return X.rename(self.dim_mapping) 50 | except ValueError: 51 | raise ValueError("Cannot transform data. Dimensions are different.") 52 | 53 | def _inverse_transform(self, X: DataVarBound) -> DataVarBound: 54 | given_dims = set(X.dims) 55 | expected_dims = set(self.dim_mapping.values()) 56 | dims = given_dims.intersection(expected_dims) 57 | return X.rename({v: k for k, v in self.dim_mapping.items() if v in dims}) 58 | 59 | def inverse_transform_data(self, X: DataVarBound) -> DataVarBound: 60 | return self._inverse_transform(X) 61 | 62 | def inverse_transform_components(self, X: DataVarBound) -> DataVarBound: 63 | return self._inverse_transform(X) 64 | 65 | def inverse_transform_scores(self, X: DataArray) -> DataArray: 66 | return self._inverse_transform(X) 67 | 68 | def inverse_transform_scores_unseen(self, X: DataArray) -> DataArray: 69 | return self._inverse_transform(X) 70 | -------------------------------------------------------------------------------- /xeofs/rotator_factory.py: -------------------------------------------------------------------------------- 1 | from .cross import MCA, HilbertMCA, HilbertMCARotator, MCARotator 2 | from .single import EOF, EOFRotator, HilbertEOF, HilbertEOFRotator 3 | 4 | 5 | class RotatorFactory: 6 | """Factory class for creating rotators. 7 | 8 | Parameters 9 | ---------- 10 | n_rot : int 11 | Number of modes to be rotated. 12 | power : int 13 | Defines the power of Promax rotation. Choosing ``power=1`` equals 14 | a Varimax solution (the default is 1). 15 | max_iter : int 16 | Number of maximal iterations for obtaining the rotation matrix 17 | (the default is 1000). 18 | rtol : float 19 | Relative tolerance to be achieved for early stopping the iteration 20 | process (the default is 1e-8). 21 | 22 | """ 23 | 24 | def __init__(self, **kwargs): 25 | self.params = kwargs 26 | self._valid_types = (EOF, HilbertEOF, MCA, HilbertMCA) 27 | 28 | def create_rotator( 29 | self, model: EOF | HilbertEOF | MCA | HilbertMCA 30 | ) -> EOFRotator | HilbertEOFRotator | MCARotator | HilbertMCARotator: 31 | """Create a rotator for the given model. 32 | 33 | Parameters 34 | ---------- 35 | model : xeofs model 36 | Model to be rotated. 37 | 38 | Returns 39 | ------- 40 | xeofs Rotator 41 | Rotator for the given model. 42 | """ 43 | # We need to check the type of the model instead of isinstance because 44 | # of inheritance. 45 | if type(model) is EOF: 46 | return EOFRotator(**self.params) 47 | elif type(model) is HilbertEOF: 48 | return HilbertEOFRotator(**self.params) 49 | elif type(model) is MCA: 50 | return MCARotator(**self.params) 51 | elif type(model) is HilbertMCA: 52 | return HilbertMCARotator(**self.params) 53 | else: 54 | err_msg = f"Invalid model type. Valid types are {self._valid_types}." 55 | raise TypeError(err_msg) 56 | -------------------------------------------------------------------------------- /xeofs/single/__init__.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | 3 | from .eeof import ExtendedEOF 4 | from .eof import EOF, ComplexEOF, HilbertEOF 5 | from .eof_rotator import ComplexEOFRotator, EOFRotator, HilbertEOFRotator 6 | from .gwpca import GWPCA 7 | from .opa import OPA 8 | from .pop import POP 9 | from .sparse_pca import SparsePCA 10 | 11 | __all__ = [ 12 | "EOF", 13 | "ExtendedEOF", 14 | "SparsePCA", 15 | "POP", 16 | "OPA", 17 | "GWPCA", 18 | "ComplexEOF", 19 | "HilbertEOF", 20 | "EOFRotator", 21 | "ComplexEOFRotator", 22 | "HilbertEOFRotator", 23 | ] 24 | 25 | 26 | DEPRECATED_NAMES = [ 27 | # ("OldClass", "NewClass"), 28 | ] 29 | 30 | 31 | def __dir__(): 32 | return sorted(__all__ + [names[0] for names in DEPRECATED_NAMES]) 33 | 34 | 35 | def __getattr__(name): 36 | for old_name, new_name in DEPRECATED_NAMES: 37 | if name == old_name: 38 | msg = ( 39 | f"Class '{old_name}' is deprecated and will be renamed to '{new_name}' in the next major release. " 40 | f"In that release, '{old_name}' will refer to a different class. " 41 | f"Please switch to '{new_name}' to maintain compatibility." 42 | ) 43 | warnings.warn(msg, DeprecationWarning, stacklevel=2) 44 | return globals()[new_name] 45 | raise AttributeError(f"module {__name__} has no attribute {name}") 46 | -------------------------------------------------------------------------------- /xeofs/single/_numpy/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/xeofs/single/_numpy/__init__.py -------------------------------------------------------------------------------- /xeofs/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/xeofs/utils/__init__.py -------------------------------------------------------------------------------- /xeofs/utils/constants.py: -------------------------------------------------------------------------------- 1 | VALID_LATITUDE_NAMES = [ 2 | "latitude", 3 | "lats", 4 | "lat", 5 | "Latitude", 6 | "Lats", 7 | "Lat", 8 | "LATITUDE", 9 | "LATS", 10 | "LAT", 11 | ] 12 | 13 | VALID_LONGITUDE_NAMES = ["lon", "lons", "longitude", "longitudes"] 14 | VALID_CARTESIAN_X_NAMES = ["x", "x_coord"] 15 | VALID_CARTESIAN_Y_NAMES = ["y", "y_coord"] 16 | 17 | VALID_KERNELS = ["bisquare", "gaussian", "exponential"] 18 | VALID_METRICS = ["euclidean", "haversine"] 19 | 20 | 21 | MULTIPLE_TESTS = [ 22 | "bonferroni", 23 | "sidak", 24 | "holm-sidak", 25 | "holm", 26 | "simes-hochberg", 27 | "hommel", 28 | "fdr_bh", 29 | "fdr_by", 30 | "fdr_tsbh", 31 | "fdr_tsbky", 32 | ] 33 | 34 | 35 | AVG_EARTH_RADIUS = 6371.0 # in km 36 | -------------------------------------------------------------------------------- /xeofs/utils/data_types.py: -------------------------------------------------------------------------------- 1 | from typing import Hashable, Sequence, TypeAlias, TypeVar 2 | 3 | import dask.array as da 4 | from xarray.core import dataarray as xr_dataarray 5 | from xarray.core import dataset as xr_dataset 6 | 7 | DataArray: TypeAlias = xr_dataarray.DataArray 8 | DataSet: TypeAlias = xr_dataset.Dataset 9 | Data: TypeAlias = DataArray | DataSet 10 | DataVar = TypeVar("DataVar", DataArray, DataSet) 11 | DataVarBound = TypeVar("DataVarBound", bound=Data) 12 | 13 | DataArrayList: TypeAlias = list[DataArray] 14 | DataSetList: TypeAlias = list[DataSet] 15 | DataList: TypeAlias = list[Data] 16 | DataVarList: TypeAlias = list[DataVar] 17 | 18 | GenericType = TypeVar("GenericType") 19 | 20 | DaskArray: TypeAlias = da.Array # type: ignore 21 | DataObject: TypeAlias = DataArray | DataSet | DataList 22 | 23 | Dims: TypeAlias = Sequence[Hashable] 24 | DimsTuple: TypeAlias = tuple[Dims, ...] 25 | DimsList: TypeAlias = list[Dims] 26 | DimsListTuple: TypeAlias = tuple[DimsList, ...] 27 | -------------------------------------------------------------------------------- /xeofs/utils/dimension_renamer.py: -------------------------------------------------------------------------------- 1 | class DimensionRenamer: 2 | """Rename dimensions of an xarray DataArray. 3 | 4 | Parameters 5 | ---------- 6 | dim : str 7 | Name of the dimension to be renamed. Can be a dimension containing a MultiIndex. 8 | suffix : str 9 | Suffix to be added to the dimension name. 10 | 11 | """ 12 | 13 | def __init__(self, dim, suffix): 14 | self.suffix = suffix 15 | self.dim = dim 16 | 17 | def fit(self, da): 18 | self.dims_mapping = { 19 | dim: dim + self.suffix for dim in da.coords[self.dim].coords.keys() 20 | } 21 | 22 | def transform(self, da): 23 | for old, new in self.dims_mapping.items(): 24 | da = da.rename({old: new}) 25 | return da 26 | 27 | def fit_transform(self, da): 28 | self.fit(da) 29 | return self.transform(da) 30 | 31 | def inverse_transform(self, da): 32 | for old, new in self.dims_mapping.items(): 33 | da = da.rename({new: old}) 34 | return da 35 | -------------------------------------------------------------------------------- /xeofs/utils/io.py: -------------------------------------------------------------------------------- 1 | from ast import literal_eval 2 | from typing import Any 3 | 4 | import numpy as np 5 | import xarray as xr 6 | 7 | 8 | def write_model_tree( 9 | dt: xr.DataTree, path: str, overwrite: bool = False, engine: str = "zarr", **kwargs 10 | ): 11 | """Write a DataTree to a file.""" 12 | write_mode = "w" if overwrite else "w-" 13 | if engine in ["netcdf4", "h5netcdf"]: 14 | dt = _sanitize_attrs_nc(dt) 15 | dt.to_netcdf(path, engine=engine, **kwargs) 16 | elif engine == "zarr": 17 | dt.to_zarr(path, mode=write_mode, **kwargs) 18 | else: 19 | raise ValueError(f"Unknown engine {engine}") 20 | 21 | 22 | def open_model_tree(path: str, engine: str = "zarr", **kwargs) -> xr.DataTree: 23 | """Open a DataTree from a file.""" 24 | if engine == "zarr" and "chunks" not in kwargs: 25 | kwargs["chunks"] = {} 26 | dt = xr.open_datatree(path, engine=engine, **kwargs) 27 | if engine in ["netcdf4", "h5netcdf"]: 28 | dt = _desanitize_attrs_nc(dt) 29 | return dt 30 | 31 | 32 | def insert_placeholders(dt: xr.DataTree) -> xr.DataTree: 33 | """Insert placeholders for data that we don't want to compute.""" 34 | for node in dt.subtree: 35 | if not node.attrs.get("allow_compute", True): 36 | dt[node.path] = xr.DataTree( 37 | xr.Dataset( 38 | data_vars={ 39 | node.name: xr.DataArray(np.nan, attrs={"placeholder": True}) 40 | }, 41 | attrs={"allow_compute": False, "placeholder": True}, 42 | ) 43 | ) 44 | return dt 45 | 46 | 47 | def _sanitize_attrs_nc(dt: xr.DataTree) -> xr.DataTree: 48 | """Sanitize both node-level and variable-level attrs to strings for netcdf.""" 49 | sanitized_types = (dict, list, bool, type(None)) 50 | for node in dt.subtree: 51 | for key, attr in node.attrs.items(): 52 | if isinstance(attr, sanitized_types): 53 | node.attrs[key] = str(attr) 54 | for v in node.variables: 55 | for key, attr in node[v].attrs.items(): 56 | if isinstance(attr, sanitized_types): 57 | node[v].attrs[key] = str(attr) 58 | return dt 59 | 60 | 61 | def _should_desanitize(attr: Any) -> bool: 62 | if isinstance(attr, str): 63 | if ( 64 | (attr[0] == "{" and attr[-1] == "}") 65 | or (attr[0] == "[" and attr[-1] == "]") 66 | or (attr in ["True", "False"]) 67 | or (attr == "None") 68 | ): 69 | return True 70 | return False 71 | 72 | 73 | def _desanitize_attrs_nc(dt: xr.DataTree) -> xr.DataTree: 74 | """Desanitize both node-level and variable-level attrs from strings for netcdf.""" 75 | for node in dt.subtree: 76 | for key, attr in node.attrs.items(): 77 | if _should_desanitize(attr): 78 | node.attrs[key] = literal_eval(attr) 79 | for v in node.variables: 80 | for key, attr in node[v].attrs.items(): 81 | if _should_desanitize(attr): 82 | node[v].attrs[key] = literal_eval(attr) 83 | return dt 84 | -------------------------------------------------------------------------------- /xeofs/utils/optional/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xarray-contrib/xeofs/29f6b51c6a19e0477db9e98ce76dbe5f23043c29/xeofs/utils/optional/__init__.py -------------------------------------------------------------------------------- /xeofs/utils/optional/kernels.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import numba 3 | 4 | 5 | @numba.njit(fastmath=True) 6 | def kernel_weights_nb(distance, bandwidth, kernel): 7 | if kernel == "bisquare": 8 | return _bisquare_nb(distance, bandwidth) 9 | elif kernel == "gaussian": 10 | return _gaussian_nb(distance, bandwidth) 11 | elif kernel == "exponential": 12 | return _exponential_nb(distance, bandwidth) 13 | else: 14 | raise ValueError( 15 | f"Invalid kernel: {kernel}. Must be one of ['bisquare', 'gaussian', 'exponential']." 16 | ) 17 | 18 | 19 | @numba.njit(fastmath=True) 20 | def _bisquare_nb(distance, bandwidth): 21 | weights = (1 - (distance / bandwidth) ** 2) ** 2 22 | return np.where(distance <= bandwidth, weights, 0) 23 | 24 | 25 | @numba.njit(fastmath=True) 26 | def _gaussian_nb(distance, bandwidth): 27 | return np.exp(-0.5 * (distance / bandwidth) ** 2) 28 | 29 | 30 | @numba.njit(fastmath=True) 31 | def _exponential_nb(distance, bandwidth): 32 | return np.exp(-0.5 * (distance / bandwidth)) 33 | -------------------------------------------------------------------------------- /xeofs/validation/__init__.py: -------------------------------------------------------------------------------- 1 | from .bootstrapper import EOFBootstrapper 2 | 3 | __all__ = ["EOFBootstrapper"] 4 | -------------------------------------------------------------------------------- /xeofs/validation/_base_bootstrapper.py: -------------------------------------------------------------------------------- 1 | 2 | --------------------------------------------------------------------------------