In [1]:
######## snakemake preamble start (automatically inserted, do not edit) ########
import sys;sys.path.extend(['/fh/fast/bloom_j/software/miniforge3/envs/dms-vep-pipeline-3_v3.17/lib/python3.11/site-packages', '/fh/fast/bloom_j/computational_notebooks/jbloom/2024/RABV_Pasteur_G_DMS/dms-vep-pipeline-3/..', '/fh/fast/bloom_j/computational_notebooks/jbloom/2024/RABV_Pasteur_G_DMS/dms-vep-pipeline-3', '/fh/fast/bloom_j/software/miniforge3/envs/dms-vep-pipeline-3_v3.17/bin', '/fh/fast/bloom_j/software/miniforge3/envs/dms-vep-pipeline-3_v3.17/lib/python3.11', '/fh/fast/bloom_j/software/miniforge3/envs/dms-vep-pipeline-3_v3.17/lib/python3.11/lib-dynload', '/home/jbloom/.local/lib/python3.11/site-packages', '/fh/fast/bloom_j/software/miniforge3/envs/dms-vep-pipeline-3_v3.17/lib/python3.11/site-packages', '/home/jbloom/.cache/snakemake/snakemake/source-cache/runtime-cache/tmp0j08q3w9/file/fh/fast/bloom_j/computational_notebooks/jbloom/2024/RABV_Pasteur_G_DMS/notebooks', '/fh/fast/bloom_j/computational_notebooks/jbloom/2024/RABV_Pasteur_G_DMS/notebooks']);import pickle;from snakemake import script;script.snakemake = pickle.loads(b'\x80\x04\x95\x0f\x10\x00\x00\x00\x00\x00\x00\x8c\x10snakemake.script\x94\x8c\tSnakemake\x94\x93\x94)\x81\x94}\x94(\x8c\x05input\x94\x8c\x0csnakemake.io\x94\x8c\nInputFiles\x94\x93\x94)\x81\x94\x8c3results/summaries/all_antibodies_and_cell_entry.csv\x94a}\x94(\x8c\x06_names\x94}\x94\x8c\x0ephenotypes_csv\x94K\x00N\x86\x94s\x8c\x12_allowed_overrides\x94]\x94(\x8c\x05index\x94\x8c\x04sort\x94eh\x12h\x06\x8c\x0eAttributeGuard\x94\x93\x94)\x81\x94}\x94\x8c\x04name\x94h\x12sbh\x13h\x15)\x81\x94}\x94h\x18h\x13sbh\x0eh\nub\x8c\x06output\x94h\x06\x8c\x0bOutputFiles\x94\x93\x94)\x81\x94(\x8c5results/dms-viz/extended_intermediate_cell_entry.json\x94\x8cDresults/dms-viz/extended_intermediate_cell_entry_no_description.json\x94\x8c<results/dms-viz/extended_intermediate_cell_entry_sitemap.csv\x94\x8c?results/dms-viz/extended_intermediate_cell_entry_phenotypes.csv\x94\x8c?results/dms-viz/extended_intermediate_cell_entry_description.md\x94\x8c4results/dms-viz/extended_intermediate_cell_entry.pdb\x94e}\x94(h\x0c}\x94(\x8c\x04json\x94K\x00N\x86\x94\x8c\x13json_no_description\x94K\x01N\x86\x94\x8c\x07sitemap\x94K\x02N\x86\x94\x8c\nphenotypes\x94K\x03N\x86\x94\x8c\x0edescription_md\x94K\x04N\x86\x94\x8c\x08pdb_file\x94K\x05N\x86\x94uh\x10]\x94(h\x12h\x13eh\x12h\x15)\x81\x94}\x94h\x18h\x12sbh\x13h\x15)\x81\x94}\x94h\x18h\x13sbh\'h\x1fh)h h+h!h-h"h/h#h1h$ub\x8c\x06params\x94h\x06\x8c\x06Params\x94\x93\x94)\x81\x94(}\x94(\x8c\x06pdb_id\x94\x8c\x046lgw\x94\x8c\x13escape_or_phenotype\x94\x8c\tphenotype\x94\x8c\nphenotypes\x94]\x94\x8c\ncell entry\x94a\x8c\nantibodies\x94N\x8c\x0fincluded-chains\x94]\x94\x8c\x01E\x94a\x8c\x0fexcluded-chains\x94]\x94\x8c\x01A\x94a\x8c\x08alphabet\x94\x8c\x14RKHDEQNSTYWFAILMVGPC\x94\x8c\x05floor\x94\x89\x8c\x0csummary-stat\x94\x8c\x04mean\x94\x8c\x0ctooltip-cols\x94}\x94\x8c\x0bfilter-cols\x94}\x94\x8c\rfilter-limits\x94}\x94\x8c\x0eheatmap-limits\x94]\x94(J\xfb\xff\xff\xffK\x00K\x02e\x8c\x05title\x94\x8cGEffects of mutations to rabies G on cell entry on extended intermediate\x94\x8c\x0bdescription\x94\x8c\x91Pseudovirus deep mutational scanning of how mutations to rabies G affect\ninto 293T cells shown on low pH extended intermediate structure (6lgw).\n\x94u\x8c\xcaStudy by [Aditham et al](https://www.biorxiv.org/content/10.1101/2024.12.17.628970v1) (2024).\nSee [https://github.com/dms-vep/RABV_Pasteur_G_DMS](https://github.com/dms-vep/RABV_Pasteur_G_DMS) for code.\x94e}\x94(h\x0c}\x94(\x8c\x06config\x94K\x00N\x86\x94\x8c\x12description_suffix\x94K\x01N\x86\x94uh\x10]\x94(h\x12h\x13eh\x12h\x15)\x81\x94}\x94h\x18h\x12sbh\x13h\x15)\x81\x94}\x94h\x18h\x13sbh_h<hah\\ub\x8c\twildcards\x94h\x06\x8c\tWildcards\x94\x93\x94)\x81\x94\x8c extended_intermediate_cell_entry\x94a}\x94(h\x0c}\x94\x8c\x06struct\x94K\x00N\x86\x94sh\x10]\x94(h\x12h\x13eh\x12h\x15)\x81\x94}\x94h\x18h\x12sbh\x13h\x15)\x81\x94}\x94h\x18h\x13sb\x8c\x06struct\x94hlub\x8c\x07threads\x94K\x01\x8c\tresources\x94h\x06\x8c\tResources\x94\x93\x94)\x81\x94(K\x01K\x01\x8c\x14/loc/scratch/3868073\x94e}\x94(h\x0c}\x94(\x8c\x06_cores\x94K\x00N\x86\x94\x8c\x06_nodes\x94K\x01N\x86\x94\x8c\x06tmpdir\x94K\x02N\x86\x94uh\x10]\x94(h\x12h\x13eh\x12h\x15)\x81\x94}\x94h\x18h\x12sbh\x13h\x15)\x81\x94}\x94h\x18h\x13sbh\x7fK\x01h\x81K\x01h\x83h|ub\x8c\x03log\x94h\x06\x8c\x03Log\x94\x93\x94)\x81\x94\x8cJresults/notebooks/configure_dms_viz_extended_intermediate_cell_entry.ipynb\x94a}\x94(h\x0c}\x94\x8c\x08notebook\x94K\x00N\x86\x94sh\x10]\x94(h\x12h\x13eh\x12h\x15)\x81\x94}\x94h\x18h\x12sbh\x13h\x15)\x81\x94}\x94h\x18h\x13sbh\x91h\x8eubh_}\x94(\x8c\rpipeline_path\x94\x8c\x12dms-vep-pipeline-3\x94\x8c\x04docs\x94\x8c\x04docs\x94\x8c\x08homepage\x94\x8c\x0fhomepage/public\x94\x8c\x18build_vitepress_homepage\x94\x88\x8c\x0fgithub_repo_url\x94\x8c-https://github.com/dms-vep/RABV_Pasteur_G_DMS\x94\x8c\x0fgithub_blob_url\x94\x8c7https://github.com/dms-vep/RABV_Pasteur_G_DMS/blob/main\x94\x8c\x0bdescription\x94\x8c5Deep mutational scanning of rabies G (Pasteur strain)\x94\x8c\x04year\x94M\xe8\x07\x8c\x07authors\x94\x8cL[Aditham et al](https://www.biorxiv.org/content/10.1101/2024.12.17.628970v1)\x94\x8c\x1euse_precomputed_barcode_counts\x94\x89\x8c\x12site_numbering_map\x94\x8c\x1bdata/site_numbering_map.csv\x94\x8c\x1emutation_design_classification\x94}\x94(\x8c\x03csv\x94\x8c\x1bdata/designed_mutations.csv\x94\x8c\x08site_col\x94\x8c\x0fsequential_site\x94u\x8c\x16neut_standard_barcodes\x94\x8c)data/neutralization_standard_barcodes.csv\x94\x8c\x11prebuilt_variants\x94N\x8c\x10prebuilt_geneseq\x94N\x8c\x0bpacbio_runs\x94\x8c\x14data/PacBio_runs.csv\x94\x8c\x0fpacbio_amplicon\x94\x8c\x17data/PacBio_amplicon.gb\x94\x8c\x15pacbio_amplicon_specs\x94\x8c$data/PacBio_feature_parse_specs.yaml\x94\x8c\x0cvariant_tags\x94}\x94(\x8c\x0cvariant_tag5\x94}\x94(\x8c\tvariant_1\x94\x8c\x01G\x94\x8c\tvariant_2\x94\x8c\x01C\x94\x8c\x08wildtype\x94hJu\x8c\x0cvariant_tag3\x94}\x94(\x8c\tvariant_1\x94h\xc1\x8c\tvariant_2\x94h\xc3\x8c\x08wildtype\x94hJuu\x8c\x12max_ccs_error_rate\x94G?\x1a6\xe2\xeb\x1cC-\x8c\x10consensus_params\x94}\x94(\x8c\rmax_sub_diffs\x94N\x8c\x0fmax_indel_diffs\x94N\x8c\x12max_minor_sub_frac\x94G?\xc9\x99\x99\x99\x99\x99\x9a\x8c\x14max_minor_indel_frac\x94G?\xc9\x99\x99\x99\x99\x99\x9a\x8c\x0bmin_support\x94K\x03u\x8c\x13gene_sequence_codon\x94\x8c\x1edata/gene_sequence/codon.fasta\x94\x8c\x15gene_sequence_protein\x94\x8c data/gene_sequence/protein.fasta\x94\x8c\x0ecodon_variants\x94\x8c#results/variants/codon_variants.csv\x94\x8c\x0cbarcode_runs\x94\x8c\x15data/barcode_runs.csv\x94\x8c\x12duplicate_fastq_R1\x94\x8c\x04warn\x94\x8c\x1eillumina_barcode_parser_params\x94}\x94(\x8c\x08upstream\x94\x8c"ACTCCACTAGGAACATTTCTCTCTCGAATCTAGA\x94\x8c\ndownstream\x94\x8c\x00\x94\x8c\x04minq\x94K\x14\x8c\x11upstream_mismatch\x94K\x02u\x8c\x13func_effects_config\x94\x8c\x1cdata/func_effects_config.yml\x94\x8c\x16antibody_escape_config\x94\x8c\x1fdata/antibody_escape_config.yml\x94\x8c\x10summaries_config\x94\x8c\x19data/summaries_config.yml\x94\x8c\x0edms_viz_config\x94\x8c\x17data/dms_viz_config.yml\x94u\x8c\x04rule\x94\x8c\x11configure_dms_viz\x94\x8c\x0fbench_iteration\x94N\x8c\tscriptdir\x94\x8cQ/fh/fast/bloom_j/computational_notebooks/jbloom/2024/RABV_Pasteur_G_DMS/notebooks\x94ub.');del script;from snakemake.logging import logger;from snakemake.script import snakemake; logger.printshellcmds = False;import os; os.chdir(r'/fh/fast/bloom_j/computational_notebooks/jbloom/2024/RABV_Pasteur_G_DMS');
######## snakemake preamble end #########

