mab_neut_ic50.ipynb¶
Make neut curve plots with altair for all six monoclonal antibodies used for mapping escape in Nipah RBP
- Written by Brendan Larsen
In [1]:
# this cell is tagged as parameters for `papermill` parameterization
altair_config = None
nipah_config = None
mab_neuts = None
mab_neuts_plot = None
mAb_neuts_table = None
In [2]:
# Parameters
altair_config = "data/custom_analyses_data/theme.py"
nipah_config = "nipah_config.yaml"
mab_neuts = "data/custom_analyses_data/experimental_data/NiV_neut_all_mabs.csv"
mab_neuts_plot = "results/images/mab_neuts_plot.html"
mAb_neuts_table = "results/tables/mAb_neuts_table.csv"
In [3]:
import warnings
import math
from IPython.display import display, HTML, SVG
import pandas as pd
import numpy as np
import neutcurve
import scipy.stats
import yaml
import altair as alt
import re
import os
print(f"Using `neutcurve` version {neutcurve.__version__}")
import sys
# allow more rows for Altair
_ = alt.data_transformers.disable_max_rows()
# setup working directory
if os.getcwd() == "/fh/fast/bloom_j/computational_notebooks/blarsen/2023/Nipah_Malaysia_RBP_DMS/":
pass
print("Already in correct directory")
else:
os.chdir("/fh/fast/bloom_j/computational_notebooks/blarsen/2023/Nipah_Malaysia_RBP_DMS/")
print("Setup in correct directory")
#import altair themes from /data/custom_analyses_data/theme.py and enable
sys.path.append('data/custom_analyses_data/')
import theme
alt.themes.register('main_theme', theme.main_theme)
alt.themes.enable('main_theme')
Using `neutcurve` version 0.5.7 Setup in correct directory
Out[3]:
ThemeRegistry.enable('main_theme')
For running interactively¶
In [4]:
if mab_neuts_plot is None:
#altair_config = "data/custom_analyses_data/theme.py"
nipah_config = "nipah_config.yaml"
mab_neuts = "data/custom_analyses_data/experimental_data/NiV_neut_all_mabs.csv"
Read in config files¶
In [5]:
with open(nipah_config) as f:
config = yaml.safe_load(f)
In [6]:
# load data
df = pd.read_csv(mab_neuts)
display(df)
serum | virus | replicate | concentration | fraction infectivity | Unnamed: 5 | Unnamed: 6 | Unnamed: 7 | Unnamed: 8 | Unnamed: 9 | Unnamed: 10 | Unnamed: 11 | Unnamed: 12 | Unnamed: 13 | Unnamed: 14 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | nAH1.3 | NiV | 1 | 2.50000 | 0.0000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1 | nAH1.3 | NiV | 1 | 0.62500 | 0.0000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2 | nAH1.3 | NiV | 1 | 0.15625 | 0.0000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
3 | nAH1.3 | NiV | 1 | 0.03906 | 0.2105 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
4 | nAH1.3 | NiV | 1 | 0.00977 | 0.7908 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
107 | EFNB3-dimeric | NiV | 2 | 0.15625 | 0.3620 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
108 | EFNB3-dimeric | NiV | 2 | 0.03906 | 0.8253 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
109 | EFNB3-dimeric | NiV | 2 | 0.00977 | 1.1068 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
110 | EFNB3-dimeric | NiV | 2 | 0.00244 | 1.1161 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
111 | EFNB3-dimeric | NiV | 2 | 0.00061 | 1.0817 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
112 rows × 15 columns
In [7]:
fits = neutcurve.curvefits.CurveFits(
data=df,
fixbottom=0,
)
fitParams = fits.fitParams(ics=[50, 90, 95, 97, 98, 99])
fitParams["ic50_ng"] = fitParams["ic50"] * 1000 # Convert from ug to ng
fitParams["ic99_ng"] = fitParams["ic99"] * 1000 # Convert from ug to ng
fit_df = fitParams[["serum", "ic50_ng"]].round(1)
fit_df = fit_df.loc[0:5]
fit_df.rename(columns={"serum": "Antibody", "ic50_ng": "ic50"}, inplace=True)
# Set 'Antibody' as the index
fit_df.set_index("Antibody", inplace=True)
# Define the new order you want; it must be a list of the index labels
new_order = ["m102.4", "HENV-26", "HENV-117", "HENV-103", "HENV-32", "nAH1.3"]
# Reindex the DataFrame with the new order
fit_df = fit_df.loc[new_order].reset_index()
fit_df["Epitope"] = [
"Receptor binding face",
"Receptor binding face",
"Receptor binding face",
"Dimerization face",
"Dimerization face",
"Distal face",
]
display(fit_df)
# Export IC50s for making a table
if mab_neuts_plot is not None:
fit_df.to_csv(mAb_neuts_table, index=False)
Antibody | ic50 | Epitope | |
---|---|---|---|
0 | m102.4 | 12.3 | Receptor binding face |
1 | HENV-26 | 13.7 | Receptor binding face |
2 | HENV-117 | 11.7 | Receptor binding face |
3 | HENV-103 | 66.7 | Dimerization face |
4 | HENV-32 | 143.0 | Dimerization face |
5 | nAH1.3 | 23.1 | Distal face |
In [8]:
def extract_dataframe_from_neutcurve(serum, viruses, replicate="average"):
curves = []
# Loop over each virus type and retrieve the curve
for sera in serum:
curve = fits.getCurve(serum=sera, virus=viruses, replicate=replicate)
df = curve.dataframe()
df["antibody"] = sera
curves.append(df)
# Concatenate all the dataframes into one
curve = pd.concat(curves, axis=0)
curve["upper"] = curve["measurement"] + curve["stderr"]
curve["lower"] = curve["measurement"] - curve["stderr"]
#make neutralization fits
curve['neut_fit'] = (1 - curve['fit']) * 100
curve['neut_stderr'] = (curve['stderr']) * 100
curve['neut_measure'] = (1 - curve['measurement']) * 100
curve['neut_measure_upper'] = curve['neut_measure'] + curve['neut_stderr']
curve['neut_measure_lower'] = curve['neut_measure'] - curve['neut_stderr']
return curve
# Example usage:
serum = ["nAH1.3", "m102.4", "HENV-26", "HENV-32", "HENV-103", "HENV-117"]
viruses = "NiV"
curve = extract_dataframe_from_neutcurve(serum, viruses)
display(curve.head(30))
concentration | measurement | fit | stderr | antibody | upper | lower | neut_fit | neut_stderr | neut_measure | neut_measure_upper | neut_measure_lower | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.000266 | NaN | 0.999789 | NaN | nAH1.3 | NaN | NaN | 0.021107 | NaN | NaN | NaN | NaN |
1 | 0.000279 | NaN | 0.999768 | NaN | nAH1.3 | NaN | NaN | 0.023211 | NaN | NaN | NaN | NaN |
2 | 0.000294 | NaN | 0.999745 | NaN | nAH1.3 | NaN | NaN | 0.025525 | NaN | NaN | NaN | NaN |
3 | 0.000309 | NaN | 0.999719 | NaN | nAH1.3 | NaN | NaN | 0.028069 | NaN | NaN | NaN | NaN |
4 | 0.000324 | NaN | 0.999691 | NaN | nAH1.3 | NaN | NaN | 0.030867 | NaN | NaN | NaN | NaN |
5 | 0.000341 | NaN | 0.999661 | NaN | nAH1.3 | NaN | NaN | 0.033944 | NaN | NaN | NaN | NaN |
6 | 0.000359 | NaN | 0.999627 | NaN | nAH1.3 | NaN | NaN | 0.037328 | NaN | NaN | NaN | NaN |
7 | 0.000377 | NaN | 0.999590 | NaN | nAH1.3 | NaN | NaN | 0.041048 | NaN | NaN | NaN | NaN |
8 | 0.000397 | NaN | 0.999549 | NaN | nAH1.3 | NaN | NaN | 0.045139 | NaN | NaN | NaN | NaN |
9 | 0.000417 | NaN | 0.999504 | NaN | nAH1.3 | NaN | NaN | 0.049638 | NaN | NaN | NaN | NaN |
10 | 0.000438 | NaN | 0.999454 | NaN | nAH1.3 | NaN | NaN | 0.054584 | NaN | NaN | NaN | NaN |
11 | 0.000461 | NaN | 0.999400 | NaN | nAH1.3 | NaN | NaN | 0.060024 | NaN | NaN | NaN | NaN |
12 | 0.000485 | NaN | 0.999340 | NaN | nAH1.3 | NaN | NaN | 0.066005 | NaN | NaN | NaN | NaN |
13 | 0.000510 | NaN | 0.999274 | NaN | nAH1.3 | NaN | NaN | 0.072582 | NaN | NaN | NaN | NaN |
14 | 0.000536 | NaN | 0.999202 | NaN | nAH1.3 | NaN | NaN | 0.079813 | NaN | NaN | NaN | NaN |
15 | 0.000563 | NaN | 0.999122 | NaN | nAH1.3 | NaN | NaN | 0.087764 | NaN | NaN | NaN | NaN |
16 | 0.000592 | NaN | 0.999035 | NaN | nAH1.3 | NaN | NaN | 0.096507 | NaN | NaN | NaN | NaN |
17 | 0.000610 | 0.922 | 0.998980 | 0.0254 | nAH1.3 | 0.9474 | 0.8966 | 0.102003 | 2.54 | 7.8 | 10.34 | 5.26 |
18 | 0.000623 | NaN | 0.998939 | NaN | nAH1.3 | NaN | NaN | 0.106119 | NaN | NaN | NaN | NaN |
19 | 0.000655 | NaN | 0.998833 | NaN | nAH1.3 | NaN | NaN | 0.116688 | NaN | NaN | NaN | NaN |
20 | 0.000689 | NaN | 0.998717 | NaN | nAH1.3 | NaN | NaN | 0.128308 | NaN | NaN | NaN | NaN |
21 | 0.000724 | NaN | 0.998589 | NaN | nAH1.3 | NaN | NaN | 0.141084 | NaN | NaN | NaN | NaN |
22 | 0.000761 | NaN | 0.998449 | NaN | nAH1.3 | NaN | NaN | 0.155130 | NaN | NaN | NaN | NaN |
23 | 0.000800 | NaN | 0.998294 | NaN | nAH1.3 | NaN | NaN | 0.170572 | NaN | NaN | NaN | NaN |
24 | 0.000842 | NaN | 0.998125 | NaN | nAH1.3 | NaN | NaN | 0.187548 | NaN | NaN | NaN | NaN |
25 | 0.000885 | NaN | 0.997938 | NaN | nAH1.3 | NaN | NaN | 0.206210 | NaN | NaN | NaN | NaN |
26 | 0.000930 | NaN | 0.997733 | NaN | nAH1.3 | NaN | NaN | 0.226724 | NaN | NaN | NaN | NaN |
27 | 0.000978 | NaN | 0.997507 | NaN | nAH1.3 | NaN | NaN | 0.249275 | NaN | NaN | NaN | NaN |
28 | 0.001029 | NaN | 0.997259 | NaN | nAH1.3 | NaN | NaN | 0.274062 | NaN | NaN | NaN | NaN |
29 | 0.001082 | NaN | 0.996987 | NaN | nAH1.3 | NaN | NaN | 0.301307 | NaN | NaN | NaN | NaN |
Make altair plot of neut curves. Need to use three separate graphs. One for points, one for line, and one for stderr.¶
In [9]:
def plot_neut():
custom_order = ['m102.4','HENV-26','HENV-117','HENV-103','HENV-32','nAH1.3']
cat10_colors = [
"#4E79A5",
"#F18F3B",
"#E0585B",
"#77B7B2",
"#5AA155",
"#EDC958",
"#AF7AA0",
"#FE9EA8",
"#9C7561",
"#BAB0AC",
]
chart = (
alt.Chart(curve)
.mark_line(size=1.5, opacity=1)
.encode(
x=alt.X(
"concentration:Q",
scale=alt.Scale(type="log"),
axis=alt.Axis(format=".0e"),
title="Concentration (μg/mL)",
),
y=alt.Y("fit:Q", title="Fraction Infectivity"),
color=alt.Color(
"antibody", title="Antibody", sort=custom_order,scale=alt.Scale(range=cat10_colors)
),
)
.properties(
height=config["neut_curve_height"],
width=config["neut_curve_width"],
)
)
circle = (
alt.Chart(curve)
.mark_circle(size=40, opacity=1)
.encode(
x=alt.X(
"concentration",
scale=alt.Scale(type="log"),
axis=alt.Axis(format=".0e",tickCount=3),
title="Concentration (μg/mL)",
),
y=alt.Y("measurement:Q", title="Fraction Infectivity"),
color=alt.Color(
"antibody", title="Antibody",sort=custom_order,scale=alt.Scale(range=cat10_colors)
),
)
.properties(
height=200,
width=300,
)
)
error = (
alt.Chart(curve)
.mark_errorbar(opacity=1)
.encode(
x="concentration",
y=alt.Y("lower", title="Fraction Infectivity", axis=alt.Axis(tickCount=3)),
y2="upper",
color=alt.Color("antibody",sort=custom_order,scale=alt.Scale(range=cat10_colors))
)
)
plot = chart + circle + error
return plot
neut_chart = plot_neut()
neut_chart.display()
if mab_neuts_plot is not None:
neut_chart.save(mab_neuts_plot)
In [ ]: