Matplotlib interactive bar chart

Viewed 1188

I am trying to explore interactive feature in matplotlib, basically user picks a y value by clicking on the graph, depending on the value the user picked, a horizontal line is drawn. And according to that line the color of barchart should change (how far is the value from the mean).

My program draws the user picked value but the color of bars do not change according. The click event calls my compare value function which draws the line but do not change color. My code is as follows, any help would be appreciated

import matplotlib.pyplot as plt
import matplotlib as mpl
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])
df=df.T
n = len(df)
std = df.std()
means = df.mean() 


ci = (1.96*std/(n**0.5))
cu = list(means + ci)
cl = list(means - ci)
yerror = list(zip(cl , cu))
lab =list(df.columns)
x = np.arange(len(lab))


my_cmap = plt.cm.get_cmap('coolwarm')
norm = mpl.colors.Normalize(vmin=0.,vmax=1.)


def cmp_val(n):
    data_c=list((n - means))
    data_c = [x / max(data_c) for x in data_c]
    for i in range(len(data_c)):
        if data_c[i] > 0:
            my_cmap = plt.cm.get_cmap('Blues')
            colors = my_cmap(norm(data_c[i]))
            bar[i].set_facecolor(colors)
        if data_c[i] < 0:
            my_cmap = plt.cm.get_cmap('Reds')
            colors = my_cmap(norm(data_c[i]*-1))
            bar[i].set_facecolor(colors)
    plt.axhline(y=n, xmin=0, xmax=1, c = 'lightslategray', linestyle = ':')

    return n


plt.figure()
bar=plt.bar(x ,list(means), width=x[1]-x[0], edgecolor='black',  yerr= ci,capsize= 20)
plt.xticks(x, lab)

def onclick(event):
    plt.cla()
    bar=plt.bar(x ,list(means), width=x[1]-x[0], edgecolor='black',  yerr= ci,capsize= 20)
    cmp_val(event.ydata)

    plt.gca().set_title('{}'.format(event.ydata))
    plt.xticks(x, lab)

plt.gcf().canvas.mpl_connect('button_press_event', onclick)

plt.show()
1 Answers

I'm not sure I understood how you wanted to normalize your color coding, but I rewrote your code to make it work. Hopefully you'll be able to adapt the code to your needs:

import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
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])
df = df.T
n = len(df)
std = df.std()
means = df.mean()

ci = (1.96 * std / (n ** 0.5))
cu = list(means + ci)
cl = list(means - ci)
yerror = list(zip(cl, cu))
lab = list(df.columns)
x = np.arange(len(lab))

my_cmap = plt.cm.get_cmap('coolwarm_r')
my_norm = mcolors.Normalize(vmin=-means.max(), vmax=means.max())


def color_bars(val, rectangles, cmap, norm):
    heights = np.array([b.get_height() for b in rectangles])
    diff = heights - val
    colors = cmap(norm(diff))
    for rectangle, color in zip(rectangles, colors):
        rectangle.set_facecolor(color)
    fig.canvas.draw()


fig, ax = plt.subplots()
bars = ax.bar(x, means, width=x[1] - x[0], edgecolor='black', yerr=ci, capsize=20)
hline = ax.axhline(y=0, c='lightslategray', linestyle=':')
ax.set_xticks(x)
ax.set_xticklabels(lab)


def onclick(event):
    if event.inaxes:
        ax.set_title('{:.2f}'.format(event.ydata))
        hline.set_ydata([event.ydata, event.ydata])
        color_bars(event.ydata, bars, cmap=my_cmap, norm=my_norm)
        fig.canvas.draw()


fig.canvas.mpl_connect('button_press_event', onclick)

plt.show()
Related