diff --git a/.github/prompts/plan-textReportInput.prompt.md b/.github/prompts/plan-textReportInput.prompt.md new file mode 100644 index 0000000..24bd71e --- /dev/null +++ b/.github/prompts/plan-textReportInput.prompt.md @@ -0,0 +1,284 @@ +# Plan: Plain-text report input (MultiQC llms-full.txt support) + +## Decisions (confirmed with user) +- Detection: by file extension only. `.json` -> existing JSON pipeline. Anything else + (`.txt`, `.md`, no extension) -> new text pipeline. No new CLI flag needed for mode + selection. +- Chunking: respects existing `--whole-report` flag. + - Not whole-report (default): split text into sections on markdown headings + (`#`..`######`), interpret each section separately, then synthesize (same shape as + JSON section-by-section mode). + - `--whole-report`: treat entire text as a single blob, one interpretation call. +- Descriptor/annotation (descriptor_schema.json, MultiQC-specific merges): NOT applied + to text mode. Skipped entirely. +- Sample grouping (samplesheet-derived group stats/percentages): NOT applied to text + mode initially. Superseded in part by Addendum 2 below, which adds shared + sample-sheet context + grouping-column awareness (but not per-metric numeric group + stats, since other text chunks remain unparsed prose/tables). +- Output structure: mirrors JSON mode (per-section markdown + optional synthesis + executive summary), using the same `combine_responses` shape but a generic title + (not "MultiQC Spatial Transcriptomics Interpretation"). +- `--as-is` flag (text mode only): when set, the system prompt drops `STYLE_GUIDE` and + `EVIDENCE_RULES` entirely, on the assumption that the input text already carries its + own analysis instructions (this is literally what MultiQC's `llms-full.txt` is for). + It does NOT change chunking — `--whole-report` vs. heading-split chunks still apply + the same way with `--as-is` on or off. Silently ignored when input is `.json`. +- `--review`: unavailable in text mode (verify.py's deterministic_findings/extract_entities + assume a JSON report dict of genes/cell types). If `--review` is passed with a + non-JSON input, print a warning and skip review (do not error). +- `--save-intermediates`: no-op in text mode (nothing to extract/annotate). Print a + warning and ignore if passed. +- Other JSON-only flags (`--descriptor`, `--extracted-output`, `--annotated-output`): + silently ignored in text mode (no warning needed). +- Oversized whole-report text exceeding num_ctx: no special handling/guard; let Ollama + behave as it normally would (truncate/error). Out of scope. +- Add unittest coverage for the new text-splitting function (header splitting, preamble + capture, duplicate-heading suffixing, no-heading fallback to a single "Full report" + section, empty-input edge case). + +## Steps + +1. `ingest/io_utils.py`: add `read_text(filepath) -> str` (plain UTF-8 read), alongside + existing `load_json`. +2. New `ingest/text_ingest.py`: add `split_text_sections(text) -> dict` that: + - Regex-matches markdown headings (`^#{1,6}\s+(.+)$`) line by line. + - Content before the first heading becomes a `"Preamble"` section if non-blank. + - Each heading's body becomes `{heading_text: {"data": body}}`; duplicate heading + names get a `" (2)"`, `" (3)"`, ... suffix. + - If no headings found at all, returns `{"Full report": {"data": text.strip()}}` + (or `{}` if text is blank). +3. `ingest/__init__.py`: export `read_text` and `split_text_sections`. +4. `interpret.py` changes (*depends on 1-3*): + - Add `TEXT_SYSTEM_PROMPT` (reuses `STYLE_GUIDE` + `EVIDENCE_RULES`, but describes a + schema-less plain-text report instead of JSON sections/descriptors/samplesheet). + - Add `TEXT_AS_IS_SYSTEM_PROMPT`, a minimal variant with no `STYLE_GUIDE` / + `EVIDENCE_RULES` appended — just a short framing sentence noting the text carries + its own instructions to follow. Used only when `--as-is` is set. + - Add `build_prompt_text(text)` (whole-report text prompt) and + `build_text_section_prompt(section_name, body)` (per-section text prompt) — + analogous to existing `build_prompt`/`build_section_prompt` but without JSON + fencing, descriptors, percentages, or group stats. + - Refactor the model-calling loop out of `interpret_report` into a shared + `_run_chunks(chunks, model, system, num_ctx, synthesize_final, think, gen_options, + samplesheet_context="", title=None)` helper (same logic currently inlined in + `interpret_report`, just parametrized with `title` passed through to + `combine_responses`). + - `combine_responses` gains a `title: str = None` param (defaults to current hardcoded + MultiQC title) so text mode can pass "Report Interpretation". + - Add `interpret_text_report(text, model, num_ctx=32768, whole_report=False, + synthesize_final=True, think=True, gen_options=None, user_instruction="", + as_is=False) -> str`: builds chunks via `split_text_sections` (or a single + whole-report chunk) exactly as before regardless of `as_is`; picks + `TEXT_AS_IS_SYSTEM_PROMPT` when `as_is` else `TEXT_SYSTEM_PROMPT`; calls + `_run_chunks`. +5. `llmize.py` changes (*depends on 4*): + - Add `is_json_input(path)` helper (extension check, case-insensitive `.json`). + - In `main()`, resolve the input path once, branch: `.json` -> existing `run_pipeline` + (unchanged); else -> new `run_text_pipeline(...)`. + - Add `--as-is` argparse flag (`store_true`) with help text explaining it drops the + style guide/evidence rules for plain-text input, assuming the text already embeds + its own instructions (e.g. MultiQC's `llms-full.txt`). + - New `run_text_pipeline(input_path, model, output_path, num_ctx, whole_report, + synthesize_final, think, gen_options, user_instruction, as_is)`: reads text via + `read_text`, calls `interpret_text_report(..., as_is=as_is)`, builds default output + filename the same way as `run_pipeline` (timestamped, based on input stem), builds + the footer via the existing `build_run_footer` with `mode="text whole-report"` or + `"text section-by-section"` (plus an `as-is` note when set), saves via existing + `save_text`. + - In `main()`, if `.txt`-mode is selected and `args.review` is set, print a warning + and skip review; if `args.save_intermediates` is set, print a warning and ignore. + `--descriptor`/`--extracted-output`/`--annotated-output` are simply unused in this + branch (no warning). +6. `tests/test_ingest.py` (*depends on 2*): add tests for `split_text_sections` covering + heading splitting, preamble capture, duplicate-heading suffixing, no-heading + fallback, and empty input. + +## Addendum: special-case chunker for MultiQC's `llms-full.txt` layout + +Real `llms-full.txt` exports don't use markdown headings at all, so the generic +`split_text_sections` falls back to one giant "Full report" chunk. The actual layout is: + +``` + +---------------------- +Tools used in the report: + +1. Sample Sheet +Description:

...

+---------------------- +2. Atlas Summary +Description:

...

+---------------------- +... (one numbered index entry per tool) +---------------------- +---------------------- +Tool: Sample Sheet +Section: +Title: Sample Sheet + +Plot type: violin plot +... +|table|...| +---------------------- +Tool: Atlas Summary +Section: +Title: Atlas Summary +... +---------------------- +... (one block per tool, this is the actual data) +``` + +### Decisions +- Detection: sniff for this layout using structural markers rather than filename — + looks_like_multiqc_llms_full(text) returns True when the text contains the + `Tool:` / `Section:` / `Title:` line triad together with `----------------------` + separator lines (e.g. at least one regex match of + `^Tool: .+\nSection: .*\nTitle: .+$` in MULTILINE mode). This is checked before + falling back to the generic markdown-heading splitter. +- Chunk granularity: one chunk per `Tool: X` block. `Section:` is blank in every + observed sample, so `Tool` name alone is used as the chunk name; if a future file has + a non-blank `Section:`, append it (`"X — Section"`) and still run duplicate-name + suffixing (reusing the same dedup helper as `split_text_sections`). +- Numbered tool-description index (`N. Tool Name` / `Description:

...

` blocks + before the data): parsed into a `{tool_name: description}` map and attached as extra + context to the matching `Tool: X` chunk (HTML-stripped plain text), mirroring how + `descriptor_schema.json` enriches JSON sections. Exact-string match on tool name; + fail-soft (no match -> chunk gets no extra description, same as an unmatched section + in the JSON pipeline today). The index itself is not turned into its own chunk. +- Leading free-form instructions block (before the tools index): dropped by default. + When `--as-is` is set AND this special-case layout is detected, this block is used + verbatim as the system prompt instead of `TEXT_AS_IS_SYSTEM_PROMPT` (it already tells + the model exactly how MultiQC wants its output formatted — bullet count, markdown + directives, etc.). Otherwise (no `--as-is`), it is discarded and `TEXT_SYSTEM_PROMPT` + (or `TEXT_AS_IS_SYSTEM_PROMPT`, if `--as-is` but a different plain-text file that + doesn't match this layout) is used as normal. +- `--whole-report` interaction: no special-casing. `--whole-report` still sends the + full raw text as a single call regardless of this detection; detection only changes + the per-section chunking path. + +### New pieces +- `ingest/text_ingest.py`: + - `looks_like_multiqc_llms_full(text) -> bool` — structural sniff described above. + - `split_multiqc_llms_full(text) -> tuple[dict, str]` — returns + `({chunk_name: {"data": body}}, leading_instructions_text)`. Parses the tool + description index first (building the `{tool_name: description}` map), then splits + the rest on `----------------------` separators, keeping only blocks that start with + `Tool: `, prepending the matched description (if any) to that block's body. +- `interpret.py` / `interpret_text_report`: when building chunks (non-whole-report + path), try `looks_like_multiqc_llms_full` first; if it matches, use + `split_multiqc_llms_full` instead of `split_text_sections`, and — only when + `as_is=True` — use the returned leading instructions text as the system prompt in + place of `TEXT_AS_IS_SYSTEM_PROMPT`. +- `tests/test_ingest.py`: new tests for `split_multiqc_llms_full` using a small + synthetic snippet in this exact layout (index + 2-3 `Tool:` blocks, one with a + matching description, one without), plus a `looks_like_multiqc_llms_full` positive/ + negative case. + +## Addendum 2: shared sample-sheet context across chunks (confirmed with user) + +Rationale: LLM-based cross-sample analysis is only useful if sample metadata (response, +timepoint, region, etc.) stays visible next to the actual results. JSON mode already does +this (`build_samplesheet_context`/`sample_groups`, keyed off `multiqc_samplesheet`); text +mode currently treats the "Sample Sheet" chunk as just another opaque chunk, with no +cross-chunk awareness of metadata or which columns can group samples. + +### Decisions +- Applies to chunks produced by **either** text chunker (`split_text_sections` and + `split_multiqc_llms_full`) — detection operates on the resulting `{name: {"data": ...}}` + dict, splitter-agnostic. +- Chunk-name match is a strict, case-insensitive alias check against + `{"sample sheet", "samplesheet", "sample_sheet"}` (after stripping any + `" —
"` suffix added by `split_multiqc_llms_full`). No fuzzy/substring + matching, to avoid swallowing an unrelated real data section. +- Once matched, the chunk's `data` text is parsed as a markdown table (first column = + row/sample id). If parsing fails or yields fewer than 2 rows, treat as "not found": + leave the chunk untouched and add no shared context (fail-soft, same philosophy as + the rest of text-mode enrichment). +- On success, the chunk is popped out of the per-chunk analysis list entirely (mirrors + JSON mode's `iter_analysis_sections` skipping `multiqc_samplesheet`) and instead + used to build a shared context block appended to the system prompt for every + remaining chunk **and** the synthesis pass. +- `--whole-report`: no change needed — the full raw text (including the sample sheet) + is already sent as a single blob. +- `--as-is`: the shared sample-sheet context is still added regardless of `as_is`; + only the *style/format* system prompt varies with `as_is`, not this metadata-context + enrichment. +- Reuses (via refactor, not duplication) the existing group-detection logic + (`sample_groups`/`build_samplesheet_context`) by splitting each into a thin + report-shaped wrapper plus a shape-agnostic core function operating on a plain + `{sample_id: {col: value}}` dict, so text mode calls the same core logic JSON mode + already relies on. + +### New pieces +- `ingest/text_ingest.py`: + - `parse_markdown_table(text) -> dict` — finds the first `|...|` header + `|---|...|` + separator + data rows in `text` (ignoring surrounding prose), returns + `{first_column_value: {other_col: value, ...}}` per row. Returns `{}` if no valid + table is found. + - `extract_samplesheet_chunk(sections: dict) -> tuple[dict, dict | None]` — looks for + a chunk name matching the alias set above; if found and its body parses into a + table with >= 2 rows via `parse_markdown_table`, returns + `(sections_without_that_chunk, parsed_samplesheet_dict)`; otherwise returns + `(sections, None)` unchanged. +- `interpret.py`: + - Refactor `sample_groups(report)` into a thin wrapper around a new + `_sample_groups_from_data(samplesheet_data: dict, max_group_values=10) -> dict` + (current body of `sample_groups`, minus the report/`SAMPLESHEET_KEY` unwrapping). + - Refactor `build_samplesheet_context(report)` into a thin wrapper around a new + `_samplesheet_context_block(samplesheet_data: dict, groups: dict) -> str` (current + body, minus the report unwrapping). Pure refactor — JSON-mode behavior unchanged. + - `interpret_text_report`: after building `sections` (non-whole-report branch, either + splitter), call `extract_samplesheet_chunk`; if a samplesheet dict comes back, + compute `groups = _sample_groups_from_data(...)` and + `samplesheet_context = _samplesheet_context_block(...)`, append it to `system` + (after the base prompt, before `build_user_instruction`), and pass it through to + `_run_chunks(..., samplesheet_context=samplesheet_context)` so synthesis also sees + it (the parameter already exists on `_run_chunks`/`synthesize_sections`, just + unused by text mode today). +- `tests/test_ingest.py`: tests for `parse_markdown_table` (clean table, table with + surrounding prose, no table present, malformed/short table) and + `extract_samplesheet_chunk` (match+pop, no match leaves sections untouched, match + but unparseable table leaves sections untouched). +- `tests/test_interpret.py`: regression check that `sample_groups`/ + `build_samplesheet_context` still behave identically after the refactor, plus a new + test that `interpret_text_report` excludes the Sample Sheet chunk from individual + analysis and includes the SAMPLE SHEET context block in the system prompt used for + every remaining chunk (reusing the existing stubbed-`chat_ollama` test style already + present in that file). + +## Relevant files +- `ingest/io_utils.py` — add `read_text`. +- `ingest/text_ingest.py` (new) — `split_text_sections`, `looks_like_multiqc_llms_full`, + `split_multiqc_llms_full`, `parse_markdown_table`, `extract_samplesheet_chunk`. +- `ingest/__init__.py` — export additions. +- `interpret.py` — `TEXT_SYSTEM_PROMPT`, `TEXT_AS_IS_SYSTEM_PROMPT`, `build_prompt_text`, + `build_text_section_prompt`, `_run_chunks` refactor, `interpret_text_report(..., as_is)` + (now also dispatching to the MultiQC-specific splitter when detected, and injecting + shared sample-sheet context), `combine_responses(title=...)`, + `_sample_groups_from_data`/`_samplesheet_context_block` (extracted from + `sample_groups`/`build_samplesheet_context`). +- `llmize.py` — `is_json_input`, `--as-is` flag, `run_text_pipeline`, dispatch in + `main()`, warnings for `--review`/`--save-intermediates` in text mode. +- `tests/test_ingest.py` — tests for `split_text_sections`, `split_multiqc_llms_full`/ + `looks_like_multiqc_llms_full`, `parse_markdown_table`, `extract_samplesheet_chunk`. +- `tests/test_interpret.py` — regression + new coverage for shared sample-sheet context + in text mode. + +## Verification +1. `python3 -m compileall llmize.py interpret.py check_env.py ingest` +2. `python3 -m unittest discover tests` (new split_text_sections tests pass) +3. Manual: run `python3 llmize.py --input some_report.txt --model gemma4 --num_ctx 16384` + against a sample MultiQC `llms-full.txt`-style file and confirm per-section + + synthesis output; repeat with `--whole-report`. +4. Manual: confirm `.json` inputs still go through the unchanged JSON pipeline + (no regression). +5. Manual: run against `data/llms-full.txt` (default section-by-section mode) and + confirm chunks are now one-per-`Tool:` block instead of a single "Full report" + blob; check descriptions are attached where the index has a matching entry. +6. Manual: run `data/llms-full.txt` with `--as-is` and confirm the leading + instructions block is used as the system prompt. +7. Manual: run `data/llms-full.txt` in default (non-whole-report) mode and confirm the + "Sample Sheet" chunk is no longer analyzed on its own, and that other chunks' + prompts/system prompt carry the sample-sheet context + grouping note (e.g. the + `response` column). + diff --git a/README.md b/README.md index 90c32a8..60a292f 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,17 @@ # llmize -Reduce, annotate, and interpret MultiQC spatial-transcriptomics reports with a -local LLM (via Ollama). The tool is built around MultiQC's report structure but -also accepts any JSON with section-keyed data — MultiQC-specific handling (sample -sheet, spatial-neighbors/co-occurrence merging) kicks in automatically when those -sections are present, and other data passes through as-is. +Interpret bioinformatics analysis/QC reports with a local LLM (via Ollama) — as JSON + (section-keyed) or plain text/markdown reports. MultiQC is supported: its `*_data.json` + exports and its `llms-full.txt` export both work out of the box. Any other section-keyed + JSON or free text is interpreted generically. Interpretation runs **entirely on your own machine** — the model and inference are local, and nothing is sent to any external service. There are two ways to run it: -- **Nextflow (recommended)** — runs in a container with Ollama and Python deps +- **Nextflow** — runs in a container with Ollama and Python deps already baked in; no local setup beyond Nextflow itself and a container engine. -- **Directly with Python** — useful for local development/debugging; needs a +- **Python** — useful for local development/debugging; needs a local Python environment and a locally running Ollama. ## Run with Nextflow @@ -36,8 +35,8 @@ don't need Ollama or the Python dependencies installed on your host for this pat ```bash nextflow run main.nf \ - -profile igs \ - --input data/multiqc_data.json \ + -profile \ + --input data.json \ --slurm_account \ -w /usr/local/scratch/$USER/work \ -resume @@ -53,7 +52,7 @@ The Nextflow module uses an Ollama model cache directory via `OLLAMA_MODELS`. ```bash nextflow run main.nf \ - -profile igs \ + -profile \ --input data/multiqc_data.json \ --slurm_account \ --ollama_models_dir /path/to/persistent/models \ @@ -65,7 +64,8 @@ nextflow run main.nf \ `-profile native` runs `llmize.py` directly on the host instead of in a container, so it needs the same local setup as "Run directly with Python" below -(Ollama installed and running, Python dependencies installed). +(Ollama installed and running, Python dependencies installed). Useful on Macs where + Docker does not have access to GPU. ## Run directly with Python @@ -93,8 +93,6 @@ Confirm everything is in place — Python version, the `ollama` client, the schema: ```bash -python3 check_env.py -# or, equivalently: python3 llmize.py --check ``` @@ -103,13 +101,13 @@ It prints a clear ✓/⚠/✗ report and exits non-zero if a required check fail ### 4. Run it ```bash -python3 llmize.py --input data/multiqc_data.json --model gemma4 +python3 llmize.py --input multiqc_data.json --model gemma4 ``` Common flags (see `python3 llmize.py --help` for the full list): ```bash -python3 llmize.py --input data/multiqc_data.json \ +python3 llmize.py --input data.json \ --prompt "Summarize immune infiltration and flag any tumor-immune interactions." \ --review --output my_interpretation.md ``` @@ -118,13 +116,54 @@ Note that `llmize.py`'s own flags use hyphens and differ slightly from the Nextflow parameter names below (e.g. `--whole-report`/`--no-synthesis` instead of `--whole_report`/`--synthesis false`). -## Continuous integration +## JSON input + +`llmize.py` accepts JSON reports with section-keyed data, such as MultiQC's `*_data.json` exports. The JSON is processed through the normal reduce/annotate/interpret pipeline, with MultiQC-specific enhancements applied automatically when relevant sections are detected. + +When using JSON input, each key may be described in a separate descriptor file, which provides metadata and instructions for how that section should be interpreted (see `ingest/descriptor_schema.json`). + + +## Plain-text input + +`llmize.py` also accepts plain text inputs — for example MultiQC's +`llms-full.txt` export, or any other free-text report. The mode is chosen purely +by file extension: `.json` goes through the reduce/annotate/interpret +pipeline; anything else (`.txt`, `.md`, no extension, ...) is treated as plain text and bypasses JSON extraction/annotation entirely (so +`--descriptor`, `--extracted-output`, `--annotated-output`, `--save-intermediates`, +and `--review` don't apply and are ignored, with a warning for the latter two). + +```bash +python3 llmize.py --input llms-full.txt --model gemma4 +``` + +By default (not `--whole-report`), the text is split into chunks and each is +interpreted separately before a final synthesis pass, same as JSON's +section-by-section mode: +- If the text has markdown headings (`#` .. `######`), each heading becomes a chunk. +- MultiQC's real `llms-full.txt` layout is auto-detected and split one chunk per `Tool:` +block, with the matching description from the report's tool index attached automatically. +- If a chunk looks like a sample sheet (a table named "Sample Sheet"/"samplesheet"), + it's pulled out of the individual chunk list and kept as shared context instead — + its content, and any metadata column that usefully groups samples (e.g. + responder/non-responder), is carried into every other chunk's analysis and into + the final synthesis. -The GitHub Actions workflow (`.github/workflows/test.yml`) runs on every pull request -to `main`, across Python 3.9 / 3.11 / 3.12. Because CI runners have no Ollama server, -it installs dependencies, byte-compiles all modules, runs the unit test suite -(`python -m unittest discover tests`), and runs `check_env.py` informationally -(non-blocking, since there's no local Ollama service in CI). +## Just run my prompt + +`--whole-report` sends the entire text as a single call with no chunking. + +`--as-is` omits any system prompt and assumes analysis prompt is included, like default +MultiQC's llms output does. + +When combined, sends the entire report to the model without any automatic chunking or system prompt, giving you full control over the input and instructions. + +```bash +python3 llmize.py --input llms-full.txt --model gemma4 --as-is --whole-report +``` + +`--as-is` only affects the system prompt/style, not chunking — `--whole-report` still +works the same way with or without it. This flag is currently `llmize.py`-only and +not yet exposed as a Nextflow parameter. ## Parameters @@ -133,10 +172,10 @@ are set explicitly, e.g. `--think false` or `--review true`. | Parameter | Default | Description | |-----------|---------|-------------| -| `--input` | — (required) | Path to the MultiQC `*_data.json` report, or any JSON with section-keyed data. | -| `--descriptor` | bundled schema | Descriptor schema JSON; override to use your own (see below). | +| `--input` | — (required) | Path to a report file: JSON (MultiQC's `*_data.json`, or any section-keyed JSON) or plain text (e.g. MultiQC's `llms-full.txt`); mode is auto-detected by file extension. See [Plain-text input](#plain-text-input). | +| `--descriptor` | bundled schema | Descriptor schema JSON (JSON mode only), keyed by top-level section name; overlaid onto matching sections to tell the model what each field means. Copy `ingest/descriptor_schema.json` and edit it to describe your own report's sections, then pass the copy here. Sections with no matching entry pass through unannotated. | | `--model` | `gemma4` | Ollama model name. | -| `--outdir` | `results` | Directory for the output interpretation. | +| `--output` | (none) | Path to the output file; if not specified, defaults to `_interpretation_.md` in the current directory. | | `--prompt` | (none) | Extra instruction appended to the model prompt. | | `--num_ctx` | `32768` | Context window size. | | `--temperature` | model default | Sampling temperature (`0` = deterministic). | @@ -150,17 +189,3 @@ are set explicitly, e.g. `--think false` or `--review true`. | `--review` | `false` | Self-review pass flagging gene/cell-type names absent from the report. | | `--review_passes` | `2` | Maximum review passes (used with `--review true`). | -Execution/infrastructure parameters (`--container`, `--ollama_models_dir`, profiles) -are covered under **Run with Nextflow** above. These are the Nextflow parameter -names (see `nextflow.config`); when running `llmize.py` directly, use `--help` to -see its own flag names. - -## Where to place your files - -- **QC report** — your MultiQC `*_data.json` (or any section-keyed JSON). Put it anywhere and point `--input` at - it; the examples keep reports in `data/`. -- **Descriptor schema** — the default ships at `ingest/descriptor_schema.json` - and is used automatically. To describe your own report sections, copy that file, edit - the entries, and pass it with `--descriptor /path/to/your_schema.json`. -- **Output** — the interpretation `.md` is written to `results/` (or wherever - `--outdir` points). diff --git a/data/llms-full.txt b/data/llms-full.txt new file mode 100644 index 0000000..00afc9b --- /dev/null +++ b/data/llms-full.txt @@ -0,0 +1,959 @@ +You are an expert in bioinformatics, sequencing technologies, genomics data analysis, and adjacent fields. + +You are given findings from a MultiQC report, generated by a bioinformatics workflow. +MultiQC supports various bioinformatics tools that output QC metrics, and aggregates those metrics +into a single report. It outputs a "General Statistics" table with key metrics for each sample across +all tools. That table is followed by more detailed sections from specific tools, that can include tables, +as well as plots of different types (bar plot, line plot, scatter plot, heatmap, etc.) + +You are given data from such a report. Your task is to analyse the data, and +give 1-2 bullet points of a very short and concise overall summary for the results. +Don't waste words: mention only the important QC issues. If there are no issues, just say so. +Just print one or two bullet points, nothing else. +Please do not add any extra headers to the response. + +Use markdown to format your reponse for readability. Use directives with pre-defined classes +.text-green, .text-red, and .text-yellow to highlight severity, e.g. :span[39.2%]{.text-red}. +Highlight any mentioned sample names or sample named prefixes or suffixes with a sample directive, +and make sure to use the same color classes for severity, e.g. :sample[A1001.2003]{.text-yellow} +or :sample[A1001]{.text-yellow}. Do not put multiple sample names inside one directive. + +You must use only multiples of 4 spaces to indent nested lists. + + + +Two examples of short summaries: + +- :span[11/13 samples]{.text-green} show consistent metrics within expected ranges. +- :sample[A1001.2003]{.text-red} and :sample[A1001.2004]{.text-red} exhibit extremely high percentage of :span[duplicates]{.text-red} (:span[65.54%]{.text-red} and :span[83.14%]{.text-red}, respectively). + +- All samples show good quality metrics with :span[75.7-77.0%]{.text-green} CpG methylation and :span[76.3-86.0%]{.text-green} alignment rates +- :sample[2wk]{.text-yellow} samples show slightly higher duplication (:span[11-15%]{.text-yellow}) compared to :sample[1wk]{.text-green} samples (:span[6-9%]{.text-green})' + + + +---------------------- + +Tools used in the report: + +1. Sample Sheet +Description:

