import matplotlib.pyplot as plt
import numpy as np
x = np.array([6, 15, 24, 33, 41, 52, 59, 66, 73, 81])
y = np.array([5, 10, 15, 20, 25, 30, 35, 40, 45, 50])
coef = np.polyfit(x, y, 1)
poly1d_fn = np.poly1d(coef) # to create a linear function with coefficients
plt.plot(x, y, 'ro', x, poly1d_fn(x), '-b')
plt.errorbar(x, poly1d_fn(x), yerr=poly1d_fn(x) - y, fmt='.k')
plt.show()
I have a working code which produces based upon my input a graph with error bars and the regression line. That's all fine. Now what I wanted to do is add a text box below and once a user inputs a number, e.g. 12 it outputs the according value (re regression line).
left, bottom, width, height = 0.15, 0.02, 0.7, 0.10
plt.subplots_adjust(left=left, bottom=0.25) # Make space for the slider
input_field = plt.axes([left, bottom, width, height])
box = TextBox(input_field, 'value')
I tried it with this approach. Though being unsuccessful: I can't get it to take a value and output it on the GUI interface matplotlib provides. The field would need to be checked for every input. Matplotlib offers on_text_change(self, func)or on_submit(self, func), so that might be working - but how to output?
Does anyone have an idea?
I would use a simple Text artist to display the result. But being fancy, I would also display lines on the graph showing the input and output values.
import matplotlib.pyplot as plt
import numpy as np
x = np.array([6, 15, 24, 33, 41, 52, 59, 66, 73, 81])
y = np.array([5, 10, 15, 20, 25, 30, 35, 40, 45, 50])
coef = np.polyfit(x, y, 1)
poly1d_fn = np.poly1d(coef) # to create a linear function with coefficients
def submit(val):
try:
x = float(val)
y = poly1d_fn(x)
ax.annotate('', xy=(x,0), xycoords=('data','axes fraction'),
xytext=(x,y), textcoords='data',
arrowprops=dict(arrowstyle='-', ls='--'))
ax.annotate(f'{x:.2f}', xy=(x,0), xycoords=('data','axes fraction'))
ax.annotate('', xy=(0,y), xycoords=('axes fraction','data'),
xytext=(x,y), textcoords='data',
arrowprops=dict(arrowstyle='-', ls='--'))
ax.annotate(f'{y:.2f}', xy=(0,y), xycoords=('axes fraction','data'))
output_box.set_text(f'Result = {y:.2f}')
plt.draw()
except ValueError:
pass
fig, ax = plt.subplots()
ax.plot(x, y, 'ro', x, poly1d_fn(x), '-b')
ax.errorbar(x, poly1d_fn(x), yerr=poly1d_fn(x) - y, fmt='.k')
left, bottom, width, height, pad = 0.15, 0.02, 0.3, 0.10, 0.1
fig.subplots_adjust(left=left, bottom=0.25) # Make space for the slider
input_field = fig.add_axes([left, bottom, width, height])
text_box = matplotlib.widgets.TextBox(input_field, 'value')
text_box.on_submit(submit)
output_box = fig.text(left+width+pad, bottom+height/2, s='Result = ', va='center')
Related
Edit:
I tried the following.
auto_y_ticks=list(axes2.get_yticklabels())
But still the output is not a list of tick values. It shows as Matplotlib text.
The following code produces bar and line plot in the same graph.
In the secondary y axis, the ytick values range from -1 to +1.
My question is, how do I store these values in a list?
from matplotlib import pyplot as plt
import numpy as np
plt.figure()
N = 5
menMeans = (20, 35, 30, 35, 27)
menStd = (2, 3, 4, 1, 2)
width = 0.35 # the width of the bars
womenMeans = (25, 32, 34, 20, 25)
womenStd = (3, 5, 2, 3, 3)
ind = np.arange(N)
plt.ylim(0.0, 65.0)
plt.bar(ind, menMeans, width, color='r', yerr=menStd, label='Men means')
plt.bar(ind+width, womenMeans, width, color='y', yerr=womenStd, label='Women means')
plt.ylabel('Bar plot')
x = np.linspace(0, N)
y = np.sin(x)
axes2 = plt.twinx()
axes2.plot(x, y, color='k', label='Sine')
axes2.set_ylim(-1, 1)
axes2.set_ylabel('Line plot')
plt.show()
auto_y_ticks=list(What's the logic)
Starting from your code,
axes2.get_yticks()
gives
array([-1. , -0.75, -0.5 , -0.25, 0. , 0.25, 0.5 , 0.75, 1. ])
Which is what you're after, right?
I have a scatter plot created from two columns of a pandas data frame and I would like to add a line across each axis representing the average. Is this possible with a scatter plot?
plt.title("NFL Conversion Rates", fontsize=40)
# simulating a pandas df['team'] column
types = df.Tm
x_coords = df['3D%']
y_coords = df['4D%']
binsy = [15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85]
binsx = [30,35,40,45,50,55]
avg_y = y_coords.mean()
avg_y = round(avg_y, 1)
display(avg_y)
avg_x = x_coords.mean()
avg_x = round(avg_x, 1)
display(avg_x)
for i,type in enumerate(types):
x = x_coords[i]
y = y_coords[i]
plt.scatter(x, y, s=30, marker='o', edgecolor='black', cmap='purple', linewidth=1, alpha = 0.5)
plt.text(x+0.2, y+0.1, type, fontsize=14)
plt.xlabel('3rd Down Conversion Percentage',fontsize=30)
plt.ylabel('4th Down Conversion Percentage', fontsize=30)
plt.xticks(binsx)
plt.yticks(binsy)
You can try
plt.axvline(<value>,color='red',ls='--') and plt.axhline(<value>,color='red',ls='--'). Substitute with the value at which you want the lines
I have some problems on controlling the figure size when using matlibplot. I want the figure to be 9cm * 14.5 cm in size exactly. I use "figsize" command to control the figure size; however, the figure exceeds the specified size. Please advise how to achieve this.
The python codes are attached as follows:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize = (9/2.54, 14.5/2.54), nrows = 2, ncols = 1)
ax[0] = plt.subplot(2, 1, 1)
x1 = [30, 40, 50, 60, 70, 80, 90, 100]
y1 = [6, 7, 8, 9, 10, 11, 12, 13]
ax[0].plot(x1, y1, 'r-s', label = 'XXXXXXXX')
ax[0].set_xlabel('Percentage load (%)')
ax[0].set_ylabel('Output parameter (%)')
ax[0].legend(loc = (0.05, 0.8))
ax[0].text(0.05, 0.7, '(a)', transform = ax[0].transAxes)
ax[0].set_xlim(x1[0], x1[-1])
x2 = [30, 40, 50, 60, 70, 80, 90, 100]
y2 = [6, 7, 8, 9, 10, 11, 12, 13]
ax[1] = plt.subplot(2, 1, 2)
ax[1].plot(x2, y2, 'k-o', label = 'YYYYYYYYY')
ax[1].set_xlabel('Percentage load (%)')
ax[1].set_ylabel('Output parameter (%)')
ax[1].legend(loc = (0.05, 0.8))
ax[1].text(0.05, 0.7, '(b)', transform = ax[1].transAxes)
ax[1].set_xlim(x2[0], x2[-1])
plt.subplots_adjust(top=1, bottom=0.0, left=0.0, right=1, hspace=0.2, wspace=0.0)
plt.savefig(fname = '2figax.png', dpi = 600, quality = 95, bbox_inches = 'tight', pad_inches = None)
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from matplotlib import cm
#from matplotlib.patches import Circle
ri = 100
ra = 300
h=20
# input xy coordinates
xy = np.array([[ri,0],[ra,0],[ra,h],[ri,h],[ri,0]])
# radial component is x values of input
r = xy[:,0]
# angular component is one revolution of 30 steps
phi = np.linspace(0, 2*np.pi, 50)
# create grid
R,Phi = np.meshgrid(r,phi)
# transform to cartesian coordinates
X = R*np.cos(Phi)
Y = R*np.sin(Phi)
# Z values are y values, repeated 30 times
Z = np.tile(xy[:,1],len(Y)).reshape(Y.shape)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
ax.set_zlim(0,200)
ax.plot_surface(X, Y, Z, alpha=0.5, color='grey', rstride=1, cstride=1, linewidth=0, edgecolor='none')
arr = np.array([[100, 15],
[114.28, 17],
[128.57, 18],
[142.85, 24],
[157.13, 26],
[171.13, 28],
[185.69, 29],
[199.97, 30],
[214.25, 31],
[228.53, 32],
[242.81, 35],
[257.09, 36],
[271.37, 37],
[288.65, 40]])
#interpolating between the single values of the arrays
new_x = np.concatenate([np.linspace(arr[i,0],arr[i+1,0], num=20)
for i in range(len(arr)-1)])
new_y = np.interp(new_x, arr[:,0], arr[:,1])
t=np.arange(260)
tmp_phi = np.linspace(0,2*np.pi,20)[:,None] # angle data
linesurf_x = new_x*np.cos(tmp_phi)
linesurf_y = new_x*np.sin(tmp_phi)
linesurf_z = np.broadcast_to(new_y, linesurf_x.shape)
linesurf_c = np.broadcast_to(t, linesurf_x.shape) # color according to t
colors = cm.jet(linesurf_c/linesurf_c.max()) # grab actual colors for the surface
ax.plot_surface(linesurf_x, linesurf_y, 1.5*linesurf_z, facecolors=colors,
rstride=1, cstride=3, linewidth=1, edgecolor='none')
cax, _ = mpl.colorbar.make_axes(plt.gca(), shrink=0.8)
cax.yaxis.set_ticks_position('right')
cbar = mpl.colorbar.ColorbarBase(cax, cmap='jet', label='test',
norm=mpl.colors.Normalize(vmin=15, vmax=41))
plt.show()
The problem is the speed. It calculates quite a long time but this is not the biggest problem. After the graph has been plotted, it is very laggy when I try to rotate the graph... Is there a possibility to increase the speed without to much effort? I googled and I read that maybe matplotlib is not the most efficient tool for plotting scatter plots. If it is true, is it very difficult to change the library? This is only a part of my code and further I am using canvas.
Matplotlib is not designed for 3d plots and is aimed at high quality (printable) graphs and not at speed. I would use another library such as mayavi for 3d visualization. Here is your code with mayavi visualization
import numpy as np
import mayavi.mlab as mlab
ri = 100
ra = 300
h=20
# input xy coordinates
xy = np.array([[ri,0],[ra,0],[ra,h],[ri,h],[ri,0]])
# radial component is x values of input
r = xy[:,0]
# angular component is one revolution of 30 steps
phi = np.linspace(0, 2*np.pi, 50)
# create grid
R,Phi = np.meshgrid(r,phi)
# transform to cartesian coordinates
X = R*np.cos(Phi)
Y = R*np.sin(Phi)
# Z values are y values, repeated 30 times
Z = np.tile(xy[:,1],len(Y)).reshape(Y.shape)
mlab.mesh(X, Y, Z, color=(0.4,0.4,0.4))
arr = np.array([[100, 15],
[114.28, 17],
[128.57, 18],
[142.85, 24],
[157.13, 26],
[171.13, 28],
[185.69, 29],
[199.97, 30],
[214.25, 31],
[228.53, 32],
[242.81, 35],
[257.09, 36],
[271.37, 37],
[288.65, 40]])
#interpolating between the single values of the arrays
new_x = np.concatenate([np.linspace(arr[i,0],arr[i+1,0], num=20)
for i in range(len(arr)-1)])
new_y = np.interp(new_x, arr[:,0], arr[:,1])
t=np.arange(260)
tmp_phi = np.linspace(0,2*np.pi,20)[:,None] # angle data
linesurf_x = new_x*np.cos(tmp_phi)
linesurf_y = new_x*np.sin(tmp_phi)
linesurf_z = np.broadcast_to(new_y, linesurf_x.shape)
linesurf_c = np.broadcast_to(t, linesurf_x.shape) # color according to t
mlab.mesh(linesurf_x, linesurf_y, 1.5*linesurf_z,scalars=linesurf_c)
mlab.show()
I wanted to display only half error bars, as they are symetric ; as I had no clue how to do this with a "clean way", I chose to use asymetric errors with 0 on the bottom side ; but then, when I displayed caps, I realised this was not the best way to do this.
Here's the code :
import numpy as np
import matplotlib.pyplot as plt
N = 5
men_means = (20, 35, 30, 35, 27)
men_std = (2, 3, 4, 1, 2)
ind = np.arange(N)
width = 0.35
fig, ax = plt.subplots()
rects1 = ax.bar(ind, men_means, width, color='r',yerr=[np.zeros(len(men_std)),men_std],capsize = 5)
women_means = (25, 32, 34, 20, 25)
women_std = (3, 5, 2, 3, 3)
rects2 = ax.bar(ind + width, women_means, width, color='y',yerr=[np.zeros(len(women_std)),women_std],capsize = 5)
plt.show()
And this is the plot I get :. As you can see, my way of plotting half error bars is probably not what should be done.
So is there any way to hide the bottom cap line or a better way to plot half error bars ?
ax.errorbar has the option to set uplims=True or lolims=True to signify that the means repesent the upper or lower limits, respectively. Unfortunately, it doesn't seem like you can use these options directly with ax.bar, so we have to plot the errorbar and the bar plot separately.
The documentation for the uplims/lolims options in ax.errorbar:
lolims / uplims / xlolims / xuplims : bool, optional, default:None
These arguments can be used to indicate that a value gives only upper/lower limits. In that case a caret symbol is used to indicate this. lims-arguments may be of the same type as xerr and yerr. To use limits with inverted axes, set_xlim() or set_ylim() must be called before errorbar().
Note that using this option changes your caps to arrows. See below for an example of how to change them back to caps, if you need flat caps instead of arrows.
You can see these options in action in this example on the matplotlib website.
Now, here's your example, modified:
import numpy as np
import matplotlib.pyplot as plt
N = 5
men_means = (20, 35, 30, 35, 27)
men_std = (2, 3, 4, 1, 2)
ind = np.arange(N)
width = 0.35
fig, ax = plt.subplots()
rects1 = ax.bar(ind, men_means, width, color='r')
err1 = ax.errorbar(ind, men_means, yerr=men_std, lolims=True, capsize = 0, ls='None', color='k')
women_means = (25, 32, 34, 20, 25)
women_std = (3, 5, 2, 3, 3)
rects2 = ax.bar(ind + width, women_means, width, color='y')
err2 = ax.errorbar(ind + width, women_means, yerr=women_std, lolims=True, capsize = 0, ls='None', color='k')
plt.show()
If you don't like the arrows, you change them to flat caps, by changing the marker of the caplines that are returned (as the second item) from ax.errorbar. We can change them from the arrows to the marker style _, and then control their size with .set_markersize:
import numpy as np
import matplotlib.pyplot as plt
N = 5
men_means = (20, 35, 30, 35, 27)
men_std = (2, 3, 4, 1, 2)
ind = np.arange(N)
width = 0.35
fig, ax = plt.subplots()
rects1 = ax.bar(ind, men_means, width, color='r')
plotline1, caplines1, barlinecols1 = ax.errorbar(
ind, men_means, yerr=men_std, lolims=True,
capsize = 0, ls='None', color='k')
caplines1[0].set_marker('_')
caplines1[0].set_markersize(20)
women_means = (25, 32, 34, 20, 25)
women_std = (3, 5, 2, 3, 3)
rects2 = ax.bar(ind + width, women_means, width, color='y')
plotline2, caplines2, barlinecols2 = ax.errorbar(
ind + width, women_means, yerr=women_std,
lolims=True, capsize = 0, ls='None', color='k')
caplines2[0].set_marker('_')
caplines2[0].set_markersize(10)
plt.show()
A simpler solution is to use zorder. The grid has zorder=0. Setting the errorbar to zorder=1 and the bar to zorder=2and lowering the bottom error a bit will hide the lower error bar with little effort. This also allows to use bar_label. The only downside is if alpha is used for the bars.
I also changed to use np.zeros_like(std) instead of np.zeros(len(std)) and use error_kw to style the errorbar.
import numpy as np
import matplotlib.pyplot as plt
N = 5
ind = np.arange(N)
width = 0.8
fig, ax = plt.subplots()
women_means = (25, 32, 34, 20, 25)
women_std = (3, 5, 2, 3, 3)
rects2 = ax.bar(ind + width, women_means, width, color='y',yerr=[np.zeros_like(women_std)+0.5,women_std],zorder=2,error_kw=dict(capsize = 10, capthick=1,zorder=1))
ax.bar_label(rects2,labels=[f'{v:.2f} ± {e:.2f}' for v,e in zip(women_means, women_std)], padding=10, fontsize=14, label_type='edge')
fig.tight_layout()
plt.show()
Many thanks,
based on your answer, method below is my solution:
def process_error_bar(ax, x, y, y_err, marker_size):
"""
hide half error_bar
:param ax: plt.subplots()
:param x: x position
:param y: y position
:param y_err: y errors
:param marker_size: size
"""
lolims = []
uplims = []
for y_value in y:
if y_value < 0:
lolims.append(False)
uplims.append(True)
else:
lolims.append(True)
uplims.append(False)
plotline, caplines, barlinecols = ax.errorbar(
x, y, yerr=y_err, lolims=lolims, uplims=uplims,
capsize=0, ls='None', color='k')
# [arrow] -> [-]
for capline in caplines:
capline.set_marker('_')
capline.set_markersize(marker_size)