Plot a vertical Normal Distribution in Python - python

This is my current code of my plotting with matplotlib:
from matplotlib import pyplot
import numpy as np
std=1.5
al=0.6
dpi=80
target=38.9675
mc_min=np.array([10-std, 15-std, 20-std, 25-std, 30-std, 35-std])
mc_max=np.array([2*std, 2*std, 2*std, 2*std, 2*std, 2*std])
mc_min_out=np.array([40-std, 45-std])
mc_max_out=np.array([2*std, 2*std])
x = np.linspace(10, 35, 6)
x_out=np.linspace(40, 45, 2)
a=35+((target-35)*1.5)
b=((target-35)*1.5)
#8,6
pyplot.figure(num=None, figsize=(8, 6), dpi=dpi, facecolor='w', edgecolor='k')
pyplot.bar(x, mc_min, width=3, color ='#000000', align='center', alpha=1)
pyplot.bar(x_out, mc_min_out, width=3, color ='#000000', align='center', alpha=al/2)
pyplot.bar(x, mc_max, width=3, bottom=mc_min, color ='#ff0000', align='center', alpha=al)
pyplot.bar(x_out, mc_max_out, width=3, bottom=mc_min_out, color ='#ff0000', align='center', alpha=al/2)
pyplot.scatter(35, target, s=20, c='y')
pyplot.scatter(35, a, s=20, c='b')
pyplot.scatter(30, a-5, s=20, c='b')
pyplot.scatter(25, a-10, s=20, c='b')
pyplot.scatter(20, a-15, s=20, c='b')
pyplot.scatter(15, a-20, s=20, c='b')
pyplot.scatter(10, a-25, s=20, c='b')
pyplot.axvline(x=35, ymin=0, ymax = 0.9, linewidth=1, color='k')
pyplot.axvline(x=30, ymin=0, ymax = 0.9, linewidth=1, color='k')
pyplot.axvline(x=25, ymin=0, ymax = 45, linewidth=1, color='k')
pyplot.axvline(x=20, ymin=0, ymax = 45, linewidth=1, color='k')
pyplot.axvline(x=15, ymin=0, ymax = 45, linewidth=1, color='k')
pyplot.axvline(x=10, ymin=0, ymax = 45, linewidth=1, color='k')
pyplot.axhline(y=10, xmin=0.04, xmax=0.12, linewidth=1, color='k')
pyplot.axhline(y=15, xmin=0.16, xmax=0.242, linewidth=1, color='k')
pyplot.axhline(y=20, xmin=0.278, xmax=0.36, linewidth=1, color='k')
pyplot.axhline(y=25, xmin=0.4, xmax=0.48, linewidth=1, color='k')
pyplot.axhline(y=30, xmin=0.515, xmax=0.6, linewidth=1, color='k')
pyplot.axhline(y=35, xmin=0.64, xmax=0.72, linewidth=1, color='k')
pyplot.axhline(y=target, xmin=0.67, xmax=0.69, linewidth=1, color='k')
pyplot.axhline(y=(a+b), xmin=0.66, xmax=0.70, linewidth=1, color='k')
pyplot.axhline(y=(a-5+b), xmin=0.54, xmax=0.58, linewidth=1, color='k')
pyplot.axhline(y=(a-10+b), xmin=0.42, xmax=0.46, linewidth=1, color='k')
pyplot.axhline(y=(a-15+b), xmin=0.3, xmax=0.34, linewidth=1, color='k')
pyplot.axhline(y=(a-20+b), xmin=0.18, xmax=0.22, linewidth=1, color='k')
pyplot.axhline(y=(a-25+b), xmin=0.06, xmax=0.10, linewidth=1, color='k')
pyplot.yticks(np.arange(0, 56, 5))
And this is the result:
My problem is that I want to plot a normal distribution in the vertical line that crosses the 35 x-positioned bar. The normal distribution would have a mean equal to the variable "a" and a standard deviation of value "b" and will fit between the edge of the red bar (35 x-positioned) and the top horizontal line that crosses the vertical 35 x-positioned line. The result would be as the second photo.

You can plot a Gaussian function in the position you want by adding x- and y-offsets to the plotted data. Here's an example function:
def draw_gaussian_at(support, sd=1.0, height=1.0,
xpos=0.0, ypos=0.0, ax=None, **kwargs):
if ax is None:
ax = plt.gca()
gaussian = np.exp((-support ** 2.0) / (2 * sd ** 2.0))
gaussian /= gaussian.max()
gaussian *= height
return ax.plot(gaussian + xpos, support + ypos, **kwargs)
xpos and ypos direct the center of the curve to that location, and sd and height control the shape of the curve. Use a negative value for height to have the curve "face" to the left. The support parameter is the range of y-values over which the curve runs, so in your case it would be something like np.linspace(a - 3.0 * b, a + 3.0 * b, 1000), which would plot the curve over 3 standard deviations centered at a.
Here's an example of the function's usage:
support = np.linspace(-2, 2, 1000)
fig, ax = plt.subplots()
for each in np.linspace(-2, 2, 5):
draw_gaussian_at(support, sd=0.5, height=-0.5, xpos=each, ypos=each, ax=ax, color='k')

Related

How to remove a particular grid line?

please see the result graph image below.
I wish to remove only one major grid line at y-axis value of 10 (Blue horizontal line), and keep all other grid lines.
Is there a way to do that?
plt.rcParams['font.family'] = 'Arial'
fig, ax = plt.subplots(figsize=(14.78, 9.84))
plt.xlim(0, 105)
plt.ylim(0, 10)
ax.xaxis.set_minor_locator(AutoMinorLocator(2))
ax.yaxis.set_minor_locator(AutoMinorLocator(2))
ax.spines['bottom'].set_linewidth(1.5)
ax.spines['left'].set_linewidth(1.5)
ax.spines['top'].set_linewidth(0)
ax.spines['right'].set_linewidth(0)
# Grid setting
plt.grid(True, color='#0100FF', which="major", ls="-")
plt.grid(True, color='#0BC904', which="minor", ls="-")
plt.xlabel("Indicator Amplitude, %FSH", fontsize=28, labelpad=15)
plt.ylabel("Function Generator Output, V", fontsize=28, labelpad=15)
# Axis setting
plt.tick_params(which="major", labelsize=22, length=10, pad=10, width=1.5)
plt.tick_params(which="minor", length=8, width=1.5)
# Plot scatter & line
plt.plot(FSH_axis, x_value[2:], color='black', marker='^', linewidth=1.5, markersize=8, label="40 dB")
plt.plot(FSH_axis, y_value[2:], color='red', marker='o', linewidth=1.5, markersize=8, label="60 dB")
plt.plot(FSH_axis, z_value[2:], color='blue', marker='v', linewidth=1.5, markersize=8, label="80 dB")
plt.legend(loc=(1 / 16, 58 / 90), ncol=1, fontsize=20, frameon=True, framealpha=1, edgecolor="black")
plt.show()
We can catch all gridlines with get_ygridlines(), then access individual gridlines as Line2D objects to modify them:
from matplotlib import pyplot as plt
from matplotlib.ticker import AutoMinorLocator
plt.rcParams['font.family'] = 'Arial'
fig, ax = plt.subplots(figsize=(14.78, 9.84))
plt.xlim(0, 105)
plt.ylim(0, 10)
ax.xaxis.set_minor_locator(AutoMinorLocator(2))
ax.yaxis.set_minor_locator(AutoMinorLocator(2))
ax.spines['bottom'].set_linewidth(1.5)
ax.spines['left'].set_linewidth(1.5)
ax.spines['top'].set_linewidth(0)
ax.spines['right'].set_linewidth(0)
# Grid setting
plt.grid(True, color='#0100FF', which="major", ls="-")
plt.grid(True, color='#0BC904', which="minor", ls="-")
#this part is added
#set the last horizontal gridline invisible
ygridlines = ax.get_ygridlines()
gridline_of_interest = ygridlines[-1]
gridline_of_interest.set_visible(False)
plt.xlabel("Indicator Amplitude, %FSH", fontsize=28, labelpad=15)
plt.ylabel("Function Generator Output, V", fontsize=28, labelpad=15)
# Axis setting
plt.tick_params(which="major", labelsize=22, length=10, pad=10, width=1.5)
plt.tick_params(which="minor", length=8, width=1.5)
# Plot scatter & line
FSH_axis = [10, 40, 100]
plt.plot(FSH_axis, [1, 3, 2], color='black', marker='^', linewidth=1.5, markersize=8, label="40 dB")
plt.plot(FSH_axis, [2, 2, 3], color='red', marker='o', linewidth=1.5, markersize=8, label="60 dB")
plt.plot(FSH_axis, [2, 1, 1], color='blue', marker='v', linewidth=1.5, markersize=8, label="80 dB")
plt.legend(loc=(1 / 16, 58 / 90), ncol=1, fontsize=20, frameon=True, framealpha=1, edgecolor="black")
plt.show()
Sample output:
Of course, the corresponding get_xgridlines() also exists.

Superimposing plot over errorbars in matplotlib

