-
-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added Tutorial to explain how explore Iris Cubes with GeoViews
- Loading branch information
1 parent
79fab65
commit 283d3b3
Showing
2 changed files
with
350 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,348 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"import iris\n", | ||
"import numpy as np\n", | ||
"import holoviews as hv\n", | ||
"import geoviews as gv\n", | ||
"from cartopy import crs as ccrs\n", | ||
"from cartopy import feature as cf\n", | ||
"\n", | ||
"hv.notebook_extension()\n", | ||
"%output widgets='live' size=200" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"<div class=\"alert alert-info\" role=\"alert\">\n", | ||
" <strong>Alert:</strong> The widgets in this notebook a set to render load dynamically and require a live notebook server. If you're viewing this notebook on a static website the widgets won't respond.\n", | ||
"</div>" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The main strength of [HoloViews](http://holoviews.org) and its extensions (like GeoViews) is the ability to quickly explore complex datasets by declaring lower dimensional views into a higher-dimensional space. In HoloViews we refer to the interface that allows you to do this as the conversion API. To begin with we will load a multi-dimensional dataset of surface temperatures using [Iris](http://scitools.org.uk/iris/):" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": true | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"import numpy as np\n", | ||
"import iris\n", | ||
"import iris.coords\n", | ||
"\n", | ||
"def realization_metadata(cube, field, fname):\n", | ||
" if not cube.coords('realization'):\n", | ||
" realization_number = fname[-6:-3]\n", | ||
" realization_coord = iris.coords.AuxCoord(np.int32(realization_number), 'realization')\n", | ||
" cube.add_aux_coord(realization_coord)\n", | ||
"\n", | ||
"surface_temp = iris.load_cube(iris.sample_data_path('GloSea4', 'ensemble_???.pp'),\n", | ||
" iris.Constraint('surface_temperature', realization=lambda value: True),\n", | ||
" callback=realization_metadata)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"As we saw in the [Introductory Tutorial](Introductory_Tutorial.ipynb) we can simple wrap this iris Cube datastructure in a HoloViews Dataset." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"cube = hv.Dataset(surface_temp)\n", | ||
"cube" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"From the repr we can immediately see the list of key dimensions (time, realization, longitude and latitude) and the value dimension of the cube (surface_temperature), however unlike most other HoloViews Elements the ``Dataset`` Element does not display itself visually. This is because it can be n-dimensional and therefore does not have a straightforward visual representation. To view the cube we first have to transform it into individually visualizable chunks." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Conversions" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"A HoloViews Dataset is a wrapper around a complex multi-dimensional datastructure which allows the user to convert their data into individually visualizable views, which are usually of lower dimensionality. This is done by grouping the data by some dimension and then casting it to a specific Element type, which visualizes itself.\n", | ||
"\n", | ||
"The ``dataset.to`` interface makes this especially easy, you supply the Element type that you want to view the data as and the key dimensions of that view and it will figure out the rest. Depending on the type of Element you can specify one or more dimensions to be displayed. This means that only certain Elements allow you to display the data on a cartographic projection. Recall that the cube we are working with has 4 coordinate dimensions (or key dimensions as they are known in HoloViews) - time, realization, longitude and latitude. A geographic plot is defined as a plot that has longitude along the x-axis and latitude along the y-axis.\n", | ||
"\n", | ||
"To declare a two dimensional plot type we therefore simply request an Image plot of ``longitude`` and ``latitude``. All the remaining dimensions are automatically inferred, i.e. the value dimension (vdim) is ``surface_temperature`` and any remaining dimensions are assigned to the ``HoloMap`` datastructure, which allows you to explore the data using widgets:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"%%opts Layout [fig_inches=(12, 4)]\n", | ||
"geo_dims = ['longitude', 'latitude']\n", | ||
"coastline = gv.Feature(cf.COASTLINE)\n", | ||
"cube.to(gv.Image, geo_dims) * coastline + cube.to(gv.Image, geo_dims) * coastline" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"In this way we can visualize the 2D data in a number of ways, currently either as an ``Image`` (as above) or as ``LineContours``, ``FilledContours`` or ``Points``:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"%%opts Layout [fig_inches=(4, 8)]\n", | ||
"%%opts Points [color_index=2 size_index=None colorbar=True]\n", | ||
"hv.Layout([cube.to(el, geo_dims) * coastline\n", | ||
" for el in [gv.FilledContours, gv.LineContours, gv.Points]]).cols(1)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Note that by default the conversion interface will automatically expand all the individual Elements, which can take some time if the data is very large. Instead we can also request the objects to be expanded dynamically using the ``dynamic`` keyword:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"cube.to(gv.Image, geo_dims, dynamic=True) * coastline" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"This means that the data for each frame is only extracted when you're actually viewing that part of the data, which can have huge benefits in terms of speed and memory consumption." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Non-geographic views" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"So far we have focused entirely on geographic views of the data, plotting the data on a projection. However the conversion interface is completely general and we can slice and dice the data in any way we like. The simplest example of such a view is simply a view showing the temperature over time for each realization, longitude and latitude coordinate:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"%%opts Curve [xrotation=25 aspect=2]\n", | ||
"cube.to(hv.Curve, 'time', dynamic=True)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Another possible view is to the data as a ``HeatMap`` over time and realization at each longitude and latitude:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"%%opts HeatMap [show_values=False colorbar=True]\n", | ||
"cube.to(hv.HeatMap, ['realization', 'time'], dynamic=True)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Lower dimensional views" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"So far all the conversions have been seen have encapsulated all the available coordinate dimensions. However often times we want to see the spread of values along one or more dimensions. A simple example of this is a box plot where we might want to see the spread of surface_temperature on each day but don't want to ignore the latitude and longitude coordinates. To ignore particular dimensions we can explicitly declare the map dimensions (i.e. the key dimensions of the HoloMap container). By declaring an empty list of ``mdims`` for instance we can tell the conversion interface to simply ignore all dimensions except the particular key dimension that was supplied in this case the 'time' and 'realization':" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"%%opts BoxWhisker [xrotation=25 bgcolor='w']\n", | ||
"hv.Layout([cube.to.box(d, mdims=[]) for d in ['time', 'realization']])" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"This also gives us access to other statistical plot types, e.g. with the ``seaborn`` library installed we also have access to the ``Distribution`` Element which visualizes the data as a kernel density estimate. In this way we can visualize how the distribution of surface temperature values varies over time and the model realizations. We do this by ignoring the 'latitude' and 'longitude' in the list of mdims, generating a lower dimensional view into the data and laying out 'time' and 'realization' in a GridSpace." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"%opts GridSpace [shared_xaxis=True] \n", | ||
"%opts Distribution [fontsize={'xticks': 6} bgcolor='w' show_grid=False xticks=[220, 300]]\n", | ||
"try:\n", | ||
" import seaborn\n", | ||
" grid = cube.to.distribution(mdims=['realization', 'time']).grid()\n", | ||
"except:\n", | ||
" grid = None\n", | ||
"grid" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Decimating the data" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"So far all the examples we have seen have displayed all the data in some way or another. Another way to explore a dataset is to explicitly reduce the dimensionality of a dataset. There are two main ways to do this, either we explicitly select a subset of the data or we collapse a dimension using an aggregation function, e.g. by computing a mean along a particular dimension." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"### Selecting a particular coordinate" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"We can select a particular coordinate using the select method, cast the data to Curves, reindex the data to drop the now constant latitude and longitude dimensions and overlay the remaining 'realization' dimension." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"%opts NdOverlay [xrotation=25 aspect=2 legend_position='right' legend_cols=2] Curve (color=Palette('Set1'))\n", | ||
"cube.select(latitude=0, longitude=0).to(hv.Curve, ['time']).reindex().overlay()" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Alternatively we can also collapse specific coordinates on the iris Cube first, then wrap it in a Dataset and convert it to curves indexed over time, which we can again overlay:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"for dim in ['longitude', 'latitude']:\n", | ||
" if cube.data.coord(dim).bounds is None:\n", | ||
" cube.data.coord(dim).guess_bounds()\n", | ||
"\n", | ||
"grid_weights = iris.analysis.cartography.area_weights(cube.data)\n", | ||
"collapsed_cube = cube.data.collapsed(['longitude', 'latitude'], iris.analysis.MEAN, weights=grid_weights)\n", | ||
"hv.Dataset(collapsed_cube).to(hv.Curve, 'time').overlay()" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"hide_input": false, | ||
"kernelspec": { | ||
"display_name": "Python 2", | ||
"language": "python", | ||
"name": "python2" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": { | ||
"name": "ipython", | ||
"version": 2 | ||
}, | ||
"file_extension": ".py", | ||
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython2", | ||
"version": "2.7.11" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 0 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters