FEC Partisanship Data
Examining partisan dimensions of Federal Election Commission enforcement actions
Under construction. This dashboard is a design preview populated with simulated placeholder data; it does not show real records. The actual dataset will replace it when it is ready for public release. Watch this space.
filtered = data.filter(d => {
let yearOk = true;
if (yearFilter !== "All") {
const [startY, endY] = yearFilter.split("-").map(Number);
yearOk = d.year >= startY && d.year <= endY;
}
const typeOk = typeFilter === "All" || d.complaint_type === typeFilter;
const partyOk = partyFilter === "All" || d.target_party === partyFilter;
return yearOk && typeOk && partyOk;
})totalClaims = filtered.length
demPct = totalClaims > 0
? Math.round(filtered.filter(d => d.target_party === "Democratic").length / totalClaims * 100)
: 0
repPct = totalClaims > 0
? Math.round(filtered.filter(d => d.target_party === "Republican").length / totalClaims * 100)
: 0
nonPct = totalClaims > 0
? Math.round(filtered.filter(d => d.target_party === "Non-Partisan").length / totalClaims * 100)
: 0html`<div class="stat-grid">
<div class="stat-box">
<div class="stat-number">${totalClaims}</div>
<div class="stat-label">Total Claims Coded</div>
</div>
<div class="stat-box stat-blue">
<div class="stat-number">${demPct}%</div>
<div class="stat-label">Targeting Democrats</div>
</div>
<div class="stat-box stat-rose">
<div class="stat-number">${repPct}%</div>
<div class="stat-label">Targeting Republicans</div>
</div>
<div class="stat-box stat-muted">
<div class="stat-number">${nonPct}%</div>
<div class="stat-label">Non-Partisan</div>
</div>
</div>`// Time series data by party
timeData = {
const years = [...new Set(filtered.map(d => d.year))].sort();
const partyCounts = {};
for (const d of filtered) {
const key = `${d.year}-${d.target_party}`;
partyCounts[key] = (partyCounts[key] || 0) + 1;
}
const result = [];
for (const year of years) {
for (const party of ["Democratic", "Republican", "Non-Partisan"]) {
result.push({
year: year,
party: party,
count: partyCounts[`${year}-${party}`] || 0
});
}
}
return result;
}// Grouped data: complaint type by party
typePartyData = {
const types = [...new Set(filtered.map(d => d.complaint_type))].sort();
const result = [];
for (const type of types) {
for (const party of ["Democratic", "Republican", "Non-Partisan"]) {
const count = filtered.filter(d => d.complaint_type === type && d.target_party === party).length;
result.push({ complaint_type: type, party, count });
}
}
return result;
}Claims by Target Party Over Time
Plot.plot({
marginLeft: 50,
height: 300,
x: { label: "Year" },
y: { label: "Number of Claims" },
color: {
domain: ["Democratic", "Republican", "Non-Partisan"],
range: ["#5B7FA5", "#B85C5C", "#7A8290"],
legend: true
},
marks: [
Plot.line(timeData, {
x: "year",
y: "count",
stroke: "party",
strokeWidth: 2,
strokeDasharray: d => d.party === "Non-Partisan" ? "4 3" : undefined
}),
Plot.dot(timeData, {
x: "year",
y: "count",
fill: "party",
r: 3
})
]
})Claim Types by Target Party
Plot.plot({
marginLeft: 130,
height: 300,
x: { label: "Count" },
y: { label: null },
color: {
domain: ["Democratic", "Republican", "Non-Partisan"],
range: ["#5B7FA5", "#B85C5C", "#7A8290"],
legend: true
},
marks: [
Plot.barX(typePartyData, Plot.groupY(
{ x: "sum" },
{
y: "complaint_type",
x: "count",
fill: "party",
sort: { y: "-x" }
}
)),
Plot.ruleX([0])
]
})unanimousPct = totalClaims > 0
? Math.round(filtered.filter(d => d.vote_pattern === "Unanimous").length / totalClaims * 100)
: 0
splitPct = totalClaims > 0
? Math.round(filtered.filter(d => d.vote_pattern === "Party Line Split").length / totalClaims * 100)
: 0
deadlockedPct = totalClaims > 0
? Math.round(filtered.filter(d => d.vote_pattern === "Deadlocked").length / totalClaims * 100)
: 0html`<div class="vote-pattern-grid">
<div class="vote-pattern-box vote-unanimous">
<div class="pattern-pct">${unanimousPct}%</div>
<div class="pattern-label">Unanimous to Proceed</div>
</div>
<div class="vote-pattern-box vote-split">
<div class="pattern-pct">${splitPct}%</div>
<div class="pattern-label">Split Along Party Lines</div>
</div>
<div class="vote-pattern-box vote-deadlocked">
<div class="pattern-pct">${deadlockedPct}%</div>
<div class="pattern-label">Deadlocked / No Action</div>
</div>
</div>`Data Table
Inputs.table(filtered, {
columns: ["mur_number", "year", "respondent", "complaint_type", "target_party", "outcome"],
header: {
mur_number: "MUR #",
year: "Year",
respondent: "Respondent",
complaint_type: "Type",
target_party: "Target Party",
outcome: "Outcome"
},
sort: "year",
reverse: true,
rows: 20
})Still building — this dashboard is under construction. The data shown is placeholder while the coding pipeline is being finalized.