I am trying to superimpose a plot over my error bars. I have searched online and this seems to be the method to do this. I am expecting the plot to look the way it does however with thin black lines running between the thick colour lines.
plt.figure(figsize=(15, 10), dpi=80)
plt.grid(True, linewidth=0.5, color='#ff0000', linestyle='-')
for i in range(len(B_arrays)):
plt.errorbar(T_arrays[i], B_arrays[i], STD_arrays[i], linestyle='None', marker='^', label = labels[i])
plt.plot(T_arrays[i], B_arrays[i], color = "k")
plt.ylabel("B")
plt.xlabel("Time")
plt.legend(loc="upper right", prop={'size': 8})
plt.show()
Use plt.plot for the black lines, but just adjust the zorder:
Either pull the black lines above with zorder > 2
for t, b, std, label in zip(T_arrays, B_arrays, STD_arrays, labels):
plt.errorbar(t, b, std, linestyle='None', marker='^', label=label)
plt.plot(t, b, color='k', zorder=3)
# ^^^^^^^^
Or push the error bars below with zorder < 2
for t, b, std, label in zip(T_arrays, B_arrays, STD_arrays, labels):
plt.errorbar(t, b, std, linestyle='None', marker='^', label=label, zorder=1)
plt.plot(t, b, color='k')
# ^^^^^^^^
The key value here is 2 because all lines (including error bars) have a default zorder of 2:
Type
Default zorder
Images
0
Patches
1
Lines
2
Major ticks
2.01
Text
3
Legend
5
I found a solution, however it is not the cleanest way. I'm open to better ways to do this if the community has other approaches.
plt.figure(figsize=(15, 10), dpi=80)
plt.grid(True, linewidth=0.5, color='#ff0000', linestyle='-')
for i in range(len(B_arrays)):
plt.errorbar(T_arrays[i], B_arrays[i], STD_arrays[i], linestyle='None', marker='^', label = labels[i])
plt.errorbar(T_arrays[i], B_arrays[i], np.zeros(len(B_arrays[i])),color = "k")
plt.ylabel("B")
plt.xlabel("Time")
plt.legend(loc="upper right", prop={'size': 8})
plt.show()

Colouring the area between two step lines with crossovers

I am trying to fill colours between two-step line plots. I have tried to do the same using fill_between function with step and interpolate parameters. However, I am not getting the output as expected. I am filling the region between two lines after comparing their values. Below is the code. Any help will be appreciated.
fig = plt.figure()
fig.tight_layout()
plt.subplot(2, 2, 1)
p1 = plt.step(df2['datetime'], df2['T1'], color='b', linewidth=3, where = 'post', label ='P1')
p2 = plt.step(df2['datetime'], df2['T3'], color='m', linewidth=3, where = 'post', label ='P2')
p3 = plt.fill_between(df2['datetime'], df2['T1'],df2['T3'], where = df2['T1'] <
df2['T3'],facecolor="blue", color='blue', alpha=0.25, step = 'post',interpolate = True ,label ='A1')
p4 = plt.fill_between(df2['datetime'], df2['T1'],df2['T3'], where = df2['T1'] >
df2['T3'],facecolor="red", color='red', alpha=0.25, step = 'post',interpolate = True, label ='A2')
plt.ylabel("T1", fontsize=12, color='black')
plt.xlabel("Hour", fontsize=12, color='black')
plt.grid(True)
plt.legend(loc='best',fontsize = 10)
plt.xticks(rotation = 90)
plt.subplot(2, 2, 2)
p1 = plt.step(df2['datetime'], df2[‘T2’], color='k', linewidth=3, where = 'post', label ='P1')
p2 = plt.step(df2['datetime'], df2['T3'], color='m', linewidth=3, where = 'post', label ='P2')
p3 = plt.fill_between(df2['datetime'], df2[‘T2’],df2['T3'], where = df2[‘T2’] <
df2['T3'],facecolor="blue", color='blue', alpha=0.25, step = 'post',label ='A1')
p4 = plt.fill_between(df2['datetime'], df2[‘T2’],df2['T3'], where = df2[‘T2’] >
df2['T3'],facecolor="red", color='red', alpha=0.25, step = 'post',label ='A2')
plt.ylabel("T2", fontsize=12, color='black')
plt.xlabel("Hour", fontsize=12, color='black')
plt.grid(True)
plt.legend(loc='best',fontsize = 10)
plt.xticks(rotation = 90)
I am also attaching my output for reference.
Left plot is with step = post and interpolate = True
Right plot is without interpolate.
As you can see filling in not working as expected near cross overs.
Apparently, fill_between between step plots and using a where parameter doesn't fill as expected.
A workaround is to mimic the step function via np.repeat:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(20)
y1 = np.random.rand(20)
y2 = np.random.rand(20)
xx = np.repeat(x, 2)[1:]
yy1 = np.repeat(y1, 2)[:-1]
yy2 = np.repeat(y2, 2)[:-1]
fig, (ax1, ax2, ax3) = plt.subplots(ncols=3, figsize=(18, 4))
for ax in (ax1, ax2, ax3):
ax.step(x, y1, color='r', lw=3, where='post')
ax.step(x, y2, color='b', lw=3, where='post')
ax1.fill_between(x, y1, y2, color='b', alpha=0.3, step='post', where=y1 < y2)
ax1.fill_between(x, y1, y2, color='r', alpha=0.3, step='post', where=y1 > y2)
ax1.set_title('fill_between with step and where')
ax2.fill_between(x, y1, y2, color='b', alpha=0.3, step='post', where=y1 < y2, interpolate=True)
ax2.fill_between(x, y1, y2, color='r', alpha=0.3, step='post', where=y1 > y2, interpolate=True)
ax2.set_title('setting interpolate=True')
ax3.fill_between(xx, yy1, yy2, color='b', alpha=0.3, where=yy1 < yy2)
ax3.fill_between(xx, yy1, yy2, color='r', alpha=0.3, where=yy1 > yy2)
ax3.set_title('mimicking step')
plt.tight_layout()
plt.show()

