Compound–target analysis
One compound per row, one target per column. Two bodies of evidence can fill it — the affinities this archive holds, and predictions from DRIFT — and they are never combined into one grid. A measurement and a model’s estimate answer different questions, and a heatmap is exactly where that difference stops being visible if you let it.
163 of 163 selected
Blank cell · not measured
163 × 80 · 785 values · 6% filled
The strip under the plate counts, for each column, how many of the selected compounds carry a published Kᵢ or Kd at that target. The bar beneath each number is that same number and nothing else — one value drawn twice, not a second measurement. Both are counted off the cells above them, so they follow the current selection rather than the whole archive. Columns are ordered by that count, which is why the bars fall away to the right: most of this pharmacology rests on a handful of targets that have been assayed against nearly everything, and a long tail measured once or twice.
Every cell is a published equilibrium constant — Ki or Kd, the strongest recorded for that pair. Functional readouts are not pooled with them. The whole grid is shown: 163 compounds against 80 molecular targets, from 2,351 measurements. It is mostly empty, and that is a true fact about how thinly this pharmacology has been assayed rather than a fact about the compounds. 24 of those targets were recorded under more than one name — ChEMBL writes “Cannabinoid receptor 1” where the PDSP ingest writes “CB1” — and are folded onto one column by the table in src/lib/targets.ts. Point at a column heading to see the other spellings; the fault and every fold are set out on the integrity report.
Network
Interaction graph
The same cells as a bipartite network: a circle for each compound, a square for each target, and an edge wherever the archive holds a value for that pair. It answers what a grid answers badly — which targets a group of compounds have in common — because a shared target is one node with several edges running into it rather than a column you read down. Node area is degree and edge weight is the stored value; position carries nothing except in the spring layout, which says so. It follows the source and the compound selection set above.
Compounds in one column, targets in the other, each ordered by degree. The honest default for two-kind data: no position is invented, and a target's edges fan visibly into one point.
163 compounds · 80 targets · 785 of 785 edges
Point at a node to trace it
Compound × compound
Profile similarity
The same evidence read the other way round: not which targets a compound binds, but how alike two compounds’ whole profiles are. Each compound becomes a vector over every target of the source above — carrying 1/Ki at each target and zero where there is no interaction — and a cell is the cosine of the angle between two of them. It follows the source and the compound selection you set for the plate above.
163 × 163 · 13,203 pairs
Each compound is a vector over all 80 targets of this source, carrying 1/Ki at each target and zero where there is no interaction. A cell is cos θ between two such vectors: 1 is the same direction, 0 is orthogonal. The diagonal is 1 by construction, not by finding. Cosine normalizes each vector by its own length, so the units cancel and the axis needs none.
The reciprocal is what makes the zero honest. 1/Ki tends to zero as Ki tends to infinity, so an empty cell holds the limit of the quantity at “does not bind” rather than a number standing in for one. Measured in pX instead, the same cell would assert a binding constant of one molar — and because a weak target would then contribute a component of the same order as a strong one, the angle would largely track whether two compounds had been assayed at the same targets. Here a target bound at 10 µM contributes about a hundred-thousandth of what one bound at 100 pM does, so the profile is set by the interactions that dominate it and being co-assayed buys nothing on its own.
What the reciprocal does not fix. An empty cell here still means “nobody ran this assay”, which is not the same fact as “this compound does not bind”. Zero is the right value for the second and says nothing about the first, and no complete-vector method can tell them apart. The drug–target plate above is the one that refuses to conflate them — it never fills a gap, and pays for it by comparing a pair only over targets they share. The two orderings disagreeing is informative rather than a fault.
Because every vector is complete, every pair here is comparable and the clustering carries no fallback distance at all. Distances are 1 − cos θ, average linkage; a group is shaded where its members joined within the closest quartile of observed distances.
Distribution
Target count
How many targets each selected compound carries a value at. The filled bars are the 163 compounds chosen above; where that is fewer than all 163, the outline behind them is the whole source. The outline is what the Z-score column standardizes against, and it deliberately does not move with the selection — a Z measured against a shifting population would be measuring the view rather than the archive.
μ = 4.82, σ = 5.48 over 163 compounds; the sample standard deviation, with n − 1 in the denominator. Both are computed over every compound of this source and over its whole target space, so neither moves when you change the selection or hide a column above.
This is why there is no p-value beside the Z. The distribution is strongly right-skewed and bounded below at zero — most compounds have been assayed at a handful of targets, a few at scores of them. A Z-score needs no distributional assumption to be computed and reads as exactly what it is: how far from typical a compound’s coverage is, in standard deviations. Turning it into a two-tailed p would assume a normal distribution, and this histogram is what that assumption would have to hold for. It also matters what is being standardized — how thoroughly a compound has been studied, which is a fact about the literature rather than about the molecule, and not a thing a hypothesis test has a null for.
Every cell in the measured grid is a Ki or Kd published for that compound at that target, shown as pX and traceable from the compound’s own page. Functional readouts are excluded: pooling IC₅₀ with an equilibrium constant puts two different quantities on one axis. Empty cells mean no measurement exists — not that the compound fails to bind. Sources.