biopipen 0.21.0__py3-none-any.whl → 0.34.26__py3-none-any.whl

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.
Files changed (290) hide show
  1. biopipen/__init__.py +1 -1
  2. biopipen/core/config.toml +28 -0
  3. biopipen/core/filters.py +79 -4
  4. biopipen/core/proc.py +12 -3
  5. biopipen/core/testing.py +75 -3
  6. biopipen/ns/bam.py +148 -6
  7. biopipen/ns/bed.py +75 -0
  8. biopipen/ns/cellranger.py +186 -0
  9. biopipen/ns/cellranger_pipeline.py +126 -0
  10. biopipen/ns/cnv.py +19 -3
  11. biopipen/ns/cnvkit.py +1 -1
  12. biopipen/ns/cnvkit_pipeline.py +20 -12
  13. biopipen/ns/delim.py +34 -35
  14. biopipen/ns/gene.py +68 -23
  15. biopipen/ns/gsea.py +63 -37
  16. biopipen/ns/misc.py +39 -14
  17. biopipen/ns/plot.py +304 -1
  18. biopipen/ns/protein.py +183 -0
  19. biopipen/ns/regulatory.py +290 -0
  20. biopipen/ns/rnaseq.py +142 -5
  21. biopipen/ns/scrna.py +2053 -473
  22. biopipen/ns/scrna_metabolic_landscape.py +228 -382
  23. biopipen/ns/snp.py +659 -0
  24. biopipen/ns/stats.py +484 -0
  25. biopipen/ns/tcr.py +683 -98
  26. biopipen/ns/vcf.py +236 -2
  27. biopipen/ns/web.py +97 -6
  28. biopipen/reports/bam/CNVpytor.svelte +4 -9
  29. biopipen/reports/cellranger/CellRangerCount.svelte +18 -0
  30. biopipen/reports/cellranger/CellRangerSummary.svelte +16 -0
  31. biopipen/reports/cellranger/CellRangerVdj.svelte +18 -0
  32. biopipen/reports/cnvkit/CNVkitDiagram.svelte +1 -1
  33. biopipen/reports/cnvkit/CNVkitHeatmap.svelte +1 -1
  34. biopipen/reports/cnvkit/CNVkitScatter.svelte +1 -1
  35. biopipen/reports/common.svelte +15 -0
  36. biopipen/reports/protein/ProdigySummary.svelte +16 -0
  37. biopipen/reports/scrna/CellsDistribution.svelte +4 -39
  38. biopipen/reports/scrna/DimPlots.svelte +1 -1
  39. biopipen/reports/scrna/MarkersFinder.svelte +6 -126
  40. biopipen/reports/scrna/MetaMarkers.svelte +3 -75
  41. biopipen/reports/scrna/RadarPlots.svelte +4 -20
  42. biopipen/reports/scrna_metabolic_landscape/MetabolicFeatures.svelte +61 -22
  43. biopipen/reports/scrna_metabolic_landscape/MetabolicPathwayActivity.svelte +88 -82
  44. biopipen/reports/scrna_metabolic_landscape/MetabolicPathwayHeterogeneity.svelte +70 -10
  45. biopipen/reports/snp/PlinkCallRate.svelte +24 -0
  46. biopipen/reports/snp/PlinkFreq.svelte +18 -0
  47. biopipen/reports/snp/PlinkHWE.svelte +18 -0
  48. biopipen/reports/snp/PlinkHet.svelte +18 -0
  49. biopipen/reports/snp/PlinkIBD.svelte +18 -0
  50. biopipen/reports/tcr/CDR3AAPhyschem.svelte +19 -66
  51. biopipen/reports/tcr/ClonalStats.svelte +16 -0
  52. biopipen/reports/tcr/CloneResidency.svelte +3 -93
  53. biopipen/reports/tcr/Immunarch.svelte +4 -155
  54. biopipen/reports/tcr/TCRClusterStats.svelte +3 -45
  55. biopipen/reports/tcr/TESSA.svelte +11 -28
  56. biopipen/reports/utils/misc.liq +22 -7
  57. biopipen/scripts/bam/BamMerge.py +11 -15
  58. biopipen/scripts/bam/BamSampling.py +90 -0
  59. biopipen/scripts/bam/BamSort.py +141 -0
  60. biopipen/scripts/bam/BamSplitChroms.py +10 -10
  61. biopipen/scripts/bam/BamSubsetByBed.py +38 -0
  62. biopipen/scripts/bam/CNAClinic.R +41 -5
  63. biopipen/scripts/bam/CNVpytor.py +153 -54
  64. biopipen/scripts/bam/ControlFREEC.py +13 -14
  65. biopipen/scripts/bam/SamtoolsView.py +33 -0
  66. biopipen/scripts/bed/Bed2Vcf.py +5 -5
  67. biopipen/scripts/bed/BedConsensus.py +5 -5
  68. biopipen/scripts/bed/BedLiftOver.sh +6 -4
  69. biopipen/scripts/bed/BedtoolsIntersect.py +54 -0
  70. biopipen/scripts/bed/BedtoolsMakeWindows.py +47 -0
  71. biopipen/scripts/bed/BedtoolsMerge.py +4 -4
  72. biopipen/scripts/cellranger/CellRangerCount.py +138 -0
  73. biopipen/scripts/cellranger/CellRangerSummary.R +181 -0
  74. biopipen/scripts/cellranger/CellRangerVdj.py +112 -0
  75. biopipen/scripts/cnv/AneuploidyScore.R +55 -20
  76. biopipen/scripts/cnv/AneuploidyScoreSummary.R +221 -163
  77. biopipen/scripts/cnv/TMADScore.R +25 -9
  78. biopipen/scripts/cnv/TMADScoreSummary.R +57 -86
  79. biopipen/scripts/cnvkit/CNVkitAccess.py +7 -6
  80. biopipen/scripts/cnvkit/CNVkitAutobin.py +26 -18
  81. biopipen/scripts/cnvkit/CNVkitBatch.py +6 -6
  82. biopipen/scripts/cnvkit/CNVkitCall.py +3 -3
  83. biopipen/scripts/cnvkit/CNVkitCoverage.py +4 -3
  84. biopipen/scripts/cnvkit/CNVkitDiagram.py +5 -5
  85. biopipen/scripts/cnvkit/CNVkitFix.py +3 -3
  86. biopipen/scripts/cnvkit/CNVkitGuessBaits.py +12 -8
  87. biopipen/scripts/cnvkit/CNVkitHeatmap.py +5 -5
  88. biopipen/scripts/cnvkit/CNVkitReference.py +6 -5
  89. biopipen/scripts/cnvkit/CNVkitScatter.py +5 -5
  90. biopipen/scripts/cnvkit/CNVkitSegment.py +5 -5
  91. biopipen/scripts/cnvkit/guess_baits.py +166 -93
  92. biopipen/scripts/delim/RowsBinder.R +1 -1
  93. biopipen/scripts/delim/SampleInfo.R +116 -118
  94. biopipen/scripts/gene/GeneNameConversion.R +67 -0
  95. biopipen/scripts/gene/GenePromoters.R +61 -0
  96. biopipen/scripts/gsea/Enrichr.R +5 -5
  97. biopipen/scripts/gsea/FGSEA.R +184 -50
  98. biopipen/scripts/gsea/GSEA.R +2 -2
  99. biopipen/scripts/gsea/PreRank.R +5 -5
  100. biopipen/scripts/misc/Config2File.py +2 -2
  101. biopipen/scripts/misc/Plot.R +80 -0
  102. biopipen/scripts/misc/Shell.sh +15 -0
  103. biopipen/scripts/misc/Str2File.py +2 -2
  104. biopipen/scripts/plot/Heatmap.R +3 -3
  105. biopipen/scripts/plot/Manhattan.R +147 -0
  106. biopipen/scripts/plot/QQPlot.R +146 -0
  107. biopipen/scripts/plot/ROC.R +88 -0
  108. biopipen/scripts/plot/Scatter.R +112 -0
  109. biopipen/scripts/plot/VennDiagram.R +5 -9
  110. biopipen/scripts/protein/MMCIF2PDB.py +33 -0
  111. biopipen/scripts/protein/PDB2Fasta.py +60 -0
  112. biopipen/scripts/protein/Prodigy.py +119 -0
  113. biopipen/scripts/protein/ProdigySummary.R +140 -0
  114. biopipen/scripts/protein/RMSD.py +178 -0
  115. biopipen/scripts/regulatory/MotifAffinityTest.R +102 -0
  116. biopipen/scripts/regulatory/MotifAffinityTest_AtSNP.R +127 -0
  117. biopipen/scripts/regulatory/MotifAffinityTest_MotifBreakR.R +104 -0
  118. biopipen/scripts/regulatory/MotifScan.py +159 -0
  119. biopipen/scripts/regulatory/VariantMotifPlot.R +78 -0
  120. biopipen/scripts/regulatory/motifs-common.R +324 -0
  121. biopipen/scripts/rnaseq/Simulation-ESCO.R +180 -0
  122. biopipen/scripts/rnaseq/Simulation-RUVcorr.R +45 -0
  123. biopipen/scripts/rnaseq/Simulation.R +21 -0
  124. biopipen/scripts/rnaseq/UnitConversion.R +325 -54
  125. biopipen/scripts/scrna/AnnData2Seurat.R +40 -0
  126. biopipen/scripts/scrna/CCPlotR-patch.R +161 -0
  127. biopipen/scripts/scrna/CellCellCommunication.py +150 -0
  128. biopipen/scripts/scrna/CellCellCommunicationPlots.R +93 -0
  129. biopipen/scripts/scrna/CellSNPLite.py +30 -0
  130. biopipen/scripts/scrna/CellTypeAnnotation-celltypist.R +185 -0
  131. biopipen/scripts/scrna/CellTypeAnnotation-direct.R +68 -31
  132. biopipen/scripts/scrna/CellTypeAnnotation-hitype.R +27 -22
  133. biopipen/scripts/scrna/CellTypeAnnotation-sccatch.R +28 -20
  134. biopipen/scripts/scrna/CellTypeAnnotation-sctype.R +48 -25
  135. biopipen/scripts/scrna/CellTypeAnnotation.R +37 -1
  136. biopipen/scripts/scrna/CellsDistribution.R +456 -167
  137. biopipen/scripts/scrna/DimPlots.R +1 -1
  138. biopipen/scripts/scrna/ExprImputation-alra.R +109 -0
  139. biopipen/scripts/scrna/ExprImputation-rmagic.R +256 -0
  140. biopipen/scripts/scrna/{ExprImpution-scimpute.R → ExprImputation-scimpute.R} +8 -5
  141. biopipen/scripts/scrna/ExprImputation.R +7 -0
  142. biopipen/scripts/scrna/LoomTo10X.R +51 -0
  143. biopipen/scripts/scrna/MQuad.py +25 -0
  144. biopipen/scripts/scrna/MarkersFinder.R +679 -400
  145. biopipen/scripts/scrna/MetaMarkers.R +265 -161
  146. biopipen/scripts/scrna/ModuleScoreCalculator.R +66 -11
  147. biopipen/scripts/scrna/PseudoBulkDEG.R +678 -0
  148. biopipen/scripts/scrna/RadarPlots.R +355 -134
  149. biopipen/scripts/scrna/ScFGSEA.R +298 -100
  150. biopipen/scripts/scrna/ScSimulation.R +65 -0
  151. biopipen/scripts/scrna/ScVelo.py +617 -0
  152. biopipen/scripts/scrna/Seurat2AnnData.R +7 -0
  153. biopipen/scripts/scrna/SeuratClusterStats-clustree.R +87 -0
  154. biopipen/scripts/scrna/SeuratClusterStats-dimplots.R +36 -30
  155. biopipen/scripts/scrna/SeuratClusterStats-features.R +138 -187
  156. biopipen/scripts/scrna/SeuratClusterStats-ngenes.R +81 -0
  157. biopipen/scripts/scrna/SeuratClusterStats-stats.R +78 -89
  158. biopipen/scripts/scrna/SeuratClusterStats.R +47 -10
  159. biopipen/scripts/scrna/SeuratClustering.R +36 -233
  160. biopipen/scripts/scrna/SeuratLoading.R +2 -2
  161. biopipen/scripts/scrna/SeuratMap2Ref.R +84 -113
  162. biopipen/scripts/scrna/SeuratMetadataMutater.R +16 -6
  163. biopipen/scripts/scrna/SeuratPreparing.R +223 -173
  164. biopipen/scripts/scrna/SeuratSubClustering.R +64 -0
  165. biopipen/scripts/scrna/SeuratTo10X.R +27 -0
  166. biopipen/scripts/scrna/Slingshot.R +65 -0
  167. biopipen/scripts/scrna/Subset10X.R +2 -2
  168. biopipen/scripts/scrna/TopExpressingGenes.R +169 -135
  169. biopipen/scripts/scrna/celltypist-wrapper.py +195 -0
  170. biopipen/scripts/scrna/scvelo_paga.py +313 -0
  171. biopipen/scripts/scrna/seurat_anndata_conversion.py +98 -0
  172. biopipen/scripts/scrna_metabolic_landscape/MetabolicFeatures.R +447 -82
  173. biopipen/scripts/scrna_metabolic_landscape/MetabolicPathwayActivity.R +348 -241
  174. biopipen/scripts/scrna_metabolic_landscape/MetabolicPathwayHeterogeneity.R +188 -166
  175. biopipen/scripts/snp/MatrixEQTL.R +217 -0
  176. biopipen/scripts/snp/Plink2GTMat.py +148 -0
  177. biopipen/scripts/snp/PlinkCallRate.R +199 -0
  178. biopipen/scripts/snp/PlinkFilter.py +100 -0
  179. biopipen/scripts/snp/PlinkFreq.R +291 -0
  180. biopipen/scripts/snp/PlinkFromVcf.py +81 -0
  181. biopipen/scripts/snp/PlinkHWE.R +85 -0
  182. biopipen/scripts/snp/PlinkHet.R +96 -0
  183. biopipen/scripts/snp/PlinkIBD.R +196 -0
  184. biopipen/scripts/snp/PlinkSimulation.py +124 -0
  185. biopipen/scripts/snp/PlinkUpdateName.py +124 -0
  186. biopipen/scripts/stats/ChowTest.R +146 -0
  187. biopipen/scripts/stats/DiffCoexpr.R +152 -0
  188. biopipen/scripts/stats/LiquidAssoc.R +135 -0
  189. biopipen/scripts/stats/Mediation.R +108 -0
  190. biopipen/scripts/stats/MetaPvalue.R +130 -0
  191. biopipen/scripts/stats/MetaPvalue1.R +74 -0
  192. biopipen/scripts/tcgamaf/Maf2Vcf.py +2 -2
  193. biopipen/scripts/tcgamaf/MafAddChr.py +2 -2
  194. biopipen/scripts/tcr/Attach2Seurat.R +3 -2
  195. biopipen/scripts/tcr/CDR3AAPhyschem.R +211 -143
  196. biopipen/scripts/tcr/CDR3Clustering.R +343 -0
  197. biopipen/scripts/tcr/ClonalStats.R +526 -0
  198. biopipen/scripts/tcr/CloneResidency.R +255 -131
  199. biopipen/scripts/tcr/CloneSizeQQPlot.R +4 -4
  200. biopipen/scripts/tcr/GIANA/GIANA.py +1356 -797
  201. biopipen/scripts/tcr/GIANA/GIANA4.py +1362 -789
  202. biopipen/scripts/tcr/GIANA/query.py +164 -162
  203. biopipen/scripts/tcr/Immunarch-basic.R +31 -9
  204. biopipen/scripts/tcr/Immunarch-clonality.R +25 -5
  205. biopipen/scripts/tcr/Immunarch-diversity.R +352 -134
  206. biopipen/scripts/tcr/Immunarch-geneusage.R +45 -5
  207. biopipen/scripts/tcr/Immunarch-kmer.R +68 -8
  208. biopipen/scripts/tcr/Immunarch-overlap.R +84 -4
  209. biopipen/scripts/tcr/Immunarch-spectratyping.R +35 -6
  210. biopipen/scripts/tcr/Immunarch-tracking.R +38 -6
  211. biopipen/scripts/tcr/Immunarch-vjjunc.R +165 -0
  212. biopipen/scripts/tcr/Immunarch.R +63 -11
  213. biopipen/scripts/tcr/Immunarch2VDJtools.R +2 -2
  214. biopipen/scripts/tcr/ImmunarchFilter.R +4 -4
  215. biopipen/scripts/tcr/ImmunarchLoading.R +38 -29
  216. biopipen/scripts/tcr/SampleDiversity.R +1 -1
  217. biopipen/scripts/tcr/ScRepCombiningExpression.R +40 -0
  218. biopipen/scripts/tcr/ScRepLoading.R +166 -0
  219. biopipen/scripts/tcr/TCRClusterStats.R +176 -22
  220. biopipen/scripts/tcr/TCRDock.py +110 -0
  221. biopipen/scripts/tcr/TESSA.R +102 -118
  222. biopipen/scripts/tcr/VJUsage.R +5 -5
  223. biopipen/scripts/tcr/immunarch-patched.R +142 -0
  224. biopipen/scripts/tcr/vdjtools-patch.sh +1 -1
  225. biopipen/scripts/vcf/BcftoolsAnnotate.py +91 -0
  226. biopipen/scripts/vcf/BcftoolsFilter.py +90 -0
  227. biopipen/scripts/vcf/BcftoolsMerge.py +31 -0
  228. biopipen/scripts/vcf/BcftoolsSort.py +113 -0
  229. biopipen/scripts/vcf/BcftoolsView.py +73 -0
  230. biopipen/scripts/vcf/TruvariBench.sh +14 -7
  231. biopipen/scripts/vcf/TruvariBenchSummary.R +16 -13
  232. biopipen/scripts/vcf/TruvariConsistency.R +1 -1
  233. biopipen/scripts/vcf/Vcf2Bed.py +2 -2
  234. biopipen/scripts/vcf/VcfAnno.py +11 -11
  235. biopipen/scripts/vcf/VcfDownSample.sh +22 -10
  236. biopipen/scripts/vcf/VcfFilter.py +5 -5
  237. biopipen/scripts/vcf/VcfFix.py +7 -7
  238. biopipen/scripts/vcf/VcfFix_utils.py +13 -4
  239. biopipen/scripts/vcf/VcfIndex.py +3 -3
  240. biopipen/scripts/vcf/VcfIntersect.py +3 -3
  241. biopipen/scripts/vcf/VcfLiftOver.sh +5 -0
  242. biopipen/scripts/vcf/VcfSplitSamples.py +4 -4
  243. biopipen/scripts/vcf/bcftools_utils.py +52 -0
  244. biopipen/scripts/web/Download.py +8 -4
  245. biopipen/scripts/web/DownloadList.py +5 -5
  246. biopipen/scripts/web/GCloudStorageDownloadBucket.py +82 -0
  247. biopipen/scripts/web/GCloudStorageDownloadFile.py +23 -0
  248. biopipen/scripts/web/gcloud_common.py +49 -0
  249. biopipen/utils/gene.py +108 -60
  250. biopipen/utils/misc.py +146 -20
  251. biopipen/utils/reference.py +64 -20
  252. biopipen/utils/reporter.py +177 -0
  253. biopipen/utils/vcf.py +1 -1
  254. biopipen-0.34.26.dist-info/METADATA +27 -0
  255. biopipen-0.34.26.dist-info/RECORD +292 -0
  256. {biopipen-0.21.0.dist-info → biopipen-0.34.26.dist-info}/WHEEL +1 -1
  257. {biopipen-0.21.0.dist-info → biopipen-0.34.26.dist-info}/entry_points.txt +6 -2
  258. biopipen/ns/bcftools.py +0 -111
  259. biopipen/ns/scrna_basic.py +0 -255
  260. biopipen/reports/delim/SampleInfo.svelte +0 -36
  261. biopipen/reports/scrna/GeneExpressionInvistigation.svelte +0 -32
  262. biopipen/reports/scrna/ScFGSEA.svelte +0 -35
  263. biopipen/reports/scrna/SeuratClusterStats.svelte +0 -82
  264. biopipen/reports/scrna/SeuratMap2Ref.svelte +0 -20
  265. biopipen/reports/scrna/SeuratPreparing.svelte +0 -38
  266. biopipen/reports/scrna/TopExpressingGenes.svelte +0 -55
  267. biopipen/reports/scrna_metabolic_landscape/MetabolicFeaturesIntraSubset.svelte +0 -31
  268. biopipen/reports/utils/gsea.liq +0 -110
  269. biopipen/scripts/bcftools/BcftoolsAnnotate.py +0 -42
  270. biopipen/scripts/bcftools/BcftoolsFilter.py +0 -79
  271. biopipen/scripts/bcftools/BcftoolsSort.py +0 -19
  272. biopipen/scripts/gene/GeneNameConversion.py +0 -66
  273. biopipen/scripts/scrna/ExprImpution-alra.R +0 -32
  274. biopipen/scripts/scrna/ExprImpution-rmagic.R +0 -29
  275. biopipen/scripts/scrna/ExprImpution.R +0 -7
  276. biopipen/scripts/scrna/GeneExpressionInvistigation.R +0 -132
  277. biopipen/scripts/scrna/Write10X.R +0 -11
  278. biopipen/scripts/scrna_metabolic_landscape/MetabolicFeaturesIntraSubset.R +0 -150
  279. biopipen/scripts/tcr/TCRClustering.R +0 -280
  280. biopipen/utils/common_docstrs.py +0 -61
  281. biopipen/utils/gene.R +0 -49
  282. biopipen/utils/gsea.R +0 -193
  283. biopipen/utils/io.R +0 -20
  284. biopipen/utils/misc.R +0 -114
  285. biopipen/utils/mutate_helpers.R +0 -433
  286. biopipen/utils/plot.R +0 -173
  287. biopipen/utils/rnaseq.R +0 -48
  288. biopipen/utils/single_cell.R +0 -115
  289. biopipen-0.21.0.dist-info/METADATA +0 -22
  290. biopipen-0.21.0.dist-info/RECORD +0 -218
