Plotting error bars in matplotlib that match scatter colours - python

I've seen quite a few questions along the same vein as this one but they always seem to diverge a little before they answer my question exactly or I can apply them.
I am looking to plot error bars in the same colour scheme as my scatter graph points. If my values were plotted on an x and y axis and I wished them to vary colour with another Z value logarithmically, currently I have:
c = np.abs(zVals)
cmhot = plt.get_cmap("plasma")
sc.set_clim(vmin=min(zVals), vmax=max(zVals))
sc = plt.scatter(xVals, yVals, c=c, norm=mplc.LogNorm(),
s=50, cmap=cmhot, edgecolors='none')
###This section all works fine, it is when I introduce the error bars I struggle
norm = mplc.LogNorm(vmin=min(zVals), vmax=max(zVals)
plt.errorbar(xVals, yVals, yerr = [negyVals,posyVals], c=cmhot(norm(zVals)))
plt.colorbar(sc)
plt.ylim([-10,110])
plt.xlim([1,100])
plt.xscale('log')
plt.show()
This results in an error of the form:
ValueError: to_rgba: Invalid rgba arg ... length of rgba sequence should be either 3 or 4
I am quite confused with the colour situation in general so any help would be much appreciated at the moment. Cheers.

I think this is surprisingly hard to do in matplotlib. The only way I've found is to use a for loop, and plot each point individually.
For example
plt.figure()
#data
x=np.linspace(-10, 10, 100)
y=np.cos(x)
y_error=0.2+0.5*np.random.randn(len(x))
z=np.linspace(0, 10, 100)
cm=plt.get_cmap('plasma')
plt.scatter(x, y, c=z_values, cmap=cm, zorder=10)
for i, (xval, yval, y_error_val, zval) in enumerate(zip(x, y, y_error, z)):
#Get the colour from the colourmap
colour=cm(1.0*zval/np.max(z))
#(could also just do colour=cm(1.0*i/len(x)) here)
#(Or Norm(zval) in your case)
plt.errorbar(xval, yval, yerr=y_error_val, linestyle='', c=colour)
plt.show()
which gives this plot
Obviously this won't be very efficient for large numbers of points!

Related

Python axis scaling in matplotlib

I am trying to make my plots a bit more readable and have come across a feature where the axes are automatically scaled by factors of tens (so instead of the y axis reading 0.00000005, 0.00000007, 0.00000009, it reads 0.5,0.7,0.9 and then says 1e-7 at the top of the axis). However some of my plots don't scale the axes automatically, and I would like to get advise of how to do that manually.
I have found threads on manually setting the tick marks, however I haven't been able to find threads on scaling only.
I can't imbed pictures but here is a link to a picture of what I would like to do: Ideal y axis and here's link to a picture of what I want to avoid: Current y axis.
I'm using seaborn formatting and matplotlib for plots and my code looks like this:
plt.plot(x_j_n,y_j_n, label='Scanning i negativ retning', color='grey', ls='dashed')
plt.plot(x_j_p,y_j_p, label='Scanning i positiv retning', color='black', ls='dashed')
plt.errorbar(x_j_n,y_j_n, yerr=std_j_n, fmt='o', color='black', mfc='white', label = 'Usikkerhed')
plt.errorbar(x_j_p,y_j_p, yerr=std_j_p, fmt='o', color='grey', mfc='white', label = 'Usikkerhed')
plt.ylabel('Målt spænding i volt (V)')
plt.xlabel('Påtrykt felt i tesla (T)')
plt.legend()
plt.show;
Set the y axis to scientific:
plt.gca().yaxis.get_major_formatter().set_scientific(True)
For example:
x = [1100000,2200000,3300000]
y = [1100000,2200000,3300000]
plt.plot(x,y)
plt.gca().xaxis.get_major_formatter().set_scientific(False)
plt.gca().yaxis.get_major_formatter().set_scientific(True)
plt.show()
will give:

matplotlib plot_surface colormap does not scale with the z-axis

I try to make simple 3D plot with plot_surface of matplotlib, below is the minimum example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
x_test = np.arange(0.001, 0.01, 0.0005)
y_test = np.arange(0.1, 100, 0.05)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
Xtest, Ytest = np.meshgrid(x_test, y_test)
Ztest = Xtest**-1 + Ytest
surf = ax.plot_surface(Xtest, Ytest, Ztest,
cmap=cm.plasma, alpha=1,
antialiased=False)
fig.colorbar(surf, shrink=0.5, aspect=5)
ax.set_ylabel(r'$ Y $', fontsize=16)
ax.set_xlabel(r'$ X $', fontsize=16)
ax.set_zlabel(r'$ Z $', fontsize=16)
The result gives strange colormap, that does not represent the magnitude of the z scale, as you can see from here 3D plot result.
I mean if you take a straight line of constant Z, you don't see the same color.
I've tried to change the ccount and rcount inside plot_surface function or changing the interval of the Xtest or Ytest data, nothing helps.
I've tried some suggestion from here and here. But it seems not related.
Could you help me how to solve this? It seems a simple problem, but I couldn't solve it.
Thanks!
Edit
I add example but using the original equation that I don't write it here (it's complicated), please take a look: Comparison.
While the left figure was done in Matlab (by my advisor),
the right figure by using matplotlib.
You can see clearly the plot on the left really make sense,
the brightest color always on the maximum z-axis. Unfortunately, i don't know matlab. I hope i can do it by using python,
Hopefully this edit makes it more clear of the problem.
Edit 2
I'm sure this is not the best solution. That's why I put it here, not as an answer. As suggested by #swatchai to use the contour3D.
Since surface is set of lines, I can generate the correct result by plotting a lot of contour lines, by using:
surf = ax.contour3D(Xtest, Ytest, Ztest, 500, cmap=cm.plasma,
alpha=0.5, antialiased=False)
The colormap is correct as you can see from herealternative1
But the plot is very heavy. When you zoom-in, it doesn't look good, unless you increase again the number of the contour.
Any suggestion are welcome :).
I don't know how this can be achieved but maybe some words on why this is happening.
plot_surface generates a mesh where the vertices are defined by x and y and z.
Each patch has 4 corners and gets a color corresponding to its z value. Looking at the plot it
could be the maximal z value of the 4 corners (just a guess).
And if you look closely the colors of the patches actually do get lighter as you move in +y direction.
But what is far more obvious are the color changes in x direction, producing the slopes you mentioned.
But this can not be avoided if each patch has just a single color.
You can see this maybe more clearly if you change the formula to Z = (X**-1 + 10 * Y)
The behavior of the surface plot is not what you expect. Only contour3D or contourf3D can display such behavior. Here is relevant code that you can try to get the plot that follows:
surf = ax.plot_surface(Xtest, Ytest, Ztest, cmap=cm.plasma, alpha=0.55)
ax.contourf3D(Xtest, Ytest, Ztest, cmap=cm.plasma)
The plot that show both surface and contourf3D:
I guess, the formal answer to plot this kind of surface is by using Axes3D.contour and Axes3D.contourf. Based on documentation
, for example:
surf2 = ax.contourf(Xtest, Ytest, Ztest, 250, cmap=cm.plasma,
alpha=0.6, antialiased=False)
surf = ax.contour(Xtest, Ytest, Ztest, 250, cmap=cm.plasma,
alpha=0.6, antialiased=False)
The result is here. The colormap shows correct z-scale.
It's not as perfect as smooth surface, as it depends on how much we zoom it or how much we put the contour. I don't know if there's a way to create this by plot_surface. thanks #swatchai.

