How to set an appropriate point of view? - matplotlib

How to set xlim and ylim to see both cureves (omega and y) on a plot? Or how to verify that it is not possible?
import matplotlib.pyplot as plt
import numpy as np
e = 1.602176634e-19
m_e = 9.1093837015e-31
k = np.arange(0.00001, 50000, 0.003)
eps_0 = 8.8541878128e-12
n_0 = 100
c = 299792458
omega_p = np.sqrt(n_0*e**2/(eps_0*m_e))
omega = np.sqrt(omega_p**2+c**2+k**2)
y = k*c
fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [5, 5]
# Plot
ax.yaxis.set_label_coords(-0.07, 0.84)
ax.xaxis.set_label_coords(0.95, -0.05)
ax.set_xlabel(r'$k$')
ax.set_ylabel(r'$\omega$', rotation='horizontal')
ax.set_xlim(10000, 40000)
ax.set_ylim(299792454, 299792462.1700816)
ax.plot(k, omega)
ax.plot(k, y)
# Focusing on appropriate part
print(omega[1000000]-omega[999999])
print(omega[-1]-omega[-2])
print(len(omega))
print(k[1000000])
print(k[-1])
print(omega[1000000])
print(omega[-1])
print(y[int(ax.get_xlim()[0])])
print(y[int(ax.get_xlim()[1])])
plt.show()
The output now:
There should be also an assymptote.

An idea is to just let matplotlib choose its default limits. Then you can interactively zoom in to an area of interest. The code below sets a log scale for the y-axis, which might help to fit everything. In order to avoid too many points, the 16 million points of np.arange(0.00001, 50000, 0.003) are replaced by np.linspace(0.00001, 50000, 10000).
import matplotlib.pyplot as plt
import numpy as np
e = 1.602176634e-19
m_e = 9.1093837015e-31
# k = np.arange(0.00001, 50000, 0.003)
k = np.linspace(0.00001, 50000, 10000)
eps_0 = 8.8541878128e-12
n_0 = 100
c = 299792458
omega_p = np.sqrt(n_0 * e ** 2 / (eps_0 * m_e))
omega = np.sqrt(omega_p ** 2 + c ** 2 + k ** 2)
y = k * c
fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [5, 5]
ax.set_xlabel(r'$k$')
ax.set_ylabel(r'$\omega$', rotation='horizontal')
ax.plot(k, omega, color='blue')
ax.plot(k, y, color='red')
ax.set_yscale('log')
plt.show()

Related

pyplot 3d z axis-log plot

