Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Binary file modified examples/visualization/demo_screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
101 changes: 92 additions & 9 deletions examples/visualization/nglview_demo.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "4e25adfecfbc4102b206c79c6f171915",
"model_id": "318d91b0cf4f43c2b2216efa73a2d99f",
"version_major": 2,
"version_minor": 0
},
Expand All @@ -29,7 +29,8 @@
"source": [
"import procaliper as pc\n",
"import procaliper.view as pcv\n",
"from nglview.color import ColormakerRegistry"
"from nglview.color import ColormakerRegistry\n",
"import matplotlib as plt"
]
},
{
Expand Down Expand Up @@ -69,16 +70,16 @@
}
],
"source": [
"protein = pc.Protein.from_uniprot_id(\"A0A0B4J2F0\")\n",
"protein = pc.Protein.from_uniprot_id(\"P07900\")\n",
"protein.fetch_pdb(save_path=\"scratch.pdb\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Visualize SASA\n",
"First we compute the sasa data and assert that it was properly created. Then, we use the sasa values to create a color scheme and nglview widget using the `view` module of `procaliper`. We remove the default representation, register our created color scheme, and add the new representation with our color scheme."
"## Visualize pLDDT\n",
"First, we examine the pLDDT score for the AlphaFold structure we have fetched. We extract this using the `protein.get_confidence` method. These values fall between 0 and 100, and we rescale them to fall in the interval [0,1] for coloring. Since we have manually scaled these, we set `rescale=False`. Green and yellow regions indicate a lower confidence in local structure prediction. We can view the chosen color scale using `plt.colormaps.get_cmap(\"viridis_r\")`."
]
},
{
Expand All @@ -89,7 +90,82 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "b1fe72f9609f436d90e99a1f47932183",
"model_id": "d40e3b436a6f48a68bd9cd1fb17a235e",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"NGLWidget()"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"_ = protein.get_confidence() # extract pLDDT values from the PDB\n",
"assert protein.confidence_data is not None\n",
"\n",
"pLDDT_scheme = pcv.ngl_scheme(\n",
" [x / 100 for x in protein.confidence_data],\n",
" color_mapper=\"viridis_r\",\n",
" rescale=False,\n",
") # create a color scheme from the pLDDT values\n",
"\n",
"view = pcv.protein_to_nglview(protein) # generate an nglview widget\n",
"view._remove_representation() # remove the default representation\n",
"\n",
"cm.add_selection_scheme(\n",
" \"all_pLDDT_value\", pLDDT_scheme\n",
") # add our color scheme to nglview\n",
"view.add_representation(\n",
" \"ribbon\", color=\"all_pLDDT_value\"\n",
") # render the protein using our color scheme\n",
"\n",
"view"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAgAAAABACAYAAABsv8+/AAAAGHRFWHRUaXRsZQB2aXJpZGlzX3IgY29sb3JtYXA0MKMeAAAAHnRFWHREZXNjcmlwdGlvbgB2aXJpZGlzX3IgY29sb3JtYXB2q0WVAAAAMHRFWHRBdXRob3IATWF0cGxvdGxpYiB2My44LjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmefc/hPAAAAMnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHYzLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZ7HVZ2gAAAIqSURBVHic7dZRbqMwGIVRk7V0/9vqKrCrJhCEiUPSqE/3nIdE/jGGGbUz3zR/f7VSSplb/f0qtVyXpZbbem7der2+zO/r6+e2f17W27xbl2mZ377rul6+6zKfD/NLN788ni/rWk7m3X3bOZfu/fr963Ofv8dwfX+P6fn6cP50Mu/P6eb93/fJ9f55o+eM9rWT8+7X+/n1c7xvW5fdug3O6a8vP6YvzNcDun2ln6/r/f3bCzw+b7v+4vz+3P33NLxvf/80uD69u3/4Hmfzd99jMC+P56/e//m+9i/nTx8+Z1u3v51fn18/fc7yC3n+5+j+Qzi872j/aN/+vO15715vj+fr+51cH95f627dDvv31w/z2t/X769Pz22D59z+FQcAoggAAAgkAAAgkAAAgEACAAACCQAACCQAACCQAACAQAIAAAIJAAAIJAAAIJAAAIBAAgAAAgkAAAgkAAAgkAAAgEACAAACCQAACCQAACCQAACAQAIAAAIJAAAIJAAAIJAAAIBAAgAAAgkAAAgkAAAgkAAAgEACAAACCQAACCQAACCQAACAQAIAAAIJAAAIJAAAIJAAAIBAAgAAAgkAAAgkAAAgkAAAgEACAAACCQAACCQAACCQAACAQAIAAAIJAAAIJAAAIJAAAIBAAgAAAgkAAAgkAAAgkAAAgEACAAACCQAACCQAACCQAACAQAIAAAIJAAAIJAAAIJAAAIBAAgAAAgkAAAgkAAAg0A9PlIAn408tHAAAAABJRU5ErkJggg==",
"text/html": [
"<div style=\"vertical-align: middle;\"><strong>viridis_r</strong> </div><div class=\"cmap\"><img alt=\"viridis_r colormap\" title=\"viridis_r\" style=\"border: 1px solid #555;\" src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAABACAYAAABsv8+/AAAAGHRFWHRUaXRsZQB2aXJpZGlzX3IgY29sb3JtYXA0MKMeAAAAHnRFWHREZXNjcmlwdGlvbgB2aXJpZGlzX3IgY29sb3JtYXB2q0WVAAAAMHRFWHRBdXRob3IATWF0cGxvdGxpYiB2My44LjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmefc/hPAAAAMnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHYzLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZ7HVZ2gAAAIqSURBVHic7dZRbqMwGIVRk7V0/9vqKrCrJhCEiUPSqE/3nIdE/jGGGbUz3zR/f7VSSplb/f0qtVyXpZbbem7der2+zO/r6+e2f17W27xbl2mZ377rul6+6zKfD/NLN788ni/rWk7m3X3bOZfu/fr963Ofv8dwfX+P6fn6cP50Mu/P6eb93/fJ9f55o+eM9rWT8+7X+/n1c7xvW5fdug3O6a8vP6YvzNcDun2ln6/r/f3bCzw+b7v+4vz+3P33NLxvf/80uD69u3/4Hmfzd99jMC+P56/e//m+9i/nTx8+Z1u3v51fn18/fc7yC3n+5+j+Qzi872j/aN/+vO15715vj+fr+51cH95f627dDvv31w/z2t/X769Pz22D59z+FQcAoggAAAgkAAAgkAAAgEACAAACCQAACCQAACCQAACAQAIAAAIJAAAIJAAAIJAAAIBAAgAAAgkAAAgkAAAgkAAAgEACAAACCQAACCQAACCQAACAQAIAAAIJAAAIJAAAIJAAAIBAAgAAAgkAAAgkAAAgkAAAgEACAAACCQAACCQAACCQAACAQAIAAAIJAAAIJAAAIJAAAIBAAgAAAgkAAAgkAAAgkAAAgEACAAACCQAACCQAACCQAACAQAIAAAIJAAAIJAAAIJAAAIBAAgAAAgkAAAgkAAAgkAAAgEACAAACCQAACCQAACCQAACAQAIAAAIJAAAIJAAAIJAAAIBAAgAAAgkAAAgkAAAg0A9PlIAn408tHAAAAABJRU5ErkJggg==\"></div><div style=\"vertical-align: middle; max-width: 514px; display: flex; justify-content: space-between;\"><div style=\"float: left;\"><div title=\"#fde725ff\" style=\"display: inline-block; width: 1em; height: 1em; margin: 0; vertical-align: middle; border: 1px solid #555; background-color: #fde725ff;\"></div> under</div><div style=\"margin: 0 auto; display: inline-block;\">bad <div title=\"#00000000\" style=\"display: inline-block; width: 1em; height: 1em; margin: 0; vertical-align: middle; border: 1px solid #555; background-color: #00000000;\"></div></div><div style=\"float: right;\">over <div title=\"#440154ff\" style=\"display: inline-block; width: 1em; height: 1em; margin: 0; vertical-align: middle; border: 1px solid #555; background-color: #440154ff;\"></div></div>"
],
"text/plain": [
"<matplotlib.colors.ListedColormap at 0x204323a2660>"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"plt.colormaps.get_cmap(\"viridis_r\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Visualize SASA\n",
"We compute the sasa data and assert that it was properly created. Then, we use the sasa values to create a color scheme and nglview widget using the `view` module of `procaliper`. We remove the default representation, register our created color scheme, and add the new representation with our color scheme."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "fa8aef4ce31541a58d5cdf6c3c65151a",
"version_major": 2,
"version_minor": 0
},
Expand Down Expand Up @@ -132,13 +208,13 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "8188a2d779884abda606020394d14616",
"model_id": "57e8921c32824989bf0dc3249ce3bcbd",
"version_major": 2,
"version_minor": 0
},
Expand All @@ -165,11 +241,18 @@
"view.add_representation(\"surface\", color=\"all_charge_value\")\n",
"view"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"display_name": "alphameter-py3.12",
"language": "python",
"name": "python3"
},
Expand Down
2 changes: 1 addition & 1 deletion procaliper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

