D3.js version3 scale.ordinal() не читает мои данные так, как хотелось бы

Я строю точечную гистограмму с помощью d3.js v3 и пытаюсь раскрасить свои данные на основе одной из переменных, которые я прочитал из своего CSV-файла (это город, а их 10). Проблема в том, что каждая точка имеет один и тот же цвет, когда я запускаю ее в браузере.

Кто-нибудь знает, что происходит?

Вот снимок моего кода:

var data = d3.csv('happy.csv', function(data) {

data.forEach(function(d) {
    d["index"] = d["index"];
    d["name"] = d["name"];
    d["city"] = d["city];
    ...
 })

var colorScheme = d3.scale.ordinal()
                .domain(data, (function(d) {
                    return d.city
                }))
                .range(["#FFADAF", "#FFC980", "#D9D9D9", "#9DACCB", "#95D1A5", 
                "#F586C6", "#BFDD81", "#F18489", "#80BCEC", "#FEFEA9"]);

...

var dots = groups.selectAll("circle")
.data(function(d) {
    return d3.range(1, +d.index + 1)
})
.enter().append("circle")
.attr("class", "dot")
.attr("r", 7.5)
.attr("cy", function(d) {
    return y(d)
})
.style("fill", function (d) { return colorScheme(d.city); } )

})

Если вы регистрируете город с помощью .attr("fill", function(d) { console.info(d.city); return colorScheme(d.city); }), он записывает undefined, верно?

Andrew Reid 22.12.2020 20:49

@AndrewReid Да, это так!

bullybear17 22.12.2020 20:55
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
2
122
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы привязываете каждый элемент массива данных к groups, а затем используете эти данные для создания массива данных для привязки к дочерним кругам:

return d.range(1, +d.index + 1);

Проблема в том, что d3.range просто создает массив чисел для указанного диапазона. Для этого связанного данных нет свойства city, которое является просто числом: нет следов исходных данных, связанных с родителем group, перенесенных вперед как часть данных, связанных с дочерними элементами. Следовательно, каждый раз, когда вы пытаетесь раскрасить круги с помощью d.city, вы передаете undefined на шкалу, поскольку это единственное значение, передаваемое на шкалу, все круги одного цвета.

Вместо этого вы можете использовать d3.range и array.map() для привязки массива объектов к дочерним элементам, которые переносят свойство города:

return d3.range(1, +d.index + 1).map(function(c) {
  return {city: d.city, index: c}
})

Теперь у нас есть собственность города, перенесенная на круги:

var data = [
  {index: 5, city: "A"},
  {index: 4, city: "B"},
  {index: 6, city: "C"}
];

var colorScheme = d3.scale.ordinal()
                .domain(data, (function(d) {
                    return d.city
                }))
                .range(["#FFADAF", "#FFC980", "#D9D9D9", "#9DACCB", "#95D1A5", 
                "#F586C6", "#BFDD81", "#F18489", "#80BCEC", "#FEFEA9"]);

var svg = d3.select("body").append("svg");

var groups = svg.selectAll("g")
  .data(data)
  .enter()
  .append("g")
  .attr("transform",function(d,i) {
    return "translate("+[0,i*20]+")";
  })

var dots = groups.selectAll("circle")
.data(function(d) {
    return d3.range(1, +d.index + 1).map(function(c) {
      return {city: d.city, index: c}
    })
})
.enter().append("circle")
.attr("class", "dot")
.attr("r", 7.5)
.attr("cx", function(d,i) {
  return i*20+10;
})
.attr("cy", function(d,i) {
    return 60;
})
.style("fill", function (d) { return colorScheme(d.city); } )
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>

В качестве альтернативы мы могли бы получить доступ к родительскому элементу данных с помощью d3.select(this.parentNode).datum() при раскрашивании кругов:

.style("fill", function (d) { return    colorScheme(d3.select(this.parentNode).datum().city); } )

Следующее:

var data = [
  {index: 5, city: "A"},
  {index: 4, city: "B"},
  {index: 6, city: "C"}
];

var colorScheme = d3.scale.ordinal()
                .domain(data, (function(d) {
                    return d.city
                }))
                .range(["#FFADAF", "#FFC980", "#D9D9D9", "#9DACCB", "#95D1A5", 
                "#F586C6", "#BFDD81", "#F18489", "#80BCEC", "#FEFEA9"]);

var svg = d3.select("body").append("svg");

var groups = svg.selectAll("g")
  .data(data)
  .enter()
  .append("g")
  .attr("transform",function(d,i) {
    return "translate("+[0,i*20]+")";
  })

var dots = groups.selectAll("circle")
.data(function(d) {
    return d3.range(1, +d.index + 1)
})
.enter().append("circle")
.attr("class", "dot")
.attr("r", 7.5)
.attr("cx", function(d,i) {
  return i*20+10;
})
.attr("cy", function(d,i) {
    return 60;
})
.style("fill", function (d) { return colorScheme(d3.select(this.parentNode).datum().city); } )
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>

Или мы могли бы применить заливку на уровне родителя и вообще не применять заливку для дочерних элементов:

var data = [
  {index: 5, city: "A"},
  {index: 4, city: "B"},
  {index: 6, city: "C"}
];

var colorScheme = d3.scale.ordinal()
                .domain(data, (function(d) {
                    return d.city
                }))
                .range(["#FFADAF", "#FFC980", "#D9D9D9", "#9DACCB", "#95D1A5", 
                "#F586C6", "#BFDD81", "#F18489", "#80BCEC", "#FEFEA9"]);

var svg = d3.select("body").append("svg");

var groups = svg.selectAll("g")
  .data(data)
  .enter()
  .append("g")
  .attr("transform",function(d,i) {
    return "translate("+[0,i*20]+")";
  })
  .attr("fill", function(d) { return colorScheme(d.city); })

var dots = groups.selectAll("circle")
.data(function(d) {
    return d3.range(1, +d.index + 1)
})
.enter().append("circle")
.attr("class", "dot")
.attr("r", 7.5)
.attr("cx", function(d,i) {
  return i*20+10;
})
.attr("cy", function(d,i) {
    return 60;
})
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>

Другие вопросы по теме