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 Loading Protein. Following this we will calculate a property and a shape to plot.

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

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.

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.

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]
../../_images/8JWX_H_AlphaShape_HF20.svg

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.

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
    )
../../_images/8JXW_Hydropathy.png

A quick plot showing how a shape looks at different angles can also be made using plot_shape as shown below.

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")
../../_images/8JXW_Alpha_Shape.png

Contour Plots#

Cartography contour plots can be made using the plot_carto_contour function, where one must provide an array of \(\phi\) and the \(\theta\) angle, together with an array of values.

Here we will get the angles, and values via the get_property function, as shown below.

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.

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"
    }
)
../../_images/8JXW_Hydropathy_contour.png

Its also possible to use categorical values when creation contour plots,

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"}

)
../../_images/8JXW_Hydropathy_contour_categorical.png

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.

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")
../../_images/8JXW_Hydropathy_scatter.png

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 :

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.

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,
    },


)
../../_images/8JXW_Hydropathy_scatter_shape.png

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)

../../_images/8JXW_Hydropathy_scatter_shape_low_res.png

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

../../_images/5BU5_scale_False.png
../../_images/5BU5_scale_True.png

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.

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.

Notebook Protein#
protein_structure

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 Viewing & Saving 3D Plots, allow customization of atom radius and color. For example :

Notebook Protein#
from ProteoTwirl.Visualization.Plot3D import plot_protein

plot_protein(
    protein_rotation    = protein_structure,
    background          = "#14181e",
    atom_color          = "Residue",
    atom_radius         = 1.0
    )

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

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.

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,
    )

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.

Notebook Protein#
plot_protein(
    protein_rotation    = protein_structure,
    background          = "#14181e",
    atom_color          = "Hydropathy Index",
    )

Or thirdly using a AtomColorFunction which can be defined as such

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

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"
    )

Quick Property View#

Warning

Under Construction - Coming Soon

Checking angle-space#
protein_structure.plot_property_3d(
    property    = "mean_Hydropathy",
    cmap        = "RdPu",
    size_range  = (1,20),
    background  = "#14181e",
    )

Plotting Selectors#

Warning

Under Construction - Coming Soon

Advanced Plotting#

Warning

Under Construction - Coming Soon