score:3
What you want is, technically speaking, a broken y axis.
It's a valid representation in data visualization (despite being frowned upon by some people), as long as you explicitly tell the user that your y axis is broken. There are several ways to visually indicate it, like these:
However, don't forget to make this clear in the text of the chart or in its legend. Besides that, have in mind that "in order to show the charts in elegant way" should never be the goal here: we don't make charts to be elegant (but it's good if they are), we make charts to clarify an information or a pattern to the user. And, in some very limited situations, breaking the y axis can better show the information.
Back to your question:
Breaking the y axis is useful when you have, like you said in your question, big differences in values. For instance, have a look at this simple demo I made:
var w = 500,
h = 220,
marginLeft = 30,
marginBottom = 30;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var data = [{
name: "foo",
value: 800
}, {
name: "bar",
value: 720
}, {
name: "baz",
value: 10
}, {
name: "foobar",
value: 17
}, {
name: "foobaz",
value: 840
}];
var xScale = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([marginLeft, w])
.padding(.5);
var yScale = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.value
})])
.range([h - marginBottom, 0]);
var bars = svg.selectAll(null)
.data(data)
.enter()
.append("rect")
.attr("x", function(d) {
return xScale(d.name)
})
.attr("width", xScale.bandwidth())
.attr("y", function(d) {
return yScale(d.value)
})
.attr("height", function(d) {
return h - marginBottom - yScale(d.value)
})
.style("fill", "teal");
var xAxis = d3.axisBottom(xScale)(svg.append("g").attr("transform", "translate(0," + (h - marginBottom) + ")"));
var yAxis = d3.axisLeft(yScale)(svg.append("g").attr("transform", "translate(" + marginLeft + ",0)"));
<script src="https://d3js.org/d3.v4.js"></script>
As you can see, the baz
and foobar
bars are dwarfed by the other bars, since their differences are very big.
The simplest solution, in my opinion, is adding midpoints in the range and domain of the y scale. So, in the above snippet, this is the y scale:
var yScale = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.value
})])
.range([h - marginBottom, 0]);
If we add midpoints to it, it become something like:
var yScale = d3.scaleLinear()
.domain([0, 20, 700, d3.max(data, function(d) {
return d.value
})])
.range([h - marginBottom, h/2 + 2, h/2 - 2, 0]);
As you can see, there are some magic numbers here. Change them according to your needs.
By inserting the midpoints, this is the correlation between domain and range:
+--------+-------------+---------------+---------------+------------+
| | First value | Second value | Third value | Last value |
+--------+-------------+---------------+---------------+------------+
| Domain | 0 | 20 | 700 | 840 |
| | maps to... | maps to... | maps to... | maps to... |
| Range | height | heigh / 2 - 2 | heigh / 2 + 2 | 0 |
+--------+-------------+---------------+---------------+------------+
You can see that the correlation is not proportional, and that's exactly what we want.
And this is the result:
var w = 500,
h = 220,
marginLeft = 30,
marginBottom = 30;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var data = [{
name: "foo",
value: 800
}, {
name: "bar",
value: 720
}, {
name: "baz",
value: 10
}, {
name: "foobar",
value: 17
}, {
name: "foobaz",
value: 840
}];
var xScale = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([marginLeft, w])
.padding(.5);
var yScale = d3.scaleLinear()
.domain([0, 20, 700, d3.max(data, function(d) {
return d.value
})])
.range([h - marginBottom, h/2 + 2, h/2 - 2, 0]);
var bars = svg.selectAll(null)
.data(data)
.enter()
.append("rect")
.attr("x", function(d) {
return xScale(d.name)
})
.attr("width", xScale.bandwidth())
.attr("y", function(d) {
return yScale(d.value)
})
.attr("height", function(d) {
return h - marginBottom - yScale(d.value)
})
.style("fill", "teal");
var xAxis = d3.axisBottom(xScale)(svg.append("g").attr("transform", "translate(0," + (h - marginBottom) + ")"));
var yAxis = d3.axisLeft(yScale).tickValues([0, 5, 10, 15, 700, 750, 800])(svg.append("g").attr("transform", "translate(" + marginLeft + ",0)"));
svg.append("rect")
.attr("x", marginLeft - 10)
.attr("y", yScale(19.5))
.attr("height", 10)
.attr("width", 20);
svg.append("rect")
.attr("x", marginLeft - 10)
.attr("y", yScale(19))
.attr("height", 6)
.attr("width", 20)
.style("fill", "white");
<script src="https://d3js.org/d3.v4.js"></script>
As you can see, I had to set the y axis ticks using tickValues
, otherwise it'd be a mess.
Finally, I can't stress this enough: do not forget to show that the y axis is broken. In the snippet above, I put the symbol (see the first figure in my answer) between the number 15
and the number 700
in the y axis. By the way, it's worth noting that in the figure you shared its author didn't put any symbol in the y axis, which is not a good practice.
Source: stackoverflow.com
Related Query
- How to cut y axis to make the chart look better?
- how to make the x axis scrollable in a d3 line chart graph
- How can I make a bar chart starting from the 0 point of the y axis and not from the bottom of the svg?
- How to make bar chart with additional vertical lines below the x axis
- How to make axis label of stacked bar chart in the middle of ticks chart in D3?
- Make simple bar chart using C3 with separate columns on the x axis
- D3 bubble chart / pack layout - How to make bubbles radiate out from the largest bubbles to the smallest?
- How to make the NVD3 discreteBarChart labels on the X axis to adapt to the width or to the number of labels?
- dimple.js How can I change the labels of a chart axis without changing the data?
- How to modify axis labels in d3 for a stacked bar chart when the axis labels are mapped as part of the scale's domain
- How to make labels appear only when the chart is zoomed in
- D3.js v7 - How to make Y axis labels always show on screen for a scrollable chart
- How do I centre the Y axis in a D3 Tornado Chart
- D3.js how to make the lines not go inside the arc of a donut chart
- How to make the grids of a chart be in line with axes
- How to make axis "smart" and "flexible" so that the starting/end points on axis are sensible in D3 V4?
- How do I make D3 draw the axis from the start of a dataset?
- How to zoom only on the X axis in a line chart in d3.js (v7)
- How can I make the legend's similar to the stacked bar chart (and change the legend's label) in D3 v6?
- How do I make my chart bars align with the chart axis?
- d3js: How to make the bubble chart zoomable?
- How do I extend the margins of my chart so that my legend doesn't get cut off?
- How can I create a basic bar chart with unique counts on the y axis and a category on the x axis?
- How do I update the y axis of the chart using D3 js
- How to rotate the text labels for the x Axis of a nvd3.js line chart
- How to make the patterns of a pie chart parallel to the center line of sectors in d3.js?
- How can I fix my chart so that the scale of the x axis is defined by a group of labels?
- d3js - Stacked Bar Chart in Horizontal Axis - How do I set x so the bars are seperated?
- d3 Plus bar chart how to force fixed max y axis range independent of the max data value
- How to plot stacked bar chart according to the custom Y axis in D3
More Query from same tag
- d3 x-axis with one incorrect value
- D3.js - .on(mouseover) not retrieving data (force-directed-layout over a map(leaflet.js))
- Bind data to axis ticks d3.js
- How can I display two trees with a central root?
- Relation between LinkDistance and LinkStrength in D3.js Force layout
- Uncaught TypeError: n is not a function d3.js. While trying to overlay us_map on google maps
- NVD3 - Positioning of xAxis labels not working
- Save D3 svg as a high quality image - viewport case
- When reading a Json file it would not understand HTML content?
- Wrong usage of d3 scale with same values in data?
- D3 sort an array of objects on 2 object properties
- Bar graph colors based on value in c3.js
- D3 map, 'd' attribute
- Dotplot distribution (frequency count)
- Axes not showing in D3 diagram
- what does the equation within d3.nest().entries() do?
- Formatting y-axis
- D3,js path data string - should I parse it or there is an API for it?
- D3 Pie chart arc partitions
- Multiline chart x-axis ticks not aligned with data points
- Uncaught TypeError: Cannot read property 'length' of undefined
- Is it possible to create a treemap when any column is called "value"
- Attempting to plot the time on the x-axis of my line graph?
- Error when creating features in d3.js cartogram
- Having trouble making a bar chart bar onClick create changes to scatter chart points using D3
- How to iterate over data in JSON file using D3 javascript
- D3 pie chart labels and value inside
- D3 timeScale path get proper click location
- Modify Pie Chart code to use with array of objects
- D3 drag and mouseup events