使用matplotlib为svg线图创建工具提示

时间:2015-05-06 04:38:23

标签: python svg matplotlib linechart

我想为使用matplotlib创建的SVG折线图创建工具提示(显示数据值)。这将使用户能够在显示该特定点的值的线图中的每个标记上单击或鼠标悬停。

为了能够这样做,我需要访问Line2D对象的标记元素,并为每个这样的元素添加一个gid。

http://matplotlib.org/examples/user_interfaces/svg_histogram.html处,有一个如何对直方图进行此操作的示例。

In [79]: ds1
Out[79]: 
array([[ 4, 13,  6,  9],
       [ 7, 12,  5,  7],
       [ 7,  0,  4, 22],
       [ 9,  8, 12,  0]])

In [80]: ds2
Out[80]: 
array([[ 4,  1],
       [ 5,  3],
       [ 6,  1],
       [ 7,  2],
       [ 8,  2],
       [ 9,  3],
       [12,  1],
       [13,  2],
       [22,  3]])

In [81]: out
Out[81]: 
array([[1, 2, 1, 3],
       [2, 1, 3, 2],
       [2, 0, 1, 3],
       [3, 2, 1, 0]])

但是,如果我尝试使用折线图,我发现Line2D对象不可迭代 - 可能是因为它不是直方图条形图等补丁的集合。

H = plt.hist([r,r1], label=labels)
containers = H[-1]
hist_patches = {}
for ic, c in enumerate(containers):
    hist_patches['hist_%d'%ic] = []
    for il, element in enumerate(c):
        element.set_gid('hist_%d_patch_%d'%(ic, il))
        hist_patches['hist_%d'%ic].append('hist_%d_patch_%d'%(ic,il))

这导致:“TypeError:'Line2D'对象不可迭代”

问题是如何从Line2D

访问单个标记

使用matplotlib的交互式后端有一些方法可以做到这一点。但我需要为非交互式SVG实现这一点。

2 个答案:

答案 0 :(得分:0)

问题的更好解决方案可能是Charts库。它使您能够使用优秀的Highcharts JavaScript库来制作精美的互动图。 Highcharts使用HTML svg标记。

默认情况下,所有图表都有交互式工具提示!

免责声明:我是图书馆的开发者。

答案 1 :(得分:0)

我在OP linked code snippet中修改了代码

这至少对我有用。但是,我还没有找到区分单个标记的方法

import matplotlib.pyplot as plt
import numpy as np
import xml.etree.ElementTree as ET
from io import BytesIO

plt.rcParams['svg.fonttype'] = 'none'
_ , ax = plt.subplots(figsize=(14,8))

Rand_Values = []
Rand_Values.append(np.random.rand(20)) 
Rand_Values.append(np.random.rand(20)) 
x_axis = range(20)

for i in range(2):
    H = ax.plot(x_axis, Rand_Values[i] , label= "data set %i" % (i+1))  
    ThisContainer = H[-1]                                     
    ThisContainer.set_gid('hist_%i' % i )

plt.xlabel("x-axis label") 
plt.ylabel("random data") 
plt.title( "Interactive chart" )  

leg = ax.legend(loc='upper left', bbox_to_anchor=(0, 1), ncol=1, fancybox=True, shadow=True)

for i, t in enumerate(leg.get_texts()):
    t.set_gid('leg_text_%d' % i)

# Save SVG in a fake file object.
f = BytesIO()
plt.savefig(f, format="svg")

# Create XML tree from the SVG file.
ET.register_namespace("", "http://www.w3.org/2000/svg")
tree, xmlid = ET.XMLID(f.getvalue())

# --- Add interactivity ---
# Add attributes to the text objects.
for i, t in enumerate(leg.get_texts()):
    el = xmlid['leg_text_%d' % i]
    el.set('cursor', 'pointer')
    el.set('onclick', "toggle_hist(this)")

# Create script defining the function `toggle_hist`.
script = """
<script type="text/ecmascript">
<![CDATA[

function toggle(oid, attribute, values) {
    /* Toggle the style attribute of an object between two values.

    Parameters
    ----------
    oid : str
      Object identifier.
    attribute : str
      Name of style attribute.
    values : [on state, off state]
      The two values that are switched between.
    */
    var obj = document.getElementById(oid);
    var a = obj.style[attribute];

    a = (a == values[0] || a == "") ? values[1] : values[0];
    obj.style[attribute] = a;
    }

function toggle_hist(obj) {
    var num = obj.id.replace( /^\D+/g, '');
    toggle( 'leg_text_' + num, 'opacity', [1, 0.5]);
    toggle( 'hist_'+ num  , 'opacity', [1,0]);
    }
]]>
</script>
""" 

# Add a transition effect
css = tree.getchildren()[0][0]

css.text = css.text + "g {-webkit-transition:opacity 0.4s ease-out;" + \
    "-moz-transition:opacity 0.4s ease-out;}"


# Insert the script and save to file.
tree.insert(0, ET.XML(script))

ET.ElementTree(tree).write("svg_lineChart.svg")