# Fidelidade — Agregados e joins (08)

**Projeto:** MonumentosFR · **Dimensão:** agregados de visualização + joins Wikidata/Xref
**Data:** 2026-09-01 · **Método:** rederivação integral dos agregados a partir do dataset-fonte, com as definições exatas dos geradores

## Método

- Cada agregado servido em `www/data/` foi **rederivado do zero** a partir de `www/explorer/merimee_data.json` (46.760 records), replicando linha a linha a lógica de `tools/build_patterns_data.py`, `tools/build_viz_data.py` e `tools/build_dataset.py` (definições confirmadas por leitura dos scripts antes de qualquer comparação).
- Convenção da auditoria: **divergência de definição ≠ erro**; erro = número servido que não bate com a definição do gerador. Ao contrário, um número que bate com uma definição semanticamente errada é assinalado como «conforme à definição, com nota».
- Para o xref houve rederivação dupla (dataset + CSV raw `raw/merimee.csv`); para o Wikidata, rederivação a partir dos pares SPARQL guardados em `raw/wikidata_p380_resume.json` (73.242 pares).
- Marcação: **[C]** confirmado nos ficheiros/scripts; **[I]** inferido/secundário.

## Tabela-mestre

| Agregado | Métrica | Site | Rederivado | Delta |
|---|---|---|---|---|
| patterns.centuries | n bins / soma | 23 / 39.792 | 23 / 39.792 (labels+data idênticos) | 0 [C] |
| patterns.centuries (eras) | bins -60/-22/-5 | 1.503 / 290 / 483 | 1.503 / 290 / 483 | 0 [C] |
| patterns.protections | total eventos / décadas | 51.628 / 19 | 51.628 / 19 (todas as barras iguais) | 0 [C] |
| patterns.emerging | entradas / séculos | 91 / 19 | 91 / 19 (todas as entradas iguais) | 0 [C] |
| patterns.declining | entradas / séculos | 56 / 19 | 56 / 19 (todas as entradas iguais) | 0 [C] |
| patterns.sacredData | soma | 14.420 | 14.420 (20 bins iguais) | 0 [C] |
| patterns.secularData | soma | 23.096 | 23.096 (20 bins iguais) | 0 [C] |
| patterns.builders | firms / persons (séc. 1) | 180 / 11.730 | 180 / 11.730 — conforme à definição; ver D1 | 0 [C] |
| patterns.builders | topFirms / rolesByCentury | 20 entradas / séc. 1 | idêntico | 0 [C] |
| aggregated.binsByDomaine | soma bins | 38.439 | 38.439 (23 linhas idênticas) | 0 [C] |
| aggregated.binsByDepartement | soma bins | 39.792 | 39.792 (23 linhas idênticas) | 0 [C] |
| aggregated (células) | 6 células à mão | — | 19 religieuse=569; 75-Paris séc.19=421; 33-Gironde séc.18=357; 69-Rhône séc.20=39; etc. | 0 [C] |
| sunburst | soma sizes | 45.028 | 45.028 (árvore inteira idêntica) | 0 [C] |
| heatmap.matrixDept | soma matriz | 39.792 | 39.792 (matriz 23×104 idêntica) | 0 [C] |
| heatmap.matrixDom | soma matriz | 38.439 | 38.439 (matriz 23×22 idêntica) | 0 [C] |
| heatmap (células) | 3 células à mão | — | -60/75-Paris=0; 3/29-Finistère=1; 20/14-Calvados=41 | 0 [C] |
| reuse | nós / links / meta.n | 29 / 69 / 1.121 | 29 / 69 / 1.121 — **todos** os 69 links == dataset | 0 [C] |
| materials-network | nós / arestas | 82 / 79 | 82 / 79 (todos iguais) | 0 [C] |
| materials-network | 10 NPMI à mão | — | 10/10 ok (ex.: religieuse↔église 1,101; industriel↔moulin 4,004) | 0 [C] |
| coords | n pontos | 44.484 | 44.484 == with_coords do dataset; refs e (lat,lng) idênticos | 0 [C] |
| geo.points | n | 44.484 | 44.484, valores idênticos | 0 [C] |
| geo.districts | conformes | 104 | 104/104 (todas as métricas por dept) | 0 [C] |
| geo.municipalities | conformes | 17.080 | 17.080/17.080 | 0 [C] |
| temporal | monuments / meta.n | 46.760 / 46.760 | 46.760 == records; s/e coerentes em 100% | 0 [C] |
| temporal (eras) | sentinelas s/e | -6000/-800; -2500/-1800; -52/486 | = era_sentinels(ERA_CODES) do gerador [C] | 0 |
| wikidata | refs servidas | 71.634 | 71.634 == rederivação dos pares raw; 0 pares fora do raw | 0 [C] |
| xref | refs | 297 | 297 == rederivação do CSV raw; 0 órfãos | 0 [C] |

