How PolitiLens measures, scores, and presents political news coverage
Each outlet is placed on a 2D political compass with two axes:
Positions are manually curated editorial estimates informed by established media research (AllSides, Ad Fontes Media, and others). They are not algorithmic outputs or objective measurements. No two researchers will agree perfectly on outlet placement, and outlets shift over time. Treat these as approximate starting points, not ground truth.
Articles from all outlets are grouped into story clusters — each cluster represents articles covering the same event or topic from different sources.
Grouping uses Jaccard similarity on the tokenized text of each article's title and summary:
similarity = |tokens_A ∩ tokens_B| / |tokens_A ∪ tokens_B| threshold: ≥ 0.22 → same story minimum: 2 articles per cluster stop words: ~100 common words excluded token min: 4 characters
Clusters are sorted by outlet count × divergence score, then limited to the top 25 for performance. Named entities (people, places, organizations) are extracted from each cluster using the compromise NLP library.
The divergence score (0–100) measures how widely the political spectrum covers a story. A high score means the story is being covered across different political leans and/or regions — not that any outlet is wrong or misleading.
divergence = (unique_outlets / 5) × 60 ← breadth (max 60)
+ 40 if both left AND right outlets present ← polarity bonus
+ 10 if ≥ 2 regions covered ← regional bonus
ranges:
0–29 low → mostly one side covering it
30–54 moderate → some cross-spectrum coverage
55–74 high → significant left/right difference
75–100 extreme → polar opposite coverageSentiment is computed with the AFINN lexicon (via the sentiment npm package). Each word in an article's title and summary is looked up in a list of ~3,500 English words with pre-assigned scores from −5 (very negative) to +5 (very positive).
Scores are averaged across all articles in a cluster to produce a cluster-level tone. The tone label is determined by the comparative score (total ÷ word count):
comparative > 0.05 → positive comparative < -0.05 → negative otherwise → neutral
When you click "Analyze Framing" on a story, PolitiLens sends up to 6 article titles and summaries to LLaMA 3.3-70B (via OpenRouter's free tier) with the following system prompt:
Analyze how different political outlets are covering this story. Be specific about framing differences and loaded language. Do not editorialize — describe factually how each side is presenting this.
The model returns a structured JSON object (validated with Zod) containing:
The temperature gauge shown in the Daily Briefing is a composite of two signals:
temperature = (avg_divergence × 0.6) + (min(sentiment_variance × 50, 100) × 0.4) labels: ≥ 75 → Volatile ≥ 55 → Hot ≥ 35 → Warm < 35 → Cold
Sentiment variance measures how much article tones differ from each other across the current news cycle. High variance means some outlets are very positive while others are very negative — a sign of polarized framing.