I would like to plot a series of contour lines, but the spacing between where the label is and the line increases higher up the page. I've plotted an example attached. I want no decimal points, hence used fmt, but this seems to change the spacing at different points (Ideally I want around half a centimetre gap between the contour line break and the writing.
As an aside, I also tried to use the manual locations so it'd plot each label at a certain place, but as there are two seperate contour lines with the same value I'm not sure if this is possible. Thanks!
Here is my code;
from netCDF4 import Dataset
import numpy as np
from matplotlib import pyplot as plt
#############################
# #
# Parameter Setup #
# #
#############################
myfile = '/home/ubuntu/Documents/Control/puma/run/Control.nc' #Control U
myfile2 = '/home/ubuntu/Documents/Control_Trop_40K/puma/run/ControlTrop.nc' #Perturbed U
Title = 'U'
Units = '!U'
Variable = 'ua'
#############################
#############################
Import = Dataset(myfile, mode='r')
Import2 = Dataset(myfile2, more='r')
latt = Import.variables['lat'][:]
level = Import.variables['lev'][:]
UControl = Import.variables[Variable][:]
#UPerturbed = Import2.variables[Variable][:]
#UChange = UPerturbed - UControl
#UChange = np.mean(UChange[:,:,:,0], axis=0)
UControl = np.mean(UControl[:,:,:,0], axis=0)
Contourrange = [10]
CS=plt.contour(latt,level,UControl,Contourrange, colors='k')
plt.clabel(CS, fontsize=10, inline=1,fmt = '%1.0f',ticks=Contourrange)
plt.gca().invert_yaxis()
plt.yscale('log', nonposy='clip')
plt.xticks(np.round((np.arange(-90, 91, 30)), 2))
plt.xlim(xmin=-90)
plt.yticks([900,800,700,600,500,400,300,200,100,50])
plt.gca().set_yticklabels([900,800,700,600,500,400,300,200,100,50])
plt.xlabel('Latitude')
plt.ylabel('Pressure (hPa)')
plt.title(Title)
plt.show()
The pictures are:
You are manually defining the values for which it should be a tick:
plt.yticks([900,800,700,600,500,400,300,200,100,50])
Since you also have chosen a logarithmic scale, and since the increment you specified is constant, matplotlib needs to vary the space between ticks to comply with both your requirements.
If you absolutely do not want this behavior, either get rid of the log option, or let matplotlib automatically set ticks for you. Alternatively, you could provide the plt.yticks fuction with an array of exponentially increasing/decreasing numbers. Like this:
plt.yticks([10^3,10^2,10^1])
You will have to make sure you are using the correct base (I simply assumed a base 10), and you will have to find suitable numbers to span your range of values.
Related
I'm trying to add a second x-axis label to the top of a plot. The normal axis is in log scale and displays as 10^-1, 10^0, etc, as it should, but I also want ticks at each 10^x.5 along the top (.1, .32, 1, 3.2, etc). When I try to do this with twiny, they align completely incorrectly and in a way that I can't even understand the reason for. Here is my code (and the resulting plot):
from pylab import *
import numpy as np
dfile = "data.txt" #DATA STUFF YOU DON'T NEED
data = np.loadtxt(dfile,dtype=float)
asep = data[:,1]
par= data[:,2]
dist = 1000/par
dsep = asep*dist
ldsep = np.log10(dsep)
#RELEVANT BITS
ax1=subplot(211)
ax1.set_xlim([0,100])
plt.gca().set_xscale("log")
plt.hist(allsep,bins=[.1,.32,1,3.2,10,32,100],facecolor='red')
plt.ylabel('$N_{stars}$')
ax2 = ax1.twiny()
ax2.set_xscale("log")
newpos=[.1,.32,1,3.2,10,32,100]
newlabel=[0.1,0.32,1.0,3.2,10,32,100]
ax2.set_xticks(newpos)
ax2.set_xticklabels(newlabel)
ax2.xaxis.set_ticks_position('top')
ax2.xaxis.set_label_position('top')
ax2.set_xlim(ax1.get_xlim())
#SECOND PLOT, NOT REALLY NECESSARY
ax3=subplot(212)
ax3.set_xlim([0,100])
plt.hist(allsep,bins=[0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80],facecolor='red')
plt.xlabel('Projected Separation (AU)')
plt.ylabel('$N_{stars}$')
plt.savefig('dhist.png',dpi=300)
plt.show()
Thanks all!
When I try to run your code, the first error message is UserWarning: Attempted to set non-positive xlimits for log-scale axis;...
So - what versions of python and matplotlib do you use? Perhaps you should consider an update.
But still, before that you could simply first test what happens if you change the setting of x-axis limits to [ 0.1,... ]:
ax1.set_xlim([0.1,100])
I am doing some plotting using cartopy and matplotlib, and I am producing a few images using the same set of points with a different domain size shown in each image. As my domain size gets bigger, the size of each plotted point remains fixed, so eventually as I zoom out, things get scrunched up, overlapped, and generally messy. I want to change the size of the points, and I know that I could do so by plotting them again, but I am wondering if there is a way to change their size without going through that process another time.
this is the line that I am using to plot the points:
plt.scatter(longs, lats, color = str(platformColors[platform]), zorder = 2, s = 8, marker = 'o')
and this is the line that I am using to change the domain size:
ax.set_extent([lon-offset, lon+offset, lat-offset, lat+offset])
Any advice would be greatly appreciated!
scatter has the option set_sizes, which you can use to set a new size. For example:
import matplotlib.pylab as pl
import numpy as np
x = np.random.random(10)
y = np.random.random(10)
s = np.random.random(10)*100
pl.figure()
l=pl.scatter(x,y,s=s)
s = np.random.random(10)*100
l.set_sizes(s)
It seems that set_sizes only accepts arrays, so for a constant marker size you could do something like:
l.set_sizes(np.ones(x.size)*100)
Or for a relative change, something like:
l.set_sizes(l.get_sizes()*2)
http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.scatter
These are the parameters that plt.scatter take and the s parameter is the size of the scattered point so change s to whatever you like, something like so
plt.scatter(longs, lats, color = str(platformColors[platform]), zorder = 2, s = 20, marker = 'o')
I am using the following codes to generate the square wave format [Eg: from 0 till 5] using for loop. I am able to print the respective binary values but not able to plot the square wave dynamically.In addition to this I am not able to dynamically resize the x axis in the plot window. I could not find any suitable code at Matplotlib animation section. Could any one help me in this?
import numpy as np
import matplotlib.pyplot as plt
limit=int(raw_input('Enter the value of limit:'))
for x in range(0,limit):
y=bin(x)[2:]
data = [int(i) for i in y]
print data
xs = np.repeat(range(len(data)),2)
ys = np.repeat(data, 2)
xs = xs[1:]
ys = ys[:-1]
plt.plot(xs, ys)
plt.xlim(0,len(data)+0.5)
plt.ylim(-2, 2)
plt.grid()
plt.show()
#plt.hold(True)
#plt.pause(0.5)
plt.clf()
Your question as stated is pretty vague so I'm going to I'm going to go out on a limb and assume that what you want is to plot a series of equal length binary codes using the same figure with some delay in between.
So, two problems here:
Generating the appropriate binary codes
Plotting those codes successively
1. Generating the appropriate binary codes
From what I can reasonably guess, you want to plot binary codes of the same length. So you'll have to zero pad your codes so they are the same length. One way to do this is with python's built in zfill function.
e.g.
bin(1).zfill(4)
This also brings light to the fact that you will have to know the length of the largest binary string you want to plot if you want to keep the x-axis range constant. Since it's not clear if you even want constant length strings I'll just leave it at this.
2. Plotting those codes successively
There are a couple different ways to create animations in matplotlib. I find manually updating data is a little bit more flexible and less buggy than the animations API currently is so I will be doing that here. I've also cut down some parts of the code that were not clear to me.
Here's a simple a implementation:
import matplotlib.pyplot as plt
import numpy as np
# Enable interactive plotting
plt.ion()
# Create a figure and axis for plotting on
fig = plt.figure()
ax = fig.add_subplot(111)
# Add the line 'artist' to the plotting axis
# use 'steps' to plot binary codes
line = plt.Line2D((),(),drawstyle='steps-pre')
ax.add_line(line)
# Apply any static formatting to the axis
ax.grid()
ax.set_ylim(-2, 2)
# ...
try:
limit = int(raw_input('Enter the value of limit:'))
codelength = int(np.ceil(np.log2(limit)))+1 # see note*
ax.set_xlim(0,codelength)
for x in range(0,limit):
# create your fake data
y = bin(x)[2:].zfill(codelength)
data = [int(i) for i in y]
print data
xs = range(len(data))
line.set_data(xs,data) # Update line data
fig.canvas.draw() # Ask to redraw the figure
# Do a *required* pause to allow figure to redraw
plt.pause(2) # delay in seconds between frames
except KeyboardInterrupt: # allow user to escape with Ctrl+c
pass
finally: # Always clean up!
plt.close(fig)
plt.ioff()
del ax,fig
Result
*Note: I padded the binary codes by an extra zero to get the plot to look right.
Changing the vertical distance between two subplot using tight_layout(h_pad=-1) changes the total figuresize. How can I define the figuresize using tight_layout?
Here is the code:
#define figure
pl.figure(figsize=(10, 6.25))
ax1=subplot(211)
img=pl.imshow(np.random.random((10,50)), interpolation='none')
ax1.set_xticklabels(()) #hides the tickslabels of the first plot
subplot(212)
x=linspace(0,50)
pl.plot(x,x,'k-')
xlim( ax1.get_xlim() ) #same x-axis for both plots
And here is the results:
If I write
pl.tight_layout(h_pad=-2)
in the last line, then I get this:
As you can see, the figure is bigger...
You can use a GridSpec object to control precisely width and height ratios, as answered on this thread and documented here.
Experimenting with your code, I could produce something like what you want, by using a height_ratio that assigns twice the space to the upper subplot, and increasing the h_pad parameter to the tight_layout call. This does not sound completely right, but maybe you can adjust this further ...
import numpy as np
from matplotlib.pyplot import *
import matplotlib.pyplot as pl
import matplotlib.gridspec as gridspec
#define figure
fig = pl.figure(figsize=(10, 6.25))
gs = gridspec.GridSpec(2, 1, height_ratios=[2,1])
ax1=subplot(gs[0])
img=pl.imshow(np.random.random((10,50)), interpolation='none')
ax1.set_xticklabels(()) #hides the tickslabels of the first plot
ax2=subplot(gs[1])
x=np.linspace(0,50)
ax2.plot(x,x,'k-')
xlim( ax1.get_xlim() ) #same x-axis for both plots
fig.tight_layout(h_pad=-5)
show()
There were other issues, like correcting the imports, adding numpy, and plotting to ax2 instead of directly with pl. The output I see is this:
This case is peculiar because of the fact that the default aspect ratios of images and plots are not the same. So it is worth noting for people looking to remove the spaces in a grid of subplots consisting of images only or of plots only that you may find an appropriate solution among the answers to this question (and those linked to it): How to remove the space between subplots in matplotlib.pyplot?.
The aspect ratios of the subplots in this particular example are as follows:
# Default aspect ratio of images:
ax1.get_aspect()
# 1.0
# Which is as it is expected based on the default settings in rcParams file:
matplotlib.rcParams['image.aspect']
# 'equal'
# Default aspect ratio of plots:
ax2.get_aspect()
# 'auto'
The size of ax1 and the space beneath it are adjusted automatically based on the number of pixels along the x-axis (i.e. width) so as to preserve the 'equal' aspect ratio while fitting both subplots within the figure. As you mentioned, using fig.tight_layout(h_pad=xxx) or the similar fig.set_constrained_layout_pads(hspace=xxx) is not a good option as this makes the figure larger.
To remove the gap while preserving the original figure size, you can use fig.subplots_adjust(hspace=xxx) or the equivalent plt.subplots(gridspec_kw=dict(hspace=xxx)), as shown in the following example:
import numpy as np # v 1.19.2
import matplotlib.pyplot as plt # v 3.3.2
np.random.seed(1)
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6.25),
gridspec_kw=dict(hspace=-0.206))
# For those not using plt.subplots, you can use this instead:
# fig.subplots_adjust(hspace=-0.206)
size = 50
ax1.imshow(np.random.random((10, size)))
ax1.xaxis.set_visible(False)
# Create plot of a line that is aligned with the image above
x = np.arange(0, size)
ax2.plot(x, x, 'k-')
ax2.set_xlim(ax1.get_xlim())
plt.show()
I am not aware of any way to define the appropriate hspace automatically so that the gap can be removed for any image width. As stated in the docstring for fig.subplots_adjust(), it corresponds to the height of the padding between subplots, as a fraction of the average axes height. So I attempted to compute hspace by dividing the gap between the subplots by the average height of both subplots like this:
# Extract axes positions in figure coordinates
ax1_x0, ax1_y0, ax1_x1, ax1_y1 = np.ravel(ax1.get_position())
ax2_x0, ax2_y0, ax2_x1, ax2_y1 = np.ravel(ax2.get_position())
# Compute negative hspace to close the vertical gap between subplots
ax1_h = ax1_y1-ax1_y0
ax2_h = ax2_y1-ax2_y0
avg_h = (ax1_h+ax2_h)/2
gap = ax1_y0-ax2_y1
hspace=-(gap/avg_h) # this divided by 2 also does not work
fig.subplots_adjust(hspace=hspace)
Unfortunately, this does not work. Maybe someone else has a solution for this.
It is also worth mentioning that I tried removing the gap between subplots by editing the y positions like in this example:
# Extract axes positions in figure coordinates
ax1_x0, ax1_y0, ax1_x1, ax1_y1 = np.ravel(ax1.get_position())
ax2_x0, ax2_y0, ax2_x1, ax2_y1 = np.ravel(ax2.get_position())
# Set new y positions: shift ax1 down over gap
gap = ax1_y0-ax2_y1
ax1.set_position([ax1_x0, ax1_y0-gap, ax1_x1, ax1_y1-gap])
ax2.set_position([ax2_x0, ax2_y0, ax2_x1, ax2_y1])
Unfortunately, this (and variations of this) produces seemingly unpredictable results, including a figure resizing similar to when using fig.tight_layout(). Maybe someone else has an explanation for what is happening here behind the scenes.
I have a lot of different files (10-20) that I read in x and y data from, then plot as a line.
At the moment I have the standard colors but I would like to use a colormap instead.
I have looked at many different examples but can't get the adjustment for my code right.
I would like the colour to change between each line (rather than along the line) using a colormap such as gist_rainbow i.e. a discrete colourmap
The image below is what I can currently achieve.
This is what I have attempted:
import pylab as py
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc, rcParams
numlines = 20
for i in np.linspace(0,1, numlines):
color1=plt.cm.RdYlBu(1)
color2=plt.cm.RdYlBu(2)
# Extract and plot data
data = np.genfromtxt('OUZ_QRZ_Lin_Disp_Curves')
OUZ_QRZ_per = data[:,1]
OUZ_QRZ_gvel = data[:,0]
plt.plot(OUZ_QRZ_per,OUZ_QRZ_gvel, '--', color=color1, label='OUZ-QRZ')
data = np.genfromtxt('PXZ_WCZ_Lin_Disp_Curves')
PXZ_WCZ_per = data[:,1]
PXZ_WCZ_gvel = data[:,0]
plt.plot(PXZ_WCZ_per,PXZ_WCZ_gvel, '--', color=color2, label='PXZ-WCZ')
# Lots more files will be plotted in the final code
py.grid(True)
plt.legend(loc="lower right",prop={'size':10})
plt.savefig('Test')
plt.show()
You could take a few different approaches. On your initial example you color each line specifically with a different color. That works fine if you are able to loop over the data/colors you want to plot. Manually assigning each color, like you do now, is a lot of work, even for 20 lines, but imagine if you have hundred or more. :)
Matplotlib also allows you to edit the default 'color cycle' with your own colors. Consider this example:
numlines = 10
data = np.random.randn(150, numlines).cumsum(axis=0)
plt.plot(data)
This gives the default behavior, and results in:
If you want to use a default Matplotlib colormap, you can use it to retrieve the colors values.
# pick a cmap
cmap = plt.cm.RdYlBu
# get the colors
# if you pass floats to a cmap, the range is from 0 to 1,
# if you pass integer, the range is from 0 to 255
rgba_colors = cmap(np.linspace(0,1,numlines))
# the colors need to be converted to hexadecimal format
hex_colors = [mpl.colors.rgb2hex(item[:3]) for item in rgba_colors.tolist()]
You can then assign the list of colors to the color cycle setting from Matplotlib.
mpl.rcParams['axes.color_cycle'] = hex_colors
Any plot made after this change will automatically cycle through these colors:
plt.plot(data)