matplotlib annotation overlapping y_tick labels on plot

I have tried a number of different things to fix my chart, from zorder on the plots to plt.rcParams.
I feel that this is such a simple problem but I just dont know where I have gone wrong. As you can see the bottom annotation in cyan blue is unreadable and mashed with the y label.
Ideally, the annotation sits over the y label to a point where text inside annotation is readable.
If possible just for the annotation to sit on top and still overlay the y label..something like this
Any help on this would be greatly appreciated.
ax = df.plot(x=df.columns[0], y=df.columns[1], legend=False, zorder=0, linewidth=1)
y1 =df.loc[:, df.columns[2]].tail(1)
y2= df.loc[:, df.columns[1]].tail(1)
colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
print(colors)
for var in (y1, y2):
plt.annotate('%0.2f' % var.max(), xy=(1, var.max()), zorder=1, xytext=(8, 0),
xycoords=('axes fraction', 'data'),
textcoords='offset points',
bbox=dict(boxstyle="round", fc=colors[0], ec=colors[0],))
ax2 = ax.twinx()
df.plot(x=df.columns[0], y=df.columns[2], ax=ax2, legend=False, color='#fa8174', zorder=0,linewidth=1)
ax.figure.legend(prop=subtitle_font)
ax.grid(True, color="white",alpha=0.2)
pack = [df.columns[1], df.columns[2], freq[0]]
plt.text(0.01, 0.95,'{0} v {1} - ({2})'.format(df.columns[1], df.columns[2], freq[0]),
horizontalalignment='left',
verticalalignment='center',
transform = ax.transAxes,
zorder=10,
fontproperties=subtitle_font)
ax.text(0.01,0.02,"Sources: FRED, Quandl, #Paul92s",
color="white",fontsize=10,
horizontalalignment='left',
transform = ax.transAxes,
verticalalignment='center',
zorder=20,
fontproperties=subtitle_font)
ax.xaxis.set_major_locator(matplotlib.dates.YearLocator())
ax.xaxis.set_minor_locator(matplotlib.dates.MonthLocator((4,7,10)))
ax.xaxis.set_major_formatter(matplotlib.dates.DateFormatter("%Y"))
ax.xaxis.set_minor_formatter(ticker.NullFormatter()) # matplotlib.dates.DateFormatter("%m")
plt.setp(ax.get_xticklabels(), rotation=0, ha="center", zorder=-1)
plt.setp(ax2.get_yticklabels(), rotation=0, zorder=-1)
plt.setp(ax.get_yticklabels(), rotation=0, zorder=-1)
plt.gcf().set_size_inches(14,7)
ax.set_xlabel('Data as of; {0}'.format(df['Date'].max().strftime("%B %d, %Y")), fontproperties=subtitle_font)
y1 =df.loc[:, df.columns[2]].tail(1)
y2= df.loc[:, df.columns[1]].tail(1)
for var in (y1, y2):
plt.annotate('%0.2f' % var.max(), xy=(1, var.max()), zorder=1,xytext=(8, 0),
xycoords=('axes fraction', 'data'),
textcoords='offset points',
bbox=dict(boxstyle="round", fc="#fa8174", ec="#fa8174"))
plt.title('{0}'.format("FRED Velocity of M2 Money Stock v Trade Weighted U.S. Dollar Index: Broad"),fontproperties=heading_font)
ax.texts.append(ax.texts.pop())
ax.set_facecolor('#181818')
ax.figure.set_facecolor('#181818')
plt.rcParams['axes.axisbelow'] = True
I don't figure out why zorder doesn't work, but you can directly set the label style of tick labels:
import matplotlib.pyplot as plt
import numpy as np
from numpy.random import rand
import matplotlib.patches as mpatches
fig, ax = plt.subplots(1, 1)
ax.plot(rand(100), '^', color='r')
for label in ax.get_xticklabels():
label.set_bbox(dict(facecolor='orange'))
ax1 = ax.twinx()
ax1.plot(rand(100), 'o', color='b')
index_to_add_bbox = [2, 4]
ax1_labels = ax1.get_yticklabels()
for i in index_to_add_bbox:
ax1_labels[i].set_bbox(dict(boxstyle='Circle', facecolor='orange'))
plt.show()

