Skip to content

Instantly share code, notes, and snippets.

@chornbaker
Last active December 14, 2016 21:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chornbaker/49c8bdccfb74cac1a54433f9c0c8a011 to your computer and use it in GitHub Desktop.
Save chornbaker/49c8bdccfb74cac1a54433f9c0c8a011 to your computer and use it in GitHub Desktop.
Matplotlib Linechart in D3
license: mit

Matplotlib Linechart in D3

This is a useful example for converting a matplotlib linechart to D3 with an initial animation. Using this template, you can quickly generate a dynamic linechart using all of your favorite matplotlib styles, and add more advanced interactions without dealing with a lot of additional formatting.

See linechart.py for code used to generate linechart.svg. It generates a simple linechart with some basic formatting. Matplotlib's savefig function will automatically output an svg format if the filepath has a .svg extension. The key is to assign a unique gid to each line using the gid parameter, so it is simple to find the lines when you read the svg in index.html.

This is Part 2 in a series of examples for using matplotlib generated plots in D3.

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<!-- CDN resource versions -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-ease.v1.min.js"></script>
<script src="https://d3js.org/d3-queue.v3.min.js"></script>
<script src="https://d3js.org/d3-selection.v1.min.js"></script>
<script src="https://d3js.org/d3-transition.v1.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
</style>
</head>
<body>
<!-- chart SVGs-->
<div id="chart"></div>
<script type="text/javascript">
d3.queue()
.defer(d3.xml, "linechart.svg")
.await(ready);
function ready(error, xml) {
if (error) throw error;
// Load SVG into chart
d3.select("#chart").node().appendChild(xml.documentElement);
///////////////////////////////////////////////////////////////////////////
/////////////////////////// Helper Functions //////////////////////////////
///////////////////////////////////////////////////////////////////////////
// Temporarily disable user interractions to allow animations to complete
var disableUserInterractions = function (time) {
isTransitioning = true;
setTimeout(function(){
isTransitioning = false;
}, time);
}//disableUserInterractions
///////////////////////////////////////////////////////////////////////////
///////////////////////// Animation Elements //////////////////////////////
///////////////////////////////////////////////////////////////////////////
// Set initial transition state
var isTransitioning = false;
// Basic plot elements
var svg, plot, lines
label = "line_";
svg = d3.select("#chart").select("svg")
plot = svg.select("#figure_1")
lines = plot.selectAll("g").filter(function(d,i,j) {
return new RegExp(label, 'g').test(j[i].id)
});
svg.on("click", function(){
if (isTransitioning == false) {
init();
}
});
///////////////////////////////////////////////////////////////////////////
////////////////// Initialize Graphic and Animations //////////////////////
///////////////////////////////////////////////////////////////////////////
function init() {
//////////////////////////////////////////////////////
///////////////////// Actions ////////////////////////
//////////////////////////////////////////////////////
var DURATION = 2000;
var DELAY = 2000;
disableUserInterractions(DURATION * lines.nodes().length);
// Intialize lines to zero
lines.each(function() {
d3.select(this).selectAll("path")
.attr("stroke-dasharray", function(d) {
var totalLength = this.getTotalLength()
return(totalLength + " " + totalLength)
})
.attr("stroke-dashoffset", function(d) {
return this.getTotalLength()
})
})
// Animate lines
lines.each(function(d,i) {
d3.select(this).selectAll("path")
.transition().delay(i * DELAY).duration(DURATION)
.ease(d3.easeLinear)
.attr("stroke-dashoffset", 0)
})
}//init
init()
};
</script>
</body>
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np
from cycler import cycler
def main():
fig = plt.figure(figsize=(10, 5))
ax = fig.add_subplot(111)
# Generate some random lines
n = 1000
lines = 5
xlim = [0, 20]
X = np.arange(xlim[0], xlim[1], step=xlim[1] / n)
ax.set_prop_cycle(cycler('color',
[plt.cm.viridis(i) for i in np.linspace(0, 1, lines)]))
ax.hold(True)
for i in range(0, lines):
Y = (np.sin(X + np.random.normal(0, 10, 1)) +
np.random.normal(0, 2, 1)) * np.exp(X*np.random.normal(0, .05, 1))
ax.plot(X, Y, linewidth=2, gid="line_" + str(i))
# Only show ticks on the left and bottom spines
ax.yaxis.set_ticks_position('left')
ax.xaxis.set_ticks_position('bottom')
# Hide the right and top spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
plt.savefig('linechart.svg', bbox_inches='tight', transparent=True)
if __name__ == '__main__':
main()
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment