From e58768ab74f3677dbb17d27bd095d5e54643b6d4 Mon Sep 17 00:00:00 2001 From: Shenyulu <59901836+shenyulu@users.noreply.github.com> Date: Mon, 4 Mar 2024 16:22:58 +0800 Subject: [PATCH 1/4] add code style in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 571c5c7d..fe0ea087 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ [![Documentation Status](https://readthedocs.org/projects/easyclimate/badge/?version=latest)](https://easyclimate.readthedocs.io/en/latest/?badge=latest) [![DOI](https://zenodo.org/badge/465206111.svg)](https://zenodo.org/doi/10.5281/zenodo.10279567) [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/shenyulu/easyclimate/main?labpath=.binder) - +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) ## About From 448b39709c40919aa5c04bb7c1dfdc8dad4937ae Mon Sep 17 00:00:00 2001 From: Shenyulu <59901836+shenyulu@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:17:15 +0800 Subject: [PATCH 2/4] change binder example location --- .binder/environment.yml | 23 +- docs/clean.sh | 3 + docs/copy_ipynb2example.ps1 | 2 + docs/copy_ipynb2example.sh | 2 + .../plot_basic_statistical_analysis.ipynb | 576 ++++++++++++++++++ .../example/plot_formatting_coordinates.ipynb | 259 ++++++++ .../plot_geographic_finite_difference.ipynb | 60 +- docs/example/plot_interp.ipynb | 169 +++++ docs/example/plot_taylor_diagram.ipynb | 205 +++++++ docs/example/plot_time_scale_average.ipynb | 176 ++++++ release_requirements.txt | 2 +- 11 files changed, 1435 insertions(+), 42 deletions(-) create mode 100644 docs/clean.sh create mode 100644 docs/copy_ipynb2example.ps1 create mode 100644 docs/copy_ipynb2example.sh create mode 100644 docs/example/plot_basic_statistical_analysis.ipynb create mode 100644 docs/example/plot_formatting_coordinates.ipynb rename {.binder => docs/example}/plot_geographic_finite_difference.ipynb (67%) create mode 100644 docs/example/plot_interp.ipynb create mode 100644 docs/example/plot_taylor_diagram.ipynb create mode 100644 docs/example/plot_time_scale_average.ipynb diff --git a/.binder/environment.yml b/.binder/environment.yml index 0726ef45..1ab6d3cb 100644 --- a/.binder/environment.yml +++ b/.binder/environment.yml @@ -3,23 +3,23 @@ channels: - conda-forge dependencies: - python=3.10 - - numpy >= 1.24.3 - - xarray >= 0.17.0,<=2023.12.0 - - cartopy >= 0.20 - - matplotlib - - pandas - - intel-fortran-rt - - scipy >=1.8.0 - - statsmodels - - dask - pip: - - easyclimate - - geocat.viz <= 2023.10.0 + - numpy >= 1.24.3 + - xarray >= 0.17.0,<=2023.12.0 + - geocat.viz + - cartopy >= 0.20 - xeofs >= 2.2.2 + - matplotlib + - pandas - fast-barnes-py - oceans + - intel-fortran-rt + - scipy >=1.8.0 + - statsmodels - geocat-comp - pyspharm-syl + - windspharm-syl + - dask - pymannkendall - flox - xarray-datatree @@ -29,3 +29,4 @@ dependencies: - tqdm - zarr - metpy + - easyclimate diff --git a/docs/clean.sh b/docs/clean.sh new file mode 100644 index 00000000..6e2baa97 --- /dev/null +++ b/docs/clean.sh @@ -0,0 +1,3 @@ +rm -r ".\source\auto_gallery_output\*" +rm ".\source\api_index\generated\*" +rm ".\source\sg_execution_times.rst" diff --git a/docs/copy_ipynb2example.ps1 b/docs/copy_ipynb2example.ps1 new file mode 100644 index 00000000..902d0cd5 --- /dev/null +++ b/docs/copy_ipynb2example.ps1 @@ -0,0 +1,2 @@ +Remove-Item .\example\*.ipynb -Recurse +Copy-Item .\source\auto_gallery_output\*.ipynb -Destination .\example -Recurse diff --git a/docs/copy_ipynb2example.sh b/docs/copy_ipynb2example.sh new file mode 100644 index 00000000..49511c4d --- /dev/null +++ b/docs/copy_ipynb2example.sh @@ -0,0 +1,2 @@ +rm -r ./example/*.ipynb +cp -r ./source/auto_gallery_output/*.ipynb ./example diff --git a/docs/example/plot_basic_statistical_analysis.ipynb b/docs/example/plot_basic_statistical_analysis.ipynb new file mode 100644 index 00000000..7d89505f --- /dev/null +++ b/docs/example/plot_basic_statistical_analysis.ipynb @@ -0,0 +1,576 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Basic Statistical Analysis\n\nBefore proceeding with all the steps, first import some necessary libraries and packages\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import easyclimate as ecl\nimport matplotlib.pyplot as plt\nimport cartopy.crs as ccrs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Obtain the sea ice concentration (SIC) data from the Barents-Kara Seas (30\u00b0\u221290\u00b0E, 65\u00b0\u221285\u00b0N).\n\n.. seealso::\n Luo, B., Luo, D., Ge, Y. et al. Origins of Barents-Kara sea-ice interannual variability modulated by the Atlantic pathway of El Ni\u00f1o\u2013Southern Oscillation. Nat Commun 14, 585 (2023). https://doi.org/10.1038/s41467-023-36136-5\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sic_data_Barents_Sea = ecl.open_tutorial_dataset(\"mini_HadISST_ice\").sic\nsic_data_Barents_Sea" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And tropical SST dataset.\n\n.. seealso::\n Rayner, N. A.; Parker, D. E.; Horton, E. B.; Folland, C. K.; Alexander, L. V.; Rowell, D. P.; Kent, E. C.; Kaplan, A. (2003) Global analyses of sea surface temperature, sea ice, and night marine air temperature since the late nineteenth century J. Geophys. Res.Vol. 108, No. D14, 4407 https://doi.org/10.1029/2002JD002670 (pdf ~9Mb)\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sst_data = ecl.open_tutorial_dataset(\"mini_HadISST_sst\").sst\nsst_data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Mean States for Special Month\n:py:func:`easyclimate.get_specific_months_data ` allows us to easily obtain data on the SIC for December alone.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sic_data_Barents_Sea_12 = ecl.get_specific_months_data(sic_data_Barents_Sea, 12)\nsic_data_Barents_Sea_12" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we try to draw the mean states of the SIC in the Barents-Kara for the December.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "draw_sic_mean_state = sic_data_Barents_Sea_12.mean(dim=\"time\")\n\nfig, ax = plt.subplots(\n subplot_kw={\n \"projection\": ccrs.Orthographic(central_longitude=70, central_latitude=70)\n }\n)\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\ndraw_sic_mean_state.plot.contourf(\n ax=ax,\n # projection on data\n transform=ccrs.PlateCarree(),\n # Colorbar is placed at the bottom\n cbar_kwargs={\"location\": \"right\"},\n cmap=\"Blues\",\n levels=21,\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Linear Trend\nAs we all know, the area of Arctic sea ice has been decreasing more and more in recent years\ndue to the impact of global warming. We can obtain the change of SIC by solving\nthe linear trend of SIC data from 1981-2022.\n:py:func:`easyclimate.calc_linregress_spatial ` can provide the calculation\nresults of solving the linear trend for each grid point.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sic_data_Barents_Sea_12_linear_trend = ecl.calc_linregress_spatial(\n sic_data_Barents_Sea_12, dim=\"time\"\n).compute()\nsic_data_Barents_Sea_12_linear_trend" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `slope` is our desired linear trend, let's try to plot the linear trend of each grid point.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "draw_sic_slope = sic_data_Barents_Sea_12_linear_trend.slope\n\nfig, ax = plt.subplots(\n subplot_kw={\n \"projection\": ccrs.Orthographic(central_longitude=70, central_latitude=70)\n }\n)\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\ndraw_sic_slope.plot.contourf(\n ax=ax,\n transform=ccrs.PlateCarree(),\n cbar_kwargs={\"location\": \"right\"},\n cmap=\"RdBu_r\",\n levels=21,\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `pvalue` is the corresponding p-value, and we can determine a significance level (e.g., significance level is set to 0.05)\nin order to plot the region of significance.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "draw_sic_pvalue = sic_data_Barents_Sea_12_linear_trend.pvalue\n\nfig, ax = plt.subplots(\n subplot_kw={\n \"projection\": ccrs.Orthographic(central_longitude=70, central_latitude=70)\n }\n)\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\necl.plot.draw_significant_area_contourf(\n draw_sic_pvalue, ax=ax, thresh=0.05, transform=ccrs.PlateCarree()\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Further, we can superimpose the linear trend and the region of significance to study the linear trend of\nthe region of significance (since the linear trend of the region of non-significance is often spurious).\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(\n subplot_kw={\n \"projection\": ccrs.Orthographic(central_longitude=70, central_latitude=70)\n }\n)\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\n# SIC slope\ndraw_sic_slope.plot.contourf(\n ax=ax,\n transform=ccrs.PlateCarree(),\n cbar_kwargs={\"location\": \"right\"},\n cmap=\"RdBu_r\",\n levels=21,\n)\n\n# SIC 95% significant level\necl.plot.draw_significant_area_contourf(\n draw_sic_pvalue, ax=ax, thresh=0.05, transform=ccrs.PlateCarree()\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Regression\nRegression analysis is a statistical technique used to investigate the connection between a dependent variable and one or more independent variables.\n\nIt is frequently employed in climatology to analyze trends and patterns in climatic data, identify correlations between different climatic parameters, and create models that can predict future changes. By identifying patterns and connections in massive datasets, regression analysis offers several benefits for weather research. For instance, regression analysis can be used to pinpoint the elements that affect global temperatures, such as solar radiation, atmospheric greenhouse gases, and volcanic eruptions. Climate scientists can create models that can accurately predict future changes by including these variables in a regression model.\n\nMoreover, regression analysis can assist climate experts in spotting natural fluctuations in climate data, like El Ni\u00f1o events, and in determining how human activities like deforestation and fossil fuel combustion affect the environment. Regression analysis can also evaluate the effectiveness of various mitigation tactics, such as carbon pricing policies or renewable energy initiatives.\n\nOverall, regression analysis is a potent tool for analyzing complex climate data and producing reliable projections of upcoming alterations.\n\n.. seealso::\n - Regression Analysis: Definition, Types, Usage & Advantages. Website: https://www.questionpro.com/blog/regression-analysis/\n - The Advantages of Regression Analysis & Forecasting. Website: https://smallbusiness.chron.com/advantages-regression-analysis-forecasting-61800.html\n\nIn this subsection we try to regress the Ni\u00f1o 3.4 index on the Barents-Kara December SIC data.\nBefore performing the regression analysis, we can see that the longitude range of the SST data is **-180\u00b0~180\u00b0**,\ntry to convert the longitude range to **0\u00b0~360\u00b0** using :py:func:`easyclimate.utility.transfer_xarray_lon_from180TO360 `.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sst_data_0_360 = ecl.utility.transfer_xarray_lon_from180TO360(sst_data)\nsst_data_0_360" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Further, :py:func:`easyclimate.remove_seasonal_cycle_mean ` is used to remove the climate state of each\nmonth in order to obtain the individual month anomalies.\nThe figure below illustrates the November SST anomaly in the tropical equatorial Pacific during the 1982-83 super El Ni\u00f1o.\n\n.. seealso::\n Philander, S. Meteorology: Anomalous El Ni\u00f1o of 1982\u201383. Nature 305, 16 (1983). https://doi.org/10.1038/305016a0\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sst_data_anormaly = ecl.remove_seasonal_cycle_mean(sst_data_0_360)\n\nfig, ax = plt.subplots(\n figsize=(10, 4), subplot_kw={\"projection\": ccrs.PlateCarree(central_longitude=180)}\n)\n\nsst_data_anormaly.sel(lon=slice(120, 290)).isel(time=22).plot.contourf(\n ax=ax,\n transform=ccrs.PlateCarree(),\n cbar_kwargs={\"location\": \"bottom\", \"pad\": 0.1},\n cmap=\"RdBu_r\",\n levels=21,\n)\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The Ni\u00f1o3.4 index is commonly used as an indicator for detecting ENSO,\nand `easyclimate` provides :py:func:`easyclimate.field.air_sea_interaction.calc_index_nino34 ` to calculate the index using SST anomalies.\n\n.. seealso::\n Anthony G. Bamston, Muthuvel Chelliah & Stanley B. Goldenberg (1997) Documentation of a highly ENSO\u2010related sst region in the equatorial pacific: Research note, Atmosphere-Ocean, 35:3, 367-383, DOI: https://doi.org/10.1080/07055900.1997.9649597\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "nino34_monthly_index = ecl.field.air_sea_interaction.calc_index_nino34(\n sst_data_anormaly\n)\n\nnino34_monthly_index.plot(\n figsize=(8, 3),\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":py:func:`easyclimate.calc_yearly_climatological_mean ` is then used to solve for the annual average of the monthly index data\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "nino34_12_index = ecl.get_specific_months_data(nino34_monthly_index, 12)\nnino34_dec_yearly_index = ecl.calc_yearly_climatological_mean(nino34_12_index)\nnino34_dec_yearly_index" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Unlike solving for linear trend without passing in `x`, regression analysis must use the parameter `x` to pass in the object to be regressed.\nCare must be taken to ensure that the `time` dimensions are identical.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sic_reg_nino34 = ecl.calc_linregress_spatial(\n sic_data_Barents_Sea_12, x=nino34_dec_yearly_index.data\n)\nsic_reg_nino34 = sic_reg_nino34.compute()\nsic_reg_nino34" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here is an attempt to plot the results of the regression analysis.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "draw_sic_slope = sic_reg_nino34.slope\ndraw_sic_pvalue = sic_reg_nino34.pvalue\n\nfig, ax = plt.subplots(\n subplot_kw={\n \"projection\": ccrs.Orthographic(central_longitude=70, central_latitude=70)\n }\n)\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\ndraw_sic_slope.plot.contourf(\n ax=ax,\n transform=ccrs.PlateCarree(),\n cbar_kwargs={\"location\": \"right\"},\n cmap=\"RdBu_r\",\n levels=21,\n)\n\necl.plot.draw_significant_area_contourf(\n draw_sic_pvalue, ax=ax, thresh=0.05, transform=ccrs.PlateCarree()\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Detrend\nSea ice area shows an approximately linear trend of decreasing due to global warming.\nWe remove the linear trend from SIC in order to study the variability of SIC itself.\nIn addition, here we explore the differences between the trend followed by regional averaging and regional averaging followed by detrending approaches.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sic_data_Barents_Sea_12_spatial_mean = sic_data_Barents_Sea_12.mean(dim=(\"lat\", \"lon\"))\nsic_data_Barents_Sea_12_spatial_detrendmean = ecl.calc_detrend_data(\n sic_data_Barents_Sea_12, time_dim=\"time\"\n).mean(dim=(\"lat\", \"lon\"))\nsic_data_Barents_Sea_12_time_detrendmean = ecl.calc_detrend_data(\n sic_data_Barents_Sea_12_spatial_mean, time_dim=\"time\"\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The results show that there is no significant difference between these two detrending methods to study the variability of SIC in the Barents-Kara Seas.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(2, 1, sharex=True)\n\nsic_data_Barents_Sea_12_spatial_mean.plot(ax=ax[0])\nax[0].set_xlabel(\"\")\nax[0].set_title(\"Original\")\n\n\nsic_data_Barents_Sea_12_spatial_detrendmean.plot(\n ax=ax[1], label=\"detrend -> spatial mean\"\n)\nsic_data_Barents_Sea_12_time_detrendmean.plot(\n ax=ax[1], ls=\"--\", label=\"spatial mean -> detrend\"\n)\nax[1].set_xlabel(\"\")\nax[1].set_title(\"Detrend\")\nax[1].legend()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Weighted Spatial Data\nWhen calculating regional averages in high-latitude areas,\nconsidering weights is necessary because regions at different latitudes cover unequal\nsurface areas on the Earth. Since the Earth is approximately a spheroid, areas\ncloser to the poles have a different distribution of surface area on the spherical surface.\n\nOne common way to incorporate weights is by using the cosine of latitude, i.e., multiplying by $\\cos (\\varphi)$,\nwhere $\\varphi$ represents the latitude of a location. This is because areas at higher latitudes,\nclose to the poles, have higher latitudes and smaller cosine values, allowing for a\nsmaller weight to be applied to these regions when calculating averages.\n\nIn summary, considering weights is done to more accurately account for the distribution\nof surface area on the Earth, ensuring that contributions from different\nregions are weighted according to their actual surface area when calculating\naverages or other regional statistical measures.\n\n:py:func:`easyclimate.utility.get_weighted_spatial_data ` can help us create\nan :py:class:`xarray.core.weighted.DataArrayWeighted ` object.\nThis object will automatically consider and calculate weights in subsequent area operations, thereby achieving the operation of the weighted spatial average.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sic_data_Barents_Sea_12_detrend = ecl.calc_detrend_data(\n sic_data_Barents_Sea_12, time_dim=\"time\"\n)\ngrid_detrend_data_weighted_obj = ecl.utility.get_weighted_spatial_data(\n sic_data_Barents_Sea_12_detrend, lat_dim=\"lat\", lon_dim=\"lon\"\n)\nprint(type(grid_detrend_data_weighted_obj))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Solve for regional averaging for `grid_detrend_data_weighted_obj` objects (the role of weights is considered at this point)\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sic_data_Barents_Sea_12_spatial_detrend_weightedmean = (\n grid_detrend_data_weighted_obj.mean(dim=(\"lat\", \"lon\"))\n)\nsic_data_Barents_Sea_12_spatial_detrend_weightedmean" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can find some differences between the data considering latitude weights and those not considering latitude weights.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(2, 1, sharex=True)\n\nsic_data_Barents_Sea_12_spatial_mean.plot(ax=ax[0])\nax[0].set_xlabel(\"\")\nax[0].set_title(\"Original\")\n\n\nsic_data_Barents_Sea_12_spatial_detrendmean.plot(ax=ax[1], label=\"Regular mean\")\nsic_data_Barents_Sea_12_spatial_detrend_weightedmean.plot(\n ax=ax[1], ls=\"--\", label=\"Weighted mean\"\n)\nax[1].set_xlabel(\"\")\nax[1].set_title(\"Detrend\")\nax[1].legend()\nfig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Skewness\n\nSkewness is a measure of the asymmetry of a probability distribution.\n\nIt quantifies the extent to which a distribution deviates from being symmetric around its mean. A distribution with a skewness\nvalue of zero is considered symmetric, meaning that it has equal probabilities of occurring\nabove and below its mean. However, when the skewness value is non-zero, the distribution\nbecomes asymmetric, indicating that there is a higher likelihood of occurrence on one side\nof the mean than the other.\n\nSkewness can be positive or negative, depending on whether the\ndistribution is skewed towards larger or smaller values.\n\nIn climate analysis, skewness can arise due to various factors such as changes in atmospheric circulation patterns, uneven\ntemperature or precipitation distributions, or differences in measurement instruments.\n\n.. seealso::\n - Distributions of Daily Meteorological Variables: Background. Website: https://psl.noaa.gov/data/atmoswrit/distributions/background/index.html\n - Bakouch HS, Cadena M, Chesneau C. A new class of skew distributions with climate data analysis. J Appl Stat. 2020 Jul 13;48(16):3002-3024. doi: https://doi.org/10.1080/02664763.2020.1791804. PMID: 35707257; PMCID: PMC9042114.\n\nThe skewness is calculated using :py:func:`easyclimate.calc_skewness_spatial `.\nThe result of the calculation contains the skewness and p-value.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sic_data_Barents_Sea_12_detrend = ecl.calc_detrend_data(\n sic_data_Barents_Sea_12, time_dim=\"time\"\n)\nsic_data_Barents_Sea_12_skew = ecl.calc_skewness_spatial(\n sic_data_Barents_Sea_12_detrend, dim=\"time\"\n)\nsic_data_Barents_Sea_12_skew" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(\n subplot_kw={\n \"projection\": ccrs.Orthographic(central_longitude=70, central_latitude=70)\n }\n)\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\n# SIC slope\nsic_data_Barents_Sea_12_skew.skewness.plot.contourf(\n ax=ax,\n transform=ccrs.PlateCarree(),\n cbar_kwargs={\"location\": \"right\"},\n cmap=\"RdBu_r\",\n levels=21,\n)\n\n# SIC 95% significant level\necl.plot.draw_significant_area_contourf(\n sic_data_Barents_Sea_12_skew.pvalue,\n ax=ax,\n thresh=0.05,\n transform=ccrs.PlateCarree(),\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Kurtosis\nKurtosis is a measure of the \"tailedness\" of a probability distribution.\nIt describes how heavy or light the tails of a distribution are relative\nto a standard normal distribution. A distribution with high kurtosis\nhas heavier tails and a greater propensity for extreme events, whereas a\ndistribution with low kurtosis has lighter tails and fewer extreme events.\nKurtosis is particularly useful in climate analysis because it can reveal\ninformation about the frequency and intensity of extreme weather events such as hurricanes, droughts, or heatwaves.\n\n.. seealso::\n - Distributions of Daily Meteorological Variables: Background. Website: https://psl.noaa.gov/data/atmoswrit/distributions/background/index.html\n - Bakouch HS, Cadena M, Chesneau C. A new class of skew distributions with climate data analysis. J Appl Stat. 2020 Jul 13;48(16):3002-3024. doi: https://doi.org/10.1080/02664763.2020.1791804. PMID: 35707257; PMCID: PMC9042114.\n\nThe skewness is calculated using :py:func:`easyclimate.calc_kurtosis_spatial `.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sic_data_Barents_Sea_12_kurt = ecl.calc_kurtosis_spatial(\n sic_data_Barents_Sea_12_detrend, dim=\"time\"\n)\nsic_data_Barents_Sea_12_kurt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Consider plotting kurtosis below\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(\n subplot_kw={\n \"projection\": ccrs.Orthographic(central_longitude=70, central_latitude=70)\n }\n)\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\n# SIC slope\nsic_data_Barents_Sea_12_kurt.plot.contourf(\n ax=ax,\n transform=ccrs.PlateCarree(),\n cbar_kwargs={\"location\": \"right\"},\n cmap=\"RdBu_r\",\n levels=21,\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Composite Analysis\nIn the process of climate analysis, **composite analysis** is a statistical and integrative method\nused to study the spatial and temporal distribution of specific climate events or phenomena.\nThe primary purpose of this analysis method is to identify common features and patterns\namong multiple events or time periods by combining their information.\n\nSpecifically, the steps of composite analysis typically include the following aspects:\n\n1. **Event Selection**: Firstly, a set of events related to the research objective is chosen. These events could be specific climate phenomena such as heavy rainfall, drought, or temperature anomalies.\n2. **Data Collection**: Collect meteorological data related to the selected events, including observational data, model outputs, or remote sensing data.\n3. **Event Alignment**: Time-align the chosen events to ensure that they are within the same temporal framework for analysis.\n4. **Data Combination**: Combine data values at corresponding time points into a composite dataset. Averages or weighted averages are often used to reduce the impact of random noise.\n\nThe advantages of this method include:\n\n- **Highlighting Common Features**: By combining data from multiple events or time periods, composite analysis can highlight common features, aiding in the identification of general patterns in climate events.\n- **Noise Reduction**: By averaging data, composite analysis helps to reduce the impact of random noise, resulting in more stable and reliable analysis outcomes.\n- **Spatial Consistency**: Through spatial averaging, this method helps reveal the consistent spatial distribution of climate events, providing a more comprehensive understanding.\n- **Facilitating Comparisons**: Composite analysis makes it convenient to compare different events or time periods as it integrates them into a unified framework.\n\nHere we try to extract the El Ni\u00f1o and La Ni\u00f1a events using the standard deviation of the Ni\u00f1o 3.4 index as a threshold.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "nino34_dec_yearly_index_std = nino34_dec_yearly_index.std(dim=\"time\").data\nnino34_dec_yearly_index_std" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":py:func:`easyclimate.get_year_exceed_index_upper_bound ` is able to obtain the years that exceed the upper bound of the Ni\u00f1o 3.4 exponential threshold,\nand similarly :py:func:`easyclimate.get_year_exceed_index_lower_bound ` can obtain the years that exceed the lower bound of the exponential threshold.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "elnino_year = ecl.get_year_exceed_index_upper_bound(\n nino34_dec_yearly_index, thresh=nino34_dec_yearly_index_std\n)\nlanina_year = ecl.get_year_exceed_index_lower_bound(\n nino34_dec_yearly_index, thresh=-nino34_dec_yearly_index_std\n)\nprint(\"El ni\u00f1o years: \", elnino_year)\nprint(\"La ni\u00f1a years: \", lanina_year)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Further we use :py:func:`easyclimate.get_specific_years_data ` to extract data\nfor the El Ni\u00f1o years within `sic_data_Barents_Sea_12_detrend`. The results show that six temporal levels were extracted.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sic_data_Barents_Sea_12_detrend_elnino = ecl.get_specific_years_data(\n sic_data_Barents_Sea_12_detrend, elnino_year\n)\nsic_data_Barents_Sea_12_detrend_elnino.name = \"El ni\u00f1o years\"\nsic_data_Barents_Sea_12_detrend_lanina = ecl.get_specific_years_data(\n sic_data_Barents_Sea_12_detrend, lanina_year\n)\nsic_data_Barents_Sea_12_detrend_lanina.name = \"La ni\u00f1a years\"\nsic_data_Barents_Sea_12_detrend_elnino" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Similarly, 5 temporal levels were extracted for the La Ni\u00f1a years.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sic_data_Barents_Sea_12_detrend_lanina" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We now plot the distribution of SIC during El Ni\u00f1o and La Ni\u00f1a years.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(\n 1,\n 2,\n subplot_kw={\n \"projection\": ccrs.Orthographic(central_longitude=70, central_latitude=70)\n },\n figsize=(10, 4),\n)\n\nfor axi in ax.flat:\n axi.gridlines(\n draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\"\n )\n axi.coastlines(edgecolor=\"black\", linewidths=0.5)\n\nsic_data_Barents_Sea_12_detrend_elnino.mean(dim=\"time\").plot.contourf(\n ax=ax[0],\n transform=ccrs.PlateCarree(),\n cbar_kwargs={\"location\": \"bottom\"},\n cmap=\"RdBu_r\",\n levels=21,\n)\nax[0].set_title(\"El ni\u00f1o years\")\n\nsic_data_Barents_Sea_12_detrend_lanina.mean(dim=\"time\").plot.contourf(\n ax=ax[1],\n transform=ccrs.PlateCarree(),\n cbar_kwargs={\"location\": \"bottom\"},\n cmap=\"RdBu_r\",\n levels=21,\n)\nax[1].set_title(\"La ni\u00f1a years\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So is there a significant difference in the distribution of SIC between these two events?\n:py:func:`easyclimate.calc_ttestSpatialPattern_spatial ` provides a\ntwo-sample t-test operation to investigate whether there is a significant difference between the means of the two samples.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sig_diff = ecl.calc_ttestSpatialPattern_spatial(\n sic_data_Barents_Sea_12_detrend_elnino,\n sic_data_Barents_Sea_12_detrend_lanina,\n dim=\"time\",\n)\nsig_diff" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can find that there is little difference in the effect on SIC under different ENSO events.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(\n subplot_kw={\n \"projection\": ccrs.Orthographic(central_longitude=70, central_latitude=70)\n }\n)\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\n# SIC slope\ndiff = sic_data_Barents_Sea_12_detrend_lanina.mean(\n dim=\"time\"\n) - sic_data_Barents_Sea_12_detrend_elnino.mean(dim=\"time\")\ndiff.plot.contourf(\n ax=ax,\n transform=ccrs.PlateCarree(),\n cbar_kwargs={\"location\": \"right\"},\n cmap=\"RdBu_r\",\n levels=21,\n)\n\nax.set_title(\"La ni\u00f1a minus El ni\u00f1o\", loc=\"left\")\n\necl.plot.draw_significant_area_contourf(\n sig_diff.pvalue, ax=ax, thresh=0.1, transform=ccrs.PlateCarree()\n)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/docs/example/plot_formatting_coordinates.ipynb b/docs/example/plot_formatting_coordinates.ipynb new file mode 100644 index 00000000..8d19d8fe --- /dev/null +++ b/docs/example/plot_formatting_coordinates.ipynb @@ -0,0 +1,259 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Formatting Coordinates\n\nBefore proceeding with all the steps, first import some necessary libraries and packages\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import easyclimate as ecl\nimport matplotlib.pyplot as plt\nimport matplotlib.ticker as ticker\nimport cartopy.crs as ccrs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The data we use here is monthly data from Jan 2022 to Feb 2022 and contains 17 vertical levels.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "u_data = ecl.tutorial.open_tutorial_dataset(\"uwnd_202201_mon_mean\").sortby(\"lat\").uwnd\nu_data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Formatting of the Latitude and Lontitude Tickes\n`draw_data1` is extracted from time level 0 and 500hPa vertical level.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "draw_data1 = u_data.isel(time=0).sel(level=500)\ndraw_data1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we call :py:func:`xarray.plot.pcolormesh ` to plot the latitudinal wind field on the 500hPa isobaric surface.\nNoting that the x-axis and y-axis are not in standard geographic coordinate format, our next step is to format these coordinates.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(1, 1)\n\ndraw_data1.plot.pcolormesh(\n ax=ax,\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":py:func:`easyclimate.plot.set_lon_format_axis `,\n:py:func:`easyclimate.plot.set_lat_format_axis ` can help us quickly\nformat the coordinates on the x-axis and y-axis, respectively, into a geographic coordinate format.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(1, 1)\n\ndraw_data1.plot.pcolormesh(\n ax=ax,\n)\n\necl.plot.set_lon_format_axis(ax, axis=\"x\")\necl.plot.set_lat_format_axis(ax, axis=\"y\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is worth mentioning that the :py:func:`easyclimate.plot.set_lon_format_axis `,\n:py:func:`easyclimate.plot.set_lat_format_axis ` methods contain a parameter `dmi`\nwhich helps us to convert DD (Decimal Degrees) format to DMS (Degrees Minutes Seconds) format.\n\nNow let's start by selecting a smaller area\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "draw_data1_1 = (\n u_data.isel(time=0).sel(level=500).sel(lon=slice(100, 110), lat=slice(20, 23))\n)\ndraw_data1_1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note the difference in geo-labeling on the x-axis.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(1, 2, figsize=(15, 5))\n\nfor axi in ax.flat:\n draw_data1_1.plot(ax=axi, cmap=\"Reds\")\n axi.xaxis.set_major_locator(ticker.LinearLocator(5))\n\necl.plot.set_lon_format_axis(ax[0], axis=\"x\")\necl.plot.set_lat_format_axis(ax[0], axis=\"y\")\nax[0].set_title(\"dms = False\")\n\necl.plot.set_lon_format_axis(ax[1], axis=\"x\", dms=True)\necl.plot.set_lat_format_axis(ax[1], axis=\"y\", dms=True)\nax[1].set_title(\"dms = True\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Barometric Profile Label Formatting\nHere we choose the longitudinally averaged latitudinal direction at time level 0 to plot the profile\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "draw_data2 = u_data.isel(time=0).mean(dim=\"lon\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice that the x-axis and y-axis labels are unformatted, so we'll take care of that next.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(1, 1)\n\ndraw_data2.plot.contourf(ax=ax, levels=21, yincrease=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":py:func:`easyclimate.plot.set_p_format_axis ` can help us format barometric vertical labels\nand similarly :py:func:`easyclimate.plot.set_lat_format_axis ` can help us format latitude labels.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(1, 1)\n\ndraw_data2.plot.contourf(ax=ax, levels=21, yincrease=False)\n\necl.plot.set_lat_format_axis(ax, axis=\"x\")\necl.plot.set_p_format_axis(ax, axis=\"y\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Polar Stereo of the Circle Boundary\nFor the sake of illustration, we use here the sea ice concentration (SIC) data from the Barents-Kara Seas (30\u00b0\u221290\u00b0E, 65\u00b0\u221285\u00b0N).\nThe results of the data under the 10th time level are described below.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sic_data = ecl.tutorial.open_tutorial_dataset(\"mini_HadISST_ice\").sic.isel(time=10)\nsic_data.plot.contourf(cmap=\"Blues\", levels=11)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":py:func:`easyclimate.plot.draw_Circlemap_PolarStereo ` helps us to easily\nestablish the boundary of the circle under the projection of the polar stereo.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(subplot_kw={\"projection\": ccrs.NorthPolarStereo()})\n\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\nax.stock_img()\n\necl.plot.draw_Circlemap_PolarStereo(\n ax=ax,\n lon_step=30,\n lat_step=10,\n lat_range=[50, 90],\n draw_labels=True,\n gridlines_kwargs={\"color\": \"grey\", \"alpha\": 0.5, \"linestyle\": \"--\"},\n)\n\nsic_data.plot.contourf(cmap=\"Blues\", levels=11, transform=ccrs.PlateCarree())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Adjusting `north_pad` and `south_pad` appropriately can help us compensate for not completing the circle boundaries.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(subplot_kw={\"projection\": ccrs.NorthPolarStereo()})\n\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\nax.stock_img()\n\necl.plot.draw_Circlemap_PolarStereo(\n ax=ax,\n lon_step=30,\n lat_step=10,\n lat_range=[50, 90],\n draw_labels=True,\n set_map_boundary_kwargs={\"north_pad\": 0.3, \"south_pad\": 0.4},\n gridlines_kwargs={\"color\": \"grey\", \"alpha\": 0.5, \"linestyle\": \"--\"},\n)\n\nsic_data.plot(cmap=\"Blues\", levels=11, transform=ccrs.PlateCarree())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/.binder/plot_geographic_finite_difference.ipynb b/docs/example/plot_geographic_finite_difference.ipynb similarity index 67% rename from .binder/plot_geographic_finite_difference.ipynb rename to docs/example/plot_geographic_finite_difference.ipynb index 835c48b9..c4439ae1 100644 --- a/.binder/plot_geographic_finite_difference.ipynb +++ b/docs/example/plot_geographic_finite_difference.ipynb @@ -33,7 +33,7 @@ }, "outputs": [], "source": [ - "u_data = ecl.tutorial.open_tutorial_dataset('uwnd_202201_mon_mean').sortby('lat').uwnd\nv_data = ecl.tutorial.open_tutorial_dataset('vwnd_202201_mon_mean').sortby('lat').vwnd\nz_data = ecl.tutorial.open_tutorial_dataset('hgt_202201_mon_mean').sortby('lat').hgt\ntemp_data = ecl.tutorial.open_tutorial_dataset('air_202201_mon_mean').sortby('lat').air\nq_data = ecl.tutorial.open_tutorial_dataset('shum_202201_mon_mean').sortby('lat').shum\nmsl_data = ecl.tutorial.open_tutorial_dataset('pressfc_202201_mon_mean').sortby('lat').pres\npr_data = ecl.tutorial.open_tutorial_dataset('precip_202201_mon_mean').sortby('lat').precip\n\nuvdata = xr.Dataset()\nuvdata['uwnd'] = u_data\nuvdata['vwnd'] = v_data" + "u_data = ecl.tutorial.open_tutorial_dataset(\"uwnd_202201_mon_mean\").sortby(\"lat\").uwnd\nv_data = ecl.tutorial.open_tutorial_dataset(\"vwnd_202201_mon_mean\").sortby(\"lat\").vwnd\nz_data = ecl.tutorial.open_tutorial_dataset(\"hgt_202201_mon_mean\").sortby(\"lat\").hgt\ntemp_data = ecl.tutorial.open_tutorial_dataset(\"air_202201_mon_mean\").sortby(\"lat\").air\nq_data = ecl.tutorial.open_tutorial_dataset(\"shum_202201_mon_mean\").sortby(\"lat\").shum\nmsl_data = (\n ecl.tutorial.open_tutorial_dataset(\"pressfc_202201_mon_mean\").sortby(\"lat\").pres\n)\npr_data = (\n ecl.tutorial.open_tutorial_dataset(\"precip_202201_mon_mean\").sortby(\"lat\").precip\n)\n\nuvdata = xr.Dataset()\nuvdata[\"uwnd\"] = u_data\nuvdata[\"vwnd\"] = v_data" ] }, { @@ -51,7 +51,7 @@ }, "outputs": [], "source": [ - "uvdata_500_202201 = uvdata.sel(level = 500, time = '2022-01-01')\nz_data_500_202201 = z_data.sel(level = 500, time = '2022-01-01')\ntemp_data_500_202201 = temp_data.sel(level = 500, time = '2022-01-01')" + "uvdata_500_202201 = uvdata.sel(level=500, time=\"2022-01-01\")\nz_data_500_202201 = z_data.sel(level=500, time=\"2022-01-01\")\ntemp_data_500_202201 = temp_data.sel(level=500, time=\"2022-01-01\")" ] }, { @@ -69,7 +69,7 @@ }, "outputs": [], "source": [ - "fig, ax = plt.subplots(subplot_kw = {'projection': ccrs.PlateCarree(central_longitude = 180)})\n\nax.stock_img()\nax.gridlines(draw_labels = ['bottom', 'left'], color = \"grey\", alpha = 0.5, linestyle=\"--\")\nax.coastlines(edgecolor = 'black', linewidths = 0.5)\n\nuvdata_500_202201.thin(lon = 3, lat = 3).plot.quiver(\n ax = ax,\n u = 'uwnd', v = 'vwnd', x = 'lon', y = 'lat',\n # projection on data\n transform = ccrs.PlateCarree(),\n)" + "fig, ax = plt.subplots(\n subplot_kw={\"projection\": ccrs.PlateCarree(central_longitude=180)}\n)\n\nax.stock_img()\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\nuvdata_500_202201.thin(lon=3, lat=3).plot.quiver(\n ax=ax,\n u=\"uwnd\",\n v=\"vwnd\",\n x=\"lon\",\n y=\"lat\",\n # projection on data\n transform=ccrs.PlateCarree(),\n)" ] }, { @@ -87,7 +87,7 @@ }, "outputs": [], "source": [ - "uwnd_dx = ecl.calc_gradient(\n uvdata_500_202201.uwnd, \n dim = 'lon'\n)\n\nuwnd_dx" + "uwnd_dx = ecl.calc_gradient(uvdata_500_202201.uwnd, dim=\"lon\")\n\nuwnd_dx" ] }, { @@ -98,7 +98,7 @@ }, "outputs": [], "source": [ - "fig, ax = plt.subplots(subplot_kw = {'projection': ccrs.PlateCarree(central_longitude = 180)})\n\nax.stock_img()\nax.gridlines(draw_labels = ['bottom', 'left'], color = \"grey\", alpha = 0.5, linestyle=\"--\")\nax.coastlines(edgecolor = 'black', linewidths = 0.5)\n\nuwnd_dx.plot.contourf(\n ax = ax,\n # projection on data\n transform = ccrs.PlateCarree(),\n # Colorbar is placed at the bottom\n cbar_kwargs = {'location': 'bottom'},\n levels = 21,\n)" + "fig, ax = plt.subplots(\n subplot_kw={\"projection\": ccrs.PlateCarree(central_longitude=180)}\n)\n\nax.stock_img()\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\nuwnd_dx.plot.contourf(\n ax=ax,\n # projection on data\n transform=ccrs.PlateCarree(),\n # Colorbar is placed at the bottom\n cbar_kwargs={\"location\": \"bottom\"},\n levels=21,\n)" ] }, { @@ -116,7 +116,7 @@ }, "outputs": [], "source": [ - "uvwnd_dx = ecl.calc_gradient(\n uvdata_500_202201, \n dim = 'lon'\n)\n\nuvwnd_dx" + "uvwnd_dx = ecl.calc_gradient(uvdata_500_202201, dim=\"lon\")\n\nuvwnd_dx" ] }, { @@ -134,7 +134,7 @@ }, "outputs": [], "source": [ - "uwnd_dlon = ecl.calc_lon_gradient(uvdata_500_202201.uwnd, lon_dim = 'lon', lat_dim = 'lat')\n\nfig, ax = plt.subplots(subplot_kw = {'projection': ccrs.PlateCarree(central_longitude = 180)})\n\nax.gridlines(draw_labels = ['bottom', 'left'], color = \"grey\", alpha = 0.5, linestyle=\"--\")\nax.coastlines(edgecolor = 'black', linewidths = 0.5)\n\nuwnd_dlon.plot.contourf(\n ax = ax,\n # projection on data\n transform = ccrs.PlateCarree(),\n # Colorbar is placed at the bottom\n cbar_kwargs = {'location': 'bottom'},\n levels = 21,\n)" + "uwnd_dlon = ecl.calc_lon_gradient(uvdata_500_202201.uwnd, lon_dim=\"lon\", lat_dim=\"lat\")\n\nfig, ax = plt.subplots(\n subplot_kw={\"projection\": ccrs.PlateCarree(central_longitude=180)}\n)\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\nuwnd_dlon.plot.contourf(\n ax=ax,\n # projection on data\n transform=ccrs.PlateCarree(),\n # Colorbar is placed at the bottom\n cbar_kwargs={\"location\": \"bottom\"},\n levels=21,\n)" ] }, { @@ -159,7 +159,7 @@ }, "outputs": [], "source": [ - "uwnd_dlon2 = ecl.calc_lon_laplacian(\n uvdata_500_202201.uwnd, \n lon_dim = 'lon', \n lat_dim = 'lat'\n)" + "uwnd_dlon2 = ecl.calc_lon_laplacian(\n uvdata_500_202201.uwnd, lon_dim=\"lon\", lat_dim=\"lat\"\n)" ] }, { @@ -177,7 +177,7 @@ }, "outputs": [], "source": [ - "uwnd_dlat2 = ecl.calc_lat_laplacian(\n uvdata_500_202201.uwnd, \n lat_dim = 'lat'\n)" + "uwnd_dlat2 = ecl.calc_lat_laplacian(uvdata_500_202201.uwnd, lat_dim=\"lat\")" ] }, { @@ -195,7 +195,7 @@ }, "outputs": [], "source": [ - "uwnd_dlonlat = ecl.calc_lon_lat_mixed_derivatives(\n uvdata_500_202201.uwnd, \n lon_dim = 'lon', \n lat_dim = 'lat'\n)" + "uwnd_dlonlat = ecl.calc_lon_lat_mixed_derivatives(\n uvdata_500_202201.uwnd, lon_dim=\"lon\", lat_dim=\"lat\"\n)" ] }, { @@ -213,7 +213,7 @@ }, "outputs": [], "source": [ - "fig, ax = plt.subplots(subplot_kw = {'projection': ccrs.PlateCarree(central_longitude = 180)})\n\nax.gridlines(draw_labels = ['bottom', 'left'], color = \"grey\", alpha = 0.5, linestyle=\"--\")\nax.coastlines(edgecolor = 'black', linewidths = 0.5)\n\nuwnd_dlon2.plot.contourf(ax = ax, transform = ccrs.PlateCarree(), cbar_kwargs = {'location': 'bottom'}, levels = 21)\nax.set_title('$\\\\frac{\\\\partial^2 F}{\\\\partial x^2}$', fontsize = 20)" + "fig, ax = plt.subplots(\n subplot_kw={\"projection\": ccrs.PlateCarree(central_longitude=180)}\n)\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\nuwnd_dlon2.plot.contourf(\n ax=ax, transform=ccrs.PlateCarree(), cbar_kwargs={\"location\": \"bottom\"}, levels=21\n)\nax.set_title(\"$\\\\frac{\\\\partial^2 F}{\\\\partial x^2}$\", fontsize=20)" ] }, { @@ -231,7 +231,7 @@ }, "outputs": [], "source": [ - "fig, ax = plt.subplots(subplot_kw = {'projection': ccrs.PlateCarree(central_longitude = 180)})\n\nax.gridlines(draw_labels = ['bottom', 'left'], color = \"grey\", alpha = 0.5, linestyle=\"--\")\nax.coastlines(edgecolor = 'black', linewidths = 0.5)\n\nuwnd_dlat2.plot.contourf(ax = ax, transform = ccrs.PlateCarree(), cbar_kwargs = {'location': 'bottom'}, levels = 21)\nax.set_title('$\\\\frac{\\\\partial^2 F}{\\\\partial y^2}$', fontsize = 20)" + "fig, ax = plt.subplots(\n subplot_kw={\"projection\": ccrs.PlateCarree(central_longitude=180)}\n)\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\nuwnd_dlat2.plot.contourf(\n ax=ax, transform=ccrs.PlateCarree(), cbar_kwargs={\"location\": \"bottom\"}, levels=21\n)\nax.set_title(\"$\\\\frac{\\\\partial^2 F}{\\\\partial y^2}$\", fontsize=20)" ] }, { @@ -249,14 +249,14 @@ }, "outputs": [], "source": [ - "fig, ax = plt.subplots(subplot_kw = {'projection': ccrs.PlateCarree(central_longitude = 180)})\n\nax.gridlines(draw_labels = ['bottom', 'left'], color = \"grey\", alpha = 0.5, linestyle=\"--\")\nax.coastlines(edgecolor = 'black', linewidths = 0.5)\n\nuwnd_dlonlat.plot.contourf(ax = ax, transform = ccrs.PlateCarree(), cbar_kwargs = {'location': 'bottom'}, levels = 21)\nax.set_title('$\\\\frac{\\\\partial^2 F}{\\\\partial x \\\\partial y}$', fontsize = 20)" + "fig, ax = plt.subplots(\n subplot_kw={\"projection\": ccrs.PlateCarree(central_longitude=180)}\n)\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\nuwnd_dlonlat.plot.contourf(\n ax=ax, transform=ccrs.PlateCarree(), cbar_kwargs={\"location\": \"bottom\"}, levels=21\n)\nax.set_title(\"$\\\\frac{\\\\partial^2 F}{\\\\partial x \\\\partial y}$\", fontsize=20)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Vorticity and Divergence\n\nVorticity and divergence are measures of the degree of atmospheric rotation and volumetric flux per unit volume respectively. For vorticity and divergence in the quasi-geostrophic case, the potential height is used as input data for the calculations. In general, we first calculate the quasi-geostrophic wind.\n\n- :py:func:`easyclimate.calc_geostrophic_wind `: calculate the geostrophic wind. \n\n\\begin{align}u_g = - \\frac{g}{f} \\frac{\\partial H}{\\partial y}, \\ v_g = \\frac{g}{f} \\frac{\\partial H}{\\partial x}\\end{align}\n\n\n" + "## Vorticity and Divergence\n\nVorticity and divergence are measures of the degree of atmospheric rotation and volumetric flux per unit volume respectively. For vorticity and divergence in the quasi-geostrophic case, the potential height is used as input data for the calculations. In general, we first calculate the quasi-geostrophic wind.\n\n- :py:func:`easyclimate.calc_geostrophic_wind `: calculate the geostrophic wind.\n\n\\begin{align}u_g = - \\frac{g}{f} \\frac{\\partial H}{\\partial y}, \\ v_g = \\frac{g}{f} \\frac{\\partial H}{\\partial x}\\end{align}\n\n\n" ] }, { @@ -267,7 +267,7 @@ }, "outputs": [], "source": [ - "geostrophic_wind_data_500_202201 = ecl.calc_geostrophic_wind(\n z_data_500_202201, \n lon_dim = 'lon', \n lat_dim = 'lat'\n)" + "geostrophic_wind_data_500_202201 = ecl.calc_geostrophic_wind(\n z_data_500_202201, lon_dim=\"lon\", lat_dim=\"lat\"\n)" ] }, { @@ -285,7 +285,7 @@ }, "outputs": [], "source": [ - "qg_vor_data_500_202201 = ecl.calc_vorticity(\n u_data = geostrophic_wind_data_500_202201.ug, \n v_data = geostrophic_wind_data_500_202201.vg,\n lon_dim = 'lon', lat_dim = 'lat',\n)\n\nqg_vor_data_500_202201.sel(lat = slice(20, 80)).plot.contourf(levels = 21)" + "qg_vor_data_500_202201 = ecl.calc_vorticity(\n u_data=geostrophic_wind_data_500_202201.ug,\n v_data=geostrophic_wind_data_500_202201.vg,\n lon_dim=\"lon\",\n lat_dim=\"lat\",\n)\n\nqg_vor_data_500_202201.sel(lat=slice(20, 80)).plot.contourf(levels=21)" ] }, { @@ -303,7 +303,7 @@ }, "outputs": [], "source": [ - "vor_data_500_202201 = ecl.calc_vorticity(\n u_data = uvdata_500_202201['uwnd'], \n v_data = uvdata_500_202201['vwnd'],\n lon_dim = 'lon', lat_dim = 'lat',\n)\n\nvor_data_500_202201.sel(lat = slice(20, 80)).plot.contourf(levels = 21)" + "vor_data_500_202201 = ecl.calc_vorticity(\n u_data=uvdata_500_202201[\"uwnd\"],\n v_data=uvdata_500_202201[\"vwnd\"],\n lon_dim=\"lon\",\n lat_dim=\"lat\",\n)\n\nvor_data_500_202201.sel(lat=slice(20, 80)).plot.contourf(levels=21)" ] }, { @@ -321,7 +321,7 @@ }, "outputs": [], "source": [ - "qg_div_data_500_202201 = ecl.calc_divergence(\n u_data = geostrophic_wind_data_500_202201.ug,\n v_data = geostrophic_wind_data_500_202201.vg,\n lon_dim = 'lon', lat_dim = 'lat',\n)\n\nqg_div_data_500_202201.sel(lat = slice(20, 80)).plot.contourf(levels = 21)" + "qg_div_data_500_202201 = ecl.calc_divergence(\n u_data=geostrophic_wind_data_500_202201.ug,\n v_data=geostrophic_wind_data_500_202201.vg,\n lon_dim=\"lon\",\n lat_dim=\"lat\",\n)\n\nqg_div_data_500_202201.sel(lat=slice(20, 80)).plot.contourf(levels=21)" ] }, { @@ -339,7 +339,7 @@ }, "outputs": [], "source": [ - "div_data_500_202201 = ecl.calc_divergence(\n u_data = uvdata_500_202201['uwnd'], \n v_data = uvdata_500_202201['vwnd'],\n lon_dim = 'lon', lat_dim = 'lat',\n)\n\ndiv_data_500_202201.sel(lat = slice(20, 80)).plot.contourf(levels = 21)" + "div_data_500_202201 = ecl.calc_divergence(\n u_data=uvdata_500_202201[\"uwnd\"],\n v_data=uvdata_500_202201[\"vwnd\"],\n lon_dim=\"lon\",\n lat_dim=\"lat\",\n)\n\ndiv_data_500_202201.sel(lat=slice(20, 80)).plot.contourf(levels=21)" ] }, { @@ -357,7 +357,7 @@ }, "outputs": [], "source": [ - "vor_data_500_202201_windspharm = ecl.windspharm.calc_relative_vorticity(\n u_data = uvdata_500_202201['uwnd'],\n v_data = uvdata_500_202201['vwnd'],\n)\n\nvor_data_500_202201_windspharm.sortby('lat').sel(lat = slice(20, 80)).plot.contourf(levels = 21)" + "vor_data_500_202201_windspharm = ecl.windspharm.calc_relative_vorticity(\n u_data=uvdata_500_202201[\"uwnd\"],\n v_data=uvdata_500_202201[\"vwnd\"],\n)\n\nvor_data_500_202201_windspharm.sortby(\"lat\").sel(lat=slice(20, 80)).plot.contourf(\n levels=21\n)" ] }, { @@ -368,7 +368,7 @@ }, "outputs": [], "source": [ - "div_data_500_202201_windspharm = ecl.windspharm.calc_divergence(\n u_data = uvdata_500_202201['uwnd'],\n v_data = uvdata_500_202201['vwnd'],\n)\n\ndiv_data_500_202201_windspharm.sortby('lat').sel(lat = slice(20, 80)).plot.contourf(levels = 21)" + "div_data_500_202201_windspharm = ecl.windspharm.calc_divergence(\n u_data=uvdata_500_202201[\"uwnd\"],\n v_data=uvdata_500_202201[\"vwnd\"],\n)\n\ndiv_data_500_202201_windspharm.sortby(\"lat\").sel(lat=slice(20, 80)).plot.contourf(\n levels=21\n)" ] }, { @@ -393,7 +393,7 @@ }, "outputs": [], "source": [ - "u_advection_500_202201 = ecl.calc_u_advection(\n u_data = uvdata_500_202201['uwnd'],\n temper_data = temp_data_500_202201\n)\n\nu_advection_500_202201.sortby('lat').sel(lat = slice(20, 80)).plot.contourf(levels = 21)" + "u_advection_500_202201 = ecl.calc_u_advection(\n u_data=uvdata_500_202201[\"uwnd\"], temper_data=temp_data_500_202201\n)\n\nu_advection_500_202201.sortby(\"lat\").sel(lat=slice(20, 80)).plot.contourf(levels=21)" ] }, { @@ -411,7 +411,7 @@ }, "outputs": [], "source": [ - "v_advection_500_202201 = ecl.calc_v_advection(\n v_data = uvdata_500_202201['vwnd'],\n temper_data = temp_data_500_202201\n)\n\nv_advection_500_202201.sortby('lat').sel(lat = slice(20, 80)).plot.contourf(levels = 21)" + "v_advection_500_202201 = ecl.calc_v_advection(\n v_data=uvdata_500_202201[\"vwnd\"], temper_data=temp_data_500_202201\n)\n\nv_advection_500_202201.sortby(\"lat\").sel(lat=slice(20, 80)).plot.contourf(levels=21)" ] }, { @@ -429,7 +429,7 @@ }, "outputs": [], "source": [ - "ecl.calc_horizontal_water_flux(\n specific_humidity_data = q_data, \n u_data = uvdata.uwnd,\n v_data = uvdata.vwnd,\n)" + "ecl.calc_horizontal_water_flux(\n specific_humidity_data=q_data,\n u_data=uvdata.uwnd,\n v_data=uvdata.vwnd,\n)" ] }, { @@ -447,7 +447,7 @@ }, "outputs": [], "source": [ - "water_flux_top2surface_integral = ecl.calc_water_flux_top2surface_integral(\n specific_humidity_data = q_data, \n u_data = u_data, \n v_data = v_data,\n surface_pressure_data = msl_data, \n surface_pressure_data_units = 'millibars',\n vertical_dim = 'level', \n vertical_dim_units = 'hPa',\n)\n\nwater_flux_top2surface_integral" + "water_flux_top2surface_integral = ecl.calc_water_flux_top2surface_integral(\n specific_humidity_data=q_data,\n u_data=u_data,\n v_data=v_data,\n surface_pressure_data=msl_data,\n surface_pressure_data_units=\"millibars\",\n vertical_dim=\"level\",\n vertical_dim_units=\"hPa\",\n)\n\nwater_flux_top2surface_integral" ] }, { @@ -465,7 +465,7 @@ }, "outputs": [], "source": [ - "draw_water_flux = water_flux_top2surface_integral.isel(time = 0).thin(lon = 3, lat = 3).sel(lat = slice(-60, 60))\ndraw_pr = pr_data.isel(time = 0).sel(lat = slice(-60, 60))" + "draw_water_flux = (\n water_flux_top2surface_integral.isel(time=0)\n .thin(lon=3, lat=3)\n .sel(lat=slice(-60, 60))\n)\ndraw_pr = pr_data.isel(time=0).sel(lat=slice(-60, 60))" ] }, { @@ -476,7 +476,7 @@ }, "outputs": [], "source": [ - "fig, ax = plt.subplots(subplot_kw = {'projection': ccrs.PlateCarree(central_longitude = 180)})\n\nax.gridlines(\n draw_labels = ['bottom', 'left'], \n color = \"grey\", \n alpha = 0.5, \n linestyle=\"--\"\n)\nax.coastlines(edgecolor = 'black', linewidths = 0.5)\n\ndraw_water_flux.plot.quiver(\n ax = ax,\n u = 'qu', v = 'qv', x = 'lon', y = 'lat',\n transform = ccrs.PlateCarree(),\n zorder = 2,\n)\n\ndraw_pr.plot.contourf(\n ax = ax,\n transform = ccrs.PlateCarree(),\n levels = 21,\n cmap = 'Greens',\n zorder = 1,\n cbar_kwargs = {'location': 'bottom'},\n vmax = 20\n)" + "fig, ax = plt.subplots(\n subplot_kw={\"projection\": ccrs.PlateCarree(central_longitude=180)}\n)\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\ndraw_water_flux.plot.quiver(\n ax=ax,\n u=\"qu\",\n v=\"qv\",\n x=\"lon\",\n y=\"lat\",\n transform=ccrs.PlateCarree(),\n zorder=2,\n)\n\ndraw_pr.plot.contourf(\n ax=ax,\n transform=ccrs.PlateCarree(),\n levels=21,\n cmap=\"Greens\",\n zorder=1,\n cbar_kwargs={\"location\": \"bottom\"},\n vmax=20,\n)" ] }, { @@ -494,7 +494,7 @@ }, "outputs": [], "source": [ - "divergence_watervaporflux_top2surface_integral = ecl.calc_divergence_watervaporflux_top2surface_integral(\n specific_humidity_data = q_data,\n u_data = u_data,\n v_data = v_data,\n surface_pressure_data = msl_data,\n surface_pressure_data_units = 'millibars',\n specific_humidity_units = 'grams/kg',\n vertical_dim = 'level',\n vertical_dim_units = 'hPa'\n)\n\ndivergence_watervaporflux_top2surface_integral" + "divergence_watervaporflux_top2surface_integral = (\n ecl.calc_divergence_watervaporflux_top2surface_integral(\n specific_humidity_data=q_data,\n u_data=u_data,\n v_data=v_data,\n surface_pressure_data=msl_data,\n surface_pressure_data_units=\"millibars\",\n specific_humidity_units=\"grams/kg\",\n vertical_dim=\"level\",\n vertical_dim_units=\"hPa\",\n )\n)\n\ndivergence_watervaporflux_top2surface_integral" ] }, { @@ -512,7 +512,7 @@ }, "outputs": [], "source": [ - "draw_data = divergence_watervaporflux_top2surface_integral.isel(time = 0).sel(lat = slice(-60, 60))" + "draw_data = divergence_watervaporflux_top2surface_integral.isel(time=0).sel(\n lat=slice(-60, 60)\n)" ] }, { @@ -523,7 +523,7 @@ }, "outputs": [], "source": [ - "fig, ax = plt.subplots(subplot_kw = {'projection': ccrs.PlateCarree(central_longitude = 180)})\n\nax.gridlines(\n draw_labels = ['bottom', 'left'], \n color = \"grey\", \n alpha = 0.5, \n linestyle=\"--\"\n)\nax.coastlines(edgecolor = 'black', linewidths = 0.5)\n\ndraw_data.plot.contourf(\n ax = ax,\n transform = ccrs.PlateCarree(),\n cbar_kwargs = {'location': 'bottom'},\n levels = 21\n)" + "fig, ax = plt.subplots(\n subplot_kw={\"projection\": ccrs.PlateCarree(central_longitude=180)}\n)\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\ndraw_data.plot.contourf(\n ax=ax, transform=ccrs.PlateCarree(), cbar_kwargs={\"location\": \"bottom\"}, levels=21\n)" ] } ], diff --git a/docs/example/plot_interp.ipynb b/docs/example/plot_interp.ipynb new file mode 100644 index 00000000..967d7a3b --- /dev/null +++ b/docs/example/plot_interp.ipynb @@ -0,0 +1,169 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Interpolation and Regriding\n\nBefore proceeding with all the steps, first import some necessary libraries and packages\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import easyclimate as ecl\nimport xarray as xr\nimport matplotlib.pyplot as plt\nimport cartopy.crs as ccrs\nimport numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interpolation from points to grid\nOpen sample surface pressure data for the European region\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data = ecl.open_tutorial_dataset(\"PressQFF_202007271200_872.csv\")\nprint(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":py:func:`easyclimate.interp.interp_point2mesh ` enables interpolation from site data to grid point data.\n\n.. seealso::\n\n - https://github.com/MeteoSwiss/fast-barnes-py\n - Z\u00fcrcher, B. K.: Fast approximate Barnes interpolation: illustrated by Python-Numba implementation fast-barnes-py v1.0, Geosci. Model Dev., 16, 1697\u20131711, https://doi.org/10.5194/gmd-16-1697-2023, 2023.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "meshdata = ecl.interp.interp_point2mesh(\n data,\n var_name=\"qff\",\n grid_x=37.5,\n grid_y=75.0,\n point=[-26.0, 34.5],\n resolution=32,\n sigma=1,\n)\nmeshdata" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plotting interpolated grid point data and corresponding station locations\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(subplot_kw={\"projection\": ccrs.PlateCarree(central_longitude=0)})\n\nax.gridlines(draw_labels=[\"bottom\", \"left\"], color=\"grey\", alpha=0.5, linestyle=\"--\")\nax.coastlines(edgecolor=\"black\", linewidths=0.5)\n\n# Draw interpolation results\nmeshdata.plot.contourf(\n ax=ax,\n transform=ccrs.PlateCarree(),\n cbar_kwargs={\"location\": \"bottom\"},\n cmap=\"RdBu_r\",\n levels=21,\n)\n\n# Draw observation stations\nax.scatter(data[\"lon\"], data[\"lat\"], s=1, c=\"r\", transform=ccrs.PlateCarree())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Regriding\nReading example raw grid data\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "u_data = ecl.tutorial.open_tutorial_dataset(\"uwnd_202201_mon_mean\").sortby(\"lat\").uwnd\nu_data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Define the target grid (only for **latitude/longitude and regular grids**)\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "target_grid = xr.DataArray(\n dims=(\"lat\", \"lon\"),\n coords={\n \"lat\": np.arange(-89, 89, 6) + 1 / 1.0,\n \"lon\": np.arange(-180, 180, 6) + 1 / 1.0,\n },\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":py:func:`easyclimate.interp.interp_point2mesh ` performs a regridding operation.\n\n.. seealso::\n\n https://github.com/EXCITED-CO2/xarray-regrid\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "regriding_data = ecl.interp.interp_mesh2mesh(u_data, target_grid)\nregriding_data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plotting differences before and after interpolation\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(1, 2, figsize=(12, 5))\n\nu_data.sel(level=500).isel(time=0).plot(ax=ax[0])\nax[0].set_title(\"Before\", size=20)\n\nregriding_data.sel(level=500).isel(time=0).plot(ax=ax[1])\nax[1].set_title(\"After\", size=20)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/docs/example/plot_taylor_diagram.ipynb b/docs/example/plot_taylor_diagram.ipynb new file mode 100644 index 00000000..205c958f --- /dev/null +++ b/docs/example/plot_taylor_diagram.ipynb @@ -0,0 +1,205 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Taylor Diagram\n\nThe Taylor Diagram is a graphical tool introduced by American meteorologist Karl E. Taylor in 2001 to assess the performance of models.\nIt provides an intuitive way to compare how well model simulations match observed data and to compare the performance of different models.\n\nThe basic structure of the Taylor Diagram includes radial and tangential coordinates. In the chart, the standard deviation of observed data is placed at the center, while the standard deviation of model simulations extends radially outward. Additionally, the correlation coefficient, which measures the strength and direction of the linear relationship between two sets of data, is represented along the tangential axis. This chart allows for a visual comparison of the dispersion, shape, and correlation with observed data.\n\nThe advantages of the Taylor Diagram in research, particularly in comparing climate model simulation performance, are as follows:\n\n- **Intuitiveness**: The Taylor Diagram presents the similarity between model simulations and observed data in an intuitive way, making it easy for researchers, including non-experts, to quickly understand model performance.\n\n- **Comprehensive Comparison**: The Taylor Diagram allows for the simultaneous comparison of model simulations with observed data and the performance of multiple models. This aids in identifying the most accurate models.\n\n- **Comprehensive Assessment**: By combining key statistical measures such as standard deviation and correlation coefficient, the Taylor Diagram offers a comprehensive evaluation of model performance, considering multiple aspects rather than focusing on a single parameter.\n\n- **Wide Applicability**: The Taylor Diagram is not limited to climate model performance; it can be applied to evaluate models in various fields such as meteorology, oceanography, and environmental science.\n\nThe Taylor Diagram, as a graphical tool, provides a clear and comprehensive way for researchers,\nincluding those without a specialized background, to assess model performance. It is particularly\nuseful for comparing multiple models and complex datasets in research.\n\n.. seealso::\n Taylor, K. E. (2001), Summarizing multiple aspects of model performance in a single diagram, J. Geophys. Res., 106(D7), 7183\u20137192, doi: https://doi.org/10.1029/2000JD900719.\n\nBefore proceeding with all the steps, first import some necessary libraries and packages\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import easyclimate as ecl\nimport xarray as xr\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport pandas as pd" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Assuming the simulation results of model 'a' are as follows\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "da_a = xr.DataArray(\n np.array([[1, 2, 3], [0.1, 0.2, 0.3], [3.2, 0.6, 1.8]]),\n dims=(\"lat\", \"time\"),\n coords={\n \"lat\": np.array([-30, 0, 30]),\n \"time\": pd.date_range(\"2000-01-01\", freq=\"D\", periods=3),\n },\n)\nda_a" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "At the same time, we also assume that model 'b' has the following simulation results\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "da_b = xr.DataArray(\n np.array([[0.2, 0.4, 0.6], [15, 10, 5], [3.2, 0.6, 1.8]]),\n dims=(\"lat\", \"time\"),\n coords={\n \"lat\": np.array([-30, 0, 30]),\n \"time\": pd.date_range(\"2000-01-01\", freq=\"D\", periods=3),\n },\n)\nda_b" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Observational (real) data should be directly obtained from instruments or reanalyzed in real life.\nHere we simply set it as the linear relationship between model `a` and model `b`.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "da_obs = (da_a + da_b) / 1.85\nda_obs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Build Dataset\n:py:func:`easyclimate.plot.calc_TaylorDiagrams_metadata ` provides us\nwith the necessary parameters for calculating the subsequent Taylor diagram.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "taylordiagrams_metadata = ecl.plot.calc_TaylorDiagrams_metadata(\n f=[da_a, da_b],\n r=[da_obs, da_obs],\n models_name=[\"f1\", \"f2\"],\n weighted=True,\n normalized=True,\n)\nprint(taylordiagrams_metadata)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Basic Figure Framework\n:py:func:`easyclimate.plot.draw_TaylorDiagrams_base ` can\ndraw the basic framework of the Taylor diagram, which provides a basic drawing board for the data we will place.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(subplot_kw={\"projection\": \"polar\"})\n\necl.plot.draw_TaylorDiagrams_base(ax=ax, std_max=2.5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Dataset Points\nTry using :py:func:`easyclimate.plot.draw_TaylorDiagrams_metadata ` to place\ndata on the basic framework of the Taylor diagram.\n\n

Note

:py:func:`ax.legend() ` or :py:func:`plt.legend() ` can add legend for Taylor diagram.

\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(subplot_kw={\"projection\": \"polar\"})\n\necl.plot.draw_TaylorDiagrams_base(ax=ax, std_max=2.5)\n\necl.plot.draw_TaylorDiagrams_metadata(\n taylordiagrams_metadata,\n ax=ax,\n marker_list=[\"o\", \"+\", \"*\"],\n color_list=[\"black\", \"red\", \"green\"],\n label_list=[\"\", \"\", \"\"],\n legend_list=taylordiagrams_metadata[\"item\"].to_list(),\n cc=\"cc\",\n std=\"std\",\n)\n\nax.legend(bbox_to_anchor=(1, 0.9))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The parameter `half_circle = True` in :py:func:`easyclimate.plot.draw_TaylorDiagrams_base ` can make the entire Taylor drawing board\nappear in a semi circular state, which allows us to discover\ndata points with negative standardized standard deviation (marked with a red cross)\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(subplot_kw={\"projection\": \"polar\"})\n\necl.plot.draw_TaylorDiagrams_base(\n ax=ax,\n std_max=2.6,\n std_interval=0.5,\n half_circle=True,\n x_label_pad=0.5,\n arc_label_pad=0.5,\n)\n\necl.plot.draw_TaylorDiagrams_metadata(\n taylordiagrams_metadata,\n ax=ax,\n marker_list=[\"o\", \"+\", \"*\"],\n color_list=[\"black\", \"red\", \"green\"],\n label_list=[\"\", \"\", \"\"],\n legend_list=taylordiagrams_metadata[\"item\"].to_list(),\n cc=\"cc\",\n std=\"std\",\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Points' Labels\n`label_list` in :py:func:`easyclimate.plot.draw_TaylorDiagrams_metadata ` can\nspecify the labels of these data points\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(subplot_kw={\"projection\": \"polar\"})\n\necl.plot.draw_TaylorDiagrams_base(ax=ax, std_max=2.5)\n\necl.plot.draw_TaylorDiagrams_metadata(\n taylordiagrams_metadata,\n ax=ax,\n marker_list=[\"o\", \"+\", \"*\"],\n color_list=[\"black\", \"red\", \"green\"],\n label_list=[\"1\", \"\", \"3\"],\n legend_list=taylordiagrams_metadata[\"item\"].to_list(),\n cc=\"cc\",\n std=\"std\",\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The position of these labels can be finely adjusted using `point_label_yoffset` and `point_label_xoffset`.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(subplot_kw={\"projection\": \"polar\"})\n\necl.plot.draw_TaylorDiagrams_base(ax=ax, std_max=2.5)\n\necl.plot.draw_TaylorDiagrams_metadata(\n taylordiagrams_metadata,\n ax=ax,\n marker_list=[\"o\", \"+\", \"*\"],\n color_list=[\"black\", \"red\", \"green\"],\n label_list=[\"1\", \"\", \"3\"],\n legend_list=taylordiagrams_metadata[\"item\"].to_list(),\n cc=\"cc\",\n std=\"std\",\n point_label_yoffset=[0.05, 0, 0.05],\n point_label_xoffset=[0.1, 0, 0],\n)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/docs/example/plot_time_scale_average.ipynb b/docs/example/plot_time_scale_average.ipynb new file mode 100644 index 00000000..25933d62 --- /dev/null +++ b/docs/example/plot_time_scale_average.ipynb @@ -0,0 +1,176 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Time Scale Average\n\nBefore proceeding with all the steps, first import some necessary libraries and packages\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import easyclimate as ecl\nimport xarray as xr" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Example data are sea ice concentration (SIC) data for the Barents-Kara Sea (30\u00b0-90\u00b0E, 65\u00b0-85\u00b0N).\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sic_data_Barents_Sea = ecl.tutorial.open_tutorial_dataset(\"mini_HadISST_ice\").sic\nsic_data_Barents_Sea" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Mean States\nSolving for the overall climatological mean state was solved using :py:func:`easyclimate.calc_all_climatological_mean `.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "ecl.calc_all_climatological_mean(sic_data_Barents_Sea, dim=\"time\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If the climate state is for each season, the results are solved using :py:func:`easyclimate.calc_seasonal_climatological_mean `.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "ecl.calc_seasonal_climatological_mean(sic_data_Barents_Sea, dim=\"time\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, if the climate state is for each month, the results are solved using :py:func:`easyclimate.calc_seasonal_cycle_mean `.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "ecl.calc_seasonal_cycle_mean(sic_data_Barents_Sea, dim=\"time\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Remove Seasonal Cycle\n:py:func:`easyclimate.remove_seasonal_cycle_mean ` helps us to remove seasonal cycles (annual cycles) from the data in order to obtain monthly average anomalies.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sic_data_Barents_Sea_remove_seasonal_cycle = ecl.remove_seasonal_cycle_mean(\n sic_data_Barents_Sea, dim=\"time\"\n)\nsic_data_Barents_Sea_remove_seasonal_cycle" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can visualize the results by plotting.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sic_data_Barents_Sea_remove_seasonal_cycle.mean(dim=(\"lat\", \"lon\")).sel(\n time=slice(\"2010-01-01\", \"2015-12-31\")\n).plot(\n figsize=(10, 3),\n marker=\".\",\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Convert to the Month-mean State corresponding to Each Month\n:py:func:`easyclimate.calc_seasonal_cycle_mean ` can solve for monthly average results,\nand :py:func:`easyclimate.transfer_monmean2everymonthmean ` can project monthly climate state results onto individual months for certain calculations.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# everymonth_climatology = ecl.transfer_monmean2everymonthmean(sic_data_Barents_Sea)\n# everymonth_climatology;" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can find that the seasonal cycles are all consistent.\neverymonth_climatology.mean(dim = ('lat', 'lon')).sel(time = slice('2010-01-01', '2015-12-31')).plot(\n figsize = (10, 3),\n marker = '.',\n)\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/release_requirements.txt b/release_requirements.txt index 3ff81543..0379c749 100644 --- a/release_requirements.txt +++ b/release_requirements.txt @@ -1,6 +1,6 @@ numpy >= 1.24.3 xarray >= 0.17.0,<=2023.12.0 -geocat.viz <= 2023.10.0 +geocat.viz cartopy >= 0.20 xeofs >= 2.2.2 matplotlib From e19070ece44d880ecc0e3e913509fd30bd9bbb19 Mon Sep 17 00:00:00 2001 From: Shenyulu <59901836+shenyulu@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:44:31 +0800 Subject: [PATCH 3/4] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fe0ea087..cdbcff27 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/shenyulu/easyclimate/main.svg)](https://results.pre-commit.ci/latest/github/shenyulu/easyclimate/main) [![Documentation Status](https://readthedocs.org/projects/easyclimate/badge/?version=latest)](https://easyclimate.readthedocs.io/en/latest/?badge=latest) [![DOI](https://zenodo.org/badge/465206111.svg)](https://zenodo.org/doi/10.5281/zenodo.10279567) -[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/shenyulu/easyclimate/main?labpath=.binder) +[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/shenyulu/easyclimate/dev?labpath=docs%2Fexample) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) From 9cdf2642844bbdf829832afec8abf1208c38420d Mon Sep 17 00:00:00 2001 From: Shenyulu <59901836+shenyulu@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:45:18 +0800 Subject: [PATCH 4/4] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cdbcff27..b67754cc 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/shenyulu/easyclimate/main.svg)](https://results.pre-commit.ci/latest/github/shenyulu/easyclimate/main) [![Documentation Status](https://readthedocs.org/projects/easyclimate/badge/?version=latest)](https://easyclimate.readthedocs.io/en/latest/?badge=latest) [![DOI](https://zenodo.org/badge/465206111.svg)](https://zenodo.org/doi/10.5281/zenodo.10279567) -[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/shenyulu/easyclimate/dev?labpath=docs%2Fexample) +[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/shenyulu/easyclimate/main?labpath=docs%2Fexample) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)