2017-02-18 7 views
0

У меня есть две серии шкал, одна линейная, а другая - группа, как я могу заставить их соответствовать, если в данных есть некоторые колпачки.Как совместить шкалу scaleBand с scaleLinear в D3.js

При необходимости ознакомьтесь с примером. Мышь, и вы видите, что поля не совпадают с разрывами линии.

ответ

1

Если вы хотите, чтобы ваш scaleBand был расширен (расширен), где не хватает данных , Я не думаю, что scaleBand является правильным способом для этого, но неясно, что это то, что вы хотите. Диапазонные шкалы предназначены для обеспечения равного расстояния для каждого значения данных и наличия всех значений - это порядковый масштаб.


Предполагая, что вы хотите только масштаб группы, чтобы привести в соответствии с вашими данными, где он присутствует:

При входе в домены каждого из й шкал (scaleBand и scaleLinear) мы находим, что scaleBand имеет домен:

[ "1", "2", "8", "9", "13", "14", "20", "22" ] // 8 elements 

И в scaleLinear есть домен:

[ 1, 22 ] // a span of 22 'elements' 

scaleBand будет нуждаться в эквивалентном домене scaleLinear. Вы можете сделать это статически (что я покажу в основном, чтобы продемонстрировать, как d3.диапазон будет работать):

let xBand = d3.scaleBand() 
    .domain(d3.range(1,23)) 
    .rangeRound([0, width]); 

Это на самом деле производит домен, который имеет 22 элементов с 1 по 22

или динамически:

let xBand = d3.scaleBand() 
    .domain(d3.range(d3.min(testData1, d => d[0], 
        d3.max(testData1, d => d[0]+1))) 

Вы можете сделать это другими способами, но d3.range()function is nice and easy.

Однако есть еще одна проблема, которая остается, это выравнивание тиков между двумя шкалами. Для линейной шкалы галочка для первого значения (1) находится на оси y, но шкала зазора зоны запускает (и не центрируется) по оси y и заполняет промежуток между 1 и 2. Другими словами , центральная точка полосы не выравнивается вертикально с вершинами линейного графа.

Эту проблему можно решить путем добавления 0,5 к обеим нижним и верхним пределами области линейной шкалы в:

let xDomain = [ 
    d3.min(testData1, d => d[0]-0.5), 
    d3.max(testData1, d => d[0]+0.5) 
]; 

Я udpated ваш codepen с соответствующими изменениями: codepen.

И в том случае, что исчезает, вот фрагмент (мышь над не работает для меня почему-то в этом фрагменте, он делает в codepen)

let width = 1000; 
 
let height = 300; 
 

 
let svg = d3.select(".wrapper-area-simple").append("svg") 
 
    .attr("width", width + 80) 
 
    .attr("height", height + 80) 
 
    .append('svg:g') 
 
    .attr('transform', 'translate(40, 30)'); 
 

 
let testData1 = [ 
 
    [ 1, 10], 
 
    [ 2, 30], 
 
    [ 8, 34], 
 
    [ 9, 26], 
 
    [13, 37], 
 
    [14, 12], 
 
    [20, 23], 
 
    [22, 16], 
 
]; 
 

 
let xDomain = [ 
 
    d3.min(testData1, d => d[0]-0.5), 
 
    d3.max(testData1, d => d[0]+0.5) 
 
]; 
 

 

 

 
let x = d3.scaleLinear() 
 
    .rangeRound([0, width]) 
 
    .domain(xDomain); 
 

 
let y = d3.scaleLinear() 
 
    .range([height, 0]) 
 
    .domain(d3.extent(testData1, d => d[1])); 
 

 
let line = d3.line() 
 
    .x(d => x(d[0])) 
 
    .y(d => y(d[1])); 
 

 
svg.append('svg:g') 
 
    .datum(testData1) 
 
    .append('svg:path') 
 
    .attr('d', line) 
 
    .attr('fill', 'none') 
 
    .attr('stroke', '#000'); 
 

 
let xAxis = d3.axisBottom(x) 
 
    .ticks(testData1.length); 
 
svg.append('svg:g') 
 
    .call(xAxis) 
 
    .attr('transform', `translate(0, 300)`); 
 
    
 
let xBand = d3.scaleBand() 
 
    .domain(d3.range(d3.min(testData1, d => d[0]), 
 
        d3.max(testData1, d => d[0]+1) 
 
        )) 
 
    .rangeRound([0, width]); 
 

 
svg.append('svg:g') 
 
    .selectAll('rect') 
 
    .data(testData1) 
 
    .enter() 
 
    .append('svg:rect') 
 
    .attr('x', d => xBand(d[0])) 
 
    .attr('width', xBand.bandwidth()) 
 
    .attr('height', height) 
 
    .attr('fill', '#000') 
 
    .on('mouseover', function() { 
 
    d3.select(this).classed('over', true); 
 
    }) 
 
    .on('mouseout', function() { 
 
    d3.select(this).classed('over', false); 
 
    });
svg { 
 
    border: 1px solid red; 
 
} 
 
rect { 
 
    opacity: .1; 
 
} 
 
rect.over { 
 
    opacity: .2; 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"> </script> 
 

 
<div class="wrapper-area-simple"></div>

+0

Мышь ** работает **, проблема в CSS. Удалите Sass. –

+0

Ах, по телефону сейчас, я буду обновлять завтра, спасибо за подсказку –

+0

Хороший ответ, именно то, что я хотел. Большое спасибо за ответ. – PRAISER

2

Ну, плохие новости для вас: они будут никогда не совпадают (в вашем случае). Давайте посмотрим, почему.

Это ваши данные:

let testData1 = [ 
    [1, 10], 
    [2, 30], 
    [8, 34], 
    [9, 26], 
    [13, 37], 
    [14, 12], 
    [20, 23], 
    [22, 16], 
]; 

Как вы можете видеть, относительно координаты х, линия скачки от 1 до 2, а затем от 2 до 8, от 8 до 9, а затем из 9 до 13 ... То есть интервалы интервала x не являются регулярными, равномерно распределенными. Все идет нормально.

Однако, когда вы передаете те же данные в масштабе диапазона, это то, что он делает: он делит диапазон ([0, width], который является в основном шириной) на testData1.length, то есть он делит диапазон на 8 и создает 8 равным интервалов. Это ваши группы, и это ожидаемое поведение диапазона шкалы. Из documentation:

значения дискретного выхода автоматически вычисляется по шкале путем деления непрерывного диапазона в равномерную полос. (Курсив мой)

Одно из решений здесь просто использует другую линейную шкалу:

let xBand = d3.scaleLinear() 
    .domain(xDomain) 
    .rangeRound([0, width]); 

И это математике по ширине прямоугольников:

.attr('width', (d,i) => testData1[i+1] ? xBand(testData1[i+1][0]) - xBand(d[0]) : 0) 

Вот ваш обновленный Codepen: http://codepen.io/anon/pen/MJdGyY?editors=0010

+1

При написании моего ответа я ожидал, что решение по этим линиям появится у вас, поэтому я оставил его в покое и пошел на другой подход. Хороший ответ, кстати. –

+1

Определенно я выбрал неправильный подход, думая, что они совпадают, вы совершенно правы, описание ясное. С другой стороны, мне нравится то, что вы сделали с шириной ящиков, и узнали от него. Но я забыл упомянуть, что хочу сохранить ширину одинаковой! Спасибо за ваш ответ. – PRAISER