Average line for bar chart in matplotlib

How do we draw an average line (horizontal) for a histogram in using matplotlib?
Right now, I'm able to draw the histogram without any issues.
Here is the code I'm using:
## necessary variables
ind = np.arange(N) # the x locations for the groups
width = 0.2 # the width of the bars
plt.tick_params(axis='both', which='major', labelsize=30)
plt.tick_params(axis='both', which='minor', labelsize=30)
ax2 = ax.twinx()
## the bars
rects1 = ax.bar(ind, PAAE1, width,
color='0.2',
error_kw=dict(elinewidth=2,ecolor='red'),
label='PAAE1')
rects2 = ax.bar(ind+width, PAAE2, width,
color='0.3',
error_kw=dict(elinewidth=2,ecolor='black'),
label='PAAE2')
rects3 = ax2.bar(ind+width+width, AAE1, width,
color='0.4',
error_kw=dict(elinewidth=2,ecolor='red'),
label='AAE1')
rects4 = ax2.bar(ind+3*width, AAE2, width,
color='0.5',
error_kw=dict(elinewidth=2,ecolor='black'),
label='AAE3')
maxi = max(dataset[2])
maxi1 = max(dataset[4])
f_max = max(maxi, maxi1)
lns = [rects1,rects2,rects3,rects4]
labs = [l.get_label() for l in lns]
ax.legend(lns, labs, loc='upper center', ncol=4)
# axes and labels
ax.set_xlim(-width,len(ind)+width)
ax.set_ylim(0, 100)
ax.set_ylabel('PAAE', fontsize=25)
ax2.set_ylim(0, f_max+500)
ax2.set_ylabel('AAE (mW)', fontsize=25)
xTickMarks = dataset[0]
ax.set_xticks(ind+width)
xtickNames = ax.set_xticklabels(xTickMarks)
plt.setp(xtickNames, rotation=90, fontsize=25)
I want to plot the average line for PAAE 1, 2 and AAE 1, 2.
What should I be using to plot the average line?
If you'd like a vertical line to denote the mean use axvline(x_value). This will place a vertical line that always spans the full (or specified fraction of) y-axis. There's also axhline for horizontal lines.
In other works, you might have something like this:
ax.axvline(data1.mean(), color='blue', linewidth=2)
ax.axvline(data2.mean(), color='green', linewidth=2)
As a more complete, but unnecessarily complex example (most of this is nicely annotating the means with curved arrows):
import numpy as np
import matplotlib.pyplot as plt
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(-2, 1.5, 1000)
fig, ax = plt.subplots()
bins = np.linspace(-10, 5, 50)
ax.hist(data1, bins=bins, color='blue', label='Dataset 1',
alpha=0.5, histtype='stepfilled')
ax.hist(data2, bins=bins, color='green', label='Dataset 2',
alpha=0.5, histtype='stepfilled')
ax.axvline(data1.mean(), color='blue', linewidth=2)
ax.axvline(data2.mean(), color='green', linewidth=2)
# Add arrows annotating the means:
for dat, xoff in zip([data1, data2], [15, -15]):
x0 = dat.mean()
align = 'left' if xoff > 0 else 'right'
ax.annotate('Mean: {:0.2f}'.format(x0), xy=(x0, 1), xytext=(xoff, 15),
xycoords=('data', 'axes fraction'), textcoords='offset points',
horizontalalignment=align, verticalalignment='center',
arrowprops=dict(arrowstyle='-|>', fc='black', shrinkA=0, shrinkB=0,
connectionstyle='angle,angleA=0,angleB=90,rad=10'),
)
ax.legend(loc='upper left')
ax.margins(0.05)
plt.show()

Categories