Matplotlib center/align ticks in imshow plot - python

I have been trying to center the x and y ticks of my imshow but without success.
The desired yticks should be: [ 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000] and xticks: [ 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55] but aligned/centered. E.g. line 1 should have the 100 value exactly in the middle of the line space (middle of the yellow box/pixel).
import numpy as np
import matplotlib.pyplot as plt
X = np.random.rand(10,11)
plt.figure(dpi=130)
plt.imshow(X, cmap = 'jet', interpolation=None, extent=[5,55,1000,100], aspect='auto')
Here, the values 5 does not appear at all in the x axis.
I have also tried the following, x axis if fine but not the y axis
plt.figure(dpi=130)
X = np.random.rand(10,11)
plt.imshow(X, cmap = 'jet', interpolation=None, extent=[2.5,57.5,1000,100], aspect='auto')
ax = plt.gca()
xticks = cluster_space
yticks = space_segment
ax.set_xticks(xticks)
ax.set_yticks(yticks)

In general, to have the pixels centered, you need to set the extent to range from the lowest pixel coordinate minus half the pixel width to the highest pixel coordinate plus half the pixel width.
import numpy as np; np.random.seed(42)
import matplotlib.pyplot as plt
X = np.random.rand(10,11)
plt.figure()
centers = [5,55,1000,100]
dx, = np.diff(centers[:2])/(X.shape[1]-1)
dy, = -np.diff(centers[2:])/(X.shape[0]-1)
extent = [centers[0]-dx/2, centers[1]+dx/2, centers[2]+dy/2, centers[3]-dy/2]
plt.imshow(X, cmap = 'jet', interpolation=None, extent=extent, aspect='auto')
plt.xticks(np.arange(centers[0], centers[1]+dx, dx))
plt.yticks(np.arange(centers[3], centers[2]+dy, dy))
plt.show()

Related

How to colour a variable on basis of highest and lowest or at some cut off value for 3d Bar graph in python

I want to give different gradient colour for z i.e Numeric variable in my 3D Bar Graph on the basis of some cut of value or gradient for lowest to highest value.I want to put condition say if dz is >=50 then green colour bar else red colour ba. Attached the code, Please share if there's any solution for this.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection = "3d")
ax.set_xlabel("Cumulative HH's")
ax.set_ylabel("Index")
ax.set_zlabel("# Observations")
xpos = [1,2,3,4,5,6,7,8,9,10,11,12]#
ypos = [2,4,6,8,10,12,14,16,18,20,22,24]#
zpos = np.zeros(12)
dx = np.ones(12)
dy = np.ones(12)
dz = [100,3,47,35,8,59,24,19,89,60,11,25]
colors=['pink']
ax.bar3d(xpos,ypos,zpos,dx,dy,dz,color=colors)
The color= parameter to bar3d can be a list of colors with one entry per bar. Such a list can be built using a colormap.
Here is an example that colors the bars using a smooth range from green for the highest and red for the lowest. Changing the colormap to cmap = plt.cm.get_cmap('RdYlGn', 2) would color all the bars higher than the mean in green and the rest in red. To set the split condition exactly at 50, you can change the norm to norm = mcolors.Normalize(0, 100).
If only a few different colors are needed, the easiest is to forget about cmap and norm and just use:
colors = ['limegreen' if u > 50 else 'crimson' for u in dz]
Here is a complete example:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
ax.set_xlabel("Cumulative HH's")
ax.set_ylabel("Index")
ax.set_zlabel("# Observations")
xpos = np.arange(1, 13)
ypos = np.arange(2, 26, 2)
zpos = np.zeros(12)
dx = np.ones(12)
dy = np.ones(12)
dz = [100, 3, 47, 35, 8, 59, 24, 19, 89, 60, 11, 25]
cmap = plt.cm.get_cmap('RdYlGn')
norm = mcolors.Normalize(min(dz), max(dz))
colors = [cmap(norm(u)) for u in dz]
ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color=colors)
plt.show()
At the left an example with a range of colors, at the right an example with only 2 colors:

Creating a Surface of Revolution

I have a 3d plot of a disk, here is the code:
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)
I get this nice plot:
Further I have this plot:
The code is:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
arr = np.array([[100, 15],
[114.28, 17],
[128.57, 18],
[142.85, 19],
[157.13, 22],
[171.13, 24],
[185.69, 25],
[199.97, 27],
[214.25, 28],
[228.53, 30],
[242.81, 31],
[257.09, 35],
[271.37, 36],
[288.65, 37],
[300, 38]])
#interpolating between the single values of the arrays
new_x = np.concatenate([np.linspace(arr[i,0],arr[i+1,0], num=50)
for i in range(len(arr)-1)])
new_y = np.interp(new_x, arr[:,0], arr[:,1])
t=np.arange(700)
p = plt.scatter(new_x,new_y,c=t, cmap="jet")
#inserting colorbar
cax, _ = mpl.colorbar.make_axes(plt.gca(), shrink=0.8)
cbar = mpl.colorbar.ColorbarBase(cax, cmap='jet', label='testvalues',
norm=mpl.colors.Normalize(15, 40))
plt.show()
Now my question:
Is there a way to plot this 2d graph into my 3d environment? Further is it possible to create a surface out of this line (points) by rotating them around the middlepoint ? I tried it the same way like I did it with my disk but I failed because I think I need a closed contour ? Here is a picture to understand better what I want:
I'm not sure how you want to include your 2d plot, so here's how you do it as a surface of revolution.
Your new_x corresponds to radial distance, new_y corresponds to height. So we need to generate an array of angles for which to generate the "cone":
from matplotlib import cm
tmp_phi = np.linspace(0,2*np.pi,50)[:,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, linesurf_z, facecolors=colors,
rstride=1, cstride=1)
Result:

matplotlib colorbar boundaries do not implemented

I am trying to create several plots all with the same colorbar limits in a loop.
I set the limits of the contour plot with map.contourf(x, y, U_10m, vmin=0, vmax=25) and this seems to give consistent colour scales for each plot. However, when I use cbar = plt.colorbar(boundaries=np.linspace(0,1,25), ticks=[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24]) # sets all cbar to same limits each plot does not have the same colorbar limits (examples of two plots with different colorbars and code below).
from netCDF4 import Dataset as NetCDFFile
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
def wrf_tseries_contour_plotter (
ncfile, time_ind, lowerllat, upperrlat, lowerllon, upperrlon, output_dir):
'''
EDITED FROM http://www.atmos.washington.edu/~ovens/wrfwinds.html
'''
print 'timestep:', + time_ind
#which figure is being generated 0 = 00:00, 144 = 23:50
nc = NetCDFFile(ncfile, 'r')
#
# get the actual longitudes, latitudes, and corners
lons = nc.variables['XLONG'][time_ind]
lats = nc.variables['XLAT'][time_ind]
#get the u10 to plot as a contour instead of t2m
U10_raw = nc.variables['U10'][time_ind] #61 is the index for 10:00am
V10_raw = nc.variables['V10'][time_ind]
#bodge to calculate U from U and V (u10 = sqrt(u^2+v^2))
v2 = np.square(V10_raw)
u2 = np.square(U10_raw)
U_10m = np.sqrt(u2 + v2)
# Make map
map = Basemap(projection='cyl',llcrnrlat=lowerllat,urcrnrlat=upperrlat,
llcrnrlon=lowerllon,urcrnrlon=upperrlon,
resolution='h')
# lllat, urlat,lllon, urlon set outside of f(x) lower left and
# upper right lat/lon for basemap axis limits
x, y = map(lons[:,:], lats[:,:])
map.contourf(x, y, U_10m, vmin=0, vmax=25)
map.drawcoastlines(linewidth = 0.5, color = '0.15')
#thinner lines for larger scale map
#plt.clim(0, 25) #added
cbar = plt.colorbar(boundaries=np.linspace(0,1,25), ticks=[0, 2, 4, 6,
8, 10, 12, 14, 16, 18, 20, 22, 24]) # sets all cbar to same limits
cbar.set_label('10m U (m/s)', size=12)
cbar.ax.set_yticklabels([0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24])
#cbar.set_clim(0, 25)
time_str = str(time_ind)
plt.title('gust 20070724' + '_' + time_str)
fig_name = '\gust20070724_'+ time_str + '.png'
plt.savefig(output_dir + fig_name)
plt.close()
#set inputs for wrf_tseries_contour_plotter(ncfile, time_ind, lllat, urlat,
lllon, urlon, output_dir)
ncfile = 'E:\WRFout_UK2Fino\wrfout_d03_2007-07-24_00%3A00%3A00'
tlist = np.arange(0,145)
#set the lower left/upper right lat/lon for axis limits on the maps
lowerllat=48
upperrlat=63
lowerllon=-10
upperrlon=25
#set output directory for figures
output_dir = '''C:\cbar_test'''
for time_ind in tlist:
wrf_tseries_contour_plotter(ncfile, time_ind, lowerllat, upperrlat,
lowerllon, upperrlon, output_dir)
You have to use vmin and vmax values to set boundaries of a colorbar like in this example:
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
# test data
x = np.linspace(0,15,100)
X,Y = np.meshgrid(x,x)
SPD1 = np.sqrt(X*X + Y*Y)
SPD2 = SPD1 * 1.3
fig = plt.figure()
# implement boundaries of colorbar and it ticks
vmin, vmax = 0, 26
levels = np.linspace(vmin,vmax,14)
# 1st subplot
ax1 = fig.add_subplot(221)
# Set contour levels and limits
CF1 = ax1.contourf(X, Y, SPD1, levels=levels, vmax=vmax, vmin=vmin)
cbar = plt.colorbar(CF1)
cbar.set_label('10m U (m/s)', size=12)
#2nd subplot
ax1 = fig.add_subplot(222)
CF1 = ax1.contourf(X, Y, SPD2, levels=levels, vmax=vmax, vmin=vmin)
cbar = plt.colorbar(CF1)
cbar.set_label('10m U (m/s)', size=12)
plt.tight_layout()
plt.show()
However you have to select vmin, vmax correctly because of if your values are outside boundaries of colorbar they will not shown (right upper corner of 2nd subplot).

Axis don't show the ticks I want

I want to plot a Ramachandron plot. On this kind of graph, x goes from -180° to 180°, and so does y. I want a tick every 60 degrees. So here is the code I use:
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
x = [-179, 179]
y = [-179, 179]
fig = plt.figure(1)
ax = plt.subplot(111)
ax.axis([-180, 180, -180, 180])
ax.set_xticks([-180, -120, -60, 0, 60, 120, 180])
ax.set_yticks([-180, -120, -60, 0, 60, 120, 180])
# 1 bim = 1 degree
# !!! Logarithmic normalization of the colors
plt.hist2d(x, y, bins=180, norm=LogNorm())
plt.colorbar()
plt.show()
On this working example, I'm plotting only two points. But the ticks -180 and 180 are not shown, for none of the axes:
If I change x and y to:
x = [-180, 180]
y = [-180, 180]
I get what I want:
Is there a way to achieve the second result without altering the data ?
You use hist2d, set axis ticks after plotting:
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
x = [-179, 179]
y = [-179, 179]
fig = plt.figure(1)
ax = plt.subplot(111)
# 1 bim = 1 degree
# !!! Logarithmic normalization of the colors
plt.hist2d(x, y, bins=180, norm=LogNorm())
plt.colorbar()
ax.axis([-180, 180, -180, 180])
ax.set_xticks([-180, -120, -60, 0, 60, 120, 180])
ax.set_yticks([-180, -120, -60, 0, 60, 120, 180])
plt.show()

How to plot bar graphs with same X coordinates side by side ('dodged')

