Skip to content

Commit e574663

Browse files
rabernatkeewis
authored andcommitted
Switch doc examples to use nbsphinx (pydata#3105)
* switching out examples to use nbsphinx * added jupyter_client to doc env * added ipykernel to doc env
1 parent 6b70107 commit e574663

File tree

7 files changed

+570
-1166
lines changed

7 files changed

+570
-1166
lines changed

doc/conf.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,19 +77,24 @@
7777
"IPython.sphinxext.ipython_directive",
7878
"IPython.sphinxext.ipython_console_highlighting",
7979
"sphinx_gallery.gen_gallery",
80+
"nbsphinx",
8081
]
8182

8283
extlinks = {
8384
"issue": ("https://github.com/pydata/xarray/issues/%s", "GH"),
8485
"pull": ("https://github.com/pydata/xarray/pull/%s", "PR"),
8586
}
8687

87-
sphinx_gallery_conf = {
88-
"examples_dirs": "gallery",
89-
"gallery_dirs": "auto_gallery",
90-
"backreferences_dir": False,
91-
"expected_failing_examples": list(allowed_failures),
92-
}
88+
nbsphinx_timeout = 600
89+
nbsphinx_execute = "always"
90+
nbsphinx_prolog = """
91+
{% set docname = env.doc2path(env.docname, base=None) %}
92+
93+
You can run this notebook in a `live session <https://mybinder.org/v2/gh/pydata/xarray/doc/examples/master?urlpath=lab/tree/doc/{{ docname }}>`_ |Binder| or view it `on Github <https://github.com/pydata/xarray/blob/master/doc/{{ docname }}>`_.
94+
95+
.. |Binder| image:: https://mybinder.org/badge.svg
96+
:target: https://mybinder.org/v2/gh/pydata/xarray/master?urlpath=lab/tree/doc/{{ docname }}
97+
"""
9398

9499
autosummary_generate = True
95100
autodoc_typehints = "none"
@@ -137,7 +142,7 @@
137142

138143
# List of patterns, relative to source directory, that match files and
139144
# directories to ignore when looking for source files.
140-
exclude_patterns = ["_build"]
145+
exclude_patterns = ["_build", "**.ipynb_checkpoints"]
141146

142147
# The reST default role (used for this markup: `text`) to use for all
143148
# documents.