Configure dms-viz JSONs¶

Imports:

In [2]:
import gzip
import os
import requests
import subprocess
import warnings

import Bio.PDB.PDBParser
import Bio.PDB.Polypeptide

import matplotlib.colors

import pandas as pd

import seaborn

Get variables from snakemake:

In [3]:
config = snakemake.params.config
pdb_id = config["pdb_id"]
escape_or_phenotype = config["escape_or_phenotype"]
phenotypes_list = config["phenotypes"]
antibody_list = config["antibodies"]

phenotypes_csv = snakemake.input.phenotypes_csv
if escape_or_phenotype == "escape":
    per_antibody_escape_csv = snakemake.input.per_antibody_escape_csv

dms_viz_json = snakemake.output.json
dms_viz_json_no_description = snakemake.output.json_no_description
dms_viz_sitemap = snakemake.output.sitemap
dms_viz_phenotypes = snakemake.output.phenotypes
pdb_file = snakemake.output.pdb_file
description_md = snakemake.output.description_md

description_suffix = snakemake.params.description_suffix

name = snakemake.wildcards.struct

Build the sitemap used by dms-viz:

In [4]:
phenotypes_all = pd.read_csv(phenotypes_csv)

sitemap = (
    phenotypes_all
    [["sequential_site", "site", "wildtype"]]
    .rename(columns={"site": "reference_site"})
    .drop_duplicates()
    .sort_values("sequential_site")
)