@@ -25,10 +25,10 @@ import sys
25
25
  import numpy as np
26
26
  import pandas as pd
27
27
 
28
- import cnvlib
29
- from cnvlib import parallel
30
- from cnvlib.descriptives import modal_location
31
- from skgenome import tabio, GenomicArray as GA
28
+ import cnvlib # type: ignore
29
+ from cnvlib import parallel # type: ignore
30
+ from cnvlib.descriptives import modal_location # type: ignore
31
+ from skgenome import tabio, GenomicArray as GA # type: ignore
32
32
 
33
33
  logging.basicConfig(level=logging.INFO, format="%(message)s")
34
34
 
@@ -36,11 +36,12 @@ logging.basicConfig(level=logging.INFO, format="%(message)s")
36
36
  # ___________________________________________
37
37
  # Guided method: guess from potential targets
38
38
 
39
+
39
40
  def filter_targets(target_bed, sample_bams, procs, fasta):
40
41
  """Check if each potential target has significant coverage."""
41
42
  try:
42
- baits = tabio.read(target_bed, 'bed4')
43
- except:
43
+ baits = tabio.read(target_bed, "bed4")
44
+ except: # noqa
44
45
  raise RuntimeError("Targets must be in BED format; try skg_convert.py")
45
46
  logging.info("Loaded %d candidate regions from %s", len(baits), target_bed)