In order to create a 3d plot using plot_surface and wireframe I wrote this (looking here around)
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import rc
from matplotlib.ticker import MultipleLocator
import matplotlib.ticker as mticker
import numpy as np
from matplotlib.ticker import FormatStrFormatter
def log_tick_formatter(val, pos=None):
return f"10$^{{{int(val)}}}$"
data=np.genfromtxt('jpdfomegal2_90.dat')
x_len= len(np.unique(data[:, 0]))
y_len= len(np.unique(data[:, 1]))
X = data[:, 0].reshape(x_len, y_len)
Y = data[:, 1].reshape(x_len, y_len)
Z = data[:, 2].reshape(x_len, y_len)
#identify lowest non-negative Z value Zmin>0
Zmin = np.where(Z > 0, Z, np.inf).min()
Zmax = Z.max()
#and substitute zero with a slightly lower value than Zmin
Z[Z==0] = 0.9 * Zmin
#log transformation because the conversion in 3D
#does not work well in matplotlib
Zlog = np.log10(Z)
rc('font',family='palatino')
rc('font',size=18)
fig = plt.figure(figsize=(12,8))
#ax = fig.add_subplot(projection='3d')
ax = Axes3D(fig)
ax.set_xlim3d(0,15)
ax.set_zlim3d(np.floor(np.log10(Zmin))-1, np.ceil(np.log10(10)))
ax.zaxis.set_major_formatter(mticker.FuncFormatter(log_tick_formatter))
ax.zaxis.set_major_locator(mticker.MaxNLocator(integer=True))
rc('font',family='palatino')
rc('font',size=18)
tmp_planes = ax.zaxis._PLANES
ax.zaxis._PLANES = ( tmp_planes[2], tmp_planes[3],
tmp_planes[0], tmp_planes[1],
tmp_planes[4], tmp_planes[5])
ax.set_xlabel('$\omega^2 /<\omega^2>$')
ax.xaxis.labelpad = 10
ax.yaxis.labelpad = 10
ax.set_ylabel('cos$(\omega,\lambda^2)$')
ax.zaxis.set_rotate_label(False) # disable automatic rotation
ax.zaxis.labelpad = 10
ax.set_zlabel('')
ax.view_init(elev=17, azim=-60)
ax.grid(False)
ax.xaxis.pane.set_edgecolor('black')
ax.yaxis.pane.set_edgecolor('black')
ax.zaxis.pane.set_edgecolor('black')
ax.xaxis.pane.fill = False
ax.yaxis.pane.fill = False
ax.zaxis.pane.fill = False
ax.xaxis.set_major_locator(MultipleLocator(2))
ax.yaxis.set_major_locator(MultipleLocator(0.2))
ax.zaxis.set_major_locator(MultipleLocator(1))
#not sure this axis scaling routine is really necessary
scale_x = 1
scale_y = 1
scale_z = 0.8
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([scale_x, scale_y, scale_z, 1]))
ax.contour(X, Y, np.log10(Z), 4, lw=0.1, colors="k", linestyles="--", offset=np.floor(np.log10(Zmin))-1)#-7)
surf = ax.plot_surface(X, Y, np.log10(Z), cmap="binary", lw=0.1,alpha=0.5)
ax.plot_wireframe(X, Y, np.log10(Z),linewidth=1,color='k')
ax.contour(X, Y, np.log10(Z), 4, lw=0.1, colors="k", linestyles="solid")
fig.colorbar(surf, shrink=0.5, aspect=20)
plt.tight_layout()
plt.savefig('jpdf_lambda2_90.png', bbox_inches='tight')
plt.show()
the problem is related to the "minorticks" along zaxis .. I obtain this :
but I would have this format and ticks in the axis
Does somebody clarify how to obtain it and as well I did not find a way to use the log scale in pyplot 3d
There's an open bug on log-scaling in 3D plots, and it looks like there won't be a fix any time soon.
You can use a matplotlib.ticker.FixedLocator to add the z-axis minor ticks, as shown below.
I didn't have your data, so I've plotted an arbitrary surface.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import rc
from matplotlib.ticker import MultipleLocator, FixedLocator
import matplotlib.ticker as mticker
import numpy as np
from matplotlib.ticker import FormatStrFormatter
def log_tick_formatter(val, pos=None):
return f"10$^{{{int(val)}}}$"
x = np.linspace(1,15,15)
y = np.linspace(0,1,15)
X, Y = np.meshgrid(x, y)
Z = 1 + X**2 * Y**2
#identify lowest non-negative Z value Zmin>0
Zmin = np.where(Z > 0, Z, np.inf).min()
Zmax = Z.max()
#and substitute zero with a slightly lower value than Zmin
Z[Z==0] = 0.9 * Zmin
rc('font',family='palatino')
rc('font',size=18)
fig = plt.figure(figsize=(12,8))
ax = Axes3D(fig, auto_add_to_figure=False)
fig.add_axes(ax)
ax.set_xlim3d(0,15)
ax.set_zlim3d(np.floor(np.log10(Zmin))-1, np.ceil(np.log10(Zmax)))
ax.zaxis.set_major_formatter(mticker.FuncFormatter(log_tick_formatter))
tmp_planes = ax.zaxis._PLANES
ax.zaxis._PLANES = ( tmp_planes[2], tmp_planes[3],
tmp_planes[0], tmp_planes[1],
tmp_planes[4], tmp_planes[5])
ax.set_xlabel('$\omega^2 /<\omega^2>$')
ax.xaxis.labelpad = 10
ax.yaxis.labelpad = 10
ax.set_ylabel('cos$(\omega,\lambda^2)$')
ax.zaxis.set_rotate_label(False) # disable automatic rotation
ax.zaxis.labelpad = 10
ax.set_zlabel('')
ax.view_init(elev=17, azim=-60)
ax.grid(False)
ax.xaxis.pane.set_edgecolor('black')
ax.yaxis.pane.set_edgecolor('black')
ax.zaxis.pane.set_edgecolor('black')
ax.xaxis.pane.fill = False
ax.yaxis.pane.fill = False
ax.zaxis.pane.fill = False
ax.xaxis.set_major_locator(MultipleLocator(2))
ax.yaxis.set_major_locator(MultipleLocator(0.2))
ax.zaxis.set_major_locator(MultipleLocator(1))
# Z minor ticks
zminorticks = []
zaxmin, zaxmax = ax.get_zlim()
for zorder in np.arange(np.floor(zaxmin),
np.ceil(zaxmax)):
zminorticks.extend(np.log10(np.linspace(2,9,8)) + zorder)
ax.zaxis.set_minor_locator(FixedLocator(zminorticks))
#not sure this axis scaling routine is really necessary
scale_x = 1
scale_y = 1
scale_z = 0.8
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([scale_x, scale_y, scale_z, 1]))
ax.contour(X, Y, np.log10(Z), 4, colors="k", linestyles="--", offset=np.floor(np.log10(Zmin))-1)#-7)
surf = ax.plot_surface(X, Y, np.log10(Z), cmap="binary", lw=0.1,alpha=0.5)
ax.plot_wireframe(X, Y, np.log10(Z),linewidth=1,color='k')
ax.contour(X, Y, np.log10(Z), 4, colors="k", linestyles="solid")
fig.colorbar(surf, shrink=0.5, aspect=20)
# get a warning that Axes3D is incompatible with tight_layout()
# plt.tight_layout()
# for saving
# fig.savefig('log3d.png')
plt.show()

