Skip to content

Commit

Permalink
report titers out of bounds of dilution series (#38)
Browse files Browse the repository at this point in the history
* plots in `serum_titers` notebook indicates titers outside dilution series range

* `aggregate_titers` plots titers at bounds

* re-run pipeline
  • Loading branch information
jbloom authored Mar 26, 2024
1 parent 67a4328 commit 67c6544
Show file tree
Hide file tree
Showing 13 changed files with 402 additions and 394 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
- In `process_plate_curvefit_qc` in the YAML configuration, there is a new key called `goodness_of_fit` and now both `min_R2` (the minimum coefficient of determination) and `max_RMSD` (the maximum mean square deviation) for each curve fit are specified as keys under that. The curves are then filtered to retain only those that meet *either* of these criteria (so must fail both to be dropped). Addresses [this issue](https://github.com/jbloomlab/seqneut-pipeline/issues/33) and [this issue](https://github.com/jbloomlab/neutcurve/issues/55#issuecomment-2016975219). Alongside this change, the `rmsd` is now reported in key output files. Also, in the tabulation of failures, `fails_min_R2` now becomes `fails_goodness_of_fit`.
- This is a **backward-incompatible change** in the configuration YAML. Previously `min_R2` was a standalone key under `process_plate_curvefit_qc`; now `goodness_of_fit` is the required key and `min_R2` and `max_RMSD` are required keys under it.

- Handle titers that are outside the range of the dilutions series by reporting them as upper or lower bounds rather than as interpolated, and marking them appropriately on plots. This change helps with low potency or high potency sera, where there may be no neutralization or high neutralization at all tested concentrations. Addresses [this issue](https://github.com/jbloomlab/seqneut-pipeline/issues/30).

- Added another plate (of H3N2 rather than H1N1) to the `test_example` to test some of the changes introduced in this version.

- Update `seqneut-pipeline` conda environment in `environment.yml`. Update `neutcurve` 2.0.1, also update other packages (`pandas`, `snakemake`, `markdown`, `papermill`) to latest versions.
Expand Down
45 changes: 23 additions & 22 deletions docs/A23038d0_r32_75K_titers.html

Large diffs are not rendered by default.

45 changes: 23 additions & 22 deletions docs/M099d0_titers.html

Large diffs are not rendered by default.

45 changes: 23 additions & 22 deletions docs/M099d30_titers.html

Large diffs are not rendered by default.

45 changes: 23 additions & 22 deletions docs/Y044d30_titers.html

Large diffs are not rendered by default.

45 changes: 23 additions & 22 deletions docs/Y154d182_titers.html

Large diffs are not rendered by default.

52 changes: 26 additions & 26 deletions docs/aggregate_qc_drops.html

Large diffs are not rendered by default.

160 changes: 80 additions & 80 deletions docs/process_H3N2_plate.html

Large diffs are not rendered by default.

168 changes: 81 additions & 87 deletions docs/process_plate11.html

Large diffs are not rendered by default.

160 changes: 80 additions & 80 deletions docs/process_plate2.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/titers.html

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions notebooks/aggregate_titers.py.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
" toggle=\"true\",\n",
")\n",
"\n",
"ncols = 8\n",
"ncols = min(8, titers[\"serum\"].nunique())\n",
"\n",
"titers_chart = (\n",
" alt.Chart(titers)\n",
Expand Down Expand Up @@ -144,6 +144,11 @@
" title=\"serum (click to select)\",\n",
" ),\n",
" ),\n",
" alt.Shape(\n",
" \"titer_bound\",\n",
" title=\"titer interpolated or at dilution bounds\",\n",
" legend=alt.Legend(orient=\"bottom\", titleLimit=200),\n",
" ),\n",
" color=alt.condition(virus_selection, alt.value(\"red\"), alt.value(\"black\")),\n",
" tooltip=[\n",
" alt.Tooltip(c, format=\".3g\") if titers[c].dtype == float else c\n",
Expand All @@ -152,6 +157,7 @@
" )\n",
" .mark_line(point=True)\n",
" .configure_axis(grid=False)\n",
" .configure_legend(padding=10, labelOffset=2, columnPadding=8, labelLimit=400)\n",
" .configure_point(size=45)\n",
" .properties(\n",
" height=alt.Step(11),\n",
Expand All @@ -176,7 +182,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "27ff41ab-c967-42a9-adb1-5856810c7f34",
"id": "d97b8038-6d77-4786-9955-32b578aba143",
"metadata": {},
"outputs": [],
"source": []
Expand All @@ -198,7 +204,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
"version": "3.11.8"
}
},
"nbformat": 4,
Expand Down
15 changes: 8 additions & 7 deletions notebooks/serum_titers.py.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@
" .encode(\n",
" alt.X(\"nt50\", scale=alt.Scale(type=\"log\", nice=False, padding=8)),\n",
" alt.Y(\"midpoint\", scale=alt.Scale(type=\"log\", nice=False, padding=8)),\n",
" alt.Color(\"titer_bound\"),\n",
" strokeWidth=alt.condition(virus_selection, alt.value(3), alt.value(0)),\n",
" size=alt.condition(virus_selection, alt.value(100), alt.value(60)),\n",
" tooltip=[\n",
Expand All @@ -201,7 +202,7 @@
" if c not in {\"serum\", \"titer_as\"}\n",
" ],\n",
" )\n",
" .mark_circle(stroke=\"red\", fillOpacity=0.45, color=\"black\")\n",
" .mark_circle(stroke=\"black\", fillOpacity=0.45, color=\"black\")\n",
" .properties(\n",
" width=350,\n",
" height=350,\n",
Expand Down Expand Up @@ -367,15 +368,15 @@
" title=f\"fails {qc_thresholds['min_replicates']=}, {qc_thresholds['max_fold_change_from_median']=}\",\n",
" legend=alt.Legend(titleLimit=500),\n",
" ),\n",
" alt.Shape(\"titer_bound\"),\n",
" strokeWidth=alt.condition(virus_selection, alt.value(2), alt.value(0)),\n",
" tooltip=[\n",
" alt.Tooltip(c, format=\".3g\") if per_rep_titers_w_fc[c].dtype == float else c\n",
" for c in per_rep_titers_w_fc\n",
" ],\n",
" )\n",
" .mark_point(\n",
" shape=\"circle\",\n",
" size=40,\n",
" size=35,\n",
" filled=True,\n",
" fillOpacity=0.5,\n",
" strokeOpacity=1,\n",
Expand All @@ -389,14 +390,14 @@
" alt.X(\"titer\", scale=alt.Scale(nice=False, padding=5, type=\"log\")),\n",
" alt.Y(\"virus\", sort=viruses),\n",
" alt.Fill(\"fails_qc\"),\n",
" strokeWidth=alt.condition(virus_selection, alt.value(2), alt.value(0)),\n",
" alt.Shape(\"titer_bound\"),\n",
" strokeWidth=alt.condition(virus_selection, alt.value(2), alt.value(0.5)),\n",
" tooltip=[\n",
" alt.Tooltip(c, format=\".3g\") if median_titers_noqc[c].dtype == float else c\n",
" for c in median_titers_noqc\n",
" ],\n",
" )\n",
" .mark_point(\n",
" shape=\"square\",\n",
" size=75,\n",
" filled=True,\n",
" fillOpacity=0.9,\n",
Expand All @@ -411,7 +412,7 @@
" .properties(\n",
" height=alt.Step(11),\n",
" width=250,\n",
" title=f\"{serum} median (square) and per-replicate (small circle) titers\",\n",
" title=f\"{serum} median (large points) and per-replicate (small points) titers\",\n",
" )\n",
" .configure_axis(grid=False)\n",
")\n",
Expand Down Expand Up @@ -619,7 +620,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
"version": "3.11.8"
}
},
"nbformat": 4,
Expand Down

0 comments on commit 67c6544

Please sign in to comment.