![]() |
| Can't wait to squash another bug. |
Working with D3 is one of my favorite pasttimes, especially when I have a new dataset to play with. However, there are some gotchas, and today's episode of Spot The Bug is a story of how one of these gotchas, well, got me.
I had some data that I wanted to plot a chart for. I truncated it a bit for brevity, but it will serve to illustrate the point. Note in particular, the column val.
datavals.csv
date,val
2024-11-01,8
2024-11-02,2
2024-11-03,17
2024-11-04,11
2024-11-05,25
2024-11-06,4
2024-11-07,7
2024-11-08,3
2024-11-09,8
2024-11-10,15
2024-11-01,8
2024-11-02,2
2024-11-03,17
2024-11-04,11
2024-11-05,25
2024-11-06,4
2024-11-07,7
2024-11-08,3
2024-11-09,8
2024-11-10,15
Here's the code I wrote to create an y-axis
<!DOCTYPE html>
<html>
<head>
<title>Chart</title>
<style>
svg
{
width: 800px;
height: 500px;
}
.scaleTick, .scale
{
stroke: rgba(100, 100, 100, 1);
stroke-width: 1px;
}
.scaleText
{
font: 8px verdana;
fill: rgba(100, 100, 100, 1);
text-anchor: end;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<svg>
</svg>
<script>
d3.csv("datavals.csv", function(data)
{
var dataVals = [];
for (var i = 0; i < data.length; i++)
{
dataVals.push(data[i].val);
}
var maxVal = d3.max(dataVals);
var chart = d3.select("svg");
chart.html("");
chart
.append("line")
.attr("class", "scale")
.attr("x1", "50px")
.attr("y1", "50px")
.attr("x2", "50px")
.attr("y2", "450px");
var pxPerUnit = Math.floor(400 / maxVal);
var scale = [];
for (var i = 450; i >= 50; i -= pxPerUnit)
{
scale.push(i);
}
chart.selectAll("line.scaleTick")
.data(scale)
.enter()
.append("line")
.attr("class", "scaleTick")
.attr("x1", "40px")
.attr("y1", function(d)
{
return d + "px";
})
.attr("x2", "50px")
.attr("y2", function(d)
{
return d + "px";
});
chart.selectAll("text.scaleText")
.data(scale)
.enter()
.append("text")
.attr("class", "scaleText")
.attr("x", "30px")
.attr("y", function(d)
{
return d + "px";
})
.text(
function(d, i)
{
return i;
});
});
</script>
</body>
</html>
<html>
<head>
<title>Chart</title>
<style>
svg
{
width: 800px;
height: 500px;
}
.scaleTick, .scale
{
stroke: rgba(100, 100, 100, 1);
stroke-width: 1px;
}
.scaleText
{
font: 8px verdana;
fill: rgba(100, 100, 100, 1);
text-anchor: end;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<svg>
</svg>
<script>
d3.csv("datavals.csv", function(data)
{
var dataVals = [];
for (var i = 0; i < data.length; i++)
{
dataVals.push(data[i].val);
}
var maxVal = d3.max(dataVals);
var chart = d3.select("svg");
chart.html("");
chart
.append("line")
.attr("class", "scale")
.attr("x1", "50px")
.attr("y1", "50px")
.attr("x2", "50px")
.attr("y2", "450px");
var pxPerUnit = Math.floor(400 / maxVal);
var scale = [];
for (var i = 450; i >= 50; i -= pxPerUnit)
{
scale.push(i);
}
chart.selectAll("line.scaleTick")
.data(scale)
.enter()
.append("line")
.attr("class", "scaleTick")
.attr("x1", "40px")
.attr("y1", function(d)
{
return d + "px";
})
.attr("x2", "50px")
.attr("y2", function(d)
{
return d + "px";
});
chart.selectAll("text.scaleText")
.data(scale)
.enter()
.append("text")
.attr("class", "scaleText")
.attr("x", "30px")
.attr("y", function(d)
{
return d + "px";
})
.text(
function(d, i)
{
return i;
});
});
</script>
</body>
</html>
Here, I got a scale. However, something was wrong. The largest value in my dataset was 25. Why was the largest value here 8?
What Went Wrong
My first clue was here, near the top of the script where I declared maxVal. Because according to this line, max() was returning 8 as the largest value.var maxVal = d3.max(dataVals);
This would explain why the scale started with the value 8 and worked its way down from there.
Why It Went Wrong
If the largest value in the dataVals array was 25 and the script thought the largest value was 8, there was one explanation for that. The script was comparing alphabetically rather than numerically. As text, "8" is always larger than "25". Basically, the dataVals array was being filled with strings rather than integers from the CSV file.var dataVals = [];
for (var i = 0; i < data.length; i++)
{
dataVals.push(data[i].val);
}
for (var i = 0; i < data.length; i++)
{
dataVals.push(data[i].val);
}
How I Fixed It
I used the parseInt() function to convert the data before pushing it into the dataVals array.var dataVals = [];
for (var i = 0; i < data.length; i++)
{
dataVals.push(parseInt(data[i].val));
}
for (var i = 0; i < data.length; i++)
{
dataVals.push(parseInt(data[i].val));
}
And now it took 25 as the largest value! Because now dataVals was filled with integers rather than text, and it was comparing accordingly.
Moral of the Story
You'd think I would have learned to sanitize these things by now, especially in JavaScript, or any loosely-typed language. Apparently not!Stay int-tellectual,
T___T
T___T



No comments:
Post a Comment