Placing points on a finished map¶
A map is fitted once, on the mapped people. Everything else is placed on it: the keywords, the projected people, and each person’s texts of one period (the trajectories). The people’s own positions come from the fit and never move.
Decision (gate G2): points are placed by their nearest people. Until then
cartolex placed them with UMAP’s own transform, which needs the fitted UMAP
model (re-fitted when read, not exactly the same on every machine) and runs a
stochastic optimisation per call. That method, and the stored layout model with
it, are retired: the map is the people’s and the keywords’ positions in the
stored embeddings, and cartolex.atlas.placement.place places everything else.
The method¶
A vector is placed from its k = 8 nearest mapped people, by cosine distance in the SVD space:
weights. The neighbours weigh as in UMAP’s fuzzy neighbourhood: the nearest weighs 1, the others
exp(-(d - d₁)/σ),σset so the weights sum tolog₂ k;the heaviest group. Two neighbours are linked when their positions on the map are within the link radius of each other, a quarter of the map’s radius (the root mean square distance of the people from their centre). The linked neighbours form groups (the connected parts of the links), and the point goes to the weighted mean of the group with the largest total weight; on a tie, the group of the nearest neighbour.
A point whose neighbours sit together lands at their weighted mean. A point whose neighbours are split between two distant places lands in the place that weighs most, never in the empty space between them: a plain weighted mean of all eight would strand it there.
No fitted model is needed and nothing is random. The arithmetic runs in a fixed
order (np.einsum without optimisation; neighbours ranked by distance, then by
index), so the positions are the same on any machine and whatever the chunk
size; the work goes in chunks, so memory stays proportional to the chunk times
the number of people. Placing the 3,072 keywords of the large demo world takes
0.09 s.
The method does not depend on the map’s dimensions: on a map in space (a map
version of dimensions 3) the neighbours’ places, the link radius and the
weighted mean have three coordinates, and every placed point gets three. Each
built map version places the same points on its own map, from the same
neighbours in the space.
where |
what is placed |
|---|---|
|
the keywords (except in the joint layout, which fits people and keywords together) |
|
each person’s time bins and time windows (all windows in one call) |
|
the projected people, from their texts |
The study¶
python tools/placement_study.py --size L --seed 0 runs the engine with the
numeric reference’s settings up to the layout, then places the same points
three ways: UMAP’s transform (the study fits a UMAP of its own on the people,
with the layout’s settings, and checks it gives the engine’s map), the plain
weighted mean of the eight neighbours (a switch of the study only), and the
heaviest group. Neighbours is the share of a placed point’s 10 nearest people
on the map that are also among its 10 nearest in the SVD space (higher is
better); stranded the share of points farther than the link radius from every
one of their eight nearest people on the map; the shift is the median distance
from UMAP’s position (for people left out one at a time, from their fitted
position), in map radii. For UMAP, the people’s row shows their fitted
positions.
Demo world L, seed 0 (343 people, 3,072 keywords):
points |
method |
neighbours |
stranded |
median shift |
|---|---|---|---|---|
people, left out one at a time |
UMAP (fitted) |
0.830 |
0.0 % |
|
average |
0.819 |
1.2 % |
0.027 |
|
heaviest group |
0.836 |
0.0 % |
0.028 |
|
keywords (3,072) |
UMAP |
0.794 |
0.7 % |
|
average |
0.784 |
5.0 % |
0.067 |
|
heaviest group |
0.824 |
0.0 % |
0.067 |
|
projected people (35) |
UMAP |
0.631 |
2.9 % |
|
average |
0.591 |
11.4 % |
0.078 |
|
heaviest group |
0.697 |
0.0 % |
0.079 |
Demo world S, seed 0 (38 people, 490 keywords; on a map this small most people are more than a quarter of the map’s radius from each other, so stranded says little):
points |
method |
neighbours |
stranded |
median shift |
|---|---|---|---|---|
keywords |
UMAP |
0.521 |
100 % |
|
average |
0.622 |
7.3 % |
0.795 |
|
heaviest group |
0.597 |
0.0 % |
0.738 |
|
projected people (4) |
UMAP |
0.475 |
100 % |
|
average |
0.550 |
0.0 % |
0.576 |
|
heaviest group |
0.525 |
0.0 % |
0.479 |
What the numbers say¶
On a realistic map (L), the heaviest group keeps neighbourhoods better than UMAP’s
transformfor keywords (0.824 against 0.794) and projected people (0.697 against 0.631), and strands nothing. The plain average strands 5 % of keywords and 11 % of projected people between two places.UMAP’s keywords on the small map sit away from all their neighbours: the halos around groups of people that the map used to show were largely an artifact of the transform.
A person left out and placed lands on average 0.03 map radii from where the fit put them: placing a new person gives very nearly where fitting them would.
Placement is exact across chunkings, needs no layout model, and makes the trajectories stage 3.3 times faster on L (18.2 s to 5.5 s).