Module pept.visualisation.plotly_grapher
The plotly_grapher module implements Plotly-based visualisation tools to aid PEPT data analysis and to create publication-ready figures.
The PlotlyGrapher class can create and automatically configure 3D subplots for PEPT data visualisation. The PEPT 3D axes convention has the y-axis pointing upwards, such that the vertical screens of a PEPT scanner represent the xy-plane. The class provides functionality for plotting 3D scatter or line plots, with optional colorbars.
If you use the pept
package, you should cite
the following paper: [TODO: paper signature].
Source code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# pept is a Python library that unifies Positron Emission Particle
# Tracking (PEPT) research, including tracking, simulation, data analysis
# and visualisation tools
#
# Copyright (C) 2019 Andrei Leonard Nicusan
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# File : plotly_grapher.py
# License: GNU v3.0
# Author : Andrei Leonard Nicusan <a.l.nicusan@bham.ac.uk>
# Date : 23.08.2019
'''The *plotly_grapher* module implements Plotly-based visualisation tools
to aid PEPT data analysis and to create publication-ready figures.
The *PlotlyGrapher* class can create and automatically configure 3D subplots
for PEPT data visualisation. The PEPT 3D axes convention has the *y*-axis
pointing upwards, such that the vertical screens of a PEPT scanner represent
the *xy*-plane. The class provides functionality for plotting 3D scatter or
line plots, with optional colorbars.
If you use the `pept` package, you should cite
the following paper: [TODO: paper signature].
'''
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pept
class PlotlyGrapher:
'''A class for PEPT data visualisation using Plotly-based 3D plots.
The *PlotlyGrapher* class can create and automatically configure 3D subplots
for PEPT data visualisation. It can create and handle any number of subplots,
automatically configuring them to the ''alternative PEPT 3D axes convention''.
The PEPT 3D axes convention has the *y*-axis
pointing upwards, such that the vertical screens of a PEPT scanner represent
the *xy*-plane. The class provides functionality for plotting 3D scatter or
line plots, with optional colorbars.
The class provides easy access to the most common configuration parameters for
the plots, such as axes limits, subplot titles, colorbar titles, etc. It can
work with pre-computed Plotly traces (such as the ones from the `pept` base
classes), as well as with numpy arrays.
Parameters
----------
rows : int, optional
The number of rows of subplots. The default is 1.
cols : int, optional
The number of columns of subplots. The default is 1.
xlim : list or numpy.ndarray, optional
A list of length 2, formatted as `[x_min, x_max]`, where `x_min` is
the lower limit of the x-axis of all the subplots and `x_max` is the
upper limit of the x-axis of all the subplots. The default is
[0, 500], as for the Birmingham PEPT.
ylim : list or numpy.ndarray, optional
A list of length 2, formatted as `[y_min, y_max]`, where `y_min` is
the lower limit of the y-axis of all the subplots and `y_max` is the
upper limit of the y-axis of all the subplots. The default is
[0, 500], as for the Birmingham PEPT.
zlim : list or numpy.ndarray, optional
A list of length 2, formatted as `[z_min, z_max]`, where `z_min` is
the lower limit of the z-axis of all the subplots and `z_max` is the
upper limit of the z-axis of all the subplots. The default is
[0, 712], a usual screen separation for the Birmingham PEPT.
subplot_titles : list of str, optional
A list of the titles of the subplots. The default is a list of empty
strings.
Attributes
----------
xlim : list or numpy.ndarray
A list of length 2, formatted as `[x_min, x_max]`, where `x_min` is
the lower limit of the x-axis of all the subplots and `x_max` is the
upper limit of the x-axis of all the subplots.
ylim : list or numpy.ndarray
A list of length 2, formatted as `[y_min, y_max]`, where `y_min` is
the lower limit of the y-axis of all the subplots and `y_max` is the
upper limit of the y-axis of all the subplots.
zlim : list or numpy.ndarray
A list of length 2, formatted as `[z_min, z_max]`, where `z_min` is
the lower limit of the z-axis of all the subplots and `z_max` is the
upper limit of the z-axis of all the subplots.
fig : Plotly Figure instance
A Plotly Figure instance, with any number of subplots (as defined by
`rows` and `cols`) pre-configured for PEPT data. It is created when
calling `create_figure`.
Raises
------
ValueError
If `rows` < 1 or `cols` < 1
TypeError
If `xlim`, `ylim` or `zlim` are not lists of length 2.
Notes
-----
The class constructor (calling PlotlyGrapher()) is separate from the figure
creation, so that one can have instances of both the `PlotlyGrapher` and the
Plotly figure it creates. An example call would be:
>>> grapher = PlotlyGrapher()
>>> fig = grapher.create_figure()
'''
def __init__(self,
rows = 1, cols = 1,
xlim = [0, 500], ylim = [0, 500], zlim = [0, 712],
subplot_titles = [' ']):
if rows < 1 or cols < 1:
raise ValueError("\n[ERROR]: The number of rows and cols have to be larger than 1\n")
self._rows = rows
self._cols = cols
if len(xlim) != 2 or len(ylim) != 2 or len(zlim) != 2:
raise TypeError("\n[ERROR]: xlim, ylim and zlim need to be lists of length 2, formatted as xlim = [x_min, x_max] etc.\n")
self._xlim = xlim
self._ylim = ylim
self._zlim = zlim
self._subplot_titles = subplot_titles
self._subplot_titles.extend([' '] * (rows * cols - len(subplot_titles)))
self._fig = None
def create_figure(self):
'''Create a Plotly figure, pre-configured for PEPT data.
This function creates a Plotly figure with any number of subplots,
as given in the class instantiation call. It pre-configures them to have
the *y*-axis pointing upwards, as per the PEPT 3D axes convention. It
also sets the axes limits and labels.
Returns
-------
fig : Plotly Figure instance
A Plotly Figure instance, with any number of subplots (as defined when instantiating
the class) pre-configured for PEPT data.
'''
specs = [[{"type": "scatter3d"}] * self._cols] * self._rows
self._fig = make_subplots(rows = self._rows, cols = self._cols,
specs = specs, subplot_titles = self._subplot_titles,
horizontal_spacing = 0.005, vertical_spacing = 0.05)
self._fig['layout'].update(margin = dict(l=0,r=0,b=30,t=30), showlegend = False)
# For every subplot (scene), set axes' ratios and limits
# Also set the y axis to point upwards
# Plotly naming convention of scenes: 'scene', 'scene2', etc.
for i in range(self._rows):
for j in range(self._cols):
if i == j == 0:
scene = 'scene'
else:
scene = 'scene{}'.format(i * self._cols + j + 1)
# Justify subplot title on the left
self._fig.layout.annotations[i * self._cols + j].update(x = (j + 0.08) / self._cols)
self._fig['layout'][scene].update(aspectmode = 'manual',
aspectratio = {'x': 1, 'y': 1, 'z': 1},
camera = {'up': {'x': 0, 'y': 1, 'z':0},
'eye': {'x': 1, 'y': 1, 'z': 1}},
xaxis = {'range': self._xlim,
'title': {'text': "<i>x</i> (mm)"}},
yaxis = {'range': self._ylim,
'title': {'text': "<i>y</i> (mm)"}},
zaxis = {'range': self._zlim,
'title': {'text': "<i>z</i> (mm)"}}
)
return self._fig
@property
def xlim(self):
'''The upper and lower limits of the *x*-axis.
The property is defined as a list of length 2, formatted as
[x_min, x_max]
Returns
-------
xlim : list or numpy.ndarray
A list of length 2 representing the upper and lower limits.
'''
return self._xlim
@xlim.setter
def xlim(self, new_xlim):
'''Set the lower and upper boundaries of the *x*-axis
Parameters
----------
new_xlim : list or numpy.ndarray
A list of length 2 representing the upper and lower limits of
the *x*-axis, formatted as [x_min, x_max]
Raises
------
TypeError
If `xlim`, `ylim` or `zlim` are not lists of length 2.
AttributeError
If no figure was created (i.e. create_figure was not called)
'''
if len(new_xlim) != 2:
raise TypeError("\n[ERROR]: xlim, ylim and zlim need to be lists of length 2, formatted as xlim = [x_min, x_max] etc.\n")
if self._fig is None:
raise AttributeError("\n[ERROR]: No figure was created! First run create_figure()\n")
self._xlim = new_xlim
# For every subplot (scene), update axes' limits
# Plotly naming convention of scenes: 'scene', 'scene2', etc.
for i in range(self._rows):
for j in range(self._cols):
if i == j == 0:
scene = 'scene'
else:
scene = 'scene{}'.format(i * self._cols + j + 1)
self._fig['layout'][scene].update(xaxis = {'range': self._xlim})
@property
def ylim(self):
'''The upper and lower limits of the *y*-axis.
The property is defined as a list of length 2, formatted as
[y_min, y_max]
Returns
-------
ylim : list or numpy.ndarray
A list of length 2 representing the upper and lower limits.
'''
return self._ylim
@ylim.setter
def ylim(self, new_ylim):
'''Set the lower and upper boundaries of the *y*-axis
Parameters
----------
new_ylim : list or numpy.ndarray
A list of length 2 representing the upper and lower limits of
the *y*-axis, formatted as [y_min, y_max]
Raises
------
TypeError
If `xlim`, `ylim` or `zlim` are not lists of length 2.
AttributeError
If no figure was created (i.e. create_figure was not called)
'''
if len(new_ylim) != 2:
raise TypeError("\n[ERROR]: xlim, ylim and zlim need to be lists of length 2, formatted as xlim = [x_min, x_max] etc.\n")
if self._fig is None:
raise AttributeError("\n[ERROR]: No figure was created! First run create_figure()\n")
self._ylim = new_ylim
# For every subplot (scene), update axes' limits
# Plotly naming convention of scenes: 'scene', 'scene2', etc.
for i in range(self._rows):
for j in range(self._cols):
if i == j == 0:
scene = 'scene'
else:
scene = 'scene{}'.format(i * self._cols + j + 1)
self._fig['layout'][scene].update(yaxis = {'range': self._ylim})
@property
def zlim(self):
'''The upper and lower limits of the *z*-axis.
The property is defined as a list of length 2, formatted as
[z_min, z_max]
Returns
-------
zlim : list or numpy.ndarray
A list of length 2 representing the upper and lower limits.
'''
return self._zlim
@zlim.setter
def zlim(self, new_zlim):
'''Set the lower and upper boundaries of the *z*-axis
Parameters
----------
new_zlim : list or numpy.ndarray
A list of length 2 representing the upper and lower limits of
the *z*-axis, formatted as [z_min, z_max]
Raises
------
TypeError
If `xlim`, `ylim` or `zlim` are not lists of length 2.
AttributeError
If no figure was created (i.e. create_figure was not called)
'''
if len(new_zlim) != 2:
raise TypeError("\n[ERROR]: xlim, ylim and zlim need to be lists of length 2, formatted as xlim = [x_min, x_max] etc.\n")
if self._fig is None:
raise AttributeError("\n[ERROR]: No figure was created! First run create_figure()\n")
self._zlim = new_zlim
# For every subplot (scene), update axes' limits
# Plotly naming convention of scenes: 'scene', 'scene2', etc.
for i in range(self._rows):
for j in range(self._cols):
if i == j == 0:
scene = 'scene'
else:
scene = 'scene{}'.format(i * self._cols + j + 1)
self._fig['layout'][scene].update(zaxis = {'range': self._zlim})
@property
def fig(self):
'''Return the Plotly Figure instance
This is useful for manual configuration of the Plotly figure
stored in this class.
Returns
-------
fig : Plotly Figure instance
The Plotly Figure instance stored in the class, containing all
data and options set.
'''
return self._fig
def add_data_as_trace(self, data, row = 1, col = 1, size = 2, color = None):
'''Create a Plotly trace of `data` and add it to the figure.
Creates Scatter3d Plotly traces from the rows of `data` and adds it to
the subplot determined by `row` and `col`.
Parameters
----------
data : (N, M >= 4) numpy.ndarray
The expected data row: [time, x, y, z, etc.]
row : int, optional
The row of the subplot to add a trace to. The default is 1.
col : int, optional
The column of the subplot to add a trace to. The default is 1.
size : int, optional
The size of the trace markers. The default is 2.
color : str, optional
The color of the trace markers. The default is `None`.
Raises
------
TypeError
If `data` is not a numpy.ndarray with shape (N, M), where M >= 4
'''
if data.ndim != 2 or data.shape[1] < 4:
raise TypeError("\n[ERROR]: data should be a numpy.ndarray with shape (N, M), where M >= 4\n")
trace = go.Scatter3d(
x = data[:, 1],
y = data[:, 2],
z = data[:, 3],
mode = 'markers',
marker = dict(
size = size,
color = color,
opacity = 0.8
)
)
self._fig.add_trace(trace, row = row, col = col)
def add_data_as_trace_colorbar(self, data,
row = 1, col = 1,
title_colorbar = None,
size = 3, colorbar_col = -1):
'''Create a colour-coded Plotly trace of `data` and add it to the figure.
Creates Scatter3d Plotly traces from the rows of `data` and adds it to
the subplot determined by `row` and `col`. It colour-codes the data in
terms of a given column `colorbar_col` from the data.
Parameters
----------
data : (N, M >= 4) numpy.ndarray
The expected data row: [time, x, y, z, etc.]
row : int, optional
The row of the subplot to add a trace to. The default is 1.
col : int, optional
The column of the subplot to add a trace to. The default is 1.
size : int, optional
The size of the trace markers. The default is 2.
title_colorbar : str, optional
The title to display on the colorbar on the right of the plot.
The default is `None`.
colorbar_col : int, optional
The column in `data` based on which the markers will be colour-coded.
The default is -1, the last column.
Raises
------
TypeError
If `data` is not a numpy.ndarray with shape (N, M), where M >= 4
ValueError
If `colorbar_col` is larger than the number of columns in `data`
'''
if data.ndim != 2 or data.shape[1] < 4:
raise TypeError("\n[ERROR]: data should be a numpy.ndarray with shape (N, M), where M >= 4\n")
if colorbar_col > data.shape[1]:
raise ValueError("\n[ERROR]: colorbar_col was larger than the number of columns in data\n")
if title_colorbar != None:
colorbar = dict(title= title_colorbar)
else:
colorbar = dict()
trace = go.Scatter3d(
x = data[:, 1],
y = data[:, 2],
z = data[:, 3],
mode = 'markers',
marker = dict(
size = size,
color = data[:, colorbar_col], # set color to sample size
colorscale = 'Magma', # choose a colorscale
colorbar = colorbar,
opacity = 0.8
)
)
self._fig.add_trace(trace, row = row, col = col)
def add_data_as_trace_line(self, data, row = 1, col = 1, width = 4, color = None):
'''Create a Plotly trace of `data` and add it to the figure.
Creates Scatter3d Plotly traces as continuous lines from the rows of
`data` and adds it to the subplot determined by `row` and `col`.
Parameters
----------
data : (N, M >= 4) numpy.ndarray
The expected data row: [time, x, y, z, etc.]
row : int, optional
The row of the subplot to add a trace to. The default is 1.
col : int, optional
The column of the subplot to add a trace to. The default is 1.
width : int, optional
The width of the trace lines. The default is 4.
color : str, optional
The color of the trace markers. The default is `None`.
Raises
------
TypeError
If `data` is not a numpy.ndarray with shape (N, M), where M >= 4
'''
if data.ndim != 2 or data.shape[1] < 4:
raise TypeError("\n[ERROR]: data should be a numpy.ndarray with shape (N, M), where M >= 4\n")
trace = go.Scatter3d(
x = data[:, 1],
y = data[:, 2],
z = data[:, 3],
mode = 'lines',
line = dict(
width = width,
color = color
)
)
self._fig.add_trace(trace, row = row, col = col)
def add_data_as_trace_lines(self, data, row = 1, col = 1, width = 2, color = None):
'''Create Plotly traces for individual lines in `data` and add them to the figure.
Creates Scatter3d Plotly traces as continuous lines from the rows of
`data` and adds it to the subplot determined by `row` and `col`. It expects
LoR-like data, where every line is defined by two points.
Parameters
----------
data : (N, M >= 7) numpy.ndarray
The expected data row: [time, x1, y1, z1, x2, y2, z2, etc.]
row : int, optional
The row of the subplot to add a trace to. The default is 1.
col : int, optional
The column of the subplot to add a trace to. The default is 1.
width : int, optional
The width of the trace lines. The default is 4.
color : str, optional
The color of the trace markers. The default is `None`.
Raises
------
TypeError
If `data` is not a numpy.ndarray with shape (N, M), where M >= 7
'''
if data.ndim != 2 or data.shape[1] < 7:
raise TypeError("\n[ERROR]: data should be a numpy.ndarray with shape (N, M), where M >= 7\n")
# data is a list of lines, each defined by two points
# data row [time, x1, y1, z1, x2, y2, z2]
for line in data:
self._fig.add_trace(
go.Scatter3d(
x = [ line[1], line[4] ],
y = [ line[2], line[5] ],
z = [ line[3], line[6] ],
mode = 'lines',
opacity = 0.6,
line = dict(
width = width,
color = color
)
),
row = row,
col = col
)
def add_trace(self, trace, row = 1, col = 1):
'''Add a precomputed Plotly trace to a given subplot.
The equivalent of the Plotly figure.add_trace method.
Parameters
----------
trace : Plotly trace (Scatter3d)
A precomputed Plotly trace
row : int, optional
The row of the subplot to add a trace to. The default is 1.
col : int, optional
The column of the subplot to add a trace to. The default is 1.
'''
# Add precomputed trace
self._fig.add_trace(trace, row = row, col = col)
def add_traces(self, traces, row = 1, col = 1):
'''Add a list of precomputed Plotly traces to a given subplot.
The equivalent of the Plotly figure.add_traces method.
Parameters
----------
traces : list [ Plotly trace (Scatter3d) ]
A list of precomputed Plotly traces
row : int, optional
The row of the subplot to add a trace to. The default is 1.
col : int, optional
The column of the subplot to add a trace to. The default is 1.
'''
# Add precomputed traces
self._fig.add_traces(traces, rows=[row]*len(traces), cols=[col]*len(traces))
def show(self):
'''Show the Plotly figure.
'''
self._fig.show()
Classes
class PlotlyGrapher (rows=1, cols=1, xlim=[0, 500], ylim=[0, 500], zlim=[0, 712], subplot_titles=[' '])
-
A class for PEPT data visualisation using Plotly-based 3D plots.
The PlotlyGrapher class can create and automatically configure 3D subplots for PEPT data visualisation. It can create and handle any number of subplots, automatically configuring them to the ''alternative PEPT 3D axes convention''. The PEPT 3D axes convention has the y-axis pointing upwards, such that the vertical screens of a PEPT scanner represent the xy-plane. The class provides functionality for plotting 3D scatter or line plots, with optional colorbars.
The class provides easy access to the most common configuration parameters for the plots, such as axes limits, subplot titles, colorbar titles, etc. It can work with pre-computed Plotly traces (such as the ones from the
pept
base classes), as well as with numpy arrays.Parameters
rows
:int
, optional- The number of rows of subplots. The default is 1.
cols
:int
, optional- The number of columns of subplots. The default is 1.
xlim
:list
ornumpy.ndarray
, optional- A list of length 2, formatted as
[x_min, x_max]
, wherex_min
is the lower limit of the x-axis of all the subplots andx_max
is the upper limit of the x-axis of all the subplots. The default is [0, 500], as for the Birmingham PEPT. ylim
:list
ornumpy.ndarray
, optional- A list of length 2, formatted as
[y_min, y_max]
, wherey_min
is the lower limit of the y-axis of all the subplots andy_max
is the upper limit of the y-axis of all the subplots. The default is [0, 500], as for the Birmingham PEPT. zlim
:list
ornumpy.ndarray
, optional- A list of length 2, formatted as
[z_min, z_max]
, wherez_min
is the lower limit of the z-axis of all the subplots andz_max
is the upper limit of the z-axis of all the subplots. The default is [0, 712], a usual screen separation for the Birmingham PEPT. subplot_titles
:list
ofstr
, optional- A list of the titles of the subplots. The default is a list of empty strings.
Attributes
xlim
:list
ornumpy.ndarray
- A list of length 2, formatted as
[x_min, x_max]
, wherex_min
is the lower limit of the x-axis of all the subplots andx_max
is the upper limit of the x-axis of all the subplots. ylim
:list
ornumpy.ndarray
- A list of length 2, formatted as
[y_min, y_max]
, wherey_min
is the lower limit of the y-axis of all the subplots andy_max
is the upper limit of the y-axis of all the subplots. zlim
:list
ornumpy.ndarray
- A list of length 2, formatted as
[z_min, z_max]
, wherez_min
is the lower limit of the z-axis of all the subplots andz_max
is the upper limit of the z-axis of all the subplots. fig
:Plotly
Figure
instance
- A Plotly Figure instance, with any number of subplots (as defined by
rows
andcols
) pre-configured for PEPT data. It is created when callingcreate_figure
.
Raises
ValueError
- If
rows
< 1 orcols
< 1 TypeError
- If
xlim
,ylim
orzlim
are not lists of length 2.
Notes
The class constructor (calling PlotlyGrapher()) is separate from the figure creation, so that one can have instances of both the
PlotlyGrapher
and the Plotly figure it creates. An example call would be:>>> grapher = PlotlyGrapher() >>> fig = grapher.create_figure()
Source code
class PlotlyGrapher: '''A class for PEPT data visualisation using Plotly-based 3D plots. The *PlotlyGrapher* class can create and automatically configure 3D subplots for PEPT data visualisation. It can create and handle any number of subplots, automatically configuring them to the ''alternative PEPT 3D axes convention''. The PEPT 3D axes convention has the *y*-axis pointing upwards, such that the vertical screens of a PEPT scanner represent the *xy*-plane. The class provides functionality for plotting 3D scatter or line plots, with optional colorbars. The class provides easy access to the most common configuration parameters for the plots, such as axes limits, subplot titles, colorbar titles, etc. It can work with pre-computed Plotly traces (such as the ones from the `pept` base classes), as well as with numpy arrays. Parameters ---------- rows : int, optional The number of rows of subplots. The default is 1. cols : int, optional The number of columns of subplots. The default is 1. xlim : list or numpy.ndarray, optional A list of length 2, formatted as `[x_min, x_max]`, where `x_min` is the lower limit of the x-axis of all the subplots and `x_max` is the upper limit of the x-axis of all the subplots. The default is [0, 500], as for the Birmingham PEPT. ylim : list or numpy.ndarray, optional A list of length 2, formatted as `[y_min, y_max]`, where `y_min` is the lower limit of the y-axis of all the subplots and `y_max` is the upper limit of the y-axis of all the subplots. The default is [0, 500], as for the Birmingham PEPT. zlim : list or numpy.ndarray, optional A list of length 2, formatted as `[z_min, z_max]`, where `z_min` is the lower limit of the z-axis of all the subplots and `z_max` is the upper limit of the z-axis of all the subplots. The default is [0, 712], a usual screen separation for the Birmingham PEPT. subplot_titles : list of str, optional A list of the titles of the subplots. The default is a list of empty strings. Attributes ---------- xlim : list or numpy.ndarray A list of length 2, formatted as `[x_min, x_max]`, where `x_min` is the lower limit of the x-axis of all the subplots and `x_max` is the upper limit of the x-axis of all the subplots. ylim : list or numpy.ndarray A list of length 2, formatted as `[y_min, y_max]`, where `y_min` is the lower limit of the y-axis of all the subplots and `y_max` is the upper limit of the y-axis of all the subplots. zlim : list or numpy.ndarray A list of length 2, formatted as `[z_min, z_max]`, where `z_min` is the lower limit of the z-axis of all the subplots and `z_max` is the upper limit of the z-axis of all the subplots. fig : Plotly Figure instance A Plotly Figure instance, with any number of subplots (as defined by `rows` and `cols`) pre-configured for PEPT data. It is created when calling `create_figure`. Raises ------ ValueError If `rows` < 1 or `cols` < 1 TypeError If `xlim`, `ylim` or `zlim` are not lists of length 2. Notes ----- The class constructor (calling PlotlyGrapher()) is separate from the figure creation, so that one can have instances of both the `PlotlyGrapher` and the Plotly figure it creates. An example call would be: >>> grapher = PlotlyGrapher() >>> fig = grapher.create_figure() ''' def __init__(self, rows = 1, cols = 1, xlim = [0, 500], ylim = [0, 500], zlim = [0, 712], subplot_titles = [' ']): if rows < 1 or cols < 1: raise ValueError("\n[ERROR]: The number of rows and cols have to be larger than 1\n") self._rows = rows self._cols = cols if len(xlim) != 2 or len(ylim) != 2 or len(zlim) != 2: raise TypeError("\n[ERROR]: xlim, ylim and zlim need to be lists of length 2, formatted as xlim = [x_min, x_max] etc.\n") self._xlim = xlim self._ylim = ylim self._zlim = zlim self._subplot_titles = subplot_titles self._subplot_titles.extend([' '] * (rows * cols - len(subplot_titles))) self._fig = None def create_figure(self): '''Create a Plotly figure, pre-configured for PEPT data. This function creates a Plotly figure with any number of subplots, as given in the class instantiation call. It pre-configures them to have the *y*-axis pointing upwards, as per the PEPT 3D axes convention. It also sets the axes limits and labels. Returns ------- fig : Plotly Figure instance A Plotly Figure instance, with any number of subplots (as defined when instantiating the class) pre-configured for PEPT data. ''' specs = [[{"type": "scatter3d"}] * self._cols] * self._rows self._fig = make_subplots(rows = self._rows, cols = self._cols, specs = specs, subplot_titles = self._subplot_titles, horizontal_spacing = 0.005, vertical_spacing = 0.05) self._fig['layout'].update(margin = dict(l=0,r=0,b=30,t=30), showlegend = False) # For every subplot (scene), set axes' ratios and limits # Also set the y axis to point upwards # Plotly naming convention of scenes: 'scene', 'scene2', etc. for i in range(self._rows): for j in range(self._cols): if i == j == 0: scene = 'scene' else: scene = 'scene{}'.format(i * self._cols + j + 1) # Justify subplot title on the left self._fig.layout.annotations[i * self._cols + j].update(x = (j + 0.08) / self._cols) self._fig['layout'][scene].update(aspectmode = 'manual', aspectratio = {'x': 1, 'y': 1, 'z': 1}, camera = {'up': {'x': 0, 'y': 1, 'z':0}, 'eye': {'x': 1, 'y': 1, 'z': 1}}, xaxis = {'range': self._xlim, 'title': {'text': "<i>x</i> (mm)"}}, yaxis = {'range': self._ylim, 'title': {'text': "<i>y</i> (mm)"}}, zaxis = {'range': self._zlim, 'title': {'text': "<i>z</i> (mm)"}} ) return self._fig @property def xlim(self): '''The upper and lower limits of the *x*-axis. The property is defined as a list of length 2, formatted as [x_min, x_max] Returns ------- xlim : list or numpy.ndarray A list of length 2 representing the upper and lower limits. ''' return self._xlim @xlim.setter def xlim(self, new_xlim): '''Set the lower and upper boundaries of the *x*-axis Parameters ---------- new_xlim : list or numpy.ndarray A list of length 2 representing the upper and lower limits of the *x*-axis, formatted as [x_min, x_max] Raises ------ TypeError If `xlim`, `ylim` or `zlim` are not lists of length 2. AttributeError If no figure was created (i.e. create_figure was not called) ''' if len(new_xlim) != 2: raise TypeError("\n[ERROR]: xlim, ylim and zlim need to be lists of length 2, formatted as xlim = [x_min, x_max] etc.\n") if self._fig is None: raise AttributeError("\n[ERROR]: No figure was created! First run create_figure()\n") self._xlim = new_xlim # For every subplot (scene), update axes' limits # Plotly naming convention of scenes: 'scene', 'scene2', etc. for i in range(self._rows): for j in range(self._cols): if i == j == 0: scene = 'scene' else: scene = 'scene{}'.format(i * self._cols + j + 1) self._fig['layout'][scene].update(xaxis = {'range': self._xlim}) @property def ylim(self): '''The upper and lower limits of the *y*-axis. The property is defined as a list of length 2, formatted as [y_min, y_max] Returns ------- ylim : list or numpy.ndarray A list of length 2 representing the upper and lower limits. ''' return self._ylim @ylim.setter def ylim(self, new_ylim): '''Set the lower and upper boundaries of the *y*-axis Parameters ---------- new_ylim : list or numpy.ndarray A list of length 2 representing the upper and lower limits of the *y*-axis, formatted as [y_min, y_max] Raises ------ TypeError If `xlim`, `ylim` or `zlim` are not lists of length 2. AttributeError If no figure was created (i.e. create_figure was not called) ''' if len(new_ylim) != 2: raise TypeError("\n[ERROR]: xlim, ylim and zlim need to be lists of length 2, formatted as xlim = [x_min, x_max] etc.\n") if self._fig is None: raise AttributeError("\n[ERROR]: No figure was created! First run create_figure()\n") self._ylim = new_ylim # For every subplot (scene), update axes' limits # Plotly naming convention of scenes: 'scene', 'scene2', etc. for i in range(self._rows): for j in range(self._cols): if i == j == 0: scene = 'scene' else: scene = 'scene{}'.format(i * self._cols + j + 1) self._fig['layout'][scene].update(yaxis = {'range': self._ylim}) @property def zlim(self): '''The upper and lower limits of the *z*-axis. The property is defined as a list of length 2, formatted as [z_min, z_max] Returns ------- zlim : list or numpy.ndarray A list of length 2 representing the upper and lower limits. ''' return self._zlim @zlim.setter def zlim(self, new_zlim): '''Set the lower and upper boundaries of the *z*-axis Parameters ---------- new_zlim : list or numpy.ndarray A list of length 2 representing the upper and lower limits of the *z*-axis, formatted as [z_min, z_max] Raises ------ TypeError If `xlim`, `ylim` or `zlim` are not lists of length 2. AttributeError If no figure was created (i.e. create_figure was not called) ''' if len(new_zlim) != 2: raise TypeError("\n[ERROR]: xlim, ylim and zlim need to be lists of length 2, formatted as xlim = [x_min, x_max] etc.\n") if self._fig is None: raise AttributeError("\n[ERROR]: No figure was created! First run create_figure()\n") self._zlim = new_zlim # For every subplot (scene), update axes' limits # Plotly naming convention of scenes: 'scene', 'scene2', etc. for i in range(self._rows): for j in range(self._cols): if i == j == 0: scene = 'scene' else: scene = 'scene{}'.format(i * self._cols + j + 1) self._fig['layout'][scene].update(zaxis = {'range': self._zlim}) @property def fig(self): '''Return the Plotly Figure instance This is useful for manual configuration of the Plotly figure stored in this class. Returns ------- fig : Plotly Figure instance The Plotly Figure instance stored in the class, containing all data and options set. ''' return self._fig def add_data_as_trace(self, data, row = 1, col = 1, size = 2, color = None): '''Create a Plotly trace of `data` and add it to the figure. Creates Scatter3d Plotly traces from the rows of `data` and adds it to the subplot determined by `row` and `col`. Parameters ---------- data : (N, M >= 4) numpy.ndarray The expected data row: [time, x, y, z, etc.] row : int, optional The row of the subplot to add a trace to. The default is 1. col : int, optional The column of the subplot to add a trace to. The default is 1. size : int, optional The size of the trace markers. The default is 2. color : str, optional The color of the trace markers. The default is `None`. Raises ------ TypeError If `data` is not a numpy.ndarray with shape (N, M), where M >= 4 ''' if data.ndim != 2 or data.shape[1] < 4: raise TypeError("\n[ERROR]: data should be a numpy.ndarray with shape (N, M), where M >= 4\n") trace = go.Scatter3d( x = data[:, 1], y = data[:, 2], z = data[:, 3], mode = 'markers', marker = dict( size = size, color = color, opacity = 0.8 ) ) self._fig.add_trace(trace, row = row, col = col) def add_data_as_trace_colorbar(self, data, row = 1, col = 1, title_colorbar = None, size = 3, colorbar_col = -1): '''Create a colour-coded Plotly trace of `data` and add it to the figure. Creates Scatter3d Plotly traces from the rows of `data` and adds it to the subplot determined by `row` and `col`. It colour-codes the data in terms of a given column `colorbar_col` from the data. Parameters ---------- data : (N, M >= 4) numpy.ndarray The expected data row: [time, x, y, z, etc.] row : int, optional The row of the subplot to add a trace to. The default is 1. col : int, optional The column of the subplot to add a trace to. The default is 1. size : int, optional The size of the trace markers. The default is 2. title_colorbar : str, optional The title to display on the colorbar on the right of the plot. The default is `None`. colorbar_col : int, optional The column in `data` based on which the markers will be colour-coded. The default is -1, the last column. Raises ------ TypeError If `data` is not a numpy.ndarray with shape (N, M), where M >= 4 ValueError If `colorbar_col` is larger than the number of columns in `data` ''' if data.ndim != 2 or data.shape[1] < 4: raise TypeError("\n[ERROR]: data should be a numpy.ndarray with shape (N, M), where M >= 4\n") if colorbar_col > data.shape[1]: raise ValueError("\n[ERROR]: colorbar_col was larger than the number of columns in data\n") if title_colorbar != None: colorbar = dict(title= title_colorbar) else: colorbar = dict() trace = go.Scatter3d( x = data[:, 1], y = data[:, 2], z = data[:, 3], mode = 'markers', marker = dict( size = size, color = data[:, colorbar_col], # set color to sample size colorscale = 'Magma', # choose a colorscale colorbar = colorbar, opacity = 0.8 ) ) self._fig.add_trace(trace, row = row, col = col) def add_data_as_trace_line(self, data, row = 1, col = 1, width = 4, color = None): '''Create a Plotly trace of `data` and add it to the figure. Creates Scatter3d Plotly traces as continuous lines from the rows of `data` and adds it to the subplot determined by `row` and `col`. Parameters ---------- data : (N, M >= 4) numpy.ndarray The expected data row: [time, x, y, z, etc.] row : int, optional The row of the subplot to add a trace to. The default is 1. col : int, optional The column of the subplot to add a trace to. The default is 1. width : int, optional The width of the trace lines. The default is 4. color : str, optional The color of the trace markers. The default is `None`. Raises ------ TypeError If `data` is not a numpy.ndarray with shape (N, M), where M >= 4 ''' if data.ndim != 2 or data.shape[1] < 4: raise TypeError("\n[ERROR]: data should be a numpy.ndarray with shape (N, M), where M >= 4\n") trace = go.Scatter3d( x = data[:, 1], y = data[:, 2], z = data[:, 3], mode = 'lines', line = dict( width = width, color = color ) ) self._fig.add_trace(trace, row = row, col = col) def add_data_as_trace_lines(self, data, row = 1, col = 1, width = 2, color = None): '''Create Plotly traces for individual lines in `data` and add them to the figure. Creates Scatter3d Plotly traces as continuous lines from the rows of `data` and adds it to the subplot determined by `row` and `col`. It expects LoR-like data, where every line is defined by two points. Parameters ---------- data : (N, M >= 7) numpy.ndarray The expected data row: [time, x1, y1, z1, x2, y2, z2, etc.] row : int, optional The row of the subplot to add a trace to. The default is 1. col : int, optional The column of the subplot to add a trace to. The default is 1. width : int, optional The width of the trace lines. The default is 4. color : str, optional The color of the trace markers. The default is `None`. Raises ------ TypeError If `data` is not a numpy.ndarray with shape (N, M), where M >= 7 ''' if data.ndim != 2 or data.shape[1] < 7: raise TypeError("\n[ERROR]: data should be a numpy.ndarray with shape (N, M), where M >= 7\n") # data is a list of lines, each defined by two points # data row [time, x1, y1, z1, x2, y2, z2] for line in data: self._fig.add_trace( go.Scatter3d( x = [ line[1], line[4] ], y = [ line[2], line[5] ], z = [ line[3], line[6] ], mode = 'lines', opacity = 0.6, line = dict( width = width, color = color ) ), row = row, col = col ) def add_trace(self, trace, row = 1, col = 1): '''Add a precomputed Plotly trace to a given subplot. The equivalent of the Plotly figure.add_trace method. Parameters ---------- trace : Plotly trace (Scatter3d) A precomputed Plotly trace row : int, optional The row of the subplot to add a trace to. The default is 1. col : int, optional The column of the subplot to add a trace to. The default is 1. ''' # Add precomputed trace self._fig.add_trace(trace, row = row, col = col) def add_traces(self, traces, row = 1, col = 1): '''Add a list of precomputed Plotly traces to a given subplot. The equivalent of the Plotly figure.add_traces method. Parameters ---------- traces : list [ Plotly trace (Scatter3d) ] A list of precomputed Plotly traces row : int, optional The row of the subplot to add a trace to. The default is 1. col : int, optional The column of the subplot to add a trace to. The default is 1. ''' # Add precomputed traces self._fig.add_traces(traces, rows=[row]*len(traces), cols=[col]*len(traces)) def show(self): '''Show the Plotly figure. ''' self._fig.show()
Instance variables
var fig
-
Return the Plotly Figure instance
This is useful for manual configuration of the Plotly figure stored in this class.
Returns
fig
:Plotly
Figure
instance
- The Plotly Figure instance stored in the class, containing all data and options set.
Source code
@property def fig(self): '''Return the Plotly Figure instance This is useful for manual configuration of the Plotly figure stored in this class. Returns ------- fig : Plotly Figure instance The Plotly Figure instance stored in the class, containing all data and options set. ''' return self._fig
var xlim
-
The upper and lower limits of the x-axis.
The property is defined as a list of length 2, formatted as [x_min, x_max]
Returns
xlim
:list
ornumpy.ndarray
- A list of length 2 representing the upper and lower limits.
Source code
@property def xlim(self): '''The upper and lower limits of the *x*-axis. The property is defined as a list of length 2, formatted as [x_min, x_max] Returns ------- xlim : list or numpy.ndarray A list of length 2 representing the upper and lower limits. ''' return self._xlim
var ylim
-
The upper and lower limits of the y-axis.
The property is defined as a list of length 2, formatted as [y_min, y_max]
Returns
ylim
:list
ornumpy.ndarray
- A list of length 2 representing the upper and lower limits.
Source code
@property def ylim(self): '''The upper and lower limits of the *y*-axis. The property is defined as a list of length 2, formatted as [y_min, y_max] Returns ------- ylim : list or numpy.ndarray A list of length 2 representing the upper and lower limits. ''' return self._ylim
var zlim
-
The upper and lower limits of the z-axis.
The property is defined as a list of length 2, formatted as [z_min, z_max]
Returns
zlim
:list
ornumpy.ndarray
- A list of length 2 representing the upper and lower limits.
Source code
@property def zlim(self): '''The upper and lower limits of the *z*-axis. The property is defined as a list of length 2, formatted as [z_min, z_max] Returns ------- zlim : list or numpy.ndarray A list of length 2 representing the upper and lower limits. ''' return self._zlim
Methods
def add_data_as_trace(self, data, row=1, col=1, size=2, color=None)
-
Create a Plotly trace of
data
and add it to the figure.Creates Scatter3d Plotly traces from the rows of
data
and adds it to the subplot determined byrow
andcol
.Parameters
data
: (N
,M
>=4
)numpy.ndarray
- The expected data row: [time, x, y, z, etc.]
row
:int
, optional- The row of the subplot to add a trace to. The default is 1.
col
:int
, optional- The column of the subplot to add a trace to. The default is 1.
size
:int
, optional- The size of the trace markers. The default is 2.
color
:str
, optional- The color of the trace markers. The default is
None
.
Raises
TypeError
- If
data
is not a numpy.ndarray with shape (N, M), where M >= 4
Source code
def add_data_as_trace(self, data, row = 1, col = 1, size = 2, color = None): '''Create a Plotly trace of `data` and add it to the figure. Creates Scatter3d Plotly traces from the rows of `data` and adds it to the subplot determined by `row` and `col`. Parameters ---------- data : (N, M >= 4) numpy.ndarray The expected data row: [time, x, y, z, etc.] row : int, optional The row of the subplot to add a trace to. The default is 1. col : int, optional The column of the subplot to add a trace to. The default is 1. size : int, optional The size of the trace markers. The default is 2. color : str, optional The color of the trace markers. The default is `None`. Raises ------ TypeError If `data` is not a numpy.ndarray with shape (N, M), where M >= 4 ''' if data.ndim != 2 or data.shape[1] < 4: raise TypeError("\n[ERROR]: data should be a numpy.ndarray with shape (N, M), where M >= 4\n") trace = go.Scatter3d( x = data[:, 1], y = data[:, 2], z = data[:, 3], mode = 'markers', marker = dict( size = size, color = color, opacity = 0.8 ) ) self._fig.add_trace(trace, row = row, col = col)
def add_data_as_trace_colorbar(self, data, row=1, col=1, title_colorbar=None, size=3, colorbar_col=-1)
-
Create a colour-coded Plotly trace of
data
and add it to the figure.Creates Scatter3d Plotly traces from the rows of
data
and adds it to the subplot determined byrow
andcol
. It colour-codes the data in terms of a given columncolorbar_col
from the data.Parameters
data
: (N
,M
>=4
)numpy.ndarray
- The expected data row: [time, x, y, z, etc.]
row
:int
, optional- The row of the subplot to add a trace to. The default is 1.
col
:int
, optional- The column of the subplot to add a trace to. The default is 1.
size
:int
, optional- The size of the trace markers. The default is 2.
title_colorbar
:str
, optional- The title to display on the colorbar on the right of the plot.
The default is
None
. colorbar_col
:int
, optional- The column in
data
based on which the markers will be colour-coded. The default is -1, the last column.
Raises
TypeError
- If
data
is not a numpy.ndarray with shape (N, M), where M >= 4 ValueError
- If
colorbar_col
is larger than the number of columns indata
Source code
def add_data_as_trace_colorbar(self, data, row = 1, col = 1, title_colorbar = None, size = 3, colorbar_col = -1): '''Create a colour-coded Plotly trace of `data` and add it to the figure. Creates Scatter3d Plotly traces from the rows of `data` and adds it to the subplot determined by `row` and `col`. It colour-codes the data in terms of a given column `colorbar_col` from the data. Parameters ---------- data : (N, M >= 4) numpy.ndarray The expected data row: [time, x, y, z, etc.] row : int, optional The row of the subplot to add a trace to. The default is 1. col : int, optional The column of the subplot to add a trace to. The default is 1. size : int, optional The size of the trace markers. The default is 2. title_colorbar : str, optional The title to display on the colorbar on the right of the plot. The default is `None`. colorbar_col : int, optional The column in `data` based on which the markers will be colour-coded. The default is -1, the last column. Raises ------ TypeError If `data` is not a numpy.ndarray with shape (N, M), where M >= 4 ValueError If `colorbar_col` is larger than the number of columns in `data` ''' if data.ndim != 2 or data.shape[1] < 4: raise TypeError("\n[ERROR]: data should be a numpy.ndarray with shape (N, M), where M >= 4\n") if colorbar_col > data.shape[1]: raise ValueError("\n[ERROR]: colorbar_col was larger than the number of columns in data\n") if title_colorbar != None: colorbar = dict(title= title_colorbar) else: colorbar = dict() trace = go.Scatter3d( x = data[:, 1], y = data[:, 2], z = data[:, 3], mode = 'markers', marker = dict( size = size, color = data[:, colorbar_col], # set color to sample size colorscale = 'Magma', # choose a colorscale colorbar = colorbar, opacity = 0.8 ) ) self._fig.add_trace(trace, row = row, col = col)
def add_data_as_trace_line(self, data, row=1, col=1, width=4, color=None)
-
Create a Plotly trace of
data
and add it to the figure.Creates Scatter3d Plotly traces as continuous lines from the rows of
data
and adds it to the subplot determined byrow
andcol
.Parameters
data
: (N
,M
>=4
)numpy.ndarray
- The expected data row: [time, x, y, z, etc.]
row
:int
, optional- The row of the subplot to add a trace to. The default is 1.
col
:int
, optional- The column of the subplot to add a trace to. The default is 1.
width
:int
, optional- The width of the trace lines. The default is 4.
color
:str
, optional- The color of the trace markers. The default is
None
.
Raises
TypeError
- If
data
is not a numpy.ndarray with shape (N, M), where M >= 4
Source code
def add_data_as_trace_line(self, data, row = 1, col = 1, width = 4, color = None): '''Create a Plotly trace of `data` and add it to the figure. Creates Scatter3d Plotly traces as continuous lines from the rows of `data` and adds it to the subplot determined by `row` and `col`. Parameters ---------- data : (N, M >= 4) numpy.ndarray The expected data row: [time, x, y, z, etc.] row : int, optional The row of the subplot to add a trace to. The default is 1. col : int, optional The column of the subplot to add a trace to. The default is 1. width : int, optional The width of the trace lines. The default is 4. color : str, optional The color of the trace markers. The default is `None`. Raises ------ TypeError If `data` is not a numpy.ndarray with shape (N, M), where M >= 4 ''' if data.ndim != 2 or data.shape[1] < 4: raise TypeError("\n[ERROR]: data should be a numpy.ndarray with shape (N, M), where M >= 4\n") trace = go.Scatter3d( x = data[:, 1], y = data[:, 2], z = data[:, 3], mode = 'lines', line = dict( width = width, color = color ) ) self._fig.add_trace(trace, row = row, col = col)
def add_data_as_trace_lines(self, data, row=1, col=1, width=2, color=None)
-
Create Plotly traces for individual lines in
data
and add them to the figure.Creates Scatter3d Plotly traces as continuous lines from the rows of
data
and adds it to the subplot determined byrow
andcol
. It expects LoR-like data, where every line is defined by two points.Parameters
data
: (N
,M
>=7
)numpy.ndarray
- The expected data row: [time, x1, y1, z1, x2, y2, z2, etc.]
row
:int
, optional- The row of the subplot to add a trace to. The default is 1.
col
:int
, optional- The column of the subplot to add a trace to. The default is 1.
width
:int
, optional- The width of the trace lines. The default is 4.
color
:str
, optional- The color of the trace markers. The default is
None
.
Raises
TypeError
- If
data
is not a numpy.ndarray with shape (N, M), where M >= 7
Source code
def add_data_as_trace_lines(self, data, row = 1, col = 1, width = 2, color = None): '''Create Plotly traces for individual lines in `data` and add them to the figure. Creates Scatter3d Plotly traces as continuous lines from the rows of `data` and adds it to the subplot determined by `row` and `col`. It expects LoR-like data, where every line is defined by two points. Parameters ---------- data : (N, M >= 7) numpy.ndarray The expected data row: [time, x1, y1, z1, x2, y2, z2, etc.] row : int, optional The row of the subplot to add a trace to. The default is 1. col : int, optional The column of the subplot to add a trace to. The default is 1. width : int, optional The width of the trace lines. The default is 4. color : str, optional The color of the trace markers. The default is `None`. Raises ------ TypeError If `data` is not a numpy.ndarray with shape (N, M), where M >= 7 ''' if data.ndim != 2 or data.shape[1] < 7: raise TypeError("\n[ERROR]: data should be a numpy.ndarray with shape (N, M), where M >= 7\n") # data is a list of lines, each defined by two points # data row [time, x1, y1, z1, x2, y2, z2] for line in data: self._fig.add_trace( go.Scatter3d( x = [ line[1], line[4] ], y = [ line[2], line[5] ], z = [ line[3], line[6] ], mode = 'lines', opacity = 0.6, line = dict( width = width, color = color ) ), row = row, col = col )
def add_trace(self, trace, row=1, col=1)
-
Add a precomputed Plotly trace to a given subplot.
The equivalent of the Plotly figure.add_trace method.
Parameters
trace
:Plotly
trace
(Scatter3d
)- A precomputed Plotly trace
row
:int
, optional- The row of the subplot to add a trace to. The default is 1.
col
:int
, optional- The column of the subplot to add a trace to. The default is 1.
Source code
def add_trace(self, trace, row = 1, col = 1): '''Add a precomputed Plotly trace to a given subplot. The equivalent of the Plotly figure.add_trace method. Parameters ---------- trace : Plotly trace (Scatter3d) A precomputed Plotly trace row : int, optional The row of the subplot to add a trace to. The default is 1. col : int, optional The column of the subplot to add a trace to. The default is 1. ''' # Add precomputed trace self._fig.add_trace(trace, row = row, col = col)
def add_traces(self, traces, row=1, col=1)
-
Add a list of precomputed Plotly traces to a given subplot.
The equivalent of the Plotly figure.add_traces method.
Parameters
traces
:list
[Plotly
trace
(Scatter3d
) ]- A list of precomputed Plotly traces
row
:int
, optional- The row of the subplot to add a trace to. The default is 1.
col
:int
, optional- The column of the subplot to add a trace to. The default is 1.
Source code
def add_traces(self, traces, row = 1, col = 1): '''Add a list of precomputed Plotly traces to a given subplot. The equivalent of the Plotly figure.add_traces method. Parameters ---------- traces : list [ Plotly trace (Scatter3d) ] A list of precomputed Plotly traces row : int, optional The row of the subplot to add a trace to. The default is 1. col : int, optional The column of the subplot to add a trace to. The default is 1. ''' # Add precomputed traces self._fig.add_traces(traces, rows=[row]*len(traces), cols=[col]*len(traces))
def create_figure(self)
-
Create a Plotly figure, pre-configured for PEPT data.
This function creates a Plotly figure with any number of subplots, as given in the class instantiation call. It pre-configures them to have the y-axis pointing upwards, as per the PEPT 3D axes convention. It also sets the axes limits and labels.
Returns
fig
:Plotly
Figure
instance
- A Plotly Figure instance, with any number of subplots (as defined when instantiating the class) pre-configured for PEPT data.
Source code
def create_figure(self): '''Create a Plotly figure, pre-configured for PEPT data. This function creates a Plotly figure with any number of subplots, as given in the class instantiation call. It pre-configures them to have the *y*-axis pointing upwards, as per the PEPT 3D axes convention. It also sets the axes limits and labels. Returns ------- fig : Plotly Figure instance A Plotly Figure instance, with any number of subplots (as defined when instantiating the class) pre-configured for PEPT data. ''' specs = [[{"type": "scatter3d"}] * self._cols] * self._rows self._fig = make_subplots(rows = self._rows, cols = self._cols, specs = specs, subplot_titles = self._subplot_titles, horizontal_spacing = 0.005, vertical_spacing = 0.05) self._fig['layout'].update(margin = dict(l=0,r=0,b=30,t=30), showlegend = False) # For every subplot (scene), set axes' ratios and limits # Also set the y axis to point upwards # Plotly naming convention of scenes: 'scene', 'scene2', etc. for i in range(self._rows): for j in range(self._cols): if i == j == 0: scene = 'scene' else: scene = 'scene{}'.format(i * self._cols + j + 1) # Justify subplot title on the left self._fig.layout.annotations[i * self._cols + j].update(x = (j + 0.08) / self._cols) self._fig['layout'][scene].update(aspectmode = 'manual', aspectratio = {'x': 1, 'y': 1, 'z': 1}, camera = {'up': {'x': 0, 'y': 1, 'z':0}, 'eye': {'x': 1, 'y': 1, 'z': 1}}, xaxis = {'range': self._xlim, 'title': {'text': "<i>x</i> (mm)"}}, yaxis = {'range': self._ylim, 'title': {'text': "<i>y</i> (mm)"}}, zaxis = {'range': self._zlim, 'title': {'text': "<i>z</i> (mm)"}} ) return self._fig
def show(self)
-
Show the Plotly figure.
Source code
def show(self): '''Show the Plotly figure. ''' self._fig.show()