에서 선택한 기능 감소 지난 몇 주 동안 간단한 개인 대시 보드를 만드는 동안 dc.js를 많이 사용했습니다.dc.js - 라디오 버튼
필자는 lineChart의 시간 차원을 그룹화하기 위해 사용하고자하는 시간 세분성을 선택하기 위해 팝업 메뉴를 구현했으며 커뮤니티의 도움을 받아 성능을 대폭 향상 시켰습니다.
이제 그룹화 된 데이터 (합계, 평균, 모드, 최소 및 최대)에서 수행하는 집계 유형을 동적으로 변경하려고합니다.
나는이 example을 매우 유용하게 찾았지만, 그럼에도 불구하고 나는 그것을 내 경우에 맞게 조정하지 못했고 제대로 작동시키지 못했다. 내 이해에서,이 경우에는 값 접근 자 함수를 변경 한 다음 다시 그려야합니다. 실제로 valueAccessor는 y 축 픽셀 위치를 결정하므로 변경해야하는 부분이 있습니다. 대신 그룹 집계에서 변경 사항을 처리 할 때 새로운 그룹으로 전체 차트를 다시 설정해야했습니다 ...
이제 라디오 버튼 위치가 전혀 인쇄되지 않는 코드가 있습니다. (합계 및 svg 만 구현 됨).
동적 valueAccessor 파트를 제거하면 기본 "sum"선택이 올바르게 작동합니다.
// Disable it or dash_reduceAvgAdd will give an error with ++p!
//'use strict';
// TODO temp dirty workaround
var selectedAggr = 'sum';
// ### Create Chart Objects
// Create chart objects associated with the container elements identified by the css selector.
// Note: It is often a good idea to have these objects accessible at the global scope so that they can be modified or
// filtered by other page controls.
var stackChart = dc.lineChart("#stack-chart");
var volumeChart = dc.barChart('#volume-chart');
// Asynchronously load the data and only when finished build the charts
queue()
.defer(d3.json, "/data")
.await(makeGraphs);
// Function to elaborate the data and build the charts
function makeGraphs(error, recordsJson) {
// Clean data
var records = recordsJson;
// Works on d3-v4 only: var dateFormat = d3.timeFormat("%Y-%m-%d %H:%M:%S");
//var dateFormat = d3.time.format("%Y-%m-%d %H:%M");
console.log(Object.prototype.toString.call(records[0].date));
// Coerce values to number and create javascript Date objects
records.forEach(function(d) {
d.date = new Date(+d.date);
d.prodPow = +d.prodPow;
d.consPow = +d.consPow;
});
// Crossfilter instance
var ndx = crossfilter(records);
// Aggregation functions
// SUM mode
//function reduceAdd(attr) { return reduceSum(function (d) { return d[attr]; }); }
function dash_reduceSumAdd(attr) { return function (p, v) { return p + +v[attr]; }; }
function dash_reduceSumSub(attr) { return function (p, v) { return p - v[attr]; }; }
function dash_reduceInit() { return 0; }
// AVG mode
function dash_reduceAvgAdd(attr) {
return function (p, v) {
++p.count;
p.sum += v[attr];
p.avg = p.sum/p.count;
return p;
};
}
function dash_reduceAvgSub(attr) {
return function (p, v) {
--p.count;
p.sum -= v[attr];
p.avg = p.count ? p.sum/p.count : 0;
return p;
}
}
function dash_reduceAvgInit() {
return function() {
return {count:0, sum:0, avg:0};
}
}
function valAccSum(d) {
return d.value;
}
function valAccAvg(d) {
return d.value.avg;
}
// Map selector to correct map-reduce functions
var aggregators = {
sum: [dash_reduceSumAdd, dash_reduceSumSub, dash_reduceInit, valAccSum],
avg: [dash_reduceAvgAdd, dash_reduceAvgSub, dash_reduceAvgInit, valAccAvg]//,
//mode: reduceMode,
//min: reduceMin,
//max: reduceMax
};
// Granularities selectable values
var granularities = {
Hours: [d3.time.hour, d3.time.hours],
Days: [d3.time.day, d3.time.days],
Weeks: [d3.time.week, d3.time.weeks],
Months: [d3.time.month, d3.time.months],
Years: [d3.time.year, d3.time.years]
};
// Assign default granularity
d3.select('#granularity').selectAll('option')
.data(Object.keys(granularities))
.enter().append('option')
.text(function(d) { return d; })
.attr('selected', function(d) { return d === 'Days' ? '' : null; });
var dateDim, consPowByHour, prodPowByHour;
// Function to build the charts from the selected granularity
function setup(aggr) {
if (dateDim) {
dateDim.dispose();
consPowByHour.dispose();
prodPowByHour.dispose();
}
var gran = granularities[d3.select('#granularity')[0][0].value];
dateDim = ndx.dimension(function (d) { return gran[0](d.date); });
consPowByHour =
dateDim
.group(function (d) { return gran[0](d); })
.reduce(aggregators[aggr][0]('consPow'), aggregators[aggr][1]('consPow'), aggregators[aggr][2]);
//consPowByHour = dateDim.group(function (d) { return granularity[0](d); }).reduceSum();
prodPowByHour =
dateDim
.group(function (d) { return gran[0](d); })
.reduce(aggregators[aggr][0]('prodPow'), aggregators[aggr][1]('prodPow'), aggregators[aggr][2]);
// Min and max dates to be used in the charts
var minDate = gran[0](dateDim.bottom(1)[0]["date"]);
var maxDate = gran[0](dateDim.top(1)[0]["date"]);
// Charts customization
stackChart
.renderArea(true)
/* Make the chart as big as the bootstrap grid by not setting ".width(960)" */
.height(350)
.transitionDuration(1500)
.margins({top: 30, right: 50, bottom: 25, left: 40})
.dimension(dateDim)
/* Grouped data to represent and label to use in the legend */
.group(consPowByHour, "Consumed Power [kW]")
/* Function to access grouped-data values in the chart */
.valueAccessor(aggregators[aggr][2])
/* x-axis range */
.x(d3.time.scale().domain([minDate, maxDate]))
.xUnits(gran[1])
/* Auto-adjust axis */
.elasticY(true)
.renderHorizontalGridLines(true)
.legend(dc.legend().x(80).y(0).itemHeight(13).gap(5))
/* When on, you can't visualize values, when off you can filter data */
.brushOn(false)
/* Add another line to the chart; pass (i) group, (ii) legend label and (iii) value accessor */
.stack(prodPowByHour, "Produced Power [kW]", aggregators[aggr][2])
/* Range chart to link the brush extent of the range with the zoom focus of the current chart. */
.rangeChart(volumeChart)
/* dc.js bug, this should be true by default to turn on visibility for reset class */
.controlsUseVisibility(true)
;
volumeChart//.width(990)
.height(60)
.margins({top: 0, right: 50, bottom: 20, left: 40})
.dimension(dateDim)
.group(consPowByHour)
.centerBar(true)
.gap(1)
.x(d3.time.scale().domain([minDate, maxDate]))
.xUnits(gran[1])
.elasticY(true)
.alwaysUseRounding(true)
/* dc.js bug, this avoids the reset and filter to remain after resetting using the brush/focus */
.on('renderlet', function (chart) {
var rangeFilter = chart.filter();
var focusFilter = chart.focusChart().filter();
if (focusFilter && !rangeFilter) {
dc.events.trigger(function() {
chart.focusChart().replaceFilter(rangeFilter);
});
}
})
;
}
// First time build charts
setup(selectedAggr);
// Render all graphs
dc.renderAll();
// Listen for changes on granularities selection
d3.select('#granularity').on('change', function() {
setup(selectedAggr);
dc.redrawAll();
});
// Listen for changes on aggregation mode selection
d3.selectAll('#select-operation input')
.on('click', function() {
stackChart.valueAccessor(aggregators[this.value][3]);
selectedAggr = this.value;
//setup(this.value);
dc.redrawAll();
});
그리고 여기이하지 않을 때 작업 때처럼 그 모습의 스크린 샷의 커플 : 여기
는 코드입니다. 사전에![Working (sum mode)](https://i.stack.imgur.com/Ksdjy.png)
![With the dynamic valueAccessor function (not working)](https://i.stack.imgur.com/5KU9G.png)
덕분에, 나는 심지어는 콘솔에서 어떤 오류가 발생하지 않기 때문에 앞으로 이동하는 방법을 정말 아무 생각이 없습니다.
편집 :
<!DOCTYPE html>
<html>
<head>
<title>Dashboard</title>
<link rel="stylesheet" href="./static/css/bootstrap.min.css">
<link rel="stylesheet" href="./static/css/dc.css">
<link rel="stylesheet" href="./static/css/custom.css">
</head>
<body class="application">
<!-- Header bar on top -->
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="./">Dashboard</a>
</div>
</div>
</div>
<!-- Chart container page -->
<div class="container-fluid">
<!-- First row of charts (compensate on the left part the strange padding on right "trbl") -->
<div class="row" style="width:100%; padding: 0px 0px 0px 25px;">
<!-- Control Panel -->
<div class="col-sm-12">
<div class="chart-wrapper control-panel">
<div class="chart-title control-panel">
Control Panel
</div>
<div class="row">
<div class="col-sm-4">
<div class="chart-stage control-panel" style="height: 100px; border-right: 1px solid #e2e2e2;">
<div class="text-center" style="padding: 10px;">
<!--<div class="inner">-->
<strong>Granularity:</strong>
<select id="granularity" style="margin-left: 10px"></select>
<div id="select-operation" style="margin-top: 15px;">
<strong>Aggregation:</strong>
<label><input type=radio name="operation" value="sum" checked="checked" style="margin-left: 10px"> sum</label>
<label><input type=radio name="operation" value="avg"> average</label>
<label><input type=radio name="operation" value="mode"> mode</label>
<label><input type=radio name="operation" value="min"> min</label>
<label><input type=radio name="operation" value="max"> max</label>
</div>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="chart-stage control-panel" style="height: 100px; border-right: 1px solid #e2e2e2;">
Test
</div>
</div>
<div class="col-sm-4">
<div class="chart-stage control-panel" style="height: 100px;">
Test
</div>
</div>
</div>
</div>
</div>
<!-- Stack Chart and its Range Chart as a single bootstrap grid -->
<div class="col-sm-12">
<div class="chart-wrapper">
<div class="chart-title">
Stack Chart
</div>
<div class="chart-stage">
<!-- Stack Chart -->
<div class="row">
<div id="stack-chart" style="width:100%;">
<a class="reset"
href="javascript:stackChart.filterAll();volumeChart.filterAll();dc.redrawAll();"
style="visibility: hidden; float: right; margin-right: 15px;">
reset chart
</a>
<span class='reset'
style='visibility: hidden; float: right; margin-right: 15px; font-style: italic;'>
Current filter: <span class='filter'></span>
</span>
<div class="clearfix"></div> <!-- Use it when using the reset class for IE -->
</div>
</div>
<!-- Range Chart -->
<div class="row">
<div id="volume-chart" style="width:100%;"></div>
<p class="muted pull-right" style="margin-right: 15px;"><i>select a time range to zoom in</i></p>
</div>
</div>
</div>
</div> <!-- End of "col-sm-12" grid -->
</div> <!-- End of first row -->
</div>
</body>
</html>
좋아요, 내 주요 문제를 발견했다고 생각합니다. 곧 해결책을 게시 할 것입니다. 즉, reduce 함수 (add, sub, init)는 group(). reduce()를 수행 할 때 계산하고자하는 모든 값을 캡슐화 할 수 있으므로 각각에 대해 하나씩 평균 및 합계를 계산할 수 있습니다. valueAccessor에서 원하는 것을 지정하기 만하면됩니다 (p.value.avg 또는 p.value.sum). 이제는 직설적 인 것 같습니다 ... – Bertone