byYear =Array.from( d3.rollup(topicFiltered, v => d3.sum(v, d => d.count), d => d.year), ([year, count]) => ({year, count})).sort((a, b) => a.year- b.year)Plot.plot({height:280,marginLeft:50,x: {label:"Year",tickFormat: d => d.toString()},y: {label:"Articles",grid:true},marks: [ Plot.areaY(byYear, {x:"year",y:"count",fill:"#1f77b4",fillOpacity:0.25}), Plot.lineY(byYear, {x:"year",y:"count",stroke:"#1f77b4",strokeWidth:1.6}), Plot.ruleY([0]) ]})
Topics within the filter
Code
byTopic =Array.from( d3.rollup(topicFiltered, v => d3.sum(v, d => d.count), d => d.topic), ([topic, count]) => ({topic, count})).sort((a, b) => b.count- a.count)Plot.plot({height:360,marginLeft:180,x: {label:"Articles",grid:true},y: {label:null,domain: byTopic.map(d => d.topic)},marks: [ Plot.barX(byTopic, {y:"topic",x:"count",fill:"#1f77b4"}), Plot.text(byTopic, {y:"topic",x:"count",text: d => d.count.toLocaleString(),dx:4,textAnchor:"start"}), Plot.ruleX([0]) ]})
Top-50 authors within the filter
If you’ve picked one author, only that author’s bar shows.
Code
byAuthor =Array.from( d3.rollup(authorFiltered, v => d3.sum(v, d => d.count), d => d.author_name), ([author, count]) => ({author, count})).filter(d => d.count>0).sort((a, b) => b.count- a.count).slice(0,25)Plot.plot({height:360,marginLeft:180,x: {label:"Articles",grid:true},y: {label:null,domain: byAuthor.map(d => d.author)},marks: [ Plot.barX(byAuthor, {y:"author",x:"count",fill:"#1f77b4"}), Plot.text(byAuthor, {y:"author",x:"count",text: d => d.count.toLocaleString(),dx:4,textAnchor:"start"}), Plot.ruleX([0]) ]})
What’s filterable, what isn’t
Year, Topic, and Author filters compose. Counts in the year and topic panels respect year + topic; the author panel respects year + author (it isn’t constrained by topic, because per-topic author counts aren’t pre-computed in this prototype).
Author filtering only knows about the 50 most-published authors.
Move to the Browse page for free-text search across every article.