annotuneagent 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- annotuneagent/__init__.py +68 -0
- annotuneagent/__main__.py +6 -0
- annotuneagent/_version.py +19 -0
- annotuneagent/annotate.py +93 -0
- annotuneagent/arbitrate.py +141 -0
- annotuneagent/cli.py +366 -0
- annotuneagent/decide.py +725 -0
- annotuneagent/evaluate.py +121 -0
- annotuneagent/evidence.py +606 -0
- annotuneagent/granularity/__init__.py +28 -0
- annotuneagent/granularity/cl_dag.py +179 -0
- annotuneagent/granularity/ontology_map.py +108 -0
- annotuneagent/granularity/relations.py +96 -0
- annotuneagent/hypotheses.py +38 -0
- annotuneagent/local_lora.py +706 -0
- annotuneagent/marker_plot.py +172 -0
- annotuneagent/markers.py +206 -0
- annotuneagent/markers.tsv +378 -0
- annotuneagent/mltools.py +128 -0
- annotuneagent/ontology/cl_isa.json +1 -0
- annotuneagent/ontology/granularity_policy.yaml +8 -0
- annotuneagent/ontology/ontology_map.jsonl +198 -0
- annotuneagent/prepare.py +1385 -0
- annotuneagent/py.typed +0 -0
- annotuneagent/read.py +76 -0
- annotuneagent/tokenizer/LICENSE +202 -0
- annotuneagent/tokenizer/merges.txt +247587 -0
- annotuneagent/tokenizer/tokenizer.json +495920 -0
- annotuneagent/tokenizer/tokenizer_config.json +305 -0
- annotuneagent/tokenizer/vocab.json +248046 -0
- annotuneagent/votes.py +83 -0
- annotuneagent/workflow.py +2037 -0
- annotuneagent-0.1.0.dist-info/METADATA +208 -0
- annotuneagent-0.1.0.dist-info/RECORD +38 -0
- annotuneagent-0.1.0.dist-info/WHEEL +5 -0
- annotuneagent-0.1.0.dist-info/entry_points.txt +2 -0
- annotuneagent-0.1.0.dist-info/licenses/LICENSE +201 -0
- annotuneagent-0.1.0.dist-info/top_level.txt +1 -0
annotuneagent/votes.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Copyright 2026 Zhao Li (李钊)
|
|
2
|
+
# Licensed under the Apache License, Version 2.0
|
|
3
|
+
|
|
4
|
+
"""Agree a cluster label from several views of the same evidence.
|
|
5
|
+
|
|
6
|
+
A view is one name or nothing. Three names that share a parent are enough to
|
|
7
|
+
write that parent. Three that name the same child write the child. One
|
|
8
|
+
dissenting view does not block. Two coarse names with no three-vote parent
|
|
9
|
+
stay unresolved.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from collections import Counter
|
|
15
|
+
|
|
16
|
+
from .evidence import NEGATIVE_MIN_LOGFC, NEGATIVE_PRESENT_PCT
|
|
17
|
+
from .hypotheses import Hypothesis
|
|
18
|
+
|
|
19
|
+
VIEWS = ("panel", "genes", "subcluster", "neighbors")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def panel_vote(record: dict) -> str | None:
|
|
23
|
+
"""Top dictionary type when its positives are high and negatives are not."""
|
|
24
|
+
if record.get("status") == "label" and record.get("cell_type"):
|
|
25
|
+
return str(record["cell_type"])
|
|
26
|
+
ranking = list(record.get("ranking") or [])
|
|
27
|
+
if not ranking or float(ranking[0]["score"]) <= 0:
|
|
28
|
+
return None
|
|
29
|
+
for gene in (record.get("evidence") or {}).get("negative") or []:
|
|
30
|
+
if (
|
|
31
|
+
gene.get("pct_in", 0) >= NEGATIVE_PRESENT_PCT
|
|
32
|
+
and gene.get("logfc", 0) >= NEGATIVE_MIN_LOGFC
|
|
33
|
+
):
|
|
34
|
+
return None
|
|
35
|
+
return str(ranking[0]["name"])
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def gene_vote(hypotheses: list[Hypothesis], logfc: dict[str, float]) -> str | None:
|
|
39
|
+
"""Dictionary type whose measured positive markers are relatively high."""
|
|
40
|
+
scored: list[tuple[int, float, str]] = []
|
|
41
|
+
for item in hypotheses:
|
|
42
|
+
present = [gene for gene in item.positive if gene in logfc]
|
|
43
|
+
high = [gene for gene in present if logfc[gene] > 0]
|
|
44
|
+
if not high:
|
|
45
|
+
continue
|
|
46
|
+
if len(present) >= 2 and len(high) < 2:
|
|
47
|
+
continue
|
|
48
|
+
scored.append((len(high), sum(logfc[gene] for gene in high), item.name))
|
|
49
|
+
if not scored:
|
|
50
|
+
return None
|
|
51
|
+
scored.sort(key=lambda item: (-item[0], -item[1], item[2]))
|
|
52
|
+
if len(scored) > 1 and scored[0][:2] == scored[1][:2]:
|
|
53
|
+
return None
|
|
54
|
+
return scored[0][2]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def subcluster_vote(children: list[dict]) -> str | None:
|
|
58
|
+
"""Name shared by every labeled child. A split casts no single vote."""
|
|
59
|
+
if not children or any(row.get("status") != "label" for row in children):
|
|
60
|
+
return None
|
|
61
|
+
names = {str(row["cell_type"]) for row in children}
|
|
62
|
+
if len(names) == 1:
|
|
63
|
+
return next(iter(names))
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def agreed_name(votes: dict[str, str | None], parents: dict[str, str]) -> str | None:
|
|
68
|
+
"""Parent first, then a child when three votes name that child."""
|
|
69
|
+
named = [name for name in votes.values() if name]
|
|
70
|
+
if len(named) < 3:
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
def coarse(name: str) -> str:
|
|
74
|
+
return parents.get(name, name)
|
|
75
|
+
|
|
76
|
+
parent, parent_votes = Counter(coarse(name) for name in named).most_common(1)[0]
|
|
77
|
+
if parent_votes < 3:
|
|
78
|
+
return None
|
|
79
|
+
fine_counts = Counter(name for name in named if coarse(name) == parent)
|
|
80
|
+
fine, fine_votes = fine_counts.most_common(1)[0]
|
|
81
|
+
if fine_votes >= 3:
|
|
82
|
+
return fine
|
|
83
|
+
return parent
|