assert len(sitemap) == sitemap["sequential_site"].nunique() == sitemap["reference_site"].nunique()

sitemap.to_csv(dms_viz_sitemap, index=False)

Get the biological assembly (see https://pdb101.rcsb.org/learn/guide-to-understanding-pdb-data/biological-assemblies#Anchor-download) in case the crystallographic unit doesn't correspond to that:

In [5]:
pdb_id = config["pdb_id"]

print(f"Getting PDB file for {pdb_id}")

r = requests.get(f"https://files.rcsb.org/download/{pdb_id}.pdb1.gz")
assert r.status_code == 200
pdb_content = gzip.decompress(r.content).decode("utf-8")
with open(pdb_file, "w") as f:
    f.write(pdb_content)
Getting PDB file for 6lgw

Check the sites mismatched between the sitemap and the protein structure in terms of residue identity:

In [6]:
with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    pdb_obj = Bio.PDB.PDBParser().get_structure(id=pdb_id, file=pdb_file)[0]

all_chains = [chain.id for chain in pdb_obj]
print(f"PDB has {all_chains=}")
if "included-chains" in config:
    included_chains = config["included-chains"]
    print(f"{included_chains=}")
    assert set(included_chains).issubset(all_chains)
else:
    included_chains = all_chains

records = []
for chain in included_chains:
    for res in pdb_obj[chain].get_residues():
        if not res.id[0].isspace():
            continue
        aa = Bio.PDB.Polypeptide.protein_letters_3to1[res.resname]
        r = res.id[1]
        records.append((chain, r, aa))
pdb_df = pd.DataFrame(records, columns=["chain", "reference_site", "pdb_aa"])

mismatched_sites = sitemap.merge(pdb_df, how="left")

print(
    f"Of {len(sitemap)} sites, {len(mismatched_sites.query('wildtype == pdb_aa'))} match, "
    f"{len(mismatched_sites.query('pdb_aa.isnull()'))} are missing from PDB, and "
    f"{len(mismatched_sites.query('pdb_aa.notnull()').query('wildtype != pdb_aa'))} differ."
)

print("Sites that differ:")
display(mismatched_sites.query("pdb_aa.notnull() and (wildtype != pdb_aa)").reset_index(drop=True))
PDB has all_chains=['A', 'E']
included_chains=['E']
Of 433 sites, 346 match, 65 are missing from PDB, and 22 differ.
Sites that differ:
sequential_site reference_site wildtype chain pdb_aa
0 12 10 K E E
1 42 40 G E E
2 58 56 M E V
3 128 126 K E R
4 135 133 V E I
5 142 140 A E T
6 149 147 R E K
7 160 158 N E K
8 164 162 V E I
9 165 163 A E T
10 189 187 M E T
11 190 188 S E P
12 204 202 K E N
13 206 204 S E N
14 207 205 E E K
15 226 224 K E R
16 249 247 N E D
17 257 255 G E D
18 348 346 R E K
19 371 369 G E D
20 372 370 N E H
21 391 389 V E K

Process the different phenotypes and escape if relevant:

In [7]:
# get extra columns dealing with fact `dms-viz` does not allow them to have spaces
extra_cols = []
for coltype in ["tooltip-cols", "filter-cols"]:
    for col in list(config[coltype]):
        col_nospace = col.replace(" ", "_")
        assert (col in phenotypes_all.columns) or (col_nospace in phenotypes_all.columns), f"no column {col} in {phenotypes_all.columns=}"
        if col != col_nospace:
            assert (col_nospace not in phenotypes_all.columns) or (col not in phenotypes_all.columns)
            assert col_nospace not in config[coltype]
            phenotypes_all = phenotypes_all.rename(columns={col: col_nospace}, errors="ignore")
            config[coltype][col_nospace] = config[coltype][col]
            del config[coltype][col]
            extra_cols.append(col_nospace)
            if coltype == "filter-cols":
                if col in config["filter-limits"]:
                    config["filter-limits"][col_nospace] = config["filter-limits"][col]
                    del config["filter-limits"][col]
        else:
            extra_cols.append(col)
print(f"Keeping {extra_cols=} for filters and tooltips")
assert set(extra_cols).issubset(phenotypes_all.columns)

id_cols = ["site", "wildtype", "mutant"]

if escape_or_phenotype == "phenotype":
    assert phenotypes_list and not antibody_list, f"{phenotypes_list=}, {antibody_list=}"
    assert set(phenotypes_list).issubset(phenotypes_all.columns), f"{phenotypes_list=}\n{phenotypes_all.columns=}"
    metric_column = "effect"
    condition_column = "phenotype"
    phenotypes = (
        phenotypes_all
        .melt(
            id_vars=id_cols + extra_cols,
            value_vars=phenotypes_list,
            var_name=condition_column,
            value_name=metric_column,
        )
    )

elif escape_or_phenotype == "escape":
    assert not phenotypes_list

    phenotypes = pd.read_csv(per_antibody_escape_csv)

    antibodies_all = list(phenotypes["antibody"].unique())
    if antibody_list is not None:
        antibodies = antibody_list
        print(f"Using just {antibodies=} from {antibodies_all=}")
        assert set(antibodies).issubset(antibodies_all), f"{antibodies=}\n{antibodies_all=}"
        assert len(antibodies) >= 1
    else:
        antibodies = antibodies_all
        print(f"Using all {antibodies=}")
    antibodies = antibodies
    metric_column = "escape"
    condition_column = "antibody"
    phenotypes = phenotypes[id_cols + [metric_column, condition_column]]
    phenotypes = phenotypes[phenotypes[condition_column].isin(antibodies)]
    
    if extra_cols:
        join_data = phenotypes_all[id_cols + extra_cols]
        assert len(join_data) == len(join_data.groupby(id_cols))
        assert set(join_data.columns).intersection(phenotypes.columns) == set(id_cols)
        phenotypes = phenotypes.merge(join_data, on=id_cols, validate="many_to_one", how="left")

else:
    raise ValueError(f"invalid {escape_or_phenotype=}")

print(f"Writing the phenotypes to {dms_viz_phenotypes}")
phenotypes.to_csv(dms_viz_phenotypes, index=False)
display(phenotypes)
Keeping extra_cols=[] for filters and tooltips
Writing the phenotypes to results/dms-viz/extended_intermediate_cell_entry_phenotypes.csv
site wildtype mutant phenotype effect
0 -2 F A cell entry -0.44860
1 -2 F C cell entry -0.18730
2 -2 F D cell entry 0.01876
3 -2 F E cell entry -0.79690
4 -2 F F cell entry 0.00000
... ... ... ... ... ...
8618 431 D S cell entry -0.26610
8619 431 D T cell entry -0.17420
8620 431 D V cell entry -0.76310
8621 431 D W cell entry -0.56050
8622 431 D Y cell entry -0.72370

8623 rows × 5 columns

Get enough colors:

In [8]:
def get_hex_color_palette(num_colors):
    colors = seaborn.color_palette("hls", num_colors)
    hex_colors = [matplotlib.colors.to_hex(color) for color in colors]
    return hex_colors

nconditions = phenotypes[condition_column].nunique()
if nconditions > 4:
    colors = ",".join(get_hex_color_palette(nconditions))
else:
    colors = "#0072B2,#CC79A7,#4C3549,#009E73"

print(f"Using {colors=}")
seaborn.palplot(seaborn.color_palette(colors.split(",")))
Using colors='#0072B2,#CC79A7,#4C3549,#009E73'
No description has been provided for this image

Get the description:

In [9]:
description_text = "\n\n".join(
    [
        f"## {config['title']}",
        config["description"] if config["description"] is not None else "",
        f"Structure shown is from PDB {pdb_id}.",
        description_suffix,
    ]
).lstrip()

print(description_text)

with open(description_md, "w") as f:
    f.write(description_text)
## Effects of mutations to rabies G on cell entry on extended intermediate

Pseudovirus deep mutational scanning of how mutations to rabies G affect
into 293T cells shown on low pH extended intermediate structure (6lgw).


Structure shown is from PDB 6lgw.

Study by [Aditham et al](https://www.biorxiv.org/content/10.1101/2024.12.17.628970v1) (2024).
See [https://github.com/dms-vep/RABV_Pasteur_G_DMS](https://github.com/dms-vep/RABV_Pasteur_G_DMS) for code.

Now run configure-dms-viz:

In [10]:
cmds = [
    "configure-dms-viz", "format",
    "--name", name,
    "--input", dms_viz_phenotypes,
    "--output", dms_viz_json_no_description,
    "--structure", pdb_file,
    "--metric", metric_column,
    "--condition", condition_column,
    "--sitemap", dms_viz_sitemap,
    "--colors", colors,
]

for col in [
    "included-chains",
    "excluded-chains",
]:
    val = config[col]
    if val:
        cmds += [f"--{col}", " ".join(val)]

if config["heatmap-limits"]:
    cmds += ["--heatmap-limits", ", ".join(str(x) for x in config["heatmap-limits"])]

for col in [
    "alphabet",
    "floor",
    "summary-stat",
    "tooltip-cols",
    "filter-cols",
    "filter-limits",
    "title",
    "description",
]:
    cmds += [f"--{col}", str(config[col])]

print(f"Running the following commands:\n{cmds}")
subprocess.run(cmds, check=True)

cmds_join = [
    "configure-dms-viz", "join",
    "--input", dms_viz_json_no_description,
    "--description", description_md,
    "--output", dms_viz_json,
]

print(f"Running the following commands:\n{cmds_join}")
subprocess.run(cmds_join, check=True)
Running the following commands:
['configure-dms-viz', 'format', '--name', 'extended_intermediate_cell_entry', '--input', 'results/dms-viz/extended_intermediate_cell_entry_phenotypes.csv', '--output', 'results/dms-viz/extended_intermediate_cell_entry_no_description.json', '--structure', 'results/dms-viz/extended_intermediate_cell_entry.pdb', '--metric', 'effect', '--condition', 'phenotype', '--sitemap', 'results/dms-viz/extended_intermediate_cell_entry_sitemap.csv', '--colors', '#0072B2,#CC79A7,#4C3549,#009E73', '--included-chains', 'E', '--excluded-chains', 'A', '--heatmap-limits', '-5, 0, 2', '--alphabet', 'RKHDEQNSTYWFAILMVGPC', '--floor', 'False', '--summary-stat', 'mean', '--tooltip-cols', '{}', '--filter-cols', '{}', '--filter-limits', '{}', '--title', 'Effects of mutations to rabies G on cell entry on extended intermediate', '--description', 'Pseudovirus deep mutational scanning of how mutations to rabies G affect\ninto 293T cells shown on low pH extended intermediate structure (6lgw).\n']
Formatting data for visualization using the 'effect' column from 'results/dms-viz/extended_intermediate_cell_entry_phenotypes.csv'...

Using sitemap from 'results/dms-viz/extended_intermediate_cell_entry_sitemap.csv'.

Warning: NaN values were found in the metric column. These rows will be filtered out.
'protein_site' column is not present in the sitemap. Assuming that the reference sites correspond to protein sites.

Three values were provided for heatmap limits, these will be the min, center, and max of the scale.

About 94.02% (346 of 368) of the wildtype residues in the data match the corresponding residues in the structure.
About 15.01% (65 of 433) of the data sites are missing from the structure.
Success! The visualization JSON was written to 'results/dms-viz/extended_intermediate_cell_entry_no_description.json'
Running the following commands:
['configure-dms-viz', 'join', '--input', 'results/dms-viz/extended_intermediate_cell_entry_no_description.json', '--description', 'results/dms-viz/extended_intermediate_cell_entry_description.md', '--output', 'results/dms-viz/extended_intermediate_cell_entry.json']
Success! 1 JSON files were merged and saved to 'results/dms-viz/extended_intermediate_cell_entry.json'
Out[10]:
CompletedProcess(args=['configure-dms-viz', 'join', '--input', 'results/dms-viz/extended_intermediate_cell_entry_no_description.json', '--description', 'results/dms-viz/extended_intermediate_cell_entry_description.md', '--output', 'results/dms-viz/extended_intermediate_cell_entry.json'], returncode=0)
In [ ]: