Д3.шкала времени js: красиво разнесенные тики через минутные интервалы, когда данные находятся в секундах?

Я работаю с временной шкалой D3. Мои входные данные находятся в секундах, и это данные о продолжительности, а не даты - так 10 секунд, 30 секунд и т. д.

Я хочу создать ось, которая позволяет мне делать следующее:

  • отображение тиков, отформатированных в минутах и секундах: например, "0m 30s", "1m 00s" и т. д. Это форматирование само по себе довольно просто, но не тогда, когда мне это нужно...
  • отображение тиков с интервалами, которые выглядят аккуратно при форматировании в минутах. Если я просто использую форматирование ТИКов по умолчанию D3, то я получаю тики с интервалами, которые имеют смысл в минутах, но не секундах.

вот мой код:

var values = [100,200,300....]; // values in seconds
var formatCount = d3.format(",.0f"),
    formatTime = d3.time.format("%Mm %Ss"),
    formatMinutes = function(d) { 
        var t = new Date(2012, 0, 1, 0, 0, d);
        t.setSeconds(t.getSeconds() + d);
        return formatTime(t); 
    };
var x = d3.scale.linear()
    .domain([0, d3.max(values)])
    .range([0, width]);
var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom")
    .tickFormat(formatMinutes);

Это дает мне красиво отформатированные тики с нерегулярными интервалами:" 16m 40s"," 33m 20s " и т. д. Как я могу генерировать Тики на "10m 00s", "20m 00s" и т. д.?

очевидным ответом было бы преобразовать массив значений в минуты, использовать линейный масштаб и написать форматер для его обработки, но я бы предпочел использовать шкала времени, если это возможно

вот JSFiddle, чтобы продемонстрировать проблему:http://jsfiddle.net/83Xmf/

1 ответов


обычно при создании шкалы времени вы бы использовали d3.time.scale(), а не линейный масштаб.

ваш случай немного странный в том, что вы используете abstract продолжительностью времени, а не конкретных точки во время для ваших данных. К сожалению, кажется, что встроенная во времени функциональность d3 не подходит для этого случая. Есть несколько вариантов, которые я могу придумать для обходных путей:


Вариант 1: Использовать a линейная шкала с ручным .tickValues()

вместо форматирования тиков с помощью

formatMinutes = function(d) { 
    var hours = Math.floor(d / 3600),
        minutes = Math.floor((d - (hours * 3600)) / 60),
        seconds = d - (minutes * 60);
    var output = seconds + 's';
    if (minutes) {
        output = minutes + 'm ' + output;
    }
    if (hours) {
        output = hours + 'h ' + output;
    }
    return output;
};

в основном, это занимает общее количество секунд, создает час за каждые 3600 секунд, создает минуту за каждые оставшиеся 60 секунд и, наконец, возвращает оставшиеся секунды. Затем он выводит строковое представление, для пример: 17s или 12m 42s или 4h 8m 22s.

затем, когда вы делаете свою ось, вы можете использовать .tickValues() метод, чтобы назначить диапазон от нуля до максимального значения ваших данных, идя шагами 600, так как есть 600 секунд в 10 минутах. Это выглядело бы так:

var x = d3.scale.linear()
  .domain([0, d3.max(values)])
  .range([0, width]);

var xAxis = d3.svg.axis()
  .scale(x)
  .orient("bottom")
  .tickFormat(formatMinutes)
  .tickValues(d3.range(0, d3.max(values), 600));

здесь JSFiddle выходной.


Вариант 2: Используйте шкалу времени с фиксированной продолжительностью для .ticks()

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

var xAxis = d3.svg.axis()
  .scale(x)
  .orient("bottom")
  .ticks(d3.time.minute, 10)

для этого необходимо сначала настроить шкалу времени. Для области вашего масштаба вы можете использовать диапазон миллисекундных значений, так как d3 превратит их в Date объекты. В этом случае, поскольку ваши данные находятся в секундах, мы можем просто умножить на 1000, чтобы получить миллисекунды. В этом деле мы округлим максимальное значение до ближайшей миллисекунды, так как это должно быть целое число, чтобы сделать допустимую дату:

var x = d3.time.scale()
  .domain([0, Math.ceil(d3.max(values) * 1000)])
  .range([0, width]);

наконец, вы можете передать свой формат непосредственно на ось, используя .tickFormat():

var xAxis = d3.svg.axis()
  .scale(x)
  .orient("bottom")
  .ticks(d3.time.minute, 10)
  .tickFormat(d3.time.format('%Mm %Ss'));
.tickFormat чтобы показать часы, а также:
.tickFormat(d3.time.format('%Hh %Mm %Ss'));

есть взгляните на JSFiddle о том, каким будет результат...

в зависимости от того, где вы находитесь в мире, вы получите разные значения для часов, место. Я на восточном побережье США, поэтому мое место часов говорит 19. - Откуда это? Разве не должно быть ноль?

ну, к сожалению, когда мы сделали домен масштаба от 0 до количества миллисекунд наибольшего значения данных, он создал regular Date объекты, используя эти значения для миллисекундного ввода. Это означает, что они представляют собой количество миллисекунд с полуночного времени UTC 1 января 1970 года. Здесь, в восточном часовом поясе США, это означает, что это было 19:00:00 31 декабря 1969 года. Вот откуда 19 взялось, или что-то еще сделать.

если вы знаете, что все ваши данные будут меньше 1 часа, то, возможно, вы можете просто игнорировать это. Если вам нужно использовать часов, вы можете работать вокруг этого принуждение d3 использовать время UTC для форматирования оси с помощью d3.time.format.utc():

.tickFormat(d3.time.format.utc('%Hh %Mm %Ss'))

здесь JSFiddle обновлено для использования UTC.

теперь вы можете видеть, что час равен 0, как и ожидалось.

конечно, если какие-либо из ваших данных когда-либо длиннее 24 часов, этот метод не будет работать вообще, и вам придется прибегнуть к выполнению оси вручную, как в 1.


надеюсь, это поможет, по крайней мере, получить вы начали, это сложная проблема, и, похоже, нет элегантного решения, встроенного в библиотеку для обработки этого. Возможно, это сделало бы хороший запрос функции на Git-РЕПО d3. Я хотел бы услышать, Есть ли у @mbostock какие-либо предложения о том, как обрабатывать абстрактные длительности времени в d3 без привязки к Date объекты, которые требуют ссылок на абсолютные точки во времени.