How to share xaxis in contour-map-subplots which share a colorbar

I try to make 3 subplot which share one colorbar and the xaxis, as already explained by spinup in
Matplotlib 2 Subplots, 1 Colorbar
Using maps (with coastlines) in the subplots, it seems that a sharex is not supported.
However, is there a way, to apply a shared axis?
import cartopy.crs as ccrs
from cartopy.mpl.geoaxes import GeoAxes
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import AxesGrid
import numpy as np
def sample_data_3d(shape):
"""Returns `lons`, `lats`, `times` and fake `data`"""
ntimes, nlats, nlons = shape
lats = np.linspace(-np.pi / 2, np.pi / 2, nlats)
lons = np.linspace(0, 2 * np.pi, nlons)
lons, lats = np.meshgrid(lons, lats)
wave = 0.75 * (np.sin(2 * lats) ** 8) * np.cos(4 * lons)
mean = 0.5 * np.cos(2 * lats) * ((np.sin(2 * lats)) ** 2 + 2)
lats = np.rad2deg(lats)
lons = np.rad2deg(lons)
data = wave + mean
times = np.linspace(-1, 1, ntimes)
new_shape = data.shape + (ntimes, )
data = np.rollaxis(data.repeat(ntimes).reshape(new_shape), -1)
data *= times[:, np.newaxis, np.newaxis]
return lons, lats, times, data
def main():
projection = ccrs.PlateCarree()
axes_class = (GeoAxes,
dict(map_projection=projection))
lons, lats, times, data = sample_data_3d((6, 73, 145))
fig = plt.figure()
axgr = AxesGrid(fig, 111, axes_class=axes_class,
nrows_ncols=(3, 1),
axes_pad=0.6,
share_all=True, #doesn't change anything
cbar_location='bottom',
cbar_mode='single',
cbar_pad=0.2,
cbar_size='3%',
label_mode='') # note the empty label_mode
for i, ax in enumerate(axgr):
ax.coastlines()
ax.add_feature(cartopy.feature.LAND, zorder=100,
edgecolor='k',facecolor='w')
ax.set_xticks(np.linspace(-180, 180, 5), crs=projection)
ax.set_yticks(np.linspace(-90, 90, 5), crs=projection)
p = ax.contourf(lons, lats, data[i, ...],
transform=projection,
cmap='RdBu')
axgr.cbar_axes[0].colorbar(p)
plt.show()

