Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Customizing Plots Docs with more description about .options #2923

Merged
merged 10 commits into from
Aug 13, 2018
97 changes: 87 additions & 10 deletions examples/user_guide/03-Customizing_Plots.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"outputs": [],
"source": [
"spike_train = pd.read_csv('../assets/spike_train.csv.gz')\n",
"curve = hv.Curve( spike_train, 'milliseconds', 'Hertz')\n",
"curve = hv.Curve(spike_train, 'milliseconds', 'Hertz')\n",
"spikes = hv.Spikes(spike_train, 'milliseconds', [])"
]
},
Expand All @@ -63,13 +63,87 @@
"metadata": {},
"outputs": [],
"source": [
"%%output size=150\n",
"%%opts Curve [height=100 width=600 xaxis=None tools=['hover']]\n",
"%%opts Curve [height=100 width=600 xaxis=None tools=['hover']]\n",
"%%opts Curve (color='red' line_width=1.5)\n",
"%%opts Spikes [height=100 width=600 yaxis=None] (color='grey' line_width=0.25)\n",
"curve = hv.Curve( spike_train, 'milliseconds', vdims='Hertz')\n",
"curve = hv.Curve( spike_train, 'milliseconds', vdims='Hertz')\n",
"spikes = hv.Spikes(spike_train, 'milliseconds', vdims=[])\n",
"(curve + spikes).cols(1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here it is, again, with the `.options` method, which is the simplest way of customizing because it will automatically deduce whether an option is a style, plot, or norm option. In addition, `.options` is portable across notebook environments and `.py` scripts, and also will be saved on export, unlike `%%opts`."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it mean to be "saved on export"? To a .py script? If so, maybe "is portable across notebook environments and .py scripts, including exporting the notebook to a .py file, unlike %%opts.".

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

I remember when I first started using HoloViews, this was one of the main issues I encountered, and I had little clue on how to use port the magic to .py script.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, here you need to move hv.renderer to a subsequent cell. The %%opts cell magic is only applied to the output of the cell, when it's displayed, not to everything in it.

Copy link
Collaborator Author

@ahuang11 ahuang11 Aug 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I understand that's only applied to the output of the cell (if I wanted to do the whole notebook I believe I can do %opts).

However, my point with this statement:
also will be saved on export, unlike %%opts`
is that when you do a renderer.save() on objects modified by cell magic, the options don't embed into the saved.html unlike using the .options()

obj = obj.options(width=1000)
hv.renderer('bokeh').save(obj, 'test2')
display(HTML('test2.html'))  # this will have width = 1000 embedded in the html

So I guess to clarify, I should reword to

and also will be embbed to the export file, unlike `%%opts`."

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I wasn't clear -- renderer.save() should keep all options that have been modified on the object; if it didn't, that would be a very strange bug. But the point here is that you're doing renderer.save() on an object that has not yet been modified by cell magic, which is why you are failing to see any effect. At that point, the cell magic application is still in the future! You have to do the renderer.save() after the cell has been displayed, because cell magics are applied only on the output of the cell, which hasn't yet occurred by the time you are saving it. So all of the wordings you are suggesting are incorrect; %%opts does change the object in a way that will export; you just have to make sure that (a) the object is in fact affected by the cell magic (by returning it as the output of the cell), and (b) that you don't save it until after it has been returned as the output of the cell. Does that make sense now?

Copy link
Collaborator Author

@ahuang11 ahuang11 Aug 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, thanks for clarifying! I see your point now and learned something new! I wonder if I'm the only one who encountered this before? If not maybe should have it as another FAQ?

"
Q: Why isn't my %%opts magic applied when exported?
A: You're probably doing renderer.save() on an object that has not yet been modified by cell magic, which is why you are failing to see any effect. At that point, the cell magic application is still in the future! You have to do the renderer.save() after the cell has been displayed, because cell magics are applied only on the output of the cell, which hasn't yet occurred by the time you are saving it. So all of the wordings you are suggesting are incorrect; %%opts does change the object in a way that will export; you just have to make sure that (a) the object is in fact affected by the cell magic (by returning it as the output of the cell), and (b) that you don't save it until after it has been returned as the output of the cell. Does that make sense now?
"

image

Copy link
Member

@jbednar jbednar Aug 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make it more general:

Q: Why isn't my %%opts cell magic being applied to my HoloViews object?
A: %%opts is convenient because it tab-completes, but it can be confusing because of the "magic" way that it works. Specifically, if you use it at the top of a Jupyter notebook cell, the indicated options will be applied to the return value of that cell, if it's a HoloViews object. So, if you want a given object to get customized, you need to make sure it is returned from the cell, or the options won't ever be applied, and you should only access it after it has been returned, or the options won't yet have been applied. For instance, if you use renderer.save() to export an object and only then return that object as the output of a cell, the exported object won't have the options applied, because they don't get applied until the object is returned (during IPython's "display hooks" processing). So to make sure that options get applied, (a) return the object from a cell, and then (b) access it (e.g. for exporting) after the object has been returned. To avoid confusion, you may prefer to use .options() directly on the object to ensure that the options have been applied before exporting.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, another FAQ:

Q: Why are my .options() settings not having any effect?
A: By default, .options() returns a copy of your object, rather than modifying your original object. In HoloViews, making a copy of the object is cheap, because only the metadata is copied, not the data, and returning a copy makes it simple to work with a variety of differently customized versions of any given object. You can pass clone=False to .options() if you wish to modify the object in place.

]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"curve_opts = dict(height=100, width=600, xaxis=None, tools=[\n",
" 'hover'], color='red', line_width=1.5)\n",
"spike_opts = dict(height=100, width=600, yaxis=None,\n",
" color='grey', line_width=0.25)\n",
"\n",
"curve = hv.Curve(spike_train, 'milliseconds', 'Hertz')\n",
"spikes = hv.Spikes(spike_train, 'milliseconds', [])\n",
"\n",
"(curve.options(**curve_opts) + spikes.options(**spike_opts)).cols(1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When using ``.options`` to apply options directly to an individual object we do not have to explicitly declare which object the options apply to, however often it is useful to set options on a composite object. In these cases the options can be declared as a dictionary of the type name and the options. The code below is therefore equivalent to the syntax we used above:"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philippjfr, does .options only accept a type name, or does it also accept a fully qualified type.group.label string?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type.group.label is valid.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, that's what I thought. The text needs to be updated here to reflect that.

]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"layout = (curve + spikes).options({'Curve': curve_opts, 'Spikes': spike_opts}).cols(1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There is, however, one advantage in using the `%%opts` magic when working in the notebook environment, and that is %%opts magic allows tab-completion of the options which is currently unavailable with the `.options` method.\n",
"f\n",
"Additionally, across the docs, you may encounter a legacy method `.opts`, which is now superseded by the `.options` method. \n",
"\n",
"`.opts` is essentially the Python script version of `%%opts`: in both, you'll have to manually categorize whether an option is a style, plot, or norm option.\n",
"\n",
"Ultimately, `.options` method is preferred because using `.opts` requires extreme verbosity in nested dictionaries, which can get overwhelming fast."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"curve = hv.Curve(spike_train, 'milliseconds', vdims='Hertz')\n",
"curve = curve.opts(\n",
" dict(plot={'Curve': dict(height=100, width=600,\n",
" xaxis=None, tools=['hover'])},\n",
" style={'Curve': dict(color='red', line_width=1.5)}\n",
" )\n",
")\n",
"spikes = hv.Spikes(spike_train, 'milliseconds', vdims=[])\n",
"(curve+spikes).cols(1)"
"spikes = spikes.opts(\n",
" dict(plot={'Spikes': dict(height=100, width=600, yaxis=None)},\n",
" style={'Spikes': dict(color='grey', line_width=0.25)})\n",
")\n",
"\n",
"(curve + spikes).cols(1)"
]
},
{
Expand Down Expand Up @@ -192,7 +266,7 @@
"\n",
"#### Dictionary format\n",
"\n",
"HoloViews avoids string parsing and special syntax (other than the basic operators described in [Composing Elements](./02-Composing_Elements.ipynb)) where possible. For this reason, all options are fundamentally reduced to a simple dictionary format. For example, here is the pure Python equivalent of the options shown above, using the ``opts`` method that will be described shortly:"
"HoloViews avoids string parsing and special syntax (other than the basic operators described in [Composing Elements](./02-Composing_Elements.ipynb)) where possible. For this reason, all options are fundamentally reduced to a simple dictionary format. For example, here is the pure Python equivalent of the options shown above, using the legacy ``opts`` method that will be described shortly:"
]
},
{
Expand Down Expand Up @@ -329,25 +403,28 @@
"\n",
"Here are the different ways of applying the options specifications and the accepted format:\n",
"\n",
"* *The ``%%opts`` cell magic*: IPython specific syntax applies to displayed object *[string format]*\n",
"* *The ``%%opts`` cell magic*: IPython specific syntax applies to displayed object *[string format]*\n",
"* *The ``%opts`` line magic*: IPython specific syntax applied globally *[string format]*\n",
"* *The ``.opts`` method*: Pure python method of HoloViews objects *[string format, dictionary format]*.\n",
"* *The ``.options`` method*: Higher level pure python method of HoloViews objects *[dictionary format]*.\n",
"* *The ``hv.opts`` utility*: Pure python equivalent to ``%opts`` and ``%%opts`` *[string format, dictionary format]*\n",
"\n",
"\n",
"In the notebook environment, the recommended approach is to use ``%opts`` for global settings at the top of the notebook, then ``%%opts`` to customize the output of specific cells and finally the ``.opts`` method as necessary. The ``hv.opts`` utility is mostly intended for use in Python scripts.\n",
"Across all environments, the recommended approach is to use ``.options``, and if you are unsure about the available options, you can apply `hv.help(obj)`, which will list all the available options for a given backend. If you are in a notebook environment, you may also utilize the tab completion ability from the ``%opts`` and ``%%opts`` magic.\n",
"\n",
"For the sake of completeness though: use ``%opts`` for global settings at the top of the notebook, then ``%%opts`` to customize the output of specific cells and finally the ``.opts`` method if you need backward compatibility of HoloViews. The ``hv.opts`` utility is mostly intended for use in Python scripts if you are used to the ``%opts`` syntax or copying from notebook.\n",
"\n",
"#### ``%%opts``\n",
"\n",
"As shown in the examples above, customizes a particular HoloViws output displayed in a code cell. This application is not global and persists for that object which means settings stay in place if the object is re-displayed. Only accepts the string specification format. All cell magics need to appear *above* any code in the cells they are used in and cannot be used if there is no code in the cell. Only accepts the string specification syntax.\n",
"As shown in the examples above, customizes a particular HoloViews output displayed in a code cell. This application is not global and persists for that object which means settings stay in place if the object is re-displayed. Only accepts the string specification format. All cell magics need to appear *above* any code in the cells they are used in and cannot be used if there is no code in the cell. Only accepts the string specification syntax.\n",
"\n",
"#### ``%opts``\n",
"\n",
"The ``%opts`` line magic is used to set global settings at the level of the notebook they are used in. If there are options you want to use throughout a notebook, they should be specified with ``%opts`` at the top of the notebook. Line magics can appear anywhere at the start of a line, whether in isolation or anywhere inside a cell containing code. Only accepts the string specification syntax.\n",
"\n",
"#### ``.opts`` method\n",
"\n",
"Used to specify options to a specific holoviews object. Useful to parameterize options programatically that can be hard to specify otherwise:"
"Used to specify options to a specific holoviews object. Legacy method."
]
},
{
Expand Down