Process output files
All four guides use the same Python entry point, porefm.outputs.open_output.
It reads saved predictions without loading model weights or using a GPU.
Use a completed run directory and keep data/meta pairs and manifests together.
completed run directory
|
v
open_output
|
+--> iter_tables ------> preview/filter individual read observations
|
`--> summarize_loci ---> one row per chromosome, position, strand
|
`--> summary Parquet for plots/analysis
Preview a few observations
from porefm.outputs import open_output
calls = open_output("outputs/zero-shot")
print(calls.format)
print(calls.schema.names)
for table in calls.iter_tables(batch_size=5):
print(table.to_pandas())
break
Change the path to outputs/supervised, outputs/pairwise, or
outputs/residual to inspect another guide's result. Opening a pairwise run
selects read_scores/; its companion site tables are read separately.
Choose the right site summary
| Workflow | What the summary measures | Example |
|---|---|---|
| Supervised | Hard class counts/fractions, optionally mean class probabilities | Supervised summary |
| Zero-shot | Fraction of valid calls with a small canonical-null p-value | Zero-shot summary |
| Pairwise | Fraction of valid test calls that deviate from the control | Pairwise summary |
| Residual prototypes | Hard class counts, including within-family fractions | Residual summary |
The snippets in those guides write directly to a summary Parquet instead of
loading all read observations into RAM. Use a new summary filename for each
analysis. The default memory budget is 8 GB; memory_limit and
temp_directory can be set for larger runs.
To preview a saved summary:
import pyarrow.parquet as pq
summary = pq.ParquetFile("summaries/zero-shot.parquet")
for batch in summary.iter_batches(batch_size=5):
print(batch.to_pandas())
break
Coordinates and denominators
Reference position is zero-based. strand is 0 for forward, 1 for
reverse. read_pos follows the producer's oriented read sequence; it is not
a genomic coordinate. Site summaries exclude observations without valid
reference coordinates. They summarize retained predictions, so coverage can
be lower than BAM depth after input filtering, context checks, or edge trimming.
Filter by region when needed, but retain the calls that form the denominator:
sites = calls.summarize_loci(
filters=[("chrom", "==", "chr3"),
("position", ">=", 1_000_000),
("position", "<", 1_010_000)],
modified_pvalue_cutoff=0.01,
)
print(sites.head())
This small-region example applies to the zero-shot run opened above. For anomaly fractions, retain both significant and nonsignificant p-values. For class fractions, retain canonical and modified calls. Filtering to positive calls first makes the resulting fraction misleading.
The name fraction_modified in anomaly summaries means the fraction passing
the chosen p-value cutoff, not a calibrated biological modification fraction.
Likewise, effect scores and supervised probabilities have different meanings
and should not be compared as if they were the same quantity.
For selected columns, read filters, DuckDB queries, and detailed schemas, see the output API reference.