如何切割y轴以使图表看起来更好? [英] How to cut y axis to make the chart look better?

查看:109
本文介绍了如何切割y轴以使图表看起来更好?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想做的是使用D3.js(v4)创建一个条形图,该条形图将显示2或3个值相差很大的条形图.

What I'm trying to do is create a bar chart using D3.js (v4) that will show 2 or 3 bars that have a big difference in values.

与黄色条下面的图片所示的值一样,值为1596.6,而绿色条的值为177.2.因此,为了以优雅的方式显示图表,决定将y轴切割为某个值,该值将接近绿条的值,并继续接近黄条的值.

Like shown on the picture below yellow bar has a value 1596.6, whereas the green bar has only 177.2. So in order to show the charts in elegant way it was decided to cut the y axis at a certain value which would be close to green bar's value and continue closer to yellow bar's value.

在图片上,y轴在500之后被切割,并在1500之后继续.

On the picture the y axis is cut after 500 and continues after 1500.

如何使用D3.js做到这一点?

How one would do that using D3.js?

推荐答案

从技术上讲,您想要的是 y轴损坏.

What you want is, technically speaking, a broken y axis.

只要您明确地告诉用户您的y轴已损坏,它便是数据可视化中的有效表示形式(尽管有些人对此不屑一顾).可以通过多种方式直观地指示它,例如:

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:

但是,请不要忘记在图表文字或图例中清楚地说明这一点.除此之外,请记住,为了以优雅的方式显示图表" 应该从不成为此处的目标:我们不会使图表显得优雅(但如果是这样的话,那就太好了),我们制作图表向用户阐明信息或模式.而且,在某些非常有限的情况下,断开y轴可以更好地显示信息.

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.

回到您的问题:

如您在问题中所述,在值上存在较大差异时,中断y轴非常有用.例如,看一下我制作的这个简单演示:

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>

如您所见,bazfoobar条形图与其他条形图相比显得相形见since,因为它们之间的差异非常大.

As you can see, the baz and foobar bars are dwarfed by the other bars, since their differences are very big.

我认为,最简单的解决方案是在y标度的范围和域中添加中点.因此,在上面的代码段中,这是y比例尺:

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.

这是结果:

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>

如您所见,我必须使用tickValues设置y轴刻度,否则会一团糟.

As you can see, I had to set the y axis ticks using tickValues, otherwise it'd be a mess.

最后,我不能对此施加足够的压力:不要忘记显示y轴已损坏.在上面的代码段中,我在y轴的数字15和数字700之间放置了符号(请参见答案中的第一个数字).顺便说一句,值得注意的是,在您共享的图中,作者没有在y轴上放置任何符号,这不是一个好习惯.

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.

这篇关于如何切割y轴以使图表看起来更好?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