Python Hexbin marginals offset from image produced - python

I have a hopefully simple question. When using the python hexbin plot option on some spatial data (Ra, and Dec are x and y) I also want to see the marginals on the side. Happily there is a simple option 'marginals = True'....
Unhappily, as you can see below... the x-axis marginals are visibly offset from the hexagon produced image. I have tried adjusting parameters but the marginals on the x-axis always appear offset to the image (and there never seems to be a problem in y), any ideas would be appreciated. Please see the code and image below, Thanks in advance!
fig5=plt.figure(5)
ax=fig5.add_subplot(111)
imageh=plt.hexbin(Radeg[CoreL],
Decdeg[CoreL],
extent=[np.min(Radeg[CoreL]), np.max(Radeg[CoreL]), np.min(Decdeg[CoreL]), np.max(Decdeg[CoreL])],
alpha=0.7,
gridsize=20,
marginals=True,
vmin=5,
vmax=105,
cmap=get_cmap("jet"),
mincnt=5)
ax.axis([305,275,-40,-25])
cbar=plt.colorbar(imageh,extend='max')
cbar.set_label(r'$\mathrm{Counts}$',fontsize=18)
ax.set_xlabel(r'$\mathrm{RA}$',fontsize=20)
ax.set_ylabel(r'$\mathrm{DEC}$',fontsize=18)
plt.show()
-- per request that I add data to test with..... my data is rather lengthy and unwieldily, but here is a standalone version that illustrates the problem. This is a altered version from 'Hooked' who posted in regard to a different hexbin question.
def generate_data(n):
"""Make random, correlated x & y arrays"""
points = np.random.multivariate_normal(mean=(0,0),
cov=[[0.4,9],[9,10]],size=int(n))
return points
if __name__ =='__main__':
color_map = plt.cm.Spectral_r
n = 1e4
points = generate_data(n)
xbnds = np.array([-20.0,20.0])
ybnds = np.array([-20.0,20.0])
extent = [xbnds[0],xbnds[1],ybnds[0],ybnds[1]]
fig=plt.figure(figsize=(10,9))
ax = fig.add_subplot(111)
x, y = points.T
image = plt.hexbin(x,y,cmap=color_map,gridsize=20,marginals=True,extent=extent,mincnt=1,bins='log')
ax.set_xlim(xbnds)
ax.set_ylim(ybnds)
plt.grid(True)
cb = plt.colorbar(image,spacing='uniform',extend='max')
plt.show()
This code gives a similar image, but this time the marginals are offset in the x and y direction, the integral should be just in one direction, over the other variable, i.e. rows and columns. In theory I would expect a marginal on the side for every row and column I have data in.

Related

How to fix issues with wrong layer order for a custom waterfall plot in Python?

So I found a custom script to make a waterfall plot from a dataframe, which I customized for my purposes. When I run the function for small number of columns, each layer plots accordingly with the subsequent plot properly placed behind the previous plot. When I keep including a larger number of the columns, it starts plotting subsequent plots over the previous ones.
I tried using PolyCollections, and LineCollections and I can't seem to fix this. Is it a bug? Is there a work-around?
def waterfall(X, numTrials):
# Function to generate formats for facecolors
cc = lambda arg: colorConverter.to_rgba(arg, alpha=1)
# This is just wrong. There must be some way to use the meshgrid or why bother.
verts = []
for i in np.arange(numTrials)+1:
verts.append(list(zip(X['Time'].values, X['Trial #'+str(i)].values)))
xmin = np.floor(np.min(X['Time'].values))
xmax = np.ceil(np.max(X['Time'].values))
ymin = -1
ymax = numTrials+1
zmin = np.floor(np.min(X.drop(columns=['Time']).values))
zmax = np.ceil(np.max(X.drop(columns=['Time']).values))
fig=plt.figure()
fig.set_size_inches(15,10)
ax = Axes3D(fig)
poly = PolyCollection(verts,closed='True',edgecolors=[cc('k')],facecolors=[cc('w')])
ax.add_collection3d(poly, zs=np.arange(numTrials)+1, zdir='y')
ax.set_xlim(xmin,xmax)
ax.set_ylim(ymin,ymax)
ax.set_zlim(zmin,zmax)
ax.view_init(25, -110)
plt.show()
for x in np.arange(5,100,20):
waterfall(CRresponse, x)
This is an example of a proper output, for low number of columns:
enter image description here
This is an example of a messed up output for higher number of columns:
enter image description here
Thank you for any help on the matter, and I appreciate your patience for a first time poster

