SVG直方图

演示如何创建交互式直方图,通过单击图例标记隐藏或显示条形图。

交互性以ecmascript(javascript)编码,并在后处理步骤中插入SVG代码中。 要渲染图像,请在Web浏览器中打开它。 大多数Linux Web浏览器和OSX用户都支持SVG。 Windows IE9支持SVG,但早期版本不支持。

注意

matplotlib后端允许我们为每个对象分配id。 这是用于描述在python中创建的matplotlib对象的机制以及在第二步中解析的相应SVG构造。 虽然灵活,但它们很难用于大量物体的收集。 可以使用两种机制来简化事情:

  • 系统地将对象分组为SVG \标签,
  • 根据每个SVG对象的来源为每个SVG对象分配类。

例如,不是修改每个单独栏的属性,而是可以将列分组到PatchCollection中,或者将列分配给class =“hist _ ##”属性。

CSS也可以广泛用于替换整个SVG生成中的重复标记。

作者:david.huard@gmail.com

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. import xml.etree.ElementTree as ET
  4. from io import BytesIO
  5. import json
  6. plt.rcParams['svg.fonttype'] = 'none'
  7. # Apparently, this `register_namespace` method works only with
  8. # python 2.7 and up and is necessary to avoid garbling the XML name
  9. # space with ns0.
  10. ET.register_namespace("", "http://www.w3.org/2000/svg")
  11. # Fixing random state for reproducibility
  12. np.random.seed(19680801)
  13. # --- Create histogram, legend and title ---
  14. plt.figure()
  15. r = np.random.randn(100)
  16. r1 = r + 1
  17. labels = ['Rabbits', 'Frogs']
  18. H = plt.hist([r, r1], label=labels)
  19. containers = H[-1]
  20. leg = plt.legend(frameon=False)
  21. plt.title("From a web browser, click on the legend\n"
  22. "marker to toggle the corresponding histogram.")
  23. # --- Add ids to the svg objects we'll modify
  24. hist_patches = {}
  25. for ic, c in enumerate(containers):
  26. hist_patches['hist_%d' % ic] = []
  27. for il, element in enumerate(c):
  28. element.set_gid('hist_%d_patch_%d' % (ic, il))
  29. hist_patches['hist_%d' % ic].append('hist_%d_patch_%d' % (ic, il))
  30. # Set ids for the legend patches
  31. for i, t in enumerate(leg.get_patches()):
  32. t.set_gid('leg_patch_%d' % i)
  33. # Set ids for the text patches
  34. for i, t in enumerate(leg.get_texts()):
  35. t.set_gid('leg_text_%d' % i)
  36. # Save SVG in a fake file object.
  37. f = BytesIO()
  38. plt.savefig(f, format="svg")
  39. # Create XML tree from the SVG file.
  40. tree, xmlid = ET.XMLID(f.getvalue())
  41. # --- Add interactivity ---
  42. # Add attributes to the patch objects.
  43. for i, t in enumerate(leg.get_patches()):
  44. el = xmlid['leg_patch_%d' % i]
  45. el.set('cursor', 'pointer')
  46. el.set('onclick', "toggle_hist(this)")
  47. # Add attributes to the text objects.
  48. for i, t in enumerate(leg.get_texts()):
  49. el = xmlid['leg_text_%d' % i]
  50. el.set('cursor', 'pointer')
  51. el.set('onclick', "toggle_hist(this)")
  52. # Create script defining the function `toggle_hist`.
  53. # We create a global variable `container` that stores the patches id
  54. # belonging to each histogram. Then a function "toggle_element" sets the
  55. # visibility attribute of all patches of each histogram and the opacity
  56. # of the marker itself.
  57. script = """
  58. <script type="text/ecmascript">
  59. <![CDATA[
  60. var container = %s
  61. function toggle(oid, attribute, values) {
  62. /* Toggle the style attribute of an object between two values.
  63. Parameters
  64. ----------
  65. oid : str
  66. Object identifier.
  67. attribute : str
  68. Name of style attribute.
  69. values : [on state, off state]
  70. The two values that are switched between.
  71. */
  72. var obj = document.getElementById(oid);
  73. var a = obj.style[attribute];
  74. a = (a == values[0] || a == "") ? values[1] : values[0];
  75. obj.style[attribute] = a;
  76. }
  77. function toggle_hist(obj) {
  78. var num = obj.id.slice(-1);
  79. toggle('leg_patch_' + num, 'opacity', [1, 0.3]);
  80. toggle('leg_text_' + num, 'opacity', [1, 0.5]);
  81. var names = container['hist_'+num]
  82. for (var i=0; i < names.length; i++) {
  83. toggle(names[i], 'opacity', [1,0])
  84. };
  85. }
  86. ]]>
  87. </script>
  88. """ % json.dumps(hist_patches)
  89. # Add a transition effect
  90. css = tree.getchildren()[0][0]
  91. css.text = css.text + "g {-webkit-transition:opacity 0.4s ease-out;" + \
  92. "-moz-transition:opacity 0.4s ease-out;}"
  93. # Insert the script and save to file.
  94. tree.insert(0, ET.XML(script))
  95. ET.ElementTree(tree).write("svg_histogram.svg")

下载这个示例