Even after incorporating " linespace = 'None' " in my code, the lines joining up points on graph have not gone away

Trying to rid my graph of the lines that join up the points in order to use my own line of best fit. I have used linespace='None' within my code for plotting the points. Very confused as to why the lines are still there after running the code again.
Any help will be greatly appreciated.
x_a = np.array(x) #defining x and y variables
y_a = np.array(y)
plt.errorbar(x, y, yerr = data) #plotting errorbars
plt.plot(x,y, ls='') #plotting x and y. Attempting to get rid of lines with ls=''
plt.show
The lines are still coming from the plt.errorbar. Use ls='' for both
plt.errorbar(x, y, yerr=data, ls='')
plt.plot(x,y, ls='')
As pointed out by #DavidG in the comments, plt.plot(x,y, ls='') serves no purpose if you hide the lines. Instead, you can use a scatter plot in addition to the errorbar to show data points as
plt.scatter(x,y)

Rainbow scatter plot Python

I have to make the following scatterplot in python. The code for this plot is :
n = 1024
X = np.random.normal(0,1,n)
Y = np.random.normal(0,1,n)
plt.scatter(X,Y)
But as espected, this wont give the colours. I've tried a lot, but can't find the solution. I know it has something to do with the angle of X/Y in the plot, but can't find out how to do this.
The logic is most likely angle from origo to point. This can be calculated easily with np.arctan2(X, Y). I don't know which colormap that is used in your example but you can probably find it here: https://matplotlib.org/examples/color/colormaps_reference.html
Use the angles of the points to the c keyword in plt.scatter
To get something similar to your example:
plt.scatter(X,Y, c=np.arctan2(X, Y), cmap='rainbow', s=50, alpha=0.8)

matplotlib pyplot 2 plots with different axes in same figure

