Skip to content

Commit

Permalink
Added Jim's edits to the introduction to elements notebook
Browse files Browse the repository at this point in the history
  • Loading branch information
jlstevens committed Jul 4, 2017
1 parent 5e747d6 commit 3208d49
Showing 1 changed file with 94 additions and 48 deletions.
142 changes: 94 additions & 48 deletions notebooks/01-introduction-to-elements.ipynb
Expand Up @@ -20,7 +20,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
Expand All @@ -33,7 +35,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Here we import numpy and pandas with their standard aliases and HoloViews with its standard short form of ``hv``. The line reading ``hv.extension('bokeh')`` loads and activates the bokeh plotting backend so all visualizations will be generated using [Bokeh](bokeh.pydata.org). We will see how to use matplotlib instead of bokeh later in the tutorial [Customizing Visual Appearance](./02-customizing-visual-appearance.ipynb)."
"Here we import the [NumPy](http://numpy.org) and [pandas](http://pandas.org) data libraries with their standard abbreviations, plus HoloViews with its standard abbreviation ``hv``. The line reading ``hv.extension('bokeh')`` loads and activates the bokeh plotting backend, so all visualizations will be generated using [Bokeh](bokeh.pydata.org). We will see how to use matplotlib instead of bokeh later in the tutorial [Customizing Visual Appearance](./02-customizing-visual-appearance.ipynb)."
]
},
{
Expand All @@ -42,9 +44,9 @@
"source": [
"# What are elements?\n",
"\n",
"In short, elements are HoloViews' most basic, core primitives that are all subclasses of ``Element``. All ``Element`` objects accept semantic metadata that allows their input data to be given an automatic, visual representation. Most importantly, element objects always preserve the raw data they are supplied.\n",
"In short, elements are HoloViews' most basic, core primitives. All the various types of ``hv.Element`` accept semantic metadata that allows their input data to be given an automatic, visual representation. Most importantly, element objects always preserve the raw data they are supplied.\n",
"\n",
"In this notebook we will explore a number of different element types and examine some of the ways that elements can supplement the supplied data with useful semantic data. There are many element types you might chose in the exercises and to help you make your choice, you can browse the [visual reference](http://holoviews.org/reference/index.html) which shows all the element types."
"In this notebook we will explore a number of different element types and examine some of the ways that elements can supplement the supplied data with useful semantic data. To choose your own types to use in the exercises, you can browse them all in the [reference gallery](http://holoviews.org/reference/index.html)."
]
},
{
Expand All @@ -53,7 +55,7 @@
"source": [
"## Creating elements\n",
"\n",
"All basic elements accept their data as a single, mandatory positional argument which may be supplied in a number of different formats, some of which we will now examine. A handful of *annotation* elements are exceptions to this rule, namely ``Arrow``, ``Text``, ``Bounds``, ``Box`` and ``Ellipse`` as they require additional positional arguments.\n",
"All basic elements accept their data as a single, mandatory positional argument which may be supplied in a number of different formats, some of which we will now examine. A handful of *annotation* elements are exceptions to this rule, namely ``Arrow``, ``Text``, ``Bounds``, ``Box`` and ``Ellipse``, as they require additional positional arguments.\n",
"\n",
"### A simple curve\n",
"\n",
Expand All @@ -63,7 +65,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"xs = [i for i in range(-10,11)]\n",
Expand All @@ -76,13 +80,15 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Here we supplied two lists in a tuple to [``hv.Curve``]((http://build.holoviews.org/reference/elements/bokeh/Curve.html), resulting in the curve object above. What you are looking at is the rich visual representations of this object generated by HoloViews using Bokeh. To demonstrate that this is an object and not a plot (which is carried out behind the scenes), here is the textual representation of this ``curve``:"
"Here we supplied two lists in a tuple to [``hv.Curve``]((http://build.holoviews.org/reference/elements/bokeh/Curve.html), resulting in the curve object above. What you are looking at is the rich visual representation of the ``simple_curve`` data object, which is automatically generated by HoloViews using Bokeh when Jupyter Notbook asks for the representation of ``simple_curve`` as the output of the cell above. To demonstrate that ``simple_curve`` itself is your data, not the plot, you can look at a textual representation for the same object using ``print`` instead:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"print(simple_curve)"
Expand All @@ -92,7 +98,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"There are a number of similar elements to [``Curve``](http://build.holoviews.org/reference/elements/bokeh/Curve.html) such as [``Area``](http://build.holoviews.org/reference/elements/bokeh/Area.html) and [``Scatter``](http://build.holoviews.org/reference/elements/bokeh/Scatter.html) which you can try out for yourself in the exercises."
"The textual representation indicates that this object is a continuous function mapping from `x` to `y`, and you can see the original data stored in it if you wish:"
]
},
{
Expand All @@ -103,7 +109,14 @@
},
"outputs": [],
"source": [
"# Exercise: Try switching hv.Curve with hv.Area and hv.Scatter"
"simple_curve.data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are a number of similar elements to [``Curve``](http://build.holoviews.org/reference/elements/bokeh/Curve.html) such as [``Area``](http://build.holoviews.org/reference/elements/bokeh/Area.html) and [``Scatter``](http://build.holoviews.org/reference/elements/bokeh/Scatter.html), which you can try out for yourself in the exercises."
]
},
{
Expand All @@ -114,28 +127,36 @@
},
"outputs": [],
"source": [
"# Optional: \n",
"# Look at the .data attribute of the elements you created to see the raw data (as a pandas DataFrame)"
"# Exercise: Try switching hv.Curve with hv.Area and hv.Scatter"
]
},
{
"cell_type": "markdown",
"metadata": {},
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"### Annotating the curve"
"# Optional: \n",
"# Look at the .data attribute of the elements you created to see the raw data (as a pandas DataFrame)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So far the only thing that we have done with our two lists (``xs`` and ``ys``) is wrap it in different types of elements. There is more to annotating your data with suitable semantics that just declaring the type, for instance we might want to specify what the x-axis and y-axis actual correpond to. Perhaps this parabola is the trajectory of a thrown ball:"
"### Annotating the curve\n",
"\n",
"So far the only thing that we have done with our pair of lists (``xs`` and ``ys``) is wrap it in different types of elements. There is more to annotating your data with suitable semantics that just declaring the type. For instance, we might want to specify what the x-axis and y-axis actually correspond to. Perhaps this parabola is the trajectory of a ball thrown into the air, in which case we could declare the object as:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"trajectory = hv.Curve((xs,ys), kdims=['distance'], vdims=['height'])\n",
Expand All @@ -146,27 +167,24 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"We have added *semantic* information about our curve to the [``Curve``](http://build.holoviews.org/reference/elements/bokeh/Curve.html) element where the ``kdim`` or *key dimension* corresponds to the independent variable ('distance') and the ``vdim`` or *value dimension* is the dependent variable. This additional information is *about* the data and is reflected in it's visual representation: we see that the axes above have now been labelled accordingly."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Casting between elements"
"Here we have added *semantic* information about our data to the [``Curve``](http://build.holoviews.org/reference/elements/bokeh/Curve.html) element. The ``kdim`` or *key dimension* corresponds to the independent variable ('distance') and the ``vdim`` or *value dimension* is the dependent variable. Even though the additional information is *about* the data itself, not just about the plot, you can see that the visual representation also changes, with the axes labeled accordingly."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The type of an element tells you something about the structure of the data and gives HoloViews the appropriate hint required to generate a suitable visual representation from it. That said, the nature of the dimensions are actual *more* fundamental to the essential semantics than the type which is why HoloViews makes it trivial to cast between compatible element types:"
"### Casting between elements\n",
"\n",
"The type of an element is a declaration of important facts about your data, which gives HoloViews the appropriate hint required to generate a suitable visual representation from it. For instance, calling it a ``Curve`` is a declaration that the data consists of samples from an underlying continuous function, which is why HoloViews plots it as a connected object. If we convert to an ``hv.Scatter`` object instead, the same set of data will show up as separated points, because \"Scatter\" does not make an assumption that the data is meant to be continuous:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"hv.Scatter(simple_curve)"
Expand All @@ -176,7 +194,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The ability to cast between elements is very useful and can be a lot easier than having to declare them anew. Note how the x- and y-axes continues to correspond to distance and height respectively as the element types change. In other words, once you have declared the key and value dimensions, this information propagated appropriately to the derived elements."
"Casting the same data between different Element types in this way is often useful as a way to see your data in a new way, particularly if you are not certain of a single best way to interpret the data. Note how the x- and y-axes continues to correspond to distance and height respectively as the element types change. In other words, once you have declared the key and value dimensions, this information will be propagated appropriately to the derived elements."
]
},
{
Expand Down Expand Up @@ -217,13 +235,15 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"This two dimensional array can be pass to [``Image``]((http://build.holoviews.org/reference/elements/bokeh/Image.html) to create an image element:"
"This two dimensional array can be passed to [``Image``]((http://build.holoviews.org/reference/elements/bokeh/Image.html) to create an image element:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"hv.Image(arr)"
Expand Down Expand Up @@ -271,7 +291,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"economic_data = pd.read_csv('../data/macro.csv')\n",
Expand All @@ -288,7 +310,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"US_data = economic_data[economic_data['country'] == 'United States'] # Select data for the US only\n",
Expand All @@ -298,7 +322,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"growth = hv.Curve(US_data, kdims=['year'], vdims=['growth'])\n",
Expand Down Expand Up @@ -331,15 +357,17 @@
"\n",
"In this example, the simplistic axis labels are starting to getting rather limiting. Changing the ``kdims`` and ``vdims`` is no longer trivial either, as they need to match the column names in the dataframe. Is the only solution to rename the columns in our dataframe?\n",
"\n",
"*Absolutely not!* The recommendation is that you use short, programmer and pandas-friendly, [tab-completeable](http://pandas.pydata.org/pandas-docs/stable/dsintro.html#dataframe-column-attribute-access-and-ipython-completion) column names as these are also the best dimension names to use with HoloViews.\n",
"Luckily, no. The recommendation is that you use short, programmer and pandas-friendly, [tab-completeable](http://pandas.pydata.org/pandas-docs/stable/dsintro.html#dataframe-column-attribute-access-and-ipython-completion) column names as these are also the best dimension names to use with HoloViews.\n",
"\n",
"What you should do instead is set the *dimension labels* as dimensions real rich objects behind the scenes: "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"gdp_growth = growth.redim.label(growth='GDP growth')\n",
Expand All @@ -356,7 +384,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"gdp_growth.vdims"
Expand All @@ -383,7 +413,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"gdp_growth.redim.unit(growth='%')"
Expand Down Expand Up @@ -416,13 +448,15 @@
"\n",
"### Layouts\n",
"\n",
"Earlier on we were casting a parabola to different element types. Viewing the different types was awkward, wasting lots of vertical space in the notebook. What we really want is to view these elements side-by-side:"
"Earlier on we were casting a parabola to different element types. Viewing the different types was awkward, wasting lots of vertical space in the notebook. What we will often want to do is view these elements side by side:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"layout = trajectory + hv.Scatter(trajectory) + hv.Area(trajectory) + hv.Spikes(trajectory)\n",
Expand All @@ -439,7 +473,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"print(layout)"
Expand All @@ -455,7 +491,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"layout.Curve.I + layout.Spikes.I"
Expand All @@ -473,7 +511,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"cannonball = trajectory.relabel('Cannonball', group='Trajectory')\n",
Expand Down Expand Up @@ -515,13 +555,15 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"You can put (almost!) any two HoloViews objects into a Layout but there is a second ``*`` operator which is useful when two elements lives in the same space (i.e matching dimensions with similar ranges) to create an [``Overlay``](http://build.holoviews.org/reference/containers/bokeh/Overlay.html):"
"You can put (almost!) any two HoloViews objects into a Layout, but there is a second ``*`` operator which is useful when two elements lives in the same space (i.e matching dimensions with similar ranges) to create an [``Overlay``](http://build.holoviews.org/reference/containers/bokeh/Overlay.html):"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"trajectory * hv.Spikes(trajectory)"
Expand All @@ -548,7 +590,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"labelled_layout.Trajectory.Filled * layout.Spikes.I"
Expand All @@ -558,13 +602,15 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"One thing that is specify to Overlays is the use of color cycles to automatically differentiate between elements of the same type and ``group``:"
"One thing that is specific to Overlays is the use of color cycles to automatically differentiate between elements of the same type and ``group``:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"tennis_ball = cannonball.clone((xs, 0.5*np.array(ys)), label='Tennis ball')\n",
Expand All @@ -575,7 +621,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Here we use the ``clone`` method to make a shallower tennis ball trajectory: the ``clone`` method create a new object that preserves semantic metadata while allowing overrides (in this case we override the input data and the ``label``).\n",
"Here we use the ``clone`` method to make a shallower tennis-ball trajectory: the ``clone`` method create a new object that preserves semantic metadata while allowing overrides (in this case we override the input data and the ``label``).\n",
"\n",
"Note that in the overlay, these two 'trajectory' curves are distinguished from each other with a color cycle where the legend uses the assigned labels."
]
Expand Down

0 comments on commit 3208d49

Please sign in to comment.