mkt.databases.conservation.KLIFSHierarchicalConservation
- class mkt.databases.conservation.KLIFSHierarchicalConservation(*, names: list[str] = <factory>, pockets: list[str] = <factory>, groups: list[str] = <factory>, position_labels: list[str] = <factory>, metric: str = 'blosum', blosum_name: str = 'BLOSUM62', linkage_method: str = 'average', conservation_threshold: float = 0.8, min_child_members: int = 2, weighting: str = 'none', exclude_pseudokinases: bool = False, gap_chars: str = '-X', distance_matrix: ~numpy.ndarray | None = None, linkage_matrix: ~numpy.ndarray | None = None, tree: ~scipy.cluster.hierarchy.ClusterNode | None = None)[source]
Bases:
BaseModelHierarchically cluster human kinases on KLIFS pocket similarity.
Builds a pairwise distance matrix over the gapless 85-column KLIFS pocket alignment (
_build_klifs_panel()), clusters it with SciPy linkage, then traces per-residue conservation up the tree. Per-node conservation reusesMSAConservationAnalyzer.column_conservation(), keeping the tree and the conservation read-out in the same coordinate system.Two metrics are provided behind a common interface: substitution-score similarity (Metric A,
"blosum", default) and percent identity (Metric B,"identity", baseline). Two views of conservation are emitted at each internal node: per-subtree (recompute on that node’s members) and survives-up (the residue must be conserved at the node and hold — same consensus residue — in both child clades).- __init__(**data: Any) None
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
Methods
_aggregate_singletons(splits, leaves)Fold singleton leaves by tree adjacency so no leaf row holds one sequence.
Find the shared-residue columns for the curated kinases of interest.
_annotation_lines(kinases, pos, label)"{kinase} {residue}{uniprot_idx}"per kinase at KLIFS columnpos.Substitution-score similarity distance (Metric A).
_conserved_columns(conservation)Filter per-column conservation to the conserved consensus residues.
Filter the panel to catalytically active kinases (in place).
Integer-encode the pocket panel against the substitution-matrix alphabet.
Family used for the color: Lipid or Manning group (pseudo folds in).
_family_name(value)Label for a family field, which may be a
Familyenum or a bare string._henikoff_weights(sequences)Henikoff (1994) position-based sequence weights for a set of members.
(kinhub_family, klifs_family)labels for leafi's dot hover.Percent-identity distance (Metric B):
1 - matches / non_gap_columns._is_kept(node_id)True if both children of
node_idare clades (>=2 members each).Kinome amino-acid background (20-vector) over all KLIFS panel pockets.
(fill, edge, linewidth)for leafi's name box / dot.Return the cached SciPy node list, indexed by node id.
_pseudocount_model([background])Cached pseudocount model for the requested KL background.
_uniprot_index_at(i, pos)1-based UniProt canonical index of leaf
iat KLIFS columnpos._walk_node(node, depth, records)Recursively score a node and its children, appending one record per node.
Walk the tree and compute per-node conservation, cached after first call.
build_display_tree([min_cluster_size, ...])Build the renderer-neutral display tree from the linkage.
Dispatch to the configured pairwise distance metric.
Cophenetic correlation between the tree and the input distances.
Most ancestral node at which each KLIFS column still survives-up.
from_conservation_data(data, **kwargs)Build a renderer from a persisted
KLIFSConservationDataartifact.gather_children(node_id)Singleton-contracted multifurcating children of
node_id.group_concordance([n_clusters])Adjusted Rand index between flat clusters and Manning groups.
members_consensus(member_idx)Per-column >=threshold consensus residue for a member set (else None).
node_conservation(member_idx)Per-column conservation for a set of member leaves.
node_information(member_idx[, background])Per-column background-relative information content (bits) and effective count.
Tabular summary of per-node conservation counts (internal nodes only).
plot_critical_depth([figsize, cmap])Plot per-KLIFS-column critical depth as an opaque track with a region bar.
residue_dot_layout([min_cluster_size])Row/leaf assignment for the per-amino-acid dot plot.
to_conservation_data([min_cluster_size, ...])Package the clustering output as a serializable
KLIFSConservationData.tree_children(node_id)The two linkage children of an internal node id.
tree_count(node_id)Number of leaves under
node_id(1 for a leaf).tree_members(node_id)Leaf indices under
node_id(the node itself if it is a leaf).Attributes
List of HGNC kinase names (one per pocket).
List of 85-character KLIFS pocket strings, column-aligned by construction.
Manning group per kinase, used for the external concordance check.
KLIFS region labels (e.g.
"g.l:4") for the 85 pocket columns."blosum"(default) or"identity".Substitution matrix name used by the
"blosum"metric."average"(UPGMA, default) or"complete".Minimum consensus-residue frequency for a column to count as conserved.
Minimum child-clade size considered in survives-up pruning; children smaller than this (e.g. singleton outliers peeled off at the root) are ignored.
"none"(default) or"henikoff".If True, drop predicted pseudokinases (
KinaseInfo.is_pseudokinase()) from the panel before clustering, so the tree and conservation reflect catalytically active kinases only (default: False).Characters treated as gap/unknown and excluded from scoring and conservation.
Square
N x Npairwise distance matrix; computed post-init unless supplied (e.g. injected byfrom_conservation_data()from a persisted artifact).SciPy linkage matrix; computed post-init unless supplied.
Root
ClusterNodeof the hierarchical tree, computed post-init._node_recordsCached per-node conservation records (see
analyze_nodes())._nodelist_cacheCached SciPy
to_tree(rd=True)node list, indexed by node id._kinome_bg_cacheCached kinome amino-acid background (from the KLIFS panel) for the IC measure.
_pssm_cacheCached substitution-aware pseudocount models, keyed by background choice.
- _aggregate_singletons(splits: list[KLIFSDisplaySplit], leaves: list[KLIFSDisplayLeaf]) None[source]
Fold singleton leaves by tree adjacency so no leaf row holds one sequence.
Mutates
splits/leavesin place: >=2 sibling singletons fold into one aggregated clade row, a lone sibling singleton folds into its nearest sibling clade, otherwise it bubbles up to the parent split (with a final safety net into the largest clade). Dead leaves are dropped and child references are remapped.
- _annotation_callouts(aa: str) list[tuple[list[str], int]][source]
Find the shared-residue columns for the curated kinases of interest.
Searches every KLIFS column and, at each, collects the kinases from
LIST_RESIDUE_DOT_KINASESthat carryaathere; a column is called out when at leastINT_RESIDUE_DOT_MIN_SHAREDof them share it. There is no pre-grouping, so any kinases sharing a homologous residue at one column are boxed together, and the callouts are discovered from the current alignment (surviving a shift in the KLIFS mapping) rather than hardcoded.- Parameters:
aa (str) – The plotted residue (e.g.
"C").- Returns:
(shared_kinases, column_index)per discovered callout.- Return type:
list[tuple[list[str], int]]
- _annotation_lines(kinases: list[str], pos: int, label: str) list[str][source]
"{kinase} {residue}{uniprot_idx}"per kinase at KLIFS columnpos.Used for the curated conserved-residue callout boxes; the residue letter and UniProt index are read from each kinase’s pocket and
KLIFS2UniProtIdxinDICT_KINASE, so the numbers stay correct without hardcoding.
- _blosum_distance_matrix() ndarray[source]
Substitution-score similarity distance (Metric A).
Sums the substitution score over jointly non-gap columns, normalizes by each sequence’s self-score (cosine-like in score space), and converts to a distance
1 - sim.Returns:
- np.ndarray
Square
N x Ndistance matrix with a zero diagonal.
- _conserved_columns(conservation: list[tuple[str | None, float]]) dict[int, str][source]
Filter per-column conservation to the conserved consensus residues.
Parameters:
- conservationlist[tuple[str | None, float]]
Per-column
(consensus_aa, fraction)tuples.
Returns:
- dict[int, str]
Mapping of column index to consensus residue for columns whose consensus frequency meets
conservation_threshold.
- _drop_pseudokinases() None[source]
Filter the panel to catalytically active kinases (in place).
Drops any panel member for which
KinaseInfo.is_pseudokinase()is True, keepingnames,pocketsandgroupsin sync. Names absent fromDICT_KINASE(e.g. a custom panel) cannot be assessed and are kept.Returns:
None
- _encode_pockets() tuple[ndarray, ndarray, ndarray][source]
Integer-encode the pocket panel against the substitution-matrix alphabet.
Returns:
- tuple[np.ndarray, np.ndarray, np.ndarray]
(enc, valid, mat_arr)whereencis theN x 85integer code (gaps mapped to 0 but masked out),validis theN x 85boolean non-gap mask, andmat_arris the dense substitution-score matrix.
- _family_label(i: int) str[source]
Family used for the color: Lipid or Manning group (pseudo folds in).
- static _family_name(value) str[source]
Label for a family field, which may be a
Familyenum or a bare string.
- _henikoff_weights(sequences: list[str]) list[float][source]
Henikoff (1994) position-based sequence weights for a set of members.
Each non-gap residue contributes
1 / (r_c * s_c)to its sequence’s weight, wherer_cis the number of distinct residues in columncands_cis the number of sequences sharing that residue. This down-weights redundant subfamilies and up-weights divergent sequences. Weights are scaled to mean 1; the per-column conservation fraction is scale-invariant regardless.Parameters:
- sequenceslist[str]
Aligned member sequences.
Returns:
- list[float]
One weight per sequence.
- _hover_families(i: int) tuple[str, str][source]
(kinhub_family, klifs_family)labels for leafi’s dot hover.
- _identity_distance_matrix() ndarray[source]
Percent-identity distance (Metric B):
1 - matches / non_gap_columns.Returns:
- np.ndarray
Square
N x Ndistance matrix with a zero diagonal.
- _is_kept(node_id: int) bool[source]
True if both children of
node_idare clades (>=2 members each).A node fails this when one child is a singleton leaf peeling off, which is exactly the pattern
gather_children()contracts away.
- _kinome_background() ndarray[source]
Kinome amino-acid background (20-vector) over all KLIFS panel pockets.
The preferred KL background for the information-content measure (“surprising relative to kinases”): a residue conserved because it is the kinome norm scores low, while a subfamily-specific residue scores high (plan Addendum A.1).
- _member_style(i: int) tuple[str, str, float][source]
(fill, edge, linewidth)for leafi’s name box / dot.Lipid kinases get no border, pseudokinases a black border, active kinases a thin white border; the fill is the Manning-group (or Lipid) color. Shared by the static tree figure and the interactive dot-plot explorer.
- _pseudocount_model(background: str = 'swissprot') SubstitutionPseudocounts[source]
Cached pseudocount model for the requested KL background.
"swissprot"(default) scores conservation relative to a random protein – the continuous companion to the >=80% rule (pan-invariants score high)."kinome"scores conservation relative to the KLIFS panel itself, so subfamily-defining residues pop while pan-invariants score near zero (plan-preferred for the subfamily-delta view).
- _uniprot_index_at(i: int, pos: int) int | None[source]
1-based UniProt canonical index of leaf
iat KLIFS columnpos.Read from
KinaseInfo.KLIFS2UniProtIdxkeyed by the column’s region label; None when the kinase, mapping, or that position is unavailable.
- _walk_node(node: ClusterNode, depth: int, records: list[dict]) dict[int, str][source]
Recursively score a node and its children, appending one record per node.
For each node the per-subtree conserved columns are recomputed on its members. At an internal node the survives-up set keeps only columns conserved at the node whose consensus residue is also conserved (identical) in every child clade with at least
min_child_membersmembers. Children below that size (e.g. a singleton outlier peeled off at the root by UPGMA) are ignored so they cannot veto an otherwise-robust residue.Parameters:
- nodeClusterNode
Current node in the tree.
- depthint
Depth of the node (root is 0).
- recordslist[dict]
Accumulator the per-node record dicts are appended to.
Returns:
- dict[int, str]
The node’s per-subtree conserved columns (column index -> consensus aa), so the parent can intersect against it for the survives-up set.
- analyze_nodes() list[dict][source]
Walk the tree and compute per-node conservation, cached after first call.
Returns:
- list[dict]
One record per node with keys
node_id,depth,n_members,is_leaf,conserved(per-subtree column->aa) andsurvives_up(the pruned, robust column->aa).
- blosum_name: str
Substitution matrix name used by the
"blosum"metric.
- build_display_tree(min_cluster_size: int = 12, aggregate_singletons: bool = True) KLIFSDisplayTree[source]
Build the renderer-neutral display tree from the linkage.
Starting at the root,
gather_children()contracts singleton-peel chains; each gathered child then either expands into its own split (>=min_cluster_sizemembers), collapses to a clade leaf, or is a peeled singleton leaf. Whenaggregate_singletonsis True (default) singleton leaves are folded by tree adjacency so no leaf row holds a single sequence (which would read as artificially 100% conserved): >=2 sibling singletons fold into one row, a lone sibling singleton folds into its nearest sibling clade, and otherwise it bubbles up to the parent split.- Parameters:
min_cluster_size (int, optional) – Members below which a gathered subtree collapses to a single leaf bar, by default
INT_TREE_MIN_CLUSTER_DISPLAY.aggregate_singletons (bool, optional) – Fold singleton leaves so every leaf row has >=2 members, by default True.
- Returns:
Splits, leaves, and the in-order leaf display order.
- Return type:
- compute_distance_matrix() ndarray[source]
Dispatch to the configured pairwise distance metric.
Returns:
- np.ndarray
Square
N x Ndistance matrix.
- conservation_threshold: float
Minimum consensus-residue frequency for a column to count as conserved.
- cophenetic_correlation() float[source]
Cophenetic correlation between the tree and the input distances.
Returns:
- float
Pearson correlation of the cophenetic distances against the condensed input distance matrix (higher is a more faithful tree).
- critical_depth() DataFrame[source]
Most ancestral node at which each KLIFS column still survives-up.
A column’s critical depth is the shallowest (closest to root) depth at which it appears in a node’s survives-up set. Columns surviving at depth 0 are the pan-kinase invariants (expect the glycine-rich loop, the HRD arginine and the DFG aspartate).
Returns:
- pd.DataFrame
One row per KLIFS column with
column(0-based),position_label,critical_depth(NaN if never robust) andconsensus_aaat the shallowest surviving node, sorted by critical depth.
- distance_matrix: ndarray | None
Square
N x Npairwise distance matrix; computed post-init unless supplied (e.g. injected byfrom_conservation_data()from a persisted artifact).
- exclude_pseudokinases: bool
If True, drop predicted pseudokinases (
KinaseInfo.is_pseudokinase()) from the panel before clustering, so the tree and conservation reflect catalytically active kinases only (default: False).
- classmethod from_conservation_data(data: KLIFSConservationData, **kwargs) KLIFSHierarchicalConservation[source]
Build a renderer from a persisted
KLIFSConservationDataartifact.Reuses the stored distances + linkage (so the rendered tree matches the persisted one and no clustering is recomputed), recovering the per-kinase pocket/group panel from
DICT_KINASEby name. Any renderer-specific keyword (e.g.min_cluster_size,font_size) is passed through.- Parameters:
data (KLIFSConservationData) – The persisted conservation-data artifact.
**kwargs – Extra fields forwarded to the (sub)class constructor.
- Returns:
An instance of
clswith injected distances, linkage, and tree.- Return type:
- gap_chars: str
Characters treated as gap/unknown and excluded from scoring and conservation.
- gather_children(node_id: int) list[int][source]
Singleton-contracted multifurcating children of
node_id.Descends through singleton-peel chains (nodes that are not
_is_kept()) so a kinase that peels off attaches directly to its clade, yielding a multifurcating child list of leaves and kept clades.- Parameters:
node_id (int) – Internal linkage node id.
- Returns:
Child node ids (leaves and kept internal nodes).
- Return type:
list[int]
- group_concordance(n_clusters: int | None = None) float[source]
Adjusted Rand index between flat clusters and Manning groups.
Parameters:
- n_clustersint | None
Number of flat clusters to cut the tree into. Defaults to the number of distinct Manning groups in the panel.
Returns:
- float
Adjusted Rand index (1.0 = perfect concordance, ~0 = chance).
- groups: list[str]
Manning group per kinase, used for the external concordance check.
- linkage_matrix: ndarray | None
SciPy linkage matrix; computed post-init unless supplied.
- linkage_method: str
"average"(UPGMA, default) or"complete".- Type:
SciPy linkage method
- members_consensus(member_idx: list[int]) list[str | None][source]
Per-column >=threshold consensus residue for a member set (else None).
Works for any set of leaves – a single leaf, a clade, or an aggregated row of unrelated singletons – so it backs both the per-node logo and the static per-leaf conservation table.
- Parameters:
member_idx (list[int]) – Leaf indices into
pockets.- Returns:
One entry per KLIFS column: the consensus residue if its frequency meets
conservation_threshold, elseNone.- Return type:
list[str | None]
- metric: str
"blosum"(default) or"identity".- Type:
Pairwise distance metric
- min_child_members: int
Minimum child-clade size considered in survives-up pruning; children smaller than this (e.g. singleton outliers peeled off at the root) are ignored. Set to 1 to require every immediate child to agree (the strict, topology-sensitive definition).
- names: list[str]
List of HGNC kinase names (one per pocket).
- node_conservation(member_idx: list[int]) list[tuple[str | None, float]][source]
Per-column conservation for a set of member leaves.
Reuses
MSAConservationAnalyzer.column_conservation()so the per-node read-out matches the panel-wide conservation analysis. Whenweightingis"henikoff"the consensus is computed with per-node Henikoff sequence weights (_henikoff_weights()).Parameters:
- member_idxlist[int]
Indices into
pocketsof the node’s member kinases.
Returns:
- list[tuple[str | None, float]]
One
(consensus_aa, fraction)tuple per KLIFS column.
- node_information(member_idx: list[int], background: str = 'swissprot') tuple[list[float | None], list[float]][source]
Per-column background-relative information content (bits) and effective count.
Continuous companion to
members_consensus()(plan Addendum A.2): for each KLIFS column, the KL divergence of the members’ substitution-smoothed residue distribution against the chosen background, plus the effective (weighted) count so the reader can see where the score is prior-influenced vs data-dominated. Sequence weighting followsweighting(uniform by default; Henikoff when set), the substitution-aware pseudocount is always applied.- Parameters:
member_idx (list[int]) – Leaf indices into
pockets.background (str, optional) – KL background:
"swissprot"(default) or"kinome"(see_pseudocount_model()).
- Returns:
(ic_per_column, n_eff_per_column);icisNonefor an all-gap column.- Return type:
tuple[list[float | None], list[float]]
- nodes_summary() DataFrame[source]
Tabular summary of per-node conservation counts (internal nodes only).
Returns:
- pd.DataFrame
One row per internal node with
node_id,depth,n_members,n_conserved(per-subtree),n_survives_up(pruned) andn_lost(conserved but not robust), sorted by depth.
- plot_critical_depth(figsize: tuple[float, float] = (15, 3), cmap: str = 'plasma') tuple[Figure, Axes][source]
Plot per-KLIFS-column critical depth as an opaque track with a region bar.
Each of the 85 KLIFS columns is colored by its critical depth — the shallowest (most ancestral) node at which it still survives-up (
critical_depth()). Lower depth means the residue holds across more of the kinome (depth 0 = a pan-kinase invariant); columns that never survive-up are drawn in grey. A KLIFS region color bar and position labels run beneath the track. Colors are flattened to opaque RGB (no alpha) for PowerPoint/PDF safety.Parameters:
- figsizetuple[float, float]
Figure size (default: (15, 3)).
- cmapstr
Matplotlib colormap name for the depth scale (default:
CMAP_CRITICAL_DEPTH).
Returns:
- tuple[plt.Figure, plt.Axes]
Figure and axis objects.
- pockets: list[str]
List of 85-character KLIFS pocket strings, column-aligned by construction.
- position_labels: list[str]
KLIFS region labels (e.g.
"g.l:4") for the 85 pocket columns.
- residue_dot_layout(min_cluster_size: int = 12) tuple[KLIFSDisplayTree, list[int], dict[int, int], dict[int, int], list[list[int]]][source]
Row/leaf assignment for the per-amino-acid dot plot.
Orders kinases by the display-tree leaf order (dendrogram order), assigning each kinase a y row and the 1-based number of the display leaf (clade) it belongs to. Shared by the interactive Bokeh explorer and the static companion figure so their ordering and clade grouping cannot diverge.
- Parameters:
min_cluster_size (int, optional) – Display-tree collapse threshold, by default
INT_TREE_MIN_CLUSTER_DISPLAY.- Returns:
(tree, ordered_members, row_of_member, leaf_of_member, leaf_members):ordered_membersare leaf indices (intonames) top-to-bottom,row_of_membermaps each to its y row,leaf_of_memberto its 1-based display-leaf number, andleaf_memberslists members per display leaf in display order.- Return type:
tuple
- to_conservation_data(min_cluster_size: int = 12, aggregate_singletons: bool = True) KLIFSConservationData[source]
Package the clustering output as a serializable
KLIFSConservationData.Stores the condensed upper-triangle distances, the SciPy linkage matrix, the leaf order, and the display-tree leaf composition (all as plain Python numbers), alongside the provenance metadata describing how they were assembled.
- Parameters:
min_cluster_size (int, optional) – Display-tree collapse threshold passed to
build_display_tree(), by defaultINT_TREE_MIN_CLUSTER_DISPLAY.aggregate_singletons (bool, optional) – Whether to fold singleton leaves in the display tree, by default True.
- Returns:
The serializable conservation-data artifact.
- Return type:
- tree: ClusterNode | None
Root
ClusterNodeof the hierarchical tree, computed post-init.
- tree_children(node_id: int) tuple[int, int][source]
The two linkage children of an internal node id.
- tree_members(node_id: int) list[int][source]
Leaf indices under
node_id(the node itself if it is a leaf).
- weighting: str
"none"(default) or"henikoff".- Type:
Per-node consensus weighting