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

add scatter3d docs and fix colorbar title #5418

Merged
merged 10 commits into from
Jan 15, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 61 additions & 7 deletions examples/reference/elements/plotly/Scatter3D.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"import holoviews as hv\n",
"from holoviews import dim, opts\n",
"\n",
Expand All @@ -30,7 +31,11 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"``Scatter3D`` represents three-dimensional coordinates which may be colormapped or scaled in size according to a value. They are therefore very similar to [``Points``](Points.ipynb) and [``Scatter``](Scatter.ipynb) types but have one additional coordinate dimension. Like other 3D elements the camera angle can be controlled using ``azimuth``, ``elevation`` and ``distance`` plot options:"
"``Scatter3D`` plots represents three-dimensional coordinates which are specified as key dimensions (`kdims`, default `kdims` are x, y, z). The points can be given a `marker`, `color` and `size`. Additionally a `colorbar` can be added.\n",
"\n",
"``Scatter3D`` plots are therefore very similar to [``Points``](Points.ipynb) and [``Scatter``](Scatter.ipynb) types, but have one additional coordinate dimension.\n",
"\n",
"Like other 3D elements the camera angle can be controlled using `azimuth`, `elevation` and `distance` plot options"
]
},
{
Expand All @@ -40,9 +45,27 @@
"outputs": [],
"source": [
"y,x = np.mgrid[-5:5, -5:5] * 0.1\n",
"heights = np.sin(x**2+y**2)\n",
"hv.Scatter3D((x.flat, y.flat, heights.flat)).opts(\n",
" cmap='fire', color='z', size=5)"
"z=np.sin(x**2+y**2)\n",
"\n",
"hv.Scatter3D((x.flat,y.flat,z.flat)).opts(cmap='fire', color='z', size=5, colorbar=True, height=600, width=600)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can also provide your data in a container and specify the `kdims` to use."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"data = pd.DataFrame(dict(lat=x.flat, lon=y.flat, height=z.flat))\n",
"\n",
"hv.Scatter3D(data, kdims=[\"lat\", \"lon\", \"height\"]).opts(cmap='blues', color='height', size=3, colorbar=True, height=600, width=600, colorbar_opts={'title': 'height (m)'}, marker=\"diamond\")"
]
},
{
Expand All @@ -59,7 +82,7 @@
"outputs": [],
"source": [
"(hv.Scatter3D(np.random.randn(100,4), vdims='Size') * hv.Scatter3D(np.random.randn(100,4)+2, vdims='Size')).opts(\n",
" opts.Scatter3D(size=(5+dim('Size'))*2, marker='diamond')\n",
" opts.Scatter3D(size=(5+dim('Size'))*2, marker='diamond', height=600, width=600)\n",
")"
]
},
Expand All @@ -69,14 +92,45 @@
"source": [
"For full documentation and the available style and plot options, use ``hv.help(hv.Scatter3D).``"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(hv.Scatter3D.__doc__)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hv.help(hv.Scatter3D)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python [conda env:holoviews_dev]",
"language": "python",
"name": "conda-env-holoviews_dev-py"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"pygments_lexer": "ipython3"
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
}
},
"nbformat": 4,
"nbformat_minor": 1
"nbformat_minor": 4
}
46 changes: 44 additions & 2 deletions holoviews/element/chart3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,50 @@ class Scatter3D(Element3D, Points):
"""
Scatter3D is a 3D element representing the position of a collection
of coordinates in a 3D space. The key dimensions represent the
position of each coordinate along the x-, y- and z-axis while the
value dimensions can optionally supply additional information.
position of each coordinate along the x-, y- and z-axis.

Scatter3D is not available for the default Bokeh backend.

Example - Matplotlib
--------------------

.. code-block::
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Previously I have put code blocks into >>> lines. But they are hard to copy and use directly. VS Code actually renders .. code-block:: nicely. So this is better to use.


import holoviews as hv
from bokeh.sampledata.iris import flowers

hv.extension("matplotlib")

hv.Scatter3D(
flowers, kdims=["sepal_length", "sepal_width", "petal_length"]
).opts(
color="petal_width",
alpha=0.7,
size=5,
cmap="fire",
marker='^'
)

Example - Plotly
----------------

.. code-block::

import holoviews as hv
from bokeh.sampledata.iris import flowers

hv.extension("plotly")

hv.Scatter3D(
flowers, kdims=["sepal_length", "sepal_width", "petal_length"]
).opts(
color="petal_width",
alpha=0.7,
size=5,
cmap="Portland",
colorbar=True,
marker="circle",
)
"""

kdims = param.List(default=[Dimension('x'),
Expand Down
18 changes: 13 additions & 5 deletions holoviews/plotting/plotly/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,11 +603,19 @@ def get_color_opts(self, eldim, element, ranges, style):
opts = {}
dim_name = dim_range_key(eldim)
if self.colorbar:
if isinstance(eldim, dim):
title = str(eldim) if eldim.ops else str(eldim)[1:-1]
else:
title = eldim.pprint_label
opts['colorbar'] = dict(title=title, **self.colorbar_opts)
opts['colorbar'] = dict(**self.colorbar_opts)
if not "title" in opts['colorbar']:
if isinstance(eldim, dim):
title = str(eldim)
if eldim.ops:
title = title
elif title.startswith("dim('") and title.endswith("')"):
title = title[5:-2]
else:
title = title[1:-1]
Copy link
Collaborator Author

@MarcSkovMadsen MarcSkovMadsen Sep 4, 2022

Choose a reason for hiding this comment

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

I don't know why title[1:-1] is a valid value from time to time. But it was there before my time.

Copy link
Member

@hoxbro hoxbro Sep 5, 2022

Choose a reason for hiding this comment

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

Probably because of something like this title = "'An awesome title'"

Copy link
Member

Choose a reason for hiding this comment

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

That's pretty strange!

else:
title = eldim.pprint_label
opts['colorbar']['title']=title
opts['showscale'] = True
else:
opts['showscale'] = False
Expand Down
23 changes: 23 additions & 0 deletions holoviews/tests/plotting/plotly/test_elementplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,26 @@ def test_overlay_plot_zlabel(self):
scatter = Path3D([]) * Scatter3D([(10, 1, 2), (100, 2, 3), (1000, 3, 5)]).opts(zlabel='Z-Axis')
state = self._get_plot_state(scatter)
self.assertEqual(state['layout']['scene']['zaxis']['title']['text'], 'Z-Axis')

class TestColorbarPlot(TestPlotlyPlot):
def test_base(self):
y,x = np.mgrid[-5:5, -5:5] * 0.1
z=np.sin(x**2+y**2)
scatter = Scatter3D((x.flat,y.flat,z.flat))
state = self._get_plot_state(scatter)
assert state['layout']['scene']['zaxis']['title']=="z"

def test_colorbar(self):
y,x = np.mgrid[-5:5, -5:5] * 0.1
z=np.sin(x**2+y**2)
scatter = Scatter3D((x.flat,y.flat,z.flat)).opts(colorbar=True)
state = self._get_plot_state(scatter)
assert state['layout']['scene']['zaxis']['title']=="z"

def test_colorbar_opts_title(self):
y,x = np.mgrid[-5:5, -5:5] * 0.1
z=np.sin(x**2+y**2)
scatter = Scatter3D((x.flat,y.flat,z.flat)).opts(colorbar=True, colorbar_opts={"title": "some-title"})
plot = plotly_renderer.get_plot(scatter)
state = self._get_plot_state(scatter)
assert state['layout']['scene']['zaxis']['title']=="some-title"
hoxbro marked this conversation as resolved.
Show resolved Hide resolved