const log = console.log; var dataTable = dc.dataTable("#dc-table-graph"); var pieChart = dc.pieChart("#pie-chart"); // const barChart = dc.barChart("#bar-chart"); // var focus = dc.barChart('#focus'); const chart_11 = dc.compositeChart("#focus"); // var range = dc.barChart('#range'); var dim = {}, // Stores all crossfilter dimensions groups = {}, // Stores all crossfilter groups cf; // index a group key -> i and i -> key function ordinal_to_linear_group(group, sort) { var _ord2int, _int2ord; return { all: function() { var ret = group.all(); if(sort) // NEW ret = ret.slice().sort(sort); _ord2int = {}; _int2ord = []; ret.forEach(function(d, i) { _ord2int[d.key] = i; _int2ord[i] = d.key; }); return ret; }, ord2int: function(o) { if(!_ord2int) this.all(); return _ord2int[o]; }, int2ord: function(i) { if(!_int2ord) this.all(); return _int2ord[i]; } }; } function update(row) { draw_charts(row); } d3.csv("info.csv").then(function(data) { // console.log("data:",data); data.forEach(d => { d.date = new Date(d.date); }); draw_charts(data); }); function draw_charts(data) { // Programmatically insert header labels for table var tableHeader = d3.select(".table-header") .selectAll("th"); tableHeader = tableHeader.data( [ {label: "pid", field_name: "pid", sort_state: "ascending"}, {label: "cid", field_name: "cid", sort_state: "ascending"}, {label: "date", field_name: "date", sort_state: "ascending"}, {label: "types", field_name: "types", sort_state: "ascending"}, {label: "probability", field_name: "probability", sort_state: "descending"} // Note Max Conf row starts off as descending ] ); // enter() into virtual selection and create new header elements for each table column tableHeader = tableHeader.enter() .append("th") .text(function (d) { return d.label; }) // Accessor function for header titles .on("click", tableHeaderCallback); function tableHeaderCallback(d) { // Highlight column header being sorted and show bootstrap glyphicon var activeClass = "info"; d3.selectAll("#dc-table-graph th") // Disable all highlighting and icons .classed(activeClass, false) .selectAll("span") .style("visibility", "hidden") // Hide glyphicon var activeSpan = d3.select(this) // Enable active highlight and icon for active column for sorting .classed(activeClass, true) // Set bootstrap "info" class on active header for highlight .select("span") .style("visibility", "visible"); // Toggle sort order state to user desired state d.sort_state = d.sort_state === "ascending" ? "descending" : "ascending"; var isAscendingOrder = d.sort_state === "ascending"; dataTable .order(isAscendingOrder ? d3.ascending : d3.descending) .sortBy(function(datum) { return datum[d.field_name]; }); // Reset glyph icon for all other headers and update this headers icon activeSpan.node().className = ''; // Remove all glyphicon classes // Toggle glyphicon based on ascending/descending sort_state activeSpan.classed( isAscendingOrder ? "glyphicon glyphicon-sort-by-attributes" : "glyphicon glyphicon-sort-by-attributes-alt", true); updateTable(); dataTable.redraw(); } // Initialize sort state and sort icon on one of the header columns // Highlight "Max Conf" cell on page load // This can be done programmatically for user specified column tableHeader.filter(function(d) { return d.label === "pid"; }) .classed("info", true); var tableSpans = tableHeader .append("span") // For Sort glyphicon on active table headers .classed("glyphicon glyphicon-sort-by-attributes-alt", true) .style("visibility", "hidden") .filter(function(d) { return d.label === "pid"; }) .style("visibility", "visible"); cf = crossfilter(data); // Main crossfilter objects let barTypeDimension = cf.dimension(function(d) { return d.cid}); let barTypeGroup = barTypeDimension.group(); var reducer_time_est_max = reductio().max(function(d) { return parseFloat(d.probability) }); let pieTypeDimension = cf.dimension(function(d) { return d.pid ? d.pid: ""}); let pieTypeGroup = pieTypeDimension.group(); var reducer_prob = reductio().max(function(d) { return parseFloat(d.probability) || 1e-5; }); var pred_prob_max_pie = reducer_prob(pieTypeGroup); pieChart .height(400) .dimension(pieTypeDimension) .group(pred_prob_max_pie ) .valueAccessor(function(p) { return parseFloat(p.value.max) || 0.0; }) // .data(function (d) { // return d.order(function (d) { // // console.log("------>",d.max); // return +d.max || 0; // }).top(10) // }) .ordering(d => -(d.value.max || 0)) .othersGrouper(null) .slicesCap(10) .innerRadius(100) .externalLabels(30) .externalRadiusPadding(50) .drawPaths(true) .legend(dc.legend()); // example of formatting the legend via svg // http://stackoverflow.com/questions/38430632/how-can-we-add-legends-value-beside-of-legend-with-proper-alignment pieChart.on('pretransition', function(chart) { chart.selectAll('.dc-legend-item text') .text('') .append('tspan') .text(function(d) { return d.name; }) .append('tspan') .attr('x', 100) .attr('text-anchor', 'end') .text(function(d) { return d.data.max; }); }); // dc.override(pieChart, 'legendables', function() { // var legendables = this._legendables(); // return legendables.filter(function(l) { // return l.data.max > 0; // }); // }); function static_copy_group(group) { var all = group.all().map(kv => ({key: kv.key, value: kv.value})); return { all: function() { return all; } } } function any_filters() { return [chart_11] .some(chart => chart.filters().length); } var chart_11_grp_copy = static_copy_group(reducer_time_est_max(barTypeGroup)); var group1 = ordinal_to_linear_group(chart_11_grp_copy, (a,b) => d3.descending(a.value.max, b.value.max)); var group = ordinal_to_linear_group(reducer_time_est_max(barTypeGroup), (a,b) => d3.descending(a.value.max, b.value.max)); var c1 = dc.barChart(chart_11) .group(chart_11_grp_copy) .colors('#ccc') // match dc.css rect.deselected // .xUnits(dc.units.integers) .keyAccessor(kv => group.ord2int(kv.key)) .valueAccessor(function(p) { return parseFloat(p.value.max) || 0; }) // .brushOn(false) .controlsUseVisibility(true); var c2 = dc.barChart(chart_11) .group(barTypeGroup) .colors('red') // .xUnits(dc.units.integers) .keyAccessor(kv => group.ord2int(kv.key)) .valueAccessor(function(p) { return parseFloat(p.value.max) || 0; }) // .addFilterHandler(function(filters, filter) {return [filter];}) .controlsUseVisibility(true) .brushOn(false); var linear_domain = [-0.5, 100 - 0.5]; chart_11 .width(850).height(350) .margins({left: 60, top: 20, right: 10, bottom: 100}) .x(d3.scaleLinear().domain(linear_domain)) .xUnits(dc.units.integers) // .x(d3.scaleOrdinal()) // .xUnits(dc.units.ordinal) // .keyAccessor(kv => group.ord2int(kv.key)) // .valueAccessor(function(p) { // console.log("PPPPPPPP:,",p); // return parseFloat(p.value.max) || 0; // }) // .centerBar(true) .yAxisLabel('counts') .elasticY(true) .brushOn(false) .dimension(barTypeDimension) // .mouseZoomable(true) .zoomScale([4,8]) // .group(barTypeGroup_mod) .title(kv => kv.key) // .elasticX(true) .transitionDuration(0) .compose([ c1,c2 ]); chart_11.xAxis() .tickFormat(function(d) { return group.int2ord(d); }); // equivalent of javascript: link, don't know why those didn't work d3.select('#reset-focus').on('click', () => { chart_11.filterAll(); dc.redrawAll(); }) d3.select('#reset-all').on('click', () => { dc.filterAll(); dc.redrawAll(); }) // unfortunately we have to recreate click-selection, since a focus chart // ordinarily filters to the visible area (and we don't want a brush either) var chart_11Filter = []; chart_11.filterAll = function() { chart_11Filter = []; chart_11.filter(null); }; chart_11.fadeDeselectedArea = function (brushSelection) { var _chart = this; var bars = c2.selectAll('rect.bar'); log("bars:::::::::", _chart.chartGroup()); if (chart_11Filter.length) { bars.classed(dc.constants.SELECTED_CLASS, function (d) { return chart_11Filter.includes(d.data.key); }); bars.classed(dc.constants.DESELECTED_CLASS, function (d) { return !chart_11Filter.includes(d.data.key); }); } else { bars.classed(dc.constants.SELECTED_CLASS, false); bars.classed(dc.constants.DESELECTED_CLASS, false); } }; c2.on('pretransition.click-handler', function(chart) { chart.selectAll('.sub._1 rect.bar').on('click.ordinal-select', function(d) { var i = chart_11Filter.indexOf(d.data.key); if(i >= 0) chart_11Filter.splice(i, 1); else chart_11Filter.push(d.data.key); if(chart_11Filter.length) chart.dimension().filterFunction(function(k) { return chart_11Filter.includes(k); }); else chart.dimension().filter(null); chart.redrawGroup(); }); }); chart_11.on('preRedraw', function(chart) { var domain = chart.x().domain(), min = Math.ceil(domain[0]), max = Math.floor(domain[1]); chart.xAxis().tickValues(d3.range(min, max+1)); }); // c1.filter(null); // Setup different dimensions for plots dim.tableMaxConfidence = cf.dimension(function (d) { return d.Project_id; }); // ############################## // Generate the dc.js dataTable // ############################## // Create generating functions for each columns var columnFunctions = [ function(d) { return d.pid; }, function(d) { return d.cid; }, function(d) { return d.date; }, function(d) { return d.types; }, function(d) { return d.probability; }, ]; // extra dimensions for filtering const cidDimension = cf.dimension(d => d.cid), dateDimension = cf.dimension(d => d.date); var color = d3.scaleSequential() //d3.scaleLinear() .domain([0, 0.9]) .interpolator(d3.interpolateBlues); // Pagination implementation inspired by: // https://github.com/dc-js/dc.js/blob/master/web/examples/table-pagination.html dataTable.width(960).height(800) .dimension(dim.tableMaxConfidence) .group(function(d) { return "Dummy"}) // Must pass in. Ignored since .showGroups(false) .size(Infinity) .columns(columnFunctions) .showGroups(false) .sortBy(function(d){ return d.pid; }) // Initially sort by max_conf column .order(d3.descending) .on('pretransition', function (table) { table.selectAll('td.dc-table-column._0') .on('click',function(d){ console.log(d); table.filter(d.pid) dc.redrawAll(); }) table.selectAll('td.dc-table-column._1') .on('click',function(d){ console.log("select:",d); cidDimension.filter(d.cid) dc.redrawAll(); }) table.selectAll('td.dc-table-column._2') .on('click',function(d){ dateDimension.filter(d.date) dc.redrawAll(); }) table.selectAll('td.dc-table-column._4') .style("background-color", function(d){ return color(d.probability)}); }); updateTable(); dc.renderAll(); dataTable.redraw(); } // Data Table Pagination var tableOffset = 0, tablePageSize = 10; function updateTable() { // Ensure Prev/Next bounds are correct, especially after filters applied to dc charts var totFilteredRecs = cf.groupAll().value(); // // Adjust values of start and end record numbers for edge cases var end = tableOffset + tablePageSize > totFilteredRecs ? totFilteredRecs : tableOffset + tablePageSize; tableOffset = tableOffset >= totFilteredRecs ? Math.floor((totFilteredRecs - 1) / tablePageSize) * tablePageSize : tableOffset; tableOffset = tableOffset < 0 ? 0 : tableOffset; // In case of zero entries dataTable.redraw(); }