Metoda

Pipeline reproducible. Cod public. Date verificabile.

Toate cele 11 etape ale analizei, cu link-uri către scripturi și surse.

Filosofie

Datele și codul sunt publice. Toate concluziile pot fi reproduse rulând scripturile din repository.

Singurele dependențe externe:

  • Cheie API Gemini (~5€ total pentru toate clasificările LLM)
  • Cheie API Webshare proxy (pentru bypass YouTube rate limit)
  • Cheie API Apify (pentru scraping Facebook)

Cele 11 etape

flowchart TB
    A["1 · Discovery"] --> B["2 · Raw collection"]
    B --> C["3 · Dedupe Jaccard"]
    C --> D["4 · Diarize LLM"]
    D --> E["5 · Clean stopwords"]
    E --> F["6 · Project scris/vorbit"]
    F --> G["7 · Analyze basic+TF-IDF"]
    G --> H["8 · BERTopic"]
    G --> I["9 · Promise Tracker"]
    G --> J["10 · Avansate (complexitate, ezitare, drift, NER, sentiment)"]
    J --> K["11 · ND-Bolojan"]
    H --> L["Interpret"]
    I --> L
    K --> L

1-2 · Colectare brută

9 surse: Facebook NicusorDan.ro (prin Apify) + 8 canale YouTube (prin yt-dlp + youtube_transcript_api cu rotație proxy Webshare IP 1-200).

Output: 1.525 documente cu YAML frontmatter complet.

3 · Deduplicate

Jaccard similarity ≥ 0,70 pe token sets per dată. Cluster cu single-link, canonical pick prioritar (Adm. Prezidențială > Privesc.Eu > NicusorDan.ro > manual > TV).

Output: 1.110 docs canonice (415 dups eliminate).

4 · Diarizare LLM

Gemini 2.5 Flash cu thinking_budget=0 și HttpOptions timeout 5min. Prompt în română pentru etichetare [ND], [JURNALIST], [ANCHOR], [MODERATOR], [OFICIAL: nume], [UNKNOWN].

Audit dublu (3 seed-uri × 60 fișiere): 95-97% acuratețe, zero cazuri de non-ND etichetat fals ca ND.

5-6 · Clean + Project

spaCy ro_core_news_sm + stopwordsiso + speech-filler stopwords custom (ă, ăă, hm, mhm, muzică, aplauze).

3 proiecții: - overall (1.062 docs) — toate sursele - scris (724 docs) — doar Facebook - vorbit (338 docs) — doar video

7 · Statistici de bază + TF-IDF

Wordclouds, top 30 cuvinte, TF-IDF temporal pe 6 perioade (precampanie → criză Q2 2026).

8 · BERTopic

Multilingual-e5-large fp16 (rulat pe Colab T4 GPU) + UMAP (5-dim, cosinus) + HDBSCAN (min_cluster_size=8) + c-TF-IDF.

26 topice descoperite în overall (+ 7 scris + 8 vorbit).

9 · Promise Tracker

Sub-pipeline cu 4 etape:

  1. Extract cu Gemini din 319 docs campanie → 150 promisiuni
  2. Dedup cu paraphrase-multilingual-mpnet cosinus (prag 0,85) → 131 unice
  3. Match cu top-8 paragrafe relevante din mandat
  4. Classify cu LLM în {KEPT, IN_PROGRESS, REFRAMED, ABANDONED, CONTRADICTED, NO_MENTION}

Audit manual stratificat (5 per status × 2 seed-uri = 32 sample-uri): 75% spot-on, 9% greșite (toate “pre-mandate fulfilled” — eroare metodologică sistematică).

Fact-check web independent pe 10 promisiuni selectate pentru vizibilitate (5 PMB + 5 policy internă): 6 din 10 au fost executate concret prin acțiuni guvernamentale (decrete, promulgări, numiri) chiar dacă clasificatorul ND-centric le-a marcat NO_MENTION/IN_PROGRESS. Pattern documentat — nu extrapolabil exact pe toate 131 fără verificare sistematică.

10 · Analize avansate

5 analize SotA rulate pe toate 3 proiecții:

# Analiză Tehnică
5 Discourse complexity spaCy parser → sentence length, tree depth, MTLD, TTR
6 Ezitare / nuanțare în limbaj (hedging) Lexicon RO custom 35+ markeri nuanțare, 24 certitudine
7 Semantic drift mpnet centroid per (topic × perioadă), cosine drift
8 NER entități GLiNER multi-v2.1 zero-shot
9 Sentiment per entitate Gemini 2.5 Flash pe paragrafe relevante

11 · Raportul ND-Bolojan

Extract Bolojan/premier/prim-ministru + Ciolacu cu pas de disambiguare LLM (scripts/utils_entity_disambiguation.py): 119 candidați → 45 spans validate (74 false positives eliminate prin Gemini “este paragraful efectiv despre Bolojan?”) + 33 spans Ciolacu. Clasificare Gemini ton relațional per perioadă. Fact-check web pe toate 6 perioade complementar.

Audit de control pe entități non-Bolojan (TRUMP, PSD, SIMION, 41 passages): 0% false positive rate — GLiNER-based matching e robust; doar regex-ul pe termeni generici (premier/prim-ministru) avea bug.

Limitări recunoscute

1. Clasificatorul de promisiuni măsoară doar discursul

Nu măsoară acțiuni reale. Pentru proiectele PMB (executate de succesori), e blind. Pentru acțiuni prezidențiale concrete (decrete, promulgări), e fidel.

Mitigat prin fact-check web independent.

2. Pre-mandate fulfilled promises

Clasificatorul caută evidence în corpus mandat. Promisiuni îndeplinite înainte (ex: “voi candida independent”) nu mai apar în mandat → fals NO_MENTION.

Eroare sistematică izolată la topic justiție electorală. Nu schimbă concluziile narative.

3. NER nu prinde “premier” fără nume — necesită disambiguare LLM

GLiNER ratează mențiuni unde Bolojan e numit doar “premierul” (fără nume). Soluție: regex pattern dedicat + pas de disambiguare LLM (scripts/utils_entity_disambiguation.py) — fiindcă regex-ul simplu pe “premier” capta și paragrafe despre Ciolacu, Ponta (2014), Tusk. Rezultat: 119 candidați → 45 validate (74 false positives eliminate, 62% rate).

4. Sentiment LLM averageiază

Pe perioade lungi cu mențiuni diverse, sentimentul detectat e o medie. Q3 2025 a apărut “colaborativ” deși conținea și dezacord TVA — surface pozitivul dominant.

Mitigat prin fact-check web.

Costuri reale

Etapă Cost
Apify scraping FB ~$10
Webshare proxy (lună) $3
Gemini API (toate clasificările + diarize + extract promisiuni) ~$0,50 total
Colab T4 (BERTopic) gratis
GLiNER local (NER) gratis
Storage GitHub gratis

Total proiect: ~$15 + ~50 ore muncă.

Reproducibilitate

Toate scripturile au:

  • Argumente CLI documentate
  • Cache deterministic (acelaiași prompt = acelaiași output cu temperature=0.1)
  • Audit trail (toate clasificările LLM salvate ca JSONL)
  • Tests pentru funcțiile critice

Pentru a reproduce de la zero:

git clone https://github.com/radusqrt/evolutie-discurs-nicusor-dan
cd evolutie-discurs-nicusor-dan
pip install -r requirements.txt
cp .env.example .env  # adaugă cheile API
python scripts/list_candidates.py
python scripts/fetch_from_candidates.py
python scripts/fb_apify_collect.py
python scripts/03_dedupe.py
python scripts/04d_diarize_llm_v2.py
python scripts/05_project.py
# ... etc

Total wall-clock pentru o rulare completă: ~6-8 ore (majoritar Gemini calls + Colab BERTopic).

← Înapoi acasă

Back to top