__author__ = """AlphaMeter"""
__email__ = "[email protected]"
__version__ = "0.1.0"
__version__ = "0.3.0"

import procaliper.network as network
import procaliper.protein_structure as protein_structure
Expand Down
8 changes: 7 additions & 1 deletion procaliper/_protein.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,13 @@ def get_biopython_residues(self) -> list[Residue]:
raise ValueError("PDB location not set; use `fetch_pdb` first")
p = PDBParser(QUIET=True)
structure = p.get_structure("", self.pdb_location_absolute)
reslist = [res for model in structure for chain in model for res in chain]
reslist = [
res
for model in structure
for chain in model
for res in chain
if res.get_id()[0] == " " # excludes heteroatoms and water
]
return reslist

def get_confidence(self) -> list[float]:
Expand Down
4 changes: 1 addition & 3 deletions procaliper/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,7 @@ def regulatory_distance_network(protein: Protein) -> nx.Graph:
all_regs = {**ptms, **binding, **active, **regions, **domains}

# residues, excluding heteroatoms and water
protein_residues = [
res for res in protein.get_biopython_residues() if res.get_id()[0] == " "
]
protein_residues = protein.get_biopython_residues()

all_regs_residues = {}
for k, v in all_regs.items():
Expand Down
8 changes: 7 additions & 1 deletion procaliper/protein_structure/distance.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,13 @@ def distance_matrix(
npt.NDArray[np.float64]: distance matrix with shape nxn where n is the
number of residues in the structure.
"""
residues = [res for model in structure for chain in model for res in chain]
residues = [
res
for model in structure
for chain in model
for res in chain
if res.get_id()[0] == " "
]
residues = list(enumerate(residues))
adj = np.ones((len(residues), len(residues))) * np.inf

Expand Down
49 changes: 35 additions & 14 deletions procaliper/view/nglview_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from typing import Callable

import matplotlib as plt

from .._protein import Protein

"""
Expand Down Expand Up @@ -49,39 +51,58 @@ def _default_float_to_hex_rb(x: float) -> str:
return f"#{int(1*255):02x}{int((1-x)*255):02x}{int((1-x)*255):02x}"


def _matplotlib_cmapper(cmap: str, two_sided: bool) -> Callable[[float], str]:
if two_sided:
return lambda x: plt.colors.to_hex(plt.colormaps.get_cmap(cmap)(x / 2 + 0.5))

return lambda x: plt.colors.to_hex(plt.colormaps.get_cmap("viridis_r")(x))


def ngl_scheme(
data: list[float],
float_to_hex: Callable[[float], str] | None = None,
color_mapper: Callable[[float], str] | None = None,
two_sided: bool = False,
rescale: bool = True,
) -> list[tuple[str, str]]:
"""Converts a list of values to an nglview color scheme.

Args:
data (list[float]): The list of values to convert.
float_to_hex (Callable[[float], str] | None, optional): Function that
converts a float to a hex color in the form `"#RRGGBB"`. If `None`,
a default function is used that interpolates between white and green
(one-sided) or red and blue (two-sided). Defaults to `None`.
color_mapper (Callable[[float], str] | str | None, optional): Function that
converts a float to a hex color in the form `"#RRGGBB"`. If a string, it
should be the name of a matplotlib colormap. If `None`, a default function
is used that interpolates between white and green (one-sided) or red and blue (two-sided).
Defaults to `None`.
two_sided (bool, optional): Whether to use a two-sided color scheme. If
`False`, we assume `data` only contains positive values. Defaults to
`False`.
rescale (bool, optional): Whether to rescale the values to be between
0 and 1 (one-sided) or -1 and 1 (two-sided). Defaults to `True`.
If `False`, the values are not rescaled, but are assumed to fall within
[0, 1] or [-1, 1] depending on `two_sided`.

Returns:
list[tuple[str, str]]: A list of color and residue number tuples that
are compatible with nglview.
"""
if float_to_hex is None:
if color_mapper is None:
if two_sided:
float_to_hex = _default_float_to_hex_rb
color_mapper = _default_float_to_hex_rb
else:
float_to_hex = _default_float_to_hex
color_mapper = _default_float_to_hex

if isinstance(color_mapper, str):
color_mapper = _matplotlib_cmapper(color_mapper, two_sided)

maxx = max(data)
scale = max(min(data), abs(maxx)) if two_sided else maxx
if rescale:
maxx = max(data)
scale = max(min(data), abs(maxx)) if two_sided else maxx

if scale == 0:
data_scaled = [0.0] * len(data)
if scale == 0:
data_scaled = [0.0] * len(data)
else:
data_scaled = [x / maxx for x in data]
else:
data_scaled = [x / maxx for x in data]
data_scaled = data

return [(float_to_hex(x), f"{i+1}") for i, x in enumerate(data_scaled)]
return [(color_mapper(x), f"{i+1}") for i, x in enumerate(data_scaled)]
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[tool]
[tool.poetry]
name = "procaliper"
version = "0.2.1"
version = "0.3.0"
homepage = "https://github.com/LifeWorks/procaliper"
description = "Skeleton project created by Python Project Wizard (ppw)."
authors = ["AlphaMeter <[email protected]>"]
Expand Down
Loading