Is it possible to add an external graph, created for example with D3.js, to my dashboard?

If so, how do you proceed?

1 Like

You have a few options:
1 - (Recommended) Write a react wrapper around your D3.js graph using the Dash plugin system: https://plot.ly/dash/plugins. This allows you to expose parameters to your D3.js graph in Python and write standard Dash callbacks to update those parameters or respond to changes in those parameters. Here’s an example from the community that creates a component with the Vis.js library: https://github.com/jimmybow/visdcc
2 - (Hacky Solution) Write a script in JS and append it to your application with app.scripts.append_script (https://plot.ly/dash/external-resources) In your script, refer to a component with a certain ID of a Div in your app’s app.layout. In your script, you’ll need to wait for that div to exist with e.g. setInterval as the Div will render after page load.
3 - (Sponsorship Option) Contract out the plotly.js advanced development team (https://plot.ly/products/consulting-and-oem/) to add a new chart type to plotly.js. Many of plotly chart types were customer driven: 3D plots, the upcoming violin plots, parallel coordinate plots, ternary plots, and a few others.

2 Likes

Relatedly, is there (more) guidance somewhere on reactifying d3? We’ve found a lot of posts of people essentially bashing their heads against a wall. But given my understanding that plotly.js is essentially react around d3 (plus magic sauce)

Seems like my colleague has been achieving some success following these instructions

plotly.js is actually its own thing that uses some of D3.js and webgl to render plots. The key thing in plotly.js is that its API is declarative and JSON based, which makes it portable in other languages like Python and relatively easy to wrap inside React. Here’s the source for how I wrap plotly.js into a React component: https://github.com/plotly/dash-core-components/blob/b7f776f4aa15be3dae160a4df7b011a4eaa7f425/src/components/Graph.react.js

To create a react version of a d3 graph, you’ll need to convert your d3 code into some type of stateless function that renders a graph based off of a JSON object. Here’s a contrived example based off googling https://bl.ocks.org/mbostock/3883245. This code is just a sketch, I have not tested this myself, I’m mostly just copying and pasting.

function LineGraph(divSelector, xData, yData, xTitle, yTitle, color) {

    var svg = d3.select(divId),
        margin = {top: 20, right: 20, bottom: 30, left: 50},
        width = +svg.attr("width") - margin.left - margin.right,
        height = +svg.attr("height") - margin.top - margin.bottom,
        g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var parseTime = d3.timeParse("%d-%b-%y");

    var x = d3.scaleTime()
        .rangeRound([0, width]);

    var y = d3.scaleLinear()
        .rangeRound([height, 0]);

    var line = d3.line()
        .x(function(d) { return x(xData); })
        .y(function(d) { return y(yData); });

    g.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x))
    .select(".domain")
      .remove();

    g.append("g")
      .call(d3.axisLeft(y))
    .append("text")
      .attr("fill", "#000")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", "0.71em")
      .attr("text-anchor", "end")
      .text(yTitle;

      g.append("path")
          .datum(data)
          .attr("fill", "none")
          .attr("stroke", color)
          .attr("stroke-linejoin", "round")
          .attr("stroke-linecap", "round")
          .attr("stroke-width", 1.5)
          .attr("d", line);
    });

}

Then, your Dash-compatible react component could be as simple as:

class LineGraph extends Component {
    constructor() {
        super();
        this.plot = this.plot.bind(this);
    }
    plot(props) {
        LineGraph(
            props.id,
            props.xData,
            props.yData,
            props.xTitle,
            props.yTitle,
            props.color
        );
    }

    componentDidMount() {
        this.plot(this.props);
    }

    componentWillReceiveProps(newProps) {
        this.plot(newProps);
    }

    render() {
        return <div id={props.id}/>
    }
}

LineGraph.propTypes = {
    id: PropTypes.string,
    xData: PropTypes.array,
    yData: PropTypes.array,
    xTitle: PropTypes.array,
    color: PropTypes.string
}

Which would then be accessible in Dash with something like:

import CustomComponents
CustomComponents.LineGraph(
    xData=df['some-column'],  # Dash converts dataframe columns to json for you
    yData=df['another-column'],  # Dash converts dataframe columns to json for you
    xTitle='my title',
    color='steel blue'
)

For reference, here is the way that plotly.js describes all of the different attributes of charts: https://plot.ly/python/reference. These attributes are accessible inside the figure property of the dcc.Graph component.

2 Likes

In the example above, what is CustomComponents? Or do I need to go through the whole approach described in https://dash.plot.ly/plugins? I am still trying to do that, but it looks like for Windows I need to install all kinds of s.tuff to get things working… I am still stuck at npm install command, just though that if I could just do something like above.

CustomComponents is just the name of the example, that example is following the approach described in Build Your Own Components | Dash for Python Documentation | Plotly

These tutorials have been consolidated in a new D3 + Dash chapter of the Dash User Guide: https://dash.plot.ly/d3-react-components

1 Like