import matplotlib.pyplot as plt
gridnumber = range(1,4)
b1 = plt.bar(gridnumber, [0.2, 0.3, 0.1], width=0.4,
label="Bar 1", align="center")
b2 = plt.bar(gridnumber, [0.3, 0.2, 0.2], color="red", width=0.4,
label="Bar 2", align="center")
plt.ylim([0,0.5])
plt.xlim([0,4])
plt.xticks(gridnumber)
plt.legend()
plt.show()
Currently b1 and b2 overlap each other. How do I plot them separately like so:
There is an example in the matplotlib site. Basically, you just shift the x values by width. Here is the relevant bit:
import numpy as np
import matplotlib.pyplot as plt
N = 5
menMeans = (20, 35, 30, 35, 27)
menStd = (2, 3, 4, 1, 2)
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars
fig = plt.figure()
ax = fig.add_subplot(111)
rects1 = ax.bar(ind, menMeans, width, color='royalblue', yerr=menStd)
womenMeans = (25, 32, 34, 20, 25)
womenStd = (3, 5, 2, 3, 3)
rects2 = ax.bar(ind+width, womenMeans, width, color='seagreen', yerr=womenStd)
# add some
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(ind + width / 2)
ax.set_xticklabels( ('G1', 'G2', 'G3', 'G4', 'G5') )
ax.legend( (rects1[0], rects2[0]), ('Men', 'Women') )
plt.show()
Below answer will explain each and every line of code in the simplest manner possible:
# Numbers of pairs of bars you want
N = 3
# Data on X-axis
# Specify the values of blue bars (height)
blue_bar = (23, 25, 17)
# Specify the values of orange bars (height)
orange_bar = (19, 18, 14)
# Position of bars on x-axis
ind = np.arange(N)
# Figure size
plt.figure(figsize=(10,5))
# Width of a bar
width = 0.3
# Plotting
plt.bar(ind, blue_bar , width, label='Blue bar label')
plt.bar(ind + width, orange_bar, width, label='Orange bar label')
plt.xlabel('Here goes x-axis label')
plt.ylabel('Here goes y-axis label')
plt.title('Here goes title of the plot')
# xticks()
# First argument - A list of positions at which ticks should be placed
# Second argument - A list of labels to place at the given locations
plt.xticks(ind + width / 2, ('Xtick1', 'Xtick3', 'Xtick3'))
# Finding the best position for legends and putting it
plt.legend(loc='best')
plt.show()
Sometimes could be tricky to find the right bar width. I usually use this np.diff to find the right dimension.
import numpy as np
import matplotlib.pyplot as plt
#The data
womenMeans = (25, 32, 34, 20, 25)
menMeans = (20, 35, 30, 35, 27)
indices = [5.5,6,7,8.5,8.9]
#Calculate optimal width
width = np.min(np.diff(indices))/3
fig = plt.figure()
ax = fig.add_subplot(111)
# matplotlib 3.0 you have to use align
ax.bar(indices-width,womenMeans,width,color='b',label='-Ymin',align='edge')
ax.bar(indices,menMeans,width,color='r',label='Ymax',align='edge')
ax.set_xlabel('Test histogram')
plt.show()
# matplotlib 2.0 (you could avoid using align)
# ax.bar(indices-width,womenMeans,width,color='b',label='-Ymin')
# ax.bar(indices,menMeans,width,color='r',label='Ymax')
This is the result:
What if my indices on my x axis are nominal values like names:
#
import numpy as np
import matplotlib.pyplot as plt
# The data
womenMeans = (25, 32, 34, 20, 25)
menMeans = (20, 35, 30, 35, 27)
indices = range(len(womenMeans))
names = ['Asian','European','North Amercian','African','Austrailian','Martian']
# Calculate optimal width
width = np.min(np.diff(indices))/3.
fig = plt.figure()
ax = fig.add_subplot(111)
ax.bar(indices-width/2.,womenMeans,width,color='b',label='-Ymin')
ax.bar(indices+width/2.,menMeans,width,color='r',label='Ymax')
#tiks = ax.get_xticks().tolist()
ax.axes.set_xticklabels(names)
ax.set_xlabel('Test histogram')
plt.show()
Here are two examples of creating a side-by-side bar chart when you have more than two "categories" in a group.
Manual Method
Manually set the position and width of each bar.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import ticker
coins = ['penny', 'nickle', 'dime', 'quarter']
worth = np.array([.01, .05, .10, .25])
# Coin values times *n* coins
# This controls how many bars we get in each group
values = [worth*i for i in range(1,6)]
n = len(values) # Number of bars to plot
w = .15 # With of each column
x = np.arange(0, len(coins)) # Center position of group on x axis
for i, value in enumerate(values):
position = x + (w*(1-n)/2) + i*w
plt.bar(position, value, width=w, label=f'{i+1}x')
plt.xticks(x, coins);
plt.ylabel('Monetary Value')
plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('$%.2f'))
plt.legend()
Pandas Method
If you put the data into a pandas DataFrame, pandas will do the hard stuff for you.
import pandas as pd
coins = ['penny', 'nickle', 'dime', 'quarter']
worth = [0.01, 0.05, 0.10, 0.25]
df = pd.DataFrame(worth, columns=['1x'], index=coins)
df['2x'] = df['1x'] * 2
df['3x'] = df['1x'] * 3
df['4x'] = df['1x'] * 4
df['5x'] = df['1x'] * 5
from matplotlib import ticker
import matplotlib.pyplot as plt
df.plot(kind='bar')
plt.ylabel('Monetary Value')
plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('$%.2f'))
plt.gca().xaxis.set_tick_params(rotation=0)
Pandas creates a similar figure...

Categories