Compare antibody neutralization valuesΒΆ

Compare neutralization values for COV2-2130 and 2130-1-0114-112 antibodies

Import Python modules:

[1]:
import os
import pickle

import altair as alt

import pandas as pd
from functools import reduce

import yaml

Read configuration:

[2]:
with open("config.yaml") as f:
    config = yaml.safe_load(f)
[3]:
antibodies = ['COV2-2130','2130-1-0114-112']

Now get the averaged polyclonal model fits:

[4]:
neutralization = []
for antibody in antibodies:
    with open(os.path.join(config["escape_dir"], f"{antibody}.pickle"), "rb") as f:
        model = pickle.load(f)
    with open(os.path.join(config["escape_dir"], f"{antibody}_avg.csv"), "rb") as h:
        variants_df = pd.read_csv(h)
        variants_df['aa_substitutions'] = variants_df['mutation']
        variants_df = variants_df.filter(['aa_substitutions', 'times_seen'], axis=1)
        variants_df['antibody'] = antibody
        model.icXX(variants_df)
    neutralization.append(model.icXX(variants_df))
[5]:
neutralization = reduce(lambda x, y: pd.merge(x, y, on = 'aa_substitutions',
                                             suffixes=('_COV2-2130', '_2130-1-0114-112')), neutralization)

[6]:
#read in functional effects
func_effect = pd.read_csv(config["muteffects_observed"])
func_effect['aa_substitutions'] = func_effect['wildtype'] + func_effect['reference_site'].astype(str) + func_effect['mutant']
func_effect

func_effect = func_effect[['aa_substitutions', 'effect']].copy()

neutralization = pd.merge(neutralization, func_effect,
                          how='left',
                          on=['aa_substitutions']
                         )
[7]:
#filter for replicates, times_seen and functional effects
neutralization = neutralization.loc[(neutralization['n_models_COV2-2130'] >= 2) & (neutralization['n_models_2130-1-0114-112'] >= 2)]
neutralization = neutralization.loc[(neutralization['times_seen_COV2-2130'] >= 3) & (neutralization['times_seen_2130-1-0114-112'] >= 3)]
neutralization = neutralization.loc[(neutralization['effect'] >= -1.38)]

[8]:
#remove some excess columns
neutralization.drop(['antibody_COV2-2130', 'antibody_2130-1-0114-112',
                     'n_models_COV2-2130', 'frac_models_COV2-2130',
                     'n_models_2130-1-0114-112', 'frac_models_2130-1-0114-112' ], axis=1, inplace=True)

Now plot the results. We will plot the median IC50s across the replicate polyclonal fits for each antibody. This is an interactive plot that you can mouse over for details:

[9]:
corr_chart = (
    alt.Chart(neutralization)
    .encode(
        x=alt.X(
            "median_IC50_COV2-2130",
            title="COV2-2130 IC50 (arbitrary units)",
            scale=alt.Scale(type="log", domainMax=1000, domainMin = 0.003),
        ),
        y=alt.Y(
            "median_IC50_2130-1-0114-112",
            title="2130-1-0114-112 IC50 (arbitrary units)",
            scale=alt.Scale(type="log", domainMax=1000, domainMin = 0.003),
        ),
        tooltip=[
            alt.Tooltip(c, format=".3g") if neutralization[c].dtype == float
            else c
            for c in neutralization.columns.tolist()
        ],
    )
    .mark_circle(filled=True, size=60, opacity=0.3)
    .configure_axis(grid=False)
    .resolve_scale(y="independent", x="independent")
    .properties(width=300, height=300)
)

corr_chart
[9]: