I am very new to DC/D3 libraries. I am trying to incorporate DC with ReactJS by having a separate pure JS file that is a reusable D3 component. I am following this example here. Here is the dummy data I am using: json snippet.
This is my App.js:
state = {
data: null,
};
componentDidMount() {
console.log("componentDidMount");
d3.json("dummy_data.json").then(data => {
this.setState({data: data});
console.log("Data is updated!");
})
this.createVisualization();
}
componentDidUpdate() {
console.log("componentDidUpdate");
this.updateVisualization();
}
createVisualization = () => {
console.log("createVisualization");
this.visualization = new Dashboard({
parentElement: d3.select(this.node)
});
}
updateVisualization = () => {
this.visualization.updateVis(this.state.data);
console.log("updateVisualization finished");
}
render() {
return (
<div style={{width: '100vw'}} id="app"
ref={node => this.node = node}>
<h1>Hello World!!!</h1>
</div>
)
}
And this is my JS file (dashboard.js) used to generate/render chart:
export default class Dashboard {
constructor(_config) {
// setting up based on input config
this.parentElement = _config.parentElement;
this.initVis();
}
initVis() {
let vis = this;
vis.chartContainer = vis
.parentElement
.append("div")
.attr("id", "chart-container")
.style("width", "100%");
vis.chart = new dc.ScatterPlot("#chart-container");
vis
.chart
.width(768)
.height(480)
.margins({top: 0, right: 10, bottom: 20, left: 50})
.x(d3.scaleLinear())
.elasticX(true)
.elasticY(true)
.brushOn(false) // turn off brush-based range filter
.symbolSize(8) // set dot radius
.clipPadding(10);
const f = vis.chart.x();
console.log(f.domain());
console.log("Finish initVis")
}
updateVis(newData) {
let vis = this;
vis.cf = crossfilter(newData);
console.log(newData);
vis.chart
.on('pretransition', () => {
// console.log(vis)
// const xext = d3.extent(vis.chart.group().all(), d => d.key);
// console.log(newData.map(d => [+d.age, +d.readmission_risk]));
const r = regression.linear(newData.map(d => [d.age, d.readmission_risk]));
const m = r.equation[0],
b = r.equation[1];
const [x1, x2] = vis.chart.x().domain();
const points = [
[
x1, m * x1 + b
],
[
x2, m * x2 + b
]
];
const xScale = vis.chart.x();
const yScale = vis.chart.y();
const margins = vis.chart.margins();
console.log(xScale(20));
var line = vis.chart.g().selectAll('line.regression').data([points]);
// console.log(vis.chart.x().domain());
function do_points(line) {
line
.attr('x1', d => xScale(d[0][0]) + margins.left)
.attr('y1', d => yScale(d[0][1]) + margins.top)
.attr('x2', d => xScale(d[1][0]) + margins.left)
.attr('y2', d => yScale(d[1][1]) + margins.top);
}
line = line.enter().append('line')
.attr('class', 'regression')
.call(do_points)
.merge(line);
line.transition().duration(vis.chart.transitionDuration()).call(do_points);
})
vis.renderVis();
}
renderVis() {
let vis = this;
const ageDimension = vis.cf.dimension(d => d.age);
const ageGroup = ageDimension.group();
vis.chart.dimension(ageDimension).group(ageGroup);
vis.chart.render();
console.log("rendering");
}
I have identified the problem to be the xScale
in the call-back function in the updateVis
method. It is returning NaN
. But I tried to call the xScale
in the initVis
the scale function seems working fine and return [0,1]
. I have been struggling because of this for a long time. Any help would be appreciated!
Thanks for including a reproducible example. It's really hard to debug D3 and dc.js code by just staring at it without running it.
The problem here is that the scatter plot expects the group keys to be a two-element array of numbers, not just a single number. You can see that the key_function()
, in the regression example which you started from, returns a function returning such keys.
Changing your dimension accordingly:
const ageDimension = vis.cf.dimension(d => [d.age, d.readmission_risk] );
caused the chart to appear.
I also had to add the CSS from the example in order to get the regression line to show; I created dashboard.css
:
line.regression {
stroke: red;
stroke-width: 5;
opacity: 0.5;
}
And added it to dashboard.js
:
import './dashboard.css';
Result:
Incidentally, the way you have calculated the regression line, it won't update when the data is filtered. If you want it to do so, this should work:
const r = regression.linear(vis.chart.group().all().filter(kv => kv.value).map(kv => [kv.key[0], kv.key[1]]));
This uses the keys from the chart's group, but only the ones which are filtered "in" (value > 0).