How to plot profiles in front of a 2D field in matplotlib? - python

I have the following script that plots me a 2D field of some quantity in the domain:
def field(x, y, z):
fig, (ax) = plt.subplots()
ax.tricontour(x, y, z)
cntr = ax.tricontourf(x, y, z)
fig.colorbar(cntr, ax=ax)
plt.show()
Which gives me something like:
I want to add profiles on top of that figure, i.e.:
How do I do this?

Just plot on ax, setting linewidth (short lw) and color (short c) as needed:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(1)
x = np.random.uniform(-5, 40, 50)
y = np.random.uniform(-.25, .25, 50)
z = y * 100
fig, ax = plt.subplots(figsize=(10,2))
cntr = ax.tricontourf(x, y, z)
fig.colorbar(cntr, ax=ax)
ax.set(xlim=(0, 35), ylim=(-.2, .2))
for i in range(5, 35, 5):
ax.plot([i-1, i+1], [0.2, -0.2], lw=5, c='k')

Related

Adding contour labels doesn't label each contour, and removes some of the contour lines [duplicate]

The sample data is generated as follows,
import matplotlib as mpl
print(mpl.__version__) # 3.3.3
import matplotlib.pyplot as plt
import numpy as np
def f(x, y=0):
return np.piecewise(x, [x < 1, np.logical_and(1 <= x, x < 10), x >= 10], [lambda x: 0, lambda x: (x - 1) / 9 * 1000, lambda x: 1000])
x = np.logspace(-5, 5, 100)
y = np.logspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
I try to plot using the following code, but some contours disappear after calling clabel.
fig, ax = plt.subplots(figsize=(5, 3), dpi=120)
cr = ax.contour(X, Y, Z, levels=3, colors='black')
ax.clabel(cr, inline=True, fontsize=8, fmt='%d')
ax.set_xscale('log')
ax.set_yscale('log')
plt.show()
This issue still appears even when contour linewidth and label font size are decreased.
fig, ax = plt.subplots(figsize=(5, 3), dpi=120)
cr = ax.contour(X, Y, Z, levels=3, colors='black', linewidths=0.6)
ax.clabel(cr, inline=True, fontsize=3, fmt='%d')
ax.set_xscale('log')
ax.set_yscale('log')
plt.show()
I cannot figure out how to fix the weird behaviours of contour and clabel, and I suspect it is due to their incompatibility with log scale.
It is indeed a problem of the log axes, especially around the asymptote zero. However, why not defining the log axes before plotting, so matplotlib can take this into consideration when plotting?
import matplotlib as mpl
print(mpl.__version__) # 3.3.3
import matplotlib.pyplot as plt
import numpy as np
def f(x, y=0):
return np.piecewise(x, [x < 1, np.logical_and(1 <= x, x < 10), x >= 10], [lambda x: 0, lambda x: (x - 1) / 9 * 1000, lambda x: 1000])
x = np.logspace(-5, 5, 100)
y = np.logspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
fig, ax = plt.subplots(figsize=(5, 3), dpi=120)
ax.set_xscale('log')
ax.set_yscale('log')
cr = ax.contour(X, Y, Z, levels=3, colors='black')
ax.clabel(cr, inline=True, fontsize=8, fmt='%d')
plt.show()
Sample output:

Why do matplotlib contour labels make contours disappear?

The sample data is generated as follows,
import matplotlib as mpl
print(mpl.__version__) # 3.3.3
import matplotlib.pyplot as plt
import numpy as np
def f(x, y=0):
return np.piecewise(x, [x < 1, np.logical_and(1 <= x, x < 10), x >= 10], [lambda x: 0, lambda x: (x - 1) / 9 * 1000, lambda x: 1000])
x = np.logspace(-5, 5, 100)
y = np.logspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
I try to plot using the following code, but some contours disappear after calling clabel.
fig, ax = plt.subplots(figsize=(5, 3), dpi=120)
cr = ax.contour(X, Y, Z, levels=3, colors='black')
ax.clabel(cr, inline=True, fontsize=8, fmt='%d')
ax.set_xscale('log')
ax.set_yscale('log')
plt.show()
This issue still appears even when contour linewidth and label font size are decreased.
fig, ax = plt.subplots(figsize=(5, 3), dpi=120)
cr = ax.contour(X, Y, Z, levels=3, colors='black', linewidths=0.6)
ax.clabel(cr, inline=True, fontsize=3, fmt='%d')
ax.set_xscale('log')
ax.set_yscale('log')
plt.show()
I cannot figure out how to fix the weird behaviours of contour and clabel, and I suspect it is due to their incompatibility with log scale.
It is indeed a problem of the log axes, especially around the asymptote zero. However, why not defining the log axes before plotting, so matplotlib can take this into consideration when plotting?
import matplotlib as mpl
print(mpl.__version__) # 3.3.3
import matplotlib.pyplot as plt
import numpy as np
def f(x, y=0):
return np.piecewise(x, [x < 1, np.logical_and(1 <= x, x < 10), x >= 10], [lambda x: 0, lambda x: (x - 1) / 9 * 1000, lambda x: 1000])
x = np.logspace(-5, 5, 100)
y = np.logspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
fig, ax = plt.subplots(figsize=(5, 3), dpi=120)
ax.set_xscale('log')
ax.set_yscale('log')
cr = ax.contour(X, Y, Z, levels=3, colors='black')
ax.clabel(cr, inline=True, fontsize=8, fmt='%d')
plt.show()
Sample output:

I am plotting a 3d plot and i want the colours to be less 'distinct'

My code can be seen here:
import numpy as np
from matplotlib import cm
import mpl_toolkits.mplot3d.axes3d as axes3d
from matplotlib.ticker import LinearLocator, FormatStrFormatter
xlist = [+30,+20,+10,0,-10,-20,-30]
ylist = [0.0008,0.0009, 0.001, 0.0012, 0.0013]
total_costs=[[2084.8771849999903, 17314.19051000003, 26026.73173, 65340.709810000015, 108130.0746, 143560.64033000002, 188387.24033], [2129.155209999997, 17314.301310000024, 26026.996729999984, 65341.17821, 108130.792, 143561.44293000002, 188388.11793], [6637.1766100000095, 17314.412110000034, 26027.26173, 65341.646609999996, 108131.5094, 143562.24553000001, 188388.99553], [6623.21941000002, 17314.63371000004, 26027.791729999997, 65342.58341000001, 108132.9442, 150322.81264000002, 191661.16901], [6637.240810000003, 17314.744510000033, 26028.05673000001, 65343.05181000002, 110971.15911000001, 146393.01711000002, 191661.93621]]
Z = np.array(total_costs)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
X, Y = np.meshgrid(xlist, ylist)
ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
linewidth=0, antialiased=False,alpha=0.5,
`rstride=1,cstride=1, label='skata')`
ax.set_xlabel('System-1 imbalance')
ax.set_ylabel('Penalization factor [€/MWh]')
ax.set_zlabel('Total balancing costs [€]')
#ax.set_legend('upper left', fontsize=15)
#ax.tick_params(axis='both', labelsize=15)
plt.show()
When i run this i get a figure like this:
What i would like is to get a figure like this:
I guess it has something to do with my result being a list within a list with discrete values. Anyone got an idea?
Thank you in advance
I guess you want a more gradual tone change on the graph - the way I know how to do it is to "simply" increase the number of points being plotted using interpolation:
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d
import scipy.interpolate as interp
xlist = np.array([+30, +20, +10, 0, -10, -20, -30])
ylist = np.array([0.0008, 0.0009, 0.001, 0.0012, 0.0013])
total_costs = [[2084.8771849999903, 17314.19051000003, 26026.73173,
65340.709810000015, 108130.0746, 143560.64033000002,
188387.24033],
[2129.155209999997, 17314.301310000024, 26026.996729999984,
65341.17821, 108130.792, 143561.44293000002, 188388.11793],
[6637.1766100000095, 17314.412110000034, 26027.26173,
65341.646609999996, 108131.5094, 143562.24553000001,
188388.99553],
[6623.21941000002, 17314.63371000004, 26027.791729999997,
65342.58341000001, 108132.9442, 150322.81264000002,
191661.16901],
[6637.240810000003, 17314.744510000033, 26028.05673000001,
65343.05181000002, 110971.15911000001, 146393.01711000002,
191661.93621]]
X, Y = np.meshgrid(xlist, ylist)
Z = np.asarray(total_costs)
Zfunc = interp.interp2d(X, Y, Z, kind='cubic', copy=False)
n_points = 100 # change this to change the "resolution"
xnew = np.linspace(start=min(xlist), stop=max(xlist), num=n_points)
ynew = np.linspace(start=min(ylist), stop=max(ylist), num=n_points)
Xnew, Ynew = np.meshgrid(xnew, ynew)
Znew = Zfunc(xnew, ynew)
fig = plt.figure(figsize=(11, 8))
ax = plt.axes([0.05, 0.05, 0.9, 0.9], projection='3d')
surface = ax.plot_surface(Xnew, Ynew, Znew, rstride=1, cstride=1,
cmap='coolwarm', linewidth=0.25)
fig.colorbar(surface, shrink=0.75, aspect=9)
plt.show()
Linear interpolation:
Cubic interpolation:
The faces of the surface plot are colorized according to the Z value.
To get mixed or random colors on the faces you can supply a color array with the facecolors argument instead of a colormap.
colors=np.random.rand(X.shape[0]-1,X.shape[1]-1, 3)
ax.plot_surface(X, Y, Z, facecolors=colors,
linewidth=0, antialiased=False,alpha=0.5,
rstride=1,cstride=1, label='skata')
produces
In order to make the colors appear more close to each other, the solution would be not to use the complete range of the colormap. E.g. you could set vmin=0.5*Z.min(), vmax=2*Z.max(), in your call to plot_surface in order to map the colors to a range much larger than the one shown in the image, such that the actual values only cover part of the colormap.
ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, vmin=0.5*Z.min(), vmax=2*Z.max(),
linewidth=0, antialiased=False,alpha=0.5,
rstride=1,cstride=1, label='skata')
Is that what you mean?
def stackQuestion():
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d
xlist = np.array([+30,+20,+10,0,-10,-20,-30])
ylist = np.array([0.0008,0.0009, 0.001, 0.0012, 0.0013])
total_costs=[[2084.8771849999903, 17314.19051000003, 26026.73173, 65340.709810000015, 108130.0746, 143560.64033000002, 188387.24033],
[2129.155209999997, 17314.301310000024, 26026.996729999984, 65341.17821, 108130.792, 143561.44293000002, 188388.11793],
[6637.1766100000095, 17314.412110000034, 26027.26173, 65341.646609999996, 108131.5094, 143562.24553000001, 188388.99553],
[6623.21941000002, 17314.63371000004, 26027.791729999997, 65342.58341000001, 108132.9442, 150322.81264000002, 191661.16901],
[6637.240810000003, 17314.744510000033, 26028.05673000001, 65343.05181000002, 110971.15911000001, 146393.01711000002, 191661.93621]]
X, Y = np.meshgrid(xlist, ylist)
Z = np.array(total_costs)
fig = plt.figure(figsize = (11, 8))
ax = plt.axes([0.05, 0.05, 0.9, 0.9], projection = '3d')
surface = ax.plot_surface(X, Y, Z, rstride = 1, cstride = 1,\
cmap = 'coolwarm', linewidth = 0.25)
fig.colorbar(surface, shrink = 0.75, aspect = 9)
plt.show()

Plotting a grid with Matplotlib

