qpictures 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qpictures-0.1.0/CHANGELOG.md +45 -0
- qpictures-0.1.0/LICENSE +21 -0
- qpictures-0.1.0/MANIFEST.in +17 -0
- qpictures-0.1.0/PKG-INFO +383 -0
- qpictures-0.1.0/README.md +340 -0
- qpictures-0.1.0/pyproject.toml +77 -0
- qpictures-0.1.0/qpictures/__init__.py +47 -0
- qpictures-0.1.0/qpictures/analysis.py +966 -0
- qpictures-0.1.0/qpictures/cli.py +770 -0
- qpictures-0.1.0/qpictures/io.py +157 -0
- qpictures-0.1.0/qpictures/plot.py +1082 -0
- qpictures-0.1.0/qpictures/py.typed +0 -0
- qpictures-0.1.0/qpictures.egg-info/PKG-INFO +383 -0
- qpictures-0.1.0/qpictures.egg-info/SOURCES.txt +17 -0
- qpictures-0.1.0/qpictures.egg-info/dependency_links.txt +1 -0
- qpictures-0.1.0/qpictures.egg-info/entry_points.txt +2 -0
- qpictures-0.1.0/qpictures.egg-info/requires.txt +15 -0
- qpictures-0.1.0/qpictures.egg-info/top_level.txt +1 -0
- qpictures-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to **qPiCtuRes** are documented here. This project
|
|
4
|
+
follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and the
|
|
5
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format.
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-06-10
|
|
10
|
+
|
|
11
|
+
Initial public release.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `qpictures inspect` — quick per-sample Cq summary of a Thermo QuantStudio
|
|
16
|
+
Design & Analysis "Well Results" CSV.
|
|
17
|
+
- `qpictures relative` — relative quantification (Livak ΔCq) against a
|
|
18
|
+
single reference sample, with asymmetric error bars on a log y-axis,
|
|
19
|
+
optional Welch / Student / Mann–Whitney significance testing, optional
|
|
20
|
+
multiple-testing correction (BH / Holm / Bonferroni), and an optional
|
|
21
|
+
background-control threshold.
|
|
22
|
+
- `qpictures paired` — per-condition paired RQ plot. Splits sample names
|
|
23
|
+
on a separator (e.g. `BB_pbs` → base=`BB`, condition=`pbs`) and
|
|
24
|
+
normalizes every base in each condition against a reference base in the
|
|
25
|
+
same condition. Adds visual grouping cues (alternating shading,
|
|
26
|
+
reference-bar hatching, per-group `vs <ref_sample>` x-tick labels).
|
|
27
|
+
- `qpictures standcurve` — absolute quantification via an OLS standard
|
|
28
|
+
curve (`Cq = slope · log10(Q) + intercept`). Reports slope, intercept,
|
|
29
|
+
R², PCR efficiency, residual SD; back-calculates per-sample quantities
|
|
30
|
+
with error bars propagated from Cq SD; flags extrapolations outside the
|
|
31
|
+
calibrated standard range. Combined HTML stacks the curve diagnostic
|
|
32
|
+
with the per-sample bar plot.
|
|
33
|
+
- Python API: `read_thermo_well_results`, `relative_quantification`,
|
|
34
|
+
`paired_relative_quantification`, `fit_standard_curve`,
|
|
35
|
+
`absolute_quantification`, plus matching plot builders
|
|
36
|
+
(`relative_quantity_barplot`, `paired_relative_quantity_barplot`,
|
|
37
|
+
`standard_curve_plot`, `absolute_quantity_barplot`,
|
|
38
|
+
`standard_curve_with_quantities_figure`).
|
|
39
|
+
- Optional static-image export (PDF / PNG / SVG) for every plotting
|
|
40
|
+
command via Plotly + `kaleido` (installed by `pip install qpictures[export]`).
|
|
41
|
+
- Tolerant CSV parser that auto-skips the Design & Analysis metadata
|
|
42
|
+
preamble and re-glues sample names containing unquoted commas.
|
|
43
|
+
|
|
44
|
+
[Unreleased]: https://github.com/SemiQuant/qPiCtuRes/compare/v0.1.0...HEAD
|
|
45
|
+
[0.1.0]: https://github.com/SemiQuant/qPiCtuRes/releases/tag/v0.1.0
|
qpictures-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jason Limberis (SemiQuant) <JasonLimberis@ucsf.edu>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
include README.md
|
|
2
|
+
include LICENSE
|
|
3
|
+
include CHANGELOG.md
|
|
4
|
+
include pyproject.toml
|
|
5
|
+
include qpictures/py.typed
|
|
6
|
+
|
|
7
|
+
# Source distribution should not ship local outputs, raw data, or env files.
|
|
8
|
+
prune data
|
|
9
|
+
prune results
|
|
10
|
+
prune build
|
|
11
|
+
prune dist
|
|
12
|
+
prune .github
|
|
13
|
+
prune .venv
|
|
14
|
+
recursive-exclude qpictures __pycache__
|
|
15
|
+
recursive-exclude qpictures *.py[cod]
|
|
16
|
+
global-exclude .DS_Store
|
|
17
|
+
global-exclude *.egg-info
|
qpictures-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: qpictures
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Interactive plots and analyses for qPCR data exported from Thermo QuantStudio Design & Analysis.
|
|
5
|
+
Author-email: "SemiQuant (Jason Limberis)" <JasonLimberis@ucsf.edu>
|
|
6
|
+
Maintainer-email: "SemiQuant (Jason Limberis)" <JasonLimberis@ucsf.edu>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/SemiQuant/qPiCtuRes
|
|
9
|
+
Project-URL: Source, https://github.com/SemiQuant/qPiCtuRes
|
|
10
|
+
Project-URL: Issues, https://github.com/SemiQuant/qPiCtuRes/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/SemiQuant/qPiCtuRes/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: qpcr,rt-qpcr,plotly,bioinformatics,thermo,quantstudio,relative-quantification,standard-curve,absolute-quantification
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: pandas>=2.0
|
|
30
|
+
Requires-Dist: numpy>=1.24
|
|
31
|
+
Requires-Dist: scipy>=1.10
|
|
32
|
+
Requires-Dist: plotly>=5.20
|
|
33
|
+
Requires-Dist: click>=8.1
|
|
34
|
+
Requires-Dist: rich>=13.0
|
|
35
|
+
Provides-Extra: export
|
|
36
|
+
Requires-Dist: kaleido>=0.2.1; extra == "export"
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
39
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
41
|
+
Requires-Dist: kaleido>=0.2.1; extra == "dev"
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
|
|
44
|
+
# qPiCtuRes
|
|
45
|
+
|
|
46
|
+
[](https://pypi.org/project/qpictures/)
|
|
47
|
+
[](https://pypi.org/project/qpictures/)
|
|
48
|
+
[](LICENSE)
|
|
49
|
+
|
|
50
|
+
Interactive plots and analyses for qPCR data exported from Thermo Fisher
|
|
51
|
+
QuantStudio Design & Analysis.
|
|
52
|
+
|
|
53
|
+
The first module turns a *Well Results* CSV into an interactive Plotly bar plot
|
|
54
|
+
of **relative quantities** (RQ = 2<sup>−ΔCq</sup>) against a chosen reference
|
|
55
|
+
sample, with log-10 y-axis and asymmetric error bars propagated from the Cq
|
|
56
|
+
SDs (Livak & Schmittgen, 2008).
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
### From PyPI (end users)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install qpictures
|
|
64
|
+
# with optional static-image export (PDF / PNG / SVG via kaleido)
|
|
65
|
+
pip install "qpictures[export]"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### From source (developers)
|
|
69
|
+
|
|
70
|
+
The repo ships with an `environment.yml` (Python 3.11) for `micromamba`:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
micromamba env create -f environment.yml
|
|
74
|
+
micromamba activate qpictures
|
|
75
|
+
# the env file already runs `pip install -e .`; if you skipped that:
|
|
76
|
+
pip install -e ".[dev]"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## CLI
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
qpictures inspect <input.csv> # per-sample Cq summary
|
|
83
|
+
qpictures relative <input.csv> -r BB_PBS [-t atpE] # RQ vs one reference sample
|
|
84
|
+
qpictures paired <input.csv> -b BB [-t atpE] # per-condition paired RQ
|
|
85
|
+
qpictures standcurve <input.csv> [-t atpE] # absolute Q from a std curve
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### `relative` — single reference sample
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
qpictures relative data/ex1/WellResult-2026-06-09-144852.csv \
|
|
92
|
+
--reference BB_PBS \
|
|
93
|
+
--out results/ex1_relative.html \
|
|
94
|
+
--csv-out results/ex1_relative.csv
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Useful flags:
|
|
98
|
+
|
|
99
|
+
| Flag | Default | What it does |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| `-r, --reference` | *required* | Reference sample (case-insensitive). |
|
|
102
|
+
| `-t, --target` | auto | Which assay/target to use (auto if only one). |
|
|
103
|
+
| `-o, --out` | `<input>.relative.html` | Output Plotly HTML. |
|
|
104
|
+
| `--csv-out` | *(off)* | Also write the RQ table as CSV. |
|
|
105
|
+
| `--pdf [PATH]` | *(off)* | Also write a PDF (auto-named from `--out` if no path). |
|
|
106
|
+
| `--png [PATH]` | *(off)* | Also write a PNG. |
|
|
107
|
+
| `--svg [PATH]` | *(off)* | Also write an SVG. |
|
|
108
|
+
| `--test` | `welch` | Per-sample significance test vs reference: `welch`, `student`, `mannwhitney`, `none`. |
|
|
109
|
+
| `--padjust` | `none` | Multiple-testing correction: `none`, `bh` (FDR), `holm`, `bonferroni`. |
|
|
110
|
+
| `--no-signif` | off | Hide significance stars on the plot. |
|
|
111
|
+
| `--background-rem [NAME]` | off | Use a named control sample as the background threshold. As a bare flag it looks up a sample called `control` (case-insensitive); pass an explicit name to override, e.g. `--background-rem PBS`. Any sample whose mean Cq exceeds the control's mean Cq is flagged with a `below_background` column and rendered as a dimmed grey bar. The control is **independent of `--reference`**, so you can normalize against one sample and threshold against another. |
|
|
112
|
+
| `--task TEXT` (repeatable) | `Unknown` | Which `Task` values to keep. |
|
|
113
|
+
| `--all-tasks` | off | Keep every `Task` (NTC / standards / etc.). |
|
|
114
|
+
| `--no-log` | log10 on | Linear y-axis instead of log10. |
|
|
115
|
+
| `--no-open` | opens | Do not auto-open the resulting HTML. |
|
|
116
|
+
|
|
117
|
+
The bars show the significance star (e.g. `**`) above the RQ value when a
|
|
118
|
+
test is run. See **Significance testing** below for the star thresholds and
|
|
119
|
+
caveats — the same rules apply in both `relative` and `paired` modes.
|
|
120
|
+
|
|
121
|
+
### `paired` — per-condition paired comparisons
|
|
122
|
+
|
|
123
|
+
For sample names of the form `<base><sep><condition>` (e.g. `BB_pbs`,
|
|
124
|
+
`TL_culture_swab`), normalize every base within each condition against a
|
|
125
|
+
**reference base** (e.g. `BB`). Each condition gets a group on the x-axis,
|
|
126
|
+
bars within a group are colored by base, and the reference base sits at
|
|
127
|
+
RQ = 1 (with its own SD propagated).
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
qpictures paired data/ex1/WellResult-2026-06-09-144852.csv \
|
|
131
|
+
--reference-base BB \
|
|
132
|
+
--out results/ex1_paired.html \
|
|
133
|
+
--csv-out results/ex1_paired.csv
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
| Flag | Default | What it does |
|
|
137
|
+
| --- | --- | --- |
|
|
138
|
+
| `-b, --reference-base` | *required* | Reference base (case-insensitive), e.g. `BB`. |
|
|
139
|
+
| `-s, --separator` | `_` | Split `base<sep>condition` on this. |
|
|
140
|
+
| `-t, --target` | auto | Which assay/target to use. |
|
|
141
|
+
| `-o, --out` | `<input>.paired.html` | Output Plotly HTML. |
|
|
142
|
+
| `--csv-out` | *(off)* | Also write the paired RQ table as CSV. |
|
|
143
|
+
| `--pdf [PATH]` | *(off)* | Also write a PDF (auto-named from `--out` if no path). |
|
|
144
|
+
| `--png [PATH]` | *(off)* | Also write a PNG. |
|
|
145
|
+
| `--svg [PATH]` | *(off)* | Also write an SVG. |
|
|
146
|
+
| `--test` | `welch` | Per-pair significance test: `welch`, `student`, `mannwhitney`, `none`. |
|
|
147
|
+
| `--padjust` | `none` | Multiple-testing correction: `none`, `bh` (FDR), `holm`, `bonferroni`. |
|
|
148
|
+
| `--no-signif` | off | Hide significance stars on the plot. |
|
|
149
|
+
| `--background-rem [NAME]` | off | Use a named control sample as a single global background threshold. Bare flag defaults to a sample called `control`; pass an explicit name (e.g. `--background-rem PBS`) to override. Lookup happens *before* the separator split, so a control sample without `_` (e.g. `Control`) is still found. Any (base, condition) row with a mean Cq above the control's mean Cq is flagged in `below_background` and dimmed in the plot. |
|
|
150
|
+
| `--no-split` | off | Suppress splitting on `--separator`. Every sample is treated as its own base under one synthetic condition (`all`), and `--reference-base` must match a *full* sample name (e.g. `BB_pbs`). Equivalent to a single-reference comparison rendered through the paired layout. |
|
|
151
|
+
| `--task TEXT` (repeatable) | `Unknown` | Which `Task` values to keep. |
|
|
152
|
+
| `--all-tasks` | off | Keep every `Task`. |
|
|
153
|
+
| `--no-log` | log10 on | Linear y-axis. |
|
|
154
|
+
| `--no-open` | opens | Don't auto-open the HTML. |
|
|
155
|
+
|
|
156
|
+
Samples lacking the separator (`Input`, etc.) are skipped with a notice,
|
|
157
|
+
as are any conditions where the reference base is missing.
|
|
158
|
+
|
|
159
|
+
The paired plot adds three visual cues so the comparisons inside each group
|
|
160
|
+
are unambiguous:
|
|
161
|
+
|
|
162
|
+
- **Alternating background bands** behind each condition box each comparison
|
|
163
|
+
unit as a single visual group.
|
|
164
|
+
- **Diagonal hatching** on the reference-base bars marks the baseline within
|
|
165
|
+
every group at a glance.
|
|
166
|
+
- A small **`vs <ref_sample>` label under each x-tick** (e.g. `vs BB_pbs`)
|
|
167
|
+
spells out the specific reference sample that group is being compared
|
|
168
|
+
against.
|
|
169
|
+
|
|
170
|
+
The Python API exposes these as `group_shading`, `mark_reference_pattern`
|
|
171
|
+
and `show_per_group_reference` on `paired_relative_quantity_barplot` (all
|
|
172
|
+
default `True`).
|
|
173
|
+
|
|
174
|
+
#### Example: `--background-rem` and `--no-split`
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# Bare flag: look up a sample called "control" and use its mean Cq as the threshold
|
|
178
|
+
qpictures paired data/ex1/WellResult-2026-06-09-14570.csv \
|
|
179
|
+
--reference-base BB --background-rem
|
|
180
|
+
|
|
181
|
+
# Explicit control sample (case-insensitive)
|
|
182
|
+
qpictures paired data/ex1/WellResult-2026-06-09-14570.csv \
|
|
183
|
+
--reference-base BB --background-rem PBS
|
|
184
|
+
|
|
185
|
+
# Don't split: use BB_pbs as a single reference, every other sample compared to it
|
|
186
|
+
qpictures paired data/ex1/WellResult-2026-06-09-14570.csv \
|
|
187
|
+
--reference-base BB_pbs --no-split --background-rem Control
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
#### Significance testing
|
|
191
|
+
|
|
192
|
+
For each non-reference (base, condition) pair we run a two-sided test on the
|
|
193
|
+
**raw `Cq` values** (Cq is already log-scaled, so a t-test on Cq is the
|
|
194
|
+
conventional choice). Multiple-testing correction across all non-reference
|
|
195
|
+
pairs is **off by default**; opt in with `--padjust bh` (FDR), `holm`, or
|
|
196
|
+
`bonferroni`:
|
|
197
|
+
|
|
198
|
+
| Threshold (adjusted p) | Star |
|
|
199
|
+
| --- | --- |
|
|
200
|
+
| p < 1e-4 | `****` |
|
|
201
|
+
| p < 1e-3 | `***` |
|
|
202
|
+
| p < 1e-2 | `**` |
|
|
203
|
+
| p < 0.05 | `*` |
|
|
204
|
+
| else | `ns` |
|
|
205
|
+
|
|
206
|
+
Output table columns: `p_value`, `p_adj`, `signif`. The star is drawn above
|
|
207
|
+
the RQ value inside each non-reference bar.
|
|
208
|
+
|
|
209
|
+
**Caveat:** typical qPCR designs have very few replicates (often `n = 3–4`),
|
|
210
|
+
so these p-values have low statistical power. Treat them as a guide, not a
|
|
211
|
+
verdict. With `n=2`, no test can run and `p_value` will be `NaN`.
|
|
212
|
+
|
|
213
|
+
### `standcurve` — absolute quantification from a standard curve
|
|
214
|
+
|
|
215
|
+
For runs that include a serial dilution of a known quantity (e.g. `DNASD_4`,
|
|
216
|
+
`DNASD_6`, … with `Task == Standard` and a `Quantity` column), fit
|
|
217
|
+
|
|
218
|
+
```text
|
|
219
|
+
Cq = slope · log10(Quantity) + intercept
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
and back-calculate the **absolute Quantity** for every `Unknown` sample.
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
qpictures standcurve data/sc_well_table_results-2026-06-10-092019.csv \
|
|
226
|
+
--out results/std.html \
|
|
227
|
+
--csv-out results/std.csv \
|
|
228
|
+
--curve-csv results/std_curve.csv \
|
|
229
|
+
--unit "copies/µL"
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
The output HTML stacks two interactive panels:
|
|
233
|
+
|
|
234
|
+
1. **Standard curve**: scatter of the standard replicates plus the OLS fit,
|
|
235
|
+
a ± 1 residual-SD band, and an annotation box summarising
|
|
236
|
+
`slope`, `intercept`, `R²`, `efficiency = 10^(-1/slope) - 1`,
|
|
237
|
+
`slope SE`, `residual SD`, `n` and number of levels. Unknown samples are
|
|
238
|
+
overlaid at their `(log10 Q_hat, Cq_mean)` so you can see whether each one
|
|
239
|
+
falls inside the calibrated range.
|
|
240
|
+
2. **Absolute-quantity bar plot**: one bar per sample, sorted by quantity,
|
|
241
|
+
with asymmetric error bars from `SD(Cq) / |slope|` propagated through the
|
|
242
|
+
curve. Bars whose mean Cq lies *outside* the standard range are recolored
|
|
243
|
+
(`extrapolated == True`); dotted horizontal guide lines mark the
|
|
244
|
+
calibrated min/max.
|
|
245
|
+
|
|
246
|
+
| Flag | Default | What it does |
|
|
247
|
+
| --- | --- | --- |
|
|
248
|
+
| `-t, --target` | auto | Which assay/target to fit (auto if only one is present). |
|
|
249
|
+
| `-o, --out` | `<input>.standcurve.html` | Combined interactive HTML (curve + bars). |
|
|
250
|
+
| `--curve-html FILE` | *(off)* | Also write a standalone HTML containing only the standard curve. |
|
|
251
|
+
| `--bars-html FILE` | *(off)* | Also write a standalone HTML containing only the bar plot. |
|
|
252
|
+
| `--csv-out FILE` | *(off)* | Per-sample absolute-quantity table as CSV. |
|
|
253
|
+
| `--curve-csv FILE` | *(off)* | Per-replicate standard wells used for the fit. |
|
|
254
|
+
| `--pdf [PATH]` | *(off)* | Also write the combined figure to PDF. |
|
|
255
|
+
| `--png [PATH]` | *(off)* | Also write PNG. |
|
|
256
|
+
| `--svg [PATH]` | *(off)* | Also write SVG. |
|
|
257
|
+
| `--unit TEXT` | *(none)* | Unit string appended to the y-axis title (e.g. `copies/µL`). |
|
|
258
|
+
| `--standard-task TEXT` | `Standard` | `Task` value identifying the standards. |
|
|
259
|
+
| `--unknown-task TEXT` | `Unknown` | `Task` value identifying the samples. |
|
|
260
|
+
| `--background-rem [NAME]` | *(off)* | Use a named control sample (e.g. `Water`) as the background threshold; samples with mean Cq above it are flagged in `below_background` and dimmed. |
|
|
261
|
+
| `--task TEXT` (repeatable) | *(none)* | Extra `Task` values to keep when reading the CSV (in addition to the standard and unknown tasks, which are always kept). |
|
|
262
|
+
| `--no-log` | log10 on | Linear y-axis for the bar plot. |
|
|
263
|
+
| `--no-open` | opens | Do not auto-open the HTML. |
|
|
264
|
+
|
|
265
|
+
**Output table columns** (`--csv-out`):
|
|
266
|
+
|
|
267
|
+
`Target, Sample, n, Cq_mean, Cq_sd, log10_Q, log10_Q_sd, Quantity,
|
|
268
|
+
Quantity_low, Quantity_high, Q_err_minus, Q_err_plus, extrapolated,
|
|
269
|
+
Background_control, below_background`.
|
|
270
|
+
|
|
271
|
+
**Quality cues to watch for**:
|
|
272
|
+
|
|
273
|
+
- `Efficiency` should sit around 90–110 % (slope ≈ −3.32 for 100 %). The
|
|
274
|
+
reported value reflects the *slope*, not the absolute accuracy of any
|
|
275
|
+
single quantity.
|
|
276
|
+
- `R²` should be ≥ 0.98 for a well-behaved dilution series.
|
|
277
|
+
- Any row with `extrapolated == True` had a mean Cq outside the calibrated
|
|
278
|
+
log10-Quantity range — its back-calculated Quantity is an extrapolation
|
|
279
|
+
and should be interpreted with care (the bar is recolored).
|
|
280
|
+
|
|
281
|
+
### Filtering defaults
|
|
282
|
+
|
|
283
|
+
`read_thermo_well_results()` (and every CLI command) by default keeps **only
|
|
284
|
+
`Omit == false`** wells and **only `Task == "Unknown"`** rows, so NTC /
|
|
285
|
+
standard / endogenous-control wells are excluded automatically. Override
|
|
286
|
+
with `--all-tasks` or `--task <value>` (repeatable) on the CLI, or
|
|
287
|
+
`tasks=None` / `tasks=("Unknown", "Standard")` from Python.
|
|
288
|
+
|
|
289
|
+
`qpictures standcurve` automatically keeps both `Standard` and `Unknown`
|
|
290
|
+
wells (configurable via `--standard-task` / `--unknown-task`); use
|
|
291
|
+
`--task <value>` to also keep additional task categories.
|
|
292
|
+
|
|
293
|
+
## Python API
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
from qpictures import (
|
|
297
|
+
read_thermo_well_results,
|
|
298
|
+
relative_quantification,
|
|
299
|
+
relative_quantity_barplot,
|
|
300
|
+
paired_relative_quantification,
|
|
301
|
+
paired_relative_quantity_barplot,
|
|
302
|
+
fit_standard_curve,
|
|
303
|
+
absolute_quantification,
|
|
304
|
+
standard_curve_plot,
|
|
305
|
+
absolute_quantity_barplot,
|
|
306
|
+
standard_curve_with_quantities_figure,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
df = read_thermo_well_results("data/ex1/WellResult-2026-06-09-144852.csv")
|
|
310
|
+
|
|
311
|
+
# 1) Single reference sample
|
|
312
|
+
rq, ref = relative_quantification(df, reference="BB_PBS") # case-insensitive
|
|
313
|
+
relative_quantity_barplot(rq, log_y=True).write_html("rq.html")
|
|
314
|
+
|
|
315
|
+
# 2) Per-condition paired (e.g. TL_pbs vs BB_pbs, TL_sputum vs BB_sputum, ...)
|
|
316
|
+
# Welch's t-test on Cq by default; multiple-testing correction is off
|
|
317
|
+
# by default — pass padjust="bh" for Benjamini–Hochberg FDR.
|
|
318
|
+
pairs, summary = paired_relative_quantification(
|
|
319
|
+
df, reference_base="BB", test="welch", padjust="none"
|
|
320
|
+
)
|
|
321
|
+
paired_relative_quantity_barplot(
|
|
322
|
+
pairs, reference_base=summary.reference_base_resolved, log_y=True
|
|
323
|
+
).write_html("paired.html")
|
|
324
|
+
|
|
325
|
+
# 3) Absolute quantification from a standard curve. The CSV here includes
|
|
326
|
+
# Task == Standard wells (DNASD_*) with known Quantity values plus the
|
|
327
|
+
# usual Unknown wells; we must read both tasks to fit a curve.
|
|
328
|
+
df_sc = read_thermo_well_results(
|
|
329
|
+
"data/sc_well_table_results-2026-06-10-092019.csv",
|
|
330
|
+
tasks=("Unknown", "Standard"),
|
|
331
|
+
)
|
|
332
|
+
curve = fit_standard_curve(df_sc) # auto-picks target
|
|
333
|
+
print(f"slope={curve.slope:.3f} R^2={curve.r_squared:.4f}"
|
|
334
|
+
f" efficiency={curve.efficiency*100:.1f}%")
|
|
335
|
+
abs_table, _ = absolute_quantification(df_sc, curve=curve)
|
|
336
|
+
standard_curve_with_quantities_figure(
|
|
337
|
+
curve, abs_table, quantity_unit="copies/µL"
|
|
338
|
+
).write_html("standcurve.html")
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Math
|
|
342
|
+
|
|
343
|
+
For a single target (no endogenous control):
|
|
344
|
+
|
|
345
|
+
- `Cq_mean(sample)` and `Cq_sd(sample)` are computed across technical replicates.
|
|
346
|
+
- `ΔCq = Cq_mean(sample) - Cq_mean(reference)`
|
|
347
|
+
- `SD(ΔCq) = sqrt( SD(sample)^2 + SD(reference)^2 )`
|
|
348
|
+
- `RQ = 2^(-ΔCq)`
|
|
349
|
+
- `RQ_high = 2^( -(ΔCq - SD(ΔCq)) )`
|
|
350
|
+
- `RQ_low = 2^( -(ΔCq + SD(ΔCq)) )`
|
|
351
|
+
|
|
352
|
+
The bar plot uses asymmetric error bars `[RQ - RQ_low, RQ_high - RQ]` so that
|
|
353
|
+
the visual error is symmetric on the log axis.
|
|
354
|
+
|
|
355
|
+
### Standard curve (absolute quantification)
|
|
356
|
+
|
|
357
|
+
For runs that include serial dilutions of a known input (`Task == Standard`,
|
|
358
|
+
`Quantity` known), OLS on individual replicates yields
|
|
359
|
+
|
|
360
|
+
- `Cq = slope · log10(Q) + intercept`
|
|
361
|
+
- `efficiency = 10^(-1/slope) - 1` (1.0 == 100 %; slope ≈ −3.32 ideal)
|
|
362
|
+
|
|
363
|
+
For each unknown sample with mean Cq `Cq_mean` and SD `Cq_sd`:
|
|
364
|
+
|
|
365
|
+
- `log10(Q_hat) = (Cq_mean - intercept) / slope`
|
|
366
|
+
- `Q_hat = 10 ^ log10(Q_hat)`
|
|
367
|
+
- `SD(log10 Q) = Cq_sd / |slope|` (replicate scatter only)
|
|
368
|
+
- `Q_low = 10 ^ (log10(Q_hat) - SD(log10 Q))`
|
|
369
|
+
- `Q_high = 10 ^ (log10(Q_hat) + SD(log10 Q))`
|
|
370
|
+
|
|
371
|
+
Uncertainty in `slope` / `intercept` is *not* propagated into per-sample
|
|
372
|
+
bounds — they reflect technical-replicate scatter only. The curve's
|
|
373
|
+
`slope_se`, `intercept_se` and `residual_sd` are still surfaced as part of
|
|
374
|
+
the fit (and the ± 1 residual-SD band on the standard-curve plot).
|
|
375
|
+
|
|
376
|
+
## Notes on the Thermo export
|
|
377
|
+
|
|
378
|
+
The parser is tolerant of two common quirks:
|
|
379
|
+
|
|
380
|
+
1. A metadata header block above the actual table is skipped automatically.
|
|
381
|
+
2. Sample names containing **unquoted commas** (e.g. `beads,3mm,WS, Slow`) are
|
|
382
|
+
detected (row has more fields than the header) and re-glued into the
|
|
383
|
+
`Sample` column.
|