Visualization
#############
ProteoTwirl allows for several different visualization of properties and
shapes calculated. Currently visualization is either cartographic projection
of properties or shapes, and interactive 3D visualizations of properties.
Example System
==============
All examples below will be created using the example system presented
below. In this setup we will load in ``8JWX`` with added hydrogens, as
done in :ref:`loading_protein`. Following this we will calculate a
property and a shape to plot.
.. code-block:: python
:caption: Loading a Protein from PDB
import ProteoTwirl.ProteinRotation as pt
protein_structure = pt.ProteinRotation(
protein = "8JXW_H.pdb", # Protein structure to load
selection = "protein and segid E (not element H)", # Select receptor only (in this case segment E)
base_rotation = "principle", # Align to principle axis
center_selection = True, # Set center of geometry to (0,0,0) (center of rotation)
atom_radii = "vdw", # Definiton of radii to use, here van der Waals
# Default values avoids having to re-inter them for each added property
default_rotation_res = 128, # Number of angles to sample
default_rotation_range = None, # Can be set of min and max value for theta and phi angle
default_selectors = None, # Allow us to select atoms based on orientation
default_sampler = None, # how we sample angles, currently only fibonacchi sphere
)
# Show interactive view of the protein loaded
protein_structure
.. raw:: html
Once the protein is loaded we will calculate a property, in this case we
will calculate the average hydropathy index of the exposed residues of
the protein.
Hydropathy is a count property and we can add it using the
name ``"count.Hydropathy"``, we also need to set the aggregation method to
``"mean"``, as otherwise no aggregation is performed, and we need one value
per angle to plot.
.. code-block:: python
:caption: Calculating Hydropathy of Exposed Residues
from ProteoTwirl.Selectors.Selector import ExposedAtomSelector
protein_structure.add_property(
property = "count.Hydropathy",
agg_method = "mean",
use_default = False,
rotation_res = 64,
selectors = [ExposedAtomSelector()],
workers = 1
)
>>> Adding mean_Hydropathy
print(len(protein_structure.mean_Hydropathy))
>>> 64
Lastly we will construct a shape of the protein, here we will use an
alpha-shape, and apply a small amount of hole-fill to clean up the shape.
.. code-block:: python
:caption: Calculating Alphashape of Protein
protein_structure.add_property(
property = "Alphashape",
hole_fill = 0.2,
use_default = False,
rotation_res = 64,
workers = 1
)
>>> Adding Alpha_Shape_45_HF20
print(len(protein_structure.Alpha_Shape_45_HF20))
>>> 32
protein_structure.Alpha_Shape_45_HF20[0]
.. figure:: ../../_static/Guides/Visualization/8JWX_H_AlphaShape_HF20.svg
:figclass: code-output
:width: 25%
:align: center
Cartographic Plots
==================
Introduction
------------
ProteoTwirl calculates angle-dependent properties, this mean we have a set
of angles, indicating where a protein is 'viewed' from, and an associated
value with that angle.
To plot this, we would have to map all angle, which can be seen as points
on a sphere, to a flat plane, something which is impossible to due exactly
(see `Gauss's Theorema Egregium `_ ).
However many approximate methods with different trade-offs have been made
in cartography, to create maps of the earth. We can re-use these
`cartographic projection `_
to plot our angle-dependent properties. In practice ProteoTwirl uses
`CartoPy `_ for
creating projection, a list of projections available in
`CartoPy `_ can be
found `here `_
Quick Plots
-----------
The easiest way to create a cartographic plot for a given property is
to use the ``plot_property`` function. This function will create a contour
plot of the property requested, with interpolation between the datapoints.
The function further creates a scatter-plot on top with the original
datapoints, with point size indicating value.
.. code-block:: python
:caption: Property Plot
import cartopy.crs as ccrs
fig,ax = protein_structure.plot_property(
property ="mean_Hydropathy", # name of property
cmap ="RdPu", # matplotlib colormap
projection = ccrs.Stereographic(), # projection
scatter_size =(1,150), # size of scatter plot
interp_degre = 16 # how many point to interpolate
scatter_col = "#222222", # scatter-plot point color
levels = 16 # number of contour levels
)
.. figure:: ../../_static/Guides/Visualization/8JXW_Hydropathy.png
:figclass: code-output
:width: 80%
:align: center
A quick plot showing how a shape looks at different angles can also be
made using `plot_shape` as shown below.
.. code-block:: python
:caption: Shape Plot
fig, ax = protein_structure.plot_shape(
shape_property = "Alpha_Shape_45_HF20",
projection = ccrs.EckertIII(),
shape_color = "#ffaaaa",
shape_size = 50,
figsize = (13,8)
)
# Setting a background color, so plot is visible on dark and light mode
# of documentation
ax.patch.set_facecolor("#14181e")
.. figure:: ../../_static/Guides/Visualization/8JXW_Alpha_Shape.png
:figclass: code-output
:width: 80%
:align: center
Contour Plots
-------------
Cartography contour plots can be made using the ``plot_carto_contour``
function, where one must provide an array of :math:`\phi` and the
:math:`\theta` angle, together with an array of values.
Here we will get the angles, and values via the ``get_property`` function,
as shown below.
.. code-block:: python
:caption: Get Property & Angles
value,angles = protein_structure.get_property(
property_name = "mean_Hydropathy",
return_angle = True
)["mean_Hydropathy"]
# value is a list of item, in this case floating point values
print(len(value))
>>> 64
# angles is an numpy array of phi and theta pairs.
print(angles.shape)
>> (64,2)
These can then be plotted using the ``plot_carto_contour`` function, as
shown below.
.. code-block:: python
:caption: Plotting using plot_carto_contour
from ProteoTwirl.Visualization.CatographyPlot import plot_carto_contour
ax = plot_carto_contour(
angles = angles, # angle
values = value, # values/height
projection = ccrs.EckertI(), # projection to use
cmap = "plasma", # matplotlib colormap
figsize = (13,13), # figure size
# gridline can be true/false or a dict with gridline keyword-arguments
gridlines = {
"colors" : "#ffffff"
},
# contour_labels can be true/false or a dict with contour label keyword-arguments
contour_labels = {
"colors" : "#ffffff",
"fmt" : "{:.2f} ".format,
},
# contour_lines can be true/false or a dict with contour line keyword-arguments
contour_lines={
"colors" : "#dddddd",
"linewidths" : 0.6,
"linestyles" :"dashed"
}
)
.. figure:: ../../_static/Guides/Visualization/8JXW_Hydropathy_contour.png
:figclass: code-output
:width: 100%
:align: center
Its also possible to use categorical values when creation contour plots,
.. code-block:: python
:caption: Plotting using plot_carto_contour
from ProteoTwirl.Visualization.CatographyPlot import plot_carto_contour
value,angles = protein_structure.get_property(
property_name = "mean_Hydropathy",
return_angle = True
)["mean_Hydropathy"]
# Convert to categorical
categorical_values = ["High" if v > 0.7 else "Medium" if v > 0.6 else "Low" for v in value]
ax = plot_carto_contour(
angles = angles,
values = categorical_values,
projection = ccrs.EckertI(),
categorical = True,
figsize = (13,6),
category_colors= {"High" : "#FFaaaa", "Medium" : "#aaFFaa", "Low" : "#aaaaFF"}
)
.. figure:: ../../_static/Guides/Visualization/8JXW_Hydropathy_contour_categorical.png
:figclass: code-output
:width: 100%
:align: center
Scatter Plots
-------------
You can alternatively also do a scatter-plot of the different angles for
a given property using ``plot_carto_scatter``. ``plot_carto_scatter`` also
further allows scaling and coloring of the scattered point according to
a value, such as the ``"mean_Hydropathy"`` also used for contour plots.
.. code-block:: python
:caption: Plotting using plot_carto_scatter
from ProteoTwirl.Visualization.CatographyPlot import plot_carto_scatter
ax = plot_carto_scatter(
angles = angles, # angle
# If string, will color all by same color, but if list of values
# will color according to the colormap in cmap
color = value,
# If single value will use that for all, if array of value will scale
# all sizes to the range of sizes in smin_max_plot, according to their value
size = value,
projection = ccrs.EckertIII(), # projection to use
cmap = "viridis", # matplotlib colormap
figsize = (13,6), # figure size
smin_max_plot = (10,1500), # set min and max marker size
# gridline can be true/false or a dict with gridline keyword-arguments
gridlines = {
"colors" : "#ffffff",
"linewidth" : 1.5,
},
)
# Setting a background color, so plot is visible on dark and light mode
# of documentation
ax.patch.set_facecolor("#14181e")
.. figure:: ../../_static/Guides/Visualization/8JXW_Hydropathy_scatter.png
:figclass: code-output
:width: 100%
:align: center
Shape-Scatter Plots
-------------------
The cartography scatter plot also allows you to use custom shapes as the
marker-style on the plot. This for example allows us to make a plot showing
how the protein looks at different angles.
We can combine this with the hydropathy values from before since they are
present in the same angle-space (matching same set of angle). We confirm
this by using :
.. code-block:: python
:caption: Checking angle-space
hydropathy_angle_space = protein_structure.get_angle_space_of_property("mean_Hydropathy")
alpha_shape_angle_sapce = protein_structure.get_angle_space_of_property("Alpha_Shape_45_HF20")
print(hydropathy_angle_space == alpha_shape_angle_sapce)
>>> True
We can then plot the shape of the protein, and color it according to the
mean hydropathy index of exposed residues from that angle.
.. code-block:: python
:caption: Checking angle-space
shape,angles = protein_structure.get_property(
property_name = "Alpha_Shape_45_HF20",
return_angle = True
)["Alpha_Shape_45_HF20"]
value,_ = protein_structure.get_property(
property_name = "mean_Hydropathy",
return_angle = True
)["mean_Hydropathy"]
ax = plot_carto_scatter(
angles = angles, # angle
color = value,
size = 50,
marker = shape,
projection = ccrs.EckertIII(), # projection to use
cmap = "viridis", # matplotlib colormap
figsize = (13,7), # figure size
# gridline can be true/false or a dict with gridline keyword-arguments
gridlines = {
"colors" : "#ffffff",
"linewidth" : 1.5,
},
)
.. figure:: ../../_static/Guides/Visualization/8JXW_Hydropathy_scatter_shape.png
:figclass: code-output
:width: 100%
:align: center
When plotting shapes, there are two additional options to keep in mind
for how the shapes are rendered. The first of these are ``shape_size``,
which determines how high of a resolution the shapes are rendered in,
below is shown an example of low resolution rendering (using ``shape_size = 8``
and ``size = 40``, but otherwise same as above)
.. figure:: ../../_static/Guides/Visualization/8JXW_Hydropathy_scatter_shape_low_res.png
:figclass: code-output
:width: 100%
:align: center
The other is ``scale_shape``, which if ``True`` will scale the shapes their relative
correct sizes, allowing better comparison of the size of protein at different
angles. On the otherhand this can make some angles hard to see if they are small,
and it precludes scaling the shapes according to some value.
Below is an example of a very elongated protein (``5BU5``) where ``scale_shape`` is
either turned on or off, highlighting that with it on it can be hard to see
some smaller shapes.
Top plot uses ``scale_shape = False``, and bottom uses ``scale_shape = True``
.. figure:: ../../_static/Guides/Visualization/5BU5_scale_False.png
:figclass: code-output
:width: 100%
:align: center
.. figure:: ../../_static/Guides/Visualization/5BU5_scale_True.png
:figclass: code-output
:width: 100%
:align: center
Note on Central Longitude
-------------------------
Note that if you change the ``central_longitude`` in a projection, then you need
to specify that as well in for the ``central_longitude`` in the function you are
using (``plot_property``, ``plot_shpae``, ``plot_carto_contour`` or ``plot_carto_scatter``)
Interactive 3D Plots
====================
ProteoTwirl also supports various interactive 3D plots. These visualizations
will show the protein in the center, with sphere around it indicating the
different angles the protein is viewed at.
.. _view3dplot :
Viewing & Saving 3D Plots
--------------------------
When viewing an interactive 3D visualization, there are two different ways to
do this. Either they can be viewed in a python notebook, or they can be opened
in an external webrowser.
In the plots following, this is controlled by ``show_notebook`` variable. This
can be set to one of three different values.
- ``show_notebook == None`` (default) : will try to check if the code is executed in a notebook, and automatically set `show_notebook` accordingly
- ``show_notebook == False`` will open the visualization in a new webbrowser tab
- ``show_notebook == True`` will show the visualization as a notebook widget
To save a 3D visualization, you can set ``html_path`` to a string indicating where
the visualization should be saved. 3D visualization are saved as HTML files.
Plotting Protein
----------------
The easiest way to visualize a protein, in a python notebook, is simply
ending a cell with a ``ProteinRotation`` object, which will show an
interactive 3D ball-representation of the protein.
.. code-block:: python
:caption: Notebook Protein
protein_structure
.. raw:: html
However more customizable visualization, and visualization outside the
notebook context, is available using the ``plot_protein`` function. Here
in addition to the view/save arguments discussed in :ref:`view3dplot`,
allow customization of atom radius and color. For example :
.. code-block:: python
:caption: Notebook Protein
from ProteoTwirl.Visualization.Plot3D import plot_protein
plot_protein(
protein_rotation = protein_structure,
background = "#14181e",
atom_color = "Residue",
atom_radius = 1.0
)
.. raw:: html
Atomic Color & Radius
---------------------
Atomic Radius
.............
The radius of atoms in the visualization can be determined in one of three ways,
1) ``atom_radius == None`` in which case the radii from ``protein_rotation`` is used
2) ``atom_radius is float`` in which case all radii is set to this radius
3) ``AtomRadiusFunction``, a function which takes an atom, and additional context as input and provides a radius
The ``AtomRadiusFunction`` type function, is defined like shown below
.. code-block:: python
:caption: Notebook Protein
def radii_function (
index,
angle,
val,
atom,
radius,
)-> float :
Here ``index`` will be the index of the angle in question, ``angle`` is a tuple
of the angle we are currently look at the protien at, if sliders are used, this
can allow radius to change depending on which angle is selected, ``val`` is related
to a potential value associated with that angle, ``atom`` is the MDAnalysis ``Atom``
to calculate radius from, and ``radius`` is the radius retrived from the
``protein_rotation`` object.
An example custom function is presented below, which sizes each atom based
on the index it has in the protein.
.. code-block:: python
:caption: Notebook Protein
def radii_function (
index,
angle,
val,
atom,
radius,
)-> float :
# atom index start at 12000 due to selection of one segment
radii = np.sqrt(atom.id - 12000)/(100)
return radii
plot_protein(
protein_rotation = protein_structure,
background = "#14181e",
atom_color = "Element",
atom_radius = radii_function,
)
.. raw:: html
Atom Color
..........
The color of each atom in the visualization can also be customized, here
there are three different options for color,
Firstly as a string indicating a constant color, such a hex-color code
Secondly as a string for either one of the predefined color schemes
- ``"Element"`` : color each atom by their element
- ``"Residue"`` : color each atom by the Residue
or the name of one of the residue count-properties, in which case
they will be colored according to the residue value.
.. code-block:: python
:caption: Notebook Protein
plot_protein(
protein_rotation = protein_structure,
background = "#14181e",
atom_color = "Hydropathy Index",
)
.. raw:: html
Or thirdly using a ``AtomColorFunction`` which can be defined as such
.. code-block:: python
:caption: Notebook Protein
def color_function (
index,
angle,
val,
atom,
)-> str|tuple[float,float,float] :
Here ``index`` will be the index of the angle in question, ``angle`` is a tuple
of the angle we are currently look at the protien at, if sliders are used, this
can allow color to change depending on which angle is selected, ``val`` is related
to a potential value associated with that angle, ``atom`` is the MDAnalysis ``Atom``
to calculate color for. The output should either by a string for a color
.. code-block:: python
:caption: Notebook Protein
def color_function (
index,
angle,
val,
atom,
) :
# Scale to roughly fit 0-255 scale for red
id_to_int = int(np.sqrt(atom.id - 12000)/(0.3))
color = format(id_to_int, "02x") + "0000"
return color
plot_protein(
protein_rotation = protein_structure,
background = "#14181e",
atom_color = color_function,
html_path = "3D_Custom_Color_Function.html"
)
.. raw:: html
Quick Property View
-------------------
.. warning::
Under Construction - Coming Soon
.. code-block:: python
:caption: Checking angle-space
protein_structure.plot_property_3d(
property = "mean_Hydropathy",
cmap = "RdPu",
size_range = (1,20),
background = "#14181e",
)
.. raw:: html
Plotting Selectors
------------------
.. warning::
Under Construction - Coming Soon
Advanced Plotting
-----------------
.. warning::
Under Construction - Coming Soon