I want to plot a self-specified grid using Matplotlib in Python.
I know of the np.meshgrid function and can use it to obtain the array of different points I want to connect, but am unsure of how to then plot the grid.
Code example:
x = np.linspace(0,100,100)
y = np.linspace(0,10,20)
xv, yv = np.meshgrid(x, y)
Now, how can I plot a grid of this xv array?
You can turn a grid on/off with grid(), but it's only possible to have the grid lines on axis ticks, so if you want it hand-made, what about this:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
xs = np.linspace(0, 100, 51)
ys = np.linspace(0, 10, 21)
ax = plt.gca()
# grid "shades" (boxes)
w, h = xs[1] - xs[0], ys[1] - ys[0]
for i, x in enumerate(xs[:-1]):
for j, y in enumerate(ys[:-1]):
if i % 2 == j % 2: # racing flag style
ax.add_patch(Rectangle((x, y), w, h, fill=True, color='#008610', alpha=.1))
# grid lines
for x in xs:
plt.plot([x, x], [ys[0], ys[-1]], color='black', alpha=.33, linestyle=':')
for y in ys:
plt.plot([xs[0], xs[-1]], [y, y], color='black', alpha=.33, linestyle=':')
plt.show()
It's much faster by using LineCollection:
import pylab as pl
from matplotlib.collections import LineCollection
x = np.linspace(0,100,100)
y = np.linspace(0,10,20)
pl.figure(figsize=(12, 7))
hlines = np.column_stack(np.broadcast_arrays(x[0], y, x[-1], y))
vlines = np.column_stack(np.broadcast_arrays(x, y[0], x, y[-1]))
lines = np.concatenate([hlines, vlines]).reshape(-1, 2, 2)
line_collection = LineCollection(lines, color="red", linewidths=1)
ax = pl.gca()
ax.add_collection(line_collection)
ax.set_xlim(x[0], x[-1])
ax.set_ylim(y[0], y[-1])

Adding errorbars to 3D plot in matplotlib

I can't find a way to draw errorbars in a 3D scatter plot in matplotlib.
Basically, for the following piece of code
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
X, Y, Z = axes3d.get_test_data(1)
ax.scatter(X, Y, zs = Z, zdir = 'z')
I am looking for something like
ax.errorbar(X,Y, zs = Z, dY, dX, zserr = dZ)
Is there a way to do this in mplot3d? If not, are there other libraries with this function?
There is clearly example on forum http://mple.m-artwork.eu/home/posts/simple3dplotwith3derrorbars
Here is the code but is not built-in functionality:
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as axes3d
fig = plt.figure(dpi=100)
ax = fig.add_subplot(111, projection='3d')
#data
fx = [0.673574075,0.727952994,0.6746285]
fy = [0.331657721,0.447817839,0.37733386]
fz = [18.13629648,8.620699842,9.807536512]
#error data
xerror = [0.041504064,0.02402152,0.059383144]
yerror = [0.015649804,0.12643117,0.068676131]
zerror = [3.677693713,1.345712547,0.724095592]
#plot points
ax.plot(fx, fy, fz, linestyle="None", marker="o")
#plot errorbars
for i in np.arange(0, len(fx)):
ax.plot([fx[i]+xerror[i], fx[i]-xerror[i]], [fy[i], fy[i]], [fz[i], fz[i]], marker="_")
ax.plot([fx[i], fx[i]], [fy[i]+yerror[i], fy[i]-yerror[i]], [fz[i], fz[i]], marker="_")
ax.plot([fx[i], fx[i]], [fy[i], fy[i]], [fz[i]+zerror[i], fz[i]-zerror[i]], marker="_")
#configure axes
ax.set_xlim3d(0.55, 0.8)
ax.set_ylim3d(0.2, 0.5)
ax.set_zlim3d(8, 19)
plt.show()
I ended up writing the method for matplotlib: official example for 3D errorbars:
import matplotlib.pyplot as plt
import numpy as np
ax = plt.figure().add_subplot(projection='3d')
# setting up a parametric curve
t = np.arange(0, 2*np.pi+.1, 0.01)
x, y, z = np.sin(t), np.cos(3*t), np.sin(5*t)
estep = 15
i = np.arange(t.size)
zuplims = (i % estep == 0) & (i // estep % 3 == 0)
zlolims = (i % estep == 0) & (i // estep % 3 == 2)
ax.errorbar(x, y, z, 0.2, zuplims=zuplims, zlolims=zlolims, errorevery=estep)
ax.set_xlabel("X label")
ax.set_ylabel("Y label")
ax.set_zlabel("Z label")
plt.show()

Categories

Resources