使用 dc.js 生成的图表比例返回 NaN

Scale of chart generated by using dc.js is returning NaN

我对 DC/D3 图书馆还很陌生。我试图通过一个单独的纯 JS 文件将 DC 与 ReactJS 结合起来,该文件是一个可重用的 D3 组件。我在这里关注 example。这是我正在使用的虚拟数据: json snippet.

这是我的 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>
        )
    }

这是我用于 generate/render 图表的 JS 文件 (dashboard.js):

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");
    }

我确定问题出在updateVis方法的回调函数中的xScale。它正在 returning NaN。但我试图在 initVis 中调用 xScale,比例函数似乎工作正常并且 return [0,1]。我为此苦苦挣扎了很长时间。任何帮助将不胜感激!

更新: 这是我收到的错误消息的快照:

感谢您提供可重现的示例。在没有 运行 的情况下,仅仅盯着它调试 D3 和 dc.js 代码真的很难。

这里的问题是散点图期望组键是一个双元素数字数组,而不仅仅是一个数字。您可以看到 key_function(),在您开始的回归示例中,returns 一个返回此类键的函数。

相应地更改尺寸:

    const ageDimension = vis.cf.dimension(d => [d.age, d.readmission_risk] );

导致图表出现。

我还必须添加示例中的 CSS 才能显示回归线;我创建了 dashboard.css:

  line.regression {
    stroke: red;
    stroke-width: 5;
    opacity: 0.5;
  }

并将其添加到 dashboard.js:

import './dashboard.css';

结果:

顺便说一句,您计算回归线的方式在过滤数据时不会更新。如果您希望它这样做,这应该可行:

const r = regression.linear(vis.chart.group().all().filter(kv => kv.value).map(kv => [kv.key[0], kv.key[1]]));

这使用图表组中的键,但仅使用“在”中过滤的键(值 > 0)。