[Django]-D3 bar chart returning height of "NaN"

3👍

You’re passing an array of arrays as data but the D3 code expects an array of objects with specific keys.
I tried to convert the data format into an array of objects.

You need to update your code slightly to achieve this. Check the code below:

const rawData = JSON.parse('{{ assign_levels|safe }}');
const data = rawData.map(d => ({level_assign: d[0], count: d[1]}));

This will convert your data to this format instead of a list of arrays.

[
  {"level_assign": "Class", "count": 36},
  {"level_assign": "School - cluster", "count": 6},
  {"level_assign": "Individual", "count": 20},
  {"level_assign": "N", "count": 1},
  {"level_assign": "School - multi-site", "count": 9},
  {"level_assign": "Not provided/ not available", "count": 4},
  {"level_assign": "Region or district", "count": 1}
]

Here is an example with full code:

//script.js
const rawData = [
    ["Class", 36],
    ["School - cluster", 6],
    ["Individual", 20],
    ["N", 1],
    ["School - multi-site", 9],
    ["Not provided/ not available", 4],
    ["Region or district", 1],
];

const data = rawData.map((d) => ({
    level_assign: d[0],
    count: d[1]
}));

const margin = {
    top: 10,
    right: 30,
    bottom: 140,
    left: 60
};
const width = 400 - margin.left - margin.right;
const height = 500 - margin.top - margin.bottom;

const svg = d3
    .select("#chart-container")
    .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", `translate(${margin.left}, ${margin.top})`);

const x = d3
    .scaleBand()
    .range([0, width])
    .domain(data.map((d) => d.level_assign))
    .padding(0.3);

const y = d3
    .scaleLinear()
    .range([height, 0])
    .domain([0, d3.max(data, (d) => d.count)]);

svg.append("g").call(d3.axisLeft(y));

svg.selectAll(".bar")
    .data(data)
    .enter()
    .append("rect")
    .attr("class", "bar")
    .attr("x", (d) => x(d.level_assign))
    .attr("width", x.bandwidth())
    .attr("y", height)
    .attr("height", 0)
    .attr("fill", "#5F9EA0")
    .transition()
    .duration(1000)
    .delay((d, i) => i * 100)
    .attr("y", (d) => y(d.count))
    .attr("height", (d) => height - y(d.count));

svg.append("g")
    .attr("transform", `translate(0, ${height})`)
    .call(d3.axisBottom(x))
    .selectAll("text")
    .attr("transform", "rotate(-45)")
    .style("text-anchor", "end")
    .attr("dx", "-.8em")
    .attr("dy", ".15em");

svg.append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 0 - margin.left)
    .attr("x", 0 - (height / 2))
    .attr("dy", "1em")
    .style("text-anchor", "middle")
    .text("Study Count");

svg.append("text")
    .attr("x", (width + margin.left + margin.right) / 2)
    .attr("y", margin.top / 2)
    .attr("text-anchor", "middle")
    .style("font-size", "16px")
    .text("Level of Assignment");
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>D3 Bar Chart</title>
</head>
<body>
    <div id="chart-container"></div>

    <script src="https://d3js.org/d3.v7.min.js"></script>
    <script src="script.js"></script>
</body>
</html>

1👍

The Python pandas dataframe returns NaN for empty results (short for “Not a Number”). You can change it by using the fillna method on your dataframe:

df = df.fillna('')

This should solve the problem downstream for display issues, but you’ll have to dig a bit deeper to find why it is being passed for height.

Leave a comment