Подтвердить что ты не робот

D3.scale.category10() не ведет себя так, как ожидалось

Я сталкиваюсь с неожиданным поведением при использовании d3.scale.category10() для генерации 10 фиксированных цветов.

Запустив, я отмечаю, что colors.range() возвращает массив правильно упорядоченных цветов в соответствии с документацией.

var colors = d3.scale.category10();
console.log(colors.range());
// -> ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"] 

Мое ожидание заключается в том, что вызывающие цвета (0) всегда возвращают нулевой элемент, цвета (1) первый и т.д. Однако то, что я наблюдаю, состоит в том, что если я сначала вызываю цвета (1), нулевой элемент возвращается вместо первого с этой точки вперед. Впоследствии вызывающие цвета (0) вернут первый элемент вместо нулевого. Таким образом, кажется, что возвращаемое значение привязано к порядку, в котором используются индексы, вместо естественного порядка.

Здесь сценарий: http://jsfiddle.net/LqHst/

Чтобы обойти это, я просто перехожу через цикл, чтобы коснуться всех цветов в правильном порядке.

for(var i = 0; i < 10; i++) {
  colors(i);
}

Либо я не понимаю, как это должно работать, либо я не вижу смысла в неправильном использовании. Я использовал эту функцию раньше и помню, как встречал ожидаемое поведение, поэтому я думаю, что я просто делаю что-то неправильно или делаю неправильное предположение.

4b9b3361

Ответ 1

Вы неправильно понимаете использование category10.

Как упоминалось в документе: d3.scale.category10() строит новую порядковую шкалу с диапазоном десяти категориальных цветов.

То есть: var color = d3.scale.category10() построит новый порядковый масштаб с пустым доменом и диапазон с десятью цветами.

Когда вы используете порядковый масштаб:

Если ни один домен не задан, диапазон должен быть установлен явно. Затем каждому уникальному значению, переданному функции масштабирования, присваивается новое значение из выходного диапазона; другими словами, домен будет выведен неявным образом из использования. Хотя домены могут быть построены неявно,

https://github.com/mbostock/d3/wiki/Ordinal-Scales#wiki-ordinal_domain вы можете прочитать API порядкового масштаба для получения дополнительной информации.

Обновление: порядковый масштаб - это карта, а не массив.

Если домен не задан явным, домен будет неявным, если последовательность ключей вы вызываете цвет (ключ).

  var color = d3.scale.category10();

  console.log(color.domain()); // []

  color("aaa");
  console.log(color.domain()); // ["aaa"]

  color("bbb");
  console.log(color.domain());  // ["aaa", "bbb"]

  color("ccc");
  console.log(color.domain()); // ["aaa", "bbb", "ccc"]

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

Если вы хотите сопоставить каждую категорию с определенным цветом, вам нужно указать ядро ​​так, чтобы отображение не зависело от последовательности клавиш.

  var color = d3.scale.category10();

  var domain = ["bbb", "ddd", "ccc", "23", "hello"];

  color.domain(domain);

  console.log(color.domain()); // ["bbb", "ddd", "ccc", "23", "hello"] 
  console.log(color.range());  // ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"] 

  color("ddd"); // "#ff7f0e" : d3 will get index of "ddd" and return range[index]

Ответ 2

Оказывается, что настройка области масштабирования устраняет эту проблему.

var colors = d3.scale.category10().domain([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);

// Now this will return the first item instead of the zeroth.
console.log(colors(1)); 

Или несколько более лаконично,

var colors = d3.scale.category10().domain(d3.range(0,10));

Обновлен скрипт: http://jsfiddle.net/LqHst/2/

Когда создается шкала категорий10, она создается с диапазоном 10 цветов и пустым доменом.

var colors = d3.scale.category10();
console.log(colors.range()); // -> ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"]
console.log(colors.domain()); // -> []

В соответствии с документацией (и принятым ответом) установка домена на порядковой шкале необязательна. Если домен не задан, его значения принимаются из вызовов функции масштабирования.

var colors = d3.scale.category10();
console.log(colors.domain()); // -> []
console.log(colors(1)); // -> #1f77b4
console.log(colors.domain()); // -> [1]
console.log(colors(0), colors(3), colors(7)); // -> #ff7f0e #2ca02c #d62728
console.log(colors.domain()); // -> [1, 0, 3, 7] 

Только если данный индекс еще не находится в домене, он добавляется.

Вот почему обходной путь, как указано в первоначальном вопросе, вызвал ожидаемое поведение. Шаг через шкалу через цикл for запросил масштаб в натуральном порядке, добавив упорядоченные индексы в область.

var colors = d3.scale.category10();

for (var i = 0; i < 10; i++) {
    colors(i)
}

console.log(colors.domain()); // -> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

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

Ответ 3

Я использую такое решение для установки цвета для сгенерированной диаграммы и ее легенды:

this.color = d3.scale.category20().range()[Math.floor(Math.random()*100%20)];