I have problems to plot two arrays with the right scaling. I use the dtw package to compare the two arrays, x and y (https://pypi.python.org/pypi/dtw/1.0). The function dtw returns a matrix and a path.
With the following code, I can plot the matrix and the path:
import matplotlib.pyplot as plt
dist, cost, acc, path = dtw(x, y, dist=lambda x, y: norm(x - y, ord=1))
plt.imshow(acc.T, origin='lower', cmap=cm.gray, interpolation='nearest')
plt.colorbar()
plt.plot(path[0], path[1], 'w')
plt.ylim((-0.5, acc.shape[1]-0.5))
plt.xlim((-0.5, acc.shape[0]-0.5))
Resulting figure:
However, I would like to plot the two curves aligned to it, like shown in (http://www.psb.ugent.be/cbd/papers/gentxwarper/DTWalgorithm.htm). One curve is above the matrix, the other one is on the left side, so that you can compare which parts are equal.
Like suggested by kwinkunks (see comment) I used this example as template. Please note that I used "plt.pcolor()" instead of "plt.image()" to plot the matrix. This is my code and the resulting figure:
'''
Plotting
'''
nullfmt = NullFormatter()
# definitions for the axes
left, width = 0.12, 0.60
bottom, height = 0.08, 0.60
bottom_h = 0.16 + width
left_h = left + 0.27
rect_plot = [left_h, bottom, width, height]
rect_x = [left_h, bottom_h, width, 0.2]
rect_y = [left, bottom, 0.2, height]
# start with a rectangular Figure
plt.figure(2, figsize=(8, 8))
axplot = plt.axes(rect_plot)
axx = plt.axes(rect_x)
axy = plt.axes(rect_y)
# Plot the matrix
axplot.pcolor(acc.T,cmap=cm.gray)
axplot.plot(path[0], path[1], 'w')
axplot.set_xlim((0, len(x)))
axplot.set_ylim((0, len(linear)))
axplot.tick_params(axis='both', which='major', labelsize=18)
# Plot time serie horizontal
axx.plot(x,'.', color='k')
axx.tick_params(axis='both', which='major', labelsize=18)
xloc = plt.MaxNLocator(4)
x2Formatter = FormatStrFormatter('%d')
axx.yaxis.set_major_locator(xloc)
axx.yaxis.set_major_formatter(x2Formatter)
# Plot time serie vertical
axy.plot(y,linear,'.',color='k')
axy.invert_xaxis()
yloc = plt.MaxNLocator(4)
xFormatter = FormatStrFormatter('%d')
axy.xaxis.set_major_locator(yloc)
axy.xaxis.set_major_formatter(xFormatter)
axy.tick_params(axis='both', which='major', labelsize=18)
#Limits
axx.set_xlim(axplot.get_xlim())
axy.set_ylim(axplot.get_ylim())
plt.show()
Related
I already have a scatter plot with different groups of elements and histograms on the margins, but they are linked to the whole data, not to the individual groups:
I'd like to have 2 histograms, one for each group of elements. How do I do that?
Here's my code:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import csv
from matplotlib.colors import LinearSegmentedColormap
data= pd.read_csv("data.csv")
x=data['Fe']
y=data['V']
z=data['Discovery']
# Fixing random state for reproducibility
np.random.seed(19680801)
# definitions for the axes
left, width = 0.1, 0.7
bottom, height = 0.1, 0.7
spacing = 0.05
rect_scatter = [left, bottom, width, height]
rect_histx = [left, bottom + height + spacing, width, 0.2]
rect_histy = [left + width + spacing, bottom, 0.2, height]
# start with a rectangular Figure
fig=plt.figure(figsize=(7, 6))
ax_scatter = plt.axes(rect_scatter)
ax_scatter.tick_params(direction='in', top=True, right=True)
ax_histx = plt.axes(rect_histx)
ax_histx.tick_params(direction='in', labelbottom=True)
ax_histy = plt.axes(rect_histy)
ax_histy.tick_params(direction='in', labelleft=False)
# the function that separates the dots in different classes:
classes = np.zeros( len(x) )
classes[(z == 'Transit')] = 1
classes[(z == 'Radial Velocity')] = 2
# create color map:
colors = ['purple', 'orange']
cm = LinearSegmentedColormap.from_list('custom', colors, N=len(colors))
# the scatter plot:
scatter = ax_scatter.scatter(x, y, c=classes, s=10, cmap=cm, alpha=0.6)
lines, labels = scatter.legend_elements()
# legend with custom labels
labels = [r'Transit', r'Radial Velocity']
legend = ax_scatter.legend(lines, labels,
loc="upper left", title="Planetary Discovery Method")
ax_scatter.add_artist(legend)
# now determine nice limits by hand:
binwidth = 0.1
ax_scatter.set_xlim((-1, 0.7))
ax_scatter.set_ylim((-0.9, 0.9))
#histogram
weights = np.ones_like(x)/(len(x))
weights2 = np.ones_like(y)/(len(y))
ax_histx.hist(x, bins=bins, weights=weights, color='chartreuse')
ax_histy.hist(y, bins=bins, weights=weights, orientation='horizontal', color='darkmagenta')
ax_histx.set_xlim(ax_scatter.get_xlim())
ax_histy.set_ylim(ax_scatter.get_ylim())
#labeling
ax_scatter.set_xlabel('[Fe/H]')
ax_scatter.set_ylabel('[V/H]')
ax_histy.set_xlabel('Relative Dist.')
ax_histx.set_ylabel('Relative Dist.')
plt.show()
I'll add an example of a plot I'm trying to reach:
I am attempting to produce a plot like this which combines a cartesian scatter plot and a polar histogram. (Radial lines optional)
A similar solution (by Nicolas Legrand) exists for looking at differences in x and y (code here), but we need to look at ratios (i.e. x/y).
More specifically, this is useful when we want to look at the relative risk measure which is the ratio of two probabilities.
The scatter plot on it's own is obviously not a problem, but the polar histogram is more advanced.
The most promising lead I have found is this central example from the matplotlib gallery here
I have attempted to do this, but have run up against the limits of my matplotlib skills. Any efforts moving towards this goal would be great.
I'm sure that others will have better suggestions, but one method that gets something like you want (without the need for extra axes artists) is to use a polar projection with a scatter and bar chart together. Something like
import matplotlib.pyplot as plt
import numpy as np
x = np.random.uniform(size=100)
y = np.random.uniform(size=100)
r = np.sqrt(x**2 + y**2)
phi = np.arctan2(y, x)
h, b = np.histogram(phi, bins=np.linspace(0, np.pi/2, 21), density=True)
colors = plt.cm.Spectral(h / h.max())
ax = plt.subplot(111, projection='polar')
ax.scatter(phi, r, marker='.')
ax.bar(b[:-1], h, width=b[1:] - b[:-1],
align='edge', bottom=np.max(r) + 0.2, color=colors)
# Cut off at 90 degrees
ax.set_thetamax(90)
# Set the r grid to cover the scatter plot
ax.set_rgrids([0, 0.5, 1])
# Let's put a line at 1 assuming we want a ratio of some sort
ax.set_thetagrids([45], [1])
which will give
It is missing axes labels and some beautification, but it might be a place to start. I hope it is helpful.
You can use two axes on top of each other:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(6,6))
ax1 = fig.add_axes([0.1,0.1,.8,.8], label="cartesian")
ax2 = fig.add_axes([0.1,0.1,.8,.8], projection="polar", label="polar")
ax2.set_rorigin(-1)
ax2.set_thetamax(90)
plt.show()
Ok. Thanks to the answer from Nicolas, and the answer from tomjn I have a working solution :)
import numpy as np
import matplotlib.pyplot as plt
# Scatter data
n = 50
x = 0.3 + np.random.randn(n)*0.1
y = 0.4 + np.random.randn(n)*0.02
def radial_corner_plot(x, y, n_hist_bins=51):
"""Scatter plot with radial histogram of x/y ratios"""
# Axis setup
fig = plt.figure(figsize=(6,6))
ax1 = fig.add_axes([0.1,0.1,.6,.6], label="cartesian")
ax2 = fig.add_axes([0.1,0.1,.8,.8], projection="polar", label="polar")
ax2.set_rorigin(-20)
ax2.set_thetamax(90)
# define useful constant
offset_in_radians = np.pi/4
def rotate_hist_axis(ax):
"""rotate so that 0 degrees is pointing up and right"""
ax.set_theta_offset(offset_in_radians)
ax.set_thetamin(-45)
ax.set_thetamax(45)
return ax
# Convert scatter data to histogram data
r = np.sqrt(x**2 + y**2)
phi = np.arctan2(y, x)
h, b = np.histogram(phi,
bins=np.linspace(0, np.pi/2, n_hist_bins),
density=True)
# SCATTER PLOT -------------------------------------------------------
ax1.scatter(x,y)
ax1.set(xlim=[0, 1], ylim=[0, 1], xlabel="x", ylabel="y")
ax1.spines['right'].set_visible(False)
ax1.spines['top'].set_visible(False)
# HISTOGRAM ----------------------------------------------------------
ax2 = rotate_hist_axis(ax2)
# rotation of axis requires rotation in bin positions
b = b - offset_in_radians
# plot the histogram
bars = ax2.bar(b[:-1], h, width=b[1:] - b[:-1], align='edge')
def update_hist_ticks(ax, desired_ratios):
"""Update tick positions and corresponding tick labels"""
x = np.ones(len(desired_ratios))
y = 1/desired_ratios
phi = np.arctan2(y,x) - offset_in_radians
# define ticklabels
xticklabels = [str(round(float(label), 2)) for label in desired_ratios]
# apply updates
ax2.set(xticks=phi, xticklabels=xticklabels)
return ax
ax2 = update_hist_ticks(ax2, np.array([1/8, 1/4, 1/2, 1, 2, 4, 8]))
# just have radial grid lines
ax2.grid(which="major", axis="y")
# remove bin count labels
ax2.set_yticks([])
return (fig, [ax1, ax2])
fig, ax = radial_corner_plot(x, y)
Thanks for the pointers!
i have a little problem to create a subplot loop.
The following code show my result for one plot.... So it starts with a dayloop than with a hour loop (8 timesteps).
If i run the code i get a nice QUiver plot with the colorbar.
for dd in range(1,15):
day=str(dd)
readfile=fns[files_indizes[dd]]
if dd < 10:
nc_u_comp = NetCDFFile(ROOT+u_comp1+'0'+day+comp)
nc_v_comp = NetCDFFile(ROOT+v_comp1+'0'+day+comp)
else:
nc_u_comp = NetCDFFile(ROOT+u_comp1+day+comp)
nc_v_comp = NetCDFFile(ROOT+v_comp1+day+comp)
time = nc_u_comp.variables['time'][:]
index=readfile.find(comp)
index=index+len(comp)
date=readfile[index-14:index-6]
plt.clf()
for tt in range(0,len(time)):
if tt < 10:
h =str(0)+str(tt)
else:
h=str(tt)
varU=nc_u_comp.variables['u10'][tt,:,:]
varV=nc_v_comp.variables['v10'][tt,:,:]
lat = nc_u_comp.variables['latitude'][:]
lon = nc_u_comp.variables['longitude'][:]
plt.rcParams["figure.figsize"] = [10,10]
#plane projection of the world
#map with box size (defintion on the top)
box = sgeom.box(minx=llcrnrlon, maxx=urcrnrlon, miny=llcrnrlat, maxy=urcrnrlat)
x0, y0, x1, y1 = box.bounds
#Map plot. The middel of the map is central_longitude
#proj = ccrs.PlateCarree(central_longitude=0)
proj=ccrs.PlateCarree()
#Change middelpoint of the map
box_proj = ccrs.PlateCarree(central_longitude=0)
ax2 = plt.axes(projection=proj)
ax2.set_extent([x0, x1, y0, y1], box_proj)
ax2.add_feature(cartopy.feature.BORDERS, linestyle='-', alpha=.5)
ax2.coastlines(resolution='50m')
#Definition of the scale_bar
gl = ax2.gridlines(ccrs.PlateCarree(), \
linestyle='--', alpha=1, linewidth=0.5, draw_labels=True)
gl.xlabels_top = False
gl.ylabels_right = False
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
magnitude = (varU ** 2 + varV ** 2) ** 0.5
strm =plt.streamplot(lon , lat , varU, varV, linewidth=2, density=2, color=magnitude)
cbar= plt.colorbar()
cbar.set_label('$m/s$')
name='Wind in 10 m '+ date + h+' UTC'
ax2.set_aspect('auto')
plt.title(name, y=1)
Now i want to create an 2x4 Subplot array with a colorbar allocate to the complete Subplot array.
I find some infromation in the internet, but it doesn't run with my code. Maybe someone can help me?
This shows how to plot an array of simple Cartopy maps in 4 rows 2 columns. Also shows how to plot a colorbar to accompany the maps array. Hope it helps.
import numpy as np
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import matplotlib as mpl
# create figure with figsize big enough to accomodate all maps, labels, etc.
fig = plt.figure(figsize=(8, 10), tight_layout=False)
# define plot array's arrangement
columns = 2
rows = 4
# set projection to use
projex = ccrs.PlateCarree()
# set the colormap and norm for
# the colorbar to use
cmap1 = mpl.cm.magma
norm1 = mpl.colors.Normalize(vmin=0, vmax=100)
def plotmymap(axs):
# your plot specs of each map should replace this
img = np.random.randint(100, size=(15, 30)) # 2d array of random values (1-100)
# render image on current axis
plims = plt.imshow(img, extent=[-180,180,-90,90], alpha=0.5, cmap=cmap1, norm=norm1)
axs.set_global()
axs.coastlines()
# add title to the map
axs.set_title("Map_"+str(i))
return plims # for use by colorbar
for i in range(1, columns*rows +1):
# add a subplot into the array of plots
ax = fig.add_subplot(rows, columns, i, projection=projex)
plims = plotmymap(ax) # a simple maps is created on subplot
# add a subplot for vertical colorbar
bottom, top = 0.1, 0.9
left, right = 0.1, 0.8
fig.subplots_adjust(top=top, bottom=bottom, left=left, right=right, hspace=0.15, wspace=0.25)
cbar_ax = fig.add_axes([0.85, bottom, 0.05, top-bottom])
fig.colorbar(plims, cax=cbar_ax) # plot colorbar
plt.show() # this plot all the maps
The resulting plots:
I'm plotting an azimuth-elevation curve on a polar plot where the elevation is the radial component. By default, Matplotlib plots the radial value from 0 in the center to 90 on the perimeter. I want to reverse that so 90 degrees is at the center. I tried setting the limits with a call to ax.set_ylim(90,0) but this results in a LinAlgError exception being thrown. ax is the axes object obtained from a call to add_axes.
Can this be done and, if so, what must I do?
Edit: Here is what I'm using now. The basic plotting code was taken from one of the Matplotlib examples
# radar green, solid grid lines
rc('grid', color='#316931', linewidth=1, linestyle='-')
rc('xtick', labelsize=10)
rc('ytick', labelsize=10)
# force square figure and square axes looks better for polar, IMO
width, height = matplotlib.rcParams['figure.figsize']
size = min(width, height)
# make a square figure
fig = figure(figsize=(size, size))
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], projection='polar', axisbg='#d5de9c')
# Adjust radius so it goes 90 at the center to 0 at the perimeter (doesn't work)
#ax.set_ylim(90, 0)
# Rotate plot so 0 degrees is due north, 180 is due south
ax.set_theta_zero_location("N")
obs.date = datetime.datetime.utcnow()
az,el = azel_calc(obs, ephem.Sun())
ax.plot(az, el, color='#ee8d18', lw=3)
obs.date = datetime.datetime.utcnow()
az,el = azel_calc(obs, ephem.Moon())
ax.plot(az, el, color='#bf7033', lw=3)
ax.set_rmax(90.)
grid(True)
ax.set_title("Solar Az-El Plot", fontsize=10)
show()
The plot that results from this is
I managed to put he radial axis inverted. I had to remap the radius, in order to match the new axis:
fig = figure()
ax = fig.add_subplot(1, 1, 1, polar=True)
def mapr(r):
"""Remap the radial axis."""
return 90 - r
r = np.arange(0, 90, 0.01)
theta = 2 * np.pi * r / 90
ax.plot(theta, mapr(r))
ax.set_yticks(range(0, 90, 10)) # Define the yticks
ax.set_yticklabels(map(str, range(90, 0, -10))) # Change the labels
Note that is just a hack, the axis is still with the 0 in the center and 90 in the perimeter. You will have to use the mapping function for all the variables that you are plotting.
When plotting several y axis in Matplotlib, is there a way to specify how to align the origin (and/or some ytick labels) of the right axis with a specific value of the left axis?
Here is my problem: I would like to plot two set of data as well as their difference (basically, I am trying to reproduce this kind of graph).
I can reproduce it, but I have to manually adjust the ylim of the right axis so that the origin is aligned with the value I want from the left axis.
I putted below an example of a simplified version of the code I use. As you can see, I have to manually adjust scale of the right axis to align the origin of the right axis as well as the square.
import numpy as np
import scipy as sp
import matplotlib.pyplot as plt
grp1 = np.array([1.202, 1.477, 1.223, 1.284, 1.701, 1.724, 1.099,
1.242, 1.099, 1.217, 1.291, 1.305, 1.333, 1.246])
grp2 = np.array([1.802, 2.399, 2.559, 2.286, 2.460, 2.511, 2.296,
1.975])
fig = plt.figure(figsize=(6, 6))
ax = fig.add_axes([0.17, 0.13, 0.6, 0.7])
# remove top and right spines and turn ticks off if no spine
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('none')
ax.xaxis.set_ticks_position('none')
ax.yaxis.set_ticks_position('left')
# postition of tick out
ax.tick_params(axis='both', direction='out', width=3, length=7,
labelsize=24, pad=8)
ax.spines['left'].set_linewidth(3)
# plot groups vs random numbers to create dot plot
ax.plot(np.random.normal(1, 0.05, grp2.size), grp2, 'ok', markersize=10)
ax.plot(np.random.normal(2, 0.05, grp1.size), grp1, 'ok', markersize=10)
ax.errorbar(1, np.mean(grp2), fmt='_r', markersize=50,
markeredgewidth=3)
ax.errorbar(2, np.mean(grp1), fmt='_r', markersize=50,
markeredgewidth=3)
ax.set_xlim((0.5, 3.5))
ax.set_ylim((0, 2.7))
# create right axis
ax2 = fig.add_axes(ax.get_position(), sharex=ax, frameon=False)
ax2.spines['left'].set_color('none')
ax2.spines['top'].set_color('none')
ax2.spines['bottom'].set_color('none')
ax2.xaxis.set_ticks_position('none')
ax2.yaxis.set_ticks_position('right')
# postition of tick out
ax2.tick_params(axis='both', direction='out', width=3, length=7,
labelsize=24, pad=8)
ax2.spines['right'].set_linewidth(3)
ax2.set_xticks([1, 2, 3])
ax2.set_xticklabels(('gr2', 'gr1', 'D'))
ax2.hlines(0, 0.5, 3.5, linestyle='dotted')
#ax2.hlines((np.mean(adult)-np.mean(nrvm)), 0, 3.5, linestyle='dotted')
ax2.plot(3, (np.mean(grp1)-np.mean(grp2)), 'sk', markersize=12)
# manual adjustment so the origin is aligned width left group2
ax2.set_ylim((-2.3, 0.42))
ax2.set_xlim((0.5, 3.5))
plt.show()
You can make a little function that calculates the alignment of ax2:
def align_yaxis(ax1, v1, ax2, v2):
"""adjust ax2 ylimit so that v2 in ax2 is aligned to v1 in ax1"""
_, y1 = ax1.transData.transform((0, v1))
_, y2 = ax2.transData.transform((0, v2))
inv = ax2.transData.inverted()
_, dy = inv.transform((0, 0)) - inv.transform((0, y1-y2))
miny, maxy = ax2.get_ylim()
ax2.set_ylim(miny+dy, maxy+dy)
by using align_yaxis(), you can align the axes quickly:
#...... your code
# adjustment so the origin is aligned width left group2
ax2.set_ylim((0, 2.7))
align_yaxis(ax, np.mean(grp2), ax2, 0)
plt.show()
The above answer is Okay, but sometimes cuts out data, it is more thoroughly answered in the second answer here,
Matplotlib axis with two scales shared origin
or with a quick hack
def align_yaxis(ax1, v1, ax2, v2, y2min, y2max):
"""adjust ax2 ylimit so that v2 in ax2 is aligned to v1 in ax1."""
"""where y2max is the maximum value in your secondary plot. I haven't
had a problem with minimum values being cut, so haven't set this. This
approach doesn't necessarily make for axis limits at nice near units,
but does optimist plot space"""
_, y1 = ax1.transData.transform((0, v1))
_, y2 = ax2.transData.transform((0, v2))
inv = ax2.transData.inverted()
_, dy = inv.transform((0, 0)) - inv.transform((0, y1-y2))
miny, maxy = ax2.get_ylim()
scale = 1
while scale*(maxy+dy) < y2max:
scale += 0.05
ax2.set_ylim(scale*(miny+dy), scale*(maxy+dy))