【问题标题】:Update (or redraw?) matplotlib bar chart using y value from onclick使用 onclick 中的 y 值更新(或重绘?)matplotlib 条形图
【发布时间】:2017-07-17 15:28:15
【问题描述】:

我有一个matplotlib 条形图,它使用yerr 来模拟箱线图。

我愿意

  1. 点击此条形图
  2. 获取此次点击的 y 值
  3. 在这个 y 值处画一条红色水平线
  4. 使用scipy.stats.ttest_1samp 运行条形图数据与 y 值的 t 测试
  5. 更新条形图颜色(如果 t t >> 2 则为红色)

我可以分别完成这些步骤中的每一个,但不能一起完成。

我不知道如何反馈 y 值以运行 t-测试并更新图表。我可以在第一次运行时提供一个 y 值 并正确为条形图着色,但我无法通过单击 y 值更新条形图。

这里有一些玩具数据。

import pandas as pd
import numpy as np

np.random.seed(12345)

df = pd.DataFrame([np.random.normal(32000,200000,3650), 
                   np.random.normal(43000,100000,3650), 
                   np.random.normal(43500,140000,3650), 
                   np.random.normal(48000,70000,3650)], 
                  index=[1992,1993,1994,1995])

这是我拼凑起来绘制图表并添加线条的内容。我还想添加一个将颜色映射到 t 统计数据的插图,但我认为这与更新条形图是分开的,我可以自己添加。

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

class PointPicker(object):
    def __init__(self, df, y=0):

        # moments for bar chart "box plot"
        mus = df.mean(axis=1)
        sigmas = df.std(axis=1)
        obs = df.count(axis=1)
        ses = sigmas / np.sqrt(obs - 1)
        err = 1.96 * ses
        Nvars = len(df)

        # map t-ststistics to colors
        ttests = ttest_1samp(df.transpose(), y)
        RdBus = plt.get_cmap('RdBu')
        colors = RdBus(1 / (1 + np.exp(ttests.statistic)))

        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(111)

        # bar chart "box plot"
        self.ax.bar(list(range(Nvars)), mus, yerr=ci, capsize=20, picker=5, color=colors)
        plt.xticks(list(range(Nvars)), df.index)
        plt.tick_params(top='off', bottom='off', left='off', right='off', labelleft='on', labelbottom='on')
        plt.gca().get_yaxis().set_major_formatter(matplotlib.ticker.FuncFormatter(lambda x, p: format(int(x), ',')))
        plt.title('Random Data for 1992 to 1995')

        self.fig.canvas.mpl_connect('pick_event', self.onpick)
        self.fig.canvas.mpl_connect('key_press_event', self.onpress)

    def onpress(self, event):
        """define some key press events"""
        if event.key.lower() == 'q':
            sys.exit()

    def onpick(self,event):
        x = event.mouseevent.xdata
        y = event.mouseevent.ydata
        self.ax.axhline(y=y, color='red')
        self.fig.canvas.draw()

if __name__ == '__main__':

    plt.ion()
    p = PointPicker(df, y=32000)
    plt.show()

点击后出现水平线,但条形图颜色不更新。

【问题讨论】:

  • 您能否检查一下这个问题中是否还没有回答:stackoverflow.com/questions/43133017/… 至少它非常相似,可以使用完全相同的数据并根据需要更新颜色。至于统计数据,您可以编辑您的问题以更具体地提出问题。
  • @ImportanceOfBeingErnest 是的,这实现了相同的全局目标。我可以调整它来做我想做的事情(使用置信区间、t-tests 等)。我仍然对如何传回点击数据感兴趣......尽管您的链接解决方案是一个更好的选择。
  • 我不确定我是否理解您所说的“传回点击数据”是什么意思。下面的答案能解决这个问题吗?如果是,您可以接受,如果不是,我建议您更详细地了解您的要求,并明确说明链接问题的答案以及下面的答案对您没有帮助的程度。
  • @ImportanceOfBeingErnest Tom 的回答显示了如何使用 y 值来更新图表。链接的答案也有帮助。谢谢。

标签: python pandas matplotlib


【解决方案1】:

您想使用 onpick 中的新 y 值重新计算 ttests。然后,您可以像以前一样重新计算颜色。然后,您可以循环使用 ax.bar 创建的条形(这里我将它们保存为 self.bars 以便于访问),并使用 bar.set_facecolor 和新计算的颜色。

我还添加了一个尝试,除了构造以在您第二次单击时更改该行的 yvalue,而不是创建一个新行。

import pandas as pd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from scipy.stats import ttest_1samp

np.random.seed(12345)

df = pd.DataFrame([np.random.normal(32000,200000,3650), 
                   np.random.normal(43000,100000,3650), 
                   np.random.normal(43500,140000,3650), 
                   np.random.normal(48000,70000,3650)], 
                  index=[1992,1993,1994,1995])


class PointPicker(object):
    def __init__(self, df, y=0):

        # Store reference to the dataframe for access later
        self.df = df

        # moments for bar chart "box plot"
        mus = df.mean(axis=1)
        sigmas = df.std(axis=1)
        obs = df.count(axis=1)
        ses = sigmas / np.sqrt(obs - 1)
        err = 1.96 * ses
        Nvars = len(df)

        # map t-ststistics to colors
        ttests = ttest_1samp(df.transpose(), y)
        RdBus = plt.get_cmap('RdBu')
        colors = RdBus(1 / (1 + np.exp(ttests.statistic)))

        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(111)

        # bar chart "box plot". Store reference to the bars here for access later
        self.bars = self.ax.bar(
                list(range(Nvars)), mus, yerr=ses, capsize=20, picker=5, color=colors)
        plt.xticks(list(range(Nvars)), df.index)
        plt.tick_params(top='off', bottom='off', left='off', right='off', labelleft='on', labelbottom='on')
        plt.gca().get_yaxis().set_major_formatter(matplotlib.ticker.FuncFormatter(lambda x, p: format(int(x), ',')))
        plt.title('Random Data for 1992 to 1995')

        self.fig.canvas.mpl_connect('pick_event', self.onpick)
        self.fig.canvas.mpl_connect('key_press_event', self.onpress)

    def onpress(self, event):
        """define some key press events"""
        if event.key.lower() == 'q':
            sys.exit()

    def onpick(self,event):
        x = event.mouseevent.xdata
        y = event.mouseevent.ydata

        # If a line already exists, just update its y value, else create a horizontal line
        try:
            self.line.set_ydata(y)
        except:
            self.line = self.ax.axhline(y=y, color='red')

        # Recalculate the ttest
        newttests = ttest_1samp(df.transpose(), y)
        RdBus = plt.get_cmap('RdBu')
        # Recalculate the colors
        newcolors = RdBus(1 / (1 + np.exp(newttests.statistic)))

        # Loop over bars and update their colors
        for bar, col in zip(self.bars, newcolors):
            bar.set_facecolor(col)

        self.fig.canvas.draw()

if __name__ == '__main__':

    #plt.ion()
    p = PointPicker(df, y=32000)
    plt.show()

这是一些示例输出:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-28
    • 2017-12-24
    • 1970-01-01
    相关资源
    最近更新 更多