The sample sheet provided as input to the pipeline.

+ +---------------------- + +2. Atlas Summary +Description:

Summary of the cells and genes in the atlas.

+ +---------------------- + +3. Atlas Cell Types +Description:

Summary of the cell types in the atlas.

+ +---------------------- + +4. Input Summary +Description:

Summary of the AnnData object right after it was read.

+ +---------------------- + +5. Output Cell Types +Description:

Assigned cell types based on deconvolution / cell typing results. Unassigned counts appear in a nameless column.

+ +---------------------- + +6. Cell Type Probabilities +Description:

Average max cell type probabilities used to assign the cell types (greater is better).

+ +---------------------- + +7. Moran I Interactions +Description:

Top 100 differential interactions across samples. There are 0 significant differential interactions (p_adj<=0.05) found between ['HC03BTC_visiumHD', 'HC01BTC_visiumHD', 'HC04BTC_visiumHD'] and ['HC05BTC_visiumHD', 'HC08BTC_visiumHD', 'HC07BTC_visiumHD'], marked with *.

+ +---------------------- + +8. Average Clustering +Description:

This graph measure shows the degree to which nodes cluster together.

+ +---------------------- + +9. Closeness Centrality +Description:

This graph measure reflects how close the group is to other nodes.

+ +---------------------- + +10. Degree Centrality +Description:

This graph measure is the fraction of non-group members connected to group members.

+ +---------------------- + +11. Deseq2 Response +Description:

DESeq2 results for response variable with significant DE genes (padj<=0.05) between non-responder and responder. Log2 fold change (X) and -log10 adjusted p-value (Y) shown.

+ +---------------------- + +12. Squidpy Ligrec Interactions +Description:

Top 100 differential interactions across samples. There are 0 significant differential interactions (p_adj<=0.05) found between ['HC03BTC_visiumHD', 'HC01BTC_visiumHD', 'HC04BTC_visiumHD'] and ['HC05BTC_visiumHD', 'HC08BTC_visiumHD', 'HC07BTC_visiumHD'], marked with *.

+ +---------------------- + +13. Spacemarkers Lrscores Interactions +Description:

Top 100 differential interactions across samples. There are 1 significant differential interactions (p_adj<=0.05) found between ['HC03BTC_visiumHD', 'HC01BTC_visiumHD', 'HC04BTC_visiumHD'] and ['HC05BTC_visiumHD', 'HC08BTC_visiumHD', 'HC07BTC_visiumHD'], marked with *.

+ +---------------------- + +14. Spatial Neighbors +Description:

Cell type immediate neighborhood across samples. The rate of self-neighborhood indicates clustering of a cell type. The rate of neighbors with other cell types (self omitted) indicates how these clusters interact with each other.

