eachanjohnson commited on
Commit
1ea15b3
·
verified ·
1 Parent(s): 19f1172

Sync from GitHub via hub-sync

Browse files
README.md CHANGED
@@ -1,14 +1,433 @@
1
  ---
2
- title: Bartab
3
- emoji: 📉
4
- colorFrom: green
5
- colorTo: purple
6
  sdk: gradio
7
- sdk_version: 6.13.0
8
  app_file: app.py
9
- pinned: false
10
- license: mit
11
- short_description: Fitness from sequencing-based pooled competition experiments
 
 
 
 
 
12
  ---
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: bartab
3
+ emoji: 🍹
4
+ colorFrom: blue
5
+ colorTo: green
6
  sdk: gradio
7
+ sdk_version: "5.50.0"
8
  app_file: app.py
9
+ pinned: true
10
+ short_description: Fitness from pooled competition experiments.
11
+ tags:
12
+ - biology
13
+ - sequencing
14
+ - pooled-screen
15
+ - fitness
16
+ - gradio
17
  ---
18
 
19
+ # 🍹 bartab
20
+
21
+ **Estimate strain fitness from sequencing-based pooled competition experiments.**
22
+
23
+ `bartab` analyses pooled barcode sequencing experiments in which multiple strains,
24
+ mutants, guides, or constructs are grown together and quantified by NGS over time.
25
+ It estimates the relative fitness of each barcode compared with a reference strain,
26
+ typically WT.
27
+
28
+ [![Open in Spaces](https://huggingface.co/datasets/huggingface/badges/resolve/main/open-in-hf-spaces-md-dark.svg)](https://huggingface.co/spaces/scbirlab/bartab)
29
+
30
+ ---
31
+
32
+ ## What this app does
33
+
34
+ Upload three tables:
35
+
36
+ 1. **Count table**
37
+ Read or UMI counts for each barcode in each sequencing sample.
38
+
39
+ 2. **Sample sheet**
40
+ Metadata describing each sample: sample ID, culture/replicate ID, timepoint,
41
+ optional concentration, optional growth measurement, and optional sampled volume.
42
+
43
+ 3. **Barcode sheet**
44
+ Barcode or strain identifiers, plus optional strain-level metadata.
45
+
46
+ `bartab` then estimates how each barcode changes relative to a reference barcode as
47
+ the culture expands.
48
+
49
+ ---
50
+
51
+ ### Option 1: Spike-in normalisation
52
+
53
+ Use this when your experiment includes a non-growing spike-in barcode, such as a
54
+ heat-killed strain, plasmid-only control, or other fitness-zero control.
55
+
56
+ If the spike-in does not grow, then changes in the spike-in:WT count ratio report how
57
+ much WT has expanded.
58
+
59
+ For a non-growing spike-in:
60
+
61
+ $$
62
+ \log \left(
63
+ \frac{c_{spike}(t)}{c_{wt}(t)}
64
+ \frac{c_{wt}(0)}{c_{spike}(0)}
65
+ \right)
66
+ =
67
+ -
68
+ \log
69
+ \frac{n_{wt}(t)}{n_{wt}(0)}
70
+ $$
71
+
72
+ Substituting this into the barcode model gives:
73
+
74
+ $$
75
+ \log \left(
76
+ \frac{c_i(t)}{c_{wt}(t)}
77
+ \frac{c_{wt}(0)}{c_i(0)}
78
+ \right)
79
+ =
80
+ \left(
81
+ 1 - \frac{w_i}{w_{wt}}
82
+ \right)
83
+ \log \left(
84
+ \frac{c_{spike}(t)}{c_{wt}(t)}
85
+ \frac{c_{wt}(0)}{c_{spike}(0)}
86
+ \right)
87
+ $$
88
+
89
+ So, when using spike-in normalisation, `bartab` estimates relative fitness from the
90
+ relationship between:
91
+
92
+ - the barcode:WT log-ratio change
93
+ - the spike-in:WT log-ratio change
94
+
95
+ ---
96
+
97
+ ### Option 2: Growth-based normalisation
98
+
99
+ Use this when you have measured culture growth directly, for example:
100
+
101
+ - OD600
102
+ - CFU/mL
103
+ - estimated generations
104
+ - another density-like growth measurement
105
+
106
+ In this mode, `bartab` uses the supplied growth column as the culture expansion axis
107
+ instead of estimating expansion from a spike-in barcode.
108
+
109
+ ---
110
+
111
+ ## Analysis modes
112
+
113
+ ### 👟 Fitness in a single condition
114
+
115
+ This is the standard mode for pooled competition assays.
116
+
117
+ `bartab` fits a weighted least-squares model to estimate relative fitness for each
118
+ barcode.
119
+
120
+ Main outputs:
121
+
122
+ | Column | Meaning |
123
+ |---|---|
124
+ | `fitness` | relative fitness compared with the reference barcode |
125
+ | `fitness_low` | lower confidence interval bound |
126
+ | `fitness_high` | upper confidence interval bound |
127
+ | `slope_p` | p-value for deviation from neutral fitness |
128
+
129
+ Interpretation:
130
+
131
+ | Fitness | Interpretation |
132
+ |---|---|
133
+ | `1` | grows like the reference |
134
+ | `< 1` | growth disadvantage |
135
+ | `> 1` | growth advantage |
136
+
137
+ ---
138
+
139
+ ### 📉 Dose response
140
+
141
+ Use this when the sample sheet contains a concentration column.
142
+
143
+ `bartab` first estimates barcode fitness across concentrations, then fits a
144
+ two-parameter Hill model to estimate dose-response parameters.
145
+
146
+ Main outputs:
147
+
148
+ | Column | Meaning |
149
+ |---|---|
150
+ | `log_ic50` | log10 concentration giving 50% inhibition |
151
+ | `log_ic50_p` | p-value associated with the IC50 estimate |
152
+
153
+ Lower IC50 values indicate greater sensitivity to the inducer or drug.
154
+
155
+ ---
156
+
157
+ ## Required input tables
158
+
159
+ The app lets you select the relevant columns after upload.
160
+
161
+ Supported file formats:
162
+
163
+ - `.csv`
164
+ - `.tsv`
165
+ - `.txt`
166
+ - `.xlsx`
167
+
168
+ ---
169
+
170
+ ### 1. Count table
171
+
172
+ One row per barcode per sample.
173
+
174
+ Required columns:
175
+
176
+ | Meaning | Example |
177
+ |---|---|
178
+ | barcode / strain identifier | `strain_id` |
179
+ | sample identifier | `sample_id` |
180
+ | read or UMI count | `count` |
181
+
182
+ Example:
183
+
184
+ | strain_id | sample_id | count |
185
+ |---|---:|---:|
186
+ | wt | sample_0 | 12034 |
187
+ | mutant_A | sample_0 | 8312 |
188
+ | spike | sample_0 | 5021 |
189
+ | wt | sample_1 | 18420 |
190
+ | mutant_A | sample_1 | 6420 |
191
+ | spike | sample_1 | 2100 |
192
+
193
+ ---
194
+
195
+ ### 2. Barcode sheet
196
+
197
+ One row per barcode.
198
+
199
+ Required columns:
200
+
201
+ | Meaning | Example |
202
+ |---|---|
203
+ | barcode / strain identifier | `strain_id` |
204
+
205
+ Optional metadata columns are carried through to the output.
206
+
207
+ Example:
208
+
209
+ | strain_id | gene | annotation |
210
+ |---|---|---|
211
+ | wt | WT | reference |
212
+ | mutant_A | geneA | deletion mutant |
213
+ | spike | spike | non-growing spike-in |
214
+
215
+ ---
216
+
217
+ ### 3. Sample sheet
218
+
219
+ One row per sequencing sample.
220
+
221
+ Required columns:
222
+
223
+ | Meaning | Example |
224
+ |---|---|
225
+ | sample identifier | `sample_id` |
226
+ | culture / biological replicate | `replicate` |
227
+ | timepoint | `timepoint` |
228
+
229
+ Optional columns:
230
+
231
+ | Meaning | Example | Used for |
232
+ |---|---|---|
233
+ | concentration | `dose` | dose-response analysis |
234
+ | growth measurement | `growth` | growth-based normalisation |
235
+ | sampled volume | `volume` | adaptive-volume sampling |
236
+
237
+ Example:
238
+
239
+ | sample_id | replicate | timepoint | dose | growth | volume |
240
+ |---|---|---:|---:|---:|---:|
241
+ | sample_0 | rep1 | 0 | 0 | 0.05 | 1.0 |
242
+ | sample_1 | rep1 | 1 | 0 | 0.20 | 1.0 |
243
+ | sample_2 | rep1 | 2 | 0 | 0.80 | 1.0 |
244
+
245
+ ---
246
+
247
+ ## Example datasets
248
+
249
+ The Space includes synthetic example datasets for:
250
+
251
+ - single-concentration analysis using spike-in normalisation
252
+ - single-concentration analysis using growth-based normalisation
253
+ - dose-response analysis using spike-in normalisation
254
+ - dose-response analysis using growth-based normalisation
255
+
256
+ Start with these examples if you want to see the expected table structure.
257
+
258
+ ---
259
+
260
+ ## Outputs
261
+
262
+ After analysis, the app returns:
263
+
264
+ 1. **Fitted parameter table**
265
+ A CSV file containing estimated fitness or dose-response parameters.
266
+
267
+ 2. **Annotated `.h5ad` object**
268
+ A full `AnnData` object containing input data, metadata, transformations,
269
+ fitted values, and model outputs.
270
+
271
+ 3. **Diagnostic plots**
272
+
273
+ Depending on the analysis mode, plots include:
274
+
275
+ - time vs count
276
+ - expansion vs count
277
+ - time vs ratio
278
+ - expansion vs ratio
279
+ - predicted vs observed
280
+ - volcano plot
281
+ - dose-response curves
282
+
283
+ ---
284
+
285
+ ## Practical guidance
286
+
287
+ - Use consistent barcode and sample identifiers across all three tables.
288
+ - If using spike-in normalisation, include the spike-in barcode in both the count table
289
+ and barcode sheet.
290
+ - If using growth-based normalisation, include a numeric growth column in the sample sheet.
291
+ - For dose-response analysis, concentration values must be numeric.
292
+ - Low-count barcodes may give unstable estimates.
293
+ - Always inspect diagnostic plots before interpreting individual hits.
294
+
295
+ ---
296
+
297
+ ## The model
298
+
299
+ [Interactive tutorial on analysis principles](https://huggingface.co/spaces/scbirlab/tutorial-seq-fitness)
300
+
301
+ In a pooled growth experiment, strains compete in the same culture. Their absolute
302
+ growth curves may be complex because the pool eventually approaches carrying capacity.
303
+ However, if we compare every strain to a reference strain, the shared density-dependent
304
+ term cancels. The reference strain’s expansion can then be used as the effective
305
+ growth clock.
306
+
307
+ For strain $i$ relative to WT:
308
+
309
+ $$
310
+ \log n_i(t)
311
+ =
312
+ \frac{w_i}{w_{wt}}
313
+ \log \frac{n_{wt}(t)}{n_{wt}(0)}
314
+ +
315
+ \log n_i(0)
316
+ $$
317
+
318
+ where:
319
+
320
+ - $n_i(t)$ is the abundance of strain $i$ at time $t$
321
+ - $w_i$ is the intrinsic growth rate of strain $i$
322
+ - $w_i / w_{wt}$ is the relative fitness
323
+
324
+ So the problem becomes: estimate how barcode abundance changes relative to WT as WT
325
+ expands.
326
+
327
+ ---
328
+
329
+ ## From cells to sequencing counts
330
+
331
+ In practice, we do not observe cell numbers directly. We observe read counts:
332
+
333
+ $$
334
+ c_i(t)
335
+ $$
336
+
337
+ These are affected by sampling, library preparation, and sequencing depth. To remove
338
+ sample-specific sequencing depth effects, `bartab` works with ratios of barcode counts
339
+ to the reference barcode.
340
+
341
+ The key quantity is:
342
+
343
+ $$
344
+ \log \left(
345
+ \frac{c_i(t)}{c_{wt}(t)}
346
+ \frac{c_{wt}(0)}{c_i(0)}
347
+ \right)
348
+ $$
349
+
350
+ This is the log-change in the abundance of barcode $i$ relative to WT, normalised
351
+ to the starting timepoint.
352
+
353
+ Under the model:
354
+
355
+ $$
356
+ \log \left(
357
+ \frac{c_i(t)}{c_{wt}(t)}
358
+ \frac{c_{wt}(0)}{c_i(0)}
359
+ \right)
360
+ =
361
+ \left(
362
+ \frac{w_i}{w_{wt}} - 1
363
+ \right)
364
+ \log
365
+ \frac{n_{wt}(t)}{n_{wt}(0)}
366
+ $$
367
+
368
+ Thus, each barcode should follow an approximately straight line. The slope gives the
369
+ barcode’s fitness relative to WT.
370
+
371
+ ---
372
+
373
+ ## Estimating culture expansion
374
+
375
+ The remaining problem is that the true WT expansion,
376
+
377
+ $$
378
+ \frac{n_{wt}(t)}{n_{wt}(0)}
379
+ $$
380
+
381
+ is usually not directly observed. `bartab` supports two ways to estimate it.
382
+
383
+ ---
384
+
385
+ ## When this model is appropriate
386
+
387
+ `bartab` is designed for pooled competition experiments where:
388
+
389
+ - barcodes identify strains, mutants, guides, or constructs
390
+ - all barcodes are grown together in shared cultures
391
+ - barcode abundance is quantified by sequencing
392
+ - one barcode can be treated as a reference
393
+ - expansion can be estimated from a spike-in or measured growth
394
+
395
+ It is especially useful for:
396
+
397
+ - bacterial pooled competition assays
398
+ - barcoded mutant libraries
399
+ - CRISPRi or guide-based growth assays
400
+ - chemical-genetic pooled fitness experiments
401
+ - inducer or drug dose-response screens
402
+
403
+ ---
404
+
405
+ ## Limitations
406
+
407
+ `bartab` estimates relative fitness, not absolute growth rate.
408
+
409
+ Results may be unreliable when:
410
+
411
+ - the reference barcode is depleted or poorly counted
412
+ - the spike-in is not truly non-growing
413
+ - barcode counts are extremely low
414
+ - barcode identities are mismatched between tables
415
+ - bottlenecks dominate the experiment
416
+ - strong barcode-specific sequencing biases are present
417
+ - timepoints or concentrations are too sparse for model fitting
418
+
419
+ For dose-response fitting, IC50 estimates are most meaningful when the tested
420
+ concentration range brackets the transition from weak to strong inhibition.
421
+
422
+ ---
423
+
424
+ ## Local use
425
+
426
+ To run the app locally:
427
+
428
+ ```bash
429
+ pip install -r requirements.txt
430
+ gradio app.py
431
+ ```
432
+
433
+ For package and source code, see https://github.com/scbirlab/bartab
app.py ADDED
@@ -0,0 +1,841 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Gradio demo for bartab."""
2
+
3
+ from typing import Iterable, List, Union
4
+ from functools import partial
5
+ from io import TextIOWrapper
6
+ import os
7
+ os.environ["COMMANDLINE_ARGS"] = "--no-gradio-queue"
8
+
9
+ from carabiner import cast, print_err
10
+ from carabiner.decorators import decorator_with_params
11
+ from carabiner.pd import read_table
12
+ import gradio as gr
13
+ import nemony as nm
14
+ import numpy as np
15
+ import pandas as pd
16
+
17
+ import anndata
18
+ anndata.settings.allow_write_nullable_strings = True
19
+ import bartab
20
+ from bartab.io import load_anndata
21
+ from bartab.models.anndata import AnnDataWLSModel, AnnDataHillModel
22
+ from bartab.plotting import (
23
+ dose_response,
24
+ expansion_vs_count,
25
+ expansion_vs_ratio,
26
+ pred_vs_true,
27
+ time_vs_count,
28
+ time_vs_ratio,
29
+ volcano
30
+ )
31
+ from bartab.transforms import compute_log_ratios
32
+
33
+ pd.options.future.infer_string = True
34
+
35
+ MODES: dict = {
36
+ "single": "👟 Fitness in a single condition",
37
+ "dose response": "📉 Fitness dose response to CRISPRi inducer",
38
+ }
39
+ def _message(s: str):
40
+ print_err(f"[INFO] {s}")
41
+ gr.Info(s, duration=10)
42
+ return None
43
+
44
+
45
+ def load_input_data(
46
+ filename: str,
47
+ cols: Iterable
48
+ ) -> List[pd.DataFrame]:
49
+ df = read_table(filename)
50
+ print_err(df)
51
+ out = [gr.update(value=df, visible=False)]
52
+ for key, col in cols.items():
53
+ if isinstance(col, tuple):
54
+ col_type = col[1]
55
+ if col_type == "string":
56
+ choices = list(df.select_dtypes(include="str"))
57
+ elif col_type == "numeric":
58
+ choices = list(df.select_dtypes(include="number"))
59
+ else:
60
+ choices = list(df)
61
+ else:
62
+ choices = list(df)
63
+ choices = [""] + choices
64
+ print_err(key, f"{choices=}")
65
+ out.append(
66
+ gr.update(
67
+ choices=choices,
68
+ value=key if key in choices else choices[0],
69
+ interactive=True,
70
+ visible=True,
71
+ )
72
+ )
73
+ print_err(out)
74
+ return out
75
+
76
+
77
+ def load_barcode_names(
78
+ df: pd.DataFrame,
79
+ strain_col: str
80
+ ) -> List[List[str]]:
81
+ strains = sorted(df[strain_col].unique())
82
+ print_err(strain_col, f"{strains=}")
83
+ return gr.update(
84
+ choices=strains,
85
+ value="wt" if "wt" in strains else strains[0],
86
+ interactive=True,
87
+ visible=True,
88
+ ), gr.update(
89
+ choices=strains,
90
+ value="spike" if "spike" in strains else "",
91
+ interactive=True,
92
+ visible=True,
93
+ ), gr.update(
94
+ choices=strains,
95
+ value=[],
96
+ allow_custom_value=True,
97
+ interactive=True,
98
+ visible=True,
99
+ )
100
+
101
+
102
+ def _prepare_to_fit(
103
+ counts: pd.DataFrame,
104
+ strain_sheet: pd.DataFrame,
105
+ sample_sheet: pd.DataFrame,
106
+ count_column: str,
107
+ strain_id_column: str,
108
+ timepoint_column: str,
109
+ concentration_column: str,
110
+ sample_id_column: str,
111
+ culture_id_column: str,
112
+ volume_column: str = "volume",
113
+ growth_column: str = "growth",
114
+ reference: str = "wt",
115
+ spike_name: str = "spike",
116
+ spike_mode: str = "Spike",
117
+ growth_type: str = "density",
118
+ pseudocount: float = 1.
119
+ ):
120
+ use_spike = (spike_mode == "Spike")
121
+ adata = load_anndata(
122
+ counts=counts,
123
+ sample_meta=sample_sheet,
124
+ strain_meta=strain_sheet,
125
+ reference=reference,
126
+ count_column=count_column,
127
+ timepoint_column=timepoint_column,
128
+ # t0=args.t0,
129
+ concentration_column=concentration_column,
130
+ strain_id=strain_id_column,
131
+ sample_id=sample_id_column,
132
+ culture_id=culture_id_column,
133
+ spike=spike_name if spike_name else None,
134
+ )
135
+ print_err(adata)
136
+ adata = compute_log_ratios(
137
+ adata=adata,
138
+ pseudocount=pseudocount,
139
+ volume_column=volume_column,
140
+ growth_column=growth_column,
141
+ growth_type=growth_type,
142
+ use_spike=use_spike,
143
+ )
144
+ print_err(adata)
145
+ return adata
146
+
147
+
148
+ def do_analysis(*args):
149
+ args = [
150
+ a if not (isinstance(a, str) and a == "") else None
151
+ for a in args
152
+ ]
153
+ mode = args[-1]
154
+ concentration_column = args[3]
155
+ print_err(args[:-1])
156
+ try:
157
+ adata = _prepare_to_fit(*args[:-1])
158
+ except TypeError as e:
159
+ print_err(*args[:-1])
160
+ raise e
161
+ _message("Using a weighted least squares model")
162
+ model = AnnDataWLSModel()
163
+ results = model.fit(adata=adata)
164
+ if mode == MODES["dose response"]:
165
+ _message(
166
+ "Using a Hill non-linear model with "
167
+ f"'{concentration_column}' for concentration."
168
+ )
169
+ model = AnnDataHillModel()
170
+ results = model.fit(adata=results, concentration=concentration_column)
171
+ return gr.update(value=results.obs, visible=True), results
172
+
173
+
174
+ def _fig2img(fig):
175
+ import PIL
176
+ # img = PIL.Image.frombytes(
177
+ # "RGBa",
178
+ # fig.canvas.get_width_height(),
179
+ # fig.canvas.buffer_rgba(),
180
+ # )
181
+ import io
182
+ buf = io.BytesIO()
183
+ fig.savefig(buf)
184
+ buf.seek(0)
185
+ img = PIL.Image.open(buf)
186
+ return img
187
+
188
+
189
+ @decorator_with_params
190
+ def _plot_wrapper(fn, message="Plotting..."):
191
+ def _fn(*args, **kwargs):
192
+ if args[1] == "":
193
+ args[1] = None
194
+ if message:
195
+ _message(message)
196
+ fig, axes = fn(*args, **kwargs)
197
+ if isinstance(fig, tuple) and isinstance(axes, bool):
198
+ fig, vis = fig
199
+ return gr.update(
200
+ value=_fig2img(fig) if fig is not None else fig,
201
+ visible=vis,
202
+ )
203
+ else:
204
+ return gr.update(value=_fig2img(fig), visible=True)
205
+ return _fn
206
+
207
+
208
+ @_plot_wrapper()
209
+ def _plot_dose_response(
210
+ adata,
211
+ highlight=None,
212
+ control_prefix: str = "ctrl_",
213
+ mode: str = MODES["single"]
214
+ ):
215
+ do_dose_response = mode == MODES["dose response"]
216
+ if do_dose_response:
217
+ print_err("Plotting dose response")
218
+ fig, axes = dose_response(
219
+ adata,
220
+ highlight_barcodes=highlight,
221
+ model_name="WLS",
222
+ control_prefix=control_prefix,
223
+ )
224
+ return fig, axes
225
+ else:
226
+ print_err("Skipping dose response")
227
+ return (None, None), False
228
+
229
+
230
+ @_plot_wrapper(message="Plotting time vs count")
231
+ def _plot_time_vs_count(
232
+ adata,
233
+ highlight=None,
234
+ control_prefix: str = "ctrl_",
235
+ *args, **kwargs
236
+ ):
237
+ return time_vs_count(
238
+ adata,
239
+ highlight_barcodes=highlight,
240
+ control_prefix=control_prefix,
241
+ )
242
+
243
+
244
+ @_plot_wrapper(message="Plotting expansion vs count")
245
+ def _plot_expansion_vs_count(
246
+ adata,
247
+ highlight=None,
248
+ control_prefix: str = "ctrl_",
249
+ *args, **kwargs
250
+ ):
251
+ return expansion_vs_count(
252
+ adata,
253
+ highlight_barcodes=highlight,
254
+ control_prefix=control_prefix,
255
+ )
256
+
257
+
258
+ @_plot_wrapper(message="Plotting time vs ratio")
259
+ def _plot_time_vs_ratio(
260
+ adata,
261
+ highlight=None,
262
+ control_prefix: str = "ctrl_",
263
+ *args, **kwargs
264
+ ):
265
+ return time_vs_ratio(
266
+ adata,
267
+ highlight_barcodes=highlight,
268
+ control_prefix=control_prefix,
269
+ )
270
+
271
+
272
+ @_plot_wrapper(message="Plotting expansion vs ratio")
273
+ def _plot_expansion_vs_ratio(
274
+ adata,
275
+ highlight=None,
276
+ control_prefix: str = "ctrl_",
277
+ *args, **kwargs
278
+ ):
279
+ return expansion_vs_ratio(
280
+ adata,
281
+ highlight_barcodes=highlight,
282
+ control_prefix=control_prefix,
283
+ )
284
+
285
+ @_plot_wrapper(message="Plotting predicted vs observed")
286
+ def _plot_pred_vs_true(
287
+ adata,
288
+ highlight=None,
289
+ control_prefix: str = "ctrl_",
290
+ mode: str = MODES["single"]
291
+ ):
292
+ do_dose_response = mode == MODES["dose response"]
293
+ return pred_vs_true(
294
+ adata,
295
+ model_name="HillFitnessModel" if do_dose_response else "WLS",
296
+ highlight_barcodes=highlight,
297
+ control_prefix=control_prefix,
298
+ )
299
+
300
+
301
+ @_plot_wrapper(message="Plotting volcano")
302
+ def _plot_volcano(
303
+ adata,
304
+ highlight=None,
305
+ control_prefix: str = "ctrl_",
306
+ mode: str = MODES["single"]
307
+ ):
308
+ do_dose_response = mode == MODES["dose response"]
309
+ return volcano(
310
+ adata,
311
+ highlight_barcodes=highlight,
312
+ control_prefix=control_prefix,
313
+ model_name="HillFitnessModel" if do_dose_response else "WLS",
314
+ param="ic50" if do_dose_response else "fitness",
315
+ xscale="log" if do_dose_response else "linear",
316
+ vline=None if do_dose_response else 1.,
317
+ p="log_ic50_p" if do_dose_response else "slope_p",
318
+ )
319
+
320
+
321
+ def download_tables(
322
+ df: pd.DataFrame,
323
+ adata
324
+ ) -> str:
325
+ df_hash = nm.hash(pd.util.hash_pandas_object(df).values)
326
+ filename = f"bartab-{df_hash}"
327
+ filename_csv = f"{filename}.csv"
328
+ df.to_csv(filename, index=False)
329
+ filename_adata = f"{filename}.h5ad"
330
+ adata.write(filename_adata)
331
+ return gr.update(
332
+ value=filename_csv,
333
+ visible=True,
334
+ ), gr.update(
335
+ value=filename_adata,
336
+ visible=True,
337
+ )
338
+
339
+
340
+
341
+ def _file_input(**kwargs):
342
+ return partial(gr.File,
343
+ file_types=[".xlsx", ".csv", ".tsv", ".txt"],
344
+ )(**kwargs)
345
+
346
+
347
+ def _load_from_file(*args):
348
+ if len(args) > 1:
349
+ return args
350
+ else:
351
+ return args[0]
352
+
353
+
354
+ def _invisible_dropdown(**kwargs):
355
+ return partial(gr.Dropdown,
356
+ choices=[],
357
+ interactive=False,
358
+ visible=True,
359
+ )(**kwargs)
360
+
361
+
362
+ def _invisible_plot(**kwargs):
363
+ return partial(gr.Image,
364
+ visible=False,
365
+ )(**kwargs)
366
+
367
+
368
+ with gr.Blocks() as demo:
369
+ gr.Markdown(
370
+ f"""
371
+ # 🍹 bartab: Fitness from pooled competition assays
372
+
373
+ *Using* `bartab` v{bartab.__version__} | [Documentation](https://github.com/scbirlab/bartab) | [Tutorial on analysis principles](https://huggingface.co/spaces/scbirlab/tutorial-seq-fitness)
374
+
375
+ Infer the competitive fitness of barcoded strains from next-generation
376
+ sequencing of pooled growth experiments.
377
+
378
+ Upload your count table, sample sheet, and barcode sheet, then
379
+ click **Calculate fitness**.
380
+
381
+ """
382
+ )
383
+ gr.Markdown(
384
+ """
385
+ ---
386
+
387
+ ## 1️⃣ Input tables
388
+
389
+ Three tables are required. You can upload CSV, TSV, or XLSX files,
390
+ or try one of the **example datasets** below.
391
+
392
+ """
393
+ )
394
+ input_filenames = {
395
+ "count_table": gr.Textbox(interactive=False, visible=False),
396
+ "sample_sheet": gr.Textbox(interactive=False, visible=False),
397
+ "strain_sheet": gr.Textbox(interactive=False, visible=False),
398
+ }
399
+ app_root = os.path.dirname(__file__)
400
+ data_path = os.path.join(app_root, "data", "examples", "single-point")
401
+ control_strains = {
402
+ "reference": _invisible_dropdown(
403
+ label="Reference (WT) barcode name",
404
+ render=False,
405
+ ),
406
+ "spike": _invisible_dropdown(
407
+ label="Spike-in barcode name (if using)",
408
+ render=False,
409
+ ),
410
+ }
411
+ analysis_opts = {
412
+ "use_spike": gr.Radio(
413
+ label="Culture expansion uses:",
414
+ choices=["Spike", "Growth"],
415
+ value="Spike",
416
+ render=False,
417
+ ),
418
+ "growth_type": gr.Radio(
419
+ label="Growth type",
420
+ choices=["density", "generations"],
421
+ value="density",
422
+ visible=False,
423
+ render=False,
424
+ ),
425
+ "pseudocount": gr.Number(
426
+ label="Pseudocount",
427
+ value=1.,
428
+ render=False,
429
+ ),
430
+ }
431
+ plotting_opts = {
432
+ "highlight": _invisible_dropdown(
433
+ label="Strain(s) to highlight in plots",
434
+ render=False,
435
+ multiselect=True,
436
+ ),
437
+ "controls": gr.Textbox(
438
+ label="Prefix of control barcode names",
439
+ value="ctrl_",
440
+ render=False,
441
+ ),
442
+ }
443
+ mode_switch = gr.Radio(
444
+ label="Analysis mode",
445
+ choices=list(MODES.values()),
446
+ value=MODES["single"],
447
+ render=False,
448
+ )
449
+ examples = gr.Examples(
450
+ label="Examples with synthetic data",
451
+ examples=[
452
+ [
453
+ os.path.join(data_path, "test_count.csv"),
454
+ os.path.join(data_path, "test_sample_meta.csv"),
455
+ os.path.join(data_path, "test_strain_meta.csv"),
456
+ MODES["single"],
457
+ "Spike",
458
+ ],
459
+ [
460
+ os.path.join(data_path, "test_count.csv"),
461
+ os.path.join(data_path, "test_sample_meta.csv"),
462
+ os.path.join(data_path, "test_strain_meta.csv"),
463
+ MODES["single"],
464
+ "Growth",
465
+ ],
466
+ [
467
+ os.path.join(data_path, "dose-response_count.csv"),
468
+ os.path.join(data_path, "dose-response_sample_meta.csv"),
469
+ os.path.join(data_path, "dose-response_strain_meta.csv"),
470
+ MODES["dose response"],
471
+ "Spike",
472
+ ],
473
+ [
474
+ os.path.join(data_path, "dose-response_count.csv"),
475
+ os.path.join(data_path, "dose-response_sample_meta.csv"),
476
+ os.path.join(data_path, "dose-response_strain_meta.csv"),
477
+ MODES["dose response"],
478
+ "Growth",
479
+ ],
480
+ ],
481
+ example_labels=[
482
+ ["Single point, using spike-in"],
483
+ ["Single point, using growth"],
484
+ ["Dose response, using spike-in"],
485
+ ["Dose response, using growth"],
486
+ ],
487
+ inputs=[
488
+ input_filenames["count_table"],
489
+ input_filenames["sample_sheet"],
490
+ input_filenames["strain_sheet"],
491
+ mode_switch,
492
+ analysis_opts["use_spike"],
493
+ ],
494
+ # cache_examples=True,
495
+ # cache_mode="eager",
496
+ )
497
+ input_files = {}
498
+ input_cols = {}
499
+ go_button = gr.Button(
500
+ value="🚀 Calculate fitness!",
501
+ interactive=False,
502
+ render=False,
503
+ )
504
+ with gr.Row():
505
+ with gr.Column():
506
+ gr.Markdown(
507
+ """
508
+ ---
509
+
510
+ ### 🧮 Count table
511
+
512
+ One row per barcode per sample. Must contain:
513
+ - a column of **barcode/strain identifiers** (matching your barcode sheet)
514
+ - a column of **sample identifiers** (matching your sample sheet)
515
+ - a column of **read or UMI counts**
516
+
517
+ """
518
+ )
519
+ input_files["count_table"] = _file_input(
520
+ label="Upload your barcode sequencing counts data here",
521
+ )
522
+ input_cols["count_table"] = {
523
+ "count": (_invisible_dropdown(
524
+ label="Counts column",
525
+ ), "numeric"),
526
+ }
527
+ with gr.Column():
528
+ gr.Markdown(
529
+ """
530
+ ---
531
+
532
+ ### 📶 Barcode information
533
+
534
+ One row per unique barcode. Must contain:
535
+ - a column of **barcode identifiers**
536
+
537
+ Optionally: any metadata about strains (gene targets, constructs,
538
+ etc.). These will be carried through to the output.
539
+
540
+ """
541
+ )
542
+ input_files["strain_sheet"] = _file_input(
543
+ label="Upload your barcode information here",
544
+ )
545
+ input_cols["strain_sheet"] = {
546
+ "strain_id": (_invisible_dropdown(
547
+ label="Barcode identifier column",
548
+ ), "string"),
549
+ }
550
+ with gr.Row():
551
+ gr.Markdown(
552
+ r"""
553
+ ---
554
+
555
+ ### 🧪 Sample sheet
556
+
557
+ One row per sample (sequencing library). Must contain:
558
+ - **Sample ID**: unique identifier matching the count table
559
+ - **Culture ID**: biological replicate identifier. Samples from
560
+ the same culture share this label
561
+ - **Timepoint**: numeric timepoint values. The earliest timepoint
562
+ is treated as $t_0$.
563
+
564
+ **For spike-in normalisation**:
565
+ no extra columns needed. Just include your spike-in barcode in
566
+ the count table and barcode sheet.
567
+
568
+ **For growth-based normalisation**: add a column of OD600, CFU/mL,
569
+ or generation counts measured at each sample.
570
+
571
+ **For dose-response analysis**: add a column of inducer/drug
572
+ concentrations. Samples with concentration = 0 are treated as
573
+ uninduced controls.
574
+
575
+ **For adaptive-volume sampling** (if you took different volumes
576
+ from each sample): add a column of sampled volumes.
577
+
578
+ """
579
+ )
580
+ input_files["sample_sheet"] = _file_input(
581
+ label="Upload your sample information here",
582
+ )
583
+ with gr.Row():
584
+ input_cols["sample_sheet"] = {
585
+ "timepoint": (_invisible_dropdown(
586
+ label="Timepoint column",
587
+ ), "any"),
588
+ "dose": (_invisible_dropdown(
589
+ label="Concentration column",
590
+ ), "numeric"),
591
+ "sample_id": (_invisible_dropdown(
592
+ label="Individual sample ID column",
593
+ ), "string"),
594
+ "replicate": (_invisible_dropdown(
595
+ label="Culture / biological replicate column",
596
+ ), "string"),
597
+ "volume": (_invisible_dropdown(
598
+ label="Volume column (if using)",
599
+ ), "numeric"),
600
+ "growth": (_invisible_dropdown(
601
+ label="Growth column (if using)",
602
+ ), "numeric"),
603
+ }
604
+
605
+ with gr.Row():
606
+ input_data = {
607
+ key: gr.Dataframe(
608
+ label=f"Input data: {key}",
609
+ max_height=50,
610
+ visible=False,
611
+ interactive=False,
612
+ ) for key in input_files
613
+ }
614
+
615
+ adata = gr.State()
616
+ with gr.Row():
617
+ with gr.Column():
618
+ gr.Markdown(
619
+ """
620
+ ---
621
+
622
+ ## 2️⃣ Control strains
623
+
624
+ - **Reference (WT)**: the strain relative to which all
625
+ fitness values are calculated. Fitness = 1 by definition.
626
+ - **Spike-in**: a non-growing strain (e.g. heat-killed or
627
+ plasmid-only) added at a fixed concentration before library
628
+ preparation. Used to infer how much the reference strain has
629
+ expanded between timepoints, removing the need for growth
630
+ measurements. Leave blank if using growth measurements instead.
631
+
632
+ """
633
+ )
634
+ for key, val in control_strains.items():
635
+ val.render()
636
+ with gr.Column():
637
+ gr.Markdown(
638
+ """
639
+ ---
640
+
641
+ ## 3️⃣ Analysis options
642
+
643
+ - **Culture expansion**: choose **Spike** if you have a
644
+ non-growing spike-in control, or **Growth** if you have
645
+ OD600/CFU measurements.
646
+ - **Pseudocount**: added to all counts before log transformation
647
+ to avoid log(0).
648
+ - **Analysis mode**: choose **Single concentration** for standard fitness
649
+ screens, or **Dose response** if your sample sheet contains a concentration
650
+ column. Dose response fitting uses a 2-parameter Hill model to estimate the
651
+ IC₅₀ and maximum effectfor each barcode.
652
+
653
+ """
654
+ )
655
+ for key, val in analysis_opts.items():
656
+ val.render()
657
+ with gr.Column():
658
+ gr.Markdown(
659
+ """
660
+ ---
661
+
662
+ ## Plotting options
663
+
664
+ """
665
+ )
666
+ for key, val in plotting_opts.items():
667
+ val.render()
668
+
669
+ with gr.Column():
670
+ mode_switch.render()
671
+ go_button.render()
672
+
673
+ mode_switch.change(
674
+ lambda x: gr.update(value=x),
675
+ inputs=[mode_switch],
676
+ outputs=[go_button],
677
+ )
678
+ gr.Markdown(
679
+ r"""
680
+ ## 4️⃣ Results
681
+
682
+ Fitness values are estimated by weighted least squares regression of the log-ratio of each barcode against the reference strain, using the spike-in or growth measurements as the x-axis.
683
+
684
+ **Key output columns**:
685
+ - For single concentration:
686
+ - `fitness`: relative fitness ($w_i / w_{wt}$). Values < 1 indicate growth disadvantage; > 1 indicates advantage.
687
+ - `fitness_low` / `fitness_high`: 95% confidence interval bounds.
688
+ - `slope_p`: p-value for the slope being different from 0 (i.e. fitness ≠ 1).
689
+ - For dose-response:
690
+ - `log_ic50` (log₁₀ concentration at 50% inhibition) and `log_ic50_p`.
691
+
692
+ Results and the full annotated dataset (`.h5ad`) can be downloaded below.
693
+
694
+ """
695
+ )
696
+
697
+ plots = {}
698
+ with gr.Row():
699
+ plots |= {
700
+ "dose_response": (
701
+ _invisible_plot(label="Dose response"),
702
+ _plot_dose_response,
703
+ ),
704
+ "count_time": (
705
+ _invisible_plot(label="Time vs count"),
706
+ _plot_time_vs_count,
707
+ ),
708
+ "count_exp": (
709
+ _invisible_plot(label="Expansion vs count"),
710
+ _plot_expansion_vs_count,
711
+ ),
712
+ }
713
+ with gr.Row():
714
+ plots |= {
715
+ "count_exp": (
716
+ _invisible_plot(label="Time vs ratio"),
717
+ _plot_time_vs_ratio,
718
+ ),
719
+ "ratio_exp": (
720
+ _invisible_plot(label="Expansion vs ratio"),
721
+ _plot_expansion_vs_ratio,
722
+ ),
723
+ }
724
+ with gr.Row():
725
+ plots |= {
726
+ "pred_obs": (
727
+ _invisible_plot(label="Predicted vs observed"),
728
+ _plot_pred_vs_true,
729
+ ),
730
+ "volcano": (
731
+ _invisible_plot(label="Volcano"),
732
+ _plot_volcano,
733
+ ),
734
+ }
735
+ with gr.Row():
736
+ download = gr.DownloadButton(
737
+ label="Download parameters as CSV",
738
+ visible=False,
739
+ )
740
+ download_adata = gr.DownloadButton(
741
+ label="Download all analysis as .h5ad",
742
+ visible=False,
743
+ )
744
+ output_table = gr.Dataframe(
745
+ label="Fitted parameters",
746
+ # max_height=100,
747
+ visible=False,
748
+ interactive=False,
749
+ )
750
+
751
+ # ======
752
+ # EVENTS
753
+ # ======
754
+
755
+ for key, input_file in input_files.items():
756
+ input_columns = input_cols[key]
757
+ event_fn = {
758
+ "fn": partial(load_input_data, cols=input_columns),
759
+ "outputs": [input_data[key]] + [
760
+ col[0] if isinstance(col, tuple) else col
761
+ for _, col in input_columns.items()
762
+ ],
763
+ }
764
+ input_filenames[key].change(
765
+ _load_from_file,
766
+ inputs=[input_filenames[key]],
767
+ outputs=[input_file],
768
+ ).then(
769
+ **event_fn,
770
+ inputs=[input_filenames[key]],
771
+ )
772
+ input_file.upload(
773
+ **event_fn,
774
+ inputs=[input_file],
775
+ )
776
+
777
+ input_cols["strain_sheet"]["strain_id"][0].change(
778
+ load_barcode_names,
779
+ inputs=[
780
+ input_data["strain_sheet"],
781
+ input_cols["strain_sheet"]["strain_id"][0],
782
+ ],
783
+ outputs=[
784
+ control_strains["reference"],
785
+ control_strains["spike"],
786
+ plotting_opts["highlight"],
787
+ ],
788
+ ).then(
789
+ lambda : gr.update(interactive=True),
790
+ inputs=[],
791
+ outputs=[go_button],
792
+ )
793
+
794
+ analysis_opts["use_spike"].change(
795
+ lambda x: gr.update(visible=x == "Growth"),
796
+ inputs=[analysis_opts["use_spike"]],
797
+ outputs=[analysis_opts["growth_type"]],
798
+ )
799
+
800
+ comptuation_inputs = (
801
+ [
802
+ f for _, f in input_data.items()
803
+ ] + [
804
+ opt[0] if isinstance(opt, tuple) else opt
805
+ for _, input_col in input_cols.items()
806
+ for _, opt in input_col.items()
807
+ ] + [
808
+ v for k, v in control_strains.items()
809
+ ] + [
810
+ v for k, v in analysis_opts.items()
811
+ ]
812
+ )
813
+
814
+ evt = go_button.click(
815
+ fn=do_analysis,
816
+ inputs=comptuation_inputs + [mode_switch],
817
+ outputs=[
818
+ output_table,
819
+ adata,
820
+ ],
821
+ ).then(
822
+ download_tables,
823
+ inputs=[output_table, adata],
824
+ outputs=[download, download_adata],
825
+ )
826
+
827
+ for key, (p, fn) in plots.items():
828
+ evt = evt.then(
829
+ fn,
830
+ inputs=[
831
+ adata,
832
+ plotting_opts["highlight"],
833
+ plotting_opts["controls"],
834
+ mode_switch,
835
+ ],
836
+ outputs=[p],
837
+ )
838
+
839
+ if __name__ == "__main__":
840
+ demo.queue()
841
+ demo.launch(share=True)
data/examples/single-point/dose-response_count.csv ADDED
The diff for this file is too large to render. See raw diff
 
data/examples/single-point/dose-response_sample_meta.csv ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ sample_id,replicate,timepoint,dose,growth,generations,is_t0
2
+ dose_0-rep_0-t_0,dose_0-rep_0,0.0,0.12345679012345678,117055.0,0.0,True
3
+ dose_0-rep_0-t_1,dose_0-rep_0,1.0,0.12345679012345678,234292.89384810615,1.0011266371943761,False
4
+ dose_0-rep_0-t_2,dose_0-rep_0,2.0,0.12345679012345678,427061.5122706585,1.8672573245346546,False
5
+ dose_0-rep_0-t_3,dose_0-rep_0,3.0,0.12345679012345678,668560.4452222767,2.5138714412935887,False
6
+ dose_0-rep_0-t_4,dose_0-rep_0,4.0,0.12345679012345678,883476.5973974025,2.9160053589907093,False
7
+ dose_0-rep_0-t_5,dose_0-rep_0,5.0,0.12345679012345678,1024302.3566620992,3.129383171737148,False
8
+ dose_0-rep_1-t_0,dose_0-rep_1,0.0,0.12345679012345678,117055.0,0.0,True
9
+ dose_0-rep_1-t_1,dose_0-rep_1,1.0,0.12345679012345678,234292.89384810615,1.0011266371943761,False
10
+ dose_0-rep_1-t_2,dose_0-rep_1,2.0,0.12345679012345678,427061.5122706585,1.8672573245346546,False
11
+ dose_0-rep_1-t_3,dose_0-rep_1,3.0,0.12345679012345678,668560.4452222767,2.5138714412935887,False
12
+ dose_0-rep_1-t_4,dose_0-rep_1,4.0,0.12345679012345678,883476.5973974025,2.9160053589907093,False
13
+ dose_0-rep_1-t_5,dose_0-rep_1,5.0,0.12345679012345678,1024302.3566620992,3.129383171737148,False
14
+ dose_0-rep_2-t_0,dose_0-rep_2,0.0,0.12345679012345678,117055.0,0.0,True
15
+ dose_0-rep_2-t_1,dose_0-rep_2,1.0,0.12345679012345678,234292.89384810615,1.0011266371943761,False
16
+ dose_0-rep_2-t_2,dose_0-rep_2,2.0,0.12345679012345678,427061.5122706585,1.8672573245346546,False
17
+ dose_0-rep_2-t_3,dose_0-rep_2,3.0,0.12345679012345678,668560.4452222767,2.5138714412935887,False
18
+ dose_0-rep_2-t_4,dose_0-rep_2,4.0,0.12345679012345678,883476.5973974025,2.9160053589907093,False
19
+ dose_0-rep_2-t_5,dose_0-rep_2,5.0,0.12345679012345678,1024302.3566620992,3.129383171737148,False
20
+ dose_1-rep_0-t_0,dose_1-rep_0,0.0,0.20117262002701805,117055.0,0.0,True
21
+ dose_1-rep_0-t_1,dose_1-rep_0,1.0,0.20117262002701805,222422.7611495618,0.9261178702440107,False
22
+ dose_1-rep_0-t_2,dose_1-rep_0,2.0,0.20117262002701805,394457.30302182614,1.7526825875833112,False
23
+ dose_1-rep_0-t_3,dose_1-rep_0,3.0,0.20117262002701805,618290.3220565522,2.401097863199693,False
24
+ dose_1-rep_0-t_4,dose_1-rep_0,4.0,0.20117262002701805,833511.6554962726,2.832015812996294,False
25
+ dose_1-rep_0-t_5,dose_1-rep_0,5.0,0.20117262002701805,988339.672526323,3.077820391955883,False
26
+ dose_1-rep_1-t_0,dose_1-rep_1,0.0,0.20117262002701805,117055.0,0.0,True
27
+ dose_1-rep_1-t_1,dose_1-rep_1,1.0,0.20117262002701805,222422.7611495618,0.9261178702440107,False
28
+ dose_1-rep_1-t_2,dose_1-rep_1,2.0,0.20117262002701805,394457.30302182614,1.7526825875833112,False
29
+ dose_1-rep_1-t_3,dose_1-rep_1,3.0,0.20117262002701805,618290.3220565522,2.401097863199693,False
30
+ dose_1-rep_1-t_4,dose_1-rep_1,4.0,0.20117262002701805,833511.6554962726,2.832015812996294,False
31
+ dose_1-rep_1-t_5,dose_1-rep_1,5.0,0.20117262002701805,988339.672526323,3.077820391955883,False
32
+ dose_1-rep_2-t_0,dose_1-rep_2,0.0,0.20117262002701805,117055.0,0.0,True
33
+ dose_1-rep_2-t_1,dose_1-rep_2,1.0,0.20117262002701805,222422.7611495618,0.9261178702440107,False
34
+ dose_1-rep_2-t_2,dose_1-rep_2,2.0,0.20117262002701805,394457.30302182614,1.7526825875833112,False
35
+ dose_1-rep_2-t_3,dose_1-rep_2,3.0,0.20117262002701805,618290.3220565522,2.401097863199693,False
36
+ dose_1-rep_2-t_4,dose_1-rep_2,4.0,0.20117262002701805,833511.6554962726,2.832015812996294,False
37
+ dose_1-rep_2-t_5,dose_1-rep_2,5.0,0.20117262002701805,988339.672526323,3.077820391955883,False
38
+ dose_2-rep_0-t_0,dose_2-rep_0,0.0,0.3278104266931334,117055.0,0.0,True
39
+ dose_2-rep_0-t_1,dose_2-rep_0,1.0,0.3278104266931334,208766.2945323125,0.834702246130147,False
40
+ dose_2-rep_0-t_2,dose_2-rep_0,2.0,0.3278104266931334,356837.17092046584,1.6080793449317268,False
41
+ dose_2-rep_0-t_3,dose_2-rep_0,3.0,0.3278104266931334,557154.5259169642,2.250890951794641,False
42
+ dose_2-rep_0-t_4,dose_2-rep_0,4.0,0.3278104266931334,767351.9087930336,2.712701791269896,False
43
+ dose_2-rep_0-t_5,dose_2-rep_0,5.0,0.3278104266931334,936068.5780837294,2.9994276677073946,False
44
+ dose_2-rep_1-t_0,dose_2-rep_1,0.0,0.3278104266931334,117055.0,0.0,True
45
+ dose_2-rep_1-t_1,dose_2-rep_1,1.0,0.3278104266931334,208766.2945323125,0.834702246130147,False
46
+ dose_2-rep_1-t_2,dose_2-rep_1,2.0,0.3278104266931334,356837.17092046584,1.6080793449317268,False
47
+ dose_2-rep_1-t_3,dose_2-rep_1,3.0,0.3278104266931334,557154.5259169642,2.250890951794641,False
48
+ dose_2-rep_1-t_4,dose_2-rep_1,4.0,0.3278104266931334,767351.9087930336,2.712701791269896,False
49
+ dose_2-rep_1-t_5,dose_2-rep_1,5.0,0.3278104266931334,936068.5780837294,2.9994276677073946,False
50
+ dose_2-rep_2-t_0,dose_2-rep_2,0.0,0.3278104266931334,117055.0,0.0,True
51
+ dose_2-rep_2-t_1,dose_2-rep_2,1.0,0.3278104266931334,208766.2945323125,0.834702246130147,False
52
+ dose_2-rep_2-t_2,dose_2-rep_2,2.0,0.3278104266931334,356837.17092046584,1.6080793449317268,False
53
+ dose_2-rep_2-t_3,dose_2-rep_2,3.0,0.3278104266931334,557154.5259169642,2.250890951794641,False
54
+ dose_2-rep_2-t_4,dose_2-rep_2,4.0,0.3278104266931334,767351.9087930336,2.712701791269896,False
55
+ dose_2-rep_2-t_5,dose_2-rep_2,5.0,0.3278104266931334,936068.5780837294,2.9994276677073946,False
56
+ dose_3-rep_0-t_0,dose_3-rep_0,0.0,0.5341665075212624,117055.0,0.0,True
57
+ dose_3-rep_0-t_1,dose_3-rep_0,1.0,0.5341665075212624,194167.85296808663,0.7301178030311807,False
58
+ dose_3-rep_0-t_2,dose_3-rep_0,2.0,0.5341665075212624,316942.8764619754,1.437036282232122,False
59
+ dose_3-rep_0-t_3,dose_3-rep_0,3.0,0.5341665075212624,489512.636121671,2.0641595411437166,False
60
+ dose_3-rep_0-t_4,dose_3-rep_0,4.0,0.5341665075212624,687865.3222216408,2.5549395655675515,False
61
+ dose_3-rep_0-t_5,dose_3-rep_0,5.0,0.5341665075212624,867383.0729686539,2.889482728766238,False
62
+ dose_3-rep_1-t_0,dose_3-rep_1,0.0,0.5341665075212624,117055.0,0.0,True
63
+ dose_3-rep_1-t_1,dose_3-rep_1,1.0,0.5341665075212624,194167.85296808663,0.7301178030311807,False
64
+ dose_3-rep_1-t_2,dose_3-rep_1,2.0,0.5341665075212624,316942.8764619754,1.437036282232122,False
65
+ dose_3-rep_1-t_3,dose_3-rep_1,3.0,0.5341665075212624,489512.636121671,2.0641595411437166,False
66
+ dose_3-rep_1-t_4,dose_3-rep_1,4.0,0.5341665075212624,687865.3222216408,2.5549395655675515,False
67
+ dose_3-rep_1-t_5,dose_3-rep_1,5.0,0.5341665075212624,867383.0729686539,2.889482728766238,False
68
+ dose_3-rep_2-t_0,dose_3-rep_2,0.0,0.5341665075212624,117055.0,0.0,True
69
+ dose_3-rep_2-t_1,dose_3-rep_2,1.0,0.5341665075212624,194167.85296808663,0.7301178030311807,False
70
+ dose_3-rep_2-t_2,dose_3-rep_2,2.0,0.5341665075212624,316942.8764619754,1.437036282232122,False
71
+ dose_3-rep_2-t_3,dose_3-rep_2,3.0,0.5341665075212624,489512.636121671,2.0641595411437166,False
72
+ dose_3-rep_2-t_4,dose_3-rep_2,4.0,0.5341665075212624,687865.3222216408,2.5549395655675515,False
73
+ dose_3-rep_2-t_5,dose_3-rep_2,5.0,0.5341665075212624,867383.0729686539,2.889482728766238,False
74
+ dose_4-rep_0-t_0,dose_4-rep_0,0.0,0.870423374374747,117055.0,0.0,True
75
+ dose_4-rep_0-t_1,dose_4-rep_0,1.0,0.870423374374747,179849.0377847039,0.619599880459945,False
76
+ dose_4-rep_0-t_2,dose_4-rep_0,2.0,0.870423374374747,278757.0181696531,1.2518215672959792,False
77
+ dose_4-rep_0-t_3,dose_4-rep_0,3.0,0.870423374374747,423504.06388361723,1.8551892530849372,False
78
+ dose_4-rep_0-t_4,dose_4-rep_0,4.0,0.870423374374747,605732.9764283933,2.3714953941895147,False
79
+ dose_4-rep_0-t_5,dose_4-rep_0,5.0,0.870423374374747,791779.1028853382,2.757911430955815,False
80
+ dose_4-rep_1-t_0,dose_4-rep_1,0.0,0.870423374374747,117055.0,0.0,True
81
+ dose_4-rep_1-t_1,dose_4-rep_1,1.0,0.870423374374747,179849.0377847039,0.619599880459945,False
82
+ dose_4-rep_1-t_2,dose_4-rep_1,2.0,0.870423374374747,278757.0181696531,1.2518215672959792,False
83
+ dose_4-rep_1-t_3,dose_4-rep_1,3.0,0.870423374374747,423504.06388361723,1.8551892530849372,False
84
+ dose_4-rep_1-t_4,dose_4-rep_1,4.0,0.870423374374747,605732.9764283933,2.3714953941895147,False
85
+ dose_4-rep_1-t_5,dose_4-rep_1,5.0,0.870423374374747,791779.1028853382,2.757911430955815,False
86
+ dose_4-rep_2-t_0,dose_4-rep_2,0.0,0.870423374374747,117055.0,0.0,True
87
+ dose_4-rep_2-t_1,dose_4-rep_2,1.0,0.870423374374747,179849.0377847039,0.619599880459945,False
88
+ dose_4-rep_2-t_2,dose_4-rep_2,2.0,0.870423374374747,278757.0181696531,1.2518215672959792,False
89
+ dose_4-rep_2-t_3,dose_4-rep_2,3.0,0.870423374374747,423504.06388361723,1.8551892530849372,False
90
+ dose_4-rep_2-t_4,dose_4-rep_2,4.0,0.870423374374747,605732.9764283933,2.3714953941895147,False
91
+ dose_4-rep_2-t_5,dose_4-rep_2,5.0,0.870423374374747,791779.1028853382,2.757911430955815,False
92
+ dose_5-rep_0-t_0,dose_5-rep_0,0.0,1.41835334112138,117055.0,0.0,True
93
+ dose_5-rep_0-t_1,dose_5-rep_0,1.0,1.41835334112138,167060.34358941315,0.5131827498868529,False
94
+ dose_5-rep_0-t_2,dose_5-rep_0,2.0,1.41835334112138,246041.4256362762,1.0717146800456518,False
95
+ dose_5-rep_0-t_3,dose_5-rep_0,3.0,1.41835334112138,367542.52779013116,1.6507246312376866,False
96
+ dose_5-rep_0-t_4,dose_5-rep_0,4.0,1.41835334112138,535033.3697428543,2.1924423140004268,False
97
+ dose_5-rep_0-t_5,dose_5-rep_0,5.0,1.41835334112138,726051.529930286,2.6328853835701116,False
98
+ dose_5-rep_1-t_0,dose_5-rep_1,0.0,1.41835334112138,117055.0,0.0,True
99
+ dose_5-rep_1-t_1,dose_5-rep_1,1.0,1.41835334112138,167060.34358941315,0.5131827498868529,False
100
+ dose_5-rep_1-t_2,dose_5-rep_1,2.0,1.41835334112138,246041.4256362762,1.0717146800456518,False
101
+ dose_5-rep_1-t_3,dose_5-rep_1,3.0,1.41835334112138,367542.52779013116,1.6507246312376866,False
102
+ dose_5-rep_1-t_4,dose_5-rep_1,4.0,1.41835334112138,535033.3697428543,2.1924423140004268,False
103
+ dose_5-rep_1-t_5,dose_5-rep_1,5.0,1.41835334112138,726051.529930286,2.6328853835701116,False
104
+ dose_5-rep_2-t_0,dose_5-rep_2,0.0,1.41835334112138,117055.0,0.0,True
105
+ dose_5-rep_2-t_1,dose_5-rep_2,1.0,1.41835334112138,167060.34358941315,0.5131827498868529,False
106
+ dose_5-rep_2-t_2,dose_5-rep_2,2.0,1.41835334112138,246041.4256362762,1.0717146800456518,False
107
+ dose_5-rep_2-t_3,dose_5-rep_2,3.0,1.41835334112138,367542.52779013116,1.6507246312376866,False
108
+ dose_5-rep_2-t_4,dose_5-rep_2,4.0,1.41835334112138,535033.3697428543,2.1924423140004268,False
109
+ dose_5-rep_2-t_5,dose_5-rep_2,5.0,1.41835334112138,726051.529930286,2.6328853835701116,False
110
+ dose_6-rep_0-t_0,dose_6-rep_0,0.0,2.3112042478354495,117055.0,0.0,True
111
+ dose_6-rep_0-t_1,dose_6-rep_0,1.0,2.3112042478354495,156640.12723312783,0.4202672814724111,False
112
+ dose_6-rep_0-t_2,dose_6-rep_0,2.0,2.3112042478354495,220761.033587925,0.9152989848252058,False
113
+ dose_6-rep_0-t_3,dose_6-rep_0,3.0,2.3112042478354495,325801.3065623647,1.4768058290896102,False
114
+ dose_6-rep_0-t_4,dose_6-rep_0,4.0,2.3112042478354495,483650.1317704568,2.0467772315256103,False
115
+ dose_6-rep_0-t_5,dose_6-rep_0,5.0,2.3112042478354495,680616.3580916729,2.5396552653559166,False
116
+ dose_6-rep_1-t_0,dose_6-rep_1,0.0,2.3112042478354495,117055.0,0.0,True
117
+ dose_6-rep_1-t_1,dose_6-rep_1,1.0,2.3112042478354495,156640.12723312783,0.4202672814724111,False
118
+ dose_6-rep_1-t_2,dose_6-rep_1,2.0,2.3112042478354495,220761.033587925,0.9152989848252058,False
119
+ dose_6-rep_1-t_3,dose_6-rep_1,3.0,2.3112042478354495,325801.3065623647,1.4768058290896102,False
120
+ dose_6-rep_1-t_4,dose_6-rep_1,4.0,2.3112042478354495,483650.1317704568,2.0467772315256103,False
121
+ dose_6-rep_1-t_5,dose_6-rep_1,5.0,2.3112042478354495,680616.3580916729,2.5396552653559166,False
122
+ dose_6-rep_2-t_0,dose_6-rep_2,0.0,2.3112042478354495,117055.0,0.0,True
123
+ dose_6-rep_2-t_1,dose_6-rep_2,1.0,2.3112042478354495,156640.12723312783,0.4202672814724111,False
124
+ dose_6-rep_2-t_2,dose_6-rep_2,2.0,2.3112042478354495,220761.033587925,0.9152989848252058,False
125
+ dose_6-rep_2-t_3,dose_6-rep_2,3.0,2.3112042478354495,325801.3065623647,1.4768058290896102,False
126
+ dose_6-rep_2-t_4,dose_6-rep_2,4.0,2.3112042478354495,483650.1317704568,2.0467772315256103,False
127
+ dose_6-rep_2-t_5,dose_6-rep_2,5.0,2.3112042478354495,680616.3580916729,2.5396552653559166,False
128
+ dose_7-rep_0-t_0,dose_7-rep_0,0.0,3.7661032130325105,117055.0,0.0,True
129
+ dose_7-rep_0-t_1,dose_7-rep_0,1.0,3.7661032130325105,148801.10472623174,0.3461986768823421,False
130
+ dose_7-rep_0-t_2,dose_7-rep_0,2.0,3.7661032130325105,202753.24840351264,0.7925384682803503,False
131
+ dose_7-rep_0-t_3,dose_7-rep_0,3.0,3.7661032130325105,297423.17062527494,1.3453304838789686,False
132
+ dose_7-rep_0-t_4,dose_7-rep_0,4.0,3.7661032130325105,450412.9783225577,1.9440618377944692,False
133
+ dose_7-rep_0-t_5,dose_7-rep_0,5.0,3.7661032130325105,653766.1815845432,2.4815881899955756,False
134
+ dose_7-rep_1-t_0,dose_7-rep_1,0.0,3.7661032130325105,117055.0,0.0,True
135
+ dose_7-rep_1-t_1,dose_7-rep_1,1.0,3.7661032130325105,148801.10472623174,0.3461986768823421,False
136
+ dose_7-rep_1-t_2,dose_7-rep_1,2.0,3.7661032130325105,202753.24840351264,0.7925384682803503,False
137
+ dose_7-rep_1-t_3,dose_7-rep_1,3.0,3.7661032130325105,297423.17062527494,1.3453304838789686,False
138
+ dose_7-rep_1-t_4,dose_7-rep_1,4.0,3.7661032130325105,450412.9783225577,1.9440618377944692,False
139
+ dose_7-rep_1-t_5,dose_7-rep_1,5.0,3.7661032130325105,653766.1815845432,2.4815881899955756,False
140
+ dose_7-rep_2-t_0,dose_7-rep_2,0.0,3.7661032130325105,117055.0,0.0,True
141
+ dose_7-rep_2-t_1,dose_7-rep_2,1.0,3.7661032130325105,148801.10472623174,0.3461986768823421,False
142
+ dose_7-rep_2-t_2,dose_7-rep_2,2.0,3.7661032130325105,202753.24840351264,0.7925384682803503,False
143
+ dose_7-rep_2-t_3,dose_7-rep_2,3.0,3.7661032130325105,297423.17062527494,1.3453304838789686,False
144
+ dose_7-rep_2-t_4,dose_7-rep_2,4.0,3.7661032130325105,450412.9783225577,1.9440618377944692,False
145
+ dose_7-rep_2-t_5,dose_7-rep_2,5.0,3.7661032130325105,653766.1815845432,2.4815881899955756,False
146
+ dose_8-rep_0-t_0,dose_8-rep_0,0.0,6.1368584903291605,117055.0,0.0,True
147
+ dose_8-rep_0-t_1,dose_8-rep_0,1.0,6.1368584903291605,143258.0596617452,0.29142974648746456,False
148
+ dose_8-rep_0-t_2,dose_8-rep_0,2.0,6.1368584903291605,190613.57755750668,0.7034643267976026,False
149
+ dose_8-rep_0-t_3,dose_8-rep_0,3.0,6.1368584903291605,279137.85937086184,1.2537912494702186,False
150
+ dose_8-rep_0-t_4,dose_8-rep_0,4.0,6.1368584903291605,430135.55843049457,1.8776048404415195,False
151
+ dose_8-rep_0-t_5,dose_8-rep_0,5.0,6.1368584903291605,639010.8809450462,2.448653936862557,False
152
+ dose_8-rep_1-t_0,dose_8-rep_1,0.0,6.1368584903291605,117055.0,0.0,True
153
+ dose_8-rep_1-t_1,dose_8-rep_1,1.0,6.1368584903291605,143258.0596617452,0.29142974648746456,False
154
+ dose_8-rep_1-t_2,dose_8-rep_1,2.0,6.1368584903291605,190613.57755750668,0.7034643267976026,False
155
+ dose_8-rep_1-t_3,dose_8-rep_1,3.0,6.1368584903291605,279137.85937086184,1.2537912494702186,False
156
+ dose_8-rep_1-t_4,dose_8-rep_1,4.0,6.1368584903291605,430135.55843049457,1.8776048404415195,False
157
+ dose_8-rep_1-t_5,dose_8-rep_1,5.0,6.1368584903291605,639010.8809450462,2.448653936862557,False
158
+ dose_8-rep_2-t_0,dose_8-rep_2,0.0,6.1368584903291605,117055.0,0.0,True
159
+ dose_8-rep_2-t_1,dose_8-rep_2,1.0,6.1368584903291605,143258.0596617452,0.29142974648746456,False
160
+ dose_8-rep_2-t_2,dose_8-rep_2,2.0,6.1368584903291605,190613.57755750668,0.7034643267976026,False
161
+ dose_8-rep_2-t_3,dose_8-rep_2,3.0,6.1368584903291605,279137.85937086184,1.2537912494702186,False
162
+ dose_8-rep_2-t_4,dose_8-rep_2,4.0,6.1368584903291605,430135.55843049457,1.8776048404415195,False
163
+ dose_8-rep_2-t_5,dose_8-rep_2,5.0,6.1368584903291605,639010.8809450462,2.448653936862557,False
164
+ dose_9-rep_0-t_0,dose_9-rep_0,0.0,10.0,117055.0,0.0,True
165
+ dose_9-rep_0-t_1,dose_9-rep_0,1.0,10.0,139507.74375037773,0.2531586444918018,False
166
+ dose_9-rep_0-t_2,dose_9-rep_0,2.0,10.0,182703.5738878364,0.642318294551051,False
167
+ dose_9-rep_0-t_3,dose_9-rep_0,3.0,10.0,267670.4959774961,1.1932715660229316,False
168
+ dose_9-rep_0-t_4,dose_9-rep_0,4.0,10.0,418045.1524475581,1.8364722136788576,False
169
+ dose_9-rep_0-t_5,dose_9-rep_0,5.0,10.0,631095.7950410473,2.4306724503882338,False
170
+ dose_9-rep_1-t_0,dose_9-rep_1,0.0,10.0,117055.0,0.0,True
171
+ dose_9-rep_1-t_1,dose_9-rep_1,1.0,10.0,139507.74375037773,0.2531586444918018,False
172
+ dose_9-rep_1-t_2,dose_9-rep_1,2.0,10.0,182703.5738878364,0.642318294551051,False
173
+ dose_9-rep_1-t_3,dose_9-rep_1,3.0,10.0,267670.4959774961,1.1932715660229316,False
174
+ dose_9-rep_1-t_4,dose_9-rep_1,4.0,10.0,418045.1524475581,1.8364722136788576,False
175
+ dose_9-rep_1-t_5,dose_9-rep_1,5.0,10.0,631095.7950410473,2.4306724503882338,False
176
+ dose_9-rep_2-t_0,dose_9-rep_2,0.0,10.0,117055.0,0.0,True
177
+ dose_9-rep_2-t_1,dose_9-rep_2,1.0,10.0,139507.74375037773,0.2531586444918018,False
178
+ dose_9-rep_2-t_2,dose_9-rep_2,2.0,10.0,182703.5738878364,0.642318294551051,False
179
+ dose_9-rep_2-t_3,dose_9-rep_2,3.0,10.0,267670.4959774961,1.1932715660229316,False
180
+ dose_9-rep_2-t_4,dose_9-rep_2,4.0,10.0,418045.1524475581,1.8364722136788576,False
181
+ dose_9-rep_2-t_5,dose_9-rep_2,5.0,10.0,631095.7950410473,2.4306724503882338,False
data/examples/single-point/dose-response_strain_meta.csv ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ strain_id,dose_params,is_reference,is_spike
2
+ wt,"[10000.0, 1.0]",True,False
3
+ spike,"[1e-09, 0.0]",False,True
4
+ mut_A,"[0.1, 0.0]",False,False
5
+ mut_B,"[1.0, 0.0]",False,False
6
+ mut_C,"[10.0, 0.0]",False,False
7
+ mut_D,"[1.0, 0.5]",False,False
8
+ ctrl_000,"[10000.0, 1.0]",False,False
9
+ ctrl_001,"[10000.0, 1.0]",False,False
10
+ ctrl_002,"[10000.0, 1.0]",False,False
11
+ ctrl_003,"[10000.0, 1.0]",False,False
12
+ ctrl_004,"[10000.0, 1.0]",False,False
13
+ ctrl_005,"[10000.0, 1.0]",False,False
14
+ ctrl_006,"[10000.0, 1.0]",False,False
15
+ ctrl_007,"[10000.0, 1.0]",False,False
16
+ ctrl_008,"[10000.0, 1.0]",False,False
17
+ ctrl_009,"[10000.0, 1.0]",False,False
18
+ str_000,"[np.float64(1.160934072833945), 0.0]",False,False
19
+ str_001,"[np.float64(0.6583176596280784), 0.0]",False,False
20
+ str_002,"[np.float64(1.2878968798670738), 0.0]",False,False
21
+ str_003,"[np.float64(1.046052043589046), 0.0]",False,False
22
+ str_004,"[np.float64(0.1412660218314743), 0.0]",False,False
23
+ str_005,"[np.float64(1.463433527455134), 0.0]",False,False
24
+ str_006,"[np.float64(1.1417095529855295), 0.0]",False,False
25
+ str_007,"[np.float64(1.1790964579154308), 0.0]",False,False
26
+ str_008,"[np.float64(0.1921704490133188), 0.0]",False,False
27
+ str_009,"[np.float64(0.6755789068433506), 0.0]",False,False
28
+ str_010,"[np.float64(0.5561970363488719), 0.0]",False,False
29
+ str_011,"[np.float64(1.3901474832729028), 0.0]",False,False
30
+ str_012,"[np.float64(0.9657976801209969), 0.0]",False,False
31
+ str_013,"[np.float64(1.234142419906245), 0.0]",False,False
32
+ str_014,"[np.float64(0.6651212982409966), 0.0]",False,False
33
+ str_015,"[np.float64(0.34085808267716533), 0.0]",False,False
34
+ str_016,"[np.float64(0.8318771805237521), 0.0]",False,False
35
+ str_017,"[np.float64(0.09572588415626299), 0.0]",False,False
36
+ str_018,"[np.float64(1.241446757988873), 0.0]",False,False
37
+ str_019,"[np.float64(0.9474965986830972), 0.0]",False,False
38
+ str_020,"[np.float64(1.1371316101280606), 0.0]",False,False
39
+ str_021,"[np.float64(0.5317889521948025), 0.0]",False,False
40
+ str_022,"[np.float64(1.4560470365923548), 0.0]",False,False
41
+ str_023,"[np.float64(1.3396816819832966), 0.0]",False,False
42
+ str_024,"[np.float64(1.1675752456106427), 0.0]",False,False
43
+ str_025,"[np.float64(0.29195806177795136), 0.0]",False,False
44
+ str_026,"[np.float64(0.7000815055905513), 0.0]",False,False
45
+ str_027,"[np.float64(0.06570564868084317), 0.0]",False,False
46
+ str_028,"[np.float64(0.23143423810132174), 0.0]",False,False
47
+ str_029,"[np.float64(1.024573429863682), 0.0]",False,False
48
+ str_030,"[np.float64(1.1171432338617258), 0.0]",False,False
49
+ str_031,"[np.float64(1.451264598651315), 0.0]",False,False
50
+ str_032,"[np.float64(0.4887380372072279), 0.0]",False,False
51
+ str_033,"[np.float64(0.5556895590523033), 0.0]",False,False
52
+ str_034,"[np.float64(0.7043337169137118), 0.0]",False,False
53
+ str_035,"[np.float64(0.28420703862642854), 0.0]",False,False
54
+ str_036,"[np.float64(0.19488225800320746), 0.0]",False,False
55
+ str_037,"[np.float64(0.7135573893389007), 0.0]",False,False
56
+ str_038,"[np.float64(0.34036402357632617), 0.0]",False,False
57
+ str_039,"[np.float64(1.0047209920237656), 0.0]",False,False
58
+ str_040,"[np.float64(0.6557278783084961), 0.0]",False,False
59
+ str_041,"[np.float64(1.249017294086756), 0.0]",False,False
60
+ str_042,"[np.float64(1.0503976530033736), 0.0]",False,False
61
+ str_043,"[np.float64(0.4685499620730616), 0.0]",False,False
62
+ str_044,"[np.float64(1.2483897020928016), 0.0]",False,False
63
+ str_045,"[np.float64(1.2071465362452027), 0.0]",False,False
64
+ str_046,"[np.float64(0.5812175685452616), 0.0]",False,False
65
+ str_047,"[np.float64(0.43249215589536616), 0.0]",False,False
66
+ str_048,"[np.float64(1.0237432559624633), 0.0]",False,False
67
+ str_049,"[np.float64(0.2096287254139647), 0.0]",False,False
68
+ str_050,"[np.float64(0.2998623037126625), 0.0]",False,False
69
+ str_051,"[np.float64(0.011043404626508269), 0.0]",False,False
70
+ str_052,"[np.float64(1.1803865662532076), 0.0]",False,False
71
+ str_053,"[np.float64(0.9972762848880482), 0.0]",False,False
72
+ str_054,"[np.float64(1.0577480679395026), 0.0]",False,False
73
+ str_055,"[np.float64(1.1710935465329517), 0.0]",False,False
74
+ str_056,"[np.float64(0.6883736633075099), 0.0]",False,False
75
+ str_057,"[np.float64(0.8531117939293407), 0.0]",False,False
76
+ str_058,"[np.float64(0.20969549719148617), 0.0]",False,False
77
+ str_059,"[np.float64(0.17179511030396016), 0.0]",False,False
78
+ str_060,"[np.float64(1.0026044426857075), 0.0]",False,False
79
+ str_061,"[np.float64(0.7066443092146988), 0.0]",False,False
80
+ str_062,"[np.float64(0.8478541597217832), 0.0]",False,False
81
+ str_063,"[np.float64(1.1474982861240384), 0.0]",False,False
82
+ str_064,"[np.float64(0.9520774800008862), 0.0]",False,False
83
+ str_065,"[np.float64(0.8303691009869938), 0.0]",False,False
84
+ str_066,"[np.float64(0.8388107411181203), 0.0]",False,False
85
+ str_067,"[np.float64(0.4559251470939183), 0.0]",False,False
86
+ str_068,"[np.float64(0.04622675185190911), 0.0]",False,False
87
+ str_069,"[np.float64(0.6550760838485435), 0.0]",False,False
88
+ str_070,"[np.float64(0.3218770092292938), 0.0]",False,False
89
+ str_071,"[np.float64(0.6127929655869542), 0.0]",False,False
90
+ str_072,"[np.float64(1.2801046099022493), 0.0]",False,False
91
+ str_073,"[np.float64(0.3509092287980111), 0.0]",False,False
92
+ str_074,"[np.float64(0.08745411253359903), 0.0]",False,False
93
+ str_075,"[np.float64(0.4220758380329948), 0.0]",False,False
94
+ str_076,"[np.float64(0.4403906366500254), 0.0]",False,False
95
+ str_077,"[np.float64(0.9928747720903426), 0.0]",False,False
96
+ str_078,"[np.float64(0.8355482285119176), 0.0]",False,False
97
+ str_079,"[np.float64(1.1758473136596201), 0.0]",False,False
98
+ str_080,"[np.float64(0.9964703104910813), 0.0]",False,False
99
+ str_081,"[np.float64(0.6095802921601058), 0.0]",False,False
100
+ str_082,"[np.float64(1.221030576999052), 0.0]",False,False
101
+ str_083,"[np.float64(0.25045937986155586), 0.0]",False,False
102
+ str_084,"[np.float64(0.034068109700790716), 0.0]",False,False
103
+ str_085,"[np.float64(0.13507179116346263), 0.0]",False,False
104
+ str_086,"[np.float64(1.0835390258946753), 0.0]",False,False
105
+ str_087,"[np.float64(0.6928158453770811), 0.0]",False,False
106
+ str_088,"[np.float64(0.2419076685504027), 0.0]",False,False
107
+ str_089,"[np.float64(0.7515671626550453), 0.0]",False,False
108
+ str_090,"[np.float64(0.22846815406975263), 0.0]",False,False
109
+ str_091,"[np.float64(1.044480562616604), 0.0]",False,False
110
+ str_092,"[np.float64(0.669234413361046), 0.0]",False,False
111
+ str_093,"[np.float64(0.5715318391447237), 0.0]",False,False
112
+ str_094,"[np.float64(0.4522681337218148), 0.0]",False,False
113
+ str_095,"[np.float64(0.9454238896783327), 0.0]",False,False
114
+ str_096,"[np.float64(0.5427189158300856), 0.0]",False,False
115
+ str_097,"[np.float64(0.1314748789741515), 0.0]",False,False
116
+ str_098,"[np.float64(0.17700885318077297), 0.0]",False,False
117
+ str_099,"[np.float64(1.4428464968242718), 0.0]",False,False
data/examples/single-point/test_count.csv ADDED
The diff for this file is too large to render. See raw diff
 
data/examples/single-point/test_sample_meta.csv ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ sample_id,replicate,timepoint,dose,growth,generations,is_t0
2
+ dose_0-rep_0-t_0,dose_0-rep_0,0.0,1.0,117055.0,0.0,True
3
+ dose_0-rep_0-t_1,dose_0-rep_0,1.0,1.0,235530.43107290982,1.008726910963631,False
4
+ dose_0-rep_0-t_2,dose_0-rep_0,2.0,1.0,457470.31913837086,1.966491583356558,False
5
+ dose_0-rep_0-t_3,dose_0-rep_0,3.0,1.0,745400.9751793981,2.6708301460588992,False
6
+ dose_0-rep_0-t_4,dose_0-rep_0,4.0,1.0,972048.1257198368,3.053841182431035,False
7
+ dose_0-rep_0-t_5,dose_0-rep_0,5.0,1.0,1088034.9223100767,3.216466397510134,False
8
+ dose_0-rep_1-t_0,dose_0-rep_1,0.0,1.0,117055.0,0.0,True
9
+ dose_0-rep_1-t_1,dose_0-rep_1,1.0,1.0,235530.43107290982,1.008726910963631,False
10
+ dose_0-rep_1-t_2,dose_0-rep_1,2.0,1.0,457470.31913837086,1.966491583356558,False
11
+ dose_0-rep_1-t_3,dose_0-rep_1,3.0,1.0,745400.9751793981,2.6708301460588992,False
12
+ dose_0-rep_1-t_4,dose_0-rep_1,4.0,1.0,972048.1257198368,3.053841182431035,False
13
+ dose_0-rep_1-t_5,dose_0-rep_1,5.0,1.0,1088034.9223100767,3.216466397510134,False
14
+ dose_0-rep_2-t_0,dose_0-rep_2,0.0,1.0,117055.0,0.0,True
15
+ dose_0-rep_2-t_1,dose_0-rep_2,1.0,1.0,235530.43107290982,1.008726910963631,False
16
+ dose_0-rep_2-t_2,dose_0-rep_2,2.0,1.0,457470.31913837086,1.966491583356558,False
17
+ dose_0-rep_2-t_3,dose_0-rep_2,3.0,1.0,745400.9751793981,2.6708301460588992,False
18
+ dose_0-rep_2-t_4,dose_0-rep_2,4.0,1.0,972048.1257198368,3.053841182431035,False
19
+ dose_0-rep_2-t_5,dose_0-rep_2,5.0,1.0,1088034.9223100767,3.216466397510134,False
data/examples/single-point/test_strain_meta.csv ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ strain_id,true_fitness,is_reference,is_spike
2
+ wt,1.0,True,False
3
+ spike,0.0,False,True
4
+ mut_A,1.5,False,False
5
+ mut_B,0.5,False,False
6
+ mut_C,0.9,False,False
7
+ mut_D,1.1,False,False
8
+ ctrl_000,1.0,False,False
9
+ ctrl_001,1.0,False,False
10
+ ctrl_002,1.0,False,False
11
+ ctrl_003,1.0,False,False
12
+ ctrl_004,1.0,False,False
13
+ ctrl_005,1.0,False,False
14
+ ctrl_006,1.0,False,False
15
+ ctrl_007,1.0,False,False
16
+ ctrl_008,1.0,False,False
17
+ ctrl_009,1.0,False,False
18
+ str_000,1.160934072833945,False,False
19
+ str_001,0.6583176596280784,False,False
20
+ str_002,1.2878968798670738,False,False
21
+ str_003,1.046052043589046,False,False
22
+ str_004,0.1412660218314743,False,False
23
+ str_005,1.463433527455134,False,False
24
+ str_006,1.1417095529855295,False,False
25
+ str_007,1.1790964579154308,False,False
26
+ str_008,0.1921704490133188,False,False
27
+ str_009,0.6755789068433506,False,False
28
+ str_010,0.5561970363488719,False,False
29
+ str_011,1.3901474832729028,False,False
30
+ str_012,0.9657976801209969,False,False
31
+ str_013,1.234142419906245,False,False
32
+ str_014,0.6651212982409966,False,False
33
+ str_015,0.34085808267716533,False,False
34
+ str_016,0.8318771805237521,False,False
35
+ str_017,0.09572588415626299,False,False
36
+ str_018,1.241446757988873,False,False
37
+ str_019,0.9474965986830972,False,False
38
+ str_020,1.1371316101280606,False,False
39
+ str_021,0.5317889521948025,False,False
40
+ str_022,1.4560470365923548,False,False
41
+ str_023,1.3396816819832966,False,False
42
+ str_024,1.1675752456106427,False,False
43
+ str_025,0.29195806177795136,False,False
44
+ str_026,0.7000815055905513,False,False
45
+ str_027,0.06570564868084317,False,False
46
+ str_028,0.23143423810132174,False,False
47
+ str_029,1.024573429863682,False,False
48
+ str_030,1.1171432338617258,False,False
49
+ str_031,1.451264598651315,False,False
50
+ str_032,0.4887380372072279,False,False
51
+ str_033,0.5556895590523033,False,False
52
+ str_034,0.7043337169137118,False,False
53
+ str_035,0.28420703862642854,False,False
54
+ str_036,0.19488225800320746,False,False
55
+ str_037,0.7135573893389007,False,False
56
+ str_038,0.34036402357632617,False,False
57
+ str_039,1.0047209920237656,False,False
58
+ str_040,0.6557278783084961,False,False
59
+ str_041,1.249017294086756,False,False
60
+ str_042,1.0503976530033736,False,False
61
+ str_043,0.4685499620730616,False,False
62
+ str_044,1.2483897020928016,False,False
63
+ str_045,1.2071465362452027,False,False
64
+ str_046,0.5812175685452616,False,False
65
+ str_047,0.43249215589536616,False,False
66
+ str_048,1.0237432559624633,False,False
67
+ str_049,0.2096287254139647,False,False
68
+ str_050,0.2998623037126625,False,False
69
+ str_051,0.011043404626508269,False,False
70
+ str_052,1.1803865662532076,False,False
71
+ str_053,0.9972762848880482,False,False
72
+ str_054,1.0577480679395026,False,False
73
+ str_055,1.1710935465329517,False,False
74
+ str_056,0.6883736633075099,False,False
75
+ str_057,0.8531117939293407,False,False
76
+ str_058,0.20969549719148617,False,False
77
+ str_059,0.17179511030396016,False,False
78
+ str_060,1.0026044426857075,False,False
79
+ str_061,0.7066443092146988,False,False
80
+ str_062,0.8478541597217832,False,False
81
+ str_063,1.1474982861240384,False,False
82
+ str_064,0.9520774800008862,False,False
83
+ str_065,0.8303691009869938,False,False
84
+ str_066,0.8388107411181203,False,False
85
+ str_067,0.4559251470939183,False,False
86
+ str_068,0.04622675185190911,False,False
87
+ str_069,0.6550760838485435,False,False
88
+ str_070,0.3218770092292938,False,False
89
+ str_071,0.6127929655869542,False,False
90
+ str_072,1.2801046099022493,False,False
91
+ str_073,0.3509092287980111,False,False
92
+ str_074,0.08745411253359903,False,False
93
+ str_075,0.4220758380329948,False,False
94
+ str_076,0.4403906366500254,False,False
95
+ str_077,0.9928747720903426,False,False
96
+ str_078,0.8355482285119176,False,False
97
+ str_079,1.1758473136596201,False,False
98
+ str_080,0.9964703104910813,False,False
99
+ str_081,0.6095802921601058,False,False
100
+ str_082,1.221030576999052,False,False
101
+ str_083,0.25045937986155586,False,False
102
+ str_084,0.034068109700790716,False,False
103
+ str_085,0.13507179116346263,False,False
104
+ str_086,1.0835390258946753,False,False
105
+ str_087,0.6928158453770811,False,False
106
+ str_088,0.2419076685504027,False,False
107
+ str_089,0.7515671626550453,False,False
108
+ str_090,0.22846815406975263,False,False
109
+ str_091,1.044480562616604,False,False
110
+ str_092,0.669234413361046,False,False
111
+ str_093,0.5715318391447237,False,False
112
+ str_094,0.4522681337218148,False,False
113
+ str_095,0.9454238896783327,False,False
114
+ str_096,0.5427189158300856,False,False
115
+ str_097,0.1314748789741515,False,False
116
+ str_098,0.17700885318077297,False,False
117
+ str_099,1.4428464968242718,False,False
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ anndata
2
+ carabiner-tools[pd,mpl]>=0.0.5.post3
3
+ gradio==5.50.0
4
+ nemony
5
+ numpy
6
+ scipy
7
+ scikit-learn
8
+ statsmodels