doc/examples/monthly-means.ipynb

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"Calculating Seasonal Averages from Timeseries of Monthly Means \n",
8+
"=====\n",
9+
"\n",
10+
"Author: [Joe Hamman](https://github.com/jhamman/)\n",
11+
"\n",
12+
"The data used for this example can be found in the [xarray-data](https://github.com/pydata/xarray-data) repository. You may need to change the path to `rasm.nc` below.\n",
13+
"\n",
14+
"Suppose we have a netCDF or `xarray.Dataset` of monthly mean data and we want to calculate the seasonal average. To do this properly, we need to calculate the weighted average considering that each month has a different number of days."
15+
]
16+
},
17+
{
18+
"cell_type": "code",
19+
"execution_count": null,
20+
"metadata": {
21+
"ExecuteTime": {
22+
"end_time": "2018-11-28T20:51:35.958210Z",
23+
"start_time": "2018-11-28T20:51:35.936966Z"
24+
}
25+
},
26+
"outputs": [],
27+
"source": [
28+
"%matplotlib inline\n",
29+
"import numpy as np\n",
30+
"import pandas as pd\n",
31+
"import xarray as xr\n",
32+
"from netCDF4 import num2date\n",
33+
"import matplotlib.pyplot as plt "
34+
]
35+
},
36+
{
37+
"cell_type": "markdown",
38+
"metadata": {},
39+
"source": [
40+
"#### Some calendar information so we can support any netCDF calendar. "
41+
]
42+
},
43+
{
44+
"cell_type": "code",
45+
"execution_count": null,
46+
"metadata": {
47+
"ExecuteTime": {
48+
"end_time": "2018-11-28T20:51:35.991620Z",
49+
"start_time": "2018-11-28T20:51:35.960336Z"
50+
}
51+
},
52+
"outputs": [],
53+
"source": [
54+
"dpm = {'noleap': [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],\n",
55+
" '365_day': [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],\n",
56+
" 'standard': [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],\n",
57+
" 'gregorian': [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],\n",
58+
" 'proleptic_gregorian': [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],\n",
59+
" 'all_leap': [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],\n",
60+
" '366_day': [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],\n",
61+
" '360_day': [0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30]} "
62+
]
63+
},
64+
{
65+
"cell_type": "markdown",
66+
"metadata": {},
67+
"source": [
68+
"#### A few calendar functions to determine the number of days in each month\n",
69+
"If you were just using the standard calendar, it would be easy to use the `calendar.month_range` function."
70+
]
71+
},
72+
{
73+
"cell_type": "code",
74+
"execution_count": null,
75+
"metadata": {
76+
"ExecuteTime": {
77+
"end_time": "2018-11-28T20:51:36.015151Z",
78+
"start_time": "2018-11-28T20:51:35.994079Z"
79+
}
80+
},
81+
"outputs": [],
82+
"source": [
83+
"def leap_year(year, calendar='standard'):\n",
84+
" \"\"\"Determine if year is a leap year\"\"\"\n",
85+
" leap = False\n",
86+
" if ((calendar in ['standard', 'gregorian',\n",
87+
" 'proleptic_gregorian', 'julian']) and\n",
88+
" (year % 4 == 0)):\n",
89+
" leap = True\n",
90+
" if ((calendar == 'proleptic_gregorian') and\n",
91+
" (year % 100 == 0) and\n",
92+
" (year % 400 != 0)):\n",
93+
" leap = False\n",
94+
" elif ((calendar in ['standard', 'gregorian']) and\n",
95+
" (year % 100 == 0) and (year % 400 != 0) and\n",
96+
" (year < 1583)):\n",
97+
" leap = False\n",
98+
" return leap\n",
99+
"\n",
100+
"def get_dpm(time, calendar='standard'):\n",
101+
" \"\"\"\n",
102+
" return a array of days per month corresponding to the months provided in `months`\n",
103+
" \"\"\"\n",
104+
" month_length = np.zeros(len(time), dtype=np.int)\n",
105+
" \n",
106+
" cal_days = dpm[calendar]\n",
107+
" \n",
108+
" for i, (month, year) in enumerate(zip(time.month, time.year)):\n",
109+
" month_length[i] = cal_days[month]\n",
110+
" if leap_year(year, calendar=calendar):\n",
111+
" month_length[i] += 1\n",
112+
" return month_length"
113+
]
114+
},
115+
{
116+
"cell_type": "markdown",
117+
"metadata": {},
118+
"source": [
119+
"#### Open the `Dataset`"
120+
]
121+
},
122+
{
123+
"cell_type": "code",
124+
"execution_count": null,
125+
"metadata": {
126+
"ExecuteTime": {
127+
"end_time": "2018-11-28T20:51:36.072316Z",
128+
"start_time": "2018-11-28T20:51:36.016594Z"
129+
}
130+
},
131+
"outputs": [],
132+
"source": [
133+
"ds = xr.tutorial.open_dataset('rasm').load()\n",
134+
"print(ds)"
135+
]
136+
},
137+
{
138+
"cell_type": "markdown",
139+
"metadata": {},
140+
"source": [
141+
"#### Now for the heavy lifting:\n",
142+
"We first have to come up with the weights,\n",
143+
"- calculate the month lengths for each monthly data record\n",
144+
"- calculate weights using `groupby('time.season')`\n",
145+
"\n",
146+
"Finally, we just need to multiply our weights by the `Dataset` and sum allong the time dimension. "
147+
]
148+
},
149+
{
150+
"cell_type": "code",
151+
"execution_count": null,
152+
"metadata": {
153+
"ExecuteTime": {
154+
"end_time": "2018-11-28T20:51:36.132413Z",
155+
"start_time": "2018-11-28T20:51:36.073708Z"
156+
}
157+
},
158+
"outputs": [],
159+
"source": [
160+
"# Make a DataArray with the number of days in each month, size = len(time)\n",
161+
"month_length = xr.DataArray(get_dpm(ds.time.to_index(), calendar='noleap'),\n",
162+
" coords=[ds.time], name='month_length')\n",
163+
"\n",
164+
"# Calculate the weights by grouping by 'time.season'.\n",
165+
"# Conversion to float type ('astype(float)') only necessary for Python 2.x\n",
166+
"weights = month_length.groupby('time.season') / month_length.astype(float).groupby('time.season').sum()\n",
167+
"\n",
168+
"# Test that the sum of the weights for each season is 1.0\n",
169+
"np.testing.assert_allclose(weights.groupby('time.season').sum().values, np.ones(4))\n",
170+
"\n",
171+
"# Calculate the weighted average\n",
172+
"ds_weighted = (ds * weights).groupby('time.season').sum(dim='time')"
173+
]
174+
},
175+
{
176+
"cell_type": "code",
177+
"execution_count": null,
178+
"metadata": {
179+
"ExecuteTime": {
180+
"end_time": "2018-11-28T20:51:36.152913Z",
181+
"start_time": "2018-11-28T20:51:36.133997Z"
182+
}
183+
},
184+
"outputs": [],
185+
"source": [
186+
"print(ds_weighted)"
187+
]
188+
},
189+
{
190+
"cell_type": "code",
191+
"execution_count": null,
192+
"metadata": {
193+
"ExecuteTime": {
194+
"end_time": "2018-11-28T20:51:36.190765Z",
195+
"start_time": "2018-11-28T20:51:36.154416Z"
196+
}
197+
},
198+
"outputs": [],
199+
"source": [
200+
"# only used for comparisons\n",
201+
"ds_unweighted = ds.groupby('time.season').mean('time')\n",
202+
"ds_diff = ds_weighted - ds_unweighted"
203+
]
204+
},
205+
{
206+
"cell_type": "code",
207+
"execution_count": null,
208+
"metadata": {
209+
"ExecuteTime": {
210+
"end_time": "2018-11-28T20:51:40.264871Z",
211+
"start_time": "2018-11-28T20:51:36.192467Z"
212+
}
213+
},
214+
"outputs": [],
215+
"source": [
216+
"# Quick plot to show the results\n",
217+
"notnull = pd.notnull(ds_unweighted['Tair'][0])\n",
218+
"\n",
219+
"fig, axes = plt.subplots(nrows=4, ncols=3, figsize=(14,12))\n",
220+
"for i, season in enumerate(('DJF', 'MAM', 'JJA', 'SON')):\n",
221+
" ds_weighted['Tair'].sel(season=season).where(notnull).plot.pcolormesh(\n",
222+
" ax=axes[i, 0], vmin=-30, vmax=30, cmap='Spectral_r', \n",
223+
" add_colorbar=True, extend='both')\n",
224+
" \n",
225+
" ds_unweighted['Tair'].sel(season=season).where(notnull).plot.pcolormesh(\n",
226+
" ax=axes[i, 1], vmin=-30, vmax=30, cmap='Spectral_r', \n",
227+
" add_colorbar=True, extend='both')\n",
228+
"\n",
229+
" ds_diff['Tair'].sel(season=season).where(notnull).plot.pcolormesh(\n",
230+
" ax=axes[i, 2], vmin=-0.1, vmax=.1, cmap='RdBu_r',\n",
231+
" add_colorbar=True, extend='both')\n",
232+
"\n",
233+
" axes[i, 0].set_ylabel(season)\n",
234+
" axes[i, 1].set_ylabel('')\n",
235+
" axes[i, 2].set_ylabel('')\n",
236+
"\n",
237+
"for ax in axes.flat:\n",
238+
" ax.axes.get_xaxis().set_ticklabels([])\n",
239+
" ax.axes.get_yaxis().set_ticklabels([])\n",
240+
" ax.axes.axis('tight')\n",
241+
" ax.set_xlabel('')\n",
242+
" \n",
243+
"axes[0, 0].set_title('Weighted by DPM')\n",
244+
"axes[0, 1].set_title('Equal Weighting')\n",
245+
"axes[0, 2].set_title('Difference')\n",
246+
" \n",
247+
"plt.tight_layout()\n",
248+
"\n",
249+
"fig.suptitle('Seasonal Surface Air Temperature', fontsize=16, y=1.02)"
250+
]
251+
},
252+
{
253+
"cell_type": "code",
254+
"execution_count": null,
255+
"metadata": {
256+
"ExecuteTime": {
257+
"end_time": "2018-11-28T20:51:40.284898Z",
258+
"start_time": "2018-11-28T20:51:40.266406Z"
259+
}
260+
},
261+
"outputs": [],
262+
"source": [
263+
"# Wrap it into a simple function\n",
264+
"def season_mean(ds, calendar='standard'):\n",
265+
" # Make a DataArray of season/year groups\n",
266+
" year_season = xr.DataArray(ds.time.to_index().to_period(freq='Q-NOV').to_timestamp(how='E'),\n",
267+
" coords=[ds.time], name='year_season')\n",
268+
"\n",
269+
" # Make a DataArray with the number of days in each month, size = len(time)\n",
270+
" month_length = xr.DataArray(get_dpm(ds.time.to_index(), calendar=calendar),\n",
271+
" coords=[ds.time], name='month_length')\n",
272+
" # Calculate the weights by grouping by 'time.season'\n",
273+
" weights = month_length.groupby('time.season') / month_length.groupby('time.season').sum()\n",
274+
"\n",
275+
" # Test that the sum of the weights for each season is 1.0\n",
276+
" np.testing.assert_allclose(weights.groupby('time.season').sum().values, np.ones(4))\n",
277+
"\n",
278+
" # Calculate the weighted average\n",
279+
" return (ds * weights).groupby('time.season').sum(dim='time')"
280+
]
281+
},
282+
{
283+
"cell_type": "code",
284+
"execution_count": null,
285+
"metadata": {},
286+
"outputs": [],
287+
"source": []
288+
}
289+
],
290+
"metadata": {
291+
"anaconda-cloud": {},
292+
"kernelspec": {
293+
"display_name": "Python 3",
294+
"language": "python",
295+
"name": "python3"
296+
},
297+
"language_info": {
298+
"codemirror_mode": {
299+
"name": "ipython",
300+
"version": 3
301+
},
302+
"file_extension": ".py",
303+
"mimetype": "text/x-python",
304+
"name": "python",
305+
"nbconvert_exporter": "python",
306+
"pygments_lexer": "ipython3",
307+
"version": "3.6.8"
308+
},
309+
"toc": {
310+
"base_numbering": 1,
311+
"nav_menu": {},
312+
"number_sections": true,
313+
"sideBar": true,
314+
"skip_h1_title": false,
315+
"title_cell": "Table of Contents",
316+
"title_sidebar": "Contents",
317+
"toc_cell": true,
318+
"toc_position": {},
319+
"toc_section_display": true,
320+
"toc_window_display": true
321+
}
322+
},
323+
"nbformat": 4,
324+
"nbformat_minor": 2
325+
}

0 commit comments

Comments
 (0)