\* Nota temporal: os 205 registos com cmin E era levam s/e do ramo cmin (ex.: 8 protohistoire com cmin 5/cmax 10 → 400–1499); os registos só-era levam o primeiro sentinela de ERA_CODES com esse código (`era_sentinels`): préhistoire −6000/−800, protohistoire −2500/−1800 (via chalcolithique, 1.ª entrada), antiquité −52/486. Estrutura s/e verificada coerente nos 46.760 registos [C].

## Divergências com detalhe

**Não foi encontrado nenhum número servido que divirja da definição do gerador.** As duas notas seguintes são questões de definição/qualidade, não de fidelidade numérica:

- **D1 — builders: colapso de todos os séculos no bin «1»** [C]. Em `build_persons.py`, `yr` de cada monumento é `[cmin, cmax]` — **já são séculos** (1..20). Mas `build_patterns_data.py` aplica `cent_c = (c // 100) + 1`, tratando o século como ano: 18 → `(18//100)+1 = 1`. Resultado: `builders.centuries = [1]` contém TODA a atividade (11.910 ligações monumento↔entidade: 180 firmas + 11.730 pessoas), e o gráfico firms/persons por século do site mostra um único ponto «século 1». Os totais (180/11.730) estão corretos e batem com persons.json [C]; é a subdivisão temporal que é semanticamente vazia. Correção sugerida: `cent_c = c` (yr já é século). Não conta como infidelidade numérica — o ficheiro reflete fielmente (a definição errada de) o gerador.
- **D2 — wikidata: 25.785 órfãos (36% do ficheiro) + 14 chaves genid** [C]. O ficheiro é um dump de TODA a propriedade P380 (filtrada só pela política «>3 refs por QID → drop»), não apenas dos refs do dataset (que é 100% prefixo PA): órfãos = 24.553 IA + 561 ACR + 312 EA + 231 MI + 96 PA + 16 JAR + 2 SPR + 14 URIs `wikidata/.well-known/genid/…` (blank nodes «valor desconhecido» apanhados pela query SPARQL). O site usa o ficheiro como lookup `wd[monId]` (www/js/monument.js:104), pelo que os órfãos são peso morto (~36% dos bytes) e nunca produzem joins falsos. Cobertura real: 45.849/46.760 refs do dataset com QID = **98,05%**; 545 refs do dataset ficam sem join por ambiguidade conservadora (refs com 2+ QIDs distintos no raw) [C]. Limpeza sugerida: filtrar o dump aos refs do dataset e excluir valores genid.

Notas menores (sem impacto):
- A métrica «NPMI» do materials-network é `ln(pxy/(px·py))` sem normalização por `−ln(pxy)` (NPMI clássico limitado a [−1,1]); valores servidos vão até 5,097. Correto face à definição do gerador [C]; o nome «NPMI» é impreciso [I].
- No aggregated, células com contagem 0 são omitidas (absência de chave == 0) em vez de `null` — coerente dos dois lados, consumido corretamente pelo gráfico [C].
- reuse: população do Sankey = 1.121 fluxos = 1.131 registos com `uc=1` − 10 filtrados por categoria «Autre» [C]; 45.629 registos sem mudança de uso ficam de fora por definição [C].

