Data analysis · ACNH catalog case study

The Join Is Dirtier Than the Data

Same audit methodology as the e-commerce case and the MovieLens 25M audit, run on a video-game catalog: the stakes are lower, the failure classes are identical.

An audit-first analysis of the Animal Crossing: New Horizons catalog (391 villagers, 30 tables, 18,962 rows) by Alex Kwon. Verdict in one line: the tables are close to immaculate; the string keys between them are not.

Dataset: Animal Crossing New Horizons Catalog (Kaggle, uploaded by jessicali9530, from the NookPlaza community spreadsheet, CC0-1.0); a mid-2020 snapshot, datamined rather than observed.

Villagers
391
0 nulls, 0 duplicate ids
Join repaired
3
villagers silently lost to one capital letter
Price columns poisoned
22/25
"NFS" sentinel forces text type
False findings caught
2
one by the data, one by the analyst

One capital letter manufactures a finding

Villagers Huck, Monty, and Paolo favorite “To The Edge”; the music table spells it “To the Edge”. Joins and GROUP BY match bytes, so the unrepaired join silently drops all three villagers and reports the song as favorited by nobody, which is false. No error is raised anywhere; the output is simply wrong with confidence. After casefolding the key on both sides, the join is total: 0 unmatched rows.

The honest “favorited by nobody” list, repaired and decomposed, is three populations, not one:

ClassSongsn
Placeholder rows (filenames, not titles)Hazure01, Hazure02, Hazure033
Event-only, not purchasable (Buy = NFS)K.K. Birthday, Welcome Horizons2
Purchasable, favorited by nobodyK.K. Mariachi, K.K. Samba, K.K. Ska, K.K. Waltz4

The unrepaired list has ten entries; the tenth ("To the Edge", 3 fans) is an artifact of the case mismatch. Favorites are designer assignments, not villager opinions, so "unloved" describes an assignment table, not taste.

Song favorites are mildly concentrated

Forest Life leads with 15 of 391 villagers (3.8%); the top five songs hold 57 of 391 (14.6%), and 89 of the 95 real songs have at least one fan. The denominator is 95, not 98: three rows of music.csv are placeholders.

Personality determines gender, 391 of 391

Every personality is single-gender: Cranky, Jock, Lazy, and Smug villagers are all male (204); Big Sister, Normal, Peppy, and Snooty are all female (187). The chart needs no overlap because none exists. Any gender comparison on this data is a personality comparison wearing a different label, so no finding here is phrased as a gender effect.

male (204)female (187)

Related routing, stated with its confound: 36 of 55 Jocks (65%) have the Fitness hobby; 63 of 104 Peppy-plus-Snooty villagers (61%) have Fashion. Personality or gender, the data cannot say which.

Absence carries the design

391 villagers occupy 361 of 366 possible calendar days; 30 days carry exactly two villagers, none carries three, and five days carry none: 29-Feb, 1-Apr, 15-Apr, 31-Oct, 24-Dec. Four map to game events or rarities (leap day, April Fools, Halloween, Toy Day eve); 15-Apr stays an open question rather than a forced story. Nothing in the documentation says event days get no birthdays; the missing rows say it.

Middle months (1.00–1.13) omitted from the chart for legibility; the full twelve are in the notebook. The spread is a designed near-uniformity: the denominator (days in month) matters more than the count.

Catalog-wide: the types lie before the values do

The same sweeps across all 30 tables: every file opens with a UTF-8 BOM (an export convention, not an accident), and the Buy column mixes integers with the sentinel string “NFS” in 22 of 25 priced tables (6,498 cells), forcing the whole column to load as text. The three tables where Buy loads numeric are clean only because nothing in them happens to be not-for-sale: same pipeline, different rows, different column type.

