I import a fits file and basically want to plot it as a contourplot. The problem arises with the aspect ratio. The image has 320x240 dimensions and since I don't want the picture to be stretched, I want the ratio of the image to be 320/240 also.
Python, however, forces it to a square like thing, which is why I used plt.axis('equal')
This however creates white areas in the plotting area and I don't know how to get rid off them...
Any ideas?
Thanks a lot!!
import numpy
import math
from astropy.io import fits
import matplotlib.pyplot as plt
scidatafile=fits.open('24-02-2015-PSF-OD00-iris15-30pin.fits')
scidata=scidatafile[0].data
oneframe=scidata[0]
oneframe[oneframe<0]=0
yvec=numpy.linspace(0,240,240)
xvec=numpy.linspace(0,320,320)
X,Y=numpy.meshgrid(xvec,yvec)
There it goes
plt.contourf(X,Y,oneframe)
plt.axis('equal')
plt.savefig('this.pdf')
plt.show()
First you should post a minimal working example. As you use data not made available, I just used some own dummy data.
Basically the problem is setting the aspect ratio before any data is in the axis. I don't know why and if it was me, I would mark this as bug not a feature. So instead set the aspect ratio after calling contourf.
I also don't understand why aspect=1 doesn't work for contourf, while it works for imshow.
from numpy import *
from matplotlib.pyplot import *
# dummy data preparations
x = linspace( -3.0, 3.0, 100 )
y = linspace( -2.0, 2.0, 100 )
def V( x, y=None ):
if y == None:
return 1.0/x
xv,yv = meshgrid( x, y, sparse=False, indexing='xy' )
return 1.0 / sqrt(xv**2+yv**2)
rlev = sqrt(18.) / 2.**linspace( 0,4,10 )
pic = V(x,y)
# your try to plot it which results in white borders
fig = figure()
title('old version')
contourf( x, y, pic, levels=V(rlev) )
axis('equal')
tight_layout()
savefig('old.png')
Need 10 rep to post picture: 'old.png'
# method without white borders
fig = figure()
title('new version')
ax = subplot(1,1,1)
contourf( x, y, pic, levels=V(rlev) )
ax.set_aspect('equal')
tight_layout()
savefig('new.png')
show()
Need 10 rep to post picture: 'new.png'
Using pylab, you can do it like this:
import pylab as pl
import numpy as np
yvec=np.linspace(0,240,240)
xvec=np.linspace(0,320,320)
X,Y=np.meshgrid(xvec,yvec)
Z = X**2 + Y**2 # function to plot
sx = 8
sy = sx*float(yvec.size)/float(xvec.size) # reset y-scale to match array dimensions
fig, ax = pl.subplots(figsize=(sx, sy))
ax.contourf(X,Y,Z)
In plain matplotlib, I think you can get the same effect using gcf() to manipulate the figure.
Related
I've tried to recreate the image attached using cmaps as well as with if/else statements.
My current attempt is based upon the advice given in this thread
I tried using 1.8<=x<=2.2 but I get an error.
Here is my current code below:
import numpy as np
import matplotlib.pyplot as plt
N = 500
# center, variation, number of points
x = np.random.normal(2,0.2,N)
y = np.random.normal(2,0.2,N)
colors = np.where(x<=2.2,'r',np.where(y<=2.2,'b','b'))
plt.scatter(x , y, c=colors)
plt.colorbar()
plt.show()
To make that plot, you need to pass an array with the color of each point. In this case the color is the distance to the point (2, 2), since the distributions are centered on that point.
import numpy as np
import matplotlib.pyplot as plt
N = 500
# center, variation, number of points
x = np.random.normal(2,0.2,N)
y = np.random.normal(2,0.2,N)
# we calculate the distance to (2, 2).
# This we are going to use to give it the color.
color = np.sqrt((x-2)**2 + (y-2)**2)
plt.scatter(x , y, c=color, cmap='plasma', alpha=0.7)
# we set a alpha
# it is what gives the transparency to the points.
# if they suppose themselves, the colors are added.
plt.show()
I am still getting my feet with python, so apologies if this is a very simple question.
I have an output file which contains 5 columns, as follows:
Depth Data#1 Data#2 Data#3 Standard_deviation
These columns contain 500 values, if this makes any difference.
What I am trying to do is simply plot data#1, data#2, and data#3 (on the x axis) against depth (on the y axis). I would like data#1 to be blue, and data#2 and data#3 to each be red.
The figsize I would like is (14,6).
I don't want the column containing standard deviation to be plotted here. If it is simpler, I can simply remove that column from the output.
Thanks in advance for any help!
With nearly everything with matplotlib, the way I go about it if i don't know how to do it already, is to just scan through the Gallery to find something that looks similar to what i want to do, and then alter the code there already.
This one has most of what you want in it:
http://matplotlib.org/examples/style_sheets/plot_fivethirtyeight.html
"""
This shows an example of the "fivethirtyeight" styling, which
tries to replicate the styles from FiveThirtyEight.com.
"""
from matplotlib import pyplot as plt
import numpy as np
x = np.linspace(0, 10)
with plt.style.context('fivethirtyeight'):
plt.plot(x, np.sin(x) + x + np.random.randn(50))
plt.plot(x, np.sin(x) + 0.5 * x + np.random.randn(50))
plt.plot(x, np.sin(x) + 2 * x + np.random.randn(50))
plt.show()
It does unfortunately have a load of extra stuff in it you don't want, but the part you should pick up on is that plt.plot(...) can just be called multiple times to plot multiple lines.
Then it's just a case of applying this;
from matplotlib import pyplot
#Make some data
depth = range(500)
allData = zip(*[[x, 2*x, 3*x] for x in depth])
#Set out colours
colours = ["blue", "red", "red"]
for data, colour in zip(allData, colours):
pyplot.plot(depth, data, color=colour)
pyplot.show()
its matplotlibs basics:
import pylab as pl
data = pl.loadtxt("myfile.txt")
pl.figure(figsize=(14,6))
pl.plot(data[:,1], data[:,0], "b")
pl.plot(data[:,2], data[:,0], "r")
pl.plot(data[:,3], data[:,0], "r")
pl.show()
As the question only regard plotting I am assuming you know how to read the data from the file. As for the plotting what you need is the following:
import matplotlib.pyplot as plt
#Create a figure with a certain size
plt.figure(figsize = (14, 6))
#Plot x versus y
plt.plot(data1, depth, color = "blue")
plt.plot(data2, depth, color = "red")
plt.plot(data3, depth, color = "red")
#Save the figure
plt.savefig("figure.png", dpi = 300, bbox_inches = "tight")
#Show the figure
plt.show()
The option bbox_inches = "tight" in savefig results in removing all the excess white boundaries of the figure.
I am plotting a 2D data array with imshow in matplotlib. I have a problem trying to scale the resulting plot. The size of the array is 30x1295 points, but the extent in units are:
extent = [-130,130,0,77]
If I plot the array without the extent, I get the right plot, but if I use extent, I get this plot with the wrong aspect.
It is a pretty beginner question, but there is always a first time: How I can control the aspect and the size of the plot at the same time?
Thanks,
Alex
P.D. The code is, for the right case:
imshow(np.log10(psirhoz+1e-5),origin='lower')
and for the wrong one:
imshow(np.log10(psirhoz+1e-5),origin='lower',
extent =[z_ax.min(),z_ax.max(),rho_ax.min(),rho_ax.max()])
I hope this clarify a bit things.
I'm guessing that you're wanting "square" pixels in the final plot?
For example, if we plot random data similar to yours:
import numpy as np
import matplotlib.pyplot as plt
data = np.random.random((30, 1295))
fig, ax = plt.subplots()
ax.imshow(data, extent=[-130,130,0,77])
plt.show()
We'll get an image with "stretched" pixels:
So, first off, "aspect" in matplotlib refers to the aspect in data coordinates. This means we have to jump through a couple of hoops to get what you want.
import numpy as np
import matplotlib.pyplot as plt
def main():
shape = (30, 1295)
extent = [-130,130,0,77]
data = np.random.random(shape)
fig, ax = plt.subplots()
ax.imshow(data, extent=extent, aspect=calculate_aspect(shape, extent))
plt.show()
def calculate_aspect(shape, extent):
dx = (extent[1] - extent[0]) / float(shape[1])
dy = (extent[3] - extent[2]) / float(shape[0])
return dx / dy
main()
In this case, pyplot.matshow() might also be useful:
from matplotlib import pyplot as plt
import numpy as np
dat = np.array(range(9)).reshape(3,3)
plt.matshow(dat)
plt.show()
result:
I'm making a surface plot on matplotlib. My axes are x, y, and depth. I have a two dimensional array which has RGB values, and the index corresponds to the (x,y) coordinate. How can I make the colormap from this 2D array? Thanks.
Code that makes numpy array:
import Image
import numpy as np
def makeImageArray(filename):
img = Image.open(filename)
a = np.array(img).astype("float32")
return a
Image is in greyscale.
From what I gather for every point (x,y) you have two pieces of information, the height and the color. You want to have a surface plot using the height, and colored according to the color at each location.
While you can easily specify custom color maps I don't think this will help you.
What you are thinking of is not that the same as a colormap which maps the height at (x,y) to a color.
The result is most evident in the Surface plots example here
I believe what you want is beyond the scope of matplotlib and can only be done with some kind of hack which I doubt you will wish to use.
Still here is my suggestion:
import pylab as py
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
X = np.arange(-5, 5, 0.1)
Y = np.arange(-5, 5, 0.1)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
colorise = [((5.0 + X[i][i])/10.0, 0.5, 0.0) for i in xrange((len(X)))]
ax = py.subplot(111, projection='3d')
for i in xrange(len(X)):
ax.plot(X[i], Y[i], Z[i], "o", color=colorise[i])
py.show()
This produces the following:
Importantly this displayed a 3D surface with the colouring not dependant on the height (it is a gradient in on direction). The most obvious issue is that coloring individual points looses matplotlibs surfaces making it painfully clear why the 3d plotting is called a projection!
Sorry this isn't very helpful, hopefully better software exists or I am unaware of matplotlibs full features.
I'm using quadmesh to create a simple polar projection plot. Here's a minimal script which produces basically what I'm trying to do:
from __future__ import unicode_literals
import numpy as np
import matplotlib.pyplot as plt
def make_plot(data,fig,subplot):
nphi,nt = data.shape
phi_coords = np.linspace(0,np.pi*2,nphi+1) - np.pi/2.
theta_coords = np.linspace(0,np.radians(35),nt+1)
ax = fig.add_subplot(subplot,projection='polar')
ax.set_thetagrids((45,90,135,180,225,270,315,360),(9,12,15,18,21,24,3,6))
ax.set_rgrids(np.arange(10,35,10),fmt='%s\u00b0')
theta,phi = np.meshgrid(phi_coords,theta_coords)
quadmesh = ax.pcolormesh(theta,phi,data)
ax.grid(True)
fig.colorbar(quadmesh,ax=ax)
return fig,ax
a = np.zeros((360,71)) + np.arange(360)[:,None]
b = np.random.random((360,71))
fig = plt.figure()
t1 = make_plot(a,fig,121)
t2 = make_plot(b,fig,122)
fig.savefig('test.png')
The above script creates a plot which looks like this:
I would like the colorbars to:
Not overlap the 6 label.
be scaled such that they are approximately the same height as the plot.
Is there any trick to make this work properly? (Note that this layout isn't the only one I will be using -- e.g. I might use a 1x2 layout, or a 4x4 layout ... It seems like there should be some way to scale the colorbar to the same height as the associated plot...)
This combination (and values near to these) seems to "magically" work for me to keep the colorbar scaled to the plot, no matter what size the display.
plt.colorbar(im,fraction=0.046, pad=0.04)
You can do this with a combination of the pad, shrink, and aspect kwargs:
from __future__ import unicode_literals
import numpy as np
import matplotlib.pyplot as plt
def make_plot(data,fig,subplot):
nphi,nt = data.shape
phi_coords = np.linspace(0,np.pi*2,nphi+1) - np.pi/2.
theta_coords = np.linspace(0,np.radians(35),nt+1)
ax = fig.add_subplot(subplot,projection='polar')
ax.set_thetagrids((45,90,135,180,225,270,315,360),(9,12,15,18,21,24,3,6))
ax.set_rgrids(np.arange(10,35,10),fmt='%s\u00b0')
theta,phi = np.meshgrid(phi_coords,theta_coords)
quadmesh = ax.pcolormesh(theta,phi,data)
ax.grid(True)
cb = fig.colorbar(quadmesh,ax=ax, shrink=.5, pad=.2, aspect=10)
return fig,ax,cb
a = np.zeros((360,71)) + np.arange(360)[:,None]
b = np.random.random((360,71))
fig = plt.figure()
t1 = make_plot(a,fig,121)
t2 = make_plot(b,fig,122)
figure.colorbar doc
The best value for these parameters will depend on the aspect ratio of the axes.
The size of the axes seems to not get shrink-wrapped to the polar plot, thus in the 1x2 arrangement there is a lot of space above and below the plot that are part in the axes object, but empty. The size of the color bar is keyed off of the rectangular size, not the round size, hence why the default values are not working well. There is probably a way to do the shrink-wrapping, but I do not know how to do that.
An alternate method is to force your figure to be the right aspect ratio ex:
fig.set_size_inches(10, 4) # for 1x2
fig.set_size_inches(4, 10) # for 2x1
which makes the sub plots square, so the default values more-or-less work.