D3.js переходы между графиками
Я пытаюсь создать переход в своем коде гистограммы, который позволил бы пользователю щелкнуть конкретное и показать другой набор данных, связанных с этой панелью.
Это примерный набор данных:
module_category,component_category,date_repair,actual,predicted
M1,P06,2009/01,39,63
M1,P06,2009/10,3,4
M1,P06,2009/11,4,3
M1,P06,2009/12,4,2
M1,P06,2009/02,29,45
M1,P06,2009/03,29,32
M1,P06,2009/04,10,22
M1,P06,2009/05,13,15
M1,P06,2009/06,9,16
M1,P06,2009/07,7,12
Полный набор данных можно найти здесь: полный набор данных
На основании моего текущего кода я могу создать эту гистограмму:
но теперь я хочу добавить интерактивность, которая позволит пользователю после нажатия на панели, например, "M2", график обновляется, чтобы показать компоненты из "component_category", связанные с этим модулем, с соответствующими "фактическими" и "прогнозируемыми" Значения также отображаются в виде гистограммы.
Это мой текущий код:
var margin = {top: 20, right: 90, bottom: 30, left: 60},
width = 980 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("#maincontent").append("svg")
.attr('id','chart')
.attr('viewBox', '0 0 980 500')
.attr('perserveAspectRatio', 'xMinYMid')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var tip=d3.tip()
.attr("class","d3-tip")
.offset([-10, 0])
.html(function(d) { return "No. of repairs: " + d.value; });
d3.csv("data/Consolidated_result.csv", function(error, data) {
if (error) throw error;
data = d3.nest()
.key(function(d) { return d.module_category;}).sortKeys(d3.ascending)
.rollup(function(values){
var counts = {}, keys = ['actual', 'predicted']
keys.forEach(function(key){
counts[key] = d3.sum(values, function(d){ return d[key]})
})
return counts
})
.entries(data);
console.log(data);
x0.domain(data.map(function(d) { return d.key; }));
x1.domain(['actual','predicted']).rangeRoundBands([0, x0.rangeBand()]);
// store all the values in an array
var yval = [];
data.forEach(function(d){
yval.push(d.values.actual);
yval.push(d.values.predicted);
});
y.domain([0, d3.max(yval)]);
svg.call(tip);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Number of Repairs");
var module = svg.selectAll(".module")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.key) + ",0)"; });
module.selectAll("rect")
.data(function(d){
var ary = [];
ary.push({name:"actual", value:d.values.actual});
ary.push({name:'predicted', value: d.values.predicted});
return ary;
})
.enter().append("rect")
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.on("click", function(d){
d3.select("svg")
.style("opacity",0)
.remove()
tip.hide()
setTimeout(componentgroupedchart, 1000);
})
/*
.on("click", function(d){
d3.select(this)
setTimeout(updateChart(name), 500);
})*/
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(['actual','predicted'])
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d){
return color(d)
});
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
Что я хочу реализовать, так это создать функцию, в которой я обновляю диаграмму, перезагружаю данные и вкладываю их в это:
data = d3.nest()
.key(function(d) { return d.module_category;}).sortKeys(d3.ascending)
.key(function(d) { return d.component_category;}).sortKeys(d3.ascending)
.rollup(function(values){
var counts = {}, keys = ['actual', 'predicted']
keys.forEach(function(key){
counts[key] = d3.sum(values, function(d){ return d[key]})
})
return counts
})
.entries(data);
Это так, что я могу получить доступ для каждого модуля:
- количество компонентов, связанных с этим модулем
- фактические и прогнозируемые значения ремонта
Полученные данные затем становятся:
var data = [{
key: "M1"
values: {
key: "P06"
values: {
actual: 156 ,
predicted: 228
},
key: "P09"
values: {
actual: 31,
predicted: 20
},
key: "P12"
values: {
actual: 140,
predicted: 176
},
key: "P15"
values: {
actual: 38,
predicted: 40
},
key: "P16"
values: {
actual: 112,
predicted:113
},
key: "P17"
values: {
actual: 20 ,
predicted: 7
},
key: "P20"
values: {
actual: 98,
predicted: 127
},
key: "P28"
values: {
actual: 143 ,
predicted: 149
},
key: "P30"
values: {
actual: 16,
predicted: 38
}
},
key: "M5"
values: {
key: "P06"
values: {
actual: 61 ,
predicted: 65
},
key: "P09"
values: {
actual: 83,
predicted: 82
},
key: "P12"
values: {
actual: 45,
predicted: 58
},
key: "P15"
values: {
actual: 26,
predicted: 31
},
key: "P16"
values: {
actual: 152,
predicted:174
},
key: "P21"
values: {
actual: 74 ,
predicted: 120
}
}
}]
Из этих новых данных диаграмма затем переходит к новому отображению гистограммы, которое показывает компоненты и их значения восстановления на основе выбранного модуля. Надеюсь, теперь вопрос намного яснее.
1 ответ
Это может быть достигнуто путем создания одной функции для построения графа модуля, другой для создания графа категории детализации. Определите домен с помощью функций, так как домен оси Y домен оси X будет меняться в зависимости от графика модуля / категории.
Я добавил комментарии в коде; Если у вас есть какие-либо вопросы, не стесняйтесь спрашивать.
var margin = {
top: 20,
right: 90,
bottom: 30,
left: 60
},
width = 980 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("body").append("svg")
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var tip = d3.tip()
.attr("class", "d3-tip")
.offset([-10, 0])
.html(function(d) {
return "No. of repairs: " + d.value;
});
d3.csv("my.csv", function(error, data) {
if (error) throw error;
fullData = data;
data = d3.nest()
.key(function(d) {
return d.module_category;
})
.rollup(function(values) {
var counts = {},
keys = ['actual', 'predicted']
keys.forEach(function(key) {
counts[key] = d3.sum(values, function(d) {
return d[key];
})
})
return counts
})
.entries(data);
//make the x axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
//make the y axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Number of Repairs");
makeModuleGraph(data)
var legend = svg.selectAll(".legend")
.data(['actual', 'predicted'])
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(0," + i * 20 + ")";
});
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d) {
return color(d);
});
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {
return d;
});
});
function makeModuleGraph(data) {
var yval = [];
data.forEach(function(d) {
yval.push(d.values.actual);
yval.push(d.values.predicted);
});
x0.domain(data.map(function(d) {
return d.key;
}));
x1.domain(['actual', 'predicted']).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(yval)]);
svg.call(tip);
svg.selectAll("g .x")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.selectAll("g .y")
.attr("class", "y axis")
.call(yAxis);
var module = svg.selectAll(".module")
.data(data)
.enter().append("g")
.attr("class", "module")
.attr("transform", function(d) {
return "translate(" + x0(d.key) + ",0)";
});
module.selectAll("rect")
.data(function(d) {
var ary = [];
ary.push({
name: "actual",
value: d.values.actual,
key: d.key
});
ary.push({
name: "predicted",
value: d.values.predicted,
key: d.key
});
return ary;
})
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) {
return x1(d.name);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
})
.style("fill", function(d) {
return color(d.name);
}).on("click", function(d) {
makeComponentCategoryGraph(d);//make the graph for category
});
}
function makeComponentCategoryGraph(d){
var filtered = fullData.filter(function(k){ if(d.key == k.module_category){return true;}else {return false;}})
var data = d3.nest()
.key(function(d) {
return d.component_category;
})
.rollup(function(values) {
var counts = {},
keys = ['actual', 'predicted']
keys.forEach(function(key) {
counts[key] = d3.sum(values, function(d) {
return d[key];
})
})
return counts
})
.entries(filtered);
var yval = [];
data.forEach(function(d) {
yval.push(d.values.actual);
yval.push(d.values.predicted);
});
x0.domain(data.map(function(d) {
return d.key;
}));
x1.domain(['actual', 'predicted']).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(yval)]);
svg.call(tip);
svg.selectAll("g .x")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.selectAll("g .y")
.attr("class", "y axis")
.call(yAxis);
svg.selectAll(".module").remove();//remove alll the bar graphs
var module = svg.selectAll(".module")
.data(data)
.enter().append("g")
.attr("class", "module")
.attr("transform", function(d) {
return "translate(" + x0(d.key) + ",0)";
});
module.selectAll("rect")
.data(function(d) {
var ary = [];
ary.push({
name: "actual",
value: d.values.actual,
key: d.key
});
ary.push({
name: "predicted",
value: d.values.predicted,
key: d.key
});
return ary;
})
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) {
return x1(d.name);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
})
.style("fill", function(d) {
return color(d.name);
})
}
Рабочий код здесь.