Я строю точечную гистограмму с помощью 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); } )
})
@AndrewReid Да, это так!
Вы привязываете каждый элемент массива данных к 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>
Если вы регистрируете город с помощью
.attr("fill", function(d) { console.info(d.city); return colorScheme(d.city); })
, он записывает undefined, верно?