46
47
  # Loop over BAMs to calculate weighted averages of bin coverage depths
@@ -48,47 +49,46 @@ def filter_targets(target_bed, sample_bams, procs, fasta):
48
49
  for bam_fname in sample_bams:
49
50
  logging.info("Evaluating targets in %s", bam_fname)
50
51
  sample = cnvlib.do_coverage(target_bed, bam_fname, processes=procs, fasta=fasta)
51
- assert len(sample) == len(baits), \
52
- "%d != %d" % (len(sample), len(baits))
53
- total_depths += sample['depth'].values
54
- baits['depth'] = total_depths / len(sample_bams)
55
- logging.info("Average candidate-target depth:\n%s",
56
- baits['depth'].describe())
52
+ assert len(sample) == len(baits), "%d != %d" % (len(sample), len(baits))
53
+ total_depths += sample["depth"].values
54
+ baits["depth"] = total_depths / len(sample_bams)
55
+ logging.info("Average candidate-target depth:\n%s", baits["depth"].describe())
57
56
  return baits
58
57
 
59
58
 
60
59
  # _________________________________________
61
60
  # Unguided method: guess from raw depths
62
61
 
63
- def scan_targets(access_bed, sample_bams, min_depth, min_gap, min_length,
64
- procs):
62
+
63
+ def scan_targets(access_bed, sample_bams, min_depth, min_gap, min_length, procs):
65
64
  """Estimate baited regions from a genome-wide, per-base depth profile."""
66
65
  bait_chunks = []
67
66
  # ENH: context manager to call rm on bed chunks? with to_chunks as pool, ck?
68
- logging.info("Scanning for enriched regions in:\n %s",
69
- '\n '.join(sample_bams))
67
+ logging.info("Scanning for enriched regions in:\n %s", "\n ".join(sample_bams))
70
68
  # with futures.ProcessPoolExecutor(procs) as pool:
71
69
  with parallel.pick_pool(procs) as pool:
72
- args_iter = ((bed_chunk, sample_bams,
73
- min_depth, min_gap, min_length)
74
- for bed_chunk in parallel.to_chunks(access_bed))
70
+ args_iter = (
71
+ (bed_chunk, sample_bams, min_depth, min_gap, min_length)
72
+ for bed_chunk in parallel.to_chunks(access_bed)
73
+ )
75
74
  for bed_chunk_fname, bait_chunk in pool.map(_scan_depth, args_iter):
76
75
  bait_chunks.append(bait_chunk)
77
76
  parallel.rm(bed_chunk_fname)
78
77
  baits = GA(pd.concat(bait_chunks))
79
- baits['depth'] /= len(sample_bams)
78
+ baits["depth"] /= len(sample_bams)
80
79
  return baits
81
80
 
82
81
 
83
82
  def _scan_depth(args):
84
83
  """Wrapper for parallel map"""
85
84
  bed_fname, bam_fnames, min_depth, min_gap, min_length = args
86
- regions = list(drop_small(merge_gaps(scan_depth(bed_fname, bam_fnames,
87
- min_depth),
88
- min_gap),
89
- min_length))
90
- result = pd.DataFrame.from_records(list(regions),
91
- columns=regions[0]._fields)
85
+ regions = list(
86
+ drop_small(
87
+ merge_gaps(scan_depth(bed_fname, bam_fnames, min_depth), min_gap),
88
+ min_length,
89
+ )
90
+ )
91
+ result = pd.DataFrame.from_records(list(regions), columns=regions[0]._fields)
92
92
  return bed_fname, result
93
93
 
94
94
 
@@ -100,32 +100,42 @@ def scan_depth(bed_fname, bam_fnames, min_depth):
100
100
  tuple
101
101
  Region coordinates (0-indexed, half-open): chromosome name, start, end
102
102
  """
103
- Region = collections.namedtuple('Region', 'chromosome start end depth')
103
+ Region = collections.namedtuple("Region", "chromosome start end depth")
104
104
 
105
105
  nsamples = len(bam_fnames)
106
106
  if nsamples == 1:
107
+
107
108
  def get_depth(depths):
108
109
  return int(depths[0])
110
+
109
111
  else:
110
112
  min_depth *= nsamples
113
+
111
114
  # NB: samtools emits additional BAMs' depths as trailing columns
112
115
  def get_depth(depths):
113
116
  return sum(map(int, depths))
114
117
 
115
- proc = subprocess.Popen([SAMTOOLS, 'depth',
116
- '-Q', '1', # Skip pseudogenes
117
- '-b', bed_fname,
118
- ] + bam_fnames,
119
- stdout=subprocess.PIPE,
120
- encoding='utf-8',
121
- shell=False)
118
+ proc = subprocess.Popen(
119
+ [
120
+ SAMTOOLS,
121
+ "depth",
122
+ "-Q",
123
+ "1", # Skip pseudogenes
124
+ "-b",
125
+ bed_fname,
126
+ ]
127
+ + bam_fnames,
128
+ stdout=subprocess.PIPE,
129
+ encoding="utf-8",
130
+ shell=False,
131
+ )
122
132
 
123
133
  # Detect runs of >= min_depth; emit their coordinates
124
134
  chrom = start = depths = None
125
- for line in proc.stdout:
126
- fields = line.split('\t')
135
+ for line in proc.stdout: # type: ignore
136
+ fields = line.split("\t")
127
137
  depth = get_depth(fields[2:])
128
- is_enriched = (depth >= min_depth)
138
+ is_enriched = depth >= min_depth
129
139
  if start is None:
130
140
  if is_enriched:
131
141
  # Entering a new captured region
@@ -137,7 +147,7 @@ def scan_depth(bed_fname, bam_fnames, min_depth):
137
147
  continue
138
148
  elif is_enriched and fields[0] == chrom:
139
149
  # Still in a captured region -- extend it
140
- depths.append(depth)
150
+ depths.append(depth) # type: ignore
141
151
  else:
142
152
  # Exiting a captured region
143
153
  # Update target region boundaries
@@ -146,10 +156,12 @@ def scan_depth(bed_fname, bam_fnames, min_depth):
146
156
  ok_dp_idx = np.nonzero(darr >= half_depth)[0]
147
157
  start_idx = ok_dp_idx[0]
148
158
  end_idx = ok_dp_idx[-1] + 1
149
- yield Region(chrom,
150
- start + start_idx,
151
- start + end_idx,
152
- darr[start_idx:end_idx].mean())
159
+ yield Region(
160
+ chrom,
161
+ start + start_idx,
162
+ start + end_idx,
163
+ darr[start_idx:end_idx].mean(),
164
+ )
153
165
  chrom = start = depths = None
154
166
 
155
167
 
@@ -170,75 +182,129 @@ def merge_gaps(regions, min_gap):
170
182
 
171
183
  def drop_small(regions, min_length):
172
184
  """Merge small gaps and filter by minimum length."""
173
- return (reg for reg in regions
174
- if reg.end - reg.start >= min_length)
185
+ return (reg for reg in regions if reg.end - reg.start >= min_length)
175
186
 
176
187
 
177
188
  # ___________________________________________
178
189
  # Shared
179
190
 
191
+
180
192
  def normalize_depth_log2_filter(baits, min_depth, enrich_ratio=0.1):
181
193
  """Calculate normalized depth, add log2 column, filter by enrich_ratio."""
182
194
  # Normalize depths to a neutral value of 1.0
183
- dp_mode = modal_location(baits.data.loc[baits['depth'] > min_depth,
184
- 'depth'].values)
185
- norm_depth = baits['depth'] / dp_mode
195
+ dp_mode = modal_location(baits.data.loc[baits["depth"] > min_depth, "depth"].values)
196
+ norm_depth = baits["depth"] / dp_mode
186
197
  # Drop low-coverage targets
187
- keep_idx = (norm_depth >= enrich_ratio)
188
- logging.info("Keeping %d/%d bins with coverage depth >= %f, modal depth %f",
189
- keep_idx.sum(), len(keep_idx), dp_mode * enrich_ratio, dp_mode)
198
+ keep_idx = norm_depth >= enrich_ratio
199
+ logging.info(
200
+ "Keeping %d/%d bins with coverage depth >= %f, modal depth %f",
201
+ keep_idx.sum(),
202
+ len(keep_idx),
203
+ dp_mode * enrich_ratio,
204
+ dp_mode,
205
+ )
190
206
  return baits[keep_idx]
191
207
 
192
208
 
193
- SAMTOOLS = 'samtools'
209
+ SAMTOOLS = "samtools"
194
210
 
195
- if __name__ == '__main__':
211
+ if __name__ == "__main__":
196
212
  AP = argparse.ArgumentParser(description=__doc__)
197
- AP.add_argument('sample_bams', nargs='+',
198
- help="""Sample BAM file(s) to test for target coverage.""")
199
- AP.add_argument('-o', '--output', metavar='FILENAME',
200
- help="""The inferred targets, in BED format.""")
201
- AP.add_argument('-c', '--coverage', metavar='FILENAME',
202
- help="""Filename to output average coverage depths in .cnn
203
- format.""")
204
- AP.add_argument('-p', '--processes', metavar='CPU',
205
- nargs='?', type=int, const=0, default=1,
206
- help="""Number of subprocesses to segment in parallel.
213
+ AP.add_argument(
214
+ "sample_bams",
215
+ nargs="+",
216
+ help="""Sample BAM file(s) to test for target coverage.""",
217
+ )
218
+ AP.add_argument(
219
+ "-o",
220
+ "--output",
221
+ metavar="FILENAME",
222
+ help="""The inferred targets, in BED format.""",
223
+ )
224
+ AP.add_argument(
225
+ "-c",
226
+ "--coverage",
227
+ metavar="FILENAME",
228
+ help="""Filename to output average coverage depths in .cnn
229
+ format.""",
230
+ )
231
+ AP.add_argument(
232
+ "-p",
233
+ "--processes",
234
+ metavar="CPU",
235
+ nargs="?",
236
+ type=int,
237
+ const=0,
238
+ default=1,
239
+ help="""Number of subprocesses to segment in parallel.
207
240
  If given without an argument, use the maximum number
208
- of available CPUs. [Default: use 1 process]""")
209
- AP.add_argument('-f', '--fasta', metavar="FILENAME",
210
- help="Reference genome, FASTA format (e.g. UCSC hg19.fa)")
211
- AP.add_argument('-s', '--samtools', metavar="SAMTOOLS",
212
- help="Path to samtools", default="samtools")
241
+ of available CPUs. [Default: use 1 process]""",
242
+ )
243
+ AP.add_argument(
244
+ "-f",
245
+ "--fasta",
246
+ metavar="FILENAME",
247
+ help="Reference genome, FASTA format (e.g. UCSC hg19.fa)",
248
+ )
249
+ AP.add_argument(
250
+ "-s",
251
+ "--samtools",
252
+ metavar="SAMTOOLS",
253
+ help="Path to samtools",
254
+ default="samtools",
255
+ )
213
256
 
214
257
  AP_x = AP.add_mutually_exclusive_group(required=True)
215
- AP_x.add_argument('-t', '--targets', metavar='TARGET_BED',
216
- help="""Potentially targeted genomic regions, e.g. all known
258
+ AP_x.add_argument(
259
+ "-t",
260
+ "--targets",
261
+ metavar="TARGET_BED",
262
+ help="""Potentially targeted genomic regions, e.g. all known
217
263
  exons in the reference genome, in BED format. Each of these
218
264
  regions will be tested as a whole for enrichment. (Faster
219
- method)""")
220
- AP_x.add_argument('-a', '--access', metavar='ACCESS_BED',
221
- # default="../data/access-5k-mappable.grch37.bed",
222
- help="""Sequencing-accessible genomic regions (e.g. from
265
+ method)""",
266
+ )
267
+ AP_x.add_argument(
268
+ "-a",
269
+ "--access",
270
+ metavar="ACCESS_BED",
271
+ # default="../data/access-5k-mappable.grch37.bed",
272
+ help="""Sequencing-accessible genomic regions (e.g. from
223
273
  'cnvkit.py access'), or known genic regions in the reference
224
274
  genome, in BED format. All bases will be tested for
225
- enrichment. (Slower method)""")
275
+ enrichment. (Slower method)""",
276
+ )
226
277
 
227
278
  AP_target = AP.add_argument_group("With --targets only")
228
- AP_target.add_argument('-d', '--min-depth', metavar='DEPTH',
229
- type=int, default=5,
230
- help="""Minimum sequencing read depth to accept as captured.
231
- [Default: %(default)s]""")
279
+ AP_target.add_argument(
280
+ "-d",
281
+ "--min-depth",
282
+ metavar="DEPTH",
283
+ type=int,
284
+ default=5,
285
+ help="""Minimum sequencing read depth to accept as captured.
286
+ [Default: %(default)s]""",
287
+ )
232
288
 
233
289
  AP_access = AP.add_argument_group("With --access only")
234
- AP_access.add_argument('-g', '--min-gap', metavar='GAP_SIZE',
235
- type=int, default=25,
236
- help="""Merge regions separated by gaps smaller than this.
237
- [Default: %(default)s]""")
238
- AP_access.add_argument('-l', '--min-length', metavar='TARGET_SIZE',
239
- type=int, default=50,
240
- help="""Minimum region length to accept as captured.
241
- [Default: %(default)s]""")
290
+ AP_access.add_argument(
291
+ "-g",
292
+ "--min-gap",
293
+ metavar="GAP_SIZE",
294
+ type=int,
295
+ default=25,
296
+ help="""Merge regions separated by gaps smaller than this.
297
+ [Default: %(default)s]""",
298
+ )
299
+ AP_access.add_argument(
300
+ "-l",
301
+ "--min-length",
302
+ metavar="TARGET_SIZE",
303
+ type=int,
304
+ default=50,
305
+ help="""Minimum region length to accept as captured.
306
+ [Default: %(default)s]""",
307
+ )
242
308
 
243
309
  args = AP.parse_args()
244
310
  SAMTOOLS = args.samtools
@@ -247,13 +313,20 @@ if __name__ == '__main__':
247
313
  args.processes = None
248
314
 
249
315
  if args.targets:
250
- baits = filter_targets(args.targets, args.sample_bams, args.processes, args.fasta)
316
+ baits = filter_targets(
317
+ args.targets, args.sample_bams, args.processes, args.fasta
318
+ )
251
319
  else:
252
- baits = scan_targets(args.access, args.sample_bams,
253
- 0.5 * args.min_depth, # More sensitive 1st pass
254
- args.min_gap, args.min_length, args.processes)
320
+ baits = scan_targets(
321
+ args.access,
322
+ args.sample_bams,
323
+ 0.5 * args.min_depth, # More sensitive 1st pass
324
+ args.min_gap,
325
+ args.min_length,
326
+ args.processes,
327
+ )
255
328
  baits = normalize_depth_log2_filter(baits, args.min_depth)
256
- tabio.write(baits, args.output or sys.stdout, 'bed')
329
+ tabio.write(baits, args.output or sys.stdout, "bed")
257
330
  if args.coverage:
258
- baits['log2'] = np.log2(baits['depth'] / baits['depth'].median())
259
- tabio.write(baits, args.coverage, 'tab')
331
+ baits["log2"] = np.log2(baits["depth"] / baits["depth"].median())
332
+ tabio.write(baits, args.coverage, "tab")
@@ -1,4 +1,4 @@
1
- source("{{biopipen_dir}}/utils/misc.R")
1
+ library(biopipen.utils)
2
2
 
3
3
  infiles <- {{in.infiles | r}}
4
4
  outfile <- {{out.outfile | r}}
@@ -1,19 +1,27 @@
1
- source("{{biopipen_dir}}/utils/misc.R")
2
- source("{{biopipen_dir}}/utils/mutate_helpers.R")
3
1
  library(rlang)
4
2
  library(dplyr)
5
- library(ggplot2)
6
- library(ggprism)
7
- library(ggsci)
8
- library(ggrepel)
3
+ library(gglogger)
4
+ library(biopipen.utils)
5
+ library(plotthis)
9
6
 
10
7
  infile <- {{in.infile | r}}
11
8
  outfile <- {{out.outfile | r}}
9
+ joboutdir <- {{job.outdir | r}}
12
10
  sep <- {{envs.sep | r}}
13
11
  mutaters <- {{envs.mutaters | r}}
14
12
  save_mutated <- {{envs.save_mutated | r}}
15
13
  defaults <- {{envs.defaults | r}}
16
14
  stats <- {{envs.stats | r}}
15
+ exclude_cols <- {{envs.exclude_cols | r}}
16
+
17
+ log <- get_logger()
18
+ reporter <- get_reporter()
19
+
20
+ if (is.null(exclude_cols)) {
21
+ exclude_cols <- c()
22
+ } else {
23
+ exclude_cols <- trimws(unlist(strsplit(exclude_cols, ",")))
24
+ }
17
25
 
18
26
  outdir <- dirname(outfile)
19
27
  indata <- read.delim(infile, sep = sep, header = TRUE, row.names = NULL)
@@ -22,6 +30,59 @@ if (colnames(indata)[1] == "row.names") {
22
30
  stop("Wrong number of column names. Do you have the right `sep`?")
23
31
  }
24
32
 
33
+ #' Get plotthis function from plot_type
34
+ #'
35
+ #' @param plot_type The plot type
36
+ #' @param gglogger_register Register the plotthis function to gglogger
37
+ #' @param return_name Return the name of the function instead of the function
38
+ #' @return The plotthis function
39
+ #' @export
40
+ get_plotthis_fn <- function(plot_type, gglogger_register = TRUE, return_name = FALSE) {
41
+ fn_name <- switch(plot_type,
42
+ hist = "Histogram",
43
+ histo = "Histogram",
44
+ histogram = "Histogram",
45
+ featuredim = "FeatureDimPlot",
46
+ splitbar = "SplitBarPlot",
47
+ enrichmap = "EnrichMap",
48
+ enrichnet = "EnrichNetwork",
49
+ enrichnetwork = "EnrichNetwork",
50
+ gsea = "GSEAPlot",
51
+ gseasummary = "GSEASummaryPlot",
52
+ gseasum = "GSEASummaryPlot",
53
+ heatmap = "Heatmap",
54
+ network = "Network",
55
+ pie = "PieChart",
56
+ wordcloud = "WordCloudPlot",
57
+ venn = "VennDiagram",
58
+ {
59
+ title_case_plot_type <- tools::toTitleCase(plot_type)
60
+ if (endsWith(title_case_plot_type, "Plot")) {
61
+ title_case_plot_type
62
+ } else if (endsWith(title_case_plot_type, "plot")) {
63
+ paste0(substr(title_case_plot_type, 1, nchar(title_case_plot_type) - 4), "Plot")
64
+ } else {
65
+ paste0(title_case_plot_type, "Plot")
66
+ }
67
+ }
68
+ )
69
+ if (return_name) {
70
+ return(fn_name)
71
+ }
72
+ fn <- tryCatch({
73
+ utils::getFromNamespace(fn_name, "plotthis")
74
+ }, error = function(e) {
75
+ stop("Unknown plot type: ", plot_type)
76
+ })
77
+
78
+ if (gglogger_register) {
79
+ gglogger::register(fn, fn_name)
80
+ } else {
81
+ fn
82
+ }
83
+ }
84
+
85
+ log$info("Applying mutaters to the data if any ...")
25
86
  if (!is.null(mutaters) && length(mutaters) > 0) {
26
87
  mutdata <- indata %>%
27
88
  mutate(!!!lapply(mutaters, parse_expr))
@@ -38,125 +99,62 @@ write.table(
38
99
  quote = FALSE
39
100
  )
40
101
 
41
- theme_set(theme_prism())
42
- for (name in names(stats)) {
43
- stat <- list_update(defaults, stats[[name]])
44
- plotfile <- file.path(outdir, paste0(name, ".png"))
45
-
46
- is_continuous <- FALSE
47
- if (!is.null(stat$distinct)) {
48
- data <- mutdata %>% distinct(!!sym(stat$distinct), .keep_all = TRUE)
49
- } else {
50
- data <- mutdata
51
- }
52
- if (!is.null(stat$group) && !stat$na_group) {
53
- data <- data %>% filter(!is.na(!!sym(stat$group)))
54
- }
55
- if (!is.null(stat$each) && !stat$na_each) {
56
- data <- data %>% filter(!is.na(!!sym(stat$each)))
57
- }
58
102
 
59
- if (is.numeric(data[[stat$on]])) {
60
- is_continuous <- TRUE
61
- }
103
+ reporter$add(
104
+ list(
105
+ kind = "descr",
106
+ content = "The samples used in the analysis. Each row is a sample, and columns are the meta information about the sample. This is literally the input sample information file, but the paths to the scRNA-seq and scTCR-seq data are hidden.",
107
+ once = TRUE
108
+ ),
109
+ list(
110
+ kind = "table",
111
+ pageSize = 50,
112
+ data = list(file = outfile, sep = sep, excluded = exclude_cols),
113
+ src = FALSE
114
+ ),
115
+ h1 = "Sample Information"
116
+ )
62
117
 
63
- if (is.null(stat$plot)) {
64
- stat$plot <- if (is_continuous) "boxplot" else "pie"
65
- }
118
+ if (length(stats) > 0) {
119
+ cases <- expand_cases(stats, defaults)
120
+ for (name in names(cases)) {
121
+ log$info("- Statistic: {name}")
66
122
 
67
- data$..group <- "All"
68
- group <- if (is.null(stat$group)) sym("..group") else sym(stat$group)
69
- count_on <- paste0("..count.", stat$on)
70
- if (!is_continuous) {
71
- data <- data %>% add_count(!!group, name = count_on)
72
- }
123
+ case <- cases[[name]]
124
+ info <- case_info(name, outdir, is_dir = FALSE, create = TRUE)
125
+ case <- extract_vars(case, "plot_type", "more_formats", "save_code", "section", "subset", "devpars", "descr")
73
126
 
74
- if (is.null(stat$devpars)) {
75
- stat$devpars <- list()
76
- }
77
- if (is.null(stat$devpars$width)) {
78
- stat$devpars$width <- 800
79
- }
80
- if (is.null(stat$devpars$height)) {
81
- stat$devpars$height <- 600
82
- }
83
- if (is.null(stat$devpars$res)) {
84
- stat$devpars$res <- 100
85
- }
127
+ plot_fn <- get_plotthis_fn(plot_type)
128
+ more_formats <- unique(c("png", more_formats))
86
129
 
87
- png(
88
- plotfile,
89
- width = stat$devpars$width,
90
- height = stat$devpars$height,
91
- res = stat$devpars$res
92
- )
93
- if (stat$plot == "boxplot" || stat$plot == "box") {
94
- p <- ggplot(data, aes(x=!!group, y=!!sym(stat$on), fill=!!group)) +
95
- geom_boxplot(position = "dodge") +
96
- scale_fill_ucscgb(alpha = .8) +
97
- xlab("")
98
- } else if (stat$plot == "violin" ||
99
- stat$plot == "violinplot" ||
100
- stat$plot == "vlnplot") {
101
- p <- ggplot(data, aes(x = !!group, y = !!sym(stat$on), fill=!!group)) +
102
- geom_violin(position = "dodge") +
103
- scale_fill_ucscgb(alpha = .8) +
104
- xlab("")
105
- } else if (
106
- (grepl("violin", stat$plot) || grepl("vln", stat$plot)) &&
107
- grepl("box", stat$plot)
108
- ) {
109
- p <- ggplot(data, aes(x = !!group, y = !!sym(stat$on), fill = !!group)) +
110
- geom_violin(position = "dodge") +
111
- geom_boxplot(width = 0.1, position = position_dodge(0.9), fill="white") +
112
- scale_fill_ucscgb(alpha = .8) +
113
- xlab("")
114
- } else if (stat$plot == "histogram" || stat$plot == "hist") {
115
- p <- ggplot(data, aes(x = !!sym(stat$on), fill = !!group)) +
116
- geom_histogram(bins = 10, position = "dodge", alpha = 0.8, color = "white") +
117
- scale_fill_ucscgb(alpha = .8)
118
- } else if (stat$plot == "pie" || stat$plot == "piechart") {
119
- if (is.null(stat$each)) {
120
- data <- data %>% distinct(!!group, .keep_all = TRUE)
130
+ if (!is.null(subset)) {
131
+ case$data <- mutdata %>% dplyr::filter(!!parse_expr(subset))
121
132
  } else {
122
- data <- data %>%
123
- distinct(!!group, !!sym(stat$each), .keep_all = TRUE) %>%
124
- group_by(!!sym(stat$each))
133
+ case$data <- mutdata
125
134
  }
126
- p <- ggplot(
127
- data %>% arrange(!!group),
128
- aes(x = "", y = !!sym(count_on), fill = !!group, label = !!sym(count_on))
129
- ) +
130
- geom_bar(stat="identity", width=1, color="white", position = position_stack(reverse = TRUE)) +
131
- coord_polar("y", start = 0) +
132
- theme_void() +
133
- theme(plot.title = element_text(hjust = 0.5)) +
134
- geom_label_repel(
135
- position = position_stack(vjust = 0.5),
136
- color="#333333",
137
- fill="#EEEEEE",
138
- size=4
139
- ) +
140
- scale_fill_ucscgb(alpha = .7, name = group) +
141
- ggtitle(paste0("# ", stat$on))
142
- } else if (stat$plot == "bar" || stat$plot == "barplot") {
143
- if (is.null(stat$each)) {
144
- data <- data %>% distinct(!!group, .keep_all = TRUE)
145
- } else {
146
- data <- data %>% distinct(!!group, !!sym(stat$each), .keep_all = TRUE)
135
+
136
+ p <- do_call(plot_fn, case)
137
+ save_plot(p, info$prefix, devpars, formats = more_formats)
138
+ if (save_code) {
139
+ save_plotcode(
140
+ p,
141
+ setup = c('library(plotthis)', '', 'load("data.RData")', 'list2env(case, envir = .GlobalEnv)'),
142
+ prefix = info$caseprefix,
143
+ "case",
144
+ auto_data_setup = FALSE
145
+ )
147
146
  }
148
- p <- ggplot(
149
- data,
150
- aes(x = !!group, y = !!sym(count_on), fill = !!group)) +
151
- geom_bar(stat = "identity") +
152
- scale_fill_ucscgb(alpha = .8) +
153
- ylab(paste0("# ", stat$on))
154
- } else {
155
- stop("Unknown plot type: ", stat$plot)
156
- }
157
- if (!is.null(stat$each)) {
158
- p <- p + facet_wrap(vars(!!sym(stat$each)), ncol = stat$ncol)
147
+
148
+ reporter$add(
149
+ reporter$image(
150
+ info$prefix,
151
+ c("png", more_formats),
152
+ save_code,
153
+ kind = "table_image"
154
+ ),
155
+ h1 = "Statistics", ui = "table_of_images:2"
156
+ )
159
157
  }
160
- print(p)
161
- dev.off()
162
158
  }
159
+
160
+ reporter$save(joboutdir)