Я создаю линейную диаграмму с d3js. Некоторые данные перекрывают друг друга. Как это:
(См. Эти 4 строки внизу). Вот как выглядят эти 4 строки после удаления остальных строк:
Я хочу, чтобы как-то удалить эти пустые вертикальные пространства, где нет данных.
Любой возможный способ сделать это?
То, что вы хотите, это сломанная ось Y. Что-то вроде этого:
Прежде чем мы продолжим, очень важно, чтобы вы сказали пользователю, что ось разбита, четко и явно. И добавьте символ на ось, показывая, что он сломан:
В противном случае вы лежите с данными!
Вернемся к вашему вопросу: чтобы создать сломанную ось Y, вам просто нужно добавить середины к вашей шкале y (как в домене, так и в диапазоне).
Давайте посмотрим на это в демо. Это базовая диаграмма, вы можете видеть, что на нижнем конце есть цепочка строк (от 0
до 12
по оси y), как и ваша диаграмма:
var w = 400, h = 300;
var data = [1, 3, 4, 7, 10, 11, 12, 160, 180, 210, 230];
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var scale = d3.scaleLinear()
.domain([0, 230])
.range([h - 4, 4]);
var colors = d3.scaleOrdinal(d3.schemeCategory10);
var lines = svg.selectAll(null)
.data(data)
.enter()
.append("line")
.attr("x1", 30)
.attr("x2", w)
.attr("y1", function(d){ return scale(d)})
.attr("y2", function(d){ return scale(d)})
.style("stroke", function(d, i){ return colors(i)})
.style("shape-rendering", "crispEdges");
var axis = d3.axisLeft(scale)(svg.append("g").attr("transform", "translate(30,0)"))
<script src="https://d3js.org/d3.v4.min.js"></script>
Очень сложно рассказать об этом здесь. Итак, позвольте сломать ось Y, создав средние точки в масштабе. В моей демонстрации я сделал это:
var scale = d3.scaleLinear()
.domain([0, 15, 150, 230])
.range([h - 4, h / 2 , h / 2, 4]);
Это означает, что все значения от 15
до 150
будут расположены в одной и той же точке шкалы.
Вот результат, используя те же данные:
var w = 500,
h = 400;
var data = [1, 3, 4, 7, 10, 11, 12, 160, 180, 210, 230];
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var scale = d3.scaleLinear()
.domain([0, 15, 150, 230])
.range([h - 4, h / 2, h / 2, 4]);
var colors = d3.scaleOrdinal(d3.schemeCategory10);
var lines = svg.selectAll(null)
.data(data)
.enter()
.append("line")
.attr("x1", 30)
.attr("x2", w)
.attr("y1", function(d) {
return scale(d)
})
.attr("y2", function(d) {
return scale(d)
})
.style("stroke", function(d, i) {
return colors(i)
})
.style("shape-rendering", "crispEdges");
var axis = d3.axisLeft(scale).tickValues([0, 5, 10, 15, 160, 180, 200, 220])(svg.append("g").attr("transform", "translate(30,0)"))
<script src="https://d3js.org/d3.v4.min.js"></script>
Конечно, я должен был установить значения тика явно.
Нелинейное масштабирование может решить вашу проблему.
Например, .scalePow или .logScale вместо.scaleLinear:
var y = d3.scalePow()
.exponent(0.3)
.domain([0, 1000, 10000, 60000])
.range([height, 0]);
См. Обновленный скрипт.