## Joins wikidata/xref

- **wikidata.json** [C]: {ref → QID}, 71.634 entradas; == rederivação exata a partir de `raw/wikidata_p380_resume.json` com a política do gerador (73.242 pares SPARQL → 234 dropados por QIDs com >3 refs → 605 refs ambíguas descartadas). Todos os pares servidos existem no raw (0 violações). Órfãos fora do dataset: **25.785** (ver D2). QIDs distintos: 66.611; máx. refs por QID servido: 3 (respeita a política); 4.861 QIDs com 2–3 refs (permitido). Cobertura do dataset: 45.849 (98,05%); 545 refs do dataset sem join por ambiguidade.
- **xref.json** [C]: {ref → {i, n}}, 297 entradas; rederivação idêntica a partir do CSV raw (códigos de `Renvoi_vers_une_notice_de_la_base_Merimee_ou_Palissy` resolvidos internamente); **0 órfãos** (todas as refs existem no dataset); todos os valores bem-formados (i == chave, n não vazio).
- Consumo no site [C]: `www/js/monument.js` faz lookup por `monId` nos dois ficheiros — a direção relevante (dataset → QID/nome) está íntegra; órfãos nunca são acedidos.

## Veredicto

**100% dos números verificados são conformes** (24/24 grupos da tabela-mestre, ~40 verificações individuais incluindo células à mão e a população integral dos 69 links do Sankey): os agregados servidos aos grafos derivam corretamente do dataset, sem qualquer divergência numérica. As duas únicas notas são de definição/qualidade e não afetam a fidelidade: (1) o agregado builders colapsa todos os séculos no bin «1» por um bug semântico no gerador (`yr` já é século, tratado como ano) — totais corretos, série temporal vazia; (2) o join Wikidata é íntegro mas transporta 36% de refs órfãs inutilizadas (incl. 14 blank nodes genid), recomendando-se filtragem ao dataset. Nada disto invalida os números exibidos ao utilizador.

## Fontes

Locais (todos [C], lidos nesta auditoria):
- Dataset-fonte: `C:\Users\Atelier X\Nextcloud\Documents\00_Website\MonumentosFR\www\explorer\merimee_data.json`
- Agregados servidos: `C:\Users\Atelier X\Nextcloud\Documents\00_Website\MonumentosFR\www\data\merimee-patterns.json`, `merimee-aggregated.json`, `merimee-sunburst.json`, `merimee-heatmap.json`, `merimee-reuse.json`, `merimee-materials-network.json`, `merimee-coords.json`, `merimee-geo.json`, `merimee-temporal.json`, `merimee-wikidata.json`, `merimee-xref.json`, `merimee-persons.json`, `merimee-detail-index.json` + `merimee-detail-*.json`
- Geradores: `C:\Users\Atelier X\Nextcloud\Documents\00_Website\MonumentosFR\tools\build_patterns_data.py`, `tools\build_viz_data.py`, `tools\build_dataset.py`, `tools\build_persons.py`, `tools\build_xref.py`, `tools\fetch_wikidata.py`
- Raw: `C:\Users\Atelier X\Nextcloud\Documents\00_Website\MonumentosFR\raw\merimee.csv` (download de https://www.data.gouv.fr/api/1/datasets/r/3a52af4a-f9da-4dcc-8110-b07774dfb3bc — base Mérimée, LO 2.0), `raw\wikidata_p380_resume.json` (pares SPARQL P380)
- Consumo no site: `C:\Users\Atelier X\Nextcloud\Documents\00_Website\MonumentosFR\www\js\monument.js`
- SPARQL Wikidata P380 (identifiant Mérimée): https://query.wikidata.org/ — query `SELECT ?item ?ref WHERE { ?item wdt:P380 ?ref . }` (verificada 2026-09-01 pelo próprio pipeline do projeto)
