Edit on GitHub

starplast.interpret

Read a run's findings back to the reader, in the order they are worth reading.

A search returns a table. A table of a hundred enriched clusters is not an answer -- it is the same problem the search was meant to solve, moved one step along, and the step that actually costs a scientist their afternoon is deciding which four rows matter and what they say. This module does that step: it ranks findings by how much they are worth acting on and writes each one as the claim it is, with the numbers that support it and the reason to doubt it.

What "worth acting on" means here

Four things, multiplied rather than added, because a finding that scores zero on any one of them is worth nothing whatever the others say.

  • Strength -- how far the evidence is past correction, and SATURATING. A claim at q = 0.04 and one at q = 1e-8 are not the same claim; a claim at q = 1e-8 and one at q = 1e-40 are, because both are certain and certainty does not come in degrees a biologist can act on. Without the saturation the term runs away with the ranking: measured, a q of 1e-40 about two genes outranked a q of 1e-8 about twenty-five, which is the opposite of useful. Past CERTAIN, only reach and novelty separate findings.
  • Reach -- how many genes it is ABOUT. A perfect enrichment that predicts two genes is a confirmation; one that predicts forty is a result.
  • Novelty -- how unstudied the implicated genes are. Predicting the localisation of a gene with two hundred papers is not a prediction. This is the term that turns a statistically strong, biologically boring cluster into the low rank it deserves.
  • Independence -- whether the layer was an input to the map. A circular finding is scored at zero and still shown, labelled, because a reader who sees only the clean ones cannot tell whether the run was clean.

What it does not do

It does not decide whether a claim is true, and it says so in every report it writes. Enrichment is evidence that genes group; whether they group for the reason the label suggests is a question for an experiment. The wording throughout is "predicted" and "consistent with", never "shows" -- a report that overstates once is a report nobody can use unread.

GOOD_REACH = 12.0
CERTAIN = 8.0
STUDIED = 8.0
def novelty(nodes: pandas.DataFrame, gene_ids) -> float:

How unstudied the implicated genes are, in [0, 1]. 1 is a set nobody has published on.

Measured on publications per gene rather than on whether the label is missing. A gene can be unlocalised and still be one of the twenty everybody works on, and predicting its compartment is a smaller contribution than predicting one for a gene with no literature at all.

def interest(findings: pandas.DataFrame, nodes: pandas.DataFrame) -> pandas.DataFrame:

Score and sort findings by how much they are worth reading. Adds the four terms as columns.

Kept as columns rather than folded into one number and thrown away: a reader who disagrees with the weighting -- and a reader who cares about one gene family will disagree -- can re-sort on the term they care about instead of arguing with a ranking they cannot see inside.

def sentence(row, nodes: pandas.DataFrame) -> str:

One finding, written as the claim it is.

def caveats(row, nodes: pandas.DataFrame, table: pandas.DataFrame = None) -> list:

Everything a reader should know before believing a finding. Never empty by design.

def report( findings: pandas.DataFrame, nodes: pandas.DataFrame, top: int = 8, alpha: float = 0.05) -> str:

The whole reading, as markdown: what the run found, in order, with the reasons to doubt it.

Written to be pasted into a lab notebook and understood a month later, which is why every claim carries its numbers inline rather than referring to a row of a table that will not be there.