├── .binder └── environment.yml ├── .codecov.yml ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yaml └── workflows │ ├── benchmarks.yml │ ├── climpred_installs.yml │ ├── climpred_testing.yml │ ├── publish-production-pypi.yml │ ├── publish-staging-testpypi.yml │ └── upstream-dev-ci.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── CHANGELOG.rst ├── CITATION.cff ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── HOWTORELEASE.rst ├── LICENSE.txt ├── README.rst ├── asv_bench ├── asv.conf.json └── benchmarks │ ├── README_CI.md │ ├── __init__.py │ └── benchmarks_PredictionEnsemble.py ├── ci ├── install-upstream-wheels.sh └── requirements │ ├── climpred-dev.yml │ ├── docs.yml │ ├── maximum-tests.yml │ └── minimum-tests.yml ├── docs ├── Makefile ├── make.bat └── source │ ├── alignment.ipynb │ ├── api.rst │ ├── api │ ├── climpred.bootstrap.bootstrap_compute.rst │ ├── climpred.bootstrap.bootstrap_hindcast.rst │ ├── climpred.bootstrap.bootstrap_perfect_model.rst │ ├── climpred.bootstrap.bootstrap_uninit_pm_ensemble_from_control_cftime.rst │ ├── climpred.bootstrap.bootstrap_uninitialized_ensemble.rst │ ├── climpred.bootstrap.dpp_threshold.rst │ ├── climpred.bootstrap.resample_skill_exclude_resample_dim_from_dim.rst │ ├── climpred.bootstrap.resample_skill_loop.rst │ ├── climpred.bootstrap.resample_skill_resample_before.rst │ ├── climpred.bootstrap.varweighted_mean_period_threshold.rst │ ├── climpred.classes.HindcastEnsemble.__init__.rst │ ├── climpred.classes.HindcastEnsemble.add_observations.rst │ ├── climpred.classes.HindcastEnsemble.add_uninitialized.rst │ ├── climpred.classes.HindcastEnsemble.bootstrap.rst │ ├── climpred.classes.HindcastEnsemble.generate_uninitialized.rst │ ├── climpred.classes.HindcastEnsemble.get_initialized.rst │ ├── climpred.classes.HindcastEnsemble.get_observations.rst │ ├── climpred.classes.HindcastEnsemble.get_uninitialized.rst │ ├── climpred.classes.HindcastEnsemble.plot.rst │ ├── climpred.classes.HindcastEnsemble.plot_alignment.rst │ ├── climpred.classes.HindcastEnsemble.remove_bias.rst │ ├── climpred.classes.HindcastEnsemble.remove_seasonality.rst │ ├── climpred.classes.HindcastEnsemble.rst │ ├── climpred.classes.HindcastEnsemble.smooth.rst │ ├── climpred.classes.HindcastEnsemble.verify.rst │ ├── climpred.classes.PerfectModelEnsemble.__init__.rst │ ├── climpred.classes.PerfectModelEnsemble.add_control.rst │ ├── climpred.classes.PerfectModelEnsemble.bootstrap.rst │ ├── climpred.classes.PerfectModelEnsemble.generate_uninitialized.rst │ ├── climpred.classes.PerfectModelEnsemble.get_control.rst │ ├── climpred.classes.PerfectModelEnsemble.get_initialized.rst │ ├── climpred.classes.PerfectModelEnsemble.get_uninitialized.rst │ ├── climpred.classes.PerfectModelEnsemble.plot.rst │ ├── climpred.classes.PerfectModelEnsemble.remove_seasonality.rst │ ├── climpred.classes.PerfectModelEnsemble.rst │ ├── climpred.classes.PerfectModelEnsemble.smooth.rst │ ├── climpred.classes.PerfectModelEnsemble.verify.rst │ ├── climpred.classes.PredictionEnsemble.__add__.rst │ ├── climpred.classes.PredictionEnsemble.__contains__.rst │ ├── climpred.classes.PredictionEnsemble.__delitem__.rst │ ├── climpred.classes.PredictionEnsemble.__getattr__.rst │ ├── climpred.classes.PredictionEnsemble.__getitem__.rst │ ├── climpred.classes.PredictionEnsemble.__init__.rst │ ├── climpred.classes.PredictionEnsemble.__iter__.rst │ ├── climpred.classes.PredictionEnsemble.__len__.rst │ ├── climpred.classes.PredictionEnsemble.__mul__.rst │ ├── climpred.classes.PredictionEnsemble.__sub__.rst │ ├── climpred.classes.PredictionEnsemble.__truediv__.rst │ ├── climpred.classes.PredictionEnsemble.chunks.rst │ ├── climpred.classes.PredictionEnsemble.chunksizes.rst │ ├── climpred.classes.PredictionEnsemble.coords.rst │ ├── climpred.classes.PredictionEnsemble.data_vars.rst │ ├── climpred.classes.PredictionEnsemble.dims.rst │ ├── climpred.classes.PredictionEnsemble.equals.rst │ ├── climpred.classes.PredictionEnsemble.identical.rst │ ├── climpred.classes.PredictionEnsemble.nbytes.rst │ ├── climpred.classes.PredictionEnsemble.plot.rst │ ├── climpred.classes.PredictionEnsemble.rst │ ├── climpred.classes.PredictionEnsemble.sizes.rst │ ├── climpred.comparisons.Comparison.__init__.rst │ ├── climpred.comparisons.Comparison.__repr__.rst │ ├── climpred.comparisons.Comparison.rst │ ├── climpred.comparisons._e2c.rst │ ├── climpred.comparisons._e2o.rst │ ├── climpred.comparisons._m2c.rst │ ├── climpred.comparisons._m2e.rst │ ├── climpred.comparisons._m2m.rst │ ├── climpred.comparisons._m2o.rst │ ├── climpred.graphics.plot_bootstrapped_skill_over_leadyear.rst │ ├── climpred.graphics.plot_ensemble_perfect_model.rst │ ├── climpred.graphics.plot_lead_timeseries_hindcast.rst │ ├── climpred.horizon.horizon.rst │ ├── climpred.metrics.Metric.__init__.rst │ ├── climpred.metrics.Metric.__repr__.rst │ ├── climpred.metrics.Metric.rst │ ├── climpred.metrics._bias_slope.rst │ ├── climpred.metrics._brier_score.rst │ ├── climpred.metrics._conditional_bias.rst │ ├── climpred.metrics._contingency.rst │ ├── climpred.metrics._crps.rst │ ├── climpred.metrics._crpss.rst │ ├── climpred.metrics._crpss_es.rst │ ├── climpred.metrics._discrimination.rst │ ├── climpred.metrics._effective_sample_size.rst │ ├── climpred.metrics._get_norm_factor.rst │ ├── climpred.metrics._less.rst │ ├── climpred.metrics._mae.rst │ ├── climpred.metrics._mape.rst │ ├── climpred.metrics._me.rst │ ├── climpred.metrics._median_absolute_error.rst │ ├── climpred.metrics._mse.rst │ ├── climpred.metrics._msess.rst │ ├── climpred.metrics._msess_murphy.rst │ ├── climpred.metrics._mul_bias.rst │ ├── climpred.metrics._nmae.rst │ ├── climpred.metrics._nmse.rst │ ├── climpred.metrics._nrmse.rst │ ├── climpred.metrics._pearson_r.rst │ ├── climpred.metrics._pearson_r_eff_p_value.rst │ ├── climpred.metrics._pearson_r_p_value.rst │ ├── climpred.metrics._rank_histogram.rst │ ├── climpred.metrics._reliability.rst │ ├── climpred.metrics._rmse.rst │ ├── climpred.metrics._roc.rst │ ├── climpred.metrics._rps.rst │ ├── climpred.metrics._smape.rst │ ├── climpred.metrics._spearman_r.rst │ ├── climpred.metrics._spearman_r_eff_p_value.rst │ ├── climpred.metrics._spearman_r_p_value.rst │ ├── climpred.metrics._spread.rst │ ├── climpred.metrics._std_ratio.rst │ ├── climpred.metrics._threshold_brier_score.rst │ ├── climpred.metrics._uacc.rst │ ├── climpred.metrics._unconditional_bias.rst │ ├── climpred.options.set_options.rst │ ├── climpred.prediction.compute_hindcast.rst │ ├── climpred.prediction.compute_perfect_model.rst │ ├── climpred.preprocessing.mpi.get_path.rst │ ├── climpred.preprocessing.shared.load_hindcast.rst │ ├── climpred.preprocessing.shared.rename_SLM_to_climpred_dims.rst │ ├── climpred.preprocessing.shared.rename_to_climpred_dims.rst │ ├── climpred.preprocessing.shared.set_integer_time_axis.rst │ ├── climpred.reference.compute_climatology.rst │ ├── climpred.reference.compute_persistence.rst │ ├── climpred.reference.compute_persistence_from_first_lead.rst │ ├── climpred.reference.compute_uninitialized.rst │ ├── climpred.smoothing.spatial_smoothing_xesmf.rst │ ├── climpred.smoothing.temporal_smoothing.rst │ ├── climpred.stats.decorrelation_time.rst │ ├── climpred.stats.dpp.rst │ ├── climpred.stats.rm_poly.rst │ ├── climpred.stats.rm_trend.rst │ ├── climpred.stats.varweighted_mean_period.rst │ ├── climpred.tutorial.load_dataset.rst │ ├── climpred.utils.convert_init_lead_to_valid_time_lead.rst │ └── climpred.utils.convert_valid_time_lead_to_init_lead.rst │ ├── bias_removal.ipynb │ ├── changelog.rst │ ├── climpred.bib │ ├── code_of_conduct.rst │ ├── comparisons.rst │ ├── conf.py │ ├── contributing.rst │ ├── contributors.rst │ ├── examples.rst │ ├── examples │ ├── NWP │ │ ├── Herbie.ipynb │ │ └── NWP_GEFS_6h_forecasts.ipynb │ ├── decadal │ │ ├── Significance.ipynb │ │ ├── diagnose-potential-predictability.ipynb │ │ ├── perfect-model-predictability-demo.ipynb │ │ ├── tropical-pacific-ssts.ipynb │ │ └── verify_dim_implications.ipynb │ ├── misc │ │ ├── climpred_gpu.ipynb │ │ └── setup_your_own_data.ipynb │ ├── monseas │ │ ├── monthly-enso-subx-example.ipynb │ │ └── seasonal-enso-subx-example.ipynb │ └── subseasonal │ │ ├── daily-S2S-ECMWF.ipynb │ │ ├── daily-S2S-IRIDL.ipynb │ │ ├── daily-subx-example.ipynb │ │ └── weekly-subx-example.ipynb │ ├── helpful-links.rst │ ├── images │ ├── alignment_plots │ │ ├── maximize_alignment.ipynb │ │ ├── maximize_alignment.png │ │ ├── same_inits_alignment.ipynb │ │ ├── same_inits_alignment.png │ │ ├── same_verifs_alignment.ipynb │ │ └── same_verifs_alignment.png │ ├── climpred-logo-inverse.png │ └── climpred-logo.png │ ├── index.rst │ ├── initialized-datasets.rst │ ├── literature.rst │ ├── metrics.rst │ ├── prediction-ensemble-object.ipynb │ ├── publications.rst │ ├── quick-start.ipynb │ ├── reference_forecast.rst │ ├── related-packages.rst │ ├── release_procedure.rst │ ├── savefig │ └── different_alignment_methods.png │ ├── scope.rst │ ├── setting-up-data.rst │ ├── significance.rst │ ├── smoothing.ipynb │ ├── terminology.rst │ └── why-climpred.rst ├── joss ├── codemeta.json ├── paper.bib └── paper.md ├── pyproject.toml ├── requirements_upstream.txt └── src └── climpred ├── __init__.py ├── alignment.py ├── bias_removal.py ├── bootstrap.py ├── checks.py ├── classes.py ├── comparisons.py ├── conftest.py ├── constants.py ├── exceptions.py ├── graphics.py ├── horizon.py ├── logging.py ├── metrics.py ├── options.py ├── prediction.py ├── preprocessing ├── __init__.py ├── mpi.py └── shared.py ├── reference.py ├── relative_entropy.py ├── smoothing.py ├── stats.py ├── testing.py ├── tests ├── __init__.py ├── test_HindcastEnsemble_class.py ├── test_PerfectModelEnsemble_class.py ├── test_PredictionEnsemble.py ├── test_alignment.py ├── test_bias_removal.py ├── test_bootstrap.py ├── test_checks.py ├── test_comparisons.py ├── test_compute_dims.py ├── test_demo_data.py ├── test_effective_p_value.py ├── test_graphics.py ├── test_hindcast_prediction.py ├── test_horizon.py ├── test_lead_time_resolutions.py ├── test_logging.py ├── test_map.py ├── test_metrics.py ├── test_metrics_perfect.py ├── test_options.py ├── test_perfect_model_prediction.py ├── test_preprocessing.py ├── test_probabilistic.py ├── test_reference.py ├── test_relative_entropy.py ├── test_repr.py ├── test_smoothing.py ├── test_stats.py ├── test_uninitialized.py ├── test_utils.py ├── test_valid_time.py └── test_versioning.py ├── tutorial.py ├── utils.py └── versioning ├── __init__.py └── print_versions.py /.binder/environment.yml: -------------------------------------------------------------------------------- 1 | name: climpred-docs-notebooks 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - python >=3.9 6 | - bias_correction >=0.4 7 | - cf_xarray >=0.6.0 8 | - cftime >=1.5.0 9 | - dask-core >=2021.10.0 10 | - esmpy 11 | - esmtools >=1.1.3 12 | - matplotlib-base 13 | - nc-time-axis >=1.4.0 14 | - netcdf4 # ==1.5.1 # see https://github.com/pydata/xarray/issues/4925 15 | - pydap 16 | - toolz 17 | - xarray >=0.19.0 18 | - xclim !=0.30.0 19 | - xesmf 20 | - xrft 21 | - xskillscore >=0.0.20 22 | - pip 23 | - pip: 24 | - git+https://github.com/pangeo-data/climpred.git # latest climpred version 25 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | # Require 90% coverage. 6 | target: 90 7 | patch: false 8 | changes: false 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 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 | **Code Sample** 14 | 15 | A "Minimal, Complete and Verifiable Example" will make it much easier for maintainers to help you: 16 | . I.e., this shouldn't rely upon a file on your local machine. Can you recreate this bug with a small dummy dataset of randomly generated `numpy`/`xarray` data? 17 | 18 | ```python 19 | # Your code here 20 | ``` 21 | 22 | **Expected behavior** 23 | A clear and concise description of what you expected to happen. 24 | 25 | **Output of `climpred.show_versions()`** 26 | 27 |
28 | # Paste the output here climpred.show_versions() here 29 |
30 | 31 | **Screenshots** 32 | If applicable, add screenshots to help explain your problem. 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: General Question 4 | url: https://gitter.im/climpred/community 5 | about: "If you have a simple question about how to leverage the package for your task, please post it in our gitter channel." 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: feature request 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/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. 4 | 5 | Closes #(issue) 6 | 7 | ## To-Do List 8 | 9 | 10 | 11 | ## Type of change 12 | 13 | 14 | 15 | - [ ] Bug fix (non-breaking change which fixes an issue) 16 | - [ ] New feature (non-breaking change which adds functionality) 17 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 18 | - [ ] This change requires a documentation update 19 | - [ ] Performance (if you modified existing code run `asv` to detect performance changes) 20 | - [ ] Refactoring 21 | - [ ] Improved Documentation 22 | 23 | # How Has This Been Tested? 24 | 25 | 26 | 27 | - [ ] Tests added for `pytest`, if necessary. 28 | 29 | ## Checklist (while developing) 30 | 31 | - [ ] I have added docstrings to all new functions. 32 | - [ ] I have commented my code, particularly in hard-to-understand areas. 33 | - [ ] I have updated the sphinx documentation, if necessary. 34 | - [ ] Any new functions are added to the API. (See contribution guide) 35 | - [ ] CHANGELOG is updated with reference to this PR. 36 | 37 | ## References 38 | 39 | 40 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'github-actions' 4 | directory: '/' 5 | schedule: 6 | # Check for updates once a week 7 | interval: 'quarterly' 8 | -------------------------------------------------------------------------------- /.github/workflows/benchmarks.yml: -------------------------------------------------------------------------------- 1 | # see https://github.com/pydata/xarray/blob/main/.github/workflows/benchmarks.yml 2 | name: Benchmark 3 | 4 | on: 5 | pull_request: 6 | types: [opened, reopened, synchronize, labeled] 7 | workflow_dispatch: 8 | 9 | jobs: 10 | benchmark: 11 | if: ${{ contains( github.event.pull_request.labels.*.name, 'run-benchmark') && github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' }} 12 | name: Linux 13 | runs-on: ubuntu-latest 14 | env: 15 | ASV_DIR: "./asv_bench" 16 | defaults: 17 | run: 18 | shell: bash -l {0} 19 | strategy: 20 | matrix: 21 | python-version: ["3.10"] 22 | steps: 23 | # We need the full repo to avoid this issue 24 | # https://github.com/actions/checkout/issues/23 25 | - uses: actions/checkout@v4 26 | with: 27 | fetch-depth: 0 28 | 29 | - name: Set up conda (micromamba) 30 | uses: mamba-org/setup-micromamba@v2 31 | with: 32 | environment-name: climpred-benchmarks 33 | cache-environment: true 34 | create-args: >- 35 | python=${{ matrix.python-version }} 36 | conda 37 | 38 | - name: Set Up Dependencies 39 | run: | 40 | python -m pip install asv 41 | sudo apt-get update -y 42 | - name: Run benchmarks 43 | id: benchmark 44 | env: 45 | OPENBLAS_NUM_THREADS: 1 46 | MKL_NUM_THREADS: 1 47 | OMP_NUM_THREADS: 1 48 | ASV_FACTOR: 1.5 49 | ASV_SKIP_SLOW: 1 50 | run: | 51 | set -x 52 | # ID this runner 53 | asv machine --yes 54 | echo "Baseline: ${{ github.event.pull_request.base.sha }} (${{ github.event.pull_request.base.label }})" 55 | echo "Contender: ${GITHUB_SHA} (${{ github.event.pull_request.head.label }})" 56 | # Use mamba for env creation 57 | # export CONDA_EXE=$(which mamba) 58 | export CONDA_EXE=$(which conda) 59 | # Run benchmarks for current commit against base 60 | ASV_OPTIONS="--split --show-stderr --factor $ASV_FACTOR" 61 | asv continuous $ASV_OPTIONS ${{ github.event.pull_request.base.sha }} ${GITHUB_SHA} \ 62 | | sed "/Traceback \|failed$\|PERFORMANCE DECREASED/ s/^/::error::/" \ 63 | | tee benchmarks.log 64 | # Report and export results for subsequent steps 65 | if grep "Traceback \|failed\|PERFORMANCE DECREASED" benchmarks.log > /dev/null ; then 66 | exit 1 67 | fi 68 | working-directory: ${{ env.ASV_DIR }} 69 | 70 | - name: Add instructions to artifact 71 | if: always() 72 | run: | 73 | cat benchmarks.log 74 | cp benchmarks.log .asv/results/ 75 | working-directory: ${{ env.ASV_DIR }} 76 | 77 | - uses: actions/upload-artifact@v4 78 | if: always() 79 | with: 80 | name: asv-benchmark-results-${{ runner.os }} 81 | path: ${{ env.ASV_DIR }}/.asv/results 82 | -------------------------------------------------------------------------------- /.github/workflows/climpred_installs.yml: -------------------------------------------------------------------------------- 1 | name: install 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: # allows you to trigger manually 6 | 7 | concurrency: 8 | group: ${{ github.workflow }}-${{ github.ref }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | detect-ci-trigger: 13 | name: detect ci trigger 14 | runs-on: ubuntu-latest 15 | if: github.event_name == 'push' || github.event_name == 'pull_request' 16 | outputs: 17 | triggered: ${{ steps.detect-trigger.outputs.trigger-found }} 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 2 22 | - uses: xarray-contrib/ci-trigger@v1.2 23 | id: detect-trigger 24 | with: 25 | keyword: "[skip-ci]" 26 | 27 | install-climpred-complete: # Installs climpred on various OS. 28 | name: Install climpred[complete], ${{ matrix.os }}, Python${{ matrix.python-version }} 29 | runs-on: ${{ matrix.os }} 30 | needs: detect-ci-trigger 31 | if: needs.detect-ci-trigger.outputs.triggered == 'false' 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | os: ["ubuntu-latest", "macos-latest", "windows-latest"] 36 | python-version: ["3.9"] 37 | steps: 38 | - uses: actions/checkout@v4 39 | - name: Setup Python 40 | uses: actions/setup-python@v5 41 | with: 42 | python-version: ${{ matrix.python-version }} 43 | - name: Install dependencies 44 | run: | 45 | pip install -e .[complete] 46 | python -c "import climpred" 47 | 48 | install-climpred: # Installs climpred on various OS. 49 | name: Install climpred, ${{ matrix.os }}, Python${{ matrix.python-version }} 50 | runs-on: ${{ matrix.os }} 51 | needs: detect-ci-trigger 52 | if: needs.detect-ci-trigger.outputs.triggered == 'false' 53 | strategy: 54 | fail-fast: false 55 | matrix: 56 | os: ["ubuntu-latest", "macos-latest", "windows-latest"] 57 | python-version: ["3.9"] 58 | steps: 59 | - uses: actions/checkout@v4 60 | with: 61 | fetch-depth: 0 # Fetch all history for all branches and tags. 62 | - name: Setup Python 63 | uses: actions/setup-python@v5 64 | with: 65 | python-version: ${{ matrix.python-version }} 66 | - name: Install dependencies 67 | run: | 68 | pip install -e . 69 | python -c "import climpred" 70 | -------------------------------------------------------------------------------- /.github/workflows/publish-production-pypi.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python 🐍 distributions 📦 to PyPI 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | build-n-publish-pypi: 10 | name: Build and publish Python 🐍 distributions 📦 to PyPI 11 | environment: production 12 | runs-on: ubuntu-latest 13 | permissions: 14 | id-token: write 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Set up Python3 18 | uses: actions/setup-python@v5 19 | with: 20 | python-version: "3.x" 21 | - name: Install packaging libraries 22 | run: | 23 | python -m pip install --upgrade pip 24 | python -m pip install build 25 | - name: Build a binary wheel and a source tarball 26 | run: | 27 | python -m build 28 | - name: Publish distribution 📦 to PyPI 29 | uses: pypa/gh-action-pypi-publish@release/v1 30 | -------------------------------------------------------------------------------- /.github/workflows/publish-staging-testpypi.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python 🐍 distributions 📦 to TestPyPI 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*' # Push events to matching v*, i.e. v1.0, v20.15.10 7 | 8 | jobs: 9 | release: 10 | name: Create Release from tag 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v4 15 | - name: Create Release 16 | uses: softprops/action-gh-release@v2 17 | env: 18 | # This token is provided by Actions, you do not need to create your own token 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | with: 21 | tag_name: ${{ github.ref }} 22 | name: Release ${{ github.ref }} 23 | draft: true 24 | prerelease: false 25 | deploy-testpypi: 26 | name: Build and publish Python 🐍 distributions 📦 to TestPyPI 27 | needs: release 28 | environment: staging 29 | runs-on: ubuntu-latest 30 | permissions: 31 | id-token: write 32 | steps: 33 | - uses: actions/checkout@v4 34 | - name: Set up Python3 35 | uses: actions/setup-python@v5 36 | with: 37 | python-version: "3.x" 38 | - name: Install packaging libraries 39 | run: | 40 | python -m pip install --upgrade pip 41 | python -m pip install build 42 | - name: Build a binary wheel and a source tarball 43 | run: | 44 | python -m build 45 | - name: Publish distribution 📦 to Test PyPI 46 | uses: pypa/gh-action-pypi-publish@release/v1 47 | with: 48 | repository-url: https://test.pypi.org/legacy/ 49 | skip-existing: true 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .*.un~ 3 | .fuse_hidden* 4 | *.nc # use github.com/pangeo-data/climpred-data 5 | 6 | # Created by https://www.gitignore.io/api/python 7 | 8 | ### Python ### 9 | # Byte-compiled / optimized / DLL files 10 | __pycache__/ 11 | *.py[cod] 12 | *$py.class 13 | 14 | # C extensions 15 | *.so 16 | 17 | # Distribution / packaging 18 | .Python 19 | build/ 20 | develop-eggs/ 21 | dist/ 22 | downloads/ 23 | eggs/ 24 | .eggs/ 25 | lib/ 26 | lib64/ 27 | parts/ 28 | sdist/ 29 | var/ 30 | wheels/ 31 | *.egg-info/ 32 | .installed.cfg 33 | *.egg 34 | 35 | # PyInstaller 36 | # Usually these files are written by a python script from a template 37 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 38 | *.manifest 39 | *.spec 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | .hypothesis/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # pyenv 81 | .python-version 82 | 83 | # celery beat schedule file 84 | celerybeat-schedule 85 | 86 | # SageMath parsed files 87 | *.sage.py 88 | 89 | # Environments 90 | .env 91 | .venv 92 | env/ 93 | venv/ 94 | ENV/ 95 | env.bak/ 96 | venv.bak/ 97 | 98 | # Spyder project settings 99 | .spyderproject 100 | .spyproject 101 | 102 | # Rope project settings 103 | .ropeproject 104 | 105 | # mkdocs documentation 106 | /site 107 | 108 | # mypy 109 | .mypy_cache/ 110 | 111 | # asv 112 | asv_bench/.asv/ 113 | 114 | # VS Code 115 | .vscode/ 116 | 117 | # PyCharm 118 | .idea/ 119 | 120 | # dask 121 | dask-worker-space 122 | 123 | # End of https://www.gitignore.io/api/python 124 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | default_language_version: 2 | python: python3 3 | 4 | repos: 5 | - repo: https://github.com/pre-commit/pre-commit-hooks 6 | rev: v5.0.0 7 | hooks: 8 | - id: check-added-large-files 9 | - id: check-docstring-first 10 | - id: check-json 11 | exclude: 'asv.conf.json' 12 | - id: check-merge-conflict 13 | - id: check-toml 14 | - id: check-yaml 15 | args: [ '--allow-multiple-documents' ] 16 | - id: debug-statements 17 | - id: end-of-file-fixer 18 | - id: no-commit-to-branch 19 | - id: pretty-format-json 20 | args: [ '--autofix', '--no-ensure-ascii', '--no-sort-keys' ] 21 | exclude: '.ipynb|asv.conf.json' 22 | - id: trailing-whitespace 23 | 24 | - repo: https://github.com/pappasam/toml-sort 25 | rev: v0.23.1 26 | hooks: 27 | - id: toml-sort-fix 28 | 29 | - repo: https://github.com/psf/black-pre-commit-mirror 30 | rev: 24.10.0 31 | hooks: 32 | - id: black 33 | 34 | - repo: https://github.com/PyCQA/flake8 35 | rev: 7.1.1 36 | hooks: 37 | - id: flake8 38 | args: [ '--max-line-length=93', '--extend-ignore=W503' ] 39 | 40 | - repo: https://github.com/PyCQA/isort 41 | rev: 5.13.2 42 | hooks: 43 | - id: isort 44 | 45 | - repo: https://github.com/keewis/blackdoc 46 | rev: v0.3.9 47 | hooks: 48 | - id: blackdoc 49 | additional_dependencies: [ 'black==24.10.0' ] 50 | - id: blackdoc-autoupdate-black 51 | 52 | - repo: https://github.com/pre-commit/mirrors-mypy 53 | rev: v1.11.2 54 | hooks: 55 | - id: mypy 56 | exclude: 'asv_bench' 57 | additional_dependencies: [ 58 | # Type stubs 59 | types-PyYAML, 60 | types-python-dateutil, 61 | types-pytz, 62 | types-setuptools, 63 | typing-extensions, 64 | # Dependencies that are typed 65 | numpy 66 | ] 67 | 68 | - repo: https://github.com/python-jsonschema/check-jsonschema 69 | rev: 0.29.3 70 | hooks: 71 | - id: check-github-workflows 72 | - id: check-readthedocs 73 | 74 | - repo: meta 75 | hooks: 76 | - id: check-hooks-apply 77 | 78 | ci: 79 | autofix_commit_msg: | 80 | [pre-commit.ci] auto fixes from pre-commit.com hooks 81 | 82 | for more information, see https://pre-commit.ci 83 | autofix_prs: true 84 | autoupdate_branch: '' 85 | autoupdate_commit_msg: '[pre-commit.ci] pre-commit autoupdate' 86 | autoupdate_schedule: quarterly 87 | skip: [ ] 88 | submodules: false 89 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "mambaforge-22.9" 7 | 8 | conda: 9 | environment: ci/requirements/docs.yml 10 | 11 | sphinx: 12 | fail_on_warning: false 13 | 14 | formats: [] 15 | 16 | python: 17 | install: 18 | - method: pip 19 | path: . 20 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | # YAML 1.2 2 | # Metadata for citation of this software according to the CFF format (https://citation-file-format.github.io/) 3 | cff-version: 1.2.0 4 | message: If you use this software, please cite it using these metadata. 5 | # FIXME title as repository name might not be the best name, please make human readable 6 | title: 'climpred: Verification of weather and climate forecasts' 7 | doi: 10.5281/zenodo.4556085 8 | # FIXME splitting of full names is error prone, please check if given/family name are correct 9 | authors: 10 | - given-names: Riley 11 | family-names: Brady 12 | affiliation: McKinsey & Company 13 | orcid: https://orcid.org/0000-0002-2309-8245 14 | - given-names: Aaron 15 | family-names: Spring 16 | affiliation: Max-Planck-Institute for Meteorology 17 | orcid: https://orcid.org/0000-0003-0216-2241 18 | - given-names: Andrew 19 | family-names: Huang 20 | - given-names: Anderson 21 | family-names: Banihirwe 22 | affiliation: '@NCAR' 23 | - given-names: Ray 24 | family-names: Bell 25 | affiliation: DTN 26 | url: https://github.com/pangeo-data/climpred 27 | license: MIT 28 | preferred-citation: 29 | type: article 30 | authors: 31 | - given-names: Riley 32 | family-names: Brady 33 | affiliation: McKinsey & Company 34 | orcid: https://orcid.org/0000-0002-2309-8245 35 | - given-names: Aaron 36 | family-names: Spring 37 | affiliation: Max-Planck-Institute for Meteorology 38 | orcid: https://orcid.org/0000-0003-0216-2241 39 | doi: 10.21105/joss.02781 40 | journal: "Journal of Open Research Software" 41 | day: 2 42 | month: 3 43 | year: 2021 44 | title: "climpred: Verification of weather and climate forecasts" 45 | volume: 6 46 | issue: 59 47 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Our Code of Conduct can be found on our documentation [here](https://climpred.readthedocs.io/en/stable/code_of_conduct.html). 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | A guide on how to contribute to `climpred` can be found on our documentation [here](https://climpred.readthedocs.io/en/stable/contributing.html). 2 | -------------------------------------------------------------------------------- /HOWTORELEASE.rst: -------------------------------------------------------------------------------- 1 | Release Procedure 2 | ----------------- 3 | 4 | We follow semantic versioning, e.g., ``v1.0.0``. A major version causes incompatible API 5 | changes, a minor version adds functionality, and a patch covers bug fixes. 6 | 7 | #. Create a new branch ``release-v1.0.0`` with the version for the release. 8 | 9 | * Update `CHANGELOG.rst `_. 10 | * Make sure all new changes and features are reflected in the documentation. 11 | 12 | #. Open a new pull request for this branch targeting ``main`` 13 | 14 | #. After all tests pass and the PR has been approved, merge the PR into ``main`` 15 | 16 | #. Tag a release and push it to GitHub:: 17 | 18 | $ git tag -a v1.0.0 -m "Version 1.0.0" 19 | $ git push upstream main --tags 20 | 21 | #. We use Github Actions to automate the new release being published to TestPyPI (staging) and PyPI (production). When a tag is pushed to the repository, the following happens: 22 | - A new release is drafted on Github 23 | - The library is built and a workflow is staged for publishing to TestPyPI 24 | - A maintainer must manually approve the workflow to publish to TestPyPI 25 | - If everything clears, maintainers can then finalize the release on GitHub, triggering an upload to PyPI 26 | 27 | If you wish to circumvent the GitHub Actions for whatever reason, you can manually do it by running the following:: 28 | 29 | $ git clean -xfd # remove any files not checked into git 30 | $ python -m build # build package 31 | $ python -m twine upload --repository-url https://test.pypi.org/legacy dist/* # register and push to testpypi 32 | $ python -m twine upload dist/* # register and push to pypi 33 | 34 | #. Next, update the stable branch with ``main``. This will trigger a stable build 35 | for ReadTheDocs:: 36 | 37 | $ git checkout stable 38 | $ git rebase main 39 | $ git push -f upstream stable 40 | $ git checkout main 41 | 42 | #. Go to https://readthedocs.org and add the new version to ``"Active Versions"`` under the version tab. 43 | Force-build ``"stable"`` if it isn't already building. 44 | 45 | #. Update climpred conda-forge feedstock 46 | 47 | * Fork `climpred-feedstock repository `_ 48 | * Clone this fork and edit recipe:: 49 | 50 | $ git clone git@github.com:username/climpred-feedstock.git 51 | $ cd climpred-feedstock 52 | $ cd recipe 53 | $ # edit meta.yaml 54 | 55 | * Update version 56 | * Get ``sha256`` from pypi.org for `climpred `_ 57 | * Check that ``requirements.txt`` from the main ``climpred`` repo is accounted for 58 | in ``meta.yaml`` from the feedstock. 59 | * Fill in the rest of information as described 60 | `here `_ 61 | * Commit and submit a PR 62 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 The Python Packaging Authority 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://i.imgur.com/HPOdOsR.png 2 | 3 | Verification of weather and climate forecasts 4 | 5 | .. 6 | Table version of badges inspired by pySTEPS. 7 | 8 | .. list-table:: 9 | :stub-columns: 1 10 | :widths: 10 90 11 | 12 | * - docs 13 | - |docs| |joss| |doi| 14 | * - tests 15 | - |ci| |upstream| |codecov| |precommit| 16 | * - package 17 | - |conda| |conda downloads| |pypi| |pypi downloads| 18 | * - license 19 | - |license| 20 | * - community 21 | - |gitter| |contributors| |forks| |stars| |issues| |PRs| 22 | * - tutorials 23 | - |gallery| |workshop| |cloud| 24 | 25 | .. |docs| image:: https://img.shields.io/readthedocs/climpred/latest.svg?style=flat 26 | :target: https://climpred.readthedocs.io/en/stable/?badge=stable 27 | :alt: Documentation Status 28 | 29 | .. |joss| image:: https://joss.theoj.org/papers/246d440e3fcb19025a3b0e56e1af54ef/status.svg 30 | :target: https://joss.theoj.org/papers/246d440e3fcb19025a3b0e56e1af54ef 31 | :alt: JOSS paper 32 | 33 | .. |doi| image:: https://zenodo.org/badge/DOI/10.5281/zenodo.4556085.svg 34 | :target: https://doi.org/10.5281/zenodo.4556085 35 | :alt: DOI 36 | 37 | .. |ci| image:: https://github.com/pangeo-data/climpred/actions/workflows/climpred_testing.yml/badge.svg 38 | :target: https://github.com/pangeo-data/climpred/actions/workflows/climpred_testing.yml 39 | :alt: CI 40 | 41 | .. |upstream| image:: https://github.com/pangeo-data/climpred/actions/workflows/upstream-dev-ci.yml/badge.svg 42 | :target: https://github.com/pangeo-data/climpred/actions/workflows/upstream-dev-ci.yml 43 | :alt: CI upstream 44 | 45 | .. |codecov| image:: https://codecov.io/gh/pangeo-data/climpred/branch/main/graph/badge.svg 46 | :target: https://codecov.io/gh/pangeo-data/climpred 47 | :alt: coverage 48 | 49 | .. |precommit| image:: https://results.pre-commit.ci/badge/github/pangeo-data/climpred/main.svg 50 | :target: https://results.pre-commit.ci/latest/github/pangeo-data/climpred/main 51 | :alt: pre-commit.ci status 52 | 53 | .. |conda| image:: https://img.shields.io/conda/vn/conda-forge/climpred.svg 54 | :target: https://anaconda.org/conda-forge/climpred 55 | :alt: Conda Version 56 | 57 | .. |pypi| image:: https://img.shields.io/pypi/v/climpred.svg 58 | :target: https://pypi.python.org/pypi/climpred/ 59 | :alt: pypi Version 60 | 61 | .. |license| image:: https://img.shields.io/github/license/pangeo-data/climpred.svg 62 | :alt: license 63 | :target: LICENSE.txt 64 | 65 | .. |gitter| image:: https://badges.gitter.im/Join%20Chat.svg 66 | :target: https://gitter.im/climpred 67 | :alt: gitter chat 68 | 69 | .. |contributors| image:: https://img.shields.io/github/contributors/pangeo-data/climpred 70 | :alt: GitHub contributors 71 | :target: https://github.com/pangeo-data/climpred/graphs/contributors 72 | 73 | .. |conda downloads| image:: https://img.shields.io/conda/dn/conda-forge/climpred 74 | :alt: Conda downloads 75 | :target: https://anaconda.org/conda-forge/climpred 76 | 77 | .. |pypi downloads| image:: https://pepy.tech/badge/climpred 78 | :alt: pypi downloads 79 | :target: https://pepy.tech/project/climpred 80 | 81 | .. |gallery| image:: https://img.shields.io/badge/climpred-examples-ed7b0e.svg 82 | :alt: climpred gallery 83 | :target: https://mybinder.org/v2/gh/pangeo-data/climpred/main?urlpath=lab%2Ftree%2Fdocs%2Fsource%2Fquick-start.ipynb 84 | 85 | .. |workshop| image:: https://img.shields.io/badge/climpred-workshop-f5a252 86 | :alt: climpred workshop 87 | :target: https://mybinder.org/v2/gh/bradyrx/climpred_workshop/master 88 | 89 | .. |cloud| image:: https://img.shields.io/badge/climpred-cloud_demo-f9c99a 90 | :alt: climpred cloud demo 91 | :target: https://github.com/aaronspring/climpred-cloud-demo 92 | 93 | .. |forks| image:: https://img.shields.io/github/forks/pangeo-data/climpred 94 | :alt: GitHub forks 95 | :target: https://github.com/pangeo-data/climpred/network/members 96 | 97 | .. |stars| image:: https://img.shields.io/github/stars/pangeo-data/climpred 98 | :alt: GitHub stars 99 | :target: https://github.com/pangeo-data/climpred/stargazers 100 | 101 | .. |issues| image:: https://img.shields.io/github/issues/pangeo-data/climpred 102 | :alt: GitHub issues 103 | :target: https://github.com/pangeo-data/climpred/issues 104 | 105 | .. |PRs| image:: https://img.shields.io/github/issues-pr/pangeo-data/climpred 106 | :alt: GitHub PRs 107 | :target: https://github.com/pangeo-data/climpred/pulls 108 | 109 | .. 110 | 111 | 112 | We are actively looking for new contributors for climpred! 113 | `Riley `_ moved to McKinsey's 114 | Climate Analytics team as a climate software engineer. 115 | `Aaron `_ moved to XING as a data scientist. 116 | We especially hope for python enthusiasts from seasonal, subseasonal or weather 117 | prediction community. In our past coding journey, collaborative coding, feedbacking 118 | issues and pull requests advanced our code and thinking about forecast verification 119 | more than we could have ever expected. 120 | Feel free to implement your own new feature or take a look at the 121 | `good first issue `_ 122 | tag in the issues. If you are interested in maintaining climpred, please ping us. 123 | 124 | Installation 125 | ============ 126 | 127 | You can install the latest release of ``climpred`` using ``pip`` or ``conda``: 128 | 129 | .. code-block:: bash 130 | 131 | python -m pip install climpred[complete] 132 | 133 | .. code-block:: bash 134 | 135 | conda install -c conda-forge climpred 136 | 137 | You can also install the bleeding edge (pre-release versions) by cloning this 138 | repository or installing directly from GitHub: 139 | 140 | .. code-block:: bash 141 | 142 | git clone https://github.com/pangeo-data/climpred.git 143 | cd climpred 144 | python -m pip install . --upgrade 145 | 146 | .. code-block:: bash 147 | 148 | pip install git+https://github.com/pangeo-data/climpred.git 149 | 150 | 151 | Documentation 152 | ============= 153 | 154 | Documentation is in development and can be found on readthedocs_. 155 | 156 | .. _readthedocs: https://climpred.readthedocs.io/en/latest/ 157 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/README_CI.md: -------------------------------------------------------------------------------- 1 | https://github.com/pydata/xarray/blob/main/asv_bench/benchmarks/README_CI.md 2 | -------------------------------------------------------------------------------- /asv_bench/benchmarks/__init__.py: -------------------------------------------------------------------------------- 1 | # https://github.com/pydata/xarray/blob/master/asv_bench/benchmarks/__init__.py 2 | import itertools 3 | import os 4 | 5 | import dask 6 | import numpy as np 7 | 8 | _counter = itertools.count() 9 | 10 | 11 | def parameterized(names, params): 12 | def decorator(func): 13 | func.param_names = names 14 | func.params = params 15 | return func 16 | 17 | return decorator 18 | 19 | 20 | def requires_dask(): 21 | try: 22 | import dask # noqa 23 | except ImportError: 24 | raise NotImplementedError 25 | 26 | 27 | def randn(shape, frac_nan=None, chunks=None, seed=0): 28 | rng = np.random.RandomState(seed) 29 | if chunks is None: 30 | x = rng.standard_normal(shape) 31 | else: 32 | import dask.array as da 33 | 34 | rng = da.random.RandomState(seed) 35 | x = rng.standard_normal(shape, chunks=chunks) 36 | 37 | if frac_nan is not None: 38 | inds = rng.choice(range(x.size), int(x.size * frac_nan)) 39 | x.flat[inds] = np.nan 40 | 41 | return x 42 | 43 | 44 | def randint(low, high=None, size=None, frac_minus=None, seed=0): 45 | rng = np.random.RandomState(seed) 46 | x = rng.randint(low, high, size) 47 | if frac_minus is not None: 48 | inds = rng.choice(range(x.size), int(x.size * frac_minus)) 49 | x.flat[inds] = -1 50 | 51 | return x 52 | 53 | 54 | def ensure_loaded(res): 55 | """Compute no lazy results.""" 56 | if dask.is_dask_collection(res): 57 | res = res.compute() 58 | return res 59 | 60 | 61 | def _skip_slow(): 62 | """ 63 | Use this function to skip slow or highly demanding tests. 64 | Use it as a `Class.setup` method or a `function.setup` attribute. 65 | Examples 66 | -------- 67 | >>> from . import _skip_slow 68 | >>> def time_something_slow(): 69 | ... pass 70 | ... 71 | >>> time_something.setup = _skip_slow 72 | """ 73 | if os.environ.get("ASV_SKIP_SLOW", "0") == "1": 74 | raise NotImplementedError("Skipping this test...") 75 | -------------------------------------------------------------------------------- /ci/install-upstream-wheels.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | conda uninstall -y --force \ 4 | numpy \ 5 | pandas \ 6 | matplotlib \ 7 | dask \ 8 | cftime \ 9 | nc-time-axis \ 10 | # bottleneck \ 11 | xarray \ 12 | xskillscore \ 13 | xclim \ 14 | bias_correction \ 15 | climpred 16 | 17 | 18 | # to limit the runtime of Upstream CI 19 | python -m pip install pytest-timeout 20 | python -m pip install \ 21 | -i https://pypi.anaconda.org/scipy-wheels-nightly/simple \ 22 | --no-deps \ 23 | --pre \ 24 | --upgrade \ 25 | numpy \ 26 | pandas 27 | python -m pip install \ 28 | -f https://7933911d6844c6c53a7d-47bd50c35cd79bd838daf386af554a83.ssl.cf2.rackcdn.com \ 29 | --no-deps \ 30 | --pre \ 31 | --upgrade \ 32 | matplotlib 33 | python -m pip install \ 34 | --no-deps \ 35 | --upgrade \ 36 | git+https://github.com/dask/dask \ 37 | git+https://github.com/Unidata/cftime \ 38 | git+https://github.com/SciTools/nc-time-axis \ 39 | git+https://github.com/pydata/xarray \ 40 | # git+https://github.com/pydata/bottleneck \ 41 | git+https://github.com/xarray-contrib/xskillscore \ 42 | git+https://github.com/xgcm/xrft \ 43 | git+https://github.com/pankajkarman/bias_correction 44 | python -m pip install --upgrade git+https://github.com/Ouranosinc/xclim 45 | -------------------------------------------------------------------------------- /ci/requirements/climpred-dev.yml: -------------------------------------------------------------------------------- 1 | name: climpred-dev 2 | channels: 3 | - conda-forge 4 | - nodefaults 5 | dependencies: 6 | - python >=3.9,<3.13 7 | # Documentation 8 | - myst-nb 9 | - nbstripout 10 | - sphinx 11 | - sphinx-book-theme 12 | - sphinx-copybutton 13 | - sphinxcontrib-bibtex 14 | - sphinxcontrib-napoleon 15 | - sphinxext-opengraph 16 | # IDE 17 | - jupyterlab 18 | - nb_conda_kernels # switch conda envs in jupyter 19 | - tqdm 20 | # Input/Output 21 | - netcdf4 22 | - pooch 23 | # Miscellaneous 24 | - cftime >=1.5.0 25 | # Numerics 26 | - numpy >=1.25.0 27 | - pandas 28 | - scipy 29 | - xarray >=2022.6.0 30 | # Package Management 31 | - asv 32 | - black ==24.4.2 33 | - coveralls 34 | - flake8 35 | - isort 36 | - mypy 37 | - pre-commit 38 | - pylint 39 | - pytest<8 40 | - pytest-cov 41 | - pytest-lazy-fixture 42 | - pytest-sugar 43 | - pytest-xdist 44 | # Performance 45 | - bottleneck 46 | - dask-core 47 | - numba 48 | # Regridding 49 | - esmpy =*=mpi* # Ensures MPI works with version of esmpy. 50 | - importlib-metadata <8.0.0 # Pin needed for esmpy compatibility. See: https://github.com/pangeo-data/xESMF/issues/374 51 | - xesmf 52 | # Statistics 53 | - bias_correction 54 | - eofs 55 | - esmtools >=1.1.3 56 | - xclim >=0.46.0 57 | - xrft 58 | - xskillscore >=0.0.20 59 | # Visualization 60 | - matplotlib-base 61 | - nc-time-axis>=1.4.0 62 | -------------------------------------------------------------------------------- /ci/requirements/docs.yml: -------------------------------------------------------------------------------- 1 | name: climpred-docs 2 | channels: 3 | - conda-forge 4 | - nodefaults 5 | dependencies: 6 | - python >=3.9,<3.10 7 | - cftime >=0.1.5 8 | - matplotlib-base 9 | - netcdf4 10 | - pip 11 | - pooch 12 | - xarray >=0.19.0,<=2022.10.0 13 | - xskillscore >=0.0.18 14 | # docs 15 | - jupyterlab 16 | - myst-nb 17 | - nbstripout 18 | - sphinx 19 | - sphinx-book-theme >=0.3.3 20 | - sphinx-copybutton 21 | - sphinxcontrib-bibtex 22 | - sphinxcontrib-napoleon 23 | - sphinxext-opengraph 24 | - tqdm 25 | # optionals 26 | - bias_correction 27 | - esmpy =*=mpi* # Ensures MPI works with version of esmpy. 28 | - esmtools 29 | - importlib-metadata <8.0.0 # Pin needed for esmpy compatibility. See: https://github.com/pangeo-data/xESMF/issues/374 30 | - nc-time-axis >=1.4.0 31 | - numba >=0.52 32 | - numpy >=1.25.0,<2.0.0 # Pin below v2.0.0 until xclim supports it. 33 | - xclim 34 | - xesmf 35 | - xrft 36 | -------------------------------------------------------------------------------- /ci/requirements/maximum-tests.yml: -------------------------------------------------------------------------------- 1 | name: climpred-maximum-tests 2 | channels: 3 | - conda-forge 4 | - nodefaults 5 | dependencies: 6 | - python >=3.9,<3.13 7 | - bias_correction 8 | - cftime >=1.5.0 9 | - coveralls 10 | - dask-core 11 | - eofs 12 | # - esmpy =*=mpi* # Ensures MPI works with version of esmpy. # Commented out for CI testing. 13 | - h5netcdf 14 | - importlib-metadata <8.0.0 # Pin needed for esmpy compatibility. See: https://github.com/pangeo-data/xESMF/issues/374 15 | - matplotlib-base 16 | - nc-time-axis >=1.4.0 17 | - numpy >=1.25.0,<2.0.0 # Pin below v2.0.0 until xclim supports it. 18 | - pip 19 | - pooch 20 | - pytest <8.0.0 21 | - pytest-cov 22 | - pytest-lazy-fixture 23 | - pytest-xdist 24 | - scipy 25 | - tqdm 26 | - xarray >=2022.6.0 27 | - xclim >=0.46 28 | - xesmf 29 | - xrft 30 | - xskillscore >=0.0.18 31 | -------------------------------------------------------------------------------- /ci/requirements/minimum-tests.yml: -------------------------------------------------------------------------------- 1 | name: climpred-minimum-tests 2 | channels: 3 | - conda-forge 4 | - nodefaults 5 | dependencies: 6 | - python >=3.9,<3.13 7 | - cftime >=1.5.0 8 | - coveralls 9 | - dask-core 10 | - h5netcdf 11 | - numpy >=1.25.0 12 | - pip 13 | - pooch 14 | - pytest <8.0.0 15 | - pytest-cov 16 | - pytest-lazy-fixture 17 | - pytest-xdist 18 | - scipy 19 | - tqdm 20 | - xarray >=0.19.0 21 | - xskillscore >=0.0.18 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 = source 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/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=source 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.http://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/source/api/climpred.bootstrap.bootstrap_compute.rst: -------------------------------------------------------------------------------- 1 | climpred.bootstrap.bootstrap\_compute 2 | ===================================== 3 | 4 | .. currentmodule:: climpred.bootstrap 5 | -------------------------------------------------------------------------------- /docs/source/api/climpred.bootstrap.bootstrap_hindcast.rst: -------------------------------------------------------------------------------- 1 | climpred.bootstrap.bootstrap\_hindcast 2 | ====================================== 3 | 4 | .. currentmodule:: climpred.bootstrap 5 | -------------------------------------------------------------------------------- /docs/source/api/climpred.bootstrap.bootstrap_perfect_model.rst: -------------------------------------------------------------------------------- 1 | climpred.bootstrap.bootstrap\_perfect\_model 2 | ============================================ 3 | 4 | .. currentmodule:: climpred.bootstrap 5 | -------------------------------------------------------------------------------- /docs/source/api/climpred.bootstrap.bootstrap_uninit_pm_ensemble_from_control_cftime.rst: -------------------------------------------------------------------------------- 1 | climpred.bootstrap.bootstrap\_uninit\_pm\_ensemble\_from\_control\_cftime 2 | ========================================================================= 3 | 4 | .. currentmodule:: climpred.bootstrap 5 | 6 | .. autofunction:: bootstrap_uninit_pm_ensemble_from_control_cftime 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.bootstrap.bootstrap_uninitialized_ensemble.rst: -------------------------------------------------------------------------------- 1 | climpred.bootstrap.bootstrap\_uninitialized\_ensemble 2 | ===================================================== 3 | 4 | .. currentmodule:: climpred.bootstrap 5 | 6 | .. autofunction:: bootstrap_uninitialized_ensemble 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.bootstrap.dpp_threshold.rst: -------------------------------------------------------------------------------- 1 | climpred.bootstrap.dpp\_threshold 2 | ================================= 3 | 4 | .. currentmodule:: climpred.bootstrap 5 | 6 | .. autofunction:: dpp_threshold 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.bootstrap.resample_skill_exclude_resample_dim_from_dim.rst: -------------------------------------------------------------------------------- 1 | climpred.bootstrap.resample\_skill\_exclude\_resample\_dim\_from\_dim 2 | ===================================================================== 3 | 4 | .. currentmodule:: climpred.bootstrap 5 | 6 | .. autofunction:: resample_skill_exclude_resample_dim_from_dim 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.bootstrap.resample_skill_loop.rst: -------------------------------------------------------------------------------- 1 | climpred.bootstrap.resample\_skill\_loop 2 | ======================================== 3 | 4 | .. currentmodule:: climpred.bootstrap 5 | 6 | .. autofunction:: resample_skill_loop 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.bootstrap.resample_skill_resample_before.rst: -------------------------------------------------------------------------------- 1 | climpred.bootstrap.resample\_skill\_resample\_before 2 | ==================================================== 3 | 4 | .. currentmodule:: climpred.bootstrap 5 | 6 | .. autofunction:: resample_skill_resample_before 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.bootstrap.varweighted_mean_period_threshold.rst: -------------------------------------------------------------------------------- 1 | climpred.bootstrap.varweighted\_mean\_period\_threshold 2 | ======================================================= 3 | 4 | .. currentmodule:: climpred.bootstrap 5 | 6 | .. autofunction:: varweighted_mean_period_threshold 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.HindcastEnsemble.__init__.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.HindcastEnsemble.\_\_init\_\_ 2 | ============================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: HindcastEnsemble.__init__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.HindcastEnsemble.add_observations.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.HindcastEnsemble.add\_observations 2 | =================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: HindcastEnsemble.add_observations 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.HindcastEnsemble.add_uninitialized.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.HindcastEnsemble.add\_uninitialized 2 | ==================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: HindcastEnsemble.add_uninitialized 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.HindcastEnsemble.bootstrap.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.HindcastEnsemble.bootstrap 2 | =========================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: HindcastEnsemble.bootstrap 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.HindcastEnsemble.generate_uninitialized.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.HindcastEnsemble.generate\_uninitialized 2 | ========================================================= 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: HindcastEnsemble.generate_uninitialized 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.HindcastEnsemble.get_initialized.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.HindcastEnsemble.get\_initialized 2 | ================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: HindcastEnsemble.get_initialized 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.HindcastEnsemble.get_observations.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.HindcastEnsemble.get\_observations 2 | =================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: HindcastEnsemble.get_observations 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.HindcastEnsemble.get_uninitialized.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.HindcastEnsemble.get\_uninitialized 2 | ==================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: HindcastEnsemble.get_uninitialized 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.HindcastEnsemble.plot.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.HindcastEnsemble.plot 2 | ====================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: HindcastEnsemble.plot 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.HindcastEnsemble.plot_alignment.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.HindcastEnsemble.plot\_alignment 2 | ================================================= 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: HindcastEnsemble.plot_alignment 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.HindcastEnsemble.remove_bias.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.HindcastEnsemble.remove\_bias 2 | ============================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: HindcastEnsemble.remove_bias 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.HindcastEnsemble.remove_seasonality.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.HindcastEnsemble.remove\_seasonality 2 | ===================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: HindcastEnsemble.remove_seasonality 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.HindcastEnsemble.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.HindcastEnsemble 2 | ================================= 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. autoclass:: HindcastEnsemble 7 | 8 | 9 | .. automethod:: __init__ 10 | 11 | 12 | .. rubric:: Methods 13 | 14 | .. autosummary:: 15 | 16 | ~HindcastEnsemble.__init__ 17 | ~HindcastEnsemble.add_observations 18 | ~HindcastEnsemble.add_uninitialized 19 | ~HindcastEnsemble.bootstrap 20 | ~HindcastEnsemble.get_initialized 21 | ~HindcastEnsemble.get_observations 22 | ~HindcastEnsemble.get_uninitialized 23 | ~HindcastEnsemble.plot 24 | ~HindcastEnsemble.remove_bias 25 | ~HindcastEnsemble.smooth 26 | ~HindcastEnsemble.verify 27 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.HindcastEnsemble.smooth.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.HindcastEnsemble.smooth 2 | ======================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: HindcastEnsemble.smooth 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.HindcastEnsemble.verify.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.HindcastEnsemble.verify 2 | ======================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: HindcastEnsemble.verify 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PerfectModelEnsemble.__init__.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PerfectModelEnsemble.\_\_init\_\_ 2 | ================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PerfectModelEnsemble.__init__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PerfectModelEnsemble.add_control.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PerfectModelEnsemble.add\_control 2 | ================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PerfectModelEnsemble.add_control 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PerfectModelEnsemble.bootstrap.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PerfectModelEnsemble.bootstrap 2 | =============================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PerfectModelEnsemble.bootstrap 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PerfectModelEnsemble.generate_uninitialized.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PerfectModelEnsemble.generate\_uninitialized 2 | ============================================================= 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PerfectModelEnsemble.generate_uninitialized 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PerfectModelEnsemble.get_control.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PerfectModelEnsemble.get\_control 2 | ================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PerfectModelEnsemble.get_control 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PerfectModelEnsemble.get_initialized.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PerfectModelEnsemble.get\_initialized 2 | ====================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PerfectModelEnsemble.get_initialized 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PerfectModelEnsemble.get_uninitialized.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PerfectModelEnsemble.get\_uninitialized 2 | ======================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PerfectModelEnsemble.get_uninitialized 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PerfectModelEnsemble.plot.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PerfectModelEnsemble.plot 2 | ========================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PerfectModelEnsemble.plot 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PerfectModelEnsemble.remove_seasonality.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PerfectModelEnsemble.remove\_seasonality 2 | ========================================================= 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PerfectModelEnsemble.remove_seasonality 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PerfectModelEnsemble.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PerfectModelEnsemble 2 | ===================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. autoclass:: PerfectModelEnsemble 7 | 8 | 9 | .. automethod:: __init__ 10 | 11 | 12 | .. rubric:: Methods 13 | 14 | .. autosummary:: 15 | 16 | ~PerfectModelEnsemble.__init__ 17 | ~PerfectModelEnsemble.add_control 18 | ~PerfectModelEnsemble.bootstrap 19 | ~PerfectModelEnsemble.generate_uninitialized 20 | ~PerfectModelEnsemble.get_control 21 | ~PerfectModelEnsemble.get_initialized 22 | ~PerfectModelEnsemble.get_uninitialized 23 | ~PerfectModelEnsemble.plot 24 | ~PerfectModelEnsemble.smooth 25 | ~PerfectModelEnsemble.verify 26 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PerfectModelEnsemble.smooth.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PerfectModelEnsemble.smooth 2 | ============================================ 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PerfectModelEnsemble.smooth 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PerfectModelEnsemble.verify.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PerfectModelEnsemble.verify 2 | ============================================ 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PerfectModelEnsemble.verify 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.__add__.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.\_\_add\_\_ 2 | =============================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PredictionEnsemble.__add__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.__contains__.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.\_\_contains\_\_ 2 | ==================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PredictionEnsemble.__contains__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.__delitem__.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.\_\_delitem\_\_ 2 | =================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PredictionEnsemble.__delitem__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.__getattr__.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.\_\_getattr\_\_ 2 | =================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PredictionEnsemble.__getattr__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.__getitem__.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.\_\_getitem\_\_ 2 | =================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PredictionEnsemble.__getitem__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.__init__.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.\_\_init\_\_ 2 | ================================================ 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PredictionEnsemble.__init__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.__iter__.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.\_\_iter\_\_ 2 | ================================================ 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PredictionEnsemble.__iter__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.__len__.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.\_\_len\_\_ 2 | =============================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PredictionEnsemble.__len__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.__mul__.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.\_\_mul\_\_ 2 | =============================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PredictionEnsemble.__mul__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.__sub__.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.\_\_sub\_\_ 2 | =============================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PredictionEnsemble.__sub__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.__truediv__.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.\_\_truediv\_\_ 2 | =================================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PredictionEnsemble.__truediv__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.chunks.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.chunks 2 | ========================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. autoproperty:: PredictionEnsemble.chunks 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.chunksizes.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.chunksizes 2 | ============================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. autoproperty:: PredictionEnsemble.chunksizes 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.coords.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.coords 2 | ========================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. autoproperty:: PredictionEnsemble.coords 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.data_vars.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.data\_vars 2 | ============================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. autoproperty:: PredictionEnsemble.data_vars 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.dims.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.dims 2 | ======================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. autoproperty:: PredictionEnsemble.dims 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.equals.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.equals 2 | ========================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PredictionEnsemble.equals 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.identical.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.identical 2 | ============================================= 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PredictionEnsemble.identical 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.nbytes.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.nbytes 2 | ========================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. autoproperty:: PredictionEnsemble.nbytes 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.plot.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.plot 2 | ======================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. automethod:: PredictionEnsemble.plot 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble 2 | =================================== 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. autoclass:: PredictionEnsemble 7 | 8 | 9 | .. automethod:: __init__ 10 | 11 | 12 | .. rubric:: Methods 13 | 14 | .. autosummary:: 15 | 16 | ~PredictionEnsemble.__init__ 17 | ~PredictionEnsemble.equals 18 | ~PredictionEnsemble.get_initialized 19 | ~PredictionEnsemble.get_uninitialized 20 | ~PredictionEnsemble.identical 21 | ~PredictionEnsemble.plot 22 | ~PredictionEnsemble.remove_seasonality 23 | ~PredictionEnsemble.smooth 24 | 25 | 26 | 27 | 28 | 29 | .. rubric:: Attributes 30 | 31 | .. autosummary:: 32 | 33 | ~PredictionEnsemble.chunks 34 | ~PredictionEnsemble.chunksizes 35 | ~PredictionEnsemble.coords 36 | ~PredictionEnsemble.data_vars 37 | ~PredictionEnsemble.dims 38 | ~PredictionEnsemble.mathType 39 | ~PredictionEnsemble.nbytes 40 | ~PredictionEnsemble.sizes 41 | -------------------------------------------------------------------------------- /docs/source/api/climpred.classes.PredictionEnsemble.sizes.rst: -------------------------------------------------------------------------------- 1 | climpred.classes.PredictionEnsemble.sizes 2 | ========================================= 3 | 4 | .. currentmodule:: climpred.classes 5 | 6 | .. autoproperty:: PredictionEnsemble.sizes 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.comparisons.Comparison.__init__.rst: -------------------------------------------------------------------------------- 1 | climpred.comparisons.Comparison.\_\_init\_\_ 2 | ============================================= 3 | 4 | .. currentmodule:: climpred.comparisons 5 | 6 | .. automethod:: Comparison.__init__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.comparisons.Comparison.__repr__.rst: -------------------------------------------------------------------------------- 1 | climpred.comparisons.Comparison.\_\_repr\_\_ 2 | ============================================= 3 | 4 | .. currentmodule:: climpred.comparisons 5 | 6 | .. automethod:: Comparison.__repr__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.comparisons.Comparison.rst: -------------------------------------------------------------------------------- 1 | climpred.comparisons.Comparison 2 | ================================ 3 | 4 | .. currentmodule:: climpred.comparisons 5 | 6 | .. autoclass:: Comparison 7 | 8 | 9 | .. automethod:: __init__ 10 | 11 | 12 | .. rubric:: Methods 13 | 14 | .. autosummary:: 15 | 16 | ~Comparison.__init__ 17 | -------------------------------------------------------------------------------- /docs/source/api/climpred.comparisons._e2c.rst: -------------------------------------------------------------------------------- 1 | climpred.comparisons.\_e2c 2 | =========================== 3 | 4 | .. currentmodule:: climpred.comparisons 5 | 6 | .. autofunction:: _e2c 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.comparisons._e2o.rst: -------------------------------------------------------------------------------- 1 | climpred.comparisons.\_e2o 2 | =========================== 3 | 4 | .. currentmodule:: climpred.comparisons 5 | 6 | .. autofunction:: _e2o 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.comparisons._m2c.rst: -------------------------------------------------------------------------------- 1 | climpred.comparisons.\_m2c 2 | =========================== 3 | 4 | .. currentmodule:: climpred.comparisons 5 | 6 | .. autofunction:: _m2c 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.comparisons._m2e.rst: -------------------------------------------------------------------------------- 1 | climpred.comparisons.\_m2e 2 | =========================== 3 | 4 | .. currentmodule:: climpred.comparisons 5 | 6 | .. autofunction:: _m2e 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.comparisons._m2m.rst: -------------------------------------------------------------------------------- 1 | climpred.comparisons.\_m2m 2 | =========================== 3 | 4 | .. currentmodule:: climpred.comparisons 5 | 6 | .. autofunction:: _m2m 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.comparisons._m2o.rst: -------------------------------------------------------------------------------- 1 | climpred.comparisons.\_m2o 2 | =========================== 3 | 4 | .. currentmodule:: climpred.comparisons 5 | 6 | .. autofunction:: _m2o 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.graphics.plot_bootstrapped_skill_over_leadyear.rst: -------------------------------------------------------------------------------- 1 | climpred.graphics.plot\_bootstrapped\_skill\_over\_leadyear 2 | =========================================================== 3 | 4 | .. currentmodule:: climpred.graphics 5 | 6 | .. autofunction:: plot_bootstrapped_skill_over_leadyear 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.graphics.plot_ensemble_perfect_model.rst: -------------------------------------------------------------------------------- 1 | climpred.graphics.plot\_ensemble\_perfect\_model 2 | ================================================ 3 | 4 | .. currentmodule:: climpred.graphics 5 | 6 | .. autofunction:: plot_ensemble_perfect_model 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.graphics.plot_lead_timeseries_hindcast.rst: -------------------------------------------------------------------------------- 1 | climpred.graphics.plot\_lead\_timeseries\_hindcast 2 | ================================================== 3 | 4 | .. currentmodule:: climpred.graphics 5 | 6 | .. autofunction:: plot_lead_timeseries_hindcast 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.horizon.horizon.rst: -------------------------------------------------------------------------------- 1 | climpred.horizon.horizon 2 | ======================== 3 | 4 | .. currentmodule:: climpred.horizon 5 | 6 | .. autofunction:: horizon 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics.Metric.__init__.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.Metric.\_\_init\_\_ 2 | ===================================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. automethod:: Metric.__init__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics.Metric.__repr__.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.Metric.\_\_repr\_\_ 2 | ===================================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. automethod:: Metric.__repr__ 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics.Metric.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.Metric 2 | ======================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autoclass:: Metric 7 | 8 | 9 | .. automethod:: __init__ 10 | 11 | 12 | .. rubric:: Methods 13 | 14 | .. autosummary:: 15 | 16 | ~Metric.__init__ 17 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._bias_slope.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_bias\_slope 2 | ============================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _bias_slope 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._brier_score.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_brier\_score 2 | =============================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _brier_score 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._conditional_bias.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_conditional\_bias 2 | ==================================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _conditional_bias 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._contingency.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_contingency 2 | ============================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _contingency 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._crps.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_crps 2 | ======================= 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _crps 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._crpss.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_crpss 2 | ======================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _crpss 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._crpss_es.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_crpss\_es 2 | ============================ 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _crpss_es 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._discrimination.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_discrimination 2 | ================================= 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _discrimination 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._effective_sample_size.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_effective\_sample\_size 2 | ========================================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _effective_sample_size 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._get_norm_factor.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_get\_norm\_factor 2 | ==================================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _get_norm_factor 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._less.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_less 2 | ======================= 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _less 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._mae.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_mae 2 | ====================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _mae 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._mape.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_mape 2 | ======================= 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _mape 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._me.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_me 2 | ===================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _me 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._median_absolute_error.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_median\_absolute\_error 2 | ========================================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _median_absolute_error 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._mse.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_mse 2 | ====================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _mse 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._msess.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_msess 2 | ======================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _msess 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._msess_murphy.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_msess\_murphy 2 | ================================ 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _msess_murphy 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._mul_bias.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_mul\_bias 2 | ============================ 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _mul_bias 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._nmae.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_nmae 2 | ======================= 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _nmae 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._nmse.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_nmse 2 | ======================= 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _nmse 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._nrmse.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_nrmse 2 | ======================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _nrmse 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._pearson_r.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_pearson\_r 2 | ============================= 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _pearson_r 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._pearson_r_eff_p_value.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_pearson\_r\_eff\_p\_value 2 | ============================================ 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _pearson_r_eff_p_value 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._pearson_r_p_value.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_pearson\_r\_p\_value 2 | ======================================= 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _pearson_r_p_value 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._rank_histogram.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_rank\_histogram 2 | ================================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _rank_histogram 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._reliability.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_reliability 2 | ============================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _reliability 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._rmse.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_rmse 2 | ======================= 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _rmse 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._roc.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_roc 2 | ====================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _roc 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._rps.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_rps 2 | ====================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _rps 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._smape.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_smape 2 | ======================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _smape 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._spearman_r.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_spearman\_r 2 | ============================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _spearman_r 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._spearman_r_eff_p_value.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_spearman\_r\_eff\_p\_value 2 | ============================================= 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _spearman_r_eff_p_value 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._spearman_r_p_value.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_spearman\_r\_p\_value 2 | ======================================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _spearman_r_p_value 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._spread.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_spread 2 | ========================= 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _spread 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._std_ratio.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_std\_ratio 2 | ============================= 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _std_ratio 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._threshold_brier_score.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_threshold\_brier\_score 2 | ========================================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _threshold_brier_score 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._uacc.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_uacc 2 | ======================= 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _uacc 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.metrics._unconditional_bias.rst: -------------------------------------------------------------------------------- 1 | climpred.metrics.\_unconditional\_bias 2 | ====================================== 3 | 4 | .. currentmodule:: climpred.metrics 5 | 6 | .. autofunction:: _unconditional_bias 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.options.set_options.rst: -------------------------------------------------------------------------------- 1 | climpred.options.set\_options 2 | ============================= 3 | 4 | .. currentmodule:: climpred.options 5 | 6 | .. autoclass:: set_options 7 | 8 | 9 | .. automethod:: __init__ 10 | 11 | 12 | .. rubric:: Methods 13 | 14 | .. autosummary:: 15 | 16 | ~set_options.__init__ 17 | -------------------------------------------------------------------------------- /docs/source/api/climpred.prediction.compute_hindcast.rst: -------------------------------------------------------------------------------- 1 | climpred.prediction.compute\_hindcast 2 | ===================================== 3 | 4 | .. currentmodule:: climpred.prediction 5 | -------------------------------------------------------------------------------- /docs/source/api/climpred.prediction.compute_perfect_model.rst: -------------------------------------------------------------------------------- 1 | climpred.prediction.compute\_perfect\_model 2 | =========================================== 3 | 4 | .. currentmodule:: climpred.prediction 5 | 6 | .. autofunction:: compute_perfect_model 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.preprocessing.mpi.get_path.rst: -------------------------------------------------------------------------------- 1 | climpred.preprocessing.mpi.get\_path 2 | ==================================== 3 | 4 | .. currentmodule:: climpred.preprocessing.mpi 5 | 6 | .. autofunction:: get_path 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.preprocessing.shared.load_hindcast.rst: -------------------------------------------------------------------------------- 1 | climpred.preprocessing.shared.load\_hindcast 2 | ============================================ 3 | 4 | .. currentmodule:: climpred.preprocessing.shared 5 | 6 | .. autofunction:: load_hindcast 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.preprocessing.shared.rename_SLM_to_climpred_dims.rst: -------------------------------------------------------------------------------- 1 | climpred.preprocessing.shared.rename\_SLM\_to\_climpred\_dims 2 | ============================================================= 3 | 4 | .. currentmodule:: climpred.preprocessing.shared 5 | 6 | .. autofunction:: rename_SLM_to_climpred_dims 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.preprocessing.shared.rename_to_climpred_dims.rst: -------------------------------------------------------------------------------- 1 | climpred.preprocessing.shared.rename\_to\_climpred\_dims 2 | ======================================================== 3 | 4 | .. currentmodule:: climpred.preprocessing.shared 5 | 6 | .. autofunction:: rename_to_climpred_dims 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.preprocessing.shared.set_integer_time_axis.rst: -------------------------------------------------------------------------------- 1 | climpred.preprocessing.shared.set\_integer\_time\_axis 2 | ====================================================== 3 | 4 | .. currentmodule:: climpred.preprocessing.shared 5 | 6 | .. autofunction:: set_integer_time_axis 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.reference.compute_climatology.rst: -------------------------------------------------------------------------------- 1 | climpred.reference.compute\_climatology 2 | ======================================= 3 | 4 | .. currentmodule:: climpred.reference 5 | 6 | .. autofunction:: compute_climatology 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.reference.compute_persistence.rst: -------------------------------------------------------------------------------- 1 | climpred.reference.compute\_persistence 2 | ======================================= 3 | 4 | .. currentmodule:: climpred.reference 5 | 6 | .. autofunction:: compute_persistence 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.reference.compute_persistence_from_first_lead.rst: -------------------------------------------------------------------------------- 1 | climpred.reference.compute\_persistence\_from\_first\_lead 2 | ========================================================== 3 | 4 | .. currentmodule:: climpred.reference 5 | 6 | .. autofunction:: compute_persistence_from_first_lead 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.reference.compute_uninitialized.rst: -------------------------------------------------------------------------------- 1 | climpred.reference.compute\_uninitialized 2 | ========================================= 3 | 4 | .. currentmodule:: climpred.reference 5 | 6 | .. autofunction:: compute_uninitialized 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.smoothing.spatial_smoothing_xesmf.rst: -------------------------------------------------------------------------------- 1 | climpred.smoothing.spatial\_smoothing\_xesmf 2 | ============================================ 3 | 4 | .. currentmodule:: climpred.smoothing 5 | 6 | .. autofunction:: spatial_smoothing_xesmf 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.smoothing.temporal_smoothing.rst: -------------------------------------------------------------------------------- 1 | climpred.smoothing.temporal\_smoothing 2 | ====================================== 3 | 4 | .. currentmodule:: climpred.smoothing 5 | 6 | .. autofunction:: temporal_smoothing 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.stats.decorrelation_time.rst: -------------------------------------------------------------------------------- 1 | climpred.stats.decorrelation\_time 2 | ================================== 3 | 4 | .. currentmodule:: climpred.stats 5 | 6 | .. autofunction:: decorrelation_time 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.stats.dpp.rst: -------------------------------------------------------------------------------- 1 | climpred.stats.dpp 2 | ================== 3 | 4 | .. currentmodule:: climpred.stats 5 | 6 | .. autofunction:: dpp 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.stats.rm_poly.rst: -------------------------------------------------------------------------------- 1 | climpred.stats.rm\_poly 2 | ======================= 3 | 4 | .. currentmodule:: climpred.stats 5 | 6 | .. autofunction:: rm_poly 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.stats.rm_trend.rst: -------------------------------------------------------------------------------- 1 | climpred.stats.rm\_trend 2 | ======================== 3 | 4 | .. currentmodule:: climpred.stats 5 | 6 | .. autofunction:: rm_trend 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.stats.varweighted_mean_period.rst: -------------------------------------------------------------------------------- 1 | climpred.stats.varweighted\_mean\_period 2 | ======================================== 3 | 4 | .. currentmodule:: climpred.stats 5 | 6 | .. autofunction:: varweighted_mean_period 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.tutorial.load_dataset.rst: -------------------------------------------------------------------------------- 1 | climpred.tutorial.load\_dataset 2 | =============================== 3 | 4 | .. currentmodule:: climpred.tutorial 5 | 6 | .. autofunction:: load_dataset 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.utils.convert_init_lead_to_valid_time_lead.rst: -------------------------------------------------------------------------------- 1 | climpred.utils.convert_init_lead_to_valid_time_lead 2 | =================================================== 3 | 4 | .. currentmodule:: climpred.utils 5 | 6 | .. autofunction:: convert_init_lead_to_valid_time_lead 7 | -------------------------------------------------------------------------------- /docs/source/api/climpred.utils.convert_valid_time_lead_to_init_lead.rst: -------------------------------------------------------------------------------- 1 | climpred.utils.convert_valid_time_lead_to_init_lead 2 | =================================================== 3 | 4 | .. currentmodule:: climpred.utils 5 | 6 | .. autofunction:: convert_valid_time_lead_to_init_lead 7 | -------------------------------------------------------------------------------- /docs/source/changelog.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../CHANGELOG.rst 2 | -------------------------------------------------------------------------------- /docs/source/code_of_conduct.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | Code of Conduct 3 | =============== 4 | 5 | Our Pledge 6 | ---------- 7 | 8 | In the interest of fostering an open and welcoming environment, we as 9 | contributors and maintainers pledge to making participation in our project and 10 | our community a harassment-free experience for everyone, regardless of age, body 11 | size, disability, ethnicity, sex characteristics, gender identity and expression, 12 | level of experience, education, socio-economic status, nationality, personal 13 | appearance, race, religion, or sexual identity and orientation. 14 | 15 | Our Standards 16 | ------------- 17 | 18 | Examples of behavior that contributes to creating a positive environment 19 | include: 20 | 21 | - Using welcoming and inclusive language 22 | - Being respectful of differing viewpoints and experiences 23 | - Gracefully accepting constructive criticism 24 | - Focusing on what is best for the community 25 | - Showing empathy towards other community members 26 | 27 | Examples of unacceptable behavior by participants include: 28 | 29 | - The use of sexualized language or imagery and unwelcome sexual attention or 30 | advances 31 | - Trolling, insulting/derogatory comments, and personal or political attacks 32 | - Public or private harassment 33 | - Publishing others' private information, such as a physical or electronic 34 | address, without explicit permission 35 | - Other conduct which could reasonably be considered inappropriate in a 36 | professional setting 37 | 38 | Our Responsibilities 39 | -------------------- 40 | 41 | Project maintainers are responsible for clarifying the standards of acceptable 42 | behavior and are expected to take appropriate and fair corrective action in 43 | response to any instances of unacceptable behavior. 44 | 45 | Project maintainers have the right and responsibility to remove, edit, or 46 | reject comments, commits, code, wiki edits, issues, and other contributions 47 | that are not aligned to this Code of Conduct, or to ban temporarily or 48 | permanently any contributor for other behaviors that they deem inappropriate, 49 | threatening, offensive, or harmful. 50 | 51 | Scope 52 | ----- 53 | 54 | This Code of Conduct applies both within project spaces and in public spaces 55 | when an individual is representing the project or its community. Examples of 56 | representing a project or community include using an official project e-mail 57 | address, posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. Representation of a project may be 59 | further defined and clarified by project maintainers. 60 | 61 | Enforcement 62 | ----------- 63 | 64 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 65 | reported by contacting the project team. 66 | All complaints will be reviewed and investigated and will result in a response that 67 | is deemed necessary and appropriate to the circumstances. The project team is 68 | obligated to maintain confidentiality with regard to the reporter of an incident. 69 | Further details of specific enforcement policies may be posted separately. 70 | 71 | Project maintainers who do not follow or enforce the Code of Conduct in good 72 | faith may face temporary or permanent repercussions as determined by other 73 | members of the project's leadership. 74 | 75 | Attribution 76 | ----------- 77 | 78 | This Code of Conduct is adapted from the `Contributor Covenant homepage `__, and `xgcm `__. For answers to common questions about this code of conduct, see `Contributor Covenant `__. 79 | -------------------------------------------------------------------------------- /docs/source/contributors.rst: -------------------------------------------------------------------------------- 1 | ************ 2 | Contributors 3 | ************ 4 | 5 | .. note:: 6 | We are actively looking for new contributors for climpred! Riley moved to McKinsey's 7 | Climate Analytics team. Aaron is finishing his PhD, but will stay in academia. 8 | We especially hope for python enthusiasts from seasonal, subseasonal or weather 9 | prediction community. In our past coding journey, collaborative coding, feedbacking 10 | issues and pull requests advanced our code and thinking about forecast verification 11 | more than we could have ever expected. 12 | 13 | `Aaron`_ can provide guidance on 14 | implementing new features into climpred. Feel free to implement 15 | your own new feature or take a look at the `good first issue`_ tag in the issues. 16 | Please reach out to us via `gitter `_. 17 | 18 | Core Developers 19 | =============== 20 | * Aaron Spring (`github `__) 21 | * Riley X. Brady (`github `__) 22 | 23 | Contributors 24 | ============ 25 | * Anderson Banihirwe (`github `__) 26 | * Ray Bell (`github `__) 27 | * Trevor Gamblin (`github `__) 28 | * Andrew Huang (`github `__) 29 | * Kathy Pegion (`github `__) 30 | * Trevor James Smith (`github `__) 31 | 32 | For a list of all the contributions, see the GitHub `contribution graph`_. 33 | 34 | .. _Aaron: https://github.com/aaronspring/ 35 | .. _contribution graph: https://github.com/pangeo-data/climpred/graphs/contributors 36 | .. _good first issue: https://github.com/pangeo-data/climpred/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22 37 | -------------------------------------------------------------------------------- /docs/source/examples.rst: -------------------------------------------------------------------------------- 1 | Examples 2 | ######## 3 | 4 | .. note:: 5 | 6 | Please use the ``climpred-dev`` environment 7 | 8 | .. code-block:: bash 9 | 10 | conda env create -f ci/requirements/climpred-dev.yml 11 | 12 | to ensure that all dependencies are installed to complete all example 13 | notebooks listed here. 14 | 15 | 16 | Numerical Weather Prediction 17 | ============================ 18 | .. toctree:: 19 | :maxdepth: 1 20 | 21 | examples/NWP/NWP_GEFS_6h_forecasts.ipynb 22 | examples/NWP/Herbie.ipynb 23 | 24 | 25 | Subseasonal 26 | =========== 27 | .. toctree:: 28 | :maxdepth: 1 29 | 30 | examples/subseasonal/daily-subx-example.ipynb 31 | examples/subseasonal/daily-S2S-IRIDL.ipynb 32 | examples/subseasonal/weekly-subx-example.ipynb 33 | examples/subseasonal/daily-S2S-ECMWF.ipynb 34 | 35 | 36 | Monthly and Seasonal 37 | ==================== 38 | .. toctree:: 39 | :maxdepth: 1 40 | 41 | examples/monseas/monthly-enso-subx-example.ipynb 42 | examples/monseas/seasonal-enso-subx-example.ipynb 43 | 44 | 45 | Decadal 46 | ======= 47 | .. toctree:: 48 | :maxdepth: 1 49 | 50 | examples/decadal/perfect-model-predictability-demo.ipynb 51 | examples/decadal/tropical-pacific-ssts.ipynb 52 | examples/decadal/diagnose-potential-predictability.ipynb 53 | examples/decadal/Significance.ipynb 54 | examples/decadal/verify_dim_implications.ipynb 55 | 56 | 57 | Misc 58 | ==== 59 | .. toctree:: 60 | :maxdepth: 1 61 | 62 | examples/misc/climpred_gpu.ipynb 63 | examples/misc/setup_your_own_data.ipynb 64 | -------------------------------------------------------------------------------- /docs/source/examples/NWP/Herbie.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "9b39af99-1bb7-4fff-be94-85de8dcb7fb8", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "!pip install herbie-data --quiet" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "id": "descending-disease", 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "# linting\n", 21 | "%load_ext nb_black\n", 22 | "%load_ext lab_black" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "id": "smart-knock", 28 | "metadata": {}, 29 | "source": [ 30 | "# Skill from ECMWF downloaded with `herbie`\n", 31 | "\n", 32 | "[`herbie`](https://herbie.readthedocs.io/en/latest/user_guide/_tutorial_notebooks/fast.html) downloads forecasts data easily. The resulting datasets is out-of-the-box compatible with `climpred`." 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "id": "insured-feeding", 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "import xarray as xr\n", 43 | "import numpy as np\n", 44 | "\n", 45 | "import climpred # forecast verification" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "id": "288c9453-6daa-4ca7-80ba-ef6457e22740", 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "from herbie import Herbie\n", 56 | "\n", 57 | "H = Herbie(date=\"2022-01-27 00:00\", model=\"ecmwf\", product=\"enfo\", fxx=24 * 1)\n", 58 | "ds = H.xarray(\":2t:\")\n", 59 | "ds" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": null, 65 | "id": "6b10672b-5374-431d-9b3e-c08c0334aac2", 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "# take the first with multiple members as forecast\n", 70 | "init = ds[0][[\"t2m\"]]" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "id": "d537fac9-5a4e-4536-92a3-60ff52818ecf", 77 | "metadata": {}, 78 | "outputs": [], 79 | "source": [ 80 | "H = Herbie(date=\"2022-01-28 00:00\", model=\"ecmwf\", product=\"enfo\", fxx=0)\n", 81 | "ds = H.xarray(\":2t:\")\n", 82 | "ds" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "id": "b09be2e9-a7a2-4ab2-9222-49a06524976c", 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "# take first and make ensemble member mean as observations\n", 93 | "obs = ds[0].mean(\"number\").drop([\"step\", \"valid_time\"]).expand_dims(\"time\")[[\"t2m\"]]" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "id": "molecular-chuck", 99 | "metadata": {}, 100 | "source": [ 101 | "## Forecast skill verification\n", 102 | "\n", 103 | "Using using {py:class}`.HindcastEnsemble`.\n", 104 | "\n", 105 | "`climpred` expects `init`, `lead` and optional `member` as dimensions, see [setting-up-your-dataset](setting-up-data.html#setting-up-your-dataset). Existing dimensions are renamed automatically if CF `standard_names` match." 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "id": "7aa2554a-a9c3-4a8f-a153-ada1d2d71d20", 112 | "metadata": {}, 113 | "outputs": [], 114 | "source": [ 115 | "hindcast = climpred.HindcastEnsemble(\n", 116 | " init.expand_dims([\"time\", \"step\"])\n", 117 | ").add_observations(obs)\n", 118 | "\n", 119 | "hindcast" 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": null, 125 | "id": "conceptual-richmond", 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "skill = hindcast.verify(\n", 130 | " metric=\"crps\", comparison=\"m2o\", dim=[\"init\", \"member\"], alignment=\"same_init\"\n", 131 | ")" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": null, 137 | "id": "colored-luther", 138 | "metadata": {}, 139 | "outputs": [], 140 | "source": [ 141 | "skill.t2m.plot(robust=True)" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": null, 147 | "id": "cdc2e81e-6a09-4505-98b0-0ea59c827f8b", 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [] 151 | } 152 | ], 153 | "metadata": { 154 | "kernelspec": { 155 | "display_name": "Python 3 (ipykernel)", 156 | "language": "python", 157 | "name": "python3" 158 | }, 159 | "language_info": { 160 | "codemirror_mode": { 161 | "name": "ipython", 162 | "version": 3 163 | }, 164 | "file_extension": ".py", 165 | "mimetype": "text/x-python", 166 | "name": "python", 167 | "nbconvert_exporter": "python", 168 | "pygments_lexer": "ipython3", 169 | "version": "3.10.8" 170 | } 171 | }, 172 | "nbformat": 4, 173 | "nbformat_minor": 5 174 | } 175 | -------------------------------------------------------------------------------- /docs/source/helpful-links.rst: -------------------------------------------------------------------------------- 1 | ************* 2 | Helpful Links 3 | ************* 4 | 5 | We hope to curate in the ``climpred`` documentation a comprehensive report of 6 | terminology, best practices, analysis methods, etc. in the prediction community. 7 | Here we suggest other resources for initialized prediction of the Earth system to round 8 | out the information provided in our documentation. 9 | 10 | Forecast Verification 11 | ##################### 12 | 13 | * `CAWCR Forecast Verification Overview `_: 14 | A nice overview of forecast verification, including a suite of metrics and their 15 | derivation. 16 | -------------------------------------------------------------------------------- /docs/source/images/alignment_plots/maximize_alignment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangeo-data/climpred/57681b8c2d92e5c80d4b98e382c39063ca397da5/docs/source/images/alignment_plots/maximize_alignment.png -------------------------------------------------------------------------------- /docs/source/images/alignment_plots/same_inits_alignment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangeo-data/climpred/57681b8c2d92e5c80d4b98e382c39063ca397da5/docs/source/images/alignment_plots/same_inits_alignment.png -------------------------------------------------------------------------------- /docs/source/images/alignment_plots/same_verifs_alignment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangeo-data/climpred/57681b8c2d92e5c80d4b98e382c39063ca397da5/docs/source/images/alignment_plots/same_verifs_alignment.png -------------------------------------------------------------------------------- /docs/source/images/climpred-logo-inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangeo-data/climpred/57681b8c2d92e5c80d4b98e382c39063ca397da5/docs/source/images/climpred-logo-inverse.png -------------------------------------------------------------------------------- /docs/source/images/climpred-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangeo-data/climpred/57681b8c2d92e5c80d4b98e382c39063ca397da5/docs/source/images/climpred-logo.png -------------------------------------------------------------------------------- /docs/source/initialized-datasets.rst: -------------------------------------------------------------------------------- 1 | ******************** 2 | Initialized Datasets 3 | ******************** 4 | 5 | Probably the hardest part in working with ``climpred`` is getting the ``initialized`` 6 | dataset complying to the expectations and data model of ``climpred``. 7 | For names, data types and conventions of :py:class:`xarray.Dataset` dimensions and 8 | coordinates, please refer to `Setting up your Dataset `_. 9 | 10 | Here, we list publicly available initialized datasets and corresponding ``climpred`` 11 | examples: 12 | 13 | .. list-table:: List of initialized Datasets 14 | :widths: 25 15 40 40 25 25 15 | :header-rows: 1 16 | 17 | * - Short Name 18 | - Community 19 | - Description 20 | - Data Source 21 | - Reference Paper 22 | - Example 23 | * - DCPP 24 | - decadal 25 | - `Decadal Climate Prediction Project (DCPP) contribution to CMIP6 `_ 26 | - `ESGF `_, `pangeo `_ 27 | - :cite:t:`Boer2016` 28 | - `with intake-esm `_, `Anderson `_ at NOAA's 45th CDP Workshop: `slides `_, `Notebook `_ 29 | * - CESM-DPLE 30 | - decadal 31 | - `Decadal Prediction Large Ensemble Project `_ 32 | - `Data `_ 33 | - :cite:t:`Yeager2018` 34 | - many standard climpred `examples `_ 35 | * - NMME 36 | - seasonal 37 | - `The North American Multimodel Ensemble: Phase-1 Seasonal-to-Interannual Prediction `_ 38 | - `IRIDL `__ 39 | - :cite:t:`Kirtman2014` 40 | - `seasonal SubX `_ 41 | * - SubX 42 | - subseasonal 43 | - `A Multimodel Subseasonal Prediction Experiment `_ 44 | - `IRIDL `__ 45 | - :cite:t:`Pegion2019` 46 | - `subseasonal SubX `_ 47 | * - S2S 48 | - subseasonal 49 | - `The Subseasonal to Seasonal (S2S) Prediction Project Database `_ 50 | - `IRIDL `__, `climetlab `_ 51 | - :cite:t:`Vitart2017` 52 | - `IRIDL `_, `EWC Cloud/climetlab `_ 53 | * - GEFS 54 | - weather 55 | - `Global Ensemble Forecast System (GEFS) `_ 56 | - `NOAA THREDDS `_ 57 | - :cite:t:`Toth1993` 58 | - `GEFS NWP `_ 59 | * - name 60 | - weather 61 | - please add a `Pull Request `_ for numerical weather prediction 62 | - dataset 63 | - appreciated 64 | - `examples to add `_ 65 | 66 | If you find or use another publicly available initialized datasets, please consider 67 | adding a `Pull Request `_. 68 | 69 | References 70 | ########## 71 | 72 | .. bibliography:: 73 | :filter: docname in docnames 74 | -------------------------------------------------------------------------------- /docs/source/literature.rst: -------------------------------------------------------------------------------- 1 | ********** 2 | Literature 3 | ********** 4 | 5 | References used in the documentation and used for studying predictability. 6 | 7 | .. bibliography:: 8 | :all: 9 | -------------------------------------------------------------------------------- /docs/source/publications.rst: -------------------------------------------------------------------------------- 1 | ******************************* 2 | Publications Using ``climpred`` 3 | ******************************* 4 | 5 | Below is a list of publications that have made use of ``climpred`` in their analysis. 6 | We appreciate a reference to ``climpred``, e.g., in your acknowledgements section to 7 | help build the community. Please cite :cite:p:`Brady2021`. 8 | 9 | Feel free to open a `Pull Request `_ to add your publication to the 10 | list! 11 | 12 | :cite:empty:`Brady2020,Krumhardt2020,Spring2020,Brady2021,Spring2021a,Spring2021b` 13 | 14 | 2021 15 | #### 16 | 17 | .. bibliography:: 18 | :filter: cited and year and (year == "2021") and docname in docnames 19 | 20 | 2020 21 | #### 22 | 23 | .. bibliography:: 24 | :filter: cited and year and (year == "2020") and docname in docnames 25 | -------------------------------------------------------------------------------- /docs/source/reference_forecast.rst: -------------------------------------------------------------------------------- 1 | ******************* 2 | Reference Forecasts 3 | ******************* 4 | 5 | To quantify the quality of an initialized forecast, it is useful to judge it against 6 | some simple reference forecast. ``climpred`` currently supports a several reference 7 | forecasts, and we are open to adding other reference forecasts. Consider opening a 8 | `Pull Request `_ for additional references. 9 | 10 | **Persistence Forecast**: Whatever is observed at the time of initialization is 11 | forecasted to persist into the forecast period :cite:p:`Jolliffe2011`. 12 | You can compute this by passing ``reference="persistence"`` into 13 | :py:meth:`.HindcastEnsemble.verify`, :py:meth:`.HindcastEnsemble.bootstrap`, 14 | :py:meth:`.PerfectModelEnsemble.verify` and :py:meth:`.PerfectModelEnsemble.bootstrap`. 15 | 16 | **Damped Persistence Forecast**: (*Not Implemented*) The amplitudes of the anomalies 17 | reduce in time exponentially at a time scale of the local autocorrelation :cite:p:`Yuan2016`. 18 | 19 | .. math:: 20 | 21 | v_{dp}(t) = v(0)e^{-\alpha t} 22 | 23 | **Climatology**: The average values at the temporal forecast resolution (e.g., annual, 24 | monthly, daily) over some long period, which is usually 30 years :cite:p:`Jolliffe2011`. 25 | You can compute this by passing ``reference="climatology"`` into 26 | :py:meth:`.HindcastEnsemble.verify`, :py:meth:`.HindcastEnsemble.bootstrap`, 27 | :py:meth:`.PerfectModelEnsemble.verify` and :py:meth:`.PerfectModelEnsemble.bootstrap`. 28 | 29 | **Uninitialized**: Uninitialized ensembles are generated by perturbing initial 30 | conditions only at one point in the historical run. 31 | These are generated via micro (round-off error perturbations) or macro (starting from 32 | completely different restart files) methods. Uninitialized ensembles are used to 33 | approximate the magnitude of internal climate variability and to confidently extract 34 | the forced response (ensemble mean) in the climate system. In ``climpred``, we use 35 | uninitialized ensembles as a baseline for how important (reoccurring) initializations 36 | are for lending predictability to the system. 37 | You can compute this by passing ``reference="uninitialized"`` into 38 | :py:meth:`.HindcastEnsemble.verify`, :py:meth:`.HindcastEnsemble.bootstrap`, 39 | :py:meth:`.PerfectModelEnsemble.verify` and :py:meth:`.PerfectModelEnsemble.bootstrap`. 40 | Some modeling centers (such as NCAR) 41 | provide a dynamical uninitialized ensemble (the CESM Large Ensemble) along with their 42 | initialized prediction system (the CESM Decadal Prediction Large Ensemble). 43 | Use :py:meth:`.HindcastEnsemble.add_uninitialized` or 44 | :py:meth:`.PerfectModelEnsemble.add_uninitialized`. 45 | This could be, for example, output from an ``uninitialized`` Large Ensemble. 46 | If ``uninitialzed`` isn't available, one can run 47 | :py:meth:`.HindcastEnsemble.generate_uninitialized` or 48 | :py:meth:`.PerfectModelEnsemble.generate_uninitialized`, which 49 | resamples the ``initialized`` from :py:class:`.HindcastEnsemble` or 50 | ``control`` from :py:class:`.PerfectModelEnsemble` to an 51 | ``uninitialized`` forecast. 52 | 53 | **Random Mechanism**: (*Not Implemented*) A probability distribution is assigned to the 54 | possible range of the variable being forecasted, and a sequence of forecasts is 55 | produced by taking a sequence of independent values from that distribution 56 | :cite:p:`Jolliffe2011`. This would be similar to computing an ``uninitialized`` 57 | forecast. 58 | 59 | References 60 | ########## 61 | 62 | .. bibliography:: 63 | :filter: docname in docnames 64 | -------------------------------------------------------------------------------- /docs/source/related-packages.rst: -------------------------------------------------------------------------------- 1 | **************** 2 | Related Packages 3 | **************** 4 | 5 | We're big fans of open-source software at ``climpred`` and want to support and 6 | collaborate with any other forecasting-related packages. 7 | Below is a list of packages that have some place in the Earth System forecasting 8 | world. Please reach out to us if you are aware of any open-source packages in this 9 | domain that are not on the list. 10 | 11 | * `Microsoft forecasting `_: 12 | A collection of best practices for time series forecasting. 13 | * `MurCSS `_: 14 | A tool for standardized evaluation of decadal hindcast systems. See also 15 | `this `__ and 16 | `this `__ page for additional resources 17 | on scoring from MurCSS. 18 | * `properscoring `_: 19 | Probabilistic forecast metrics in python. 20 | (We've since wrapped these functions in 21 | `xskillscore `_.) 22 | * `pySTEPS `_: 23 | Framework for short-term ensemble forecasting of precipitation. 24 | * `S2D Verification `_: 25 | An R package for a common set of tools for forecast verification. 26 | * `SwirlsPy `_: 27 | Analysis and prediction of nowcasts for precipitation and weather phenomena. 28 | * `xskillscore `_: 29 | Metrics for verifying forecasts (a key dependency to ``climpred``). 30 | * `doppyo `_ with many metrics transferred to 31 | `xskillscore `_ 32 | -------------------------------------------------------------------------------- /docs/source/release_procedure.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../HOWTORELEASE.rst 2 | -------------------------------------------------------------------------------- /docs/source/savefig/different_alignment_methods.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangeo-data/climpred/57681b8c2d92e5c80d4b98e382c39063ca397da5/docs/source/savefig/different_alignment_methods.png -------------------------------------------------------------------------------- /docs/source/scope.rst: -------------------------------------------------------------------------------- 1 | Scope of ``climpred`` 2 | ===================== 3 | 4 | ``climpred`` aims to be the primary package used to analyze output from initialized 5 | dynamical forecast models, ranging from short-term weather forecasts to decadal climate 6 | forecasts. The code base is driven by the geoscientific prediction community through 7 | open source development. It leverages `xarray `_ 8 | to keep track of core prediction ensemble dimensions (e.g., ensemble member, 9 | initialization date, and lead time) and `dask `_ to perform 10 | out-of-memory computations on large datasets. 11 | 12 | The primary goal of ``climpred`` is to offer a comprehensive set of analysis tools for 13 | assessing the forecasts relative to a validation product (e.g., observations, 14 | reanalysis products, control simulations, baseline forecasts). This ranges from simple 15 | deterministic and probabilistic verification `metrics `_ — such as, e.g. 16 | mean absolute error or rank histogram — to more advanced contingency table-derived 17 | metrics. ``climpred`` expects users to handle their domain-specific post-processing of 18 | model output, so that the package can focus on the actual analysis of forecasts. 19 | 20 | Finally, the ``climpred`` documentation will serve as a repository of unified analysis 21 | methods through `jupyter `_ notebook `examples `_, 22 | and collects relevant references and literature. 23 | -------------------------------------------------------------------------------- /docs/source/setting-up-data.rst: -------------------------------------------------------------------------------- 1 | *********************** 2 | Setting Up Your Dataset 3 | *********************** 4 | 5 | ``climpred`` relies on a consistent naming system for 6 | `xarray `_ dimensions. 7 | This allows things to run more easily under-the-hood. 8 | 9 | :py:class:`.PredictionEnsemble` expects at the minimum to contain dimensions 10 | ``init`` and ``lead``. 11 | 12 | ``init`` is the initialization dimension, that relays the time 13 | steps at which the ensemble was initialized. 14 | ``init`` is known as ``forecast_reference_time`` in the `CF convention `_. 15 | ``init`` must be of type :py:class:`pandas.DatetimeIndex`, or 16 | :py:class:`xarray.CFTimeIndex`. 17 | If ``init`` is of type ``int``, it is assumed to be annual data starting Jan 1st. 18 | A UserWarning is issues when this assumption is made. 19 | 20 | ``lead`` is the lead time of the forecasts from initialization. 21 | ``lead`` is known as ``forecast_period`` in the `CF convention `_. 22 | ``lead`` must be ``int`` or ``float``. 23 | The units for the ``lead`` dimension must be specified in as an attribute. 24 | Valid options are ``["years", "seasons", "months"]`` and 25 | ``["weeks", "pentads", "days", "hours", "minutes", "seconds"]``. 26 | If ``lead`` is provided as :py:class:`pandas.Timedelta` up to ``"weeks"``, ``lead`` 27 | is converted to ``int`` and a corresponding ``lead.attrs["units"]``. 28 | For larger ``lead`` as :py:class:`pandas.Timedelta` 29 | ``["months", "seasons" or "years"]``, no conversion is possible. 30 | 31 | ``valid_time=init+lead`` will be calculated in :py:class:`.PredictionEnsemble` upon 32 | instantiation. 33 | 34 | Another crucial dimension is ``member``, which holds the various ensemble members, 35 | which is only required for probabilistic metrics. ``member`` is known as 36 | ``realization`` in the `CF convention `_ 37 | 38 | Any additional dimensions will be broadcasted: these could be dimensions like ``lat``, 39 | ``lon``, ``depth``, etc. 40 | 41 | If the expected dimensions are not found, but the matching `CF convention `_ 42 | ``standard_name`` in a coordinate attribute, the dimension is renamed to the 43 | corresponding ``climpred`` ensemble dimension. 44 | 45 | Check out the demo to setup a ``climpred``-ready prediction ensemble 46 | `from your own data `_ or via 47 | `intake-esm `_ from `CMIP DCPP `_. 48 | 49 | **Verification products** are expected to contain the ``time`` dimension at the minimum. 50 | For best use of ``climpred``, their ``time`` dimension should cover the full length of 51 | ``init`` and be the same calendar type as the accompanying prediction ensemble. 52 | The ``time`` dimension must be :py:class:`pandas.DatetimeIndex`, or 53 | :py:class:`xarray.CFTimeIndex`. 54 | ``time`` dimension of type ``int`` is assumed to be annual data starting Jan 1st. 55 | A UserWarning is issued when this assumption is made. 56 | These products can also include additional dimensions, such as ``lat``, ``lon``, 57 | ``depth``, etc. 58 | 59 | See the below table for a summary of dimensions used in ``climpred``, and data types 60 | that ``climpred`` supports for them. 61 | 62 | .. list-table:: List of ``climpred`` dimension and coordinates 63 | :widths: 25 25 25 25 25 64 | :header-rows: 1 65 | 66 | * - Short Name 67 | - Types 68 | - Long name 69 | - `CF convention `_ 70 | - Attribute(s) 71 | * - ``lead`` 72 | - ``int``, ``float`` or :py:class:`pandas.Timedelta` up to ``weeks`` 73 | - lead timestep after initialization ``init`` 74 | - ``forecast_period`` 75 | - units (str) [``years``, ``seasons``, ``months``, ``weeks``, ``pentads``, ``days``, ``hours``, ``minutes``, ``seconds``] or :py:class:`pandas.Timedelta` 76 | * - ``init`` 77 | - :py:class:`pandas.DatetimeIndex` or :py:class:`xarray.CFTimeIndex`. 78 | - initialization as start date of experiment 79 | - ``forecast_reference_time`` 80 | - None 81 | * - ``member`` 82 | - ``int``, ``str`` 83 | - ensemble member 84 | - ``realization`` 85 | - None 86 | 87 | Probably the most challenging part is concatenating 88 | (:py:func:`xarray.concat`) raw model output with dimension ``time`` of 89 | multiple simulations to a multi-dimensional :py:class:`xarray.Dataset` containing 90 | dimensions ``init``, (``member``) and ``lead``, where ``time`` becomes 91 | ``valid_time=init+lead``. One way of doing it is 92 | :py:func:`climpred.preprocessing.shared.load_hindcast`. 93 | -------------------------------------------------------------------------------- /docs/source/significance.rst: -------------------------------------------------------------------------------- 1 | #################### 2 | Significance Testing 3 | #################### 4 | 5 | Significance testing is important for assessing whether a given initialized prediction 6 | system is skillful. Some questions that significance testing can answer are: 7 | 8 | - Is the correlation coefficient of a lead time series significantly different from 9 | zero? 10 | 11 | - What is the probability that the retrospective forecast is more valuable than a 12 | historical/uninitialized simulation? 13 | 14 | - Are correlation coefficients statistically significant despite temporal and 15 | spatial autocorrelation? 16 | 17 | All of these questions deal with statistical significance. See below on how to use 18 | ``climpred`` to address these questions. 19 | Please also have a look at the 20 | `significance testing example `__. 21 | 22 | p value for temporal correlations 23 | ################################# 24 | 25 | For the correlation `metrics `__, like 26 | :py:func:`~climpred.metrics._pearson_r` and :py:func:`~climpred.metrics._spearman_r`, 27 | ``climpred`` also hosts the associated p-value, like 28 | :py:func:`~climpred.metrics._pearson_r_p_value`, 29 | that this correlation is significantly different from zero. 30 | :py:func:`~climpred.metrics._pearson_r_eff_p_value` also incorporates the reduced 31 | degrees of freedom due to temporal autocorrelation. See 32 | `example `__. 33 | 34 | Bootstrapping with replacement 35 | ############################## 36 | 37 | Testing statistical significance through bootstrapping is commonly used in the field of 38 | climate prediction. Bootstrapping relies on 39 | resampling the underlying data with replacement for a large number of ``iterations``, as 40 | proposed by the decadal prediction framework :cite:p:`Goddard2013,Boer2016`. 41 | This means that the ``initialized`` ensemble is resampled with replacement along a 42 | dimension (``init`` or ``member``) and then that resampled ensemble is verified against 43 | the observations. This leads to a distribution of ``initialized`` skill. Further, a 44 | ``reference`` forecast uses the resampled ``initialized`` ensemble, which creates a 45 | ``reference`` skill distribution. Lastly, an ``uninitialized`` skill distribution is 46 | created from the underlying historical members or the control simulation. 47 | 48 | The probability or p value is the fraction of these resampled ``initialized`` metrics 49 | beaten by the ``uninitialized`` or resampled reference metrics calculated from their 50 | respective distributions. Confidence intervals using these distributions are also 51 | calculated. 52 | 53 | This behavior is incorporated by :py:meth:`.HindcastEnsemble.bootstrap` and 54 | :py:meth:`.PerfectModelEnsemble.bootstrap`, see 55 | `example `__. 56 | 57 | 58 | Field significance 59 | ################## 60 | 61 | Please use :py:func:`esmtools.testing.multipletests` to control the false discovery 62 | rate (FDR) in geospatial data from the above obtained p-values :cite:p:`Wilks2016`. 63 | See the `FDR example `__. 64 | 65 | 66 | Sign test 67 | ######### 68 | 69 | Use DelSole's sign test relying on the statistics of a random walk to decide whether 70 | one forecast is significantly better than another forecast 71 | :cite:p:`Benjamini1994,DelSole2016`, see :py:func:`xskillscore.sign_test` and 72 | `sign test example `__. 73 | 74 | References 75 | ########## 76 | 77 | .. bibliography:: 78 | :filter: docname in docnames 79 | -------------------------------------------------------------------------------- /docs/source/why-climpred.rst: -------------------------------------------------------------------------------- 1 | Overview: Why climpred? 2 | ======================= 3 | 4 | There are many packages out there related to computing metrics on initialized 5 | geoscience predictions. However, we didn't find any one package that unified all our 6 | needs. 7 | 8 | Output from earth system prediction hindcast (also called re-forecast) experiments is 9 | difficult to work with. A typical output file could contain the dimensions 10 | ``initialization``, ``lead time``, ``ensemble member``, ``latitude``, ``longitude``, 11 | ``depth``. ``climpred`` leverages the labeled dimensions of ``xarray`` to handle the 12 | headache of bookkeeping for you. We offer :py:class:`.HindcastEnsemble` and 13 | :py:class:`.PerfectModelEnsemble` objects that carry products to verify against (e.g., 14 | control runs, reconstructions, uninitialized ensembles) along with your initialized 15 | prediction output. 16 | 17 | When computing lead-dependent skill scores, ``climpred`` handles all of the 18 | ``init+lead-valid_time``-matching for you, properly aligning the multiple 19 | ``time`` dimensions between the hindcast and verification datasets. 20 | We offer a suite of vectorized `deterministic `_ 21 | and `probabilistic `_ metrics that can be applied to time 22 | series and grids. It's as easy as concatenating your initialized prediction output into 23 | one :py:class:`xarray.Dataset` and running the :py:meth:`.HindcastEnsemble.verify` 24 | command: 25 | 26 | .. :: python 27 | 28 | >>> HindcastEnsemble.verify( 29 | ... metric="rmse", comparison="e2o", dim="init", alignment="maximize" 30 | ... ) 31 | -------------------------------------------------------------------------------- /joss/codemeta.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://raw.githubusercontent.com/codemeta/codemeta/master/codemeta.jsonld", 3 | "@type": "Code", 4 | "author": [ 5 | { 6 | "@id": "0000-0002-2309-8245", 7 | "@type": "Person", 8 | "email": "riley.brady@colorado.edu", 9 | "name": "Riley X. Brady", 10 | "affiliation": "University of Colorado Boulder" 11 | }, 12 | { 13 | "@id": "0000-0003-0216-2241", 14 | "@type": "Person", 15 | "email": "aaron.spring@mpimet.mpg.de", 16 | "name": "Aaron Spring", 17 | "affiliation": "Max Planck Institute for Meteorology" 18 | } 19 | ], 20 | "identifier": "", 21 | "codeRepository": "https://www.github.com/pangeo-data/climpred", 22 | "datePublished": "2020-02-22", 23 | "dateModified": "2021-02-22", 24 | "dateCreated": "2020-10-14", 25 | "description": "verification of weather and climate forecasts.", 26 | "keywords": "python, climate, prediction, geospatial", 27 | "license": "MIT", 28 | "title": "climpred", 29 | "version": "v2.1.2" 30 | } 31 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools>=64", 4 | "setuptools_scm>=8", 5 | "wheel" 6 | ] 7 | build-backend = "setuptools.build_meta" 8 | 9 | [project] 10 | name = "climpred" 11 | authors = [ 12 | {name = "Aaron Spring", email = "aaron.spring@mpimet.mpg.de"}, 13 | {name = "Riley Brady", email = "riley.brady@colorado.edu"} 14 | ] 15 | maintainers = [ 16 | {name = "Aaron Spring", email = "aaron.spring@mpimet.mpg.de"}, 17 | {name = "Trevor James Smith", email = "smith.trevorj@ouranos.ca"} 18 | ] 19 | readme = "README.rst" 20 | license = {file = "LICENSE.txt"} 21 | requires-python = ">=3.9" 22 | classifiers = [ 23 | "Development Status :: 5 - Production/Stable", 24 | "Intended Audience :: Education", 25 | "Intended Audience :: Science/Research", 26 | "License :: OSI Approved :: MIT License", 27 | "Natural Language :: English", 28 | "Operating System :: OS Independent", 29 | "Programming Language :: Python :: 3 :: Only", 30 | "Programming Language :: Python :: 3.9", 31 | "Programming Language :: Python :: 3.10", 32 | "Programming Language :: Python :: 3.11", 33 | "Programming Language :: Python :: 3.12", 34 | "Topic :: Scientific/Engineering :: Atmospheric Science" 35 | ] 36 | dependencies = [ 37 | "cf_xarray >=0.6.0", 38 | "cftime >=1.5.0", 39 | "dask >=2021.10.0", 40 | "numpy >=1.25.0", 41 | "packaging", 42 | "pooch >=1.4", 43 | "xarray >=0.19.0", 44 | "xskillscore >=0.0.20" 45 | ] 46 | dynamic = ["version", "description"] 47 | 48 | [project.optional-dependencies] 49 | accel = ["numba >=0.52", "bottleneck"] 50 | bias-correction = ["xclim >=0.46", "bias-correction >=0.4", "numpy >=1.25.0,<2.0.0"] # pinned until xclim supports numpy>=2.0.0 51 | io = ["netcdf4"] # use h5netcdf when encountering seg faults as in GitHub Actions CI 52 | regridding = ["xesmf"] # for installation see https://pangeo-xesmf.readthedocs.io/ 53 | relative_entropy = ["eofs"] 54 | test = ["netcdf4", "pre-commit", "pytest <8.0.0", "pytest-cov", "pytest-lazy-fixture", "pytest-xdist"] 55 | viz = ["matplotlib", "nc-time-axis >=1.4.0"] 56 | vwmp = ["xrft"] 57 | complete = ["climpred[accel,bias-correction,viz,io,test,relative_entropy,vwmp]"] 58 | docs = [ 59 | "climpred[complete]", 60 | "myst_nb", 61 | "sphinx", 62 | "sphinx-book-theme >=0.3.3", 63 | "sphinx-copybutton", 64 | "sphinxcontrib-bibtex", 65 | "sphinxcontrib-napoleon" 66 | ] 67 | 68 | [project.urls] 69 | "Homepage" = "https://climpred.readthedocs.io/en/stable/" 70 | "Source" = "https://github.com/pangeo-data/climpred" 71 | "Changelog" = "https://climpred.readthedocs.io/en/stable/changelog.html" 72 | "Issue Tracker" = "https://github.com/pangeo-data/climpred/issues" 73 | 74 | [tool.black] 75 | line-length = 88 76 | target-version = ["py39", "py310", "py311", "py312"] 77 | 78 | [tool.isort] 79 | known_first_party = "climpred" 80 | multi_line_output = 3 81 | include_trailing_comma = true 82 | force_grid_wrap = 0 83 | use_parentheses = true 84 | line_length = 88 85 | combine_as_imports = true 86 | skip = [ 87 | "docs/source/conf.py" 88 | ] 89 | 90 | [tool.mypy] 91 | exclude = "asv_bench|doc" 92 | files = "." 93 | show_error_codes = true 94 | ignore_missing_imports = true 95 | 96 | [tool.mypy.overrides] 97 | "bottleneck.*" = {ignore_missing_imports = true} 98 | "cftime.*" = {ignore_missing_imports = true} 99 | "mypy-dask.*" = {ignore_missing_imports = true} 100 | "mypy-distributed.*" = {ignore_missing_imports = true} 101 | "mypy-fsspec.*" = {ignore_missing_imports = true} 102 | "mypy-matplotlib.*" = {ignore_missing_imports = true} 103 | "mypy-nc_time_axis.*" = {ignore_missing_imports = true} 104 | "mypy-numpy.*" = {ignore_missing_imports = true} 105 | "mypy-netCDF4.*" = {ignore_missing_imports = true} 106 | "mypy-pandas.*" = {ignore_missing_imports = true} 107 | "mypy-pytest.*" = {ignore_missing_imports = true} 108 | "mypy-scipy.*" = {ignore_missing_imports = true} 109 | "mypy-setuptools" = {ignore_missing_imports = true} 110 | "mypy-toolz.*" = {ignore_missing_imports = true} 111 | 112 | [tool.pytest.ini_options] 113 | python_files = ["test_*.py"] 114 | addopts = [ 115 | "--color=yes", 116 | "--verbose" 117 | ] 118 | testpaths = [ 119 | "climpred/tests" 120 | ] 121 | filterwarnings = [ 122 | # xarray 123 | "ignore: Using a non-tuple sequence for multidimensional indexing is deprecated:FutureWarning", 124 | # upstream xarray 125 | "ignore: Mean of empty slice:RuntimeWarning", 126 | # only shows up during CI: no idea where its coming from, something inside bootstrapping 127 | "ignore: `np.alen` is deprecated, use `len` instead:DeprecationWarning", 128 | # ignore UserWarnings raise by warnings.warn() which do not break functionality 129 | # therefore ignore in testing 130 | "ignore::UserWarning", 131 | # xarray 132 | "ignore: tostring:DeprecationWarning", 133 | # properscoring 134 | "ignore: invalid value encountered:RuntimeWarning", 135 | "ignore: divide by zero:RuntimeWarning", 136 | # xarray 137 | "ignore: 'base' in .resample():FutureWarning", 138 | # matplotlib 139 | "ignore: More than 20 figures:RuntimeWarning", 140 | # numpy https://stackoverflow.com/questions/40659212/futurewarning-elementwise-comparison-failed-returning-scalar-but-in-the-futur#46721064 141 | "ignore: elementwise comparison failed", 142 | # pydap 143 | "ignore: Using or importing the ABCs from", 144 | # xarray 145 | "ignore: Passing method to CFTimeIndex.get_loc is deprecated", 146 | # statsmodels 147 | "ignore: pandas.Int64Index is deprecated" 148 | ] 149 | markers = [ 150 | "mistral: tests requiring being on MPI supercomputer", 151 | "slow: marks tests as slow (deselect with '-m \"not slow\"')" 152 | ] 153 | 154 | [tool.setuptools.dynamic] 155 | description = {file = "src/climpred/__init__.py"} 156 | 157 | [tool.setuptools_scm] 158 | fallback_version = "999" 159 | version_scheme = "post-release" 160 | local_scheme = "dirty-tag" 161 | -------------------------------------------------------------------------------- /requirements_upstream.txt: -------------------------------------------------------------------------------- 1 | bias_correction @ git+https://github.com/pankajkarman/bias_correction 2 | bottleneck @ git+https://github.com/pydata/bottleneck 3 | cftime @ git+https://github.com/Unidata/cftime 4 | dask @ git+https://github.com/dask/dask 5 | nc-time-axis @ git+https://github.com/SciTools/nc-time-axis 6 | xarray @ git+https://github.com/pydata/xarray 7 | xclim @ git+https://github.com/Ouranosinc/xclim 8 | xrft @ git+https://github.com/xgcm/xrft 9 | xskillscore @ git+https://github.com/xarray-contrib/xskillscore 10 | -------------------------------------------------------------------------------- /src/climpred/__init__.py: -------------------------------------------------------------------------------- 1 | """Verification of weather and climate forecasts and prediction.""" 2 | 3 | # flake8: noqa 4 | from importlib.metadata import PackageNotFoundError, version as _get_version 5 | 6 | from . import ( 7 | bias_removal, 8 | bootstrap, 9 | comparisons, 10 | constants, 11 | exceptions, 12 | graphics, 13 | horizon, 14 | metrics, 15 | prediction, 16 | relative_entropy, 17 | smoothing, 18 | stats, 19 | testing, 20 | tutorial, 21 | ) 22 | from .classes import HindcastEnsemble, PerfectModelEnsemble 23 | from .options import set_options 24 | from .preprocessing import mpi, shared 25 | from .versioning.print_versions import show_versions 26 | 27 | try: 28 | __version__ = _get_version("climpred") 29 | except PackageNotFoundError: # pragma: no cover 30 | # package is not installed 31 | pass # pragma: no cover 32 | -------------------------------------------------------------------------------- /src/climpred/constants.py: -------------------------------------------------------------------------------- 1 | # for general checks of climpred-required dimensions 2 | CLIMPRED_ENSEMBLE_DIMS = ["init", "member", "lead"] 3 | # corresponding CF-complying standard_names from http://cfconventions.org/Data/cf-standard-names/current/build/cf-standard-name-table.html to rename from # noqa: E501 4 | CF_STANDARD_NAMES = { 5 | "init": "forecast_reference_time", 6 | "member": "realization", 7 | "lead": "forecast_period", 8 | } 9 | CF_LONG_NAMES = { 10 | "init": "Initialization", 11 | "member": "Member", 12 | "lead": "Lead", 13 | } 14 | CLIMPRED_DIMS = CLIMPRED_ENSEMBLE_DIMS + ["time"] 15 | 16 | # List of frequencies to check to infer time series stride 17 | FREQ_LIST_TO_INFER_STRIDE = ["day", "month", "year"] 18 | 19 | # calendar type for perfect-model (PM) (needed for bootstrapping_uninit) 20 | # Leap also works, but changing Leap,NoLeap fails 21 | PM_CALENDAR_STR = "DatetimeNoLeap" 22 | # standard calendar for hindcast experiments 23 | HINDCAST_CALENDAR_STR = "DatetimeProlepticGregorian" 24 | 25 | # default kwargs when using concat 26 | # data_vars='minimal' could be added but needs to check that xr.Dataset 27 | CONCAT_KWARGS = {"coords": "minimal", "compat": "override"} 28 | 29 | # name for additional dimension in m2m comparison 30 | M2M_MEMBER_DIM = "forecast_member" 31 | 32 | # Valid keywords for aligning inits and verification dates. 33 | VALID_ALIGNMENTS = ["same_inits", "same_init", "same_verifs", "same_verif", "maximize"] 34 | 35 | # Valid units for lead dimension 36 | VALID_LEAD_UNITS = [ 37 | "years", 38 | "seasons", 39 | "months", 40 | "weeks", 41 | "pentads", 42 | "days", 43 | "minutes", 44 | "hours", 45 | "seconds", 46 | ] 47 | 48 | # Valid keywords for reference forecast 49 | VALID_REFERENCES = ["uninitialized", "persistence", "climatology"] 50 | 51 | # https://github.com/pankajkarman/bias_correction/blob/master/bias_correction.py 52 | BIAS_CORRECTION_BIAS_CORRECTION_METHODS = [ 53 | "modified_quantile", 54 | "gamma_mapping", 55 | "basic_quantile", 56 | "normal_mapping", 57 | ] 58 | XCLIM_BIAS_CORRECTION_METHODS = [ 59 | "DetrendedQuantileMapping", 60 | "LOCI", 61 | "EmpiricalQuantileMapping", 62 | # 'ExtremeValues', 63 | # 'NpdfTransform', 64 | "PrincipalComponents", 65 | "QuantileDeltaMapping", 66 | "Scaling", 67 | ] 68 | INTERNAL_BIAS_CORRECTION_METHODS = [ 69 | "additive_mean", 70 | "multiplicative_mean", 71 | "multiplicative_std", 72 | ] 73 | BIAS_CORRECTION_METHODS = ( 74 | BIAS_CORRECTION_BIAS_CORRECTION_METHODS 75 | + INTERNAL_BIAS_CORRECTION_METHODS 76 | + XCLIM_BIAS_CORRECTION_METHODS 77 | ) 78 | BIAS_CORRECTION_TRAIN_TEST_SPLIT_METHODS = ["unfair", "unfair-cv", "fair"] 79 | CROSS_VALIDATE_METHODS = ["LOO", False, True] 80 | 81 | # seasonality: climpred.set_options(seasonality='...') 82 | GROUPBY_SEASONALITIES = ["dayofyear", "weekofyear", "month", "season"] 83 | -------------------------------------------------------------------------------- /src/climpred/exceptions.py: -------------------------------------------------------------------------------- 1 | class Error(Exception): 2 | """Base class for custom exceptions in climpred.""" 3 | 4 | pass 5 | 6 | 7 | class CoordinateError(Error): 8 | """Exception raised when the input xarray object doesn't have the 9 | appropriate coordinates.""" 10 | 11 | def __init__(self, message): 12 | self.message = message 13 | 14 | 15 | class DatasetError(Error): 16 | """Exception raised when there is any issues related to a dataset.""" 17 | 18 | def __init__(self, message): 19 | self.message = message 20 | 21 | 22 | class DimensionError(Error): 23 | """Exception raised when the input xarray object doesn't have the 24 | appropriate dimensions.""" 25 | 26 | def __init__(self, message): 27 | self.message = message 28 | 29 | 30 | class KeywordError(Error): 31 | """Exception raised when the keyword used in the function is not appropriate or 32 | does not work in the given case.""" 33 | 34 | def __init__(self, message): 35 | self.message = message 36 | 37 | 38 | class VariableError(Error): 39 | """Exception raised when the input xarray object doesn't have the 40 | appropriate variables.""" 41 | 42 | def __init__(self, message): 43 | self.message = message 44 | -------------------------------------------------------------------------------- /src/climpred/horizon.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import xarray as xr 3 | 4 | 5 | def _last_item_cond_true(cond, dim): 6 | """Return the final item from cond that evaluates to True. 7 | 8 | Args: 9 | cond (xr.DataArray, xr.Dataset): User-defined boolean array where True means 10 | the system is predictable at the given lead. E.g., this could be based on 11 | the dynamical forecast beating a reference forecast, p values, confidence 12 | intervals, etc. cond should contain the dimension dim at the minimum. 13 | dim (str): Dimension to check for condition == True over. 14 | 15 | Returns: 16 | xr.DataArray, xr.Dataset: ``dim`` value until condition is True. 17 | 18 | """ 19 | # force DataArray because isel (when transforming to dim space) requires DataArray 20 | if isinstance(cond, xr.Dataset): 21 | was_dataset = True 22 | cond = cond.to_array() 23 | else: 24 | was_dataset = False 25 | # index last True 26 | reached = cond.argmin(dim) 27 | # fix below one 28 | reached = reached.where(reached >= 1, np.nan) 29 | # reset where always true to len(lead) 30 | reached = reached.where(~cond.all("lead"), other=cond[dim].size) 31 | # fix locations where always nan to nan 32 | mask = cond.notnull().all("lead") 33 | reached = reached.where(mask, other=np.nan) 34 | # shift back into coordinate space 35 | # problem: cannot convert nan to idx in isel 36 | # therefore set to dim:0 and mask again afterwards 37 | reached_notnull = reached.notnull() # remember where not masked 38 | reached = reached.where( 39 | reached.notnull(), other=cond.isel({dim: 0}) 40 | ) # set nan to dim:0 41 | # take one index before calculated by argmin 42 | reached_dim_space = cond[dim].isel( 43 | {dim: reached.astype(int) - 1} 44 | ) # to not break conversion to dim space 45 | reached_dim_space = reached_dim_space.where( 46 | reached_notnull, other=np.nan 47 | ) # cleanup replace dim:0 with nan again 48 | if was_dataset: 49 | reached_dim_space = reached_dim_space.to_dataset(dim="variable").squeeze( 50 | drop=True 51 | ) 52 | if "lead" in reached_dim_space.coords: 53 | reached_dim_space = reached_dim_space.drop_vars("lead") 54 | return reached_dim_space 55 | 56 | 57 | def horizon(cond): 58 | """Calculate the predictability horizon based on a condition ```cond``. 59 | 60 | Args: 61 | cond (xr.DataArray, xr.Dataset): User-defined boolean array where True means 62 | the system is predictable at the given lead. E.g., this could be based on 63 | the dynamical forecast beating a reference forecast, p values, confidence 64 | intervals, etc. cond should contain the dimension lead at the minimum. 65 | 66 | Returns: 67 | xr.DataArray, xr.Dataset: predictability horizon reduced by ``lead`` dimension. 68 | 69 | Example: 70 | >>> skill = PerfectModelEnsemble.verify( 71 | ... metric="acc", 72 | ... comparison="m2e", 73 | ... dim=["init", "member"], 74 | ... reference=["persistence"], 75 | ... ) 76 | >>> horizon(skill.sel(skill="initialized") > skill.sel(skill="persistence")) 77 | 78 | Dimensions: () 79 | Data variables: 80 | tos float64 15.0 81 | Attributes: 82 | units: years 83 | standard_name: forecast_period 84 | long_name: Lead 85 | description: Forecast period is the time interval between the forecast... 86 | 87 | 88 | >>> bskill = PerfectModelEnsemble.bootstrap( 89 | ... metric="acc", 90 | ... comparison="m2e", 91 | ... dim=["init", "member"], 92 | ... reference="uninitialized", 93 | ... iterations=201, 94 | ... resample_dim="init", 95 | ... ) 96 | >>> horizon(bskill.sel(skill="uninitialized", results="p") <= 0.05) 97 | 98 | Dimensions: () 99 | Coordinates: 100 | skill None: 6 | """Add header to the log for a `HindcastEnsemble.verify()` instance.""" 7 | logging.info( 8 | f"`HindcastEnsemble.verify()` for metric {metric.name}, " 9 | f"comparison {comparison.name}, dim {dim}, alignment {alignment} and " 10 | f"reference {reference} at {str(datetime.now())}\n" 11 | f"++++++++++++++++++++++++++++++++++++++++++++++++" 12 | ) 13 | 14 | 15 | def log_hindcast_verify_inits_and_verifs( 16 | dim, lead, inits, verif_dates, reference=None 17 | ) -> None: 18 | """At each lead, log the inits and verification dates being used in computations.""" 19 | if reference is None: 20 | reference = "initialized" 21 | logging.info( 22 | f"{reference} | lead: {str(lead).zfill(2)} | " 23 | # This is the init-sliced forecast, thus displaying actual 24 | # initializations. 25 | f"inits: {inits[lead].min().values}" 26 | f"-{inits[lead].max().values} | " 27 | # This is the verification window, thus displaying the 28 | # verification dates. 29 | f"verifs: {verif_dates[lead].min()}" 30 | f"-{verif_dates[lead].max()}" 31 | ) 32 | init_output = [ 33 | f"{i.year}-{str(i.month).zfill(2)}-{str(i.day).zfill(2)}" 34 | for i in inits[lead].values 35 | ] 36 | verif_output = [ 37 | f"{i.year}-{str(i.month).zfill(2)}-{str(i.day).zfill(2)}" 38 | for i in verif_dates[lead].values 39 | ] 40 | logging.debug(f"\ninits: {init_output}" f"\nverifs: {verif_output}") 41 | -------------------------------------------------------------------------------- /src/climpred/preprocessing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangeo-data/climpred/57681b8c2d92e5c80d4b98e382c39063ca397da5/src/climpred/preprocessing/__init__.py -------------------------------------------------------------------------------- /src/climpred/preprocessing/mpi.py: -------------------------------------------------------------------------------- 1 | import glob as _glob 2 | import os as _os 3 | 4 | 5 | def get_path( 6 | dir_base_experiment: str = "/work/bm1124/m300086/CMIP6/experiments", 7 | member: int = 1, 8 | init: int = 1960, 9 | model: str = "hamocc", 10 | output_stream: str = "monitoring_ym", 11 | timestr: str = "*1231", 12 | ending: str = "nc", 13 | ) -> str: 14 | """Get the path of a file for MPI-ESM standard output file names and directory. 15 | 16 | Args: 17 | dir_base_experiment (str): Path of experiments folder. Defaults to 18 | "/work/bm1124/m300086/CMIP6/experiments". 19 | member (int): member label. Defaults to 1. 20 | init (int): initialization label. Typically year. Defaults to 1960. 21 | model (str): submodel name. Defaults to "hamocc". 22 | Allowed: ['echam6', 'jsbach', 'mpiom', 'hamocc']. 23 | output_stream (str): output_stream name. Defaults to "monitoring_ym". 24 | Allowed: ['data_2d_mm', 'data_3d_ym', 'BOT_mm', ...] 25 | timestr (str): timestr likely including *. Defaults to "*1231". 26 | ending (str): ending indicating file format. Defaults to "nc". 27 | Allowed: ['nc', 'grb']. 28 | 29 | Returns: 30 | str: path of requested file(s) 31 | 32 | """ 33 | # get experiment_id 34 | dirs = _os.listdir(dir_base_experiment) 35 | experiment_id = [ 36 | x for x in dirs if (f"{init}" in x and "r" + str(member) + "i" in x) 37 | ] 38 | assert len(experiment_id) == 1 39 | experiment_id = experiment_id[0] # type: ignore 40 | dir_outdata = f"{dir_base_experiment}/{experiment_id}/outdata/{model}" 41 | path = f"{dir_outdata}/{experiment_id}_{model}_{output_stream}_{timestr}.{ending}" 42 | if _os.path.exists(_glob.glob(path)[0]): 43 | return path 44 | else: 45 | raise ValueError(f"Path not found or no access: {path}") 46 | -------------------------------------------------------------------------------- /src/climpred/preprocessing/shared.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Callable, Optional, Union 2 | 3 | import numpy as np 4 | import xarray as xr 5 | 6 | from ..constants import CLIMPRED_ENSEMBLE_DIMS 7 | from .mpi import get_path as get_path_mpi 8 | 9 | 10 | def load_hindcast( 11 | inits=range(1961, 1965, 1), 12 | members=range(1, 3, 1), 13 | preprocess: Optional[Callable] = None, 14 | lead_offset: int = 1, 15 | parallel: bool = True, 16 | engine: Optional[str] = None, 17 | get_path: Callable = get_path_mpi, 18 | **get_path_kwargs: Any, 19 | ) -> Union[xr.DataArray, xr.Dataset]: 20 | """ 21 | Concat multi-member, multi-initialization hindcast experiment. 22 | 23 | Into one :py:class:`xarray.Dataset` compatible with `climpred`. 24 | 25 | Args: 26 | inits (list, array): List of initializations to be loaded. 27 | Defaults to ``range(1961, 1965)``. 28 | members (list, array): List of initializations to be loaded. 29 | Defaults to ``range(1, 3)``. 30 | preprocess (Callable): ``preprocess`` function accepting and returning 31 | :py:class:`xarray.Dataset` only. To be passed to 32 | :py:func:`xarray.open_dataset`. Defaults to None. 33 | parallel (bool): passed to `xr.open_mfdataset`. Defaults to ``True``. 34 | engine (str): passed to `xr.open_mfdataset`. Defaults to ``None``. 35 | get_path (callable): ``get_path`` function specific to modelling center output 36 | format. Defaults to :py:func:`~climpred.preprocessing.mpi.get_path`. 37 | **get_path_kwargs (dict): parameters passed to ``**get_path``. 38 | 39 | Returns: 40 | ``climpred`` compatible dataset with dims: ``member``, ``init``, ``lead``. 41 | 42 | """ 43 | init_list = [] 44 | for init in inits: 45 | print(f"Processing init {init} ...") 46 | member_list = [] 47 | for member in members: 48 | # get path p 49 | p = get_path(member=member, init=init, **get_path_kwargs) 50 | # open all leads for specified member and init 51 | member_ds = xr.open_mfdataset( 52 | p, 53 | combine="nested", 54 | concat_dim="time", 55 | preprocess=preprocess, 56 | parallel=parallel, 57 | engine=engine, 58 | coords="minimal", # expecting identical coords 59 | data_vars="minimal", # expecting identical vars 60 | compat="override", # speed up 61 | ).squeeze() 62 | # set new integer time 63 | member_ds = set_integer_time_axis(member_ds) 64 | member_list.append(member_ds) 65 | member_ds = xr.concat(member_list, "member") 66 | init_list.append(member_ds) 67 | ds = xr.concat(init_list, "init").rename({"time": "lead"}) 68 | ds["member"] = members 69 | ds["init"] = inits 70 | return ds 71 | 72 | 73 | def rename_SLM_to_climpred_dims( 74 | xro: Union[xr.DataArray, xr.Dataset] 75 | ) -> Union[xr.DataArray, xr.Dataset]: 76 | """ 77 | Rename ensemble dimensions common to SubX or CESM output. 78 | 79 | * ``S`` : Refers to start date and is changed to ``init`` 80 | * ``L`` : Refers to lead time and is changed to ``lead`` 81 | * ``M``: Refers to ensemble member and is changed to ``member`` 82 | 83 | Args: 84 | xro (xr.Dataset): input from CESM/SubX containing dimensions: `S`, `L`, `M`. 85 | Returns: 86 | ``climpred`` compatible with dimensions: ``member``, ``init``, ``lead``. 87 | """ 88 | dim_dict = {"S": "init", "L": "lead", "M": "member"} 89 | for dim in dim_dict.keys(): 90 | if dim in xro.dims: 91 | xro = xro.rename({dim: dim_dict[dim]}) 92 | return xro 93 | 94 | 95 | def rename_to_climpred_dims( 96 | xro: Union[xr.DataArray, xr.Dataset] 97 | ) -> Union[xr.DataArray, xr.Dataset]: 98 | """ 99 | Rename existing dimension to `CLIMPRED_ENSEMBLE_DIMS`. 100 | 101 | This function attempts to autocorrect dimension names to 102 | climpred standards. e.g., `ensemble_member` becomes `member` and `lead_time` 103 | becomes `lead`, and `time` gets renamed to `lead`. 104 | 105 | Args: 106 | xro (xr.Dataset): input from DCPP via `intake-esm `_ 107 | containing dimension names like `dcpp_init_year`, `time`, `member_id`. 108 | Returns: 109 | ``climpred`` compatible with dimensions: ``member``, ``init``, ``lead``. 110 | """ 111 | for cdim in CLIMPRED_ENSEMBLE_DIMS: 112 | renamed = False # set renamed flag to false initiallly 113 | if cdim not in xro.dims: # if a CLIMPRED_ENSEMBLE_DIMS is not found 114 | for c in xro.dims: # check in xro.dims for dims 115 | if cdim in c: # containing the string of this CLIMPRED_ENSEMBLE_DIMS 116 | xro = xro.rename({c: cdim}) 117 | renamed = True 118 | # special case for hindcast when containing time 119 | if "time" in xro.dims and "lead" not in xro.dims: 120 | xro = xro.rename({"time": "lead"}) 121 | renamed = True 122 | elif "lead" in xro.dims: 123 | renamed = True 124 | if not renamed: 125 | raise ValueError( 126 | f"Couldn't find a dimension to rename to `{cdim}`, found {xro.dims}." 127 | ) 128 | return xro 129 | 130 | 131 | def set_integer_time_axis( 132 | xro: Union[xr.DataArray, xr.Dataset], offset: int = 1, time_dim: str = "time" 133 | ) -> Union[xr.DataArray, xr.Dataset]: 134 | """ 135 | Set time axis to integers starting from `offset`. 136 | 137 | Used in hindcast preprocessing before the concatination of `intake-esm` happens. 138 | """ 139 | xro[time_dim] = np.arange(offset, offset + xro[time_dim].size) 140 | return xro 141 | -------------------------------------------------------------------------------- /src/climpred/testing.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from xarray.testing import assert_allclose, assert_equal, assert_identical 4 | 5 | 6 | def assert_PredictionEnsemble(he, he2, how="equal", **assert_how_kwargs): 7 | """Loops over all datasets in PredictionEnsemble and applies assert_{how}.""" 8 | 9 | def non_empty_datasets(he): 10 | """Check for same non-empty datasets.""" 11 | return [k for k in he._datasets.keys() if he._datasets[k]] 12 | 13 | assert non_empty_datasets(he) == non_empty_datasets(he2) 14 | # check all datasets 15 | if how == "equal": 16 | assert_func = assert_equal 17 | elif how == "allclose": 18 | assert_func = assert_allclose 19 | elif how == "identical": 20 | assert_func = assert_identical 21 | 22 | for dataset in he._datasets: 23 | if he._datasets[dataset]: 24 | if dataset == "observations": 25 | for obs_dataset in he._datasets["observations"]: 26 | logging.info("check observations", obs_dataset) 27 | assert_func( 28 | he2._datasets["observations"][obs_dataset], 29 | he2._datasets["observations"][obs_dataset], 30 | **assert_how_kwargs, 31 | ) 32 | else: 33 | logging.info("check", dataset) 34 | assert_func( 35 | he2._datasets[dataset], he._datasets[dataset], **assert_how_kwargs 36 | ) 37 | 38 | 39 | def check_dataset_dims_and_data_vars(before, after, dataset): 40 | """Checks that dimensions, coordiantes, and variables are identical in 41 | PredictionEnsemble `before` and PredictionEnsemble `after`. 42 | 43 | Args: 44 | before, after (climpred.PredictionEnsemble): PredictionEnsembles that have had 45 | some operation performed on them. 46 | dataset (str): Name of dataset within the PredictionEnsemble. This should be 47 | a key to the dictionary. 48 | 49 | Asserts: 50 | That dimensions, coordinates, and variables are identical before and after the 51 | transformation. 52 | """ 53 | before = before._datasets[dataset] 54 | after = after._datasets[dataset] 55 | 56 | assert before.dims == after.dims 57 | assert list(before.data_vars) == list(after.data_vars) 58 | assert list(before.coords) == list(after.coords) 59 | -------------------------------------------------------------------------------- /src/climpred/tests/__init__.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | 3 | import pytest 4 | from packaging import version 5 | 6 | 7 | def _importorskip(modname, minversion=None): 8 | try: 9 | mod = importlib.import_module(modname) 10 | has = True 11 | if minversion is not None: 12 | if version.parse(mod.__version__) < version.parse(minversion): 13 | raise ImportError("Minimum version not satisfied") 14 | except ImportError: 15 | has = False 16 | func = pytest.mark.skipif(not has, reason=f"requires {modname}") 17 | return has, func 18 | 19 | 20 | has_matplotlib, requires_matplotlib = _importorskip("matplotlib") 21 | has_nc_time_axis, requires_nc_time_axis = _importorskip("nc_time_axis", "1.4.0") 22 | has_xclim, requires_xclim = _importorskip("xclim", "0.31") 23 | has_bias_correction, requires_bias_correction = _importorskip("bias_correction") 24 | has_xesmf, requires_xesmf = _importorskip("xesmf") 25 | has_xrft, requires_xrft = _importorskip("xrft") 26 | has_eofs, requires_eofs = _importorskip("eofs") 27 | -------------------------------------------------------------------------------- /src/climpred/tests/test_checks.py: -------------------------------------------------------------------------------- 1 | """Testing checks.py.""" 2 | 3 | import pytest 4 | 5 | from climpred.checks import ( 6 | has_dataset, 7 | has_dims, 8 | has_min_len, 9 | has_valid_lead_units, 10 | is_in_list, 11 | match_initialized_dims, 12 | match_initialized_vars, 13 | ) 14 | from climpred.constants import VALID_LEAD_UNITS 15 | from climpred.exceptions import DatasetError, DimensionError, VariableError 16 | 17 | 18 | def test_has_dims_str(da1): 19 | """Test if check works for a string.""" 20 | assert has_dims(da1, "x", "arbitrary") 21 | 22 | 23 | def test_has_dims_list(da1): 24 | """Test if check works for a list.""" 25 | # returns None if no errors 26 | assert has_dims(da1, ["x", "y"], "arbitrary") 27 | 28 | 29 | def test_has_dims_str_fail(da1): 30 | """Test if check fails properly for a string.""" 31 | with pytest.raises(DimensionError) as e: 32 | has_dims(da1, "z", "arbitrary") 33 | assert "Your arbitrary object must contain" in str(e.value) 34 | 35 | 36 | def test_has_dims_list_fail(da1): 37 | """Test if check fails properly for a list.""" 38 | with pytest.raises(DimensionError) as e: 39 | has_dims(da1, ["z"], "arbitrary") 40 | assert "Your arbitrary object must contain" in str(e.value) 41 | 42 | 43 | def test_has_min_len_arr(da1): 44 | """Test if check works for min len.""" 45 | assert has_min_len(da1.values, 2, "arbitrary") 46 | 47 | 48 | def test_has_min_len_fail(da1): 49 | """Test if check fails properly.""" 50 | with pytest.raises(DimensionError) as e: 51 | has_min_len(da1.values, 5, "arbitrary") 52 | assert "Your arbitrary array must be at least" in str(e.value) 53 | 54 | 55 | def test_has_dataset(): 56 | """Test if check works for a non-empty list.""" 57 | obj = [5] 58 | assert has_dataset(obj, "list", "something") 59 | 60 | 61 | def test_has_dataset_fail(): 62 | """Test if check works to fail for an empty list.""" 63 | obj = [] 64 | with pytest.raises(DatasetError) as e: 65 | has_dataset(obj, "test", "something") 66 | assert "You need to add at least one test dataset" in str(e.value) 67 | 68 | 69 | def test_match_initialized_dims(da1, da2): 70 | """Test if check works if both da has the proper dims.""" 71 | assert match_initialized_dims(da1.rename({"y": "init"}), da2.rename({"y": "time"})) 72 | 73 | 74 | def test_match_initialized_dims_fail(da1, da2): 75 | """Test if check works if the da does not have the proper dims.""" 76 | with pytest.raises(DimensionError) as e: 77 | match_initialized_dims(da1.rename({"y": "init"}), da2.rename({"y": "not_time"})) 78 | assert "Verification contains more dimensions than initialized" in str(e.value) 79 | 80 | 81 | def test_match_initialized_vars(ds1, ds2): 82 | """Test if check works if both have the same variables.""" 83 | assert match_initialized_vars(ds1, ds2) 84 | 85 | 86 | def test_match_initialized_vars_fail(ds1, ds2): 87 | """Test if check works if both do not have the same variables.""" 88 | with pytest.raises(VariableError) as e: 89 | match_initialized_vars(ds1, ds2.rename({"air": "tmp"})) 90 | assert "Please provide a Dataset/DataArray with at least" in str(e.value) 91 | 92 | 93 | def test_is_in_list(): 94 | """Test if check works if key is in dict.""" 95 | some_list = ["key"] 96 | assert is_in_list("key", some_list, "metric") 97 | 98 | 99 | def test_is_in_list_fail(): 100 | """Test if check works if key is not in dict.""" 101 | some_list = ["key"] 102 | with pytest.raises(KeyError) as e: 103 | is_in_list("not_key", some_list, "metric") 104 | assert "Specify metric from" in str(e.value) 105 | 106 | 107 | @pytest.mark.parametrize("lead_units", VALID_LEAD_UNITS) 108 | def test_valid_lead_units(da_lead, lead_units): 109 | """Test that lead units check passes with appropriate lead units.""" 110 | da_lead["lead"].attrs["units"] = lead_units 111 | assert has_valid_lead_units(da_lead) 112 | 113 | 114 | def test_valid_lead_units_no_units(da_lead): 115 | """Test that valid lead units check breaks if there are no units.""" 116 | with pytest.raises(AttributeError): 117 | has_valid_lead_units(da_lead) 118 | 119 | 120 | def test_valid_lead_units_invalid_units(da_lead): 121 | """Test that valid lead units check breaks if invalid units provided.""" 122 | da_lead["lead"].attrs["units"] = "dummy" 123 | with pytest.raises(AttributeError): 124 | has_valid_lead_units(da_lead) 125 | 126 | 127 | @pytest.mark.parametrize("lead_units", VALID_LEAD_UNITS) 128 | def test_nonplural_lead_units_works(da_lead, lead_units): 129 | """Test that non-plural lead units work on lead units check.""" 130 | da_lead["lead"].attrs["units"] = lead_units[:-1] 131 | with pytest.warns(UserWarning) as record: 132 | has_valid_lead_units(da_lead) 133 | expected = f'The letter "s" was appended to the lead units; now {lead_units}.' 134 | assert record[0].message.args[0] == expected 135 | -------------------------------------------------------------------------------- /src/climpred/tests/test_demo_data.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from climpred.tutorial import FILE_ALIAS_DICT, load_dataset 4 | 5 | filepaths = list(FILE_ALIAS_DICT.keys()) 6 | 7 | 8 | @pytest.mark.parametrize("filepath", filepaths) 9 | def test_open_dataset_locally(filepath): 10 | """Opens all files listed in file_alias_dict.""" 11 | print(filepath) 12 | d = load_dataset(filepath) 13 | assert d.nbytes > 0 14 | 15 | 16 | def test_load_datasets_empty(): 17 | actual = load_dataset() 18 | assert actual is None 19 | 20 | 21 | @pytest.mark.parametrize("cache", [False, True]) 22 | def test_load_dataset_cache(cache): 23 | load_dataset(cache=cache) 24 | -------------------------------------------------------------------------------- /src/climpred/tests/test_effective_p_value.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from climpred.comparisons import HINDCAST_COMPARISONS 4 | from climpred.metrics import PM_METRICS 5 | 6 | kw = dict(dim="init", alignment="same_inits") 7 | 8 | 9 | @pytest.mark.parametrize("comparison", HINDCAST_COMPARISONS) 10 | def test_eff_sample_size_smaller_than_n_hindcast_hist_obs_1d( 11 | hindcast_hist_obs_1d, comparison 12 | ): 13 | """Tests that effective sample size is less than or equal to the actual sample size 14 | of the data.""" 15 | N = hindcast_hist_obs_1d.get_initialized().mean("member").count("init") 16 | eff_N = hindcast_hist_obs_1d.verify( 17 | comparison=comparison, 18 | **kw, 19 | metric="eff_n", 20 | ) 21 | assert (eff_N <= N).to_array().all() 22 | 23 | 24 | @pytest.mark.parametrize("comparison", HINDCAST_COMPARISONS) 25 | def test_eff_pearson_p_greater_or_equal_to_normal_p_hindcast_hist_obs_1d( 26 | hindcast_hist_obs_1d, comparison 27 | ): 28 | """Tests that the Pearson effective p value (more conservative) is greater than or 29 | equal to the standard p value.""" 30 | normal_p = hindcast_hist_obs_1d.verify( 31 | comparison=comparison, 32 | **kw, 33 | metric="pearson_r_p_value", 34 | ) 35 | eff_p = hindcast_hist_obs_1d.verify( 36 | comparison=comparison, 37 | **kw, 38 | metric="pearson_r_eff_p_value", 39 | ) 40 | assert (normal_p <= eff_p).to_array().all() 41 | 42 | 43 | @pytest.mark.parametrize("comparison", HINDCAST_COMPARISONS) 44 | def test_eff_spearman_p_greater_or_equal_to_normal_p_hindcast_hist_obs_1d( 45 | hindcast_hist_obs_1d, comparison 46 | ): 47 | """Tests that the Pearson effective p value (more conservative) is greater than or 48 | equal to the standard p value.""" 49 | normal_p = hindcast_hist_obs_1d.verify( 50 | comparison=comparison, 51 | **kw, 52 | metric="spearman_r_p_value", 53 | ) 54 | eff_p = hindcast_hist_obs_1d.verify( 55 | comparison=comparison, 56 | **kw, 57 | metric="spearman_r_eff_p_value", 58 | ) 59 | assert (normal_p <= eff_p).to_array().all() 60 | 61 | 62 | def test_effective_metrics_not_in_PM(): 63 | """Checks that the effective p value metrics are not included in PM.""" 64 | assert "effective_sample_size" not in PM_METRICS 65 | assert "pearson_r_eff_p_value" not in PM_METRICS 66 | assert "spearman_r_eff_p_value" not in PM_METRICS 67 | -------------------------------------------------------------------------------- /src/climpred/tests/test_graphics.py: -------------------------------------------------------------------------------- 1 | """Test graphics.py and PredictionEnsemble.plot().""" 2 | 3 | import pytest 4 | 5 | from climpred import HindcastEnsemble, PerfectModelEnsemble 6 | from climpred.checks import DimensionError 7 | from climpred.graphics import plot_bootstrapped_skill_over_leadyear 8 | 9 | from . import requires_matplotlib, requires_nc_time_axis 10 | 11 | ITERATIONS = 3 12 | 13 | 14 | @requires_matplotlib 15 | def test_PerfectModelEnsemble_plot_bootstrapped_skill_over_leadyear( 16 | perfectModelEnsemble_initialized_control, 17 | ): 18 | """ 19 | Checks plots from PerfectModelEnsemble.bootstrap(). 20 | """ 21 | res = perfectModelEnsemble_initialized_control.bootstrap( 22 | metric="pearson_r", 23 | iterations=ITERATIONS * 100, 24 | reference=["uninitialized", "persistence"], 25 | comparison="m2e", 26 | dim=["init", "member"], 27 | ) 28 | res_ax = plot_bootstrapped_skill_over_leadyear(res) 29 | assert res_ax is not None 30 | 31 | 32 | @requires_matplotlib 33 | @pytest.mark.parametrize("cmap", ["tab10", "jet"]) 34 | @pytest.mark.parametrize("show_members", [True, False]) 35 | @pytest.mark.parametrize("variable", ["tos", None]) 36 | def test_PerfectModelEnsemble_plot( 37 | PM_ds_initialized_1d, PM_ds_control_1d, variable, show_members, cmap 38 | ): 39 | """Test PredictionEnsemble.plot().""" 40 | pm = PerfectModelEnsemble(PM_ds_initialized_1d) 41 | kws = {"cmap": cmap, "show_members": show_members, "variable": variable} 42 | pm.plot(**kws) 43 | pm = pm.add_control(PM_ds_control_1d) 44 | pm.plot(**kws) 45 | pm = pm.generate_uninitialized() 46 | pm.plot(**kws) 47 | 48 | 49 | @requires_matplotlib 50 | def test_PerfectModelEnsemble_plot_fails_3d(PM_ds_initialized_3d): 51 | """Test PredictionEnsemble.plot().""" 52 | pm = PerfectModelEnsemble(PM_ds_initialized_3d) 53 | with pytest.raises(DimensionError) as excinfo: 54 | pm.plot() 55 | assert "does not allow dimensions other" in str(excinfo.value) 56 | 57 | 58 | @requires_matplotlib 59 | @pytest.mark.parametrize("x", ["time", "init"]) 60 | @pytest.mark.parametrize("show_members", [True, False]) 61 | @pytest.mark.parametrize("variable", ["SST", None]) 62 | def test_PredictionEnsemble_plot( 63 | hind_ds_initialized_1d, 64 | hist_ds_uninitialized_1d, 65 | reconstruction_ds_1d, 66 | observations_ds_1d, 67 | variable, 68 | show_members, 69 | x, 70 | ): 71 | """Test PredictionEnsemble.plot().""" 72 | he = HindcastEnsemble(hind_ds_initialized_1d) 73 | kws = {"show_members": show_members, "variable": variable, "x": x} 74 | he.plot(**kws) 75 | he = he.add_uninitialized(hist_ds_uninitialized_1d) 76 | he.plot(**kws) 77 | he = he.add_observations(reconstruction_ds_1d) 78 | he.plot(**kws) 79 | he = he.add_observations(observations_ds_1d) 80 | he.plot(**kws) 81 | 82 | if x == "time": 83 | pm = PerfectModelEnsemble(hind_ds_initialized_1d) 84 | pm.plot(**kws) 85 | pm = pm.add_control(hist_ds_uninitialized_1d.isel(member=0, drop=True)) 86 | pm.plot(**kws) 87 | 88 | 89 | @requires_matplotlib 90 | @requires_nc_time_axis 91 | @pytest.mark.parametrize("alignment", ["same_inits", None]) 92 | @pytest.mark.parametrize("return_xr", [False, True]) 93 | def test_HindcastEnsemble_plot_alignment(hindcast_hist_obs_1d, alignment, return_xr): 94 | """Test HindcastEnsemble.plot_alignment()""" 95 | import matplotlib 96 | import xarray as xr 97 | 98 | if return_xr: 99 | assert isinstance( 100 | hindcast_hist_obs_1d.plot_alignment( 101 | alignment=alignment, return_xr=return_xr 102 | ), 103 | xr.DataArray, 104 | ) 105 | else: 106 | assert isinstance( 107 | hindcast_hist_obs_1d.plot_alignment( 108 | alignment=alignment, return_xr=return_xr 109 | ), 110 | (xr.plot.facetgrid.FacetGrid, matplotlib.collections.QuadMesh), 111 | ) 112 | -------------------------------------------------------------------------------- /src/climpred/tests/test_hindcast_prediction.py: -------------------------------------------------------------------------------- 1 | """Test compute_persistence.""" 2 | 3 | from climpred.reference import compute_persistence 4 | 5 | 6 | def test_persistence_lead0_lead1( 7 | hind_ds_initialized_1d, hind_ds_initialized_1d_lead0, reconstruction_ds_1d 8 | ): 9 | """ 10 | Checks that compute persistence returns the same results with a lead-0 and lead-1 11 | framework. 12 | """ 13 | res1 = compute_persistence( 14 | hind_ds_initialized_1d, reconstruction_ds_1d, metric="rmse" 15 | ) 16 | res2 = compute_persistence( 17 | hind_ds_initialized_1d_lead0, reconstruction_ds_1d, metric="rmse" 18 | ) 19 | assert (res1.SST.values == res2.SST.values).all() 20 | -------------------------------------------------------------------------------- /src/climpred/tests/test_horizon.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pytest 3 | import xarray as xr 4 | 5 | from climpred.horizon import _last_item_cond_true, horizon 6 | 7 | 8 | @pytest.mark.parametrize("input", ["DataArray", "Dataset"]) 9 | @pytest.mark.parametrize("threshold,expected", [(1, 1), (2, 3), (3, 6), (3.5, 6)]) 10 | def test_least_item_cond_true(threshold, expected, input): 11 | """test `last_item_cond_true` on artificial data.""" 12 | ds = xr.DataArray( 13 | [1, 2, 2, 3, 3, 1, 4], dims="lead", coords={"lead": np.arange(1, 1 + 7)} 14 | ) 15 | if input == "Dataset": 16 | ds = ds.to_dataset(name="test") 17 | cond = ds <= threshold 18 | actual = _last_item_cond_true(cond, "lead") 19 | assert actual == expected 20 | assert isinstance(ds, type(actual)) 21 | 22 | 23 | def test_horizon_bootstrap_1d(perfectModelEnsemble_initialized_control): 24 | """test horizon for pm.bootstrap for 1d.""" 25 | bskill = perfectModelEnsemble_initialized_control.bootstrap( 26 | iterations=201, 27 | metric="rmse", 28 | comparison="m2e", 29 | dim=["member", "init"], 30 | reference="uninitialized", 31 | ) 32 | ph = horizon(bskill.sel(results="p", skill="uninitialized") <= 0.05) 33 | assert ph.tos.attrs["units"] == "years", print(ph.tos) 34 | assert int(ph.tos) in [ 35 | 2, 36 | 3, 37 | 4, 38 | 5, 39 | 6, 40 | 7, 41 | 8, 42 | ] # used to be 6, testing on the safe side 43 | 44 | 45 | def test_horizon_3d(hindcast_recon_3d): 46 | he = hindcast_recon_3d 47 | # set one grid to all nan mimicking land 48 | land = he._datasets["initialized"]["SST"][:, :, 0, 0] 49 | he._datasets["initialized"]["SST"][:, :, 0, 0] = land.where(land == 100000) 50 | # verify 51 | skill = he.verify( 52 | metric="acc", 53 | comparison="e2o", 54 | dim="init", 55 | alignment="maximize", 56 | reference=["persistence"], 57 | ) 58 | 59 | ph = horizon(skill.sel(skill="initialized") > skill.sel(skill="persistence")) 60 | assert "variable" not in skill.dims 61 | assert "variable" not in skill.coords 62 | # test all nan on land 63 | assert ph["SST"][0, 0].isnull() 64 | # test significant everywhere 65 | assert (ph >= 1).all() 66 | assert (ph.isel(nlat=-1) == 2).all() 67 | 68 | 69 | @pytest.mark.parametrize("smooth", [True, False]) 70 | def test_horizon_smooth(perfectModelEnsemble_initialized_control, smooth): 71 | """test horizon for pm.smooth(lead).verify.""" 72 | pm = perfectModelEnsemble_initialized_control 73 | if smooth: 74 | pm = pm.smooth( 75 | {"lead": 2}, how="mean" 76 | ) # converts lead to '1-2', '2-3', ... after verify 77 | skill = pm.verify( 78 | metric="rmse", 79 | comparison="m2e", 80 | dim=["member", "init"], 81 | reference="persistence", 82 | ) 83 | assert skill.lead.attrs["units"] == "years", print(skill.lead.attrs) 84 | # initialized better than persistence if RMSE smaller 85 | cond = skill.sel(skill="initialized", drop=True) < skill.sel( 86 | skill="persistence", drop=True 87 | ) 88 | ph = horizon(cond) 89 | assert ph.tos.attrs["units"] == "years", print("ph.tos.attrs = ", ph.tos.attrs) 90 | assert ph.tos.values == skill.lead.isel(lead=-1).values 91 | 92 | 93 | def test_horizon_weird_coords(): 94 | """Test horizon for weird coords.""" 95 | cond = xr.DataArray([True] * 10, dims="lead").to_dataset(name="SST") 96 | # Change leads to something weird 97 | cond["lead"] = [0.25, 0.75, 1, 3, 4, 5, 6, 7, 8, 9] 98 | assert _last_item_cond_true(cond, "lead") == 9.0 99 | 100 | cond[0] = False 101 | assert _last_item_cond_true(cond, "lead").isnull(), print(cond) 102 | 103 | cond[0] = True 104 | cond[1] = False 105 | assert _last_item_cond_true(cond, "lead") == cond.lead[0], print(cond) 106 | 107 | cond["lead"] = np.arange(cond.lead.size) 108 | cond[0] = True 109 | cond[1] = False 110 | assert _last_item_cond_true(cond, "lead") == 0.0, print(cond) 111 | -------------------------------------------------------------------------------- /src/climpred/tests/test_lead_time_resolutions.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import pytest 4 | import xarray as xr 5 | 6 | from climpred import HindcastEnsemble, PerfectModelEnsemble 7 | 8 | NLEADS = 5 9 | NMEMBERS = 3 10 | NINITS = 10 11 | START = "1990" 12 | 13 | HindcastEnsemble_verify_kw = dict( 14 | metric="rmse", comparison="e2o", dim="init", alignment="maximize" 15 | ) 16 | PerfectModelEnsemble_verify_kw = dict(metric="rmse", comparison="e2c", dim="init") 17 | 18 | 19 | @pytest.fixture( 20 | params=[ 21 | "seconds", 22 | "minutes", 23 | "hours", 24 | "days", 25 | "pentads", 26 | "weeks", 27 | "months", 28 | "seasons", 29 | "years", 30 | ] 31 | ) 32 | def HindcastEnsemble_time_resolution(request): 33 | """Create HindcastEnsemble of given lead time resolution.""" 34 | if request.param == "pentads": 35 | freq = "5D" 36 | elif request.param == "weeks": 37 | freq = "7D" 38 | elif request.param == "minutes": 39 | freq = "T" 40 | elif request.param in "months": 41 | freq = "MS" 42 | elif request.param == "seasons": 43 | freq = "QS" 44 | elif request.param == "years": 45 | freq = "YS" 46 | else: 47 | freq = request.param[0].upper() 48 | # create initialized 49 | init = xr.cftime_range(START, freq=freq, periods=NINITS) 50 | lead = np.arange(NLEADS) 51 | member = np.arange(NMEMBERS) 52 | initialized = xr.DataArray( 53 | np.random.rand(len(init), len(lead), len(member)), 54 | dims=["init", "lead", "member"], 55 | coords=[init, lead, member], 56 | ).to_dataset(name="var") 57 | initialized.lead.attrs["units"] = request.param 58 | 59 | # create observations 60 | time = xr.cftime_range(START, freq=freq, periods=NINITS + NLEADS) 61 | obs = xr.DataArray( 62 | np.random.rand(len(time)), dims=["time"], coords=[time] 63 | ).to_dataset(name="var") 64 | # climpred.PredictionEnsemble 65 | hindcast = HindcastEnsemble(initialized).add_observations(obs) 66 | return hindcast 67 | 68 | 69 | def test_HindcastEnsemble_time_resolution_verify(HindcastEnsemble_time_resolution): 70 | """Test that HindcastEnsemble.verify() in any lead time resolution works.""" 71 | assert ( 72 | HindcastEnsemble_time_resolution.verify(**HindcastEnsemble_verify_kw) 73 | .notnull() 74 | .any() 75 | ) 76 | 77 | 78 | def test_PerfectModelEnsemble_time_resolution_verify(HindcastEnsemble_time_resolution): 79 | """Test that PerfectModelEnsemble.verify() in any lead time resolution works.""" 80 | pm = PerfectModelEnsemble(HindcastEnsemble_time_resolution.get_initialized()) 81 | assert pm.verify(**PerfectModelEnsemble_verify_kw).notnull().any() 82 | 83 | 84 | @pytest.mark.parametrize( 85 | "lead_res", ["seconds", "minutes", "hours", "days", "pentads", "weeks"] 86 | ) 87 | def test_HindcastEnsemble_lead_pdTimedelta(hind_ds_initialized_1d, lead_res): 88 | """Test to see HindcastEnsemble can be initialized with lead as pd.Timedelta.""" 89 | if lead_res == "pentads": 90 | n, freq = 5, "d" 91 | else: 92 | n, freq = 1, lead_res[0].lower() 93 | initialized = hind_ds_initialized_1d 94 | 95 | initialized["lead"] = [ 96 | pd.Timedelta(f"{i * n} {freq}") for i in initialized.lead.values 97 | ] 98 | hindcast = HindcastEnsemble(initialized) 99 | 100 | assert hindcast.get_initialized().lead.attrs["units"] == lead_res 101 | 102 | 103 | def test_monthly_leads_real_example(hindcast_NMME_Nino34): 104 | skill = ( 105 | hindcast_NMME_Nino34.isel(lead=[0, 1, 2]) 106 | .sel(init=slice("2005", "2006")) 107 | .verify( 108 | metric="crps", 109 | comparison="m2o", 110 | dim=["init", "member"], 111 | alignment="same_inits", 112 | ) 113 | ) 114 | assert skill.to_array().notnull().all() 115 | 116 | 117 | def test_daily_leads_real_example(hindcast_S2S_Germany): 118 | skill = ( 119 | hindcast_S2S_Germany.isel(lead=[0, 1]) 120 | .sel(init=slice("2005", "2006")) 121 | .verify( 122 | metric="crps", 123 | comparison="m2o", 124 | dim=["init", "member"], 125 | alignment="same_inits", 126 | ) 127 | ) 128 | assert skill.to_array().notnull().all() 129 | -------------------------------------------------------------------------------- /src/climpred/tests/test_logging.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | 4 | def test_log_HindcastEnsemble_verify(hindcast_hist_obs_1d, caplog): 5 | """Test that verify logs.""" 6 | LOG_STRINGS = ["lead", "inits", "verifs"] 7 | with caplog.at_level(logging.INFO): 8 | hindcast_hist_obs_1d.verify( 9 | metric="mse", comparison="e2o", dim="init", alignment="same_verif" 10 | ) 11 | for i, record in enumerate(caplog.record_tuples): 12 | # Skip header information. 13 | if i >= 2: 14 | print(record) 15 | assert all(x in record[2] for x in LOG_STRINGS) 16 | assert "initialized" in record[2] 17 | -------------------------------------------------------------------------------- /src/climpred/tests/test_map.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import xarray as xr 3 | 4 | from climpred.stats import rm_poly 5 | from climpred.testing import assert_PredictionEnsemble 6 | 7 | xr.set_options(display_style="text") 8 | 9 | 10 | def test_PredictionEnsemble_raises_error(hindcast_hist_obs_1d): 11 | """Tests that PredictionEnsemble raises error.""" 12 | with pytest.raises(TypeError): 13 | hindcast_hist_obs_1d.smooth({"lead": 2.5}) # expects int 14 | 15 | 16 | def test_PredictionEnsemble_raises_warning(hindcast_hist_obs_1d): 17 | """Tests that PredictionEnsemble raises warning.""" 18 | with pytest.warns(UserWarning): 19 | hindcast_hist_obs_1d.map(rm_poly, dim="init", deg=2) 20 | 21 | 22 | def test_PredictionEnsemble_xr_calls(hindcast_hist_obs_1d): 23 | """Tests that PredictionEnsemble passes through xarray calls.""" 24 | assert ( 25 | hindcast_hist_obs_1d.isel(lead=[1, 2]) 26 | .get_initialized() 27 | .identical(hindcast_hist_obs_1d.get_initialized().isel(lead=[1, 2])) 28 | ) 29 | 30 | 31 | def test_PredictionEnsemble_map_dim_or(hindcast_hist_obs_1d): 32 | """Tests that PredictionEnsemble allows dim0_or_dim1 as kwargs without UserWarning.""" 33 | with pytest.warns(None): # no warnings 34 | he_or = hindcast_hist_obs_1d.map(rm_poly, dim="init_or_time", deg=2) 35 | assert he_or != hindcast_hist_obs_1d 36 | 37 | with pytest.warns(UserWarning) as record: # triggers warnings 38 | he_chained = hindcast_hist_obs_1d.map(rm_poly, dim="init", deg=2).map( 39 | rm_poly, dim="time", deg=2 40 | ) 41 | assert he_chained != hindcast_hist_obs_1d 42 | 43 | assert len(record) >= 3 # for init, uninit and obs 44 | # for r in record: 45 | # assert "Error due to " in str(r.message) 46 | 47 | assert_PredictionEnsemble(he_or, he_chained) 48 | 49 | 50 | def test_PredictionEnsemble_map_dim_or_fails_if_both_dims_in_dataset( 51 | hindcast_hist_obs_1d, 52 | ): 53 | """Tests PredictionEnsemble dim0_or_dim1 in kwargs fails if both in all dataset.""" 54 | with pytest.raises(ValueError, match="cannot be both in"): 55 | hindcast_hist_obs_1d.map(rm_poly, dim="init_or_lead", deg=2) 56 | -------------------------------------------------------------------------------- /src/climpred/tests/test_metrics.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pytest 3 | import xarray as xr 4 | from xarray.testing import assert_allclose 5 | 6 | from climpred.comparisons import PM_COMPARISONS 7 | from climpred.metrics import __ALL_METRICS__ as all_metrics, Metric, __pearson_r 8 | from climpred.stats import rm_poly 9 | 10 | 11 | def my_mse_function(forecast, verif, dim=None, **metric_kwargs): 12 | # function 13 | return ((forecast - verif) ** 2).mean(dim) 14 | 15 | 16 | my_mse = Metric( 17 | name="mse", 18 | function=my_mse_function, 19 | positive=True, 20 | probabilistic=False, 21 | unit_power=2, 22 | long_name="MSE", 23 | aliases=["mSe", "<< 1]) 72 | print(f"Duplicate metrics: {duplicates}") 73 | assert len(duplicates) == 0 74 | 75 | 76 | def test_contingency(hindcast_hist_obs_1d): 77 | """Test contingency table perfect results.""" 78 | hindcast = hindcast_hist_obs_1d 79 | hindcast = hindcast.map(xr.ones_like) 80 | category_edges = np.array([-0.5, 0.0, 0.5, 0.9, 1.1]) 81 | metric_kwargs = { 82 | "forecast_category_edges": category_edges, 83 | "observation_category_edges": category_edges, 84 | "score": "table", 85 | } 86 | skill = hindcast.verify( 87 | metric="contingency", 88 | comparison="m2o", 89 | dim=["member", "init"], 90 | alignment="same_verifs", 91 | **metric_kwargs, 92 | ).SST 93 | 94 | assert skill.isel(observations_category=-1, forecasts_category=-1).notnull().all() 95 | assert ( 96 | skill.isel( 97 | observations_category=slice(None, -1), forecasts_category=slice(None, -1) 98 | ) 99 | == 0.0 100 | ).all() 101 | 102 | 103 | def test_overconfident(hindcast_hist_obs_1d): 104 | """Test rank_histogram and less for overconfident/underdisperive.""" 105 | hindcast = hindcast_hist_obs_1d.copy() 106 | hindcast = hindcast.map(rm_poly, dim="init_or_time", deg=2) 107 | hindcast._datasets["initialized"] *= 0.3 # make overconfident 108 | less = hindcast.verify( 109 | metric="less", 110 | comparison="m2o", 111 | dim=["member", "init"], 112 | alignment="same_verifs", 113 | ).SST 114 | 115 | rh = hindcast.verify( 116 | metric="rank_histogram", 117 | comparison="m2o", 118 | dim=["member", "init"], 119 | alignment="same_verifs", 120 | ).SST 121 | 122 | assert ( 123 | rh.isel(rank=[0, -1]) > rh.isel(rank=rh["rank"].size // 2) 124 | ).all() # outer ranks larger 125 | assert (less < 0).all() # underdisperive: neg less 126 | 127 | 128 | def test_underconfident(hindcast_hist_obs_1d): 129 | """Test rank_histogram and less for underconfident/overdisperive.""" 130 | hindcast = hindcast_hist_obs_1d.copy() 131 | hindcast = hindcast.map(rm_poly, dim="init_or_time", deg=2) 132 | hindcast._datasets["initialized"] *= 30 # make underconfident 133 | less = hindcast.verify( 134 | metric="less", 135 | comparison="m2o", 136 | dim=["member", "init"], 137 | alignment="same_verifs", 138 | ).SST 139 | 140 | rh = hindcast.verify( 141 | metric="rank_histogram", 142 | comparison="m2o", 143 | dim=["member", "init"], 144 | alignment="same_verifs", 145 | ).SST 146 | 147 | assert ( 148 | (rh.isel(rank=[0, -1]) < rh.isel(rank=rh["rank"].size // 2)) 149 | .isel(lead=slice(-3, None)) 150 | .all() 151 | ) # outer ranks smaller 152 | assert (less > 0).all() # overdisperive: pos less 153 | -------------------------------------------------------------------------------- /src/climpred/tests/test_options.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import climpred 4 | 5 | 6 | @pytest.mark.skip # some error when unpinning xarray <= 2022.10.0 7 | @pytest.mark.parametrize("cross_validate", [False, True]) 8 | def test_seasonality_remove_bias(hindcast_recon_1d_dm, cross_validate): 9 | """Test the climpred.set_option(seasonality) changes bias reduction.""" 10 | hindcast = hindcast_recon_1d_dm 11 | hindcast._datasets["initialized"] = ( 12 | hindcast.get_initialized().resample(init="1MS").interpolate("linear") 13 | ) 14 | 15 | alignment = "maximize" 16 | kw = { 17 | "metric": "mse", 18 | "comparison": "e2o", 19 | "dim": "init", 20 | "alignment": alignment, 21 | "reference": None, 22 | } 23 | 24 | with climpred.set_options(seasonality="dayofyear"): 25 | dayofyear_seasonality = hindcast.remove_bias( 26 | alignment=alignment, cross_validate=cross_validate 27 | ) 28 | with climpred.set_options(seasonality="weekofyear"): 29 | weekofyear_seasonality = hindcast.remove_bias( 30 | alignment=alignment, cross_validate=cross_validate 31 | ) 32 | 33 | assert not dayofyear_seasonality.get_initialized().to_array().isnull().all() 34 | assert not weekofyear_seasonality.get_initialized().to_array().isnull().all() 35 | assert not weekofyear_seasonality.get_initialized().equals( 36 | dayofyear_seasonality.get_initialized() 37 | ) 38 | assert not weekofyear_seasonality.verify(**kw).equals( 39 | dayofyear_seasonality.verify(**kw) 40 | ) 41 | 42 | 43 | def test_seasonality_climatology(hindcast_recon_1d_dm): 44 | """Test the climpred.set_option(seasonality) changes climatology.""" 45 | hindcast = hindcast_recon_1d_dm 46 | alignment = "maximize" 47 | kw = { 48 | "metric": "mse", 49 | "comparison": "e2o", 50 | "dim": "init", 51 | "alignment": alignment, 52 | "reference": "climatology", 53 | } 54 | with climpred.set_options(seasonality="dayofyear"): 55 | dayofyear_seasonality = hindcast.verify(**kw).sel(skill="climatology") 56 | with climpred.set_options(seasonality="month"): 57 | month_seasonality = hindcast.verify(**kw).sel(skill="climatology") 58 | assert not month_seasonality.identical(dayofyear_seasonality) 59 | 60 | 61 | @pytest.mark.parametrize("option_bool", [False, True]) 62 | def test_option_warn_for_failed_PredictionEnsemble_xr_call( 63 | hindcast_recon_1d_dm, option_bool 64 | ): 65 | with climpred.set_options(warn_for_failed_PredictionEnsemble_xr_call=option_bool): 66 | with pytest.warns(None if not option_bool else UserWarning) as record: 67 | hindcast_recon_1d_dm.sel(lead=[1, 2]) 68 | if not option_bool: 69 | assert len(record) == 0, print(record[0]) 70 | 71 | 72 | @pytest.mark.parametrize("option_bool", [False, True]) 73 | def test_climpred_warnings(hindcast_recon_1d_dm, option_bool): 74 | with climpred.set_options(warn_for_failed_PredictionEnsemble_xr_call=True): 75 | with climpred.set_options(climpred_warnings=option_bool): 76 | with pytest.warns(UserWarning if option_bool else None) as record: 77 | hindcast_recon_1d_dm.sel(lead=[1, 2]) 78 | if not option_bool: 79 | assert len(record) == 0, print(record[0]) 80 | 81 | 82 | def test_option_resample_iterations_func(hindcast_recon_1d_ym): 83 | """Singleton dimension makes resample_iterations_idx fail for py < 3.11""" 84 | with climpred.set_options(resample_iterations_func="resample_iterations_idx"): 85 | hindcast_recon_1d_ym.expand_dims("lon").bootstrap( 86 | metric="mse", 87 | comparison="e2o", 88 | dim="init", 89 | alignment="maximize", 90 | iterations=2, 91 | resample_dim="member", 92 | ) 93 | 94 | with climpred.set_options(resample_iterations_func="resample_iterations"): 95 | hindcast_recon_1d_ym.expand_dims("lon").bootstrap( 96 | metric="mse", 97 | comparison="e2o", 98 | dim="init", 99 | alignment="maximize", 100 | iterations=2, 101 | resample_dim="member", 102 | ) 103 | -------------------------------------------------------------------------------- /src/climpred/tests/test_perfect_model_prediction.py: -------------------------------------------------------------------------------- 1 | """Test compute_perfect_model.""" 2 | 3 | import pytest 4 | import xarray as xr 5 | 6 | from climpred.reference import compute_persistence 7 | 8 | xr.set_options(display_style="text") 9 | v = "tos" 10 | 11 | 12 | @pytest.mark.parametrize("metric", ["mse", "pearson_r"]) 13 | def test_compute_persistence_lead0_lead1( 14 | PM_ds_initialized_1d, PM_ds_initialized_1d_lead0, PM_ds_control_1d, metric 15 | ): 16 | """ 17 | Checks that persistence forecast results are identical for a lead 0 and lead 1 setup 18 | """ 19 | res1 = compute_persistence( 20 | PM_ds_initialized_1d, PM_ds_control_1d, metric=metric, alignment="same_inits" 21 | ) 22 | res2 = compute_persistence( 23 | PM_ds_initialized_1d_lead0, 24 | PM_ds_control_1d, 25 | metric=metric, 26 | alignment="same_inits", 27 | ) 28 | assert (res1[v].values == res2[v].values).all() 29 | -------------------------------------------------------------------------------- /src/climpred/tests/test_preprocessing.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import numpy as np 4 | import pytest 5 | import xarray as xr 6 | 7 | from climpred.constants import CLIMPRED_ENSEMBLE_DIMS 8 | from climpred.preprocessing.shared import ( 9 | load_hindcast, 10 | rename_SLM_to_climpred_dims, 11 | rename_to_climpred_dims, 12 | set_integer_time_axis, 13 | ) 14 | 15 | on_mistral = False 16 | try: 17 | host = os.environ["HOSTNAME"] 18 | for node in ["mlogin", "mistralpp"]: 19 | if node in host: 20 | on_mistral = True 21 | from climpred.preprocessing.mpi import get_path 22 | except KeyError: 23 | pass 24 | 25 | # check for intake_esm to be installed 26 | try: 27 | import intake 28 | import intake_esm 29 | 30 | print(intake_esm.__version__) 31 | intake_esm_loaded = True 32 | except ImportError: 33 | intake_esm_loaded = False 34 | 35 | 36 | def preprocess_1var(ds, v="global_primary_production"): 37 | return ds[v].to_dataset(name=v).squeeze() 38 | 39 | 40 | @pytest.mark.mistral 41 | @pytest.mark.skipif(not on_mistral, reason="requires to be on mistral.dkrz.de") 42 | @pytest.mark.parametrize( 43 | "inits,members", 44 | [(range(1961, 1964), range(3, 6)), (range(1970, 1972), range(1, 3))], 45 | ) 46 | def test_load_hindcast(inits, members): 47 | """Test that `load_hindcast` loads the appropriate files.""" 48 | actual = load_hindcast( 49 | inits=inits, 50 | members=members, 51 | preprocess=preprocess_1var, 52 | get_path=get_path, 53 | ) 54 | assert isinstance(actual, xr.Dataset) 55 | assert (actual["init"].values == inits).all() 56 | assert (actual["member"].values == members).all() 57 | assert "global_primary_production" in actual.data_vars 58 | assert len(actual.data_vars) == 1 59 | 60 | 61 | @pytest.mark.mistral 62 | @pytest.mark.skipif(not on_mistral, reason="requires to be on mistral.dkrz.de") 63 | @pytest.mark.skipif(not intake_esm_loaded, reason="requires intake_esm to be installed") 64 | def test_climpred_pre_with_intake_esm(): 65 | """Test that `preprocess` including `set_integer_time_axis` enables concatination 66 | of all hindcast into one xr.object.""" 67 | col_url = "/home/mpim/m300524/intake-esm-datastore/catalogs/mistral-cmip6.json" 68 | col = intake.open_esm_datastore(col_url) 69 | # load 2 members for 2 inits from one model 70 | query = dict( 71 | experiment_id=["dcppA-hindcast"], 72 | table_id="Amon", 73 | member_id=["r1i1p1f1", "r2i1p1f1"], 74 | dcpp_init_year=[1970, 1971], 75 | variable_id="tas", 76 | source_id="MPI-ESM1-2-HR", 77 | ) 78 | cat = col.search(**query) 79 | cdf_kwargs = {"chunks": {"time": 12}, "decode_times": False} 80 | 81 | def preprocess(ds): 82 | # extract tiny spatial and temporal subset 83 | ds = ds.isel(lon=[50, 51, 52], lat=[50, 51, 52], time=np.arange(12 * 2)) 84 | # make time dim identical 85 | ds = set_integer_time_axis(ds) 86 | return ds 87 | 88 | dset_dict = cat.to_dataset_dict(cdf_kwargs=cdf_kwargs, preprocess=preprocess) 89 | # get first dict value 90 | ds = dset_dict[list(dset_dict.keys())[0]] 91 | assert isinstance(ds, xr.Dataset) 92 | ds = rename_to_climpred_dims(ds) 93 | # check for all CLIMPRED_DIMS 94 | for c in CLIMPRED_ENSEMBLE_DIMS: 95 | assert c in ds.coords 96 | # check for requested dimsizes 97 | assert ds.member.size == 2 98 | assert ds.init.size == 2 99 | 100 | 101 | def test_rename_SLM(da_SLM): 102 | """Check that dimensions in input are renamed by rename_SLM_to_climpred_dims.""" 103 | da_renamed = rename_SLM_to_climpred_dims(da_SLM) 104 | for dim in CLIMPRED_ENSEMBLE_DIMS: 105 | assert dim in da_renamed.dims 106 | for dim in da_SLM.dims: 107 | assert dim not in da_renamed.dims 108 | 109 | 110 | def test_rename_climpred_dims(da_dcpp): 111 | """Check that dimensions in input are renamed by rename_to_climpred_dims.""" 112 | da_renamed = rename_to_climpred_dims(da_dcpp) 113 | for dim in CLIMPRED_ENSEMBLE_DIMS: 114 | assert dim in da_renamed.dims 115 | for dim in da_dcpp.dims: 116 | assert dim not in da_renamed.dims 117 | -------------------------------------------------------------------------------- /src/climpred/tests/test_reference.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import xarray as xr 3 | 4 | from climpred import set_options 5 | 6 | 7 | @pytest.mark.parametrize("seasonality", ["month", "season", "dayofyear", "weekofyear"]) 8 | @pytest.mark.parametrize("reference", ["persistence", "climatology", "uninitialized"]) 9 | def test_HindcastEnsemble_verify_reference( 10 | hindcast_hist_obs_1d, seasonality, reference 11 | ): 12 | with set_options(seasonality=seasonality): 13 | hindcast_hist_obs_1d.verify( 14 | metric="mse", 15 | comparison="e2o", 16 | dim="init", 17 | alignment="same_verifs", 18 | reference=reference, 19 | ) 20 | 21 | 22 | @pytest.mark.parametrize("comparison", ["m2m", "m2e", "m2c", "e2c"]) 23 | def test_PerfectModelEnsemble_verify_persistence_from_first_lead( 24 | perfectModelEnsemble_initialized_control, comparison 25 | ): 26 | """Test compute_persistence_from_first_lead vs compute_persistence.""" 27 | kw = dict( 28 | metric="mse", 29 | comparison=comparison, 30 | dim="init" if comparison == "e2c" else ["member", "init"], 31 | reference=["persistence", "climatology"], 32 | ) 33 | with set_options(PerfectModel_persistence_from_initialized_lead_0=True): 34 | new_persistence = perfectModelEnsemble_initialized_control.verify(**kw) 35 | with set_options(PerfectModel_persistence_from_initialized_lead_0=False): 36 | old_persistence = perfectModelEnsemble_initialized_control.verify(**kw) 37 | assert not new_persistence.sel(skill="persistence").equals( 38 | old_persistence.sel(skill="persistence") 39 | ) 40 | assert "forecast_member" not in new_persistence.dims 41 | assert "forecast_member" not in old_persistence.dims 42 | 43 | 44 | @pytest.mark.parametrize("call", ["verify", "bootstrap"]) 45 | def test_PerfectModelEnsemble_persistence_from_first_lead_warning_lead_non_zero( 46 | perfectModelEnsemble_initialized_control, call 47 | ): 48 | """Test that compute_persistence_from_first_lead warns if first lead not zero.""" 49 | kw = dict( 50 | metric="mse", comparison="m2e", dim=["member", "init"], reference="persistence" 51 | ) 52 | if call == "bootstrap": 53 | kw["iterations"] = 2 54 | with set_options(PerfectModel_persistence_from_initialized_lead_0=True): 55 | with pytest.warns(UserWarning, match="Calculate persistence from lead=1"): 56 | # perfectModelEnsemble_initialized_control starts with lead 1 57 | getattr(perfectModelEnsemble_initialized_control, call)(**kw) 58 | 59 | # with pytest.does_not_warn(FutureWarning): 60 | with xr.set_options(keep_attrs=True): 61 | perfectModelEnsemble_initialized_control._datasets["initialized"][ 62 | "lead" 63 | ] = ( 64 | perfectModelEnsemble_initialized_control._datasets["initialized"][ 65 | "lead" 66 | ] 67 | - 1 68 | ) 69 | getattr(perfectModelEnsemble_initialized_control, call)(**kw) 70 | -------------------------------------------------------------------------------- /src/climpred/tests/test_relative_entropy.py: -------------------------------------------------------------------------------- 1 | """Test relative_entropy.py""" 2 | 3 | import pytest 4 | 5 | from climpred.graphics import plot_relative_entropy 6 | from climpred.relative_entropy import ( 7 | bootstrap_relative_entropy, 8 | compute_relative_entropy, 9 | ) 10 | 11 | from . import requires_eofs 12 | 13 | 14 | @requires_eofs 15 | def test_compute_relative_entropy(PM_ds_initialized_3d, PM_ds_control_3d): 16 | """ 17 | Checks that there are no NaNs. 18 | """ 19 | actual = compute_relative_entropy( 20 | PM_ds_initialized_3d.tos, PM_ds_control_3d.tos, nmember_control=5, neofs=2 21 | ) 22 | actual_any_nan = actual.isnull().any() 23 | for var in actual_any_nan.data_vars: 24 | assert not actual_any_nan[var] 25 | 26 | 27 | @requires_eofs 28 | def test_bootstrap_relative_entropy(PM_ds_initialized_3d, PM_ds_control_3d): 29 | """ 30 | Checks that there are no NaNs. 31 | """ 32 | actual = bootstrap_relative_entropy( 33 | PM_ds_initialized_3d.tos, 34 | PM_ds_control_3d.tos, 35 | nmember_control=5, 36 | neofs=2, 37 | bootstrap=2, 38 | ) 39 | actual_any_nan = actual.isnull() 40 | for var in actual_any_nan.data_vars: 41 | assert not actual_any_nan[var] 42 | 43 | 44 | @requires_eofs 45 | @pytest.mark.skip 46 | def test_plot_relative_entropy(PM_ds_initialized_3d, PM_ds_control_3d): 47 | res = compute_relative_entropy( 48 | PM_ds_initialized_3d, PM_ds_control_3d, nmember_control=5, neofs=2 49 | ) 50 | threshold = bootstrap_relative_entropy( 51 | PM_ds_initialized_3d.tos, 52 | PM_ds_control_3d.tos, 53 | nmember_control=5, 54 | neofs=2, 55 | bootstrap=2, 56 | ) 57 | res_ax = plot_relative_entropy(res, threshold) 58 | assert res_ax is not None 59 | -------------------------------------------------------------------------------- /src/climpred/tests/test_repr.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import xarray as xr 3 | 4 | from climpred.classes import HindcastEnsemble, PerfectModelEnsemble 5 | 6 | 7 | def assert_repr(pe, display_style): 8 | if display_style == "text": 9 | repr_str = pe.__repr__() 10 | elif display_style == "html": 11 | repr_str = pe._repr_html_() 12 | assert "Ensemble" in repr_str 13 | if display_style == "text": 14 | assert "" not in repr_str 15 | elif display_style == "html": 16 | assert "icon" in repr_str 17 | 18 | 19 | @pytest.mark.parametrize("display_style", ("html", "text")) 20 | def test_repr_PerfectModelEnsemble( 21 | PM_ds_initialized_1d, PM_ds_control_1d, display_style 22 | ): 23 | """Test html and text repr.""" 24 | with xr.set_options(display_style=display_style): 25 | pm = PerfectModelEnsemble(PM_ds_initialized_1d) 26 | assert_repr(pm, display_style) 27 | pm = pm.add_control(PM_ds_control_1d) 28 | assert_repr(pm, display_style) 29 | pm = pm.generate_uninitialized() 30 | assert_repr(pm, display_style) 31 | 32 | 33 | @pytest.mark.parametrize("display_style", ("html", "text")) 34 | def test_repr_HindcastEnsemble( 35 | hind_ds_initialized_1d, 36 | hist_ds_uninitialized_1d, 37 | observations_ds_1d, 38 | display_style, 39 | ): 40 | """Test html repr.""" 41 | with xr.set_options(display_style=display_style): 42 | he = HindcastEnsemble(hind_ds_initialized_1d) 43 | assert_repr(he, display_style) 44 | he = he.add_uninitialized(hist_ds_uninitialized_1d) 45 | assert_repr(he, display_style) 46 | he = he.add_observations(observations_ds_1d) 47 | assert_repr(he, display_style) 48 | # no uninit 49 | he = HindcastEnsemble(hind_ds_initialized_1d) 50 | he = he.add_observations(observations_ds_1d) 51 | assert_repr(he, display_style) 52 | -------------------------------------------------------------------------------- /src/climpred/tests/test_stats.py: -------------------------------------------------------------------------------- 1 | import dask 2 | import pytest 3 | import xarray as xr 4 | from xarray.core.utils import Frozen 5 | from xarray.testing import assert_allclose 6 | 7 | from climpred.bootstrap import dpp_threshold 8 | from climpred.stats import decorrelation_time, dpp 9 | 10 | try: 11 | from climpred.bootstrap import varweighted_mean_period_threshold 12 | from climpred.stats import varweighted_mean_period 13 | except ImportError: 14 | pass 15 | 16 | from . import requires_xrft 17 | 18 | ITERATIONS = 2 19 | 20 | 21 | @pytest.mark.parametrize("chunk", (True, False)) 22 | def test_dpp(PM_ds_control_3d, chunk): 23 | """Check for positive diagnostic potential predictability in NA SST.""" 24 | res = dpp(PM_ds_control_3d, chunk=chunk) 25 | assert res.mean() > 0 26 | 27 | 28 | @requires_xrft 29 | @pytest.mark.parametrize("func", (varweighted_mean_period, decorrelation_time)) 30 | def test_potential_predictability_likely(PM_ds_control_3d, func): 31 | """Check for positive diagnostic potential predictability in NA SST.""" 32 | res = func(PM_ds_control_3d) 33 | assert res.mean() > 0 34 | 35 | 36 | def test_bootstrap_dpp_sig50_similar_dpp(PM_ds_control_3d): 37 | sig = 50 38 | actual = dpp_threshold(PM_ds_control_3d, iterations=ITERATIONS, sig=sig).drop_vars( 39 | "quantile" 40 | ) 41 | expected = dpp(PM_ds_control_3d) 42 | xr.testing.assert_allclose(actual, expected, atol=0.5, rtol=0.5) 43 | 44 | 45 | @requires_xrft 46 | def test_bootstrap_vwmp_sig50_similar_vwmp(PM_ds_control_3d): 47 | sig = 50 48 | actual = varweighted_mean_period_threshold( 49 | PM_ds_control_3d, iterations=ITERATIONS, sig=sig 50 | ).drop_vars("quantile") 51 | expected = varweighted_mean_period(PM_ds_control_3d) 52 | xr.testing.assert_allclose(actual, expected, atol=2, rtol=0.5) 53 | 54 | 55 | def test_bootstrap_func_multiple_sig_levels(PM_ds_control_3d): 56 | sig = [5, 95] 57 | actual = dpp_threshold(PM_ds_control_3d, iterations=ITERATIONS, sig=sig) 58 | assert actual["quantile"].size == len(sig) 59 | assert (actual.isel(quantile=0) <= actual.isel(quantile=1)).to_array().all() 60 | 61 | 62 | @requires_xrft 63 | @pytest.mark.parametrize("step", [1, 2, -1]) 64 | @pytest.mark.parametrize( 65 | "func", 66 | [dpp, varweighted_mean_period, decorrelation_time], 67 | ) 68 | def test_stats_functions_dask_chunks(PM_ds_control_3d, func, step): 69 | """Check whether selected stats functions be chunked and computed along other 70 | dim.""" 71 | dim = "time" 72 | for chunk_dim in PM_ds_control_3d.isel({dim: 0}).dims: 73 | control_chunked = PM_ds_control_3d.chunk({chunk_dim: step}) 74 | res_chunked = func(control_chunked, dim=dim) 75 | res = func(PM_ds_control_3d, dim=dim) 76 | # check for chunks 77 | assert dask.is_dask_collection(res_chunked) 78 | assert res_chunked.chunks is not None 79 | # check for no chunks 80 | assert not dask.is_dask_collection(res) 81 | assert res.chunks == Frozen({}) 82 | # check for identical result 83 | assert_allclose(res, res_chunked.compute(), atol=0.001, rtol=1e-4) 84 | -------------------------------------------------------------------------------- /src/climpred/tests/test_uninitialized.py: -------------------------------------------------------------------------------- 1 | """Test compute_uninitialized.""" 2 | 3 | import pytest 4 | 5 | from climpred.constants import VALID_ALIGNMENTS 6 | from climpred.reference import compute_uninitialized 7 | 8 | 9 | @pytest.mark.parametrize("alignment", VALID_ALIGNMENTS) 10 | def test_compute_uninitialized_alignment( 11 | hind_ds_initialized_1d, reconstruction_ds_1d, hist_ds_uninitialized_1d, alignment 12 | ): 13 | """Tests that compute_uninitialized works for various alignments.""" 14 | res = ( 15 | compute_uninitialized( 16 | hind_ds_initialized_1d, 17 | hist_ds_uninitialized_1d, 18 | reconstruction_ds_1d, 19 | metric="pr", 20 | comparison="e2o", 21 | alignment=alignment, 22 | ) 23 | .isnull() 24 | .any() 25 | ) 26 | for var in res.data_vars: 27 | assert not res[var] 28 | 29 | 30 | def test_compute_uninitialized_same_verifs( 31 | hind_ds_initialized_1d, reconstruction_ds_1d, hist_ds_uninitialized_1d 32 | ): 33 | """Tests that uninitialized skill is same at all leads for `same_verifs` 34 | alignment.""" 35 | res = compute_uninitialized( 36 | hind_ds_initialized_1d, 37 | hist_ds_uninitialized_1d, 38 | reconstruction_ds_1d, 39 | metric="pr", 40 | comparison="e2o", 41 | alignment="same_verifs", 42 | ).to_array() 43 | assert ((res - res[0]) == 0).all() 44 | 45 | 46 | @pytest.mark.parametrize( 47 | "dim", ["init", ["member", "init"]], ids=["init", "member_init"] 48 | ) 49 | def test_verify_uninitialized_keeps_member_dim(hindcast_hist_obs_1d, dim): 50 | """https://github.com/pangeo-data/climpred/issues/735""" 51 | skill = hindcast_hist_obs_1d.verify( 52 | dim=dim, 53 | metric="mse", 54 | comparison="m2o", 55 | reference="uninitialized", 56 | alignment="maximize", 57 | ).SST 58 | if "member" in dim: 59 | assert "member" not in skill.dims 60 | else: 61 | assert "member" in skill.dims 62 | assert (skill.std("member") > 0).all() 63 | 64 | 65 | @pytest.mark.parametrize( 66 | "dim", ["init", ["member", "init"]], ids=["init", "member_init"] 67 | ) 68 | def test_bootstrap_uninitialized_no_member_dim_if_dim_member(hindcast_hist_obs_1d, dim): 69 | """https://github.com/pangeo-data/climpred/issues/735""" 70 | skill = hindcast_hist_obs_1d.bootstrap( 71 | dim=dim, 72 | metric="mse", 73 | comparison="m2o", 74 | reference="uninitialized", 75 | alignment="maximize", 76 | iterations=2, 77 | resample_dim="member", 78 | ).SST 79 | if "member" in dim: 80 | assert "member" not in skill.dims 81 | else: 82 | assert "member" in skill.dims 83 | -------------------------------------------------------------------------------- /src/climpred/tests/test_valid_time.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pytest 3 | import xarray as xr 4 | 5 | from climpred import HindcastEnsemble 6 | 7 | 8 | @pytest.mark.parametrize( 9 | "init_freq,lead_unit", 10 | [ 11 | ("AS-JUL", "years"), 12 | ("AS-JUL", "months"), 13 | ("AS-JUL", "seasons"), 14 | ("MS", "months"), 15 | ("3M", "days"), 16 | ("7D", "days"), 17 | ("1D", "hours"), 18 | ("1H", "seconds"), 19 | ], 20 | ) 21 | @pytest.mark.parametrize("calendar", ["ProlepticGregorian", "standard", "360_day"]) 22 | def test_hindcastEnsemble_init_time(init_freq, lead_unit, calendar): 23 | """Test to see HindcastEnsemble can be initialized and creates valid_time 24 | coordinate depending on init and lead for different calendars and lead units.""" 25 | p = 3 26 | nlead = 2 27 | lead = [0, 1] 28 | 29 | init = xr.cftime_range(start="2000", freq=init_freq, periods=p) 30 | data = np.random.rand(p, nlead) 31 | init = xr.DataArray( 32 | data, 33 | dims=["init", "lead"], 34 | coords={"init": init, "lead": lead}, 35 | name="initialized", 36 | ) 37 | init.lead.attrs["units"] = lead_unit 38 | coords = HindcastEnsemble(init).coords 39 | assert "valid_time" in coords 40 | assert (coords["valid_time"].isel(lead=0, drop=True) == coords["init"]).all() 41 | assert (coords["valid_time"].isel(lead=1, drop=True) != coords["init"]).all() 42 | 43 | 44 | @pytest.mark.parametrize( 45 | "reference", [None, "climatology", "uninitialized", "persistence"] 46 | ) 47 | @pytest.mark.parametrize("alignment", ["same_verif", "same_inits", "maximize"]) 48 | def test_verify_valid_time(hindcast_hist_obs_1d, alignment, reference): 49 | """Test that verify has 2d valid_time coordinate.""" 50 | result = hindcast_hist_obs_1d.verify( 51 | metric="rmse", 52 | comparison="e2o", 53 | dim=[], 54 | alignment=alignment, 55 | reference=reference, 56 | ) 57 | assert "time" not in result.dims 58 | assert "valid_time" in result.coords 59 | assert len(result.coords["valid_time"].dims) == 2 60 | if reference: 61 | assert set(result.dims) == set(["init", "lead", "skill"]) 62 | else: 63 | assert set(result.dims) == set(["init", "lead"]) 64 | 65 | 66 | @pytest.mark.parametrize( 67 | "reference", [None, "climatology", "uninitialized", "persistence"] 68 | ) 69 | @pytest.mark.parametrize("alignment", ["same_inits", "maximize"]) 70 | def test_bootstrap_valid_time(hindcast_hist_obs_1d, alignment, reference): 71 | """Test that bootstrap has 2d valid_time coordinate.""" 72 | result = hindcast_hist_obs_1d.bootstrap( 73 | iterations=2, 74 | metric="rmse", 75 | comparison="e2o", 76 | dim=[], 77 | alignment=alignment, 78 | reference=reference, 79 | ) 80 | assert "time" not in result.dims 81 | assert "valid_time" in result.coords 82 | assert len(result.coords["valid_time"].dims) == 2 83 | if reference: 84 | assert set(result.dims) == set(["init", "lead", "results", "skill"]) 85 | else: 86 | assert set(result.dims) == set(["init", "lead", "results"]) 87 | 88 | 89 | @pytest.mark.parametrize( 90 | "start,freq,units,expected_shift", 91 | [("2000-01-01", "6MS", "years", "12MS"), ("2000-01-01", "MS", "seasons", "3MS")], 92 | ) 93 | def test_valid_time_from_larger_than_monthly_init_longer_freq_lead( 94 | start, freq, units, expected_shift 95 | ): 96 | """Raised in https://github.com/pangeo-data/climpred/issues/698""" 97 | init = xr.cftime_range(start=start, end="2002-01-01", freq=freq) 98 | print("init", init) 99 | 100 | lead = range(0, 5) 101 | data = np.random.random((len(init), len(lead))) 102 | hind = xr.DataArray(data, coords=[init, lead], dims=["init", "lead"], name="var") 103 | hind["lead"].attrs["units"] = units 104 | 105 | # Add "valid_time" coordinate using `climpred.utils.add_time_from_init_lead()` 106 | from climpred.utils import add_time_from_init_lead 107 | 108 | hind = add_time_from_init_lead(hind) 109 | for lead in [0, 1]: 110 | assert ( 111 | hind.valid_time.sel(lead=lead).to_index() 112 | == hind.init.to_index().shift(lead, expected_shift) 113 | ).all(), print( 114 | hind.valid_time.sel(lead=lead).to_index(), 115 | "\n", 116 | hind.init.to_index().shift(lead, expected_shift), 117 | ) 118 | -------------------------------------------------------------------------------- /src/climpred/tests/test_versioning.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from climpred.versioning.print_versions import show_versions 4 | 5 | 6 | @pytest.mark.parametrize("as_json", [True, False]) 7 | def test_show_versions(as_json): 8 | show_versions(as_json=as_json) 9 | -------------------------------------------------------------------------------- /src/climpred/versioning/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangeo-data/climpred/57681b8c2d92e5c80d4b98e382c39063ca397da5/src/climpred/versioning/__init__.py -------------------------------------------------------------------------------- /src/climpred/versioning/print_versions.py: -------------------------------------------------------------------------------- 1 | """utility functions for printing version information 2 | see pandas/pandas/util/_print_versions.py""" 3 | 4 | import codecs 5 | import importlib 6 | import locale 7 | import os 8 | import platform 9 | import struct 10 | import subprocess 11 | import sys 12 | 13 | 14 | def get_sys_info(): 15 | "Returns system information as a dict" 16 | 17 | blob = [] 18 | 19 | # get full commit hash 20 | commit = None 21 | if os.path.isdir(".git") and os.path.isdir("xarray"): 22 | try: 23 | pipe = subprocess.Popen( 24 | 'git log --format="%H" -n 1'.split(" "), 25 | stdout=subprocess.PIPE, 26 | stderr=subprocess.PIPE, 27 | ) 28 | so, _ = pipe.communicate() 29 | except Exception: 30 | pass 31 | else: 32 | if pipe.returncode == 0: 33 | commit = so 34 | try: 35 | commit = so.decode("utf-8") 36 | except ValueError: 37 | pass 38 | commit = commit.strip().strip('"') 39 | 40 | blob.append(("commit", commit)) 41 | 42 | try: 43 | (sysname, _, release, _, machine, processor) = platform.uname() 44 | blob.extend( 45 | [ 46 | ("python", sys.version), 47 | ("python-bits", struct.calcsize("P") * 8), 48 | ("OS", "%s" % (sysname)), 49 | ("OS-release", "%s" % (release)), 50 | # ("Version", "%s" % (version)), 51 | ("machine", "%s" % (machine)), 52 | ("processor", "%s" % (processor)), 53 | ("byteorder", "%s" % sys.byteorder), 54 | ("LC_ALL", "%s" % os.environ.get("LC_ALL", "None")), 55 | ("LANG", "%s" % os.environ.get("LANG", "None")), 56 | ("LOCALE", "%s.%s" % locale.getlocale()), 57 | ] 58 | ) 59 | except Exception: 60 | pass 61 | 62 | return blob 63 | 64 | 65 | def show_versions(as_json=False): 66 | sys_info = get_sys_info() 67 | 68 | deps = [ 69 | # (MODULE_NAME, f(mod) -> mod version) 70 | ("climpred", lambda mod: mod.__version__), 71 | ("xarray", lambda mod: mod.__version__), 72 | ("pandas", lambda mod: mod.__version__), 73 | ("numpy", lambda mod: mod.__version__), 74 | ("scipy", lambda mod: mod.__version__), 75 | ("cftime", lambda mod: mod.__version__), 76 | ("netcdf4", lambda mod: mod.__version__), 77 | ("nc_time_axis", lambda mod: mod.__version__), 78 | ("matplotlib", lambda mod: mod.__version__), 79 | ("cf_xarray", lambda mod: mod.__version__), 80 | ("xclim", lambda mod: mod.__version__), 81 | ("dask", lambda mod: mod.__version__), 82 | ("distributed", lambda mod: mod.__version__), 83 | # setup/test 84 | ("setuptools", lambda mod: mod.__version__), 85 | ("pip", lambda mod: mod.__version__), 86 | ("conda", lambda mod: mod.__version__), 87 | # Misc. 88 | ("IPython", lambda mod: mod.__version__), 89 | ("sphinx", lambda mod: mod.__version__), 90 | ] 91 | 92 | deps_blob = list() 93 | for modname, ver_f in deps: 94 | try: 95 | if modname in sys.modules: 96 | mod = sys.modules[modname] 97 | else: 98 | mod = importlib.import_module(modname) 99 | except Exception: 100 | deps_blob.append((modname, None)) 101 | else: 102 | try: 103 | ver = ver_f(mod) 104 | deps_blob.append((modname, ver)) 105 | except Exception: 106 | deps_blob.append((modname, "installed")) 107 | 108 | if as_json: 109 | import json 110 | 111 | j = dict(system=dict(sys_info), dependencies=dict(deps_blob)) 112 | 113 | if as_json is True: 114 | print(j) 115 | else: 116 | with codecs.open(as_json, "wb", encoding="utf8") as f: 117 | json.dump(j, f, indent=2) 118 | 119 | else: 120 | print("\nINSTALLED VERSIONS") 121 | print("------------------") 122 | 123 | for k, stat in sys_info: 124 | print("%s: %s" % (k, stat)) 125 | 126 | print("") 127 | for k, stat in deps_blob: 128 | print("%s: %s" % (k, stat)) 129 | 130 | 131 | def main(): 132 | from optparse import OptionParser 133 | 134 | parser = OptionParser() 135 | parser.add_option( 136 | "-j", 137 | "--json", 138 | metavar="FILE", 139 | nargs=1, 140 | help="Save output as JSON into file, pass in " "'-' to output to stdout", 141 | ) 142 | 143 | (options, args) = parser.parse_args() 144 | 145 | if options.json == "-": 146 | options.json = True 147 | 148 | show_versions(as_json=options.json) 149 | 150 | return 0 151 | 152 | 153 | if __name__ == "__main__": 154 | sys.exit(main()) 155 | --------------------------------------------------------------------------------