Spaces:
Sleeping
Sleeping
File size: 8,501 Bytes
0fff343 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | "use client";
import { useId, useState } from "react";
import { safeParseProgramRepr } from "../lib/programRepr";
interface Props {
text: string;
onTextChange: (next: string) => void;
onDraw: (repr: string, label: string) => void;
}
const PLACEHOLDER =
"Combine(Reduce(Select(M,[g05347,g00048]),mean),Reduce(Select(M,[g06271]),max),sub)";
export default function PasteToDraw({ text, onTextChange, onDraw }: Props) {
const [error, setError] = useState<string | null>(null);
const [showFormat, setShowFormat] = useState(false);
const panelId = useId();
function draw() {
const trimmed = text.trim();
if (!trimmed) {
setError("Paste a program first.");
return;
}
const { ok, error: pErr } = safeParseProgramRepr(trimmed);
if (!ok) {
setError(`Couldn't parse: ${pErr}`);
return;
}
setError(null);
onDraw(trimmed, "pasted program");
}
return (
<div>
<label
className="mb-1 block text-[11px] font-medium tracking-wide text-muted"
htmlFor={`${panelId}-textarea`}
>
Paste a program
</label>
<textarea
id={`${panelId}-textarea`}
value={text}
onChange={(e) => onTextChange(e.target.value)}
placeholder={PLACEHOLDER}
spellCheck={false}
rows={2}
className="w-full rounded-md border border-border bg-white px-3 py-2 font-mono text-xs text-ink focus:border-accent focus:outline-none"
/>
<p className="mt-1 text-[11px] text-muted">
Paste a program here, or click any tile to load it. Use Copy to grab
a program's text. Reveal / evaluate is only available for actual
run winners β pasted programs render as opaque IDs only.
</p>
<div className="mt-2 flex items-center justify-end gap-3">
<button
type="button"
onClick={draw}
className="rounded-md bg-accent px-3 py-1.5 text-xs font-medium text-card hover:bg-ink"
>
Draw
</button>
</div>
{error && (
<p className="mt-2 rounded-md border border-highlight/40 bg-highlight/10 px-3 py-2 text-xs text-highlight">
{error}
</p>
)}
<div className="mt-3 text-xs">
<button
type="button"
onClick={() => setShowFormat((v) => !v)}
aria-expanded={showFormat}
aria-controls={`${panelId}-format`}
className="inline-flex items-center gap-1.5 rounded text-ink hover:text-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent"
>
<span
aria-hidden
style={{
display: "inline-block",
transition: "transform 120ms ease",
transform: showFormat ? "rotate(90deg)" : "rotate(0deg)",
}}
>
βΈ
</span>
<span className="font-medium">Format & examples</span>
</button>
{showFormat && <FormatGuide id={`${panelId}-format`} />}
</div>
</div>
);
}
function FormatGuide({ id }: { id: string }) {
return (
<div
id={id}
className="mt-3 rounded-md border border-border bg-bg p-4 text-[12.5px] leading-relaxed text-ink"
>
<p className="mb-3">
<strong>What a program is:</strong> a recipe that turns gene
expression into ONE score per patient.
</p>
<p className="mb-2 font-semibold text-ink">The pieces:</p>
<ul className="mb-3 ml-5 list-disc space-y-1 marker:text-muted">
<li>
<code className="font-mono text-ink">M</code> β the expression
matrix (all patients Γ genes); the starting data.
</li>
<li>
<code className="font-mono text-ink">Select(M, [g#####, ...])</code>{" "}
β pick a group of genes β a smaller matrix.
</li>
<li>
<code className="font-mono text-ink">Search(M, k)</code> β bounded
nested search: keep the top-k columns by univariate target
association (gated; experimental).
</li>
<li>
<code className="font-mono text-ink">Reduce(<matrix>, <agg>)</code>{" "}
β collapse a matrix into one score per patient. {" "}
<code className="font-mono text-ink"><agg></code> ={" "}
<code className="font-mono text-ink">mean</code> |{" "}
<code className="font-mono text-ink">median</code> |{" "}
<code className="font-mono text-ink">max</code> |{" "}
<code className="font-mono text-ink">min</code> |{" "}
<code className="font-mono text-ink">var</code>.
</li>
<li>
<code className="font-mono text-ink">Combine(<scoreA>, <scoreB>, <op>)</code>{" "}
β merge two scores into one.{" "}
<code className="font-mono text-ink"><op></code> ={" "}
<code className="font-mono text-ink">add</code> |{" "}
<code className="font-mono text-ink">sub</code> |{" "}
<code className="font-mono text-ink">mul</code> |{" "}
<code className="font-mono text-ink">protected_div</code> (safe) |{" "}
<code className="font-mono text-ink">mean</code>.
</li>
<li>
<code className="font-mono text-ink">Split(<score>, <predicate>)</code>{" "}
β partition patients into two branches by a name-blind predicate
(<code className="font-mono text-ink">score</code> = above /
below median; <code className="font-mono text-ink">stage_late</code>{" "}
= AJCC stage III/IV), mean-centre per side, then recombine. One
level of Split only.
</li>
<li>
<code className="font-mono text-ink">FitApply(<score>, <target>)</code>{" "}
β train a small model on the score against the chosen label
(<code className="font-mono text-ink">msi</code> or{" "}
<code className="font-mono text-ink">tmb</code>) and emit
predicted probabilities / values back to the program.
</li>
<li>
<code className="font-mono text-ink">Associate(<score>, <target>, <kind>)</code>{" "}
β Scalar output: plain observational correlation against the
target. <code className="font-mono text-ink"><kind></code>{" "}
= <code className="font-mono text-ink">spearman</code> |{" "}
<code className="font-mono text-ink">pearson</code>.
</li>
<li>
<code className="font-mono text-ink">Effect(<score>, <target>, <kind>)</code>{" "}
β Scalar output: same correlation AFTER residualising on the
named clinical confounders (stage + age). Observational
backdoor adjustment β only as good as the measured confounders.
</li>
<li>
<strong>Output:</strong> one score per patient (or a Scalar for
Associate / Effect-rooted programs), used to satisfy the
objective.
</li>
</ul>
<p className="mb-2 font-semibold text-ink">Rules:</p>
<ul className="mb-3 ml-5 list-disc space-y-1 marker:text-muted">
<li>
Gene IDs are the opaque{" "}
<code className="font-mono text-ink">g#####</code> IDs shown on
the tiles and in the result.
</li>
<li>
List a <code className="font-mono text-ink">Select</code>'s
genes in <code className="font-mono text-ink">[square brackets]</code>
, comma-separated.
</li>
<li>Spaces are optional β paste with or without them.</li>
<li>
Every <code className="font-mono text-ink">Reduce</code> should
wrap a <code className="font-mono text-ink">Select(...)</code> so it
reduces a chosen group, not the whole matrix.
</li>
</ul>
<p className="mb-1 font-semibold text-ink">Annotated example:</p>
<p className="mb-1 font-mono text-[12px] text-ink">
Combine(
<br />
Reduce(Select(M,[g05347,g00048]), mean),
<br />
Reduce(Select(M,[g06271]), max),
<br />
sub
<br />)
</p>
<p className="text-muted">
β average g05347 & g00048 β scoreA; take the max of g06271 β
scoreB; final score = scoreA β scoreB.
</p>
</div>
);
}
|