Matplotlib - How to assign an alpha value to each point on a 3D scatter plot? Radar Data Included

So I am trying to create a 3D scatter plot of radar data, where each point is assigned an alpha value based on the amplitude of the corresponding pixel.
I have done this looping through all x,y,z points and building the scatter plot point by point assigning the alpha values on each iteration. But once the scatter plot is created it is very slow and unable to manipulate the graph without considerable time spent waiting for the plot to update.
Points is a normalised (0 to 1) array.
Here is a link to my data
Data
File preparation:
def normalise0to1(data):
normalised = (data - np.min(data)) / (np.max(data) - np.min(data))
return normalised
Data = np.loadtxt('filepath.txt')
points2D = normalise0to1(Data) #Is (101,400) size
points3D = np.reshape(points2D,(101,20,20)) #Is (101,20,20) size
And the first attempt at creating the 3D scatter plot:
def Scatter_Plot1(points3D):
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
for x in range(0,points3D[1]):
for y in range(0,points3D[2]):
for z in range(0,points3D[0]):
val = points3D[z,x,y]
ax.scatter(x, y, z, alpha=val,c='black',s=1)
plt.show()
This takes a long time to run and is very slow once created.
In 2D, I can do something like this. Bear in mind this is the same array as the 3D 'points' array, but the (20x20) has been flattened to 400. I have provided an image of the flattened array, you can see how it creates an image where intensity is scaled to the alpha value.
def Scatter_Plot2(points2D):
fig = plt.figure()
ax = fig.add_subplot()
x_=np.linspace(0,points2D.shape[1]-1,points2D.shape[1])
y_=np.linspace(0,points2D.shape[0]-1,points2D.shape[0])
x,y = np.meshgrid(x_,y_)
ax.scatter(x,y,alpha=points2D.flatten(),s=1,c='black')
plt.show()
This image is the flattened version of the 3D plot I want to create, where instead of 400 length, it would be (20x20) and overall the 3D shape is (101,20,20).
The problem comes when trying to assign the alpha to a 3D plot, in 2D it seems happy enough when I provide a flattened array to the alpha parameter.
I would like something like this, but whether that's possible is another question..
def Scatter_Plot3(points3D):
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
x_=np.linspace(0,points3D.shape[2]-1,points3D.shape[2])
y_=np.linspace(0,points3D.shape[1]-1,points3D.shape[1])
z_=np.linspace(0,points3D.shape[0]-1,points3D.shape[0])
x,y,z = np.meshgrid(x_,y_,z_)
ax.scatter(x,y,z,alpha=points3D,s=1,c='black')
plt.show()
Results in this image, which seems a bit random in terms of how the alpha is assigned. You would expect to see the dark horizontal lines as in the first picture. What I want may not be possible, I'm open to using another package, perhaps pyqtgraph or mayavi etc. But matplotlib would be preferable.
Thank you!
Edit:
I have achieved something similar to what I would like, though not exactly. I have used the c and cmap inputs. This isn't ideal as I am dealing with a 3D cube and viewing the centre is still difficult but it has correctly mapped a variation to the data. But it doesn't work when I use the alpha parameter.
Notice the 2 main horizontal bands and the dark bit in the centre which is hard to see.
What I need is the same mapping but rather opacity than colour.
c = (points2D.T.flatten())
ax.scatter(x,y,z,s=1,c=c,cmap='viridis_r')

How to match top and bottom x-axes in Python with Matplotlib?

