Skip to content

Instantly share code, notes, and snippets.

@jdunkerley
Last active November 14, 2018 16:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jdunkerley/a56c8b66cc958448739fc86be584b1d0 to your computer and use it in GitHub Desktop.
Save jdunkerley/a56c8b66cc958448739fc86be584b1d0 to your computer and use it in GitHub Desktop.
AlterD3 Stacked Bar Chart
<!-- ref: https://d3js.org/d3.v5.min.js -->
<!-- Input Expected: Group, Set, Value -->
<div id="ChartHtml">
<div id="chart1" style="background-color: white;">
<svg id="chartSVG" style="width:800px; height:600px;background-color: white;"></svg>
</div>
<script>
const svg = d3.select("#chartSVG")
const width = +svg.style("width").replace(/px/,"")
const height = +svg.style("height").replace(/px/,"")
const margin = { left: 30, right: 10, top: 5, bottom: 30}
// Access Data Here
const data = alteryxData()
const getGroup = d => d.Group
const getSet = d => d.Set
const getValue = d => d.Value
const totalByGroup = data.reduce((c, d) => { c[getGroup(d)] = (c[getGroup(d)] || 0) + getValue(d); return c }, {})
const groups = Object.keys(totalByGroup).sort((a,b) => -Math.sign(totalByGroup[a] - totalByGroup[b]))
const x = d3.scaleBand().domain(groups).range([margin.left, width - margin.right]).padding(0.1)
const xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).tickSizeOuter(0))
.call(g => g.selectAll(".domain").remove())
const sets = data.map(getSet).filter((v, i, a) => a.indexOf(v) === i)
const color = d3.scaleOrdinal().unknown("#ccc").domain(sets).range(d3.quantize(t => d3.interpolateSpectral(t * 0.8 + 0.1), sets.length).reverse())
const totals = groups.map(g => totalByGroup[g])
const y = d3.scaleLinear().domain([0, d3.max(totals)]).range([height - margin.bottom, margin.top])
const yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).ticks(null, "s"))
.call(g => g.selectAll(".domain").remove())
const shapedData = sets.map(k => groups.map(g => d3.sum(data.filter(d => getGroup(d) === g && getSet(d) === k).map(getValue))))
sets.forEach((s,i) => shapedData[i] = shapedData[i].map((c, j) => (i === 0 ? [0, c] : [shapedData[i-1][j][1], c + shapedData[i-1][j][1]])))
svg
.append("g")
.selectAll("g")
.data(shapedData)
.enter().append("g")
.attr("fill", (d, i) => color(sets[i]))
.selectAll("rect")
.data(d => d)
.enter().append("rect")
.attr("x", (d, i) => x(groups[i]))
.attr("y", (d) => y(d[1]))
.attr("height", d => y(d[0]) - y(d[1]))
.attr("width", x.bandwidth())
svg.append("g").call(xAxis)
svg.append("g").call(yAxis)
const legend = svg
.append("g")
.attr("transform", `translate(${width - margin.right},${margin.top})`)
.attr("font-family", "sans-serif") .attr("font-size", 10)
.attr("text-anchor", "end")
.selectAll("g")
.data(sets.slice().reverse())
.enter().append("g")
.attr("transform", (d, i) => `translate(0,${i * 20})`)
legend.append("rect").attr("x", -19).attr("width", 19).attr("height", 19).attr("fill", color)
legend.append("text").attr("x", -24).attr("y", 9.5).attr("dy", "0.35em").text(d => d)
</script>
</div>

Taking the example from D3 Stacked Bar Chart

By default the data should have a 'Group', 'Set' and 'Value' column. The 'Value' should be numeric.

Each colour represents a Group Each bar represents a Set

        const data = alteryxData()
        const getGroup = d => d.Group
        const getSet = d => d.Set
        const getValue = d => d.Value

This section controls how the data is read from the Alteryx input

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment