import%20marimo%0A%0A__generated_with%20%3D%20%220.17.6%22%0Aapp%20%3D%20marimo.App(width%3D%22full%22)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Fold-change%20titer%20plots%0A%0A%20%20%20%20Interactive%20Altair%20plots%20comparing%20titers%20between%20paired%20sera%20cohorts%0A%20%20%20%20(e.g.%2C%20pre-%20vs%20post-vaccination)%2C%20with%20fold-change%20panels.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20%23%20Load%20context%20from%20pickled%20file.%0A%20%20%20%20%23%0A%20%20%20%20%23%20This%20cell%20supports%20multiple%20ways%20to%20provide%20context%3A%0A%20%20%20%20%23%201.%20Via%20command-line%3A%20marimo%20export%20html%20notebook.py%20--%20--context-pickle%20path%2Fto%2Fcontext.pickle%0A%20%20%20%20%23%202.%20Via%20saved%20pickle%3A%20Manually%20save%20a%20context%20pickle%20to%20a%20dev%20location%0A%20%20%20%20%23%203.%20Stub%20context%3A%20If%20no%20pickle%20available%2C%20creates%20minimal%20empty%20context%20for%20exploration%0A%0A%20%20%20%20import%20argparse%0A%20%20%20%20import%20os%0A%20%20%20%20import%20pathlib%0A%20%20%20%20import%20pickle%0A%20%20%20%20import%20sys%0A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20from_cmdline%20%3D%20%22--context-pickle%22%20in%20sys.argv%0A%0A%20%20%20%20if%20from_cmdline%3A%0A%20%20%20%20%20%20%20%20print(%22Loading%20context%20from%20command-line%20argument%22)%0A%20%20%20%20%20%20%20%20p%20%3D%20argparse.ArgumentParser()%0A%20%20%20%20%20%20%20%20p.add_argument(%22--context-pickle%22%2C%20required%3DTrue)%0A%20%20%20%20%20%20%20%20args%20%3D%20p.parse_args()%0A%20%20%20%20%20%20%20%20context_pickle_path%20%3D%20pathlib.Path(args.context_pickle)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20print(%22Running%20in%20marimo%20edit%20mode%22)%0A%20%20%20%20%20%20%20%20context_pickle_path%20%3D%20None%0A%20%20%20%20%20%20%20%20context_pickle_path%20%3D%20pathlib.Path(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22results%2Ftiter_plots%2FPENN_pre_post_vax_plot_fold_changes_vertical_context.pickle%22%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20if%20context_pickle_path%20and%20context_pickle_path.exists()%3A%0A%20%20%20%20%20%20%20%20print(f%22Reading%20context%20from%20%7Bcontext_pickle_path%7D%22)%0A%20%20%20%20%20%20%20%20with%20open(context_pickle_path%2C%20%22rb%22)%20as%20f_context%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20context%20%3D%20pickle.load(f_context)%0A%0A%20%20%20%20%20%20%20%20context_workdir%20%3D%20context%5B%22workdir%22%5D%0A%20%20%20%20%20%20%20%20current_workdir%20%3D%20os.getcwd()%0A%0A%20%20%20%20%20%20%20%20if%20from_cmdline%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20context_workdir%20!%3D%20current_workdir%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20raise%20RuntimeError(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22Context%20workdir%20mismatch!%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%20%20Context%20was%20created%20in%3A%20%7Bcontext_workdir%7D%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%20%20Currently%20running%20in%3A%20%20%20%7Bcurrent_workdir%7D%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22This%20should%20not%20happen%20when%20running%20via%20Snakemake.%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22Verified%20working%20directory%3A%20%7Bcurrent_workdir%7D%22)%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20context_workdir%20and%20context_workdir%20!%3D%20current_workdir%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%22Changing%20directory%20from%20%7Bcurrent_workdir%7D%20to%20%7Bcontext_workdir%7D%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20os.chdir(context_workdir)%0A%20%20%20%20%20%20%20%20%20%20%20%20elif%20context_workdir%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%22Already%20in%20correct%20working%20directory%3A%20%7Bcontext_workdir%7D%22)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20print(%22Creating%20minimal%20stub%20context%20that%20you%20need%20to%20complete%22)%0A%20%20%20%20%20%20%20%20context%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22input%22%3A%20%7B%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22output%22%3A%20%7B%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22params%22%3A%20%7B%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22wildcards%22%3A%20%7B%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22threads%22%3A%201%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22resources%22%3A%20%7B%7D%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20return%20context%2C%20mo%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Read%20data%20and%20pair%20sera%20across%20cohorts%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20itertools%0A%20%20%20%20import%20json%0A%0A%20%20%20%20import%20altair%20as%20alt%0A%20%20%20%20import%20pandas%20as%20pd%0A%0A%20%20%20%20_%20%3D%20alt.data_transformers.disable_max_rows()%0A%20%20%20%20return%20alt%2C%20itertools%2C%20json%2C%20pd%0A%0A%0A%40app.cell%0Adef%20_(context)%3A%0A%20%20%20%20%23%20Extract%20variables%20from%20context%0A%20%20%20%20titers_csv%20%3D%20context%5B%22input%22%5D%5B%22titers_csv%22%5D%0A%20%20%20%20sera_multicohort_csv%20%3D%20context%5B%22input%22%5D%5B%22sera_multicohort_csv%22%5D%0A%20%20%20%20viruses_csv%20%3D%20context%5B%22input%22%5D%5B%22viruses_csv%22%5D%0A%20%20%20%20recent_vaccine_strains%20%3D%20context%5B%22params%22%5D%5B%22recent_vaccine_strains%22%5D%0A%20%20%20%20circulating_strain_type%20%3D%20context%5B%22params%22%5D%5B%22circulating_strain_type%22%5D%0A%20%20%20%20plot_titer_summaries_params%20%3D%20context%5B%22params%22%5D%5B%22plot_titer_summaries_params%22%5D%0A%20%20%20%20subtypes%20%3D%20context%5B%22params%22%5D%5B%22subtypes%22%5D%0A%20%20%20%20fold_change_config%20%3D%20context%5B%22params%22%5D%5B%22fold_change_config%22%5D%0A%20%20%20%20facet_orientation%20%3D%20context%5B%22params%22%5D%5B%22facet_orientation%22%5D%0A%20%20%20%20chart_htmls%20%3D%20context%5B%22output%22%5D%5B%22chart_htmls%22%5D%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20chart_htmls%2C%0A%20%20%20%20%20%20%20%20circulating_strain_type%2C%0A%20%20%20%20%20%20%20%20facet_orientation%2C%0A%20%20%20%20%20%20%20%20fold_change_config%2C%0A%20%20%20%20%20%20%20%20plot_titer_summaries_params%2C%0A%20%20%20%20%20%20%20%20recent_vaccine_strains%2C%0A%20%20%20%20%20%20%20%20sera_multicohort_csv%2C%0A%20%20%20%20%20%20%20%20subtypes%2C%0A%20%20%20%20%20%20%20%20titers_csv%2C%0A%20%20%20%20%20%20%20%20viruses_csv%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell%0Adef%20_(fold_change_config%2C%20mo%2C%20pd%2C%20sera_multicohort_csv%2C%20titers_csv)%3A%0A%20%20%20%20%23%20Read%20titers%0A%20%20%20%20titers%20%3D%20pd.read_csv(titers_csv)%0A%20%20%20%20mo.output.append(mo.md(f%22Read%20%7Blen(titers)%3D%7D%20titers%20from%20%7Btiters_csv%3D%7D%22))%0A%0A%20%20%20%20required_titer_cols%20%3D%20%7B%22serum%22%2C%20%22virus%22%2C%20%22titer%22%7D%0A%20%20%20%20missing_titer_cols%20%3D%20required_titer_cols%20-%20set(titers.columns)%0A%20%20%20%20if%20missing_titer_cols%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError(f%22titers_csv%20missing%20required%20columns%3A%20%7Bmissing_titer_cols%7D%22)%0A%0A%20%20%20%20%23%20Read%20sera%20multicohort%0A%20%20%20%20sera_mc%20%3D%20pd.read_csv(sera_multicohort_csv)%0A%20%20%20%20mo.output.append(mo.md(f%22%5CnRead%20%7Blen(sera_mc)%3D%7D%20rows%20from%20%7Bsera_multicohort_csv%3D%7D%22))%0A%0A%20%20%20%20%23%20Extract%20fold-change%20config%0A%20%20%20%20cohorts_config%20%3D%20fold_change_config%5B%22cohorts%22%5D%0A%20%20%20%20condition_colors%20%3D%20fold_change_config%5B%22condition_colors%22%5D%0A%20%20%20%20fold_change_title%20%3D%20fold_change_config%5B%22title%22%5D%0A%0A%20%20%20%20if%20len(condition_colors)%20!%3D%20len(cohorts_config)%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError(%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22condition_colors%20has%20%7Blen(condition_colors)%7D%20entries%20but%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22cohorts%20has%20%7Blen(cohorts_config)%7D%20entries%3B%20must%20match%22%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20mo.output.append(mo.md(f%22%5CnCohorts%20config%3A%20%7Bcohorts_config%7D%22))%0A%20%20%20%20mo.output.append(mo.md(f%22Condition%20colors%3A%20%7Bcondition_colors%7D%22))%0A%0A%20%20%20%20%23%20Validate%20all%20specified%20cohorts%20exist%0A%20%20%20%20available_cohorts%20%3D%20set(sera_mc%5B%22cohort%22%5D.unique())%0A%20%20%20%20for%20cohort_name%20in%20cohorts_config%3A%0A%20%20%20%20%20%20%20%20if%20cohort_name%20not%20in%20available_cohorts%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22Cohort%20%7Bcohort_name!r%7D%20not%20found%20in%20sera_multicohort.%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22Available%20cohorts%3A%20%7Bsorted(available_cohorts)%7D%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%23%20Filter%20to%20specified%20cohorts%20with%20non-null%20subject_id%0A%20%20%20%20sera_filtered%20%3D%20sera_mc%5B%0A%20%20%20%20%20%20%20%20sera_mc%5B%22cohort%22%5D.isin(cohorts_config)%0A%20%20%20%20%20%20%20%20%26%20sera_mc%5B%22subject_id%22%5D.notna()%0A%20%20%20%20%20%20%20%20%26%20(sera_mc%5B%22subject_id%22%5D%20!%3D%20%22%22)%0A%20%20%20%20%5D.copy()%0A%20%20%20%20mo.output.append(%0A%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22%5CnFiltered%20to%20%7Blen(sera_filtered)%7D%20rows%20with%20specified%20cohorts%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22and%20non-null%20subject_id%22%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Find%20subject_ids%20present%20in%20ALL%20specified%20cohorts%0A%20%20%20%20subjects_per_cohort%20%3D%20sera_filtered.groupby(%22cohort%22)%5B%22subject_id%22%5D.apply(set)%0A%20%20%20%20paired_subjects%20%3D%20set.intersection(*subjects_per_cohort.values)%0A%20%20%20%20if%20not%20paired_subjects%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22No%20subject_ids%20found%20in%20all%20specified%20cohorts.%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Subjects%20per%20cohort%3A%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%2B%20%22%2C%20%22.join(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%7Bc%7D%3A%20%7Blen(s)%7D%20subjects%22%20for%20c%2C%20s%20in%20subjects_per_cohort.items()%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20mo.output.append(mo.md(f%22%5CnFound%20%7Blen(paired_subjects)%7D%20paired%20subjects%22))%0A%0A%20%20%20%20if%20%22all%20subjects%22%20in%20paired_subjects%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Found%20subject_id%20'all%20subjects'%20in%20data%3B%20this%20conflicts%20with%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22the%20dropdown%20label%20used%20in%20individual_sera%20plots%22%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%23%20Filter%20to%20paired%20subjects%20and%20add%20condition%20labels%0A%20%20%20%20sera_paired%20%3D%20sera_filtered%5B%0A%20%20%20%20%20%20%20%20sera_filtered%5B%22subject_id%22%5D.isin(paired_subjects)%0A%20%20%20%20%5D.copy()%0A%20%20%20%20sera_paired%5B%22condition%22%5D%20%3D%20sera_paired%5B%22cohort%22%5D.map(cohorts_config)%0A%0A%20%20%20%20%23%20Validate%20each%20subject%20has%20exactly%20one%20serum%20per%20cohort%0A%20%20%20%20serum_counts%20%3D%20sera_paired.groupby(%5B%22subject_id%22%2C%20%22cohort%22%5D)%5B%22serum%22%5D.nunique()%0A%20%20%20%20multi_sera%20%3D%20serum_counts%5Bserum_counts%20%3E%201%5D%0A%20%20%20%20if%20len(multi_sera)%20%3E%200%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError(f%22Some%20subjects%20have%20multiple%20sera%20in%20a%20cohort%3A%5Cn%7Bmulti_sera%7D%22)%0A%0A%20%20%20%20%23%20Join%20with%20titers%0A%20%20%20%20paired_titers%20%3D%20sera_paired%5B%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22serum%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22subject_id%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22condition%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22cohort%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22age%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22sex%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22age_numeric%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22serum_collection_date%22%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%5D.merge(titers%5B%5B%22serum%22%2C%20%22virus%22%2C%20%22titer%22%5D%5D%2C%20on%3D%22serum%22)%0A%20%20%20%20mo.output.append(mo.md(f%22%5CnPaired%20titers%3A%20%7Blen(paired_titers)%7D%20rows%22))%0A%0A%20%20%20%20%23%20Compute%20fold%20change%20relative%20to%20baseline%20(first%20cohort)%0A%20%20%20%20baseline_cohort%20%3D%20list(cohorts_config.keys())%5B0%5D%0A%20%20%20%20baseline_label%20%3D%20cohorts_config%5Bbaseline_cohort%5D%0A%20%20%20%20baseline%20%3D%20paired_titers%5Bpaired_titers%5B%22cohort%22%5D%20%3D%3D%20baseline_cohort%5D%5B%0A%20%20%20%20%20%20%20%20%5B%22subject_id%22%2C%20%22virus%22%2C%20%22titer%22%5D%0A%20%20%20%20%5D.rename(columns%3D%7B%22titer%22%3A%20%22baseline_titer%22%7D)%0A%0A%20%20%20%20paired_titers%20%3D%20paired_titers.merge(baseline%2C%20on%3D%5B%22subject_id%22%2C%20%22virus%22%5D)%0A%20%20%20%20paired_titers%5B%22fold_change%22%5D%20%3D%20(%0A%20%20%20%20%20%20%20%20paired_titers%5B%22titer%22%5D%20%2F%20paired_titers%5B%22baseline_titer%22%5D%0A%20%20%20%20)%0A%20%20%20%20mo.output.append(%0A%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22%5CnComputed%20fold%20changes%20relative%20to%20baseline%3A%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22%7Bbaseline_label!r%7D%20(%7Bbaseline_cohort%7D)%22%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20baseline_label%2C%0A%20%20%20%20%20%20%20%20cohorts_config%2C%0A%20%20%20%20%20%20%20%20condition_colors%2C%0A%20%20%20%20%20%20%20%20fold_change_title%2C%0A%20%20%20%20%20%20%20%20paired_titers%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20circulating_strain_type%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20paired_titers%2C%0A%20%20%20%20pd%2C%0A%20%20%20%20recent_vaccine_strains%2C%0A%20%20%20%20subtypes%2C%0A%20%20%20%20viruses_csv%2C%0A)%3A%0A%20%20%20%20%23%20Read%20viruses%20and%20process%20same%20as%20plot_titer_summaries%0A%20%20%20%20viruses%20%3D%20pd.read_csv(viruses_csv)%0A%20%20%20%20mo.output.append(mo.md(f%22Read%20%7Blen(viruses)%3D%7D%20viruses%20from%20%7Bviruses_csv%3D%7D%22))%0A%0A%20%20%20%20required_virus_cols%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22virus%22%2C%0A%20%20%20%20%20%20%20%20%22subtype%22%2C%0A%20%20%20%20%20%20%20%20%22strain_type%22%2C%0A%20%20%20%20%20%20%20%20%22subclade%22%2C%0A%20%20%20%20%20%20%20%20%22derived_haplotype%22%2C%0A%20%20%20%20%20%20%20%20%22vaccine_type%22%2C%0A%20%20%20%20%7D%0A%20%20%20%20missing_virus_cols%20%3D%20required_virus_cols%20-%20set(viruses.columns)%0A%20%20%20%20if%20missing_virus_cols%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError(f%22viruses_csv%20missing%20required%20columns%3A%20%7Bmissing_virus_cols%7D%22)%0A%0A%20%20%20%20%23%20Validate%20recent_vaccine_strains%20are%20in%20viruses%0A%20%20%20%20missing_vaccine_strains%20%3D%20set(recent_vaccine_strains)%20-%20set(viruses%5B%22virus%22%5D)%0A%20%20%20%20if%20missing_vaccine_strains%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError(%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22recent_vaccine_strains%20not%20in%20viruses%3A%20%7Bmissing_vaccine_strains%7D%22%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%23%20Validate%20strain_type%20values%0A%20%20%20%20valid_strain_types%20%3D%20%7Bcirculating_strain_type%2C%20%22vaccine%22%7D%0A%20%20%20%20invalid_strain_types%20%3D%20set(viruses%5B%22strain_type%22%5D)%20-%20valid_strain_types%0A%20%20%20%20if%20invalid_strain_types%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError(%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22Invalid%20strain_type%20values%3A%20%7Binvalid_strain_types%7D.%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22Expected%3A%20%7Bvalid_strain_types%7D%22%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%23%20Mark%20recent%20vaccine%20strains%0A%20%20%20%20viruses%5B%22strain_type%22%5D%20%3D%20viruses%5B%22strain_type%22%5D.where(%0A%20%20%20%20%20%20%20%20~viruses%5B%22virus%22%5D.isin(recent_vaccine_strains)%2C%20%22recent_vaccine%22%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Validate%20subtypes%0A%20%20%20%20data_subtypes%20%3D%20set(viruses%5B%22subtype%22%5D.unique())%0A%20%20%20%20param_subtypes%20%3D%20set(subtypes)%0A%20%20%20%20if%20not%20param_subtypes.issubset(data_subtypes)%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError(%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22subtypes%20param%20%7Bparam_subtypes%7D%20not%20all%20in%20viruses%20data.%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22Available%20subtypes%3A%20%7Bdata_subtypes%7D%22%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%23%20Compute%20strain%20plot%20order%0A%20%20%20%20viruses_sorted%20%3D%20viruses.sort_values(%0A%20%20%20%20%20%20%20%20by%3D%5B%22subclade%22%2C%20%22virus%22%5D%2C%0A%20%20%20%20%20%20%20%20key%3Dlambda%20col%3A%20col.fillna(%22zzz%22)%20if%20col.name%20%3D%3D%20%22subclade%22%20else%20col%2C%0A%20%20%20%20)%0A%20%20%20%20viral_strain_plot_order%20%3D%20viruses_sorted%5B%22virus%22%5D.tolist()%0A%20%20%20%20assert%20set(viruses%5B%22virus%22%5D)%20%3D%3D%20set(viral_strain_plot_order)%0A%0A%20%20%20%20%23%20Merge%20virus%20metadata%20into%20paired_titers%0A%20%20%20%20virus_cols%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%22virus%22%2C%0A%20%20%20%20%20%20%20%20%22subtype%22%2C%0A%20%20%20%20%20%20%20%20%22strain_type%22%2C%0A%20%20%20%20%20%20%20%20%22subclade%22%2C%0A%20%20%20%20%20%20%20%20%22derived_haplotype%22%2C%0A%20%20%20%20%20%20%20%20%22vaccine_type%22%2C%0A%20%20%20%20%5D%0A%20%20%20%20paired_titers_full%20%3D%20paired_titers.merge(%0A%20%20%20%20%20%20%20%20viruses%5Bvirus_cols%5D%2C%20on%3D%22virus%22%2C%20how%3D%22left%22%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Validate%20all%20viruses%20have%20metadata%0A%20%20%20%20missing_virus_meta%20%3D%20paired_titers_full%5B%22subtype%22%5D.isna().sum()%0A%20%20%20%20if%20missing_virus_meta%20%3E%200%3A%0A%20%20%20%20%20%20%20%20missing%20%3D%20set(paired_titers%5B%22virus%22%5D)%20-%20set(viruses%5B%22virus%22%5D)%0A%20%20%20%20%20%20%20%20raise%20ValueError(f%22Viruses%20in%20titers%20but%20not%20in%20viruses_csv%3A%20%7Bmissing%7D%22)%0A%0A%20%20%20%20mo.output.append(%0A%20%20%20%20%20%20%20%20mo.md(f%22%5CnComputed%20strain%20plot%20order%3A%20%7Blen(viral_strain_plot_order)%7D%20viruses%22)%0A%20%20%20%20)%0A%20%20%20%20return%20paired_titers_full%2C%20viral_strain_plot_order%2C%20viruses%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Build%20charts%0A%0A%20%20%20%20%23%23%23%20Assign%20label%20colors%20by%20subclade%20%2F%20vaccine%20type%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20circulating_strain_type%2C%0A%20%20%20%20json%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20pd%2C%0A%20%20%20%20plot_titer_summaries_params%2C%0A%20%20%20%20viral_strain_plot_order%2C%0A%20%20%20%20viruses%2C%0A)%3A%0A%20%20%20%20strain_color_prop%20%3D%20viruses.assign(%0A%20%20%20%20%20%20%20%20strain%3Dlambda%20x%3A%20pd.Categorical(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%5B%22virus%22%5D%2C%20viral_strain_plot_order%2C%20ordered%3DTrue%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20color_prop%3Dlambda%20x%3A%20x%5B%22subclade%22%5D.where(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%5B%22strain_type%22%5D%20%3D%3D%20circulating_strain_type%2C%20x%5B%22vaccine_type%22%5D%20%2B%20%22%20vaccine%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20).sort_values(%22strain%22)%0A%0A%20%20%20%20assert%20strain_color_prop%5B%22color_prop%22%5D.notnull().all()%0A%20%20%20%20assert%20set(viruses%5B%22virus%22%5D)%20%3D%3D%20set(strain_color_prop%5B%22strain%22%5D)%0A%0A%20%20%20%20viruses%5B%22color_prop%22%5D%20%3D%20viruses%5B%22virus%22%5D.map(%0A%20%20%20%20%20%20%20%20strain_color_prop.set_index(%22strain%22)%5B%22color_prop%22%5D.to_dict()%0A%20%20%20%20)%0A%0A%20%20%20%20prop_colors%20%3D%20dict(plot_titer_summaries_params%5B%22prop_colors%22%5D)%0A%20%20%20%20other_prop_colors%20%3D%20plot_titer_summaries_params%5B%22other_prop_colors%22%5D%0A%0A%20%20%20%20for%20_subtype%20in%20strain_color_prop%5B%22subtype%22%5D.unique()%3A%0A%20%20%20%20%20%20%20%20subtype_color_props%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20strain_color_prop%5Bstrain_color_prop%5B%22subtype%22%5D%20%3D%3D%20_subtype%5D%5B%22color_prop%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20.unique()%0A%20%20%20%20%20%20%20%20%20%20%20%20.tolist()%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20props_not_yet_colored%20%3D%20%5Bp%20for%20p%20in%20subtype_color_props%20if%20p%20not%20in%20prop_colors%5D%0A%20%20%20%20%20%20%20%20if%20len(props_not_yet_colored)%20%3E%20len(other_prop_colors)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22props_not_yet_colored%3D%7Bprops_not_yet_colored!r%7D%20longer%20than%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22other_prop_colors%3D%7Bother_prop_colors!r%7D%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20prop_colors.update(dict(zip(props_not_yet_colored%2C%20other_prop_colors)))%0A%0A%20%20%20%20mo.output.append(%0A%20%20%20%20%20%20%20%20pd.Series(prop_colors).rename(%22color%22).rename_axis(%22property%22).to_frame()%0A%20%20%20%20)%0A%20%20%20%20assert%20set(strain_color_prop%5B%22color_prop%22%5D).issubset(prop_colors)%0A%0A%20%20%20%20strain_color_prop%20%3D%20strain_color_prop.assign(%0A%20%20%20%20%20%20%20%20color%3Dlambda%20x%3A%20x%5B%22color_prop%22%5D.map(prop_colors)%0A%20%20%20%20)%0A%0A%20%20%20%20color_mapping%20%3D%20strain_color_prop.set_index(%22virus%22)%5B%22color%22%5D.to_dict()%0A%20%20%20%20labelColor_expr%20%3D%20f%22(%7Bjson.dumps(color_mapping)%7D)%5Bdatum.label%5D%20%7C%7C%20'black'%22%0A%0A%20%20%20%20label_mapping%20%3D%20%7B%0A%20%20%20%20%20%20%20%20row%5B%22virus%22%5D%3A%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20row%5B%22derived_haplotype%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20pd.notna(row%5B%22derived_haplotype%22%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20else%20row%5B%22virus%22%5D.rsplit(%22_%22%2C%201)%5B0%5D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20for%20_%2C%20row%20in%20viruses.iterrows()%0A%20%20%20%20%7D%0A%20%20%20%20labelText_expr%20%3D%20f%22(%7Bjson.dumps(label_mapping)%7D)%5Bdatum.label%5D%20%7C%7C%20datum.label%22%0A%20%20%20%20return%20labelColor_expr%2C%20labelText_expr%2C%20strain_color_prop%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Make%20charts%20with%20titer%20%2B%20fold-change%20panels%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20alt%2C%0A%20%20%20%20baseline_label%2C%0A%20%20%20%20chart_htmls%2C%0A%20%20%20%20circulating_strain_type%2C%0A%20%20%20%20cohorts_config%2C%0A%20%20%20%20condition_colors%2C%0A%20%20%20%20facet_orientation%2C%0A%20%20%20%20fold_change_title%2C%0A%20%20%20%20itertools%2C%0A%20%20%20%20labelColor_expr%2C%0A%20%20%20%20labelText_expr%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20paired_titers_full%2C%0A%20%20%20%20pd%2C%0A%20%20%20%20plot_titer_summaries_params%2C%0A%20%20%20%20strain_color_prop%2C%0A%20%20%20%20subtypes%2C%0A%20%20%20%20viral_strain_plot_order%2C%0A%20%20%20%20viruses%2C%0A)%3A%0A%20%20%20%20facet_size%20%3D%20plot_titer_summaries_params%5B%22facet_size%22%5D%0A%20%20%20%20titer_lower_limit%20%3D%20plot_titer_summaries_params%5B%22titer_lower_limit%22%5D%0A%0A%20%20%20%20if%20facet_orientation%20not%20in%20%7B%22vertical%22%2C%20%22horizontal%22%7D%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError(%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22facet_orientation%20must%20be%20'vertical'%20or%20'horizontal'%2C%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22got%20%7Bfacet_orientation!r%7D%22%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%23%20Condition%20color%20scale%0A%20%20%20%20condition_domain%20%3D%20list(cohorts_config.values())%0A%20%20%20%20condition_range%20%3D%20list(condition_colors)%0A%0A%20%20%20%20%23%20Titer%20scale%20(log)%0A%20%20%20%20titer_scale%20%3D%20alt.Scale(%0A%20%20%20%20%20%20%20%20type%3D%22log%22%2C%20nice%3DFalse%2C%20domainMin%3Dtiter_lower_limit%2C%20padding%3D4%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Fold-change%20scale%20(log)%0A%20%20%20%20fold_change_scale%20%3D%20alt.Scale(type%3D%22log%22%2C%20nice%3DFalse%2C%20padding%3D4)%0A%0A%20%20%20%20%23%20Shared%20virus%20axis%20encoding%0A%20%20%20%20virus_sort%20%3D%20list(reversed(viral_strain_plot_order))%0A%20%20%20%20virus_axis%20%3D%20alt.Axis(%0A%20%20%20%20%20%20%20%20labelLimit%3D500%2C%0A%20%20%20%20%20%20%20%20labelColor%3D%7B%22expr%22%3A%20labelColor_expr%7D%2C%0A%20%20%20%20%20%20%20%20labelFontWeight%3D600%2C%0A%20%20%20%20%20%20%20%20labelExpr%3DlabelText_expr%2C%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Mouseover%20selections%20for%20cross-panel%20highlighting%20(added%20to%20concatenated%20chart)%0A%20%20%20%20virus_selection%20%3D%20alt.selection_point(%0A%20%20%20%20%20%20%20%20fields%3D%5B%22virus%22%5D%2C%20on%3D%22mouseover%22%2C%20empty%3DFalse%2C%20clear%3D%22mouseout%22%2C%20nearest%3DFalse%0A%20%20%20%20)%0A%20%20%20%20subject_selection%20%3D%20alt.selection_point(%0A%20%20%20%20%20%20%20%20fields%3D%5B%22subject_id%22%5D%2C%0A%20%20%20%20%20%20%20%20on%3D%22mouseover%22%2C%0A%20%20%20%20%20%20%20%20empty%3DFalse%2C%0A%20%20%20%20%20%20%20%20clear%3D%22mouseout%22%2C%0A%20%20%20%20%20%20%20%20nearest%3DFalse%2C%0A%20%20%20%20)%0A%20%20%20%20color_prop_selection%20%3D%20alt.selection_point(%0A%20%20%20%20%20%20%20%20fields%3D%5B%22color_prop%22%5D%2C%20bind%3D%22legend%22%2C%20empty%3D%22all%22%2C%20toggle%3D%22true%22%2C%20clear%3DFalse%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Age%20sliders%0A%20%20%20%20max_age%20%3D%205%20*%20int(paired_titers_full%5B%22age_numeric%22%5D.max()%20%2F%2F%205)%20%2B%205%0A%20%20%20%20min_age_slider%20%3D%20alt.param(%0A%20%20%20%20%20%20%20%20value%3D0%2C%0A%20%20%20%20%20%20%20%20bind%3Dalt.binding_range(%0A%20%20%20%20%20%20%20%20%20%20%20%20min%3D0%2C%20max%3Dmax_age%2C%20step%3D5%2C%20name%3D%22minimum%20subject%20age%20(years)%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20)%0A%20%20%20%20max_age_slider%20%3D%20alt.param(%0A%20%20%20%20%20%20%20%20value%3Dmax_age%2C%0A%20%20%20%20%20%20%20%20bind%3Dalt.binding_range(%0A%20%20%20%20%20%20%20%20%20%20%20%20min%3D0%2C%20max%3Dmax_age%2C%20step%3D5%2C%20name%3D%22maximum%20subject%20age%20(years)%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Condition%20legend%20(clickable)%0A%20%20%20%20condition_selection%20%3D%20alt.selection_point(%0A%20%20%20%20%20%20%20%20fields%3D%5B%22condition%22%5D%2C%20bind%3D%22legend%22%2C%20empty%3D%22all%22%2C%20toggle%3D%22true%22%2C%20clear%3DFalse%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20---%20Build%20and%20save%20charts%20---%0A%20%20%20%20made_chart%20%3D%20%7Bc%3A%20False%20for%20c%20in%20chart_htmls%7D%0A%0A%20%20%20%20for%20_subtype%2C%20strain_type%2C%20(chart_desc%2C%20chart_title)%20in%20itertools.product(%0A%20%20%20%20%20%20%20%20subtypes%2C%0A%20%20%20%20%20%20%20%20%5B%22recent%22%2C%20%22vaccine%22%5D%2C%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22individual_sera%22%2C%20%22per-subject%20(lines)%20and%20median%20(points)%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22interquartile_range%22%2C%20%22median%20(points)%20and%20interquartile%20range%22)%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20filepattern%20%3D%20f%22%7B_subtype%7D_%7Bstrain_type%7D_%7Bchart_desc%7D%22%0A%20%20%20%20%20%20%20%20filename%20%3D%20%5Bc%20for%20c%20in%20chart_htmls%20if%20filepattern%20in%20c%5D%0A%20%20%20%20%20%20%20%20assert%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20len(filename)%20%3D%3D%201%0A%20%20%20%20%20%20%20%20)%2C%20f%22did%20not%20find%20one%20filepattern%3D%7Bfilepattern!r%7D%20in%20chart_htmls%3D%7Bchart_htmls!r%7D%22%0A%20%20%20%20%20%20%20%20filename%20%3D%20filename%5B0%5D%0A%0A%20%20%20%20%20%20%20%20strain_types%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22recent%22%3A%20%5Bcirculating_strain_type%2C%20%22recent_vaccine%22%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22vaccine%22%3A%20%5B%22vaccine%22%2C%20%22recent_vaccine%22%5D%2C%0A%20%20%20%20%20%20%20%20%7D%5Bstrain_type%5D%0A%0A%20%20%20%20%20%20%20%20%23%20Filter%20data%20for%20this%20subtype%20and%20strain_type%0A%20%20%20%20%20%20%20%20chart_data%20%3D%20paired_titers_full%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20(paired_titers_full%5B%22subtype%22%5D%20%3D%3D%20_subtype)%0A%20%20%20%20%20%20%20%20%20%20%20%20%26%20(paired_titers_full%5B%22strain_type%22%5D.isin(strain_types))%0A%20%20%20%20%20%20%20%20%5D.copy()%0A%0A%20%20%20%20%20%20%20%20%23%20Minimal%20chart%20data%20for%20Altair%20(reduces%20serialized%20spec%20size%20via%20transform_lookup)%0A%20%20%20%20%20%20%20%20chart_data_minimal%20%3D%20chart_data%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B%22serum%22%2C%20%22virus%22%2C%20%22titer%22%2C%20%22baseline_titer%22%2C%20%22fold_change%22%5D%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20chart_viruses%20%3D%20viruses%5Bviruses%5B%22virus%22%5D.isin(chart_data%5B%22virus%22%5D.unique())%5D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22virus%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22strain_type%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subclade%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22derived_haplotype%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22vaccine_type%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22color_prop%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20chart_sera%20%3D%20chart_data%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22serum%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subject_id%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22condition%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22age%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22age_numeric%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22sex%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22serum_collection_date%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%5D.drop_duplicates(subset%3D%5B%22serum%22%5D)%0A%0A%20%20%20%20%20%20%20%20if%20len(chart_data)%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.output.append(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mo.md(f%22**Skipping**%20%7B_subtype%7D%20%7Bstrain_type%7D%3A%20no%20data%20after%20filtering%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20chart_data_empty%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Chart(pd.DataFrame(%7B%22x%22%3A%20%5B0%5D%2C%20%22y%22%3A%20%5B0%5D%2C%20%22text%22%3A%20%5B%22No%20data%22%5D%7D))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.mark_text()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.encode(x%3D%22x%3AQ%22%2C%20y%3D%22y%3AQ%22%2C%20text%3D%22text%3AN%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20chart_data_empty.save(filename)%0A%20%20%20%20%20%20%20%20%20%20%20%20made_chart%5Bfilename%5D%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%0A%0A%20%20%20%20%20%20%20%20%23%20Virus%20axis%20encoding%20kwargs%20(shared%20between%20panels)%0A%20%20%20%20%20%20%20%20if%20facet_orientation%20%3D%3D%20%22vertical%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20virus_enc_key%20%3D%20%22y%22%0A%20%20%20%20%20%20%20%20%20%20%20%20titer_enc_key%20%3D%20%22x%22%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20virus_enc_key%20%3D%20%22x%22%0A%20%20%20%20%20%20%20%20%20%20%20%20titer_enc_key%20%3D%20%22y%22%0A%0A%20%20%20%20%20%20%20%20virus_enc_labeled%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20virus_enc_key%3A%20alt.Y(%22virus%3AN%22%2C%20sort%3Dvirus_sort%2C%20axis%3Dvirus_axis)%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20virus_enc_no_labels%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20virus_enc_key%3A%20alt.Y(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22virus%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sort%3Dvirus_sort%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20axis%3Dalt.Axis(labels%3DFalse%2C%20ticks%3DFalse%2C%20title%3DNone)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20step_props%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%22height%22%3A%20alt.Step(11)%2C%20%22width%22%3A%20facet_size%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20facet_orientation%20%3D%3D%20%22vertical%22%0A%20%20%20%20%20%20%20%20%20%20%20%20else%20%7B%22width%22%3A%20alt.Step(11)%2C%20%22height%22%3A%20facet_size%7D%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20%23%20vertical%20(hconcat)%3A%20titer%20left%20with%20labels%2C%20fc%20right%20without%0A%20%20%20%20%20%20%20%20%23%20horizontal%20(vconcat)%3A%20titer%20top%20without%20labels%2C%20fc%20bottom%20with%0A%20%20%20%20%20%20%20%20titer_virus_enc%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20virus_enc_labeled%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20facet_orientation%20%3D%3D%20%22vertical%22%0A%20%20%20%20%20%20%20%20%20%20%20%20else%20virus_enc_no_labels%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20fc_virus_enc%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20virus_enc_no_labels%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20facet_orientation%20%3D%3D%20%22vertical%22%0A%20%20%20%20%20%20%20%20%20%20%20%20else%20virus_enc_labeled%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20%23%20Color%20encoding%20for%20conditions%0A%20%20%20%20%20%20%20%20condition_color_enc%20%3D%20alt.Color(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22condition%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D%22condition%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20scale%3Dalt.Scale(domain%3Dcondition_domain%2C%20range%3Dcondition_range)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20legend%3Dalt.Legend(orient%3D%22bottom%22%2C%20columns%3D6)%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20%23%20Base%20chart%20for%20titer%20panel%0A%20%20%20%20%20%20%20%20titer_base%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20alt.Chart(chart_data_minimal)%0A%20%20%20%20%20%20%20%20%20%20%20%20.transform_lookup(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20lookup%3D%22virus%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20from_%3Dalt.LookupData(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20data%3Dchart_viruses%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20key%3D%22virus%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fields%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22strain_type%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subclade%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22derived_haplotype%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22vaccine_type%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22color_prop%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.transform_lookup(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20lookup%3D%22serum%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20from_%3Dalt.LookupData(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20data%3Dchart_sera%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20key%3D%22serum%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fields%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subject_id%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22condition%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22age%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22age_numeric%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22sex%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22serum_collection_date%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.add_params(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color_prop_selection%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20condition_selection%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20min_age_slider%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20max_age_slider%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.transform_filter(color_prop_selection)%0A%20%20%20%20%20%20%20%20%20%20%20%20.transform_filter(condition_selection)%0A%20%20%20%20%20%20%20%20%20%20%20%20.transform_filter(alt.datum%5B%22age_numeric%22%5D%20%3E%3D%20min_age_slider)%0A%20%20%20%20%20%20%20%20%20%20%20%20.transform_filter(alt.datum%5B%22age_numeric%22%5D%20%3C%3D%20max_age_slider)%0A%20%20%20%20%20%20%20%20%20%20%20%20.encode(**titer_virus_enc)%0A%20%20%20%20%20%20%20%20%20%20%20%20.properties(**step_props)%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20%23%20Base%20chart%20for%20fold-change%20panel%0A%20%20%20%20%20%20%20%20fc_base%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20alt.Chart(chart_data_minimal)%0A%20%20%20%20%20%20%20%20%20%20%20%20.transform_lookup(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20lookup%3D%22virus%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20from_%3Dalt.LookupData(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20data%3Dchart_viruses%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20key%3D%22virus%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fields%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22strain_type%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subclade%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22derived_haplotype%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22vaccine_type%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22color_prop%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.transform_lookup(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20lookup%3D%22serum%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20from_%3Dalt.LookupData(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20data%3Dchart_sera%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20key%3D%22serum%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fields%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subject_id%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22condition%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22age%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22age_numeric%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22sex%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22serum_collection_date%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.transform_filter(f%22datum.condition%20!%3D%3D%20'%7Bbaseline_label%7D'%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20.transform_filter(color_prop_selection)%0A%20%20%20%20%20%20%20%20%20%20%20%20.transform_filter(condition_selection)%0A%20%20%20%20%20%20%20%20%20%20%20%20.transform_filter(alt.datum%5B%22age_numeric%22%5D%20%3E%3D%20min_age_slider)%0A%20%20%20%20%20%20%20%20%20%20%20%20.transform_filter(alt.datum%5B%22age_numeric%22%5D%20%3C%3D%20max_age_slider)%0A%20%20%20%20%20%20%20%20%20%20%20%20.encode(**fc_virus_enc)%0A%20%20%20%20%20%20%20%20%20%20%20%20.properties(**step_props)%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20%23%20Subject%20dropdown%20filter%20(only%20active%20for%20individual_sera%20charts)%0A%20%20%20%20%20%20%20%20if%20chart_desc%20%3D%3D%20%22individual_sera%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20chart_subject_ids%20%3D%20sorted(chart_data%5B%22subject_id%22%5D.unique())%0A%20%20%20%20%20%20%20%20%20%20%20%20subject_dropdown%20%3D%20alt.param(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22subject_dropdown%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20value%3D%22all%20subjects%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bind%3Dalt.binding_select(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20options%3D%5B%22all%20subjects%22%5D%20%2B%20chart_subject_ids%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22subject%20%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20subject_filter_expr%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subject_dropdown%20%3D%3D%20'all%20subjects'%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%7C%7C%20datum.subject_id%20%3D%3D%20subject_dropdown%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20titer_base_filtered%20%3D%20titer_base.transform_filter(subject_filter_expr)%0A%20%20%20%20%20%20%20%20%20%20%20%20fc_base_filtered%20%3D%20fc_base.transform_filter(subject_filter_expr)%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20subject_dropdown%20%3D%20None%0A%20%20%20%20%20%20%20%20%20%20%20%20titer_base_filtered%20%3D%20titer_base%0A%20%20%20%20%20%20%20%20%20%20%20%20fc_base_filtered%20%3D%20fc_base%0A%0A%20%20%20%20%20%20%20%20%23%20---%20Titer%20panel%20median%20points%20---%0A%20%20%20%20%20%20%20%20titer_median%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20titer_base_filtered.transform_aggregate(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20median_titer%3D%22median(titer)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20groupby%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22virus%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22condition%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22derived_haplotype%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subclade%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22strain_type%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20**%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20titer_enc_key%3A%20alt.X(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22median_titer%3AQ%22%2C%20title%3D%22titer%22%2C%20scale%3Dtiter_scale%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3Dcondition_color_enc%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tooltip%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22virus%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22derived_haplotype%3AN%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subclade%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22strain_type%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22condition%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22median_titer%3AQ%22%2C%20format%3D%22.1f%22%2C%20title%3D%22median%20titer%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3Dalt.condition(virus_selection%2C%20alt.value(80)%2C%20alt.value(40))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.mark_circle(opacity%3D1)%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20%23%20---%20Fold-change%20panel%20median%20points%20---%0A%20%20%20%20%20%20%20%20fc_median%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20fc_base_filtered.transform_aggregate(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20median_fold_change%3D%22median(fold_change)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20groupby%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22virus%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22condition%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22derived_haplotype%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subclade%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22strain_type%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20**%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20titer_enc_key%3A%20alt.X(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22median_fold_change%3AQ%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20title%3D%22fold%20change%20in%20titer%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20scale%3Dfold_change_scale%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3Dcondition_color_enc%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tooltip%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22virus%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22derived_haplotype%3AN%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subclade%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22strain_type%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22condition%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22median_fold_change%3AQ%22%2C%20format%3D%22.2f%22%2C%20title%3D%22median%20fold%20change%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3Dalt.condition(virus_selection%2C%20alt.value(80)%2C%20alt.value(40))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.mark_circle(opacity%3D1)%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20%23%20---%20Fold-change%20reference%20line%20at%201.0%20---%0A%20%20%20%20%20%20%20%20if%20facet_orientation%20%3D%3D%20%22vertical%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20fc_ref_line%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Chart(pd.DataFrame(%7B%22val%22%3A%20%5B1.0%5D%7D))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.mark_rule(strokeDash%3D%5B4%2C%204%5D%2C%20color%3D%22gray%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.encode(x%3D%22val%3AQ%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20fc_ref_line%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Chart(pd.DataFrame(%7B%22val%22%3A%20%5B1.0%5D%7D))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.mark_rule(strokeDash%3D%5B4%2C%204%5D%2C%20color%3D%22gray%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.encode(y%3D%22val%3AQ%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20if%20chart_desc%20%3D%3D%20%22individual_sera%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20---%20Titer%20panel%3A%20individual%20lines%20---%0A%20%20%20%20%20%20%20%20%20%20%20%20titer_lines%20%3D%20titer_base_filtered.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20**%7Btiter_enc_key%3A%20alt.X(%22titer%3AQ%22%2C%20scale%3Dtiter_scale)%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20detail%3D%22subject_id%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3Dcondition_color_enc%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tooltip%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22virus%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22derived_haplotype%3AN%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subclade%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22strain_type%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subject_id%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22condition%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22serum%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22titer%3AQ%22%2C%20format%3D%22.1f%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22serum_collection_date%3AN%22%2C%20title%3D%22serum%20date%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22age%3AN%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22sex%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3Dalt.condition(subject_selection%2C%20alt.value(3)%2C%20alt.value(1.5))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20opacity%3Dalt.condition(subject_selection%2C%20alt.value(1)%2C%20alt.value(0.2))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20).mark_line()%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20titer_panel%20%3D%20titer_lines%20%2B%20titer_median%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20---%20Fold-change%20panel%3A%20individual%20lines%20---%0A%20%20%20%20%20%20%20%20%20%20%20%20fc_lines%20%3D%20fc_base_filtered.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20**%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20titer_enc_key%3A%20alt.X(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22fold_change%3AQ%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20title%3D%22fold%20change%20in%20titer%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20scale%3Dfold_change_scale%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20detail%3D%22subject_id%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3Dcondition_color_enc%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tooltip%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22virus%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22derived_haplotype%3AN%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subclade%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22strain_type%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subject_id%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22condition%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22fold_change%3AQ%22%2C%20format%3D%22.2f%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22titer%3AQ%22%2C%20format%3D%22.1f%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22baseline_titer%3AQ%22%2C%20format%3D%22.1f%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20size%3Dalt.condition(subject_selection%2C%20alt.value(3)%2C%20alt.value(1.5))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20opacity%3Dalt.condition(subject_selection%2C%20alt.value(1)%2C%20alt.value(0.2))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20).mark_line()%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20fc_panel%20%3D%20fc_ref_line%20%2B%20fc_lines%20%2B%20fc_median%0A%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20interquartile_range%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20---%20Titer%20panel%3A%20IQR%20bands%20---%0A%20%20%20%20%20%20%20%20%20%20%20%20titer_iqr%20%3D%20titer_base.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20**%7Btiter_enc_key%3A%20alt.X(%22titer%3AQ%22%2C%20scale%3Dtiter_scale)%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3Dcondition_color_enc%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tooltip%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22virus%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22derived_haplotype%3AN%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subclade%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22strain_type%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22condition%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20).mark_errorband(extent%3D%22iqr%22%2C%20opacity%3D0.3%2C%20interpolate%3D%22linear%22)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20titer_panel%20%3D%20titer_iqr%20%2B%20titer_median%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20---%20Fold-change%20panel%3A%20IQR%20bands%20---%0A%20%20%20%20%20%20%20%20%20%20%20%20fc_iqr%20%3D%20fc_base.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20**%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20titer_enc_key%3A%20alt.X(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22fold_change%3AQ%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20title%3D%22fold%20change%20in%20titer%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20scale%3Dfold_change_scale%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3Dcondition_color_enc%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tooltip%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22virus%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(%22derived_haplotype%3AN%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22subclade%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22strain_type%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22condition%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20).mark_errorband(extent%3D%22iqr%22%2C%20opacity%3D0.3%2C%20interpolate%3D%22linear%22)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20fc_panel%20%3D%20fc_ref_line%20%2B%20fc_iqr%20%2B%20fc_median%0A%0A%20%20%20%20%20%20%20%20%23%20---%20Virus%20type%20color%20legend%20---%0A%20%20%20%20%20%20%20%20plotted_colors%20%3D%20strain_color_prop%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20(strain_color_prop%5B%22subtype%22%5D%20%3D%3D%20_subtype)%0A%20%20%20%20%20%20%20%20%20%20%20%20%26%20(strain_color_prop%5B%22strain_type%22%5D.isin(strain_types))%0A%20%20%20%20%20%20%20%20%5D%5B%5B%22color_prop%22%2C%20%22color%22%5D%5D.drop_duplicates()%0A%0A%20%20%20%20%20%20%20%20label_color_legend%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20alt.Chart(plotted_colors)%0A%20%20%20%20%20%20%20%20%20%20%20%20.add_params(color_prop_selection)%0A%20%20%20%20%20%20%20%20%20%20%20%20.mark_point(opacity%3D0)%0A%20%20%20%20%20%20%20%20%20%20%20%20.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fill%3Dalt.Fill(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22color_prop%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20title%3D%22virus%20type%20(click%20to%20select)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20scale%3Dalt.Scale(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20domain%3Dlist(reversed(plotted_colors%5B%22color_prop%22%5D.tolist()))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20range%3Dlist(reversed(plotted_colors%5B%22color%22%5D.tolist()))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20legend%3Dalt.Legend(symbolType%3D%22square%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.properties(width%3D1%2C%20height%3D1)%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20%23%20---%20Assemble%20full%20chart%20---%0A%20%20%20%20%20%20%20%20%23%20Concatenate%20titer%20and%20fold-change%20panels%20sharing%20the%20virus%20axis.%0A%20%20%20%20%20%20%20%20%23%20vertical%20orientation%3A%20viruses%20on%20Y%20%E2%86%92%20hconcat%20(side-by-side)%20shares%20Y%20axis%0A%20%20%20%20%20%20%20%20%23%20horizontal%20orientation%3A%20viruses%20on%20X%20%E2%86%92%20vconcat%20(stacked)%20shares%20X%20axis%0A%20%20%20%20%20%20%20%20%23%20Mouseover%20selections%20added%20here%20so%20they%20work%20bidirectionally%20across%20panels.%0A%20%20%20%20%20%20%20%20concat_params%20%3D%20%5Bvirus_selection%2C%20subject_selection%5D%0A%20%20%20%20%20%20%20%20if%20subject_dropdown%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20concat_params.append(subject_dropdown)%0A%0A%20%20%20%20%20%20%20%20if%20facet_orientation%20%3D%3D%20%22vertical%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20panels_concat%20%3D%20alt.hconcat(titer_panel%2C%20fc_panel%2C%20spacing%3D5).add_params(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20*concat_params%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20panels_concat%20%3D%20alt.vconcat(titer_panel%2C%20fc_panel%2C%20spacing%3D5).add_params(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20*concat_params%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20chart%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20alt.vconcat(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20panels_concat%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20label_color_legend%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20spacing%3D5%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.resolve_scale(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3D%22shared%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fill%3D%22independent%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.configure_axis(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20grid%3DFalse%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20titleFontWeight%3D%22normal%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20titleFontSize%3D13%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20labelOverlap%3DTrue%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.configure_view(stroke%3D%22black%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20.configure_legend(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20labelFontSize%3D12%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20titleFontSize%3D13%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20symbolStrokeWidth%3D1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20symbolOpacity%3D1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20symbolStrokeColor%3D%22black%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20columns%3D12%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20orient%3D%22bottom%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.properties(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20title%3Dalt.TitleParams(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%7Bfold_change_title%7D%3A%20%7B_subtype%7D%20%7Bstrain_type%7D%20strains%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20subtitle%3Dchart_title%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20anchor%3D%22middle%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fontSize%3D13%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20if%20not%20any(made_chart.values())%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.output.append(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%22Displaying%20just%20the%20first%20chart%20here%20(since%20they%20are%20large).%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.output.append(chart)%0A%0A%20%20%20%20%20%20%20%20mo.output.append(mo.md(f%22Saving%20to%20filename%3D%7Bfilename!r%7D%5Cn%22))%0A%20%20%20%20%20%20%20%20chart.save(filename)%0A%0A%20%20%20%20%20%20%20%20made_chart%5Bfilename%5D%20%3D%20True%0A%0A%20%20%20%20assert%20all(made_chart.values())%2C%20f%22made_chart%3D%7Bmade_chart!r%7D%22%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
1d56ef2282c1f55d6efd9ccabc56e4f608d721cd017cdad3f1ae897ed493366e