I have a small issue with matplotlib.pyplot and I hope someone might have come across it before.
I have data that contain X,Y,e values that are the X, Y measurements of a variable and e are the errors of the measurements in Y. I need to plot them in a log log scale.
I use the plt.errorbars function to plot them and then set yscale and xscale to log and this works fine. But I need to also plot a line on the same graph that needs to be in linear scale.
I am able to have the plots done separately just fine but I would like to have them in the same image if possible. Do you have any ideas? I am posting what I have done for now.
Cheers,
Kimon
tdlist = np.array([0.01,0.02,0.05,0.1,0.2,0.3,0.4,0.5,0.8,1,2,5,10,15,20,25,30,40,60,80,100,150,200,250,300,400])
freqlist=np.array([30,40,50,60,70,80,90,100,110,120,140,160,180,200,220,250,300,350,400,450])
filename=opts.filename
data = reader(filename)
data2 = logconv(data)
#x,y,e the data. Calculating usefull sums
x = data2[0]
y = data2[1]
e = data2[2]
xoe2 = np.sum(x/e**2)
yoe2 = np.sum(y/e**2)
xyoe2 = np.sum(x*y/e**2)
oe2 = np.sum(1/e**2)
x2oe2 = np.sum(x**2/e**2)
aslope = (xoe2*yoe2-xyoe2*oe2)/(xoe2**2-x2oe2*oe2)
binter = (xyoe2-aslope*x2oe2)/xoe2
aerr = np.sqrt(oe2/(x2oe2*oe2-xoe2**2))
berr = np.sqrt(x2oe2/(x2oe2*oe2-xoe2**2))
print('slope is ',aslope,' +- ', aerr)
print('inter is ',binter,' +- ', berr)
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
ax2 = fig.add_axes(ax1.get_position(), frameon=False)
ax1.errorbar(data[0],data[1],yerr=data[2],fmt='o')
ax1.set_xscale('log',basex=10)
ax1.set_yscale('log',basey=10)
ax1.set_yticks([])
ax1.set_xticks([])
ax2.plot(x,aslope*x+binter,'r')
ax2.plot(x,(aslope-aerr)*x+(binter+berr),'--')
ax2.plot(x,(aslope+aerr)*x+(binter-berr),'--')
ax2.set_xscale('linear')
ax2.set_yscale('linear')
plt.xticks(np.log10(freqlist),freqlist.astype('int'))
plt.yticks(np.log10(tdlist),tdlist.astype('float'))
plt.xlabel('Frequency (MHz)')
plt.ylabel('t_s (msec)')
fitndx1 = 'Fit slope '+"{0:.2f}".format(aslope)+u"\u00B1"+"{0:.2f}".format(aerr)
plt.legend(('Data',fitndx1))
plt.show()
Following Molly's suggestion I managed to get closer to my goal but still not there. I am adding a bit more info for what I am trying to do and it might clarify things a bit.
I am setting ax1 to the errobar plot that uses loglog scale. I need to use errorbar and not loglog plot so that I can display the errors with my points.
I am using ax2 to plot the linear fit in linealinear scale.
Moreover I do not want the x and y axes to display values that are 10,100,1000 powers of ten but my own axes labels that have the spacing I want therefore I am using the plt.xticks. I tried ax1.set_yticks and ax1.set_yticklabes but with no success. Below is the image I am getting.
I do not have enough reputation to post an image but here is the link of it uploaded
http://postimg.org/image/uojanigab/
The values of my points should be x range = 40 - 80 and y range = 5 -200 as the fit lines are now.
You can create two overlapping axes using the add_suplot method of figure. Here's an example:
from matplotlib import pyplot as plt
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
ax2 = fig.add_axes(ax1.get_position(), frameon=False)
ax1.loglog([1,10,100,1000],[1000,1,100,10])
ax2.plot([5,10,11,13],'r')
plt.show()
You can then turn off the x and y ticks for the linear scale plot like this:
ax2.set_xticks([])
ax2.set_yticks([])
I was not able to get two sets of axis working with the errorbar function so I had to convert everything to log scale including my linear plot. Below is the code I use to get it might be useful to someone.
plt.errorbar(data[0],data[1],yerr=data[2],fmt='o')
plt.xscale('log',basex=10)
plt.yscale('log',basey=10)
plt.plot(data[0],data[0]**aslope*10**binter,'r')
plt.plot(data[0],data[0]**(aslope-aerr)*10**(binter+berr),'--')
plt.plot(data[0],data[0]**(aslope+aerr)*10**(binter-berr),'--')
plt.xticks(freqlist,freqlist.astype('int'))
plt.yticks(tdlist,tdlist.astype('float'))
plt.xlabel('Frequency (MHz)')
plt.ylabel('t_s (msec)')
fitndx1 = 'Fit slope '+"{0:.2f}".format(aslope)+u"\u00B1"+"{0:.2f}".format(aerr)
plt.legend(('Data',fitndx1))
plt.show()
And here is the link to the final image
http://postimg.org/image/bevj2k6nf/

Categories

Resources