Pretty confusion matrix visualisation with matplotlib

I'm wondering if there are some templates for viewing confusion matrices in matplotlib with a similar rendering, of which I ignore the specific nomenclature.
I have tried doing something similar with your fig 2. Here is my code using hand written digits data.
import numpy as np
from scipy import ndimage
from matplotlib import pyplot as plt
from sklearn import manifold, datasets
from scipy.spatial.distance import pdist, squareform
from scipy.cluster.hierarchy import leaves_list, linkage
def get_small_Xy(X, y, n=8):
X = np.vstack([X[y==e][0:n] for e in np.unique(y)])
y = np.hstack([[e]*n for e in np.unique(y)])
return X, y
# Load digit data
X_, y_ = datasets.load_digits(return_X_y=True)
# get a small set of data
X, y = get_small_Xy(X_, y_)
# Get similarity matrix
D = 1-squareform(pdist(X, metric='cosine'))
Z = linkage(D, method='ward')
ind = leaves_list(Z)
D = D[ind, :]
D = D[:, ind]
# labels and colors related
lbs = np.array([i if i==j else 10 for i in y for j in y])
colors = np.array(['C{}'.format(i) for i in range(10)]+['gray'])
colors[7] = '#413c39'
c = colors[lbs]
font1 = {'family': 'Arial',
'weight': 'normal',
'size': 8,
}
fig, ax = plt.subplots(1, 1, figsize=(10, 10))
n = np.product(X.shape[0])
xx, yy = np.meshgrid(range(n), range(n))
xy = np.stack([xx.ravel(), yy.ravel()]).T
ax.scatter(xy[:, 0], xy[:, 1], s=D**4*30, fc=c, ec=None, alpha=0.8)
ax.set_xlim(-1, n)
ax.set_ylim(n, -1)
ax.tick_params(top=False, bottom=False, left=False, right=False, labelleft=False, labelbottom=False)
# place text
for i, e in enumerate(y):
ax.text(-1.2, i, e, ha='right', va='center', fontdict=font1, c=colors[e])
for i, e in enumerate(y):
ax.text(i, -1, e, ha='center', va='bottom', fontdict=font1, c=colors[e])
# draw lines
for e in np.where(np.diff(y))[0]:
ax.axhline(e+0.5, color='gray', lw=0.5, alpha=0.8)
ax.axvline(e+0.5, color='gray', lw=0.5, alpha=0.8)
One issue is the alpha of all points, which seems not to possible to set with different values with plot scatters in one run.

How to create a swarm plot with matplotlib

I know the question is not very informative.. but as I do not know the name of his type of plot, I can not be more informative..
[EDIT] I changed the title, and now it is more informative...
You can do something similar with seaborn.swarmplot. I also use seaborn.boxplot (with the whiskers and caps turned off) to plot the mean and range:
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style("whitegrid")
tips = sns.load_dataset("tips")
ax = sns.swarmplot(x="day", y="total_bill", data=tips)
ax = sns.boxplot(x="day", y="total_bill", data=tips,
showcaps=False,boxprops={'facecolor':'None'},
showfliers=False,whiskerprops={'linewidth':0})
plt.show()
If (for whatever reason) you don't want to use seaborn, you can have a go at making them yourself (see e.g. this explanation: https://www.flerlagetwins.com/2020/11/beeswarm.html ).
A simple version is:
#!/usr/bin/env python3
import matplotlib.pyplot as plt
import numpy as np
def simple_beeswarm(y, nbins=None):
"""
Returns x coordinates for the points in ``y``, so that plotting ``x`` and
``y`` results in a bee swarm plot.
"""
y = np.asarray(y)
if nbins is None:
nbins = len(y) // 6
# Get upper bounds of bins
x = np.zeros(len(y))
ylo = np.min(y)
yhi = np.max(y)
dy = (yhi - ylo) / nbins
ybins = np.linspace(ylo + dy, yhi - dy, nbins - 1)
# Divide indices into bins
i = np.arange(len(y))
ibs = [0] * nbins
ybs = [0] * nbins
nmax = 0
for j, ybin in enumerate(ybins):
f = y <= ybin
ibs[j], ybs[j] = i[f], y[f]
nmax = max(nmax, len(ibs[j]))
f = ~f
i, y = i[f], y[f]
ibs[-1], ybs[-1] = i, y
nmax = max(nmax, len(ibs[-1]))
# Assign x indices
dx = 1 / (nmax // 2)
for i, y in zip(ibs, ybs):
if len(i) > 1:
j = len(i) % 2
i = i[np.argsort(y)]
a = i[j::2]
b = i[j+1::2]
x[a] = (0.5 + j / 3 + np.arange(len(b))) * dx
x[b] = (0.5 + j / 3 + np.arange(len(b))) * -dx
return x
fig = plt.figure(figsize=(2, 4))
fig.subplots_adjust(0.2, 0.1, 0.98, 0.99)
ax = fig.add_subplot(1, 1, 1)
y = np.random.gamma(20, 10, 100)
x = simple_beeswarm(y)
ax.plot(x, y, 'o')
fig.savefig('bee.png')

group boxplot histogramming

I would like to group my data and to plot the boxplot for all the groups. There are many questions and answer about that, my problem is that I want to group by a continuos variable, so I want to histogramming my data.
Here what I have done. My data:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
x = np.random.chisquare(5, size=100000)
y = np.random.normal(size=100000) / (0.05 * x + 0.1) + 2 * x
f, ax = plt.subplots()
ax.plot(x, y, '.', alpha=0.05)
plt.show()
I want to study the behaviour of y (location, width, ...) as a function of x. I am not interested in the distribution of x so I will normalized it.
f, ax = plt.subplots()
xbins = np.linspace(0, 25, 50)
ybins = np.linspace(-20, 50, 50)
H, xedges, yedges = np.histogram2d(y, x, bins=(ybins, xbins))
norm = np.sum(H, axis = 0)
H /= norm
ax.pcolor(xbins, ybins, np.nan_to_num(H), vmax=.4)
plt.show()
I can plot histogram, but I want boxplot
binning = np.concatenate(([0], np.sort(np.random.random(20) * 25), [25]))
idx = np.digitize(x, binning)
data_to_plot = [y[idx == i] for i in xrange(len(binning))]
f, ax = plt.subplots()
midpoints = 0.5 * (binning[1:] + binning[:-1])
widths = 0.9 * (binning[1:] - binning[:-1])
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
majorLocator = MultipleLocator(2)
ax.boxplot(data_to_plot, positions = midpoints, widths=widths)
ax.set_xlim(0, 25)
ax.xaxis.set_major_locator(majorLocator)
ax.set_xlabel('x')
ax.set_ylabel('median(y)')
plt.show()
Is there an automatic way to do that, like ax.magic(x, y, binning)? Is there a better way to do that? (Have a look to https://root.cern.ch/root/html/TProfile.html for example, which plot the mean and the error of the mean as error bars)
In addition, I want to minize the memory footprint (my real data are much more than 100000), I am worried about data_to_plot, is it a copy?