I am trying to plot the energy consumption profile of an electric vehicle. I am using the elevation profile vs the horizontal distance the vehicle runs along a path. I want to add a second x-axis on top of the plot to represent by each chunk of distance, what the energy consumption value was at that precise location.
This is what I have so far, but it's not precisely what I need:
I know this should be fairly simple as it is only adding a second x-axis that matches with the primary x-axis, but I have wasted an entire day trying to figure out unsuccessfully :(
Any insights will be greatly appreciated.
Code:
fig, ax1 = plt.subplots()
elevation_distance_np = elevation_distance.to_numpy()
plt.plot(elevation_distance_np[:,0], elevation_distance_np[:,1], color = 'blue')
plt.grid(True)
plt.xlabel("Distancia recorrida")
plt.ylabel("Elevación de distancia recorrrida")
axes2 = ax1.twiny()
axes2.set_xticks(suma_kWh_np[::mth.ceil(len(suma_kWh_np)/8)])
plt.title("Elevación vs Distancia Recorrida")
plt.show()
This is a not so trivial endeavor, as these questions show, so don't feel frustrated for not getting this on your own.
Disclaimer: this is not the most elegant solution, but it works. I made a toy example where the conversion from one axis to the other is obtained by dividing the main by 8.5. Also, I replotted your data on this secondary axis, to set the values of its own X axis to something sensible, then removed this extra line.
x = np.linspace(0, 140) # Some x values, similar to your range
# Caps them to a minimum of 0
y = np.clip(x * (-1) + 100, a_min=0, a_max=100)
# Creates something similar to your data
elevation_distance_np = np.hstack((x[:, np.newaxis], y[:, np.newaxis]))
# I guessed some transform. If you don't have a formula,
# you'll need to interpolate between known values, probably.
suma_kWh_np = x / 8.5
fig, ax1 = plt.subplots()
# Changed to explicit notation, so we don't go back and forth between them
ax1.plot(elevation_distance_np[:,0], elevation_distance_np[:,1], color = 'blue')
ax1.grid(True)
ax1.set_xlabel("Distancia recorrida")
ax1.set_ylabel("Elevación de distancia recorrrida")
ax2 = ax1.twiny()
# Added a copy of your line, but which will be removed later
extra_line = ax2.plot(suma_kWh_np, elevation_distance_np[:,1], color = 'r')
# Now, we get the x ticks and transform them to kWh.
# Here, I had to remove the first and last points ([1:-1])
# because ax1.get_xticks() returned a range from -20 to 160,
ax2.set_xticks(ax1.get_xticks()[1:-1] / 8.5)
ax1.set_title("Elevación vs Distancia Recorrida")
ax2.lines.pop() # We remove the temporary line right before plotting
plt.show()
Here's the result.

Scale colormap for contour and contourf

I'm trying to plot the contour map of a given function f(x,y), but since the functions output scales really fast, I'm losing a lot of information for lower values of x and y. I found on the forums to work that out using vmax=vmax, it actually worked, but only when plotted for a specific limit of x and y and levels of the colormap.
Say I have this plot:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
u = np.linspace(-2,2,1000)
x,y = np.meshgrid(u,u)
z = (1-x)**2+100*(y-x**2)**2
cont = plt.contour(x,y,z,500,colors='black',linewidths=.3)
cont = plt.contourf(x,y,z,500,cmap="jet",vmax=100)
plt.colorbar(cont)
plt.show
I want to uncover whats beyond the axis limits keeping the same scale, but if I change de x and y limits to -3 and 3 I get:
See how I lost most of my levels since my max value for the function at these limits are much higher. A work around to this problem is to increase the levels to 1000, but that takes a lot of computational time.
Is there a way to plot only the contour levels that I need? That is, between 0 and 100.
An example of a desired output would be:
With the white space being the continuation of the plot without resizing the levels.
The code I'm using is the one given after the first image.
There are a few possible ideas here. The one I very much prefer is a logarithmic representation of the data. An example would be
from matplotlib import ticker
fig = plt.figure(1)
cont1 = plt.contourf(x,y,z,cmap="jet",locator=ticker.LogLocator(numticks=10))
plt.colorbar(cont1)
plt.show()
fig = plt.figure(2)
cont2 = plt.contourf(x,y,np.log10(z),100,cmap="jet")
plt.colorbar(cont2)
plt.show()
The first example uses matplotlibs LogLocator functions. The second one just directly computes the logarithm of the data and plots that normally.
The third example just caps all data above 100.
fig = plt.figure(3)
zcapped = z.copy()
zcapped[zcapped>100]=100
cont3 = plt.contourf(x,y,zcapped,100,cmap="jet")
cbar = plt.colorbar(cont3)
plt.show()

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