FindingScopeDisposition
"To The Edge" vs "To the Edge" breaks the song join3 villagers, 1 false findingkeys casefolded; join verified total
Placeholder rows (Hazure01-03) in music.csv3 of 98 rowsexcluded from denominators
UTF-8 BOM opens every file30 of 30 tablesnoted; absorbed by this pipeline
"NFS" sentinel poisons the Buy column's type22 of 25 tables, 6,498 cellsflagged; split before any price math
En dashes + non-breaking spaces inside time stringsfish, insects (2,168 chars)flagged for parsers; initially misdiagnosed as file corruption: the mojibake was the analyst's console. Correction kept on the record

Study notes: is this data biased?

This case is a clean worked example for the standard bias taxonomy, mostly because of which biases it cannot have. Sampling bias is structurally impossible: the rows are the entire villager roster, a census, and "the sample misrepresents the population" cannot arise when population = dataset. Observer bias is absent: the values were datamined from game files, not measured by people. What remains lives on the analyst's side of the desk.

Bias typeApplies here?Why
SamplingNo, structurallycensus, not a sample; population = dataset
ObserverNodatamined, not observed or measured by people
InterpretationYes, analyst-side"unloved songs" and "event days" are readings of designer intent, phrased as readings; the console-mojibake false alarm was interpretation bias caught in the act
ConfirmationThe standing riskarrive believing "the game is gendered" and support appears; the full sweep and parts-sum-to-totals are the counterweight

What the file has instead of bias: a currency limit (a census of mid-2020; later-added villagers are absent) and a faithfully recorded biased generator: the personality-gender partition and the unequal quotas are design decisions the data records accurately. Unbiased file, biased world. The transferable rule: "unbiased" is a property of a question-file pair, not of a file. This data answers "what is the roster's composition?" cleanly and cannot answer "do male villagers prefer fitness?" at all.

Study notes: the data types on display

All 30 tables are structured data; inside them, nearly every textbook type appears, and the two that misbehave here misbehave everywhere.

TypeWhere it livesNote
Nominal (qualitative)Name, Species, Colors, Source, Hobbycategories without order; most of every table
Ordinal (qualitative)fish Shadow sizes, achievement tiersa set scale
Discrete (quantitative)Sell, Buy, Miles Price, catch countsthe whole economy is counted, never measured
Continuous (quantitative)essentially absentthe fingerprint of designed vs measured data
BooleanDIY, Genuine, Sequentialstored as Yes/No strings; loads as text until cast
Date/time-shaped stringsBirthday ("27-Jan"), availability windowsBirthday is the exact format spreadsheet autocorrect destroys; the windows hide en dashes and non-breaking spaces
Wide-format time dataNH Jan–SH Dec month columns24 columns per hemisphere; melt to long before any seasonality analysis

The two live traps: a sentinel string in a numeric column ("NFS" in Buy) changes the column's type only in the tables where the sentinel occurs, and Booleans-as-words fail equality tests against real booleans. Both are invisible until something downstream computes.

What we cannot conclude

⚠ limits

Villager popularity: this catalog has no popularity signal; rankings elsewhere come from scraped third-party wish-list data. Gender-vs-personality mechanisms: perfectly confounded by construction. Why 15-Apr is birthday-free. And whether the four unloved songs are disliked: favorites are designer assignments, not opinions. The dataset is a census of the roster as of mid-2020; villagers added in later game updates are absent.

A census, not a sample: no sampling bias is possible, and the data was datamined rather than observed. What it records faithfully is a biased generator (unequal personality quotas, a perfect personality-gender partition): unbiased file, biased world. Analysis: Alex Kwon. Full workings, findings with row names, and the runnable notebook: the acnh-villager-audit repository (FINDINGS.md, REPORT.md, acnh_villager_audit.ipynb). Every figure independently recomputed before publication.

Cite: Kwon, A. (2026). The Join Is Dirtier Than the Data: an audit-first analysis of the ACNH catalog. github.com/collapseindex/acnh-villager-audit (machine-readable: CITATION.cff in the repository). License: report CC BY 4.0; code Apache-2.0. Disclosure: AI-assisted implementation; methodology and validation framework developed in prior research; every figure recomputed from raw data by the runnable code in the repository.

Alex Kwon · ask@collapseindex.org · more case studies · github.com/collapseindex