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 [ ]: