Merge branch 'report_glow_up' into 'dev'
[CW-7207] Volcanic See merge request epi2melabs/workflows/wf-transcriptomes!249
This commit is contained in:
commit
4a3c1d1b35
@ -4,23 +4,36 @@ import json
|
|||||||
import math
|
import math
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from dominate.tags import div, h4, p, pre, strong
|
from bokeh.resources import INLINE as BOKEH_INLINE
|
||||||
|
from dominate.tags import div, h3, h4, p, pre, script, strong
|
||||||
from dominate.util import raw
|
from dominate.util import raw
|
||||||
from ezcharts.components import fastcat
|
from ezcharts.components import fastcat
|
||||||
|
from ezcharts.components.ezchart import EZChart
|
||||||
from ezcharts.components.reports import labs
|
from ezcharts.components.reports import labs
|
||||||
|
from ezcharts.components.theme import LAB_head_resources
|
||||||
|
from ezcharts.layout.resource import Resource as EZC_Resource
|
||||||
from ezcharts.layout.snippets import Tabs
|
from ezcharts.layout.snippets import Tabs
|
||||||
from ezcharts.layout.snippets.table import DataTable
|
from ezcharts.layout.snippets.table import DataTable
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
from .util import get_named_logger, wf_parser # noqa: ABS101
|
from .util import get_named_logger, wf_parser # noqa: ABS101
|
||||||
|
from .volcano import volcano # noqa: ABS101
|
||||||
|
|
||||||
|
|
||||||
def _find_table(directory, pattern):
|
def get_bokeh_widgets_js():
|
||||||
matches = sorted(Path(directory).glob(pattern))
|
"""Return the inline Bokeh widgets JavaScript bundle."""
|
||||||
return matches[0] if matches else None
|
widgets_index = BOKEH_INLINE.components_for("js").index("bokeh-widgets")
|
||||||
|
return raw(BOKEH_INLINE.js_raw[widgets_index])
|
||||||
|
|
||||||
|
|
||||||
|
def get_bokeh_tables_js():
|
||||||
|
"""Return the inline Bokeh tables JavaScript bundle."""
|
||||||
|
tables_index = BOKEH_INLINE.components_for("js").index("bokeh-tables")
|
||||||
|
return raw(BOKEH_INLINE.js_raw[tables_index])
|
||||||
|
|
||||||
|
|
||||||
def _read_table(path, **kwargs):
|
def _read_table(path, **kwargs):
|
||||||
|
"""Read a TSV file into a DataFrame, returning None if path is absent."""
|
||||||
if path is None or not Path(path).exists():
|
if path is None or not Path(path).exists():
|
||||||
return None
|
return None
|
||||||
return pd.read_csv(path, sep="\t", **kwargs)
|
return pd.read_csv(path, sep="\t", **kwargs)
|
||||||
@ -56,6 +69,7 @@ def _format_ratio_value(value):
|
|||||||
|
|
||||||
|
|
||||||
def _cohort_summary(cohort_dir):
|
def _cohort_summary(cohort_dir):
|
||||||
|
"""Return cohort-level metrics and transcript class counts DataFrames."""
|
||||||
tx_meta = _read_table(Path(cohort_dir) / "transcript_metadata.tsv")
|
tx_meta = _read_table(Path(cohort_dir) / "transcript_metadata.tsv")
|
||||||
if tx_meta is None:
|
if tx_meta is None:
|
||||||
return None, None
|
return None, None
|
||||||
@ -79,6 +93,7 @@ def _cohort_summary(cohort_dir):
|
|||||||
|
|
||||||
|
|
||||||
def _sample_summaries(samples_dir):
|
def _sample_summaries(samples_dir):
|
||||||
|
"""Return a dict of per-sample metrics DataFrames keyed by sample name."""
|
||||||
summaries = {}
|
summaries = {}
|
||||||
for sample_dir in sorted(Path(samples_dir).iterdir()):
|
for sample_dir in sorted(Path(samples_dir).iterdir()):
|
||||||
if not sample_dir.is_dir():
|
if not sample_dir.is_dir():
|
||||||
@ -104,6 +119,7 @@ def _sample_summaries(samples_dir):
|
|||||||
|
|
||||||
|
|
||||||
def _sqanti_tables(sqanti_dir):
|
def _sqanti_tables(sqanti_dir):
|
||||||
|
"""Return a dict of SQANTI3 classification summary DataFrames keyed by label."""
|
||||||
tables = {}
|
tables = {}
|
||||||
for summary in sorted(Path(sqanti_dir).rglob("classification_summary.tsv")):
|
for summary in sorted(Path(sqanti_dir).rglob("classification_summary.tsv")):
|
||||||
label = summary.parent.name
|
label = summary.parent.name
|
||||||
@ -111,7 +127,8 @@ def _sqanti_tables(sqanti_dir):
|
|||||||
return tables
|
return tables
|
||||||
|
|
||||||
|
|
||||||
def _top_results(de_dir, filename, n=20):
|
def _contrast_results(de_dir, filename, n=None):
|
||||||
|
"""Return a dict of per-contrast result DataFrames read from filename."""
|
||||||
tables = {}
|
tables = {}
|
||||||
for contrast_dir in sorted(Path(de_dir).iterdir()):
|
for contrast_dir in sorted(Path(de_dir).iterdir()):
|
||||||
if not contrast_dir.is_dir():
|
if not contrast_dir.is_dir():
|
||||||
@ -119,7 +136,10 @@ def _top_results(de_dir, filename, n=20):
|
|||||||
table = _read_table(contrast_dir / filename)
|
table = _read_table(contrast_dir / filename)
|
||||||
if table is None or table.empty:
|
if table is None or table.empty:
|
||||||
continue
|
continue
|
||||||
tables[contrast_dir.name] = table.head(n)
|
data = table
|
||||||
|
if n is not None:
|
||||||
|
data = data.head(n)
|
||||||
|
tables[contrast_dir.name] = data
|
||||||
return tables
|
return tables
|
||||||
|
|
||||||
|
|
||||||
@ -266,6 +286,10 @@ def main(args):
|
|||||||
args.params,
|
args.params,
|
||||||
args.versions,
|
args.versions,
|
||||||
args.wf_version,
|
args.wf_version,
|
||||||
|
head_resources=[
|
||||||
|
*LAB_head_resources,
|
||||||
|
EZC_Resource(func=get_bokeh_widgets_js, tag=script),
|
||||||
|
EZC_Resource(func=get_bokeh_tables_js, tag=script)]
|
||||||
)
|
)
|
||||||
|
|
||||||
with open(args.metadata, "r") as handle:
|
with open(args.metadata, "r") as handle:
|
||||||
@ -869,7 +893,9 @@ def main(args):
|
|||||||
|
|
||||||
with report.add_section("Differential gene expression", "DGE"):
|
with report.add_section("Differential gene expression", "DGE"):
|
||||||
tabs = Tabs()
|
tabs = Tabs()
|
||||||
for contrast, table in _top_results(args.de_dir, "results_dge.tsv").items():
|
for contrast, table in _contrast_results(
|
||||||
|
args.de_dir, "results_dge.tsv", n=20
|
||||||
|
).items():
|
||||||
with tabs.add_tab(contrast):
|
with tabs.add_tab(contrast):
|
||||||
# Check for contrast-specific warnings
|
# Check for contrast-specific warnings
|
||||||
if de_qc and contrast in de_qc.get("contrasts", {}):
|
if de_qc and contrast in de_qc.get("contrasts", {}):
|
||||||
@ -900,9 +926,35 @@ def main(args):
|
|||||||
|
|
||||||
DataTable.from_pandas(table, use_index=False)
|
DataTable.from_pandas(table, use_index=False)
|
||||||
|
|
||||||
|
h3("Gene expression volcano Plot")
|
||||||
|
vol, class_table, selected_table = volcano(table)
|
||||||
|
EZChart(vol, width="100%", height="550")
|
||||||
|
raw("""
|
||||||
|
<style>
|
||||||
|
.volcano-table-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 20px 10px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
.volcano-table-grid > * {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 1000px) {
|
||||||
|
.volcano-table-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
""")
|
||||||
|
with div(_class="volcano-table-grid"):
|
||||||
|
EZChart(class_table, width="100%", height="auto")
|
||||||
|
EZChart(selected_table, width="100%", height="auto")
|
||||||
|
|
||||||
with report.add_section("Differential transcript usage", "DTU"):
|
with report.add_section("Differential transcript usage", "DTU"):
|
||||||
tabs = Tabs()
|
tabs = Tabs()
|
||||||
dtu_tables = _top_results(args.de_dir, "results_dtu_transcript.tsv")
|
dtu_tables = _contrast_results(
|
||||||
|
args.de_dir, "results_dtu_transcript.tsv", n=20)
|
||||||
|
|
||||||
for contrast in sorted(Path(args.de_dir).iterdir()):
|
for contrast in sorted(Path(args.de_dir).iterdir()):
|
||||||
if not contrast.is_dir():
|
if not contrast.is_dir():
|
||||||
@ -940,6 +992,9 @@ def main(args):
|
|||||||
dtu_tables[contrast_name],
|
dtu_tables[contrast_name],
|
||||||
use_index=False,
|
use_index=False,
|
||||||
)
|
)
|
||||||
|
h3("Transcript expression volcano Plot")
|
||||||
|
# EZChart(volcano(dtu_tables[contrast_name]))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
p("No DTU results available for this contrast.")
|
p("No DTU results available for this contrast.")
|
||||||
|
|
||||||
|
|||||||
@ -94,13 +94,16 @@ def _build_report_args(tmp_path, de_qc=None):
|
|||||||
contrast_dir.mkdir()
|
contrast_dir.mkdir()
|
||||||
_write(
|
_write(
|
||||||
contrast_dir / "results_dge.tsv",
|
contrast_dir / "results_dge.tsv",
|
||||||
"GENEID\tlog2FoldChange\tpadj\n"
|
(
|
||||||
"gene1\t1.0\t0.01\n",
|
"GENEID\tnewGeneClass\tgene_name\tbaseMean\tlog2FoldChange\tlfcSE"
|
||||||
|
"\tstat\tpvlaue\tpadj\n"
|
||||||
|
"gene1\tannotation\tgene2\t100.0\t1.0\t-0.02\t-8.5\t0.001\t0.05\n"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
_write(
|
_write(
|
||||||
contrast_dir / "results_dtu_transcript.tsv",
|
contrast_dir / "results_dtu_transcript.tsv",
|
||||||
"featureID\tgroupID\tpadj\n"
|
"featureID\tgroupID\tlog2FoldChange\tpvalue\tpadj\texonBaseMean\n"
|
||||||
"tx1\tgene1\t0.05\n",
|
"tx1\tgene1\t1.0\t0.01\t0.05\t20.0\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
out_report = tmp_path / "wf-transcriptomes-report.html"
|
out_report = tmp_path / "wf-transcriptomes-report.html"
|
||||||
|
|||||||
1269
bin/workflow_glue/volcano.py
Normal file
1269
bin/workflow_glue/volcano.py
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user