+ +---------------------- + +---------------------- + +Tool: Sample Sheet +Section: +Title: Sample Sheet + +Plot type: violin plot + +Number of samples: 6 + +Metrics: +file - file +type - type +response - response +data_directory - data_directory +expression_profile - expression_profile + +|sample|file|type|response|data_directory|expression_profile| +|---|---|---|---|---|---| +|HC01BTC_visiumHD|samplesheet.csv|visium-hd|non-responder|s3://project-5678660b-483c-470c-a0b1-2476fb01fa56/datasets/e4b04bf1-f4e9-408f-9704-0d67d4e4e7cf/data|| +|HC03BTC_visiumHD|samplesheet.csv|visium-hd|non-responder|s3://project-5678660b-483c-470c-a0b1-2476fb01fa56/datasets/2b620ce7-62ac-4c56-93d3-3910db3c7b15/data|| +|HC04BTC_visiumHD|samplesheet.csv|visium-hd|non-responder|s3://project-5678660b-483c-470c-a0b1-2476fb01fa56/datasets/1fd5269a-5cc2-47aa-9363-867b02797acb/data|| +|HC05BTC_visiumHD|samplesheet.csv|visium-hd|responder|s3://project-5678660b-483c-470c-a0b1-2476fb01fa56/datasets/f88669be-96e9-42f4-9fed-13bf1b734778/data|| +|HC07BTC_visiumHD|samplesheet.csv|visium-hd|responder|s3://project-5678660b-483c-470c-a0b1-2476fb01fa56/datasets/7dc58554-509b-4abe-8d64-d515829ec8b2/data|| +|HC08BTC_visiumHD|samplesheet.csv|visium-hd|responder|s3://project-5678660b-483c-470c-a0b1-2476fb01fa56/datasets/a3a9b3be-f793-4bcb-9c61-02f77dd52854/data|| + +---------------------- + +Tool: Atlas Summary +Section: +Title: Atlas Summary + +Plot type: violin plot + +Number of samples: 1 + +Metrics: +n_genes - n_genes +n_cells - n_cells +mean_genes_by_counts - mean_genes_by_counts +mean_cells_by_counts - mean_cells_by_counts +mean_total_nnz_counts - mean_total_nnz_counts +mean_percent_mito - mean_percent_mito + +|Sample|n_genes|n_cells|mean_genes_by_counts|mean_cells_by_counts|mean_total_nnz_counts|mean_percent_mito| +|---|---|---|---|---|---|---| +|updated_CAFsubtype_TNK_PTonly_LovelessAtlas|36601|561855|1301.66|19981.50|1.60|1.49| + +---------------------- + +Tool: Atlas Cell Types +Section: +Title: Atlas Cell Types + +Plot type: violin plot + +Number of samples: 1 + +Metrics: +PDAC - PDAC +TNK - TNK +MYELOID - MYELOID +ENDOTHELIAL - ENDOTHELIAL +CAF - CAF +PERICYTES - PERICYTES +myCAF - myCAF +ENDOCRINE - ENDOCRINE +ACINAR - ACINAR +CYCLING TNK - CYCLING TNK +B CELLS - B CELLS +PLASMA - PLASMA +MAST - MAST +CYCLING. MYELOID - CYCLING. MYELOID +iCAF - iCAF + +|Sample|PDAC|TNK|MYELOID|ENDOTHELIAL|CAF|PERICYTES|myCAF|ENDOCRINE|ACINAR|CYCLING TNK|B CELLS|PLASMA|MAST|CYCLING. MYELOID|iCAF| +|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +|updated_CAFsubtype_TNK_PTonly_LovelessAtlas|227293|92076|54465|31908|31589|31487|29488|15344|13990|13986|8941|5353|3070|1985|880| + +---------------------- + +Tool: Input Summary +Section: +Title: Input Summary + +Plot type: violin plot + +Number of samples: 6 + +Metrics: +n_genes - n_genes +n_cells - n_cells +mean_genes_by_counts - mean_genes_by_counts +mean_cells_by_counts - mean_cells_by_counts +mean_total_nnz_counts - mean_total_nnz_counts +mean_percent_mito - mean_percent_mito + +|Sample|n_genes|n_cells|mean_genes_by_counts|mean_cells_by_counts|mean_total_nnz_counts|mean_percent_mito| +|---|---|---|---|---|---|---| +|HC01BTC_visiumHD|18085|169550|267.61|2508.89|1.25|1.09| +|HC03BTC_visiumHD|18085|175561|176.92|1717.47|1.25|1.51| +|HC04BTC_visiumHD|18085|163383|187.03|1689.68|1.21|1.50| +|HC05BTC_visiumHD|18085|164263|97.44|884.99|2.37|1.03| +|HC07BTC_visiumHD|18085|175561|82.64|802.26|1.16|1.18| +|HC08BTC_visiumHD|18085|142989|91.70|725.02|1.17|1.57| + +---------------------- + +Tool: Output Cell Types +Section: +Title: Output Cell Types + +Plot type: violin plot + +Number of samples: 6 + +Metrics: +myCAF - myCAF + - +PDAC - PDAC +CAF - CAF +PERICYTES - PERICYTES +ENDOTHELIAL - ENDOTHELIAL +ENDOCRINE - ENDOCRINE +MYELOID - MYELOID +iCAF - iCAF +CYCLING MYELOID - CYCLING MYELOID +PLASMA - PLASMA +B CELLS - B CELLS +ACINAR - ACINAR +TNK - TNK +MAST - MAST +CYCLING TNK - CYCLING TNK + +|Sample|myCAF||PDAC|CAF|PERICYTES|ENDOTHELIAL|ENDOCRINE|MYELOID|iCAF|CYCLING MYELOID|PLASMA|B CELLS|ACINAR|TNK|MAST|CYCLING TNK| +|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +|HC01BTC_visiumHD|58143|39655|44122|11574|1297|4305|981|5516|1888|804|530|43|157|360|139|36| +|HC03BTC_visiumHD|103846|36805|21777|4571|2271|1899|1745|975|645|340|255|191|140|57|33|11| +|HC04BTC_visiumHD|48045|53426|18800|14705|1835|2868|2013|11849|2032|2016|3179|208|363|1907|79|58| +|HC05BTC_visiumHD|2711|95018|12448|6441|1577|1970|3996|317|242|314|44|145|38121|701|149|69| +|HC07BTC_visiumHD|13075|119492|3514|20198|1566|975|4353|6247|1203|710|569|651|217|2553|163|75| +|HC08BTC_visiumHD|2966|85584|716|10290|18293|6325|95|4881|4563|1487|2166|1142|4|4071|231|175| + +---------------------- + +Tool: Cell Type Probabilities +Section: +Title: Cell Type Probabilities + +Plot type: violin plot + +Number of samples: 6 + +Metrics: +ACINAR - ACINAR +B CELLS - B CELLS +CAF - CAF +CYCLING MYELOID - CYCLING MYELOID +CYCLING TNK - CYCLING TNK +ENDOCRINE - ENDOCRINE +ENDOTHELIAL - ENDOTHELIAL +MAST - MAST +MYELOID - MYELOID +PDAC - PDAC +PERICYTES - PERICYTES +PLASMA - PLASMA +TNK - TNK +iCAF - iCAF +myCAF - myCAF + +|Sample|ACINAR|B CELLS|CAF|CYCLING MYELOID|CYCLING TNK|ENDOCRINE|ENDOTHELIAL|MAST|MYELOID|PDAC|PERICYTES|PLASMA|TNK|iCAF|myCAF| +|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +|HC01BTC_visiumHD|0.48|0.28|0.45|0.34|0.31|0.51|0.44|0.34|0.45|0.73|0.42|0.36|0.32|0.38|0.65| +|HC03BTC_visiumHD|0.35|0.50|0.45|0.38|0.28|0.54|0.45|0.31|0.41|0.61|0.43|0.43|0.34|0.37|0.68| +|HC04BTC_visiumHD|0.40|0.33|0.42|0.37|0.29|0.59|0.39|0.30|0.46|0.72|0.38|0.39|0.38|0.35|0.56| +|HC05BTC_visiumHD|0.74|0.29|0.44|0.33|0.30|0.67|0.40|0.32|0.35|0.58|0.42|0.38|0.34|0.33|0.47| +|HC07BTC_visiumHD|0.36|0.40|0.49|0.36|0.31|0.61|0.42|0.32|0.46|0.52|0.41|0.42|0.40|0.36|0.53| +|HC08BTC_visiumHD|0.81|0.42|0.47|0.36|0.33|0.34|0.51|0.36|0.42|0.57|0.86|0.45|0.40|0.44|0.49| + +---------------------- + +Tool: Moran I Interactions +Section: +Title: Interaction scores + +Plot type: heatmap +X axis: Interaction +Y axis: Sample +Z axis: Score + +|Sample|TMEM221-I|NAGS-I|TAC3-I|AC011005.1-I|SLC24A2-I|IL20-I|KRTAP5-7-I|NBPF19-I|TSSK2-I|MSGN1-I|FLRT1-I|LEUTX-I|EPS8L1-I|PTGES2-I|TMEM176B-I|NAT2-I|SNAI2-I|NBPF3-I|CNBD1-I|HOXC5-I|RHEBL1-I|GALNT12-I|TYRP1-I|DHRS13-I|C8orf88-I|FOLH1-I|TMEM249-I|NANOGNB-I|THSD1-I|MCEMP1-I|PPP1R14D-I|SYNGR2-I|CORIN-I|RPEL1-I|LKAAEAR1-I|XKRX-I|NCOA2-I|MAGEB16-I|PLA2R1-I|RPTN-I|TMEM185B-I|MET-I|PRSS27-I|CRYBG3-I|DGAT2L6-I|GPR45-I|BX276092.9-I|LAMP5-I|ADGRF2-I|DRD2-I|VXN-I|CNN2-I|IZUMO3-I|ARHGAP11B-I|GUCY2F-I|KRTAP4-5-I|ACAA2-I|OR6A2-I|MAGEE2-I|FCRL4-I|MAPKAPK3-I|SYCE1-I|UBASH3A-I|DOP1B-I|TGM1-I|KRT3-I|CLEC4C-I|WDR25-I|IGFBP5-I|LHX8-I|PTPRG-I|OR1G1-I|ITGA2-I|SPHK1-I|FAM126B-I|PDGFC-I|COL12A1-I|FNDC10-I|SULF1-I|SP8-I|FAM229A-I|TMEM43-I|CCDC77-I|PIP-I|SYNJ2-I|MPP4-I|ZDHHC3-I|S100A4-I|HAAO-I|CPXCR1-I|RAD51-I|ARSH-I|ARAP3-I|KRT6C-I|IGSF8-I|PYGO2-I|CTSW-I|DUXA-I|GRP-I|CST9L-I| +|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +|HC03BTC_visiumHD|-0.00025810732190540315|0.007050402882932278|0.01652071275751681|-0.00028645464438694203|0.0027654322385936296|-0.0002278929586779162|0.006306926730363823|-0.0003475783475775075|-0.00011393349701811443|-6.978118613744625e-05|-5.85911275127273e-05|-0.0001595141654278819|0.17263992886006618|0.0484540975370524|0.04845854950257183|0.00607946806869098|0.022129094134331503|0.004442969790520042|0.006381545387179923|0.005430304338547155|-0.0002739224111476947|0.024305006064326957|0.005085028334253226|0.008557790276716881|0.0035442840949464873|0.014504630940673446|-0.00020895735679519795|-7.975072202882087e-05|0.0038187123174745264|0.015803062571687242|0.01095053882146345|0.1630244261388757|0.012541178517850124|0.006382443748100474|-0.0001370964285339737|-0.000229792503067756|0.007587443030679992|-0.00014621965230032669|0.009843731484637222|-0.00014887830451967984|0.01652115263649864|0.0415683467760739|0.0023850890494561162|0.005473364995054542|-0.0001139334970180829|-9.6841817436966e-05|-9.684181743694267e-05|0.03220261214703856|-0.00013672331189390976|-0.00017043523176260155|-0.00011393349701787661|0.10711136872681606|0.018408973043798946|0.004488655001318324|-0.00012584622523502178|-0.00029580629293338243|0.007981122080533752|-0.00034277265313506733|-0.00017185948161028665|-0.0002594241959968643|0.019320880492155225|-0.00039527419841336737|0.003675873851503606|0.003551198156868351|0.004852170265035515|-7.40538200378374e-05|-0.0001428956643269542|0.001325972472418312|0.3385997225616511|-9.228413487649897e-05|0.023563842818559444|-9.792698607374334e-05|0.06983672064225264|0.04702239932118398|0.01218023050158476|0.03695704820204996|0.13273165590804745|0.009483277047791793|0.15585796518677628|-3.645589463340599e-05|-0.0007478545815958999|0.01896635547372113|0.00735990979081639|-5.81842446828328e-05|0.010711580566310493|-0.0002853045974753236|0.03135736005708128|0.15323883809701416|0.003964207524852096|-0.00016022639633660874|0.004957702825612759|-0.00010922703421137686|0.017962405096313507|-0.00014242092790064858|0.016014150707312665|0.009443770690553455|-0.0005796340833632157|-0.0003257363302212479|0.01842555592164358|-9.684181743665259e-05| +|HC01BTC_visiumHD|-0.00023062518568414163|0.008025392602010515|0.01690665294446874|-0.00029007053441159106|0.0029421608528292583|-0.00025367983167590147|0.007842268493515684|-0.00041303295799216923|-0.00020450743332955945|-7.66803314200638e-05|-0.00020647383924819336|-0.00016517225072857654|0.18803871250585452|0.04454616137882226|0.05020037468493455|0.004762360564638222|0.025234130975536437|0.0033277318247786737|0.007351001088184993|0.004054091757919187|-0.0006571652015031101|0.021168936398690226|0.0044243703225322634|0.008334634081060069|0.0033041503723278236|0.012078136502729027|-0.00021296439900011735|-9.555729356172885e-05|0.0033465510797611358|0.013648328398658377|0.014439476770670742|0.16722803390305538|0.012237550903660065|0.00451953523855929|-0.0001307567314059237|-0.00020801179894352662|0.007403930059845158|-0.00013745753458512357|0.009468333919166553|-0.00018926433356205148|0.019744349761052514|0.039454476075786546|0.003055746202301571|0.004996457034808703|-0.00014157287439102076|-8.454600136448924e-05|-0.00010027664544298728|0.03942038671543307|-0.0001243656338482363|-0.0002064738392478047|-0.00017008910066891914|0.0917129986462148|0.012269504438474826|0.006006129951120699|-0.00017362364956593747|-0.00038942188369580684|0.008493123869560635|-0.00043779698943218886|-0.00019467303732442374|-0.00023843514225815348|0.02140884586868459|-0.00016025545879659494|0.002586380709424759|0.005378825818340599|0.006529303025995496|-8.257930553937795e-05|0.003964197849524629|0.0026814208405079717|0.4538137754998903|-0.00012527022966760644|0.027074302903682845|-0.00012338251208421038|0.09944393138969329|0.05396904415309361|0.013070483606757954|0.032360734179169404|0.13891130170547034|0.005724467655799893|0.20913375849276777|-4.128794802483099e-05|0.0006971667913252518|0.02169759149228987|0.008856791500383692|-5.8983826589584913e-05|0.013331562359130621|-0.0003911632435669851|0.03836279153856863|0.11718702250528917|0.006809267512188163|-0.0002428722089166778|0.0031647263827581772|-0.00011797461169127613|0.016744876074125045|-0.00020352312499887073|0.017924723574048004|0.010279698905197707|0.0013425933514987253|-0.00030482529641861946|0.031216028992707704|-0.00014332860578922078| +|HC05BTC_visiumHD|0.021042645611911857|-0.0005944258195704587|-9.032717519790083e-05|-8.52520082844599e-05|-9.865007379269407e-05|-6.263271217464878e-05|-0.00017153344966020344|0.01543458863783346|0.02620553462431476|-3.095285585423314e-05|0.015944758154421337|-3.044547756037263e-05|0.03638016957674306|0.02088324244119882|0.032118973681206635|-0.00011676186330703674|0.011197605778241271|-0.0005093912990075454|-9.996911437647175e-05|-0.00019843708671982258|0.017090617248264876|0.009457389789952936|-0.00018422303891595088|0.0031505327783753366|0.008808163404529064|-0.0002830917366544455|-9.134199356763664e-05|-2.4356233768023096e-05|-0.0007007152376009448|-8.119170918241469e-05|-0.0002862606845815843|0.05444350920843376|0.005520957367823563|-0.00017051855096245975|-6.08928086929411e-05|-8.52520082845842e-05|0.013633213689923747|-6.089280869286595e-05|0.0040769739956705835|-3.65347954955406e-05|0.008061262457006231|0.021580734285013347|-0.0003103678905859376|0.009027056961571496|-4.483853035129591e-05|-1.8267064116990037e-05|-4.363895651857504e-05|0.012429625266610254|-1.8267064117024085e-05|-7.256486026663644e-05|0.02055252553821758|0.030950196112720384|-7.916209715750565e-05|-0.00018371558338765702|-3.0445477560310406e-05|-0.00012534632398641062|0.01370141026578483|-0.00017127992485757056|-8.926592885606862e-05|-3.7042176878304445e-05|0.012205925230274984|0.010167089888849906|0.008237566096928037|0.010736003511067313|-0.0006816355755965369|-3.0952855854195326e-05|0.02543506636208853|0.0062936305666022015|0.08929707483152247|-1.2177968606365622e-05|0.010310078056909838|-3.653479549545554e-05|0.01493824366569541|0.023860147668319665|0.005201269917939069|0.016463117986640567|0.005609985761923528|-0.0006175825551903385|0.01255621420322109|-1.2177968606360828e-05|0.009213861580792576|0.004753045184532865|-0.00048379339106168975|-3.095285585425362e-05|0.003992599439289952|-0.00010961239435774071|0.017338814304785784|0.017337712609366385|0.012828735323430634|-4.871365380076245e-05|-0.00013810181593460058|-4.262418757519043e-05|-0.0015033896065025651|-3.6534795495619084e-05|0.005273457358457412|0.0019201333474520527|0.022095474967107644|-5.480319417298677e-05|-0.0001460829138105331|-3.0445477560499098e-05| +|HC04BTC_visiumHD|-0.00019706413697756538|0.007422547308693787|0.014894294649189725|-0.00029689822005706017|0.0033698697260259917|-0.00023264213989383152|0.007711957923039916|-0.0003361833163197903|-0.00023273945870697224|-7.498465859039722e-05|-0.00014079678685765152|-0.00018623395856770715|0.16625977889602583|0.04299490777856785|0.04783970523453048|0.005167770117409016|0.02423832139822755|0.004021474960933612|0.005490129415202627|0.005303723809074337|-0.00048207543064397946|0.025365289579350383|0.006070763895948532|0.010016256869235357|0.00421879770949575|0.010482818865110493|-0.00021120936480072835|-8.569767065405092e-05|0.004763862704287038|0.011328278347432321|0.015183842952228863|0.19674080141094413|0.012268734171121839|0.005077241961841349|-0.00012855201415112934|-0.0002454461650392867|0.008161972833531038|-0.0001469192856444592|0.010571842923004355|-0.0001750449401208991|0.018535817494331138|0.04592471333572872|0.0020437630129965466|0.0061502129378924785|-0.00013561626950584876|-0.00010476793987987248|-0.00010355325986833424|0.03072672246008593|-0.00010406336817389218|-0.00020865855783388007|-9.916578288220582e-05|0.10237316367732445|0.013717346069641211|0.00719462419837524|-0.00015388635820359197|-0.00033062911095645955|0.009746775806692452|-0.0004237822517160441|-0.00017265364459446192|-0.0001907991801085385|0.021618057408808088|-0.0006224623718894961|0.004247516930060053|0.006278686986895741|0.0039269383310032175|-6.296012251666835e-05|-0.0005110793233906171|0.0019103828190958577|0.41374375264098806|-0.00013373273326384888|0.028528748853325528|-9.181949488136947e-05|0.0955209923388124|0.041712716198882324|0.014205942729120857|0.02851635495698355|0.0935310261597968|0.007145289910542979|0.1452306645710484|-3.672577471688409e-05|0.0027034977884194747|0.017107552038169035|0.012331340483669148|-6.121112317516574e-05|0.010903589753400904|-0.0003339699142728076|0.03768617659303415|0.1639093217426787|0.005512960699638331|-0.00023162179212116968|0.005518442263826131|-9.356471722670468e-05|0.015591205530288444|-0.00016528723176153617|0.01894941643586347|0.013823434846362616|-0.00034427360670230767|-0.000247394126630847|0.022723452422718296|-0.0001234501140107452| +|HC08BTC_visiumHD|0.019105287021845503|-0.0003196264860007197|-0.00011762167596742856|-0.0001049332587076755|-8.103035895691919e-05|-8.671391844989132e-05|-0.0001507003901998879|0.017097285842083376|0.024914969777479517|-3.0311508453633822e-05|0.019856746634257624|-3.853768495428376e-05|0.042226801931546064|0.018192715923779874|0.03436902485170293|-0.0001404988346029833|0.010564708381980465|-0.0002736716861233617|-0.00024371153985549055|-0.00010551616826020213|0.01294905634686954|0.009469747802530432|-0.00018283129217784295|0.002992341832714111|0.008142637589394161|-0.0002293068385405619|-6.295731317883039e-05|-3.439243784701797e-05|-0.0007016706759148002|-0.00022865138887024172|-9.793702312852548e-05|0.06114665420439262|0.0038032936968420954|-0.0001851911965061508|-3.4975306632125246e-05|-8.671391844957473e-05|0.016053348607111686|-3.808434115698111e-05|0.004307140317144332|-5.341754155194499e-05|0.006376919287320663|0.019265750411550504|-0.00013072140136534733|0.008952175373920995|-2.0984890451513273e-05|-2.8562914327658374e-05|-3.4975306632091635e-05|0.00902722455621797|-3.808434115691442e-05|-7.195186207197667e-05|0.013282924582939495|0.050148786850506506|-3.7177653561775445e-05|-0.00031496894262716106|-2.7980049619089832e-05|-9.258702666078126e-05|0.012778062491962226|-0.0001282542397972112|-4.896611420259089e-05|-6.995305944932585e-05|0.013342434998500605|0.006473097941529821|0.007875714005607427|0.011346975726986952|-0.0002609835403257655|-2.0984890451546633e-05|0.037339900933628316|0.006396327811930685|0.12170444971536985|-2.518197421081695e-05|0.014882154640046361|-3.84734243234969e-05|0.024994788719893083|0.020255790206942535|0.007567582983310747|0.015072651152418631|0.008146211090197768|-0.0005859702234210913|0.04061583765494127|-2.098489045153579e-05|0.009740304800611993|0.007101656467160665|-0.0003827136321455018|-2.0984890451594036e-05|0.00484457222731847|-0.00011484416030333908|0.010444133217463712|0.04935010193172595|0.010992852806136683|-2.7980049619069825e-05|-0.00015332671008744822|-6.994865644018735e-06|0.005765654123966632|-5.596166476393084e-05|0.009593806695439069|0.0016189302192120966|0.017329733276108247|-0.0001259225526617635|-6.708268616332883e-05|-3.49753066320252e-05| +|HC07BTC_visiumHD|0.021651663313932003|-0.0003541745392515301|-5.696350348336226e-05|-7.291444742826244e-05|-0.00014434904421743058|-6.83569829504006e-05|-0.00010253897905373456|0.013367548418760887|0.031161703551684496|-3.645589463340294e-05|0.016540027757157336|-4.509559316373242e-05|0.017031595378758334|0.019526278141119|0.034663893194174095|-0.00015429109650202559|0.009928504249780031|-0.0003598148964677926|-0.00012054243640691494|-0.00021282349551029513|0.013367548418742312|0.009368041847432937|-0.00018458530404764662|0.0025627854161375933|0.007719524564655106|-0.0002962811023921801|-0.00010206426155840715|-2.8480940554689598e-05|-0.0003770850288189525|-5.696350348333565e-05|-9.114472072687836e-05|0.0414177373237225|0.0027130511596295957|-0.00011488305190208925|-3.549188243694074e-05|-5.126686110103099e-05|0.015392650866702327|-2.603965350993909e-05|0.005545213110149122|-2.8480940554675876e-05|0.008456767752649677|0.023594084174860305|-0.0001766079872385652|0.008510723263596684|-3.987377103353972e-05|-3.645589463339643e-05|-1.708836965558446e-05|0.009506131031262594|-3.370263829939286e-05|-3.987377103334428e-05|0.018615126113878964|0.04381038431764022|-6.835698295042319e-05|-0.00022272446744102796|-4.557028361797177e-05|-0.00011963085336715142|0.013597459109322717|-0.0001139334970179813|-7.839431090647438e-05|-6.835698295036029e-05|0.014574131687544152|0.00719472379725033|0.007155091293392787|0.011678011464880551|-0.0005206000827536152|-2.8480940554702558e-05|0.02655799425218748|0.005138965720361473|0.16118530220672486|-2.848094055478102e-05|0.010951455377575423|-3.417732334585962e-05|0.021498151896590302|0.021412768513309848|0.00720176855398699|0.014719976735372979|0.0274178025626532|0.00033485154203814093|0.022978960132523273|-1.1392181545855027e-05|0.007623009669972109|0.009076095651177958|-0.00026275633405211323|-1.1392181545847757e-05|0.0019425735441146808|-0.0001424209279006879|0.007905607182942085|0.030038638434273606|0.01186344284845031|-2.848094055460155e-05|-7.839431090619864e-05|-2.563277349453046e-05|0.00267345185717725|-5.301966722915683e-05|0.008185509388212203|0.0028886847410878524|0.013040959194505252|-5.696350348325143e-05|-0.00012054243640659046|-3.607614573098558e-05| + +---------------------- + +Tool: Average Clustering +Section: +Title: average_clustering centrality score across samples + +Plot type: violin plot + +Number of samples: 6 + +Metrics: +PDAC - PDAC + +|Sample Name|PDAC| +|---|---| +|HC01BTC_visiumHD|0.0| +|HC03BTC_visiumHD|0.0| +|HC04BTC_visiumHD|0.0| +|HC05BTC_visiumHD|0.0| +|HC07BTC_visiumHD|0.0| +|HC08BTC_visiumHD|0.0| + +---------------------- + +Tool: Closeness Centrality +Section: +Title: closeness_centrality centrality score across samples + +Plot type: violin plot + +Number of samples: 6 + +Metrics: +PDAC - PDAC + +|Sample Name|PDAC| +|---|---| +|HC01BTC_visiumHD|0.10| +|HC03BTC_visiumHD|0.15| +|HC04BTC_visiumHD|0.06| +|HC05BTC_visiumHD|0.04| +|HC07BTC_visiumHD|0.02| +|HC08BTC_visiumHD|0.03| + +---------------------- + +Tool: Degree Centrality +Section: +Title: degree_centrality centrality score across samples + +Plot type: violin plot + +Number of samples: 6 + +Metrics: +PDAC - PDAC + +|Sample Name|PDAC| +|---|---| +|HC01BTC_visiumHD|0.18| +|HC03BTC_visiumHD|0.13| +|HC04BTC_visiumHD|0.07| +|HC05BTC_visiumHD|0.08| +|HC07BTC_visiumHD|0.02| +|HC08BTC_visiumHD|0.01| + +---------------------- + +Tool: Deseq2 Response +Section: +Title: DESeq2 results for response + +Plot type: scatter plot +X axis: log2 fold change +Y axis: -log10 adjusted p-value + +### ACINAR + +|Sample|X|Y| +|---|---|---| +|OLFM4|5.7823158033990785|3.063758167653291| + +### CAF + +|Sample|X|Y| +|---|---|---| +|IGFBP5|2.532368549922801|5.3703966199547795| +|RNASE1|-1.0990033652574343|3.0478664540965656| +|FXYD1|-1.6774465499156503|3.0478664540965656| +|CTSE|2.6046823743262504|2.9606934696969653| +|RFLNA|3.2457557998418722|2.9606934696969653| +|LMCD1|1.2977947254283684|2.7591324374321147| +|IGFBP3|2.217905517615093|2.7183518186750275| +|BMP8A|2.14893036068999|2.4720289814575067| +|ASPA|-2.10180762124256|2.449131304678608| +|CST2|2.60384967004427|2.449131304678608| +|C19orf33|2.3830196475794345|2.3872152383357275| +|MSLN|2.617925641926931|2.3872152383357275| +|PRRT2|-1.2828754310182704|2.3872152383357275| +|LAMB3|2.661909981699421|2.3872152383357275| +|NT5DC2|1.3488922280658808|2.3872152383357275| +|IGFL2|3.8086722457209894|2.1395961198899944| +|S100P|3.471460417400203|1.9583498120861436| +|NEGR1|-1.4295733207848937|1.8598451644481813| +|STRA6|2.4557067689750784|1.826683385099505| +|KRT7|1.7710113975449497|1.7792155023444651| +|AC007906.2|-2.5070324650666023|1.5244657418179957| +|SPAG4|1.732246197493873|1.5181185555980687| +|GATM|-2.883602279128695|1.4845061710260916| +|COL10A1|2.201212222058579|1.4842695741357648| +|CNTN4|-1.8317746740958372|1.3928991702278974| +|CD177|4.144114262248583|1.3928991702278974| +|LILRB5|-2.021562857301353|1.3928991702278974| +|LRRC32|1.0811883180749258|1.3928991702278974| +|DERL3|1.4750810500827438|1.3781106622127042| +|OGN|-1.654024851341906|1.3781106622127042| +|COL21A1|-1.4506413779805671|1.3425836402598912| +|SHISA2|2.462552716942487|1.3425836402598912| +|CXCL5|2.6120624729156274|1.3317716373790633| +|DPT|-1.7617809445748773|1.3059108923648788| + +### CYCLING MYELOID + +|Sample|X|Y| +|---|---|---| +|IGFBP5|2.3763033530768873|8.698481050320385| +|MMP11|3.9903109901126013|8.632447306752141| +|SLC11A1|3.25517132599394|4.039421650808853| +|COL10A1|3.2381018807030992|3.5438582303951103| +|IGFBP3|1.927748486060407|2.559821830007274| +|COL5A1|1.8461865872864616|2.559821830007274| +|POSTN|2.3414803849701036|2.559821830007274| +|FN1|1.9202863209691685|2.559821830007274| +|AEBP1|1.474861877894249|2.4867995967892687| +|COL4A1|1.6661100249447178|2.4867995967892687| +|LAMC2|3.3991351317592806|2.254787223632755| +|SULF1|1.9555153971698853|1.8371809797155922| +|BMP8A|2.967154261301106|1.7692067359371437| +|CTSE|2.7388098756494954|1.7692067359371437| +|ALOX5AP|1.5623687642524888|1.694223012997507| +|CLEC5A|2.9602835878034512|1.6117713668596598| +|PTGDS|-1.4017757458599809|1.6035382102589861| +|MMP14|1.4727376405012627|1.4713891558650656| +|ADAMTS2|2.283337117082079|1.3397932835895556| +|COL4A2|1.2579498034083203|1.3051540349956874| +|CHPF|1.9316083401714188|1.3038184837521791| + +### ENDOCRINE + +|Sample|X|Y| +|---|---|---| +|OLFM4|5.584935621315393|3.000729930854006| + +### ENDOTHELIAL + +|Sample|X|Y| +|---|---|---| +|IGFBP5|2.2281344644756307|10.582387900466788| +|COL10A1|3.7506007532700028|7.3486624039524155| +|CTSE|4.044331823822081|6.219313427093319| +|BMP8A|2.9383688625540243|4.199552553309853| +|SPP1|1.5556203817167524|4.199552553309853| +|PTP4A3|1.8736544235587476|4.199552553309853| +|ID1|1.9123012915857838|4.199552553309853| +|CXCL12|-1.4688963230675127|3.824117569602054| +|IGFBP3|2.021432058528985|3.4583990249190877| +|LAMB3|3.2979144352106484|3.2752594821941727| +|CLU|-1.6857807469341428|3.2752594821941727| +|DPT|-2.114261274899343|3.1879426283361685| +|LCN10|-2.635755795961248|2.9982827985483476| +|UNC5B|2.4182475026178434|2.992564519340121| +|LRATD1|2.880321203216227|2.9499570337311445| +|LIFR|-2.4210356140582947|2.633330841556226| +|INSR|2.1077830823676194|2.5989049993000806| +|CTHRC1|1.9575863073324538|2.5989049993000806| +|FCN3|2.650515697854958|2.5989049993000806| +|CXCL5|5.369901325409571|2.5989049993000806| +|SRPX2|1.8206241641132201|2.5893901743290333| +|C2CD4B|-3.185642295281793|2.554280624843103| +|KRT7|2.610978122894808|2.4724216161475576| +|AEBP1|1.327604221659729|2.4653815082158084| +|MMP11|4.061620703747917|2.432209898046663| +|NT5DC2|1.7109445098446803|2.3863109280768984| +|CD248|1.6159796680279395|2.333964742316936| +|TACSTD2|2.0377898695340813|2.2945310316498873| +|OLFML2A|1.8062609673086236|2.2945310316498873| +|PTGDS|-1.6809299661525854|2.2676343375193784| +|CD36|-1.3640659964583486|2.25870725367265| +|ADIRF|-1.9223173287296538|2.25870725367265| +|UBC|-2.0208192579991966|2.25870725367265| +|EFNB2|1.9987264490811667|2.1106810329418346| +|MMP14|1.6559740660185873|1.9758260139240003| +|LCN6|-2.649192002987194|1.87242840345239| +|S100P|2.824890130322954|1.840315912336768| +|TRIM29|3.0756312308017972|1.819029843949298| +|TCEA3|-2.107907166432504|1.7233758519208164| +|COL5A1|1.7374913065436797|1.7157504542969364| +|LAMC2|2.909270088852938|1.694168093901533| +|FXYD1|-1.668447893829053|1.6432053157885949| +|STRA6|2.6867247755383037|1.6416546404004106| +|KCNE3|3.0372210057933486|1.6058125810953616| +|CLDN18|5.699370278213735|1.6058125810953616| +|JUN|-1.710186727689548|1.5892910404953942| +|SYNE1|-1.4503808480469718|1.5576666687458907| +|IGFL2|5.803647790429473|1.5484061150140078| +|PCDH1|1.1909368858237963|1.533029975273858| +|ADAMTS5|2.526589170576275|1.5230420239118356| +|PLXDC1|1.5378251882258875|1.4765255156694987| +|MEOX2|-2.259003624203048|1.4183779193510118| +|PTCHD4|-3.5099483141743026|1.3873607977793292| +|S100B|-2.4128904284151167|1.3793590157784736| +|ITPR2|-1.0900537352755422|1.340205201760686| +|MFAP2|1.8279001225467086|1.307897443668635| +|DKK3|1.0399492502023402|1.307897443668635| +|CHST1|2.0970446471941466|1.307897443668635| + +### MYELOID + +|Sample|X|Y| +|---|---|---| +|PLA2G2D|-4.015421414357566|3.5950280728927844| +|IGFBP5|2.520611490792308|2.9392367873480105| +|MMP11|3.692870729400397|2.9392367873480105| +|CXCL5|4.293615154896593|2.9090236332480983| +|MMP1|4.321089774131857|2.0794736189628633| +|INAVA|3.566744755662055|2.0794736189628633| +|IGFL2|4.807697804726632|2.066005970564889| +|LAMB3|3.917118365534666|1.9302758381706098| +|CTSE|3.5013746011219387|1.8772436301392756| +|MUC6|-5.774615591618022|1.6915549033716806| +|ITGA2|2.3973808660248497|1.6915549033716806| +|DPT|-3.161907757582382|1.6915549033716806| +|OGN|-2.138727516202467|1.6749748291060838| +|SLC11A1|1.8844866081781204|1.6749748291060838| +|IGFBP3|2.1654245066914157|1.6545641078084927| +|KRT7|2.898149009360561|1.5087875059461524| +|MISP|3.56517717505739|1.3842273398188063| +|SPOCD1|3.7511605636933862|1.3265697175361124| +|LYVE1|-3.708010712439031|1.3177037055405836| +|LAMC2|3.4293574622847145|1.3113753032639381| +|EPS8L1|3.7405409119790463|1.3113753032639381| +|SULF1|2.1975136094325447|1.3067951139292897| +|C19orf33|3.002984438884998|1.3067951139292897| + +### PDAC + +|Sample|X|Y| +|---|---|---| +|RELN|-6.248246401436558|29.309363487138295| +|CERS4|-2.733253027433746|7.742456879389006| +|ADH1B|-3.99088539721612|7.380742931221326| +|CCL19|-5.549407113458123|7.367248386262784| +|ART3|-4.628353662577072|7.1338265652600175| +|ST6GALNAC1|3.338181923977133|6.775304908087187| +|FUT3|2.2698809814184835|6.120367579732312| +|TNXB|-2.831640979004529|6.101396189562754| +|CA4|-5.995775543145266|5.744733994345563| +|FXYD1|-2.6196433881967063|5.674115211514125| +|ATP6V0E2|-2.2717667329019586|5.65188302959472| +|CNR1|-4.34458905386252|5.65188302959472| +|TDRD1|-4.085691923394813|5.617039733624844| +|ASCL2|-4.13011956070653|5.287349772662018| +|GHRL|-3.785024450043621|5.287349772662018| +|PTPRS|-2.4763525335352083|5.223777225216855| +|TSPAN33|-2.0829238011096054|5.223777225216855| +|OGN|-2.9975364865454885|5.223777225216855| +|CDO1|-2.494324120940599|5.223777225216855| +|ASPA|-3.0124374961736495|5.097546158144537| +|IL1RL1|-3.409261917148393|5.028325655071541| +|C7|-3.5046169950325523|5.028325655071541| +|ETHE1|2.457346448344917|4.967511936650043| +|FGD3|-2.180803144080558|4.967511936650043| +|ITGA11|3.4431642197734287|4.875347443697687| +|DPYS|-4.104798793479607|4.870811875610915| +|PDK4|-2.6473713786103223|4.870811875610915| +|GIMAP7|-2.3570641228768427|4.812778770072743| +|GNG7|-3.6715944091229034|4.6841897748870185| +|ADA2|-2.5402764270990423|4.577396381235273| +|ANPEP|-2.870102469770123|4.577396381235273| +|SCN7A|-2.9853076578466853|4.556581174499201| +|MPP1|-2.157798364390482|4.475288379868162| +|ABCA8|-2.30590856399973|4.402286647089987| +|USP2|-2.491324044758677|4.332849965392637| +|COL14A1|-2.317612306883204|4.332849965392637| +|MDH1B|-2.835254311177277|4.332849965392637| +|PTGDS|-2.69590581127869|4.274637264696124| +|COL21A1|-3.5596296338607165|4.247686555532057| +|RGS13|-2.9234923312734025|4.2281370722383995| +|GPX3|-2.1235741847310226|4.104248546808734| +|GYPC|-2.135846134309035|3.930410007164666| +|SPINT2|1.523046801359111|3.749243001580381| +|ADHFE1|-2.482330740549866|3.676967799714114| +|OCIAD2|1.8239841531024232|3.676967799714114| +|GUCY1A2|-2.7125949004779235|3.6733983320078267| +|CLDN18|5.539457406330368|3.6733983320078267| +|SLC2A2|-4.187414510033499|3.6546482105139995| +|ANGPTL1|-3.2251202469103997|3.6157079263377163| +|NR1I2|3.216570419326869|3.6157079263377163| +|SERPINA6|-4.358714544592782|3.5836637115425427| +|NR6A1|-2.2019624880006847|3.5836637115425427| +|DACT2|-3.6768613679397326|3.5720263070001015| +|CHRDL1|-2.8715031914585123|3.5669705956269286| +|COL11A1|4.683926853317267|3.5669705956269286| +|CLU|-3.5591625953611943|3.5305153409229004| +|WDR72|-2.6199521621774684|3.520041097388085| +|TM4SF5|2.64858817658125|3.520041097388085| +|CALN1|-4.598085545003107|3.5181096224433466| +|FBLN5|-1.7058180541466945|3.5181096224433466| +|CD3D|-2.97656231019311|3.5173338604616533| +|MS4A1|-2.8900937075690707|3.4853268773049217| +|TTN|-3.355964099977802|3.485104352703702| +|KLRG1|-2.9913290484642117|3.4789731168193456| +|PCDH7|2.9051202204339948|3.4684018917766593| +|CPA3|-2.49081963779236|3.4248297256016325| +|CTSG|-3.8479400358424782|3.4248297256016325| +|DNASE1L3|-4.0718615801095|3.380811058706997| +|DKK1|4.28022988918121|3.343710810265758| +|HDC|-3.842315399973887|3.343710810265758| +|MMP11|3.808852648087456|3.3425210033077994| +|TMEM92|2.407236419393213|3.339701876366401| +|CRISP2|-4.028535775862388|3.336386077293249| +|LCNL1|-2.694957608939155|3.331767343746667| +|MBOAT4|-3.261126121413354|3.294545067970554| +|LTC4S|-2.64706842282308|3.273840303695802| +|ZNF66|-3.159527821141406|3.2653049192467734| +|ANGPT1|-3.2856297897442492|3.2653049192467734| +|FAM13C|-1.9698077368911282|3.243019572266416| +|PTPRR|2.4908396553923065|3.1954509503076576| +|LCN12|-2.032355052300355|3.1784116899753014| +|TRABD2B|-2.0334823260860677|3.1784116899753014| +|PAPLN|-2.4841344827970357|3.17003109850772| +|LDLRAD3|-1.9634826171183744|3.1648153309972415| +|SORCS1|-3.3033326655142083|3.1203785481800477| +|ZSCAN18|-1.6318724219692298|3.091162780545453| +|INAVA|2.1955182119643633|3.091162780545453| +|BCL11A|-3.2205691133398138|3.088541644292247| +|C6|-4.932720843408985|3.084957846569225| +|ZNF141|-1.8944487471415132|3.084957846569225| +|ZAP70|-3.4567326736907478|3.084957846569225| +|RTN4RL1|-3.1766204080148728|3.0742356974786924| +|SLC38A3|-4.289927919599607|3.0639106235428466| +|ZDHHC7|1.4238879389764354|3.033245752090204| +|NR1H4|-2.604247175280612|3.0243536420813966| +|CD8A|-3.599411784838742|3.016073144716917| +|TRAC|-3.020155463461551|3.003022019843852| +|SETBP1|-2.516029042143032|3.0015670230994553| +|SOX6|-2.2555481161331596|2.972610145920577| +|KCNJ5|-3.1695671767774196|2.963027517696933| + +### PERICYTES + +|Sample|X|Y| +|---|---|---| +|IGFBP3|2.5701456718595592|6.594818508434717| +|CTSE|4.483002636971405|6.080608101191777| +|MMP11|3.639633771088996|6.080608101191777| +|S100P|5.39700368399828|5.228697009997666| +|LAMC2|5.516644567265996|3.093114161976664| +|HIST1H1B|2.1888318341317685|2.5612366429301754| +|OGN|-3.0130491602078004|2.552301074777332| +|RAMP2|2.8176057978724587|2.380445126089816| +|EPS8L1|4.54243078858276|2.380445126089816| +|HSPB6|-2.1820971930036213|2.380445126089816| +|MMP9|3.261369461508835|2.380445126089816| +|FOS|-2.3192498531098047|2.133280936621898| +|IGFBP5|1.4898596064911347|2.041088548371927| +|CLU|-1.7852772835260924|2.041088548371927| +|WNK2|-2.7784547720991686|1.9057727046817752| +|OLFML2A|2.363090062870108|1.8917056537675294| +|P2RX1|-3.7692648128234465|1.8917056537675294| +|COL10A1|3.2895839754415497|1.8917056537675294| +|FAP|2.098146646798374|1.8917056537675294| +|EGFL6|3.951426118531778|1.8917056537675294| +|SERPINH1|1.5739328978586682|1.6968462074628685| +|TCEA3|-1.9921214460940813|1.6968462074628685| +|FXYD1|-2.0263198332036887|1.5300751658501408| +|ADIRF|-2.1747794607116706|1.5300751658501408| +|CKB|-2.3661974998598585|1.4262347675479574| +|SLC4A4|-3.0489023742642054|1.4262347675479574| +|CXCL14|3.0343450507564844|1.4091066237299703| +|COL5A1|1.7214082465711125|1.4091066237299703| +|TOB1|-1.7692936567104296|1.361082335090872| +|SLC11A1|2.1308563459417615|1.3422743926574305| +|ARPC1B|1.2824231183618413|1.328077687361728| +|BTG2|-1.8145878849359849|1.328077687361728| +|SPHK1|1.7240214477719467|1.328077687361728| +|LAMB3|3.2131310229114787|1.328077687361728| +|CTHRC1|1.7831311248613786|1.328077687361728| +|C15orf48|3.5257115267861887|1.328077687361728| +|CTRC|-13.897319927833426|1.328077687361728| +|FXYD3|2.9667167221901067|1.328077687361728| +|VCAN|1.4281390533426461|1.328077687361728| +|RHOB|-2.076050756890642|1.3198900493898291| +|DLL4|2.5576395048404863|1.3151459386697264| + +### iCAF + +|Sample|X|Y| +|---|---|---| +|IGFBP5|3.0708773280008383|2.3414136868469146| +|DPT|-2.2905756930699304|1.8427689141838643| + +### myCAF + +|Sample|X|Y| +|---|---|---| +|ASPA|-3.4345206039161247|10.992444244658305| +|LEPR|-2.0380550865490568|4.7522039790857| +|FXYD1|-2.4131172879433658|4.7522039790857| +|CDO1|-1.853222535104022|4.7522039790857| +|COL10A1|1.6976784043069055|4.538624346349931| +|RERG|-1.6755667155456488|4.42555692948581| +|TACSTD2|2.4268649357709755|4.211302814265617| +|SULF1|1.7956348221035052|4.098505458873962| +|FUT3|3.9355658340215838|4.087399485171446| +|LYVE1|-3.100122457478349|3.536775965309263| +|EVA1A|1.5346152398267554|3.2362090440627584| +|SCT|3.0042151669374557|3.1712471469113557| +|PRRT2|-1.7525628622916396|3.1302951743195395| +|NEGR1|-1.6627633994230373|3.1233831705588377| +|CTSE|3.3629030744357764|3.1233831705588377| +|AOX1|-2.186435839785802|3.1233831705588377| +|ARNTL2|1.3983308992115429|3.099529560050013| +|MMP11|3.141511058674591|3.099529560050013| +|MMD|1.4707812829530034|3.099529560050013| +|KLRC3|-2.431524022151066|3.0931737450940946| +|MMP1|4.059681693349765|3.0931737450940946| +|PTGDS|-2.35509422778933|3.036873785693285| +|TNNT3|-3.03401723991396|3.036873785693285| +|GFRA1|-2.457394539311135|2.93777248222841| +|AXL|0.8642657822362412|2.93777248222841| +|IGFL2|3.5066981102593098|2.93777248222841| +|FASLG|-2.46297194063881|2.9195716347583582| +|SIT1|-2.3334779421505614|2.9195716347583582| +|CBLN4|-3.482267759697255|2.9195716347583582| +|PLP1|-4.300921285049405|2.9195716347583582| +|SETBP1|-1.469842215243426|2.9012174309437646| +|HSPA4L|-1.9509322096710855|2.8960554812382124| +|ADD3|-0.7003551066903513|2.8960554812382124| +|PRSS22|3.00322192881803|2.8178429195798644| +|CLU|-1.8489372990004098|2.7831619374513745| +|GADD45G|-1.3761918396318429|2.7831619374513745| +|SPINT2|1.548879730175669|2.740349802386073| +|TSPAN11|-2.0838057101129412|2.688805656608786| +|KRT80|2.327261617803162|2.5815312683603864| +|DTX4|1.7492760861085825|2.5319590759031834| +|KCND3|-2.153601785693334|2.5086511399768336| +|CALB2|5.343808206261627|2.4354935051622655| +|F11R|1.9076763477397352|2.3973579958035502| +|SLC9A9|-1.8648489228676162|2.3757059999769523| +|SELENOP|-2.254155618472379|2.3757059999769523| +|CXCL5|3.8250159819652643|2.3757059999769523| +|JUP|2.1239141049643013|2.3605108749192243| +|BEX4|-0.9021478323300647|2.3582043861920905| +|GASK1A|-1.7195313473393343|2.3582043861920905| +|PLPP1|-0.888554778383221|2.341160571833556| +|TMSB10|1.1749530573234734|2.341160571833556| +|SHD|-5.133190484687222|2.341160571833556| +|DSG2|1.7431773810754743|2.341160571833556| +|C1orf116|3.211914359850313|2.341160571833556| +|AQP8|-6.129629899802924|2.3381758682354157| +|MUC17|3.6598549169048256|2.3381758682354157| +|LAMC2|3.0867082140091777|2.3238482686133555| +|IGFN1|-3.9563282050316206|2.2845928849097286| +|INS|-3.2626845423083286|2.2845928849097286| +|BMP8B|3.652203033745734|2.2834972309356565| +|NIBAN3|-2.4659289953532895|2.2733430360169296| +|LAD1|2.4782554770094722|2.233378154267469| +|KLRK1|-2.487352666912534|2.1416365389055017| +|VIT|-2.865021938447034|2.0986917116356203| +|KRT7|2.274624731719479|2.0986917116356203| +|ING5|-0.9326965678541096|2.0986917116356203| +|CHRDL1|-3.032011670511993|2.0986917116356203| +|FGF14|-2.2877668321561853|2.0986917116356203| +|METTL7A|-1.2609085048438757|2.0815102723636993| +|TSPAN15|1.4591844181684137|2.0777892140546497| +|CLDN4|2.625770010215371|2.0565051715171596| +|TBC1D16|0.9517613514791673|2.0357058415765357| +|ABI3BP|-2.156180571801698|2.0291155197962105| +|MGAT4B|1.06200439881648|2.0291155197962105| +|CD36|-1.6092599584218776|2.016735479586154| +|SLITRK6|-2.4419095977366925|2.016735479586154| +|CD55|1.7109721395165238|2.016735479586154| +|TIFAB|-2.644202819137535|1.99079372298863| +|NFIX|-0.7833865419027055|1.9814819695281938| +|SPOCD1|2.8806340939276276|1.9807956729373595| +|LTC4S|-1.9477013442411004|1.9500030440164784| +|PID1|-1.2737270371202658|1.9321702939764052| +|KANK4|4.9627664359779455|1.9235621988885692| +|SMIM22|2.5137673153896434|1.9235621988885692| +|COX6B2|3.464093147422644|1.9115560660131155| +|FCGR2B|-1.3563514507691712|1.9060216220032489| +|LIPH|3.6957217369191717|1.9060216220032489| +|FAM180A|-1.382038430429385|1.9060216220032489| +|OGN|-2.9349763912015976|1.8695466005300245| +|BMP8A|1.9695678581444362|1.8695466005300245| +|SPRY1|-0.9023851312691458|1.8608537665859062| +|CEACAM6|2.7312792834104105|1.8543558955799384| +|ATP2C2|2.9871301574311215|1.8542796340978756| +|JAM2|-1.072829577084566|1.8419467964356624| +|TEAD2|1.014447088716648|1.8350688080983222| +|MFAP4|-1.3290332628055104|1.8350688080983222| +|ITGAD|-3.456570207384647|1.8222896727415556| +|C19orf33|2.7638070789945033|1.8222896727415556| +|LAMB3|2.697108926343082|1.8222896727415556| +|ALDH1A1|-1.5751041549225557|1.815719477341744| + +### NA + +|Sample|X|Y| +|---|---|---| +|LAMB3|4.5796061659719705|20.730529958386203| +|CXCL5|4.816355799426711|15.870066471050277| +|ITGA2|2.275236374335114|13.914494299327874| +|MFAP4|-2.129740346848263|11.150043991012938| +|ADGRF1|4.311917636117123|10.64478101813796| +|LAMC2|4.687172573599736|10.19707275736736| +|ALDH1A1|-1.8969087249909498|9.983702185955766| +|C15orf48|3.8154915463869297|9.36446610454014| +|CLDN18|6.37452476890864|9.349961015186226| +|CDH3|2.9852621384085984|8.84076318679393| +|SLPI|3.1218401662436013|8.84076318679393| +|EPS8L1|4.321327757425343|8.569823141427092| +|CTSE|4.300595053925055|8.534840093658012| +|PTPRH|3.315768139592765|8.534840093658012| +|CEACAM6|3.920642386751421|8.461872410217754| +|MUC17|3.6027236906282227|8.321675498310551| +|PRRT2|-2.415712027292564|8.038834897729892| +|ITIH5|-2.099785109061577|7.931524827417779| +|REG4|4.317076421331523|7.8168427768112965| +|TACSTD2|3.0232709895078056|7.665641869145054| +|MMP11|4.215241271946524|7.616590609184572| +|IGFBP3|2.6475679430682644|7.527256712834085| +|OGN|-3.2687363663915896|7.510495070746773| +|TRIM29|4.103356649985229|7.510495070746773| +|COX6B2|3.406824997623382|6.6473880996217| +|FXYD3|3.9679725447413206|6.585739738474402| +|NEGR1|-1.8726635205113118|6.376193352010522| +|S100P|4.987654959822093|6.376193352010522| +|PSCA|3.5147230145467976|6.376193352010522| +|KRT17|4.5307329699572625|6.332734060311361| +|LEPR|-1.8629443655540734|6.0228878083950725| +|SCEL|4.906383362167821|5.901163325726194| +|TFF1|4.603403930195959|5.835367222169883| +|FUT3|4.456518260167687|5.792725701285986| +|MISP|2.9351083062651044|5.72524818804121| +|MYEOV|3.5314885254806883|5.678429162260365| +|MMP1|4.170404849862626|5.678429162260365| +|DSG2|2.8741125347326912|5.586421118510082| +|CST2|3.3504679945245006|5.5286259056935965| +|TSPAN1|4.376138330758627|5.418614782261198| +|DPT|-3.3392065793747685|5.209859190144128| +|MMP28|1.8374936734420035|5.119826801326761| +|PLS1|3.4533614323750172|5.055890357330188| +|SLIT3|-2.3084318636882077|5.054430909807954| +|FHL5|-3.381382471047907|5.038062561166182| +|PKP3|3.8839509809076276|4.9865787685741525| +|HSD17B6|2.9155365737823593|4.953069134354903| +|SELENOP|-2.4417191824780655|4.81510781720212| +|SLC6A14|5.320963519515254|4.785653618562347| +|IGFL2|4.073603296585453|4.785653618562347| +|CXCL12|-2.008743012262087|4.782110290578958| +|KRT7|3.066383476087767|4.780034979306317| +|SFN|4.268428522771893|4.687750058679393| +|PIK3C2G|3.2943735485311354|4.687750058679393| +|SLC26A9|4.007243097705238|4.673589085305595| +|CEACAM5|4.739759455552954|4.671014769326975| +|DSG3|5.1917529429879545|4.647292000161793| +|EPPK1|3.2301069429752123|4.637905991500384| +|NECTIN4|3.8271948796095088|4.637905991500384| +|ATP2C2|3.616753520842545|4.539333527490287| +|HSPB6|-2.3202789977319016|4.4847702327298915| +|CCDC102B|-1.907520321864629|4.484230641286441| +|LIFR|-2.2223755552655216|4.471063652849013| +|MACC1|2.967424419938717|4.411138262701094| +|C16orf89|-2.8243316206254883|4.411138262701094| +|FXYD1|-3.056826369331408|4.397688579023081| +|LILRB5|-3.478622731951745|4.39586390533619| +|CLU|-2.202542758482805|4.39586390533619| +|PID1|-2.0483800471588522|4.358737661969522| +|C4BPB|3.286246532968844|4.356452375958778| +|S100A14|3.7618849747059717|4.330747459091773| +|ABCA10|-1.659239374574969|4.320386101935216| +|ABCC9|-1.5369644685298907|4.320386101935216| +|SPOCD1|2.829674384434679|4.280709764620503| +|COL17A1|4.260452672681274|4.197923036645144| +|COL14A1|-2.418030059227171|4.182807173264262| +|CD55|1.7826494271058446|4.111498656599606| +|C19orf33|3.4174522841459454|4.094020726582275| +|IL1RN|2.8567419563147283|4.072557189400466| +|NMU|4.048773873508523|3.934318496015891| +|RAB3B|2.8724143592599103|3.934318496015891| +|MMP10|3.6239915456505827|3.926783444370103| +|INAVA|3.1247223715632026|3.913740040488799| +|ARHGAP40|4.862510646232235|3.8924573937355276| +|MUC1|2.70581729536829|3.890680448709002| +|JUP|2.125408209563236|3.890680448709002| +|ANXA3|2.9488257111685736|3.8873247353949045| +|KCNK1|2.042650034664113|3.883979993212787| +|TMPRSS3|2.875071266583272|3.86438284401938| +|SPINT2|1.964281275228285|3.838834587967488| +|OSR1|-2.813118921773773|3.794609486214741| +|GCNT3|2.687744309658352|3.794609486214741| +|MIA|3.0244490537639273|3.7493148747080913| +|CALB2|4.432380332780905|3.704661672593308| +|DKK1|2.7489672815701|3.702300596296096| +|SDR16C5|3.948852827751102|3.6758649788709783| +|CLDN4|3.027824345736727|3.6382734867380964| +|SERINC2|2.97790011499713|3.6345910132994694| +|KRT80|2.932524244595926|3.6297444115529727| +|KLK8|5.813221909168593|3.6017179702262876| + + +---------------------- + +Tool: Squidpy Ligrec Interactions +Section: +Title: Interaction scores + +Plot type: heatmap +X axis: Interaction +Y axis: Sample +Z axis: Score + +|Sample|SPP1-ITGB8-myCAF-PDAC|COL16A1-ITGA2-myCAF-PDAC|VCAN-LDLR-CAF-PDAC|LAMA3-ITGB1-PDAC-PDAC|COL16A1-ITGA2-CAF-PDAC|TNFSF13-TNFRSF1A-MYELOID-PDAC|VCAN-LDLR-iCAF-PDAC|SPP1-ITGA2-MYELOID-PDAC|HSPA8-LDLR-myCAF-PDAC|SPP1-TFRC-MYELOID-PDAC|VEGFB-ERBB2-PLASMA-PDAC|SPP1-ITGA6-MYELOID-PDAC|SPP1-ITGA3-MYELOID-PDAC|SPP1-ITGB8-MYELOID-PDAC|LAMB1-ITGA3-myCAF-PDAC|SPP1-ITGB4-MYELOID-PDAC|SPP1-ITGAV-MYELOID-PDAC|COMP-ITGA6-myCAF-PDAC|SPP1-ITGB6-MYELOID-PDAC|EZR-ITGB1-TNK-PDAC|SPP1-ITGB5-MYELOID-PDAC|LAMB1-ITGA3-ENDOCRINE-PDAC|LAMA5-ITGA3-PDAC-PDAC|SPP1-ITGB1-MYELOID-PDAC|COL10A1-ITGA2-myCAF-PDAC|HSPA8-LDLR-PDAC-PDAC|MUC1-LGALS3-PDAC-MAST|LAMA5-ITGAV-PDAC-PDAC|VCAN-ITGB1-CAF-PDAC|LAMA5-ITGA5-PDAC-B CELLS|ANXA2-SRC-MYELOID-PDAC|LAMA2-ITGA3-CAF-PDAC|LAMA2-ITGA3-ENDOCRINE-PDAC|CEACAM6-STAB1-PDAC-CYCLING MYELOID|LAMB1-ITGA3-CAF-PDAC|SPP1-ITGA3-myCAF-PDAC|HSPA8-LDLR-TNK-PDAC|LAMB1-ITGA3-PERICYTES-PDAC|COMP-ITGB4-myCAF-PDAC|SPP1-ITGB4-CYCLING MYELOID-PDAC|LAMC2-CD44-PDAC-MAST|TLN1-ITGA3-B CELLS-PDAC|MUC1-SYK-PDAC-B CELLS|S100A4-ERBB2-MYELOID-PDAC|LAMB3-ITGB6-PDAC-ACINAR|LAMC2-CD44-PDAC-CAF|LAMA5-ITGB8-ENDOCRINE-PDAC|LAMA5-ITGB5-PDAC-myCAF|SPP1-CD44-MYELOID-PDAC|LAMC2-ITGB4-PDAC-PDAC|HSPA8-LDLR-ENDOTHELIAL-PDAC|COL16A1-ITGA2-PLASMA-PDAC|APOE-LDLR-ENDOCRINE-PDAC|SPP1-ITGA2-myCAF-PDAC|LAMA3-ITGB5-PDAC-myCAF|SPP1-ITGB6-myCAF-PDAC|COMP-ITGA2-myCAF-PDAC|NID1-ITGA3-ENDOCRINE-PDAC|COMP-SDC1-myCAF-PDAC|THBS2-ITGAV-CAF-PDAC|VCAN-CD44-iCAF-PDAC|MUC1-LGALS3-PDAC-MYELOID|LAMC2-ITGA4-PDAC-TNK|COMP-ITGB4-CYCLING MYELOID-PDAC|THBS2-ITGA3-CAF-PDAC|CEACAM6-STAT3-PDAC-ACINAR|PDGFA-ERBB2-PERICYTES-PDAC|LAMC2-CD44-PDAC-CYCLING MYELOID|HSPA8-LDLR-PERICYTES-PDAC|MUC1-LGALS3-PDAC-myCAF|LAMB2-ITGA3-PDAC-PDAC|PTPN6-IFNGR2-B CELLS-PDAC|VCAN-LDLR-myCAF-PDAC|CEACAM6-CEACAM5-PDAC-PDAC|CEACAM5-CEACAM6-PDAC-PDAC|ANXA2-SRC-CYCLING MYELOID-PDAC|MUC1-ABL1-PDAC-ACINAR|HSPA8-LDLR-CYCLING MYELOID-PDAC|ITGB5-SRC-myCAF-PDAC|LAMA5-ITGA3-ENDOCRINE-PDAC|SPP1-ITGA6-CYCLING MYELOID-PDAC|VCAN-CD44-myCAF-PDAC|COL27A1-ITGA2-ENDOCRINE-PDAC|THBS2-ITGA2-CAF-PDAC|LAMA4-ITGB8-PERICYTES-PDAC|MUC1-ABL1-PDAC-B CELLS|MUC1-ABL1-PDAC-ENDOCRINE|LAMA3-ITGB1-PDAC-myCAF|MUC1-SRC-PDAC-PDAC|LAMB3-ITGB1-PDAC-CYCLING TNK|LAMB1-ITGB8-ENDOTHELIAL-PDAC|LAMB3-ITGA1-PDAC-ACINAR|LAMB3-ITGB8-PDAC-PDAC|LAMC2-ITGAV-PDAC-ACINAR|VCAN-ITGB1-myCAF-PDAC|LAMB1-ITGA3-ENDOTHELIAL-PDAC|LAMB1-ITGA2-myCAF-PDAC|LAMA3-ITGA2-PDAC-PDAC|LAMA3-ITGA1-PDAC-PERICYTES|LAMA5-ITGA3-ENDOTHELIAL-PDAC| +|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +|HC03BTC_visiumHD|0.12815858582914982|0.08607184810720811|0.22338499341981613|0.06910611962128146|0.10845760402278973|0.06339551943194432|0.18030506722132608|0.43229187127763224|0.06328403499799383|0.40204788223576027|0.06890448571519402|0.425038894743572|0.4385471411717537|0.4395852822112968|0.07725714781169125|0.4374903633261322|0.4139830314332178|0.16319127215300777|0.4424073230756922|0.06699391856963319|0.4297795675284033|0.06450374986916113|0.0711076456810339|0.4448840949887768|0.12371850269520815|0.07372486273636528|0.19909579215854342|0.04654353594249802|0.2346427787796361|0.03857221325342955|0.11581833310277771|0.0757540001614816|0.06105348654554121|0.17757765231864886|0.10139429754347581|0.12712044478960674|0.07822864313329642|0.07533058189318317|0.17564274073556796|0.4124448978349258|0.15601314483939455|0.06680442422498666|0.17845053495418373|0.062132671035885656|0.0982116888281552|0.12258892393313423|0.0653562010278153|0.07853458055653818|0.4617279444235569|0.1429523277483614|0.06274237102401173|0.056679744428546217|0.10007089093275402|0.12086517489548532|0.07019610067976265|0.1309806266935452|0.17044424868706803|0.05462952543694275|0.16876560297738294|0.14613229628847768|0.20840670201592618|0.189070218682888|0.12211228347097947|0.1034525851616656|0.17069640602701355|0.13041902861275687|0.04951227410627358|0.11965449619455794|0.05691652123163431|0.17036745841488174|0.06390807062966351|0.05472946887455796|0.22885149621870393|0.19879940497189993|0.19879940497189993|0.11534190902656397|0.1575266628293203|0.06007453780680308|0.06921833417866545|0.06431805998827221|0.3999934292523656|0.256953131013304|0.04585693140328915|0.16444113613289213|0.08128403197495032|0.15658331366459147|0.15268359867176345|0.06377785775086936|0.16152078550622503|0.18531500702863815|0.09743285179371676|0.09093030953290826|0.11075467633152489|0.11003066245510093|0.2401092815785239|0.09639471075417369|0.07100187791756983|0.056513895910136946|0.0647410740707541|0.0771042898766231| +|HC01BTC_visiumHD|0.12740783232113242|0.095250948464357|0.19658541769262855|0.07266453203942039|0.10174772184772875|0.06418330491542532|0.1909993724983836|0.4133389183924236|0.07547236543720888|0.3869459215218766|0.05777241214070329|0.4523441519067439|0.41463859208457543|0.38522650826051635|0.08322106881125604|0.4951547986589041|0.38694168622640135|0.1805693594711842|0.39147546126994914|0.07403384708701158|0.39502683619857665|0.07474540908246975|0.0800326190413363|0.422170592742778|0.13710052612581916|0.09224022520514989|0.25616833486258017|0.052335713183162255|0.2057658000642008|0.041294578130728475|0.10638629206121532|0.08646663126094371|0.06942165176465258|0.212578145743118|0.09785631648820306|0.1568199161451915|0.09503458612769007|0.08660900154405232|0.22338000622334442|0.2908528587493437|0.17688280315557536|0.0631585420983981|0.20629804382493527|0.06610504839171708|0.1260057596747918|0.09294701894115612|0.05873270511604705|0.0880168275087134|0.4109519087617901|0.16877531887004527|0.08941364665606927|0.07429722750079373|0.12980782331183582|0.15552024245303966|0.07311673984859489|0.13365678533056524|0.1415641259568639|0.07227414457376596|0.1344058129963747|0.13059067032057006|0.1889610708889679|0.2784341246171891|0.08736728953763394|0.16245180372545404|0.15828757617874412|0.189291377273789|0.0688776893104115|0.10292188091034192|0.08446112526157226|0.2449116568143091|0.07578316798762016|0.0557890008598938|0.3224962413702599|0.19892196623589642|0.19892196623589642|0.10359447611908221|0.21803302539429126|0.08025131631638582|0.08445688790440528|0.08814478894010613|0.24804221199718346|0.32045793976084425|0.049025082308745783|0.15698790248659228|0.09597464766011354|0.21931193899279613|0.22130791291059476|0.07734844728529505|0.22891286359738122|0.15333973141259472|0.0798146510714898|0.1265856154888934|0.11905685276685347|0.0773591289783461|0.33167662374183216|0.10922673489554888|0.0819213951191042|0.06383285768906596|0.07730582139979437|0.0916956443828329| +|HC05BTC_visiumHD|0.033618979671105415|0.04156954961556941|0.06614575031921868|0.02680313213462618|0.04055369391822223|0.039808867813293944|0.037330683216096006|0.04161047524905758|0.03354462189330276|0.0451631749739929|0.022639484865657096|0.052949313583264865|0.048099908162699045|0.06072542947435997|0.036635245762499116|0.051199633051299515|0.051510432494632476|0.07647324326607671|0.04757256949262077|0.04398413525335987|0.05069004183718012|0.025955417865329747|0.0242980096553833|0.05857359053544606|0.027149934088524043|0.03106446939539691|0.08970749220177893|0.02770853398731673|0.07128064014946249|0.02356729975405956|0.05936142724701195|0.04341895842518855|0.022077709611382165|0.0701298527880097|0.05087325638872242|0.02099345835944449|0.04182648384642|0.03127073313990554|0.07472356273411135|0.05810002969312906|0.10873878386671326|0.034673643847143984|0.08269597639959392|0.042610883044573514|0.006568992921254569|0.04227033959635254|0.04278997392224117|0.03326838310998394|0.13001558792991935|0.01997682948508192|0.030339698975154185|0.02157871201103342|0.034007921394828076|0.014504025445803023|0.025299823216479812|0.020466119689366218|0.06513440493186942|0.018092320543658814|0.07124174115630208|0.0333637238113655|0.11390757044081311|0.09623212102810365|0.04289988022105351|0.026398804214191385|0.029953199479432074|0.04507583706786365|0.028243494307686332|0.03792440527686673|0.02692088956798831|0.0863829503039788|0.028256152244165042|0.043426859551110614|0.0937944274921492|0.051239550992927894|0.051239550992927894|0.06385980883106174|0.07013151825725047|0.031539826361419346|0.038218860925262596|0.030164452610580244|0.05984971022509441|0.17037131471686628|0.023073757891244277|0.023463766565790606|0.04309404044159451|0.07011803212498754|0.07284258512560446|0.027667316601404632|0.08343258038208673|0.014480212733083187|0.05175620937656587|0.010551893023121466|0.03028374656395637|0.010205765074167417|0.09892931732239302|0.039130688064904946|0.03014581284885765|0.009840016848237702|0.037622756618449764|0.030830090038675304| +|HC04BTC_visiumHD|0.11058494901268513|0.0822123500419093|0.2215612242730075|0.08248132173883471|0.08909365686331515|0.06303433301977786|0.19628160346451537|0.3802831913203382|0.07336767742888843|0.3758341690009722|0.062383146554884174|0.3834567207904002|0.3930382771736035|0.39391914775015513|0.06911301593341218|0.412865702520135|0.37716681705763055|0.1883959171371013|0.36818570625666136|0.0651934499104344|0.3718068560876175|0.060877573559997775|0.06147912900657096|0.3783391577845067|0.09933609861270468|0.07410878926634472|0.2390626814898225|0.045607668890597974|0.19019128609018462|0.04541365148769402|0.09338380464771867|0.07266354647049378|0.05929815192052572|0.20355537312605398|0.08270785383935986|0.10970407843613351|0.08390578963912519|0.06556857731769894|0.21780489886683607|0.354688836516547|0.1798048288435714|0.05379375426737369|0.24439269547026038|0.07084050825763026|0.07648834629140071|0.09799517126569471|0.06417388290908528|0.06294060069920271|0.3876232353825265|0.11336457566159996|0.07543680387309273|0.05271444960268773|0.0957022931737733|0.0969489925828682|0.09864191282056325|0.08485150751919138|0.1852223876670393|0.05470677730087785|0.17052640835624522|0.12188345664639445|0.1741957428797123|0.2578929735688355|0.11007949551323011|0.12566896331014676|0.13775491676236745|0.19441736220365832|0.056365859517861105|0.09289580833953162|0.0734751308814437|0.2452747796266643|0.05328189927272181|0.06036618334988098|0.29454731069338835|0.23591267137352298|0.23591267137352298|0.08925461012485089|0.23715040909443336|0.07400951957524488|0.06150750156027576|0.06329301233253366|0.3252798547868122|0.2724614501085853|0.0469941225806535|0.12499983090910212|0.06979960409476733|0.2514130536455862|0.24148141972018963|0.09623451082299708|0.24855107734395943|0.1085651243489334|0.07495665704346818|0.07064810867210183|0.1015966294717757|0.06618719905467411|0.2631773725105655|0.07407578646691657|0.05635793008014686|0.08442535527466619|0.09405647799277106|0.06060632137180838| +|HC08BTC_visiumHD|0.029763565901232615|0.03896481781984671|0.08765616045756802|0.031099116169540572|0.03970226753625871|0.03368165572227772|0.07723029762112271|0.02434218618531162|0.029327557589777165|0.020545743435281896|0.02134970204832065|0.034435928480079335|0.02587400159139235|0.03596362199812656|0.03282141217799869|0.031361478779729134|0.01718596433368836|0.06575473044876162|0.024116004487649303|0.039159874693702426|0.01610042100503153|0.03162450514683405|0.027042861112169712|0.027076632417440693|0.03394156016743731|0.031221314505075606|0.06599430803211075|0.018354823854465723|0.09292622281542127|0.015909064631910188|0.050737975368998545|0.04739859105905969|0.035191368652297904|0.10910407159681348|0.045557118641769756|0.019673945494498402|0.04351972300625122|0.021085345593220697|0.06268028074841142|0.03221776523859765|0.08201317148218572|0.028703129384133632|0.06332821359881408|0.03080240019025588|0.0|0.04948574456203973|0.03513146042385651|0.026421892230690327|0.05716790734255808|0.049229378088809256|0.03025194362332706|0.022053056204535595|0.03756697240935392|0.018142130088417672|0.029275516462012846|0.017915948390755356|0.055660988153993896|0.026936662263272265|0.053203651917515665|0.050641942322428524|0.11259163490409335|0.06857895031332742|0.04994334871455869|0.025948566438213732|0.059329979580132516|0.0|0.022865735425985002|0.058369587699587375|0.025473482809910353|0.06672708598069538|0.02921500889449146|0.0332911902556909|0.1345166771816541|0.11230754446467207|0.11230754446467207|0.0475759939924643|0.0|0.0390608930465065|0.029306779462401896|0.0250418400171223|0.03529221493894785|0.16987801446462475|0.034297333140845196|0.057798164174051786|0.03546748128153847|0.05814646873447637|0.056336601978341505|0.03352068684971289|0.06166240387705452|0.043860107488032346|0.04406482282687811|0.0|0.06653371704065933|0.0|0.13978673953950735|0.033975202420143895|0.03128959677191796|0.028364669937411498|0.03544479303763634|0.03243950779861109| +|HC07BTC_visiumHD|0.03873944809088712|0.034506430168263845|0.0939203124827324|0.03106980326632647|0.036951968141745346|0.029802909620487476|0.06789927757971848|0.1318485354922637|0.025536315107881628|0.1317657791308797|0.029754355608314995|0.13767051671945127|0.1410889958893488|0.14157209227628295|0.027272239464818306|0.14181776005085878|0.13521806970764735|0.09562652612841346|0.13106389193705206|0.0436752476848558|0.13492438589358122|0.029139703761153796|0.02837704255006567|0.15032130875003177|0.02710091623126465|0.025755778162840352|0.046208314116145885|0.02250611636836419|0.10526898929909925|0.01632586217340011|0.05314181859751075|0.03884588489982044|0.0281331301042808|0.08773066925623915|0.03702911525387888|0.038256351703953|0.027712414406648296|0.0319155620464845|0.09977376945982097|0.11094774447926015|0.1014551310537309|0.02557287543128675|0.0423078398062527|0.02806812476655159|0.013254022129539493|0.03725640550029797|0.029683813846905414|0.03239330306099888|0.20574415754130593|0.02517734311932383|0.026518346712584934|0.02070241295824928|0.04792200161039344|0.029015891306867866|0.02585375091657674|0.028231247751656238|0.08980454490122589|0.02457273677103744|0.08620639549121527|0.0745468909781741|0.13467080318735947|0.070911490356874|0.03352401479673814|0.038499885350664065|0.08041781715987559|0.04263401509502501|0.023707716773112956|0.03216134828027998|0.028458299278374427|0.04153269337422541|0.027590173976702586|0.03482837813917617|0.12277156504013155|0.033622318120778254|0.033622318120778254|0.05001752183660855|0.03715237604462128|0.023308401091735056|0.03106836549714924|0.029200717459971298|0.10680050114785263|0.18954309064777256|0.02269459793826853|0.07117735676279045|0.042975372301712944|0.03311603307053861|0.03405611038061571|0.0236629558555674|0.0356150003129963|0.01661655464851524|0.04452510770016312|0.016036101404842115|0.02777891940350857|0.02132703444704539|0.1341202418564984|0.044042011313229004|0.018031779067733164|0.012597030008558396|0.031827026583971886|0.030940474032107042| + +---------------------- + +Tool: Spacemarkers Lrscores Interactions +Section: +Title: Interaction scores + +Plot type: heatmap +X axis: Interaction +Y axis: Sample +Z axis: Score + +|Sample|APP_CD74-MYELOID_to_PDAC*|COL6A3_ITGA9_ITGB1-PDAC_to_myCAF|COL1A1_ITGA10_ITGB1-PDAC_to_myCAF|COL6A3_ITGA10_ITGB1-PDAC_to_myCAF|COL1A2_ITGA9_ITGB1-PDAC_to_myCAF|COL1A2_ITGA10_ITGB1-PDAC_to_myCAF|TNFSF12_TNFRSF12A-PDAC_to_CYCLING.MYELOID|WNT10A_FZD8_LRP6-PERICYTES_to_PDAC|APP_CD74-iCAF_to_PDAC|WNT5A_FZD8-ENDOTHELIAL_to_PDAC|GABA-B-GAD2_SLC6A6_GABBR1-CYCLING.MYELOID_to_PDAC|APP_CD74-myCAF_to_PDAC|CXCL1_CXCR1-PERICYTES_to_PDAC|CXCL8_CXCR1-PERICYTES_to_PDAC|WNT5A_FZD8-CYCLING.MYELOID_to_PDAC|WNT5A_FZD8-MYELOID_to_PDAC|APP_CD74-CAF_to_PDAC|BMP8A_BMPR1B_BMPR2-CYCLING.MYELOID_to_PDAC|LTC4-LTC4S_CYSLTR1-CYCLING.MYELOID_to_PDAC|THBS4_CD47-PDAC_to_ENDOCRINE|BMP8A_BMPR1A_BMPR2-CYCLING.MYELOID_to_PDAC|EFNB1_EPHB4-myCAF_to_PDAC|BMP8A_BMPR1A_ACVR2B-CAF_to_PDAC|THBS4_CD36-CYCLING.MYELOID_to_PLASMA|COL1A1_ITGA9_ITGB1-PDAC_to_myCAF|EFNB1_EPHB1-myCAF_to_PDAC|BMP8A_ACVR1_BMPR2-CYCLING.MYELOID_to_PDAC|WNT5A_FZD8-TNK_to_PDAC|WNT10A_FZD8_LRP5-PERICYTES_to_PDAC|PECAM1_CD177-CYCLING.MYELOID_to_PLASMA|BMP8A_BMPR1A_ACVR2B-MYELOID_to_PDAC|COL6A3_ITGA11_ITGB1-PDAC_to_myCAF|BMP8A_BMPR1A_BMPR2-CAF_to_PDAC|COL1A1_ITGA11_ITGB1-PDAC_to_myCAF|LAMB3_ITGA1_ITGB1-CAF_to_PDAC|CXCL5_CXCR1-MYELOID_to_PDAC|WNT10A_FZD5_LRP6-PERICYTES_to_PDAC|LTC4-LTC4S_CYSLTR1-MYELOID_to_PDAC|VWF_GP1BA_GP1BB_GP5_GP9-PDAC_to_ENDOTHELIAL|PCDHB13_PCDHB13-PDAC_to_ENDOTHELIAL|WNT10A_FZD7_LRP6-PERICYTES_to_PDAC|LAMB2_SV2A-TNK_to_PDAC|COL6A3_ITGA1_ITGB1-PDAC_to_myCAF|TGFB1_TGFBR1_TGFBR2-myCAF_to_PDAC|PGE2-PTGES2_PTGER1-myCAF_to_PDAC|WNT5B_FZD8-CYCLING.MYELOID_to_PDAC|EFNA1_EPHA7-CAF_to_PDAC|LAMA3_DAG1-iCAF_to_PDAC|ITGAV_ITGB1_ADGRE5-myCAF_to_PDAC|BMP2_BMPR1A_ACVR2B-myCAF_to_PDAC|LAMB3_ITGA9_ITGB1-CAF_to_PDAC|LipoxinA4-ALOX5_FPR-RS7-CYCLING.MYELOID_to_PDAC|ITGAV_ITGB1_ADGRE5-PERICYTES_to_PDAC|WNT2B_FZD1_LRP5-PERICYTES_to_PDAC|APP_CD74-CYCLING.MYELOID_to_PDAC|CXCL5_ACKR1-ENDOTHELIAL_to_PDAC|WNT2B_FZD8_LRP5-PERICYTES_to_PDAC|LAMA2_ITGA7_ITGB1-PDAC_to_PLASMA|WNT10A_FZD5_LRP5-PERICYTES_to_PDAC|BMP8A_BMPR1A_ACVR2B-CYCLING.MYELOID_to_PDAC|APP_CD74-PERICYTES_to_PDAC|LAMB3_ITGA6_ITGB1-CAF_to_PDAC|DSC2_DSG3-CAF_to_PDAC|BMP8A_BMPR1A_ACVR2A-CAF_to_PDAC|COL1A2_ITGA11_ITGB1-PDAC_to_myCAF|INHBABB_ACVR1C_ACVR2A-PDAC_to_myCAF|WNT2B_FZD8_LRP6-PERICYTES_to_PDAC|BMP2_BMPR1A_ACVR2A-myCAF_to_PDAC|TGFB1_ACVR1_TGFBR1-myCAF_to_PDAC|SLITRK5_PTPRS-PDAC_to_ENDOCRINE|IGFBP3_TMEM219-myCAF_to_PDAC|LAMB3_ITGA2_ITGB1-CAF_to_PDAC|IGFBP3_TMEM219-TNK_to_PDAC|PECAM1_CD177-ENDOTHELIAL_to_PERICYTES|BMP8A_BMPR1A_ACVR2B-TNK_to_PDAC|LAMA3_ITGA6_ITGB1-iCAF_to_PDAC|NRXN2_LRRTM4-CAF_to_PDAC|THBS4_ITGAV_ITGB3-CYCLING.MYELOID_to_PLASMA|NECTIN3_NECTIN1-CYCLING.MYELOID_to_PDAC|BMP8A_ACVR1_ACVR2B-CAF_to_PDAC|CEACAM6_CEACAM6-CYCLING.MYELOID_to_PDAC|WNT7A_FZD7_LRP6-CAF_to_PDAC|Glutamate-Glu-SLC1A3_GLS_GRIN1_GRIN2D-PDAC_to_myCAF|LAMB3_ITGA3_ITGB1-CAF_to_PDAC|BMP8A_ACVR1_ACVR2B-CYCLING.MYELOID_to_PDAC|LAMB3_DAG1-CAF_to_PDAC|COL4A2_ADGRG6-myCAF_to_PDAC|WNT10A_FZD7_LRP5-PERICYTES_to_PDAC|WNT7A_FZD8_LRP5-CYCLING.MYELOID_to_PDAC|CXCL5_CXCR2-iCAF_to_PDAC|WNT2B_FZD1_LRP6-PERICYTES_to_PDAC|BMP8A_ACVR1_ACVR2B-MYELOID_to_PDAC|Glutamate-Glu-SLC1A1_GLS_GRIA1-ENDOCRINE_to_PDAC|LAMB3_ITGA6_ITGB4-CAF_to_PDAC|BMP8A_BMPR1A_ACVR2A-MYELOID_to_PDAC|BMP8A_ACVR1_BMPR2-CAF_to_PDAC|WNT5A_FZD5-ENDOTHELIAL_to_PDAC|ITGAV_ITGB3_ADGRE5-MYELOID_to_PDAC|COL4A2_ITGA11_ITGB1-ENDOCRINE_to_PDAC|LAMA3_ITGA6_ITGB4-iCAF_to_PDAC| +|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +|HC03BTC_visiumHD|0.0608052359618465|0.376257696171576|0.735731588081478|0.430897949488131|0.670943163602949|0.686930124704347|0.138361044270196|0.165768389767744|0.0724849075384881|0.274972383660542|0.205512348977244|0.0918558191254842|0.112706723465852|0.112706723465852|0.271781408068108|0.274872776206925|0.0537810403670591|0.232680735291127|0.179180341544518|0.0625577823664269|0.246405067400415|0.399465812501536|0.32672260057304|0.306763811012441|0.730556857206859|0.189617819812053|0.325423234524852|0.350002461694781|0.171535121393948|0.276411570923784|0.325710274630567|0.615260958345569|0.342931652230079|1.05052001873262|0.244607021214486|0.270407200019269|0.194463663965824|0.181108554340908|0.209514453152022|0.101782019010847|0.11973778022271|0.140222500215742|0.352467777075465|0.210163053890682|0.103599844187953|0.292720252051384|0.139428633992238|0.378982221307562|0.238490514127362|0.179259198334722|0.217868265962363|0.125551226207763|0.132810386596285|0.130325861874702|0.106993655157373|0.260608139203481|0.147332019461741|0.264434775836361|0.199573467937892|0.234758453738256|0.0750886731186079|0.405854985715811|0.283591385306575|0.316454877999742|0.980838473109696|0.260775091639138|0.142378956734539|0.174772345117452|0.202832476557983|0.263171950372894|0.5079057929143|0.271411265498837|0.453376562308259|0.239270312464137|0.339570188567083|0.458511259144608|0.0308515172074671|0.186544450356396|0.278416775714336|0.388522412409268|0.264433012089802|0.205543016789219|0.135268650564832|0.250673030974351|0.310785402803684|0.330672572431596|0.49484414737982|0.127090478071642|0.198491395322078|0.188923847676557|0.124858867053504|0.38770898087457|0.0823734052425829|0.380562218598851|0.315474365840317|0.406821617074107|0.363150573252868|0.108337630695425|0.251695687072345|0.434228031219982| +|HC01BTC_visiumHD|0.0323095160830059|0.399363046811862|0.67082351244181|0.363058472471679|0.748711292967821|0.602626517849915|0.140611527865649|0.160966000682344|0.0387857630732569|0.340053332576629|0.17742781812008|0.0324111965186696|0.120900055171005|0.120900055171005|0.302880079749735|0.304629484705839|0.0343058244928467|0.258959382818475|0.17010228784961|0.0905224895430721|0.317794024889129|0.376957799214208|0.338172263586684|0.221258884564993|0.855186874523154|0.244541094220874|0.382110742107521|0.253743646225798|0.179921884703945|0.297894592705626|0.295915547123144|0.521494301073614|0.300674058862384|0.963565555660949|0.228452201381218|0.319218575922688|0.215837285075714|0.144404247550204|0.228937061652496|0.145281609407703|0.155681864747522|0.158012330519666|0.434889888355861|0.247003855069124|0.159365519021484|0.194610584541761|0.209854270431725|0.271012816176151|0.19119983383913|0.127607197089751|0.244441533705898|0.175121376711335|0.206778168332488|0.117360988017161|0.0351661963613583|0.320760093198661|0.124863650099129|0.386929964853743|0.231188393414818|0.35742732564856|0.0304866086519307|0.461587828824243|0.193024976954369|0.319987162599171|0.865607935259195|0.261029722554037|0.111708491827596|0.122128848262636|0.2448372595684|0.243869226713782|0.488879171948976|0.323161048818626|0.617392095307104|0.184248794066369|0.321036493488485|0.313426338201374|0.0426838846256004|0.102607369924376|0.310026894273688|0.413558004437286|0.255194274918349|0.179098498711226|0.122139779039206|0.301407133596292|0.429317949407448|0.417748602922261|0.438427395300265|0.175087541338475|0.157752898365467|0.162481809746544|0.103482201945093|0.377906878776208|0.0924453282327674|0.434150317099968|0.280002787007529|0.368083738865672|0.548960928661245|0.137364652220216|0.279338692216556|0.285650239171378| +|HC05BTC_visiumHD|0.596322688855876|0.0883719914662843|0.176571932456532|0.0616055848019414|0.21434054639183|0.125854519075288|0.0373941459265828|0.0457602849295012|0.507494878603673|0.0504825189145874|0.0813785973583637|0.49526084922493|0.0212420285124071|0.0212420285124071|0.0709561468904263|0.0509693435664448|0.474022509748237|0.0588900856030123|0.0527315581766588|0.200493173240991|0.0634904106191236|0.1781888026773|0.0417289399200774|0.031124000858257|0.326203332483391|0.0343611104084141|0.126036535168174|0.0681792250937563|0.05147293473845|0.054980595832141|0.0914763351718603|0.151993079379143|0.0431451535135328|0.435637642792875|0.0763566968496396|0.0395170381593423|0.0874891917441598|0.0259006551484618|0.114553175988658|0.0236060325835502|0.0499262618825489|0.0689447505872877|0.0736833130819803|0.148633948278798|0.0099470209462406|0.0737805612933362|0.0457874568900446|0.120667976287897|0.0399109069701762|0.0189729411568194|0.0763397659170185|0.0660280574129332|0.0551966463129551|0.0484184013290776|0.396495742003952|0.0538212467776594|0.0268633689249712|0.13713198548938|0.091049486970494|0.0614063762548777|0.466184640929601|0.179670010278462|0.028389782973169|0.043520733084019|0.310507821158305|0.155530392360401|0.0238819764682026|0.0196168647871209|0.149895529419115|0.0791441623647721|0.365682643702489|0.0870226330327849|0.317000837648936|0.0534601595238126|0.106031747323822|0.15622510406814|0.141454031333321|0.0306522139861941|0.073659516202602|0.0944339942514176|0.099465865371505|0.0456045486709751|0.0633782336467373|0.0805161952394401|0.122585804358152|0.141081760047756|0.245321458964983|0.0552983299178243|0.0985945136758666|0.0637579666236862|0.0465881890157615|0.160451742699873|0.0301473864999432|0.163781540794646|0.0954042248411711|0.0970922652900829|0.224596534419513|0.0235422889356877|0.123092239157944|0.140798016827256| +|HC04BTC_visiumHD|0.0293138557913453|0.409896728202045|0.669078075787563|0.390335026075315|0.678241745766911|0.585840454285928|0.166379082798798|0.161502182292643|0.0317331995240564|0.357541166404333|0.190580668266944|0.0307151990168325|0.0924764949594423|0.0924764949594423|0.355478309066854|0.375752307228722|0.0298558174710456|0.215108854729676|0.148683725631667|0.100394124933466|0.270417440289704|0.334275735663225|0.287593683298039|0.300027495522499|0.799731969855697|0.174202352864861|0.355208413201753|0.366487644972934|0.16625610115825|0.203032509217386|0.279102343617903|0.523919806117305|0.306666178549978|0.898057392564927|0.22018095517618|0.239799471494474|0.192000252293861|0.170056159781481|0.209586964682684|0.129713044471536|0.163151309287957|0.122951167924031|0.441775824749473|0.230630810977271|0.128647103759385|0.234958570411765|0.204011943356488|0.35449155489152|0.171756347786|0.136410765014323|0.288807028072936|0.159382709637127|0.166517349107665|0.114590035575561|0.0305021345547178|0.18876066331612|0.16068336727335|0.311579375151155|0.196167965707289|0.253599363479429|0.0302030117912281|0.50167830008676|0.291429180361185|0.232218602940194|0.786333269425673|0.250495896980588|0.15608879488925|0.115110048392711|0.22361536293519|0.16431187175517|0.529711548652252|0.367208644513675|0.592651400055356|0.241397274401491|0.230747599141049|0.397745955841888|0.0504772819160821|0.174443668082756|0.202789601105682|0.364710887340896|0.275814987972622|0.139823811832885|0.155841952506978|0.346617959392832|0.3350056901076|0.459518634665539|0.437315175257222|0.167868745339933|0.217328984080501|0.120071128404757|0.108555799171924|0.357402955676181|0.0970741838360576|0.480063744215804|0.225362238728717|0.386705000527463|0.488534173819296|0.155321790114305|0.213316485712239|0.373483634936752| +|HC08BTC_visiumHD|0.56503845251578|0.135254866491441|0.228596714408201|0.106095897798243|0.305212931443486|0.204493138007896|0.0424348752030393|0.0748354300328921|0.633531893710494|0.0393375231080291|0.0556610241773143|0.573458642076959|0.0233376700230303|0.0233376700230303|0.0234480568635285|0.0480116133339704|0.651372018209959|0.0959311916476622|0.0164586282260977|0.180402367317648|0.0645737779200444|0.208995602850126|0.0780762598665141|0.0667867227938375|0.350445710972839|0.0441192857128383|0.163523468838166|0.0435605814223835|0.0971609932324534|0.061205823211983|0.0951399556783986|0.283375168094786|0.074646389739638|0.610566795848467|0.127911158406338|0.0990964981218529|0.113353802998498|0.0393973671218511|0.048625736995787|0.0474921314239927|0.0658189440301035|0.0502465147078404|0.19608189681526|0.167016575050683|0.0351457913499835|0.0275367815911855|0.0504517828927302|0.100407682371601|0.0835906904104278|0.019503571853477|0.0874604400032397|0.0267909616706309|0.0328185870060223|0.061024194036939|0.641760011892588|0.0425274217699204|0.0629346106315821|0.0563453095982735|0.130370949925266|0.0675408292756424|0.392360244468198|0.277669009863194|0.0975591096738923|0.127161426776983|0.546187727893259|0.0711148079471117|0.0484735539837394|0.0287264673492525|0.164762523125269|0.0574217693143803|0.422905231311759|0.163643390287878|0.242199161576594|0.117269626410188|0.102506895778766|0.140010217963192|0.140555775881107|0.0094013505670157|0.0382104573433428|0.195892908756706|0.183021545638594|0.0459430642595192|0.0875499321092481|0.147762268875071|0.177624179397809|0.215857887839615|0.230681135641741|0.0899399580872234|0.0522002865424208|0.0451540974926805|0.046115841841049|0.22386983654574|0.0602357088889257|0.26969471186796|0.154952767054264|0.180341933566112|0.0883414367878975|0.0212345397365227|0.0457779112195451|0.128616250999582| +|HC07BTC_visiumHD|0.635282417375154|0.115881681356535|0.235391319387687|0.0774191485806613|0.271121442310783|0.153633663563244|0.0342025696054785|0.0645730396562156|0.66363768313113|0.0296190139178466|0.074782477336806|0.662089959833916|0.029723918068586|0.029723918068586|0.0605079300731246|0.0559576961622495|0.679787230255771|0.0975003020991913|0.0525035072906118|0.20017544426375|0.105285553196255|0.207029389444436|0.12930287877006|0.0462569628710051|0.460245376555229|0.04359665316791|0.197324256248854|0.0698644790967081|0.0689044418768171|0.0484789806562643|0.162169935507025|0.176694517777467|0.149130374216527|0.537236025333898|0.12619788493967|0.0133489381342891|0.127941050273838|0.076148813023101|0.0950396417691098|0.0374895243343174|0.0609495968745714|0.0322191034456727|0.108357760623321|0.153757328008031|0.0244606515245634|0.0551022479386266|0.0521446322937919|0.150882364149899|0.0291078055720034|0.0566968225641851|0.132486034158738|0.0526910355961226|0.0227715718350067|0.0800075733805851|0.689424638911547|0.0487590129576572|0.0689909615327442|0.0627601293530607|0.130531300316619|0.0912874066915959|0.682843787230686|0.26160458427036|0.0275750834435674|0.10307994105701|0.350639687924362|0.140440776621321|0.0646541205999262|0.0473604752460642|0.158487784036613|0.037879459264278|0.414083983928062|0.154149202131379|0.260853077701736|0.0417123770900829|0.143035273067534|0.196945140884627|0.0934248850795594|0.0234514979850655|0.112179132192194|0.232049712500271|0.145469969969985|0.0867033402407957|0.0527900468033521|0.148375033957461|0.183428205732143|0.207235767787366|0.33825888589042|0.0654680629051685|0.0945254750117547|0.0163425522779562|0.0761657543662984|0.270403548959521|0.0446657181435276|0.249868332429081|0.129281478898937|0.249629203693684|0.149479283445641|0.0688585635267284|0.12535968516869|0.18381171842648| diff --git a/ingest/__init__.py b/ingest/__init__.py index 32359c5..6227914 100644 --- a/ingest/__init__.py +++ b/ingest/__init__.py @@ -1,15 +1,28 @@ """ingest: load, reduce, and annotate MultiQC JSON reports.""" -from .io_utils import DATA_DIR, resolve_path, load_json, save_json +from .io_utils import DATA_DIR, resolve_path, load_json, save_json, read_text from .extract import extract_report_saved_raw_data from .annotate import extract_focal_labels, annotate +from .text_ingest import ( + split_text_sections, + looks_like_multiqc_llms_full, + split_multiqc_llms_full, + parse_markdown_table, + extract_samplesheet_chunk, +) __all__ = [ "DATA_DIR", "resolve_path", "load_json", "save_json", + "read_text", "extract_report_saved_raw_data", "extract_focal_labels", "annotate", + "split_text_sections", + "looks_like_multiqc_llms_full", + "split_multiqc_llms_full", + "parse_markdown_table", + "extract_samplesheet_chunk", ] diff --git a/ingest/io_utils.py b/ingest/io_utils.py index 5bd35a8..d5fd23c 100644 --- a/ingest/io_utils.py +++ b/ingest/io_utils.py @@ -21,6 +21,11 @@ def load_json(filepath: str) -> dict: return json.load(f) +def read_text(filepath: str) -> str: + with open(filepath, "r", encoding="utf-8") as f: + return f.read() + + def save_json(data: dict, data_dir: str, filename: str, indent: int = 2) -> str: output_path = os.path.join(data_dir, os.path.basename(filename)) os.makedirs(data_dir, exist_ok=True) diff --git a/ingest/text_ingest.py b/ingest/text_ingest.py new file mode 100644 index 0000000..5fd022e --- /dev/null +++ b/ingest/text_ingest.py @@ -0,0 +1,183 @@ +"""Splitting of arbitrary text reports (e.g. MultiQC's llms-full.txt) into sections. + +Independent of the JSON extract/annotate pipeline: no descriptor overlay, no +MultiQC-specific merging. Sections are keyed by markdown heading text and each +holds a plain-text `data` string, so they can be iterated the same way as the +JSON pipeline's section dicts. +""" + +import html +import re + +_HEADER_RE = re.compile(r"^(#{1,6})\s+(.+?)\s*$") + + +def _add_unique(result: dict, name: str, body: str) -> None: + """Insert body under name into result, suffixing " (2)", " (3)", ... on collision.""" + unique_name = name + suffix = 2 + while unique_name in result: + unique_name = f"{name} ({suffix})" + suffix += 1 + result[unique_name] = {"data": body} + + +def split_text_sections(text: str) -> dict: + """Split text into {heading: {"data": body}} on markdown headings (# .. ######). + + Content before the first heading becomes a "Preamble" section (if non-blank). + If no headings are found at all, the whole text is returned as one + "Full report" section (or {} if the text is blank). + """ + sections = [] + current_name = None + current_lines = [] + preamble_lines = [] + + def flush(): + if current_name is None: + return + sections.append((current_name, "\n".join(current_lines).strip())) + + for line in text.splitlines(): + match = _HEADER_RE.match(line) + if match: + if current_name is None: + preamble_lines = current_lines + else: + flush() + current_name = match.group(2).strip() + current_lines = [] + else: + current_lines.append(line) + flush() + + if not sections: + stripped = text.strip() + return {"Full report": {"data": stripped}} if stripped else {} + + result = {} + preamble = "\n".join(preamble_lines).strip() + if preamble: + _add_unique(result, "Preamble", preamble) + for name, body in sections: + _add_unique(result, name, body) + return result + + +_SEPARATOR_RE = re.compile(r"(?m)^-{10,}\s*$") +_TOOL_BLOCK_RE = re.compile(r"(?m)^Tool:\s*(.+)$") +_SECTION_LINE_RE = re.compile(r"(?m)^Section:[ \t]*(.*)$") +_INDEX_ENTRY_RE = re.compile(r"^\d+\.\s+(.+?)\s*\nDescription:\s*(.*)", re.MULTILINE | re.DOTALL) + + +def looks_like_multiqc_llms_full(text: str) -> bool: + """Sniff for MultiQC's real llms-full.txt layout: 'Tool:'/'Section:'/'Title:' blocks.""" + return bool(re.search(r"(?m)^Tool: .+\nSection: .*\nTitle: .+$", text)) + + +def _clean_description(raw: str) -> str: + """Strip HTML tags/entities from a MultiQC tool Description value.""" + return html.unescape(re.sub(r"<[^>]+>", "", raw)).strip() + + +def split_multiqc_llms_full(text: str) -> tuple: + """Split MultiQC's real llms-full.txt layout into per-Tool chunks. + + Returns ({chunk_name: {"data": body}}, leading_instructions_text). The leading + instructions block is MultiQC's own prompt for its LLM-summary tool; the caller + decides whether to use it (only relevant for --as-is). Each numbered index entry's + Description is attached (best-effort, exact name match) to its matching Tool block. + """ + blocks = [b.strip() for b in _SEPARATOR_RE.split(text)] + blocks = [b for b in blocks if b] + if not blocks: + return {}, "" + + leading_instructions = blocks[0] + descriptions = {} + tool_blocks = [] + for block in blocks[1:]: + match = _INDEX_ENTRY_RE.search(block) + if match: + descriptions[match.group(1).strip()] = _clean_description(match.group(2)) + continue + if _TOOL_BLOCK_RE.match(block): + tool_blocks.append(block) + + result = {} + for block in tool_blocks: + tool_name = _TOOL_BLOCK_RE.match(block).group(1).strip() + section_match = _SECTION_LINE_RE.search(block) + section_name = section_match.group(1).strip() if section_match else "" + chunk_name = f"{tool_name} \u2014 {section_name}" if section_name else tool_name + + description = descriptions.get(tool_name) + body = f"Description: {description}\n\n{block}" if description else block + _add_unique(result, chunk_name, body) + + return result, leading_instructions + + +_TABLE_ROW_RE = re.compile(r"^\|(.+)\|$") +_TABLE_SEPARATOR_RE = re.compile(r"^\|[\s:|-]+\|$") +_SAMPLESHEET_ALIASES = {"sample sheet", "samplesheet", "sample_sheet"} + + +def _split_row_cells(line: str) -> list: + return [cell.strip() for cell in line.strip()[1:-1].split("|")] + + +def parse_markdown_table(text: str) -> dict: + """Parse the first markdown table found in text into {first_col_value: {col: val}}. + + Scans for a header row, a '|---|---|...' separator row, then consecutive data + rows; ignores any surrounding prose. Returns {} if no valid table (header + + separator + at least one data row) is found. + """ + lines = text.splitlines() + for i in range(len(lines) - 1): + header_match = _TABLE_ROW_RE.match(lines[i].strip()) + if not header_match: + continue + if not _TABLE_SEPARATOR_RE.match(lines[i + 1].strip()): + continue + header = _split_row_cells(lines[i]) + if len(header) < 2: + continue + + rows = {} + for line in lines[i + 2:]: + row_match = _TABLE_ROW_RE.match(line.strip()) + if not row_match: + break + cells = _split_row_cells(line) + row_id = cells[0] + if not row_id: + continue + rows[row_id] = { + col: cells[j] for j, col in enumerate(header[1:], start=1) if j < len(cells) + } + if rows: + return rows + return {} + + +def extract_samplesheet_chunk(sections: dict) -> tuple: + """Pop a sample-sheet-like chunk out of sections, returning (remaining, parsed_dict). + + Matches a chunk name against a small alias set (case-insensitive, ignoring any + " —
" suffix). If matched and its body parses into a table with at least + two rows, the chunk is removed and the parsed {sample_id: {col: val}} dict is + returned. Otherwise sections is returned unchanged with None (fail-soft). + """ + for name, obj in sections.items(): + bare_name = name.split(" \u2014 ", 1)[0].strip().lower() + if bare_name not in _SAMPLESHEET_ALIASES: + continue + parsed = parse_markdown_table(obj.get("data", "")) + if len(parsed) >= 2: + remaining = {k: v for k, v in sections.items() if k != name} + return remaining, parsed + return sections, None + diff --git a/interpret.py b/interpret.py index 1897e7b..b2d4f1e 100644 --- a/interpret.py +++ b/interpret.py @@ -11,6 +11,12 @@ import ollama from verify import deterministic_findings, extract_entities +from ingest import ( + split_text_sections, + looks_like_multiqc_llms_full, + split_multiqc_llms_full, + extract_samplesheet_chunk, +) SAMPLESHEET_KEY = "multiqc_samplesheet" @@ -62,16 +68,48 @@ analyse the data and give a concise summary. """ + STYLE_GUIDE + EVIDENCE_RULES +TEXT_SYSTEM_PROMPT = """You are an expert bioinformatician. You are given a plain-text +report produced by a bioinformatics tool or workflow (for example, a MultiQC +"llms-full.txt" export). It has no fixed schema, so infer the meaning of each +section from its heading and content. Your task is to analyse it and give a +concise summary. +""" + STYLE_GUIDE + EVIDENCE_RULES + +TEXT_AS_IS_SYSTEM_PROMPT = """You are an expert bioinformatician. You are given a +plain-text report produced by a bioinformatics tool or workflow. The text already +contains its own analysis instructions and formatting guidance — follow those +instructions exactly as written instead of any separate style guide.""" + def build_prompt(report: dict) -> str: report_str = json.dumps(report, indent=2) return ( - "Here is an annotated spatial transcriptomics report.\n" + "Here is an annotated bioinformatics report.\n" "Please analyze it and provide a structured biological interpretation.\n\n" f"```json\n{report_str}\n```" ) +def build_prompt_text(text: str) -> str: + return ( + "Here is a plain-text report.\n" + "Please analyze it and provide a structured interpretation.\n\n" + f"---\n{text.strip()}\n---" + ) + + +def build_text_section_prompt(section_name: str, body: str) -> str: + """Build the prompt analysing one heading-delimited chunk of a plain-text report.""" + return ( + f"Analyze the section `{section_name}` of the report in isolation.\n\n" + "Section text:\n" + f"---\n{body.strip()}\n---\n\n" + "Give a concise, analytical interpretation of THIS section only: the main " + "findings, notable or outlier values, and their likely meaning. Cite specific " + "numbers where present. Do not speculate about sections you were not shown." + ) + + def _split_descriptor(section_obj: dict) -> tuple: """Split a section into (descriptor, data).""" descriptor = {k: v for k, v in section_obj.items() if k != "data"} @@ -79,12 +117,8 @@ def _split_descriptor(section_obj: dict) -> tuple: return descriptor, data -def build_samplesheet_context(report: dict) -> str: - """Sample sheet block for the shared system prompt, naming the grouping columns.""" - samplesheet = report.get(SAMPLESHEET_KEY) - if not isinstance(samplesheet, dict): - return "" - groups = sample_groups(report) +def _samplesheet_context_block(samplesheet_data: dict, groups: dict) -> str: + """Build the shared SAMPLE SHEET context block from a plain {sample_id: {col: val}} dict.""" if groups: cols = "; ".join( f"{col} ({', '.join(sorted(set(mapping.values())))})" @@ -102,11 +136,20 @@ def build_samplesheet_context(report: dict) -> str: ) return ( "\n\n--- SAMPLE SHEET (shared context for every section below) ---\n" - f"```json\n{json.dumps(samplesheet, indent=2)}\n```\n" + f"```json\n{json.dumps(samplesheet_data, indent=2)}\n```\n" + grouping_note ) +def build_samplesheet_context(report: dict) -> str: + """Sample sheet block for the shared system prompt, naming the grouping columns.""" + samplesheet = report.get(SAMPLESHEET_KEY) + if not isinstance(samplesheet, dict): + return "" + groups = sample_groups(report) + return _samplesheet_context_block(samplesheet, groups) + + def _percentages(counts: dict) -> dict: """Return {key: percent-of-total} for a flat {key: count} mapping (1 d.p.).""" total = sum(v for v in counts.values() if isinstance(v, (int, float))) @@ -163,16 +206,25 @@ def _is_number(s) -> bool: def sample_groups(report: dict, max_group_values: int = 10) -> dict: - """Auto-detect categorical grouping columns from the sample sheet. + """Auto-detect categorical grouping columns from the report's sample sheet. + + Thin wrapper around `_sample_groups_from_data` that unwraps the report's + `multiqc_samplesheet` section into a plain {sample_id: {col: value}} dict. + """ + samplesheet = report.get(SAMPLESHEET_KEY, {}) + data = samplesheet.get("data", {}) if isinstance(samplesheet, dict) else {} + return _sample_groups_from_data(data, max_group_values=max_group_values) + + +def _sample_groups_from_data(samplesheet_data: dict, max_group_values: int = 10) -> dict: + """Auto-detect categorical grouping columns from a plain {sample_id: {col: value}} dict. Returns {column: {sample_id: value}} for each sample-sheet column usable to group samples — 2+ distinct categorical values, not all-identical, not near-unique, not continuous. Duplicate partitions (e.g. timepoint vs timepoint_date) are collapsed. Metadata-agnostic: works for response, timepoint, region, sample_type, etc. """ - samplesheet = report.get(SAMPLESHEET_KEY, {}) - data = samplesheet.get("data", {}) if isinstance(samplesheet, dict) else {} - samples = {sid: meta for sid, meta in data.items() if isinstance(meta, dict)} + samples = {sid: meta for sid, meta in samplesheet_data.items() if isinstance(meta, dict)} n = len(samples) if n < 2: return {} @@ -319,7 +371,7 @@ def iter_analysis_sections(report: dict): SYNTHESIS_SYSTEM_PROMPT = """You are an expert bioinformatician. You are given -the per-section analyses of a spatial transcriptomics report. +the per-section analyses of a bioinformatics report. Synthesize them into one concise executive summary of a few short paragraphs. Cover: @@ -343,6 +395,30 @@ def iter_analysis_sections(report: dict): heading. """ + STYLE_GUIDE + EVIDENCE_RULES +TEXT_SYNTHESIS_SYSTEM_PROMPT = """You are an expert bioinformatician. You are given +the per-section analyses of a plain-text report produced by a bioinformatics +tool or workflow. + +Synthesize them into one concise executive summary of a few short paragraphs. +Cover: +- the overall picture and whether the report appears internally consistent, +- any notable patterns, outliers, or data-quality issues surfaced across sections, +- the most important quantitative findings, citing key numbers and percentages, +- findings across sections that are consistent with each other, or contradict each other, +- any well-supported group differences only when the report itself provides the needed + grouping context; otherwise focus on overall patterns and limitations. + +Do not repeat each section verbatim or list sections one by one. Output only the +summary prose — do NOT add your own title or markdown heading. +""" + STYLE_GUIDE + EVIDENCE_RULES + +TEXT_AS_IS_SYNTHESIS_SYSTEM_PROMPT = """You are an expert bioinformatician. You are +given the per-section analyses of a plain-text report produced by a bioinformatics +tool or workflow. The report's text already carries its own analysis instructions +and formatting guidance, so preserve that guidance while writing one concise +executive summary across the sections. Output only the summary prose — do NOT add +your own title or markdown heading.""" + def synthesize_sections( responses: list, @@ -351,6 +427,7 @@ def synthesize_sections( samplesheet_context: str = "", think: bool = True, gen_options: dict = None, + system_prompt: str = SYNTHESIS_SYSTEM_PROMPT, ) -> tuple: """Final call condensing the section analyses into a summary; returns (content, thinking).""" combined = "\n\n".join(f"### {name}\n{text.strip()}" for name, text in responses) @@ -362,7 +439,7 @@ def synthesize_sections( return chat_ollama( prompt, model=model, - system=SYNTHESIS_SYSTEM_PROMPT + samplesheet_context, + system=system_prompt + samplesheet_context, num_ctx=num_ctx, think=think, gen_options=gen_options, @@ -378,9 +455,9 @@ def _strip_leading_heading(text: str) -> str: return stripped -def combine_responses(responses: list, summary: str = None) -> str: +def combine_responses(responses: list, summary: str = None, title: str = None) -> str: """Combine section responses into one document, summary up front if given.""" - parts = ["# MultiQC Spatial Transcriptomics Interpretation\n"] + parts = [f"# {title or 'Report Interpretation'}\n"] if summary: parts.append(f"\n## Overview\n\n{_strip_leading_heading(summary)}\n") parts.append("\n---\n\n## Per-section detail\n") @@ -389,12 +466,12 @@ def combine_responses(responses: list, summary: str = None) -> str: return "\n".join(parts) -REVIEW_SYSTEM_PROMPT = """You are an expert bioinformatician reviewing a spatial transcriptomics interpretation for factual grounding and internal consistency. Correct statements that are not supported by the underlying report data, remove claims about genes, proteins, or cell types that do not appear in the data, and resolve contradictions between sections. Preserve the document's headings, tables, and structure. Do not add new findings and do not soften the removal of unsupported claims. Output only the corrected document, with no preamble.""" + STYLE_GUIDE + EVIDENCE_RULES +REVIEW_SYSTEM_PROMPT = """You are an expert bioinformatician reviewing a bioinformatics report interpretation for factual grounding and internal consistency. Correct statements that are not supported by the underlying report data, remove claims about genes, proteins, or cell types that do not appear in the data, and resolve contradictions between sections. Preserve the document's headings, tables, and structure. Do not add new findings and do not soften the removal of unsupported claims. Output only the corrected document, with no preamble.""" + STYLE_GUIDE + EVIDENCE_RULES def build_review_prompt(text: str, findings: list) -> str: parts = [ - "Review the spatial transcriptomics interpretation below. Correct any statement " + "Review the bioinformatics report interpretation below. Correct any statement " "that is not supported by the report data, and resolve any internal contradiction " "between sections. Do not introduce new claims, and do not invent gene or protein " "functions." @@ -458,24 +535,20 @@ def build_chunks(report: dict, whole_report: bool, groups: dict) -> list: for name, obj in iter_analysis_sections(report)] -def interpret_report( - report: dict, +def _run_chunks( + chunks: list, model: str, - num_ctx: int = 32768, - whole_report: bool = False, - synthesize_final: bool = True, - think: bool = True, - gen_options: dict = None, - user_instruction: str = "", + system: str, + num_ctx: int, + synthesize_final: bool, + think: bool, + gen_options: dict, + samplesheet_context: str = "", + title: str = None, + synthesis_system_prompt: str = SYNTHESIS_SYSTEM_PROMPT, ) -> str: - """Interpret the report as one or more chunks; a single chunk is the whole report.""" - samplesheet_context = build_samplesheet_context(report) - system = (SYSTEM_PROMPT + samplesheet_context - + build_user_instruction(user_instruction)) - groups = sample_groups(report) - + """Run each (name, prompt) chunk through the model, then optionally synthesize.""" model = ensure_model(model) - chunks = build_chunks(report, whole_report, groups) total = len(chunks) print(f"[interpret] Analyzing {total} chunk(s) with model '{model}'" f"{' (thinking)' if think else ''}.", flush=True) @@ -499,12 +572,96 @@ def interpret_report( print("[interpret] Synthesizing executive summary from per-section analyses...", flush=True) started = time.monotonic() summary, summary_thinking = synthesize_sections( - responses, model, num_ctx, samplesheet_context, think=think, gen_options=gen_options + responses, + model, + num_ctx, + samplesheet_context, + think=think, + gen_options=gen_options, + system_prompt=synthesis_system_prompt, ) print(f"[interpret] Synthesis done in {time.monotonic() - started:.1f}s", flush=True) print_thinking("Overview (synthesis)", summary_thinking) - return combine_responses(responses, summary) + return combine_responses(responses, summary, title=title) + + +def interpret_report( + report: dict, + model: str, + num_ctx: int = 32768, + whole_report: bool = False, + synthesize_final: bool = True, + think: bool = True, + gen_options: dict = None, + user_instruction: str = "", +) -> str: + """Interpret the report as one or more chunks; a single chunk is the whole report.""" + samplesheet_context = build_samplesheet_context(report) + system = (SYSTEM_PROMPT + samplesheet_context + + build_user_instruction(user_instruction)) + groups = sample_groups(report) + chunks = build_chunks(report, whole_report, groups) + return _run_chunks( + chunks, model, system, num_ctx, synthesize_final, think, gen_options, + samplesheet_context=samplesheet_context, + ) + + +def interpret_text_report( + text: str, + model: str, + num_ctx: int = 32768, + whole_report: bool = False, + synthesize_final: bool = True, + think: bool = True, + gen_options: dict = None, + user_instruction: str = "", + as_is: bool = False, +) -> str: + """Interpret a plain-text report (e.g. MultiQC llms-full.txt), bypassing the JSON pipeline. + + `as_is` drops the style guide/evidence rules, assuming the text already carries its + own analysis instructions; it does not affect whole-report vs. heading-split chunking. + """ + base_prompt = TEXT_AS_IS_SYSTEM_PROMPT if as_is else TEXT_SYSTEM_PROMPT + synthesis_prompt = ( + TEXT_AS_IS_SYNTHESIS_SYSTEM_PROMPT if as_is else TEXT_SYNTHESIS_SYSTEM_PROMPT + ) + samplesheet_context = "" + if whole_report: + chunks = [("Whole report", build_prompt_text(text))] + else: + # MultiQC's real llms-full.txt has no markdown headings; special-case its + # `Tool:`/`Section:`/`Title:` block layout instead of falling back to one blob. + if looks_like_multiqc_llms_full(text): + sections, leading_instructions = split_multiqc_llms_full(text) + if as_is and leading_instructions: + base_prompt = leading_instructions + else: + sections = split_text_sections(text) + + # Keep sample metadata next to the analysis: pull out a Sample Sheet-like + # chunk (if present and parseable) and reuse it as shared context instead of + # analyzing it in isolation, mirroring the JSON pipeline's samplesheet handling. + sections, samplesheet_data = extract_samplesheet_chunk(sections) + if samplesheet_data: + groups = _sample_groups_from_data(samplesheet_data) + samplesheet_context = _samplesheet_context_block(samplesheet_data, groups) + + chunks = [ + (name, build_text_section_prompt(name, obj["data"])) + for name, obj in sections.items() + ] + if not chunks: + chunks = [("Whole report", build_prompt_text(text))] + system = base_prompt + samplesheet_context + build_user_instruction(user_instruction) + return _run_chunks( + chunks, model, system, num_ctx, synthesize_final, think, gen_options, + title="Report Interpretation", + synthesis_system_prompt=synthesis_prompt, + samplesheet_context=samplesheet_context, + ) def _available_models() -> list: diff --git a/llmize.py b/llmize.py index b7a29c0..b29dc4c 100644 --- a/llmize.py +++ b/llmize.py @@ -13,12 +13,14 @@ resolve_path, load_json, save_json, + read_text, extract_report_saved_raw_data, extract_focal_labels, annotate, ) from interpret import ( interpret_report, + interpret_text_report, build_gen_options, build_run_footer, review_interpretation, @@ -34,7 +36,9 @@ def parse_args() -> argparse.Namespace: parser.add_argument( "--input", "-i", required=True, - help="Path to the raw JSON input file (MultiQC report, or any JSON with section-keyed data).", + help="Path to the input file: JSON (MultiQC report, or any section-keyed JSON) " + "is auto-detected by a .json extension; anything else is treated as plain " + "text (e.g. MultiQC's llms-full.txt).", ) parser.add_argument( "--model", "-m", @@ -80,6 +84,13 @@ def parse_args() -> argparse.Namespace: action="store_true", help="Interpret the whole report in one call instead of section-by-section.", ) + parser.add_argument( + "--as-is", + action="store_true", + help="Plain-text input only: skip llmize's style guide and evidence rules, " + "assuming the text already carries its own analysis instructions " + "(e.g. MultiQC's llms-full.txt). Ignored for JSON input.", + ) parser.add_argument( "--no-synthesis", action="store_true", @@ -132,6 +143,10 @@ def resolve_input_path(path: str) -> str: return resolved +def is_json_input(path: str) -> bool: + return os.path.splitext(path)[1].lower() == ".json" + + def default_output_name(input_path: str, prefix: str) -> str: stem = os.path.splitext(os.path.basename(input_path))[0] return f"{prefix}{stem}" @@ -212,6 +227,53 @@ def run_pipeline( return output_path +def run_text_pipeline( + input_txt: str, + model: str, + output_path: str | None, + num_ctx: int, + whole_report: bool = False, + synthesize_final: bool = True, + think: bool = True, + gen_options: dict = None, + user_instruction: str = "", + as_is: bool = False, +) -> str: + input_path = resolve_input_path(input_txt) + print(f"[pipeline] Loading raw text: {input_path}") + text = read_text(input_path) + + mode = "whole report" if whole_report else "section-by-section" + print(f"[pipeline] Calling Ollama model '{model}' ({mode}, text mode" + f"{', as-is' if as_is else ''})...") + response = interpret_text_report( + text, + model=model, + num_ctx=num_ctx, + whole_report=whole_report, + synthesize_final=synthesize_final, + think=think, + gen_options=gen_options, + user_instruction=user_instruction, + as_is=as_is, + ) + + if output_path is None: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + stem = os.path.splitext(os.path.basename(input_path))[0] + output_path = f"{stem}_interpretation_{timestamp}.md" + + footer_mode = "text whole-report" if whole_report else "text section-by-section" + if as_is: + footer_mode += " (as-is)" + footer = build_run_footer( + model=model, num_ctx=num_ctx, think=think, gen_options=gen_options, + mode=footer_mode, user_instruction=user_instruction, input_path=input_path, + ) + save_text(response + footer, output_path) + return output_path + + def main() -> None: if "--check" in sys.argv: from check_env import main as check_main @@ -223,23 +285,43 @@ def main() -> None: temperature=args.temperature, top_p=args.top_p, top_k=args.top_k, seed=args.seed, num_predict=args.num_predict, ) - final_path = run_pipeline( - input_json=args.input, - descriptor_path=args.descriptor, - model=args.model, - extracted_filename=args.extracted_output, - annotated_filename=args.annotated_output, - output_path=args.output, - num_ctx=args.num_ctx, - whole_report=args.whole_report, - synthesize_final=not args.no_synthesis, - think=args.think, - gen_options=gen_options, - user_instruction=args.prompt, - review=args.review, - review_passes=args.review_passes, - save_intermediates=args.save_intermediates, - ) + + if is_json_input(args.input): + final_path = run_pipeline( + input_json=args.input, + descriptor_path=args.descriptor, + model=args.model, + extracted_filename=args.extracted_output, + annotated_filename=args.annotated_output, + output_path=args.output, + num_ctx=args.num_ctx, + whole_report=args.whole_report, + synthesize_final=not args.no_synthesis, + think=args.think, + gen_options=gen_options, + user_instruction=args.prompt, + review=args.review, + review_passes=args.review_passes, + save_intermediates=args.save_intermediates, + ) + else: + if args.review: + print("[pipeline] --review is not supported for plain-text input; ignoring.") + if args.save_intermediates: + print("[pipeline] --save-intermediates has no effect for plain-text input " + "(there is no JSON extraction step); ignoring.") + final_path = run_text_pipeline( + input_txt=args.input, + model=args.model, + output_path=args.output, + num_ctx=args.num_ctx, + whole_report=args.whole_report, + synthesize_final=not args.no_synthesis, + think=args.think, + gen_options=gen_options, + user_instruction=args.prompt, + as_is=args.as_is, + ) print(f"[pipeline] Completed. Final report: {final_path}") diff --git a/tests/test_ingest.py b/tests/test_ingest.py index c11d61c..de90033 100644 --- a/tests/test_ingest.py +++ b/tests/test_ingest.py @@ -14,7 +14,15 @@ if PROJECT_ROOT not in sys.path: sys.path.insert(0, PROJECT_ROOT) -from ingest import extract_report_saved_raw_data, annotate +from ingest import ( + extract_report_saved_raw_data, + annotate, + split_text_sections, + looks_like_multiqc_llms_full, + split_multiqc_llms_full, + parse_markdown_table, + extract_samplesheet_chunk, +) class TestExtractReportSavedRawData(unittest.TestCase): @@ -67,5 +75,157 @@ def test_annotate_unmatched_section_passthrough(self): self.assertEqual(annotated["some_section"], {"data": {"sample_1": {"metric_a": 1.2}}}) +class TestSplitTextSections(unittest.TestCase): + def test_splits_on_markdown_headings(self): + text = "## Section One\nbody one\n## Section Two\nbody two\n" + sections = split_text_sections(text) + self.assertEqual(sections["Section One"], {"data": "body one"}) + self.assertEqual(sections["Section Two"], {"data": "body two"}) + + def test_captures_preamble_before_first_heading(self): + text = "intro text\n## Section One\nbody one\n" + sections = split_text_sections(text) + self.assertEqual(sections["Preamble"], {"data": "intro text"}) + self.assertEqual(sections["Section One"], {"data": "body one"}) + + def test_duplicate_headings_get_suffixed(self): + text = "## Section\nfirst\n## Section\nsecond\n" + sections = split_text_sections(text) + self.assertEqual(sections["Section"], {"data": "first"}) + self.assertEqual(sections["Section (2)"], {"data": "second"}) + + def test_preamble_heading_collision_gets_suffixed(self): + text = "intro text\n## Preamble\nbody text\n" + sections = split_text_sections(text) + self.assertEqual(sections["Preamble"], {"data": "intro text"}) + self.assertEqual(sections["Preamble (2)"], {"data": "body text"}) + + def test_no_headings_falls_back_to_full_report(self): + text = "just plain text with no headings\n" + sections = split_text_sections(text) + self.assertEqual(sections, {"Full report": {"data": "just plain text with no headings"}}) + + def test_empty_input_returns_empty_dict(self): + self.assertEqual(split_text_sections(""), {}) + self.assertEqual(split_text_sections(" \n "), {}) + + +class TestMultiqcLlmsFull(unittest.TestCase): + SAMPLE = ( + "You are an expert bioinformatician. Summarize the findings.\n" + "----------------------\n\n" + "Tools used in the report:\n\n" + "1. Sample Sheet\n" + "Description:

The sample sheet provided as input.

\n\n" + "----------------------\n\n" + "2. Atlas Summary\n" + "Description:

Summary of cells and genes.

\n\n" + "----------------------\n\n" + "----------------------\n\n" + "Tool: Sample Sheet\n" + "Section: \n" + "Title: Sample Sheet\n\n" + "Plot type: violin plot\n\n" + "|sample|type|\n|---|---|\n|S1|visium|\n\n" + "----------------------\n\n" + "Tool: Unmatched Tool\n" + "Section: \n" + "Title: Unmatched Tool\n\n" + "Plot type: violin plot\n\n" + "|sample|value|\n|---|---|\n|S1|1|\n" + ) + + def test_looks_like_multiqc_llms_full_detects_layout(self): + self.assertTrue(looks_like_multiqc_llms_full(self.SAMPLE)) + self.assertFalse(looks_like_multiqc_llms_full("## Section One\nbody\n")) + + def test_splits_one_chunk_per_tool_block(self): + sections, leading = split_multiqc_llms_full(self.SAMPLE) + self.assertIn("Sample Sheet", sections) + self.assertIn("Unmatched Tool", sections) + self.assertEqual( + leading, "You are an expert bioinformatician. Summarize the findings." + ) + + def test_attaches_matching_index_description(self): + sections, _ = split_multiqc_llms_full(self.SAMPLE) + self.assertIn( + "Description: The sample sheet provided as input.", + sections["Sample Sheet"]["data"], + ) + + def test_unmatched_tool_has_no_description_prefix(self): + sections, _ = split_multiqc_llms_full(self.SAMPLE) + self.assertFalse(sections["Unmatched Tool"]["data"].startswith("Description:")) + self.assertTrue(sections["Unmatched Tool"]["data"].startswith("Tool: Unmatched Tool")) + + def test_non_blank_section_appended_to_chunk_name(self): + text = ( + "instructions\n----------------------\n\n" + "Tool: Foo\nSection: Bar\nTitle: Foo Bar\n\ndata here\n" + ) + sections, _ = split_multiqc_llms_full(text) + self.assertIn("Foo \u2014 Bar", sections) + + +class TestParseMarkdownTable(unittest.TestCase): + def test_parses_clean_table(self): + text = "|sample|response|\n|---|---|\n|S1|responder|\n|S2|non-responder|\n" + table = parse_markdown_table(text) + self.assertEqual(table, { + "S1": {"response": "responder"}, + "S2": {"response": "non-responder"}, + }) + + def test_ignores_surrounding_prose(self): + text = ( + "Tool: Sample Sheet\nTitle: Sample Sheet\n\n" + "|sample|type|\n|---|---|\n|S1|visium|\n\nsome trailing note\n" + ) + table = parse_markdown_table(text) + self.assertEqual(table, {"S1": {"type": "visium"}}) + + def test_no_table_returns_empty_dict(self): + self.assertEqual(parse_markdown_table("just plain text, no pipes here\n"), {}) + + def test_single_column_table_returns_empty_dict(self): + text = "|sample|\n|---|\n|S1|\n|S2|\n" + self.assertEqual(parse_markdown_table(text), {}) + + +class TestExtractSamplesheetChunk(unittest.TestCase): + def test_pops_matching_samplesheet_chunk(self): + sections = { + "Sample Sheet": {"data": "|sample|response|\n|---|---|\n|S1|r|\n|S2|nr|\n"}, + "Other Tool": {"data": "some data"}, + } + remaining, parsed = extract_samplesheet_chunk(sections) + self.assertNotIn("Sample Sheet", remaining) + self.assertIn("Other Tool", remaining) + self.assertEqual(parsed, {"S1": {"response": "r"}, "S2": {"response": "nr"}}) + + def test_no_match_leaves_sections_unchanged(self): + sections = {"Other Tool": {"data": "some data"}} + remaining, parsed = extract_samplesheet_chunk(sections) + self.assertEqual(remaining, sections) + self.assertIsNone(parsed) + + def test_match_with_unparseable_table_leaves_sections_unchanged(self): + sections = {"Sample Sheet": {"data": "no table here at all"}} + remaining, parsed = extract_samplesheet_chunk(sections) + self.assertEqual(remaining, sections) + self.assertIsNone(parsed) + + def test_section_qualified_name_still_matches(self): + sections = { + "Sample Sheet \u2014 Overview": { + "data": "|sample|type|\n|---|---|\n|S1|a|\n|S2|b|\n" + }, + } + remaining, parsed = extract_samplesheet_chunk(sections) + self.assertEqual(remaining, {}) + self.assertEqual(parsed, {"S1": {"type": "a"}, "S2": {"type": "b"}}) + + if __name__ == "__main__": unittest.main() diff --git a/tests/test_interpret.py b/tests/test_interpret.py new file mode 100644 index 0000000..edff431 --- /dev/null +++ b/tests/test_interpret.py @@ -0,0 +1,95 @@ +from __future__ import annotations + +import os +import sys +import unittest +from unittest.mock import patch + +PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if PROJECT_ROOT not in sys.path: + sys.path.insert(0, PROJECT_ROOT) + +import interpret + + +class TestInterpretTextReport(unittest.TestCase): + @patch("interpret.ensure_model", return_value="test-model") + @patch("interpret.chat_ollama") + def test_text_synthesis_uses_text_specific_prompt(self, chat_ollama, _ensure_model): + chat_ollama.side_effect = [ + ("section one", ""), + ("section two", ""), + ("summary", ""), + ] + + interpret.interpret_text_report( + "## Section One\nbody one\n## Section Two\nbody two\n", + "test-model", + synthesize_final=True, + think=False, + ) + + self.assertEqual(chat_ollama.call_count, 3) + self.assertEqual( + chat_ollama.call_args_list[-1].kwargs["system"], + interpret.TEXT_SYNTHESIS_SYSTEM_PROMPT, + ) + + @patch("interpret.ensure_model", return_value="test-model") + @patch("interpret.chat_ollama") + def test_text_synthesis_uses_as_is_prompt_without_style_rules(self, chat_ollama, _ensure_model): + chat_ollama.side_effect = [ + ("section one", ""), + ("section two", ""), + ("summary", ""), + ] + + interpret.interpret_text_report( + "## Section One\nbody one\n## Section Two\nbody two\n", + "test-model", + synthesize_final=True, + think=False, + as_is=True, + ) + + self.assertEqual(chat_ollama.call_count, 3) + self.assertEqual( + chat_ollama.call_args_list[-1].kwargs["system"], + interpret.TEXT_AS_IS_SYNTHESIS_SYSTEM_PROMPT, + ) + + +class TestInterpretTextReportSamplesheet(unittest.TestCase): + TEXT = ( + "## Sample Sheet\n" + "|sample|response|\n|---|---|\n|S1|responder|\n|S2|non-responder|\n" + "## Section One\nbody one\n" + "## Section Two\nbody two\n" + ) + + @patch("interpret.ensure_model", return_value="test-model") + @patch("interpret.chat_ollama") + def test_samplesheet_chunk_excluded_and_context_shared(self, chat_ollama, _ensure_model): + chat_ollama.side_effect = [ + ("section one", ""), + ("section two", ""), + ("summary", ""), + ] + + interpret.interpret_text_report( + self.TEXT, "test-model", synthesize_final=True, think=False, + ) + + # Sample Sheet is popped out: only 2 chunk analyses + 1 synthesis call. + self.assertEqual(chat_ollama.call_count, 3) + prompts = [call.args[0] for call in chat_ollama.call_args_list] + self.assertFalse(any("`Sample Sheet`" in p for p in prompts)) + + # Every call (including synthesis) carries the shared sample-sheet context. + for call in chat_ollama.call_args_list: + self.assertIn("SAMPLE SHEET (shared context", call.kwargs["system"]) + self.assertIn("response", call.kwargs["system"]) + + +if __name__ == "__main__": + unittest.main()