plotting the stair step plot and extract continuous values - python

I have discrete x and y axis data as
y=[14.0,11.0,14.0,31.0]
x=[3.45,3.88,3.99,4.33]
I need to plot the figure as depicted below.
Finally i want to extract continuous red line values.
I tried using the code below but it doesnot give the expected result.Hope experts may help me.
import numpy as np
import matplotlib.pyplot as plt
y=[14.0,11.0,14.0,0.0]
x=[3.45,3.88,3.99,4.33]
plt.step(x,y)
plt.show()

To get continuous values, use interp1d from scipy:
import numpy as np
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
x = np.array([3.45, 3.88, 3.99, 4.33])
y = np.array([14.0, 11.0, 14.0, 31.0])
y = np.cumsum(y) # ???
y0 = np.array([0])
x0 = np.interp([0], y, x)
x = np.concatenate([x0, x])
y = np.concatenate([y0, y])
# Continuous data
NUM = 1000 # modify the number of points you want
funct = interp1d(x, y, kind='next')
x_cont = np.linspace(x[0], x[-1], NUM)
y_cont = funct(x_cont)
# Plot
fig, ax = plt.subplots()
ax.step(x_cont, y_cont, color='r', label='test.mod', where='post')
ax.set_xlim(2, 5)
ax.set_ylim(0, y.max())
ax.invert_yaxis()
plt.show()
Output:
>>> x_cont
array([3.45 , 3.54777778, 3.64555556, 3.74333333, 3.84111111,
3.93888889, 4.03666667, 4.13444444, 4.23222222, 4.33 ])
>>> y_cont
array([ 0., 25., 25., 25., 25., 39., 70., 70., 70., 70.])

The first thing to notice is the x values of the step plot. Matplotlib threats these as absolute values. You also need to add the x-value 3.45 twice for the first vertical segment.
For the vertical segments there is a possibility to set it to 'pre' to draw the vertical line before the point or 'post' for after the point. I chose to set it to 'post'.
import numpy as np
import matplotlib.pyplot as plt
x = [3.45, 3.45, 3.88, 3.99, 4.33]
y = [0.00, 14.0, 25.0, 39.0, 70.0]
plt.step(x, y, 'r', label='test.mod', where='post')
# set range of x-axis
plt.xlim([2.0, 5.0])
# invert y-axis by setting the lower limit to a higher value than the upper limit
plt.ylim([60, 0])
plt.legend()
plt.show()

Related

Add constant x, y , z lines into matplotlib 3D scatter plot in Python

I want to add constant x, y, z lines into a matplotlib 3D scatter plot in Python which extended from this limit point, may I know how could I do so?
x_limit = [-0.5] y_limit = [151] z_limit = [1090]
Example code:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import pandas as pd
from scipy.stats import multivariate_normal
fig = plt.figure(figsize=(8,8)) # size 4 inches X 4 inches
ax = fig.add_subplot(111, projection='3d')
np.random.seed(42)
xs = np.random.random(100)*-0.8
ys = np.random.random(100)*300
zs = np.random.random(100)*10500
plot = ax.scatter(xs,ys,zs)
ax.set_title("3D plot with limit")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
x_limit = [-0.5]
y_limit = [151]
z_limit = [1090]
ax.scatter(x_limit, y_limit, z_limit, c = 'g', marker = "s", s = 50)
plt.show()
This code should do the trick. There are a couple important things to note though. First, the mesh grid created only worked because each of the three axes share the same limits. Second, while the x_limit and y_limit values work as the X and Y arguments it appears that the Z argument is expected to be of a higher dimensionality (hence why I used full_like to fill an array of the same shape as x_1 and x_2 with the Z limit).
x_1, x_2 = np.meshgrid(np.arange(0, 1.1, 0.1), np.arange(0, 1.1, 0.1))
ax.plot_surface(x_limit, x_1, x_2, color='r', alpha=0.5)
ax.plot_surface(x_1, y_limit, x_2, color='g', alpha=0.5)
ax.plot_surface(x_1, x_2, np.full_like(x_1, z_limit), color='b', alpha=0.5)

Pulling x-values of spline integer y-values?

I have the given sample data and interpolated spline:
import matplotlib.pyplot as plt
import numpy as np
from scipy import interpolate
x = [0.1, 1.5, 2.3, 5.5, 6.7, 7]
y = [5, 4, 5, 2, 2, 3]
s = interpolate.UnivariateSpline(x, y, s=0)
xs = np.linspace(min(x), max(x), 10000) #values for x axis
ys = s(xs)
plt.figure()
plt.plot(xs, ys, label='spline')
plt.plot(x, y, 'x', label='collected data')
plt.legend()
I would like to pull the x values that correspond to the integer y values of the spline, but am not sure how to do this. I assume I will be using np.where() and have tried (to no avail):
root_index = np.where(ys == ys.round())
You could use the find_roots function from this post to find the exact interpolated x-values:
import matplotlib.pyplot as plt
import numpy as np
from scipy import interpolate
def find_roots(x, y):
s = np.abs(np.diff(np.sign(y))).astype(bool)
return x[:-1][s] + np.diff(x)[s] / (np.abs(y[1:][s] / y[:-1][s]) + 1)
x = [0.1, 1.5, 2.3, 5.5, 6.7, 7]
y = [5, 4, 5, 2, 2, 3]
s = interpolate.UnivariateSpline(x, y, s=0)
xs = np.linspace(min(x), max(x), 500) # values for x axis
ys = s(xs)
plt.figure()
plt.plot(xs, ys, label='spline')
for y0 in range(0, 7):
r = find_roots(xs, ys - y0)
if len(r) > 0:
plt.scatter(r, np.repeat(y0, len(r)), label=f'y0 = {y0}')
for ri in r:
plt.text(ri, y0, f' {ri:.3f}', ha='left', va='center')
plt.legend()
plt.xlim(min(x) - 0.2, max(x) + 1)
plt.show()
PS: with much less points for the x's, e.g. xs = np.linspace(min(x), max(x), 50), the curve would look a bit bumpy, and the interpolation would be slightly different:
It's not a good idea comparing two floats with equal. Use:
# play with `thresh`
thresh = 1e-4
root_index = np.where(np.abs(ys - ys.round())<1e-4)
printt(xs[root_index])
print(ys[root_index])
Output:
array([0.1 , 0.44779478, 2.29993999, 4.83732373, 7. ])
array([5. , 4.00009501, 4.99994831, 3.00006626, 3. ])
Numpy also has np.isclose. So something like:
root_index = np.isclose(ys, ys.round(), rtol=1e-5, atol=1e-4)
ys[root_index]
And you have the same output.
The object returned by scipy.interpolate.UnivariateSpline is a wrapper about fitpack interpolation routines. You can get an identical interpolation using the CubicSpline class: replace s = interpolation.UnivariateSpline(x, y, s=0) with s = interpolation.CubicSpline(x, y). The results are identical, as you can see in the figure at the end.
The advantage to using CubicSpline is that it returns a PPoly object which has working roots and solve methods. You can use this to compute the roots with integer offsets.
To compute the range of possible integers, you can use the derivative method coupled with roots:
x_extrema = np.concatenate((x[:1], s.derivative().roots(), x[-1:]))
y_extrema = s(x_extrema)
i_min = np.ceil(y_extrema.min())
i_max = np.floor(y_extrema.max())
Now you can compute the roots of the offset spline:
x_ints = [s.solve(i) for i in np.arange(i_min, i_max + 1)]
x_ints = np.concatenate(x_ints)
x_ints.sort()
Here are some additional plotting commands and their output:
plt.figure()
plt.plot(xs, interpolate.UnivariateSpline(x, y, s=0)(xs), label='Original Spline')
plt.plot(xs, ys, 'r:', label='CubicSpline')
plt.plot(x, y, 'x', label = 'Collected Data')
plt.plot(x_extrema, y_extrema, 's', label='Extrema')
plt.hlines([i_min, i_max], xs[0], xs[-1], label='Integer Limits')
plt.plot(x_ints, s(x_ints), '^', label='Integer Y')
plt.legend()
You can verify the numbers:
>>> s(x_ints)
array([5., 4., 4., 5., 5., 4., 3., 2., 2., 3., 4., 5.])

animated 3d python plot with several moving points

I have a similar problem to this one:
Animate a python pyplot by moving a point plotted via scatter.
The given values are positions (q) with corresponding velocities (v). Now I want to animate the shift in position over time for each point. My attempt
from matplotlib import pyplot as plt
import numpy as np
import mpl_toolkits.mplot3d.axes3d as p3
from matplotlib import animation
fig = plt.figure()
ax = p3.Axes3D(fig)
q = [[-4.32, -2.17, -2.25, 4.72, 2.97, 1.74],
[ 2.45, 9.73, 7.45,4.01,3.42, 1.80],[-1.40, -1.76, -3.08,-9.94,-3.13,-1.13]]
v = [[ 0.0068,0.024, -0.014,-0.013, -0.0068,-0.04],[ 0.012,
0.056, -0.022,0.016, 0.0045, 0.039],
[-0.0045, 0.031, 0.077,0.0016, -0.015,-0.00012]]
t=np.arange(0, 1000, 2)
x=q[0]
y=q[1]
z=q[2]
s=v[0]
u=v[1]
w=v[2]
points, = ax.plot(x, y, z, '*')
def update_points(t, x, y, z, points):
point = []
for i in range(0,len(x)-1,1):
points.set_data(np.array([x[i]+s[i]*t*5, y[i]+u[i]*t*5]))
points.set_3d_properties(z[i]+w[i]*t*5, 'z')
return point
ani=animation.FuncAnimation(fig, update_points, 10, fargs=(x, y, z, points))
plt.show()
does not work properly. The first image of the animation shows all points, but afterward, only the movement of the last point is simulated. The error happens in the defined update_points function because it seems like only the values for the last I are stored. Does anybody know how I have to change the code that all points are moving simultaneously?
Your problem is that you are only passing one set of coordinates to set_data()/set_3d_properties, and therefore only one point remains. You need to update all the coordinates of all your points, and then update points with those arrays.
I could not figure out exactly how you were doing your math in the update function, so here's an example using random fluctuations:
from matplotlib import pyplot as plt
import numpy as np
import mpl_toolkits.mplot3d.axes3d as p3
from matplotlib import animation
fig = plt.figure()
ax = p3.Axes3D(fig)
q = [[-4.32, -2.17, -2.25, 4.72, 2.97, 1.74],
[ 2.45, 9.73, 7.45,4.01,3.42, 1.80],[-1.40, -1.76, -3.08,-9.94,-3.13,-1.13]]
v = [[ 0.0068,0.024, -0.014,-0.013, -0.0068,-0.04],[ 0.012,
0.056, -0.022,0.016, 0.0045, 0.039],
[-0.0045, 0.031, 0.077,0.0016, -0.015,-0.00012]]
x=np.array(q[0])
y=np.array(q[1])
z=np.array(q[2])
s=np.array(v[0])
u=np.array(v[1])
w=np.array(v[2])
points, = ax.plot(x, y, z, '*')
txt = fig.suptitle('')
def update_points(num, x, y, z, points):
txt.set_text('num={:d}'.format(num)) # for debug purposes
# calculate the new sets of coordinates here. The resulting arrays should have the same shape
# as the original x,y,z
new_x = x+np.random.normal(1,0.1, size=(len(x),))
new_y = y+np.random.normal(1,0.1, size=(len(y),))
new_z = z+np.random.normal(1,0.1, size=(len(z),))
# update properties
points.set_data(new_x,new_y)
points.set_3d_properties(new_z, 'z')
# return modified artists
return points,txt
ani=animation.FuncAnimation(fig, update_points, frames=10, fargs=(x, y, z, points))
plt.show()
Thank you very much! It works now
from matplotlib import pyplot as plt
import numpy as np
import mpl_toolkits.mplot3d.axes3d as p3
from matplotlib import animation
fig = plt.figure()
ax = p3.Axes3D(fig)
q = [[-4.32, -2.17, -2.25, 4.72, 2.97, 1.74],
[ 2.45, 9.73, 7.45,4.01,3.42, 1.80],[-1.40, -1.76, -3.08,-9.94,-3.13,-1.13]]
v = [[ 0.0068,0.024, -0.014,-0.013, -0.0068,-0.04],[ 0.012,
0.056, -0.022,0.016, 0.0045, 0.039],
[-0.0045, 0.031, 0.077,0.0016, -0.015,-0.00012]]
x=np.array(q[0])
y=np.array(q[1])
z=np.array(q[2])
s=np.array(v[0])
u=np.array(v[1])
w=np.array(v[2])
points, = ax.plot(x, y, z, '*')
txt = fig.suptitle('')
def update_points(t, x, y, z, points):
txt.set_text('num={:d}'.format(t))
new_x = x + s * t
new_y = y + u * t
new_z = z + w * t
print('t:', t)
# update properties
points.set_data(new_x,new_y)
points.set_3d_properties(new_z, 'z')
# return modified artists
return points,txt
ani=animation.FuncAnimation(fig, update_points, frames=15, fargs=(x, y, z, points))
ax.set_xlabel("x [pc]")
ax.set_ylabel("y [pc]")
ax.set_zlabel('z [pc]')
plt.show()

Drawing heat map in python

I'm having two lists x, y representing coordinates in 2D. For example x = [1,4,0.5,2,5,10,33,0.04] and y = [2,5,44,0.33,2,14,20,0.03]. x[i] and y[i] represent one point in 2D. Now I also have a list representing "heat" values for each (x,y) point, for example z = [0.77, 0.88, 0.65, 0.55, 0.89, 0.9, 0.8,0.95]. Of course x,y and z are much higher dimensional than the example.
Now I would like to plot a heat map in 2D where x and y represents the axis coordinates and z represents the color. How can this be done in python?
This code produces a heat map. With a few more data points, the plot starts looking pretty nice and I've found it to be very quick in general even for >100k points.
import matplotlib.pyplot as plt
import matplotlib.tri as tri
import numpy as np
import math
x = [1,4,0.5,2,5,10,33,0.04]
y = [2,5,44,0.33,2,14,20,0.03]
z = [0.77, 0.88, 0.65, 0.55, 0.89, 0.9, 0.8, 0.95]
levels = [0.7, 0.75, 0.8, 0.85, 0.9]
plt.figure()
ax = plt.gca()
ax.set_aspect('equal')
CS = ax.tricontourf(x, y, z, levels, cmap=plt.get_cmap('jet'))
cbar = plt.colorbar(CS, ticks=np.sort(np.array(levels)),ax=ax, orientation='horizontal', shrink=.75, pad=.09, aspect=40,fraction=0.05)
cbar.ax.set_xticklabels(list(map(str,np.sort(np.array(levels))))) # horizontal colorbar
cbar.ax.tick_params(labelsize=8)
plt.title('Heat Map')
plt.xlabel('X Label')
plt.ylabel('Y Label')
plt.show()
Produces this image:
or if you're looking for a more gradual color change, change the tricontourf line to this:
CS = ax.tricontourf(x, y, z, np.linspace(min(levels),max(levels),256), cmap=cmap)
and then the plot will change to:
Based on this answer, you might want to do something like:
import numpy as np
from matplotlib.mlab import griddata
import matplotlib.pyplot as plt
xs0 = [1,4,0.5,2,5,10,33,0.04]
ys0 = [2,5,44,0.33,2,14,20,0.03]
zs0 = [0.77, 0.88, 0.65, 0.55, 0.89, 0.9, 0.8,0.95]
N = 30j
extent = (np.min(xs0),np.max(xs0),np.min(ys0),np.max(ys0))
xs,ys = np.mgrid[extent[0]:extent[1]:N, extent[2]:extent[3]:N]
resampled = griddata(xs0, ys0, zs0, xs, ys, interp='linear')
plt.imshow(np.fliplr(resampled).T, extent=extent,interpolation='none')
plt.colorbar()
The example here might also help: http://matplotlib.org/examples/pylab_examples/griddata_demo.html

Render non-uniform grid and mark maximum value's location in contour plot

I have two problems with the contour plot in matplotlib:
How can I render an arbitrary meshgrid as a regular one?
I would like the position of the ticks on both axes to be evenly distributed while still reflecting the position of my nodes.
How can I highlight the position of my data's highest value with a colored marker?
Here is my code:
import numpy as np
import pylab as pl
def plot_s(data, xlist, ylist):
pl.subplot(111)
x = np.array(xlist)
y = np.array(ylist)
X, Y = np.meshgrid(x, y)
CS = pl.contour(X, Y, data, colors='k')
pl.clabel(CS, inline = 1, fontsize=10)
pl.xlabel('x list')
pl.ylabel('y list')
pl.xticks(xlist)
pl.yticks(ylist)
pl.title('Contour plot')
pl.show()
def main():
data = np.array([[ 0.56555019, 0.57933922, 0.58266252, 0.58067285, 0.57660236, 0.57185625, 0.56711252, 0.55557035, 0.55027705, 0.54480605],
[ 0.55486559, 0.57349717, 0.57940478, 0.57843897, 0.57463271, 0.56963449, 0.5643922 , 0.55095598, 0.54452534, 0.53762606],
[ 0.53529358, 0.56254991, 0.57328105, 0.57409218, 0.57066168, 0.5654082 , 0.55956853, 0.5432474 , 0.53501127, 0.52601203],
[ 0.50110483, 0.54004071, 0.55800178, 0.56173719, 0.55894846, 0.55328279, 0.54642887, 0.52598388, 0.51533094, 0.50354147]])
xlist = [10., 20., 30., 40., 50., 60., 70., 100., 120., 150.]
ylist = [50, 70, 90, 100]
plot_s(data, xlist, ylist)
if __name__ == '__main__':
main()
How can I render an arbitrary meshgrid as a regular one?
One suggestion is to create a regular meshgrid, by first creating arrays of evenly spaced values between your minimum and maximum x and y. Further you could use custom ticks to reflect the fact your data-points are not equidistant. See comments in the codes about how I implemented that.
How can I highlight the position of my data's highest value with a colored marker?
To retrieve the highest value, you could use np.max() and then find the position of this value in the data-array with np.where. Simply plot a marker on this location.
Alternatively, using plt.contour you could create a contour with a level sufficiently close to your maximum value's position, to create a ring around it, or even a point on it:
epsillon = 0.0001
levels = np.arange(max_value - epsillon, max_value + epsillon)
CS2 = plt.contour(X,Y,data, levels,
origin='lower',
linewidths=2,
extent=(-3,3,-2,2))
Note that with the first method, the dot will end up on the top of an existing grid node, while plt.contour interpolates your data, and depending on the interpolation algorithm used, it may result in a somewhat different location. Yet here it appear to concur.
The code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
def plot_s(data, x, y, xlist, ylist):
ax = plt.gca()
########### create your uniform meshgrid..... ############
X, Y = np.meshgrid(x, y)
CS = ax.contour(X, Y, data, colors='k')
###### ... and let ticks indicate that your new space is not linear
# assign tick positions according to the regular array
ax.set_yticks(y)
# Assign the label to reflect your original nodes position
ax.set_yticklabels(ylist)
# and same for x
ax.set_xticks(x)
ax.set_xticklabels(xlist)
#############################################################
########### GET MAXIMUM AND MARK IT WITH A POINT ########
# get maximum value in your data
max_value = np.max(data)
# get position index of this calue in your data array
local_max_index = np.where(data==max_value)
## retrieve position of your
max_x = X[local_max_index[0], local_max_index[1]]
max_y = Y[local_max_index[0], local_max_index[1]]
# plot one marker on this position
plt.plot(max_x, max_y, color="red", marker = "o", zorder = 10,
markersize=15, clip_on=False)
##############################################################
plt.title('Contour plot')
plt.show()
def main():
# Your data: 4 x 10 array
data = np.array([[ 0.56555019, 0.57933922, 0.58266252, 0.58067285, 0.57660236,
0.57185625, 0.56711252, 0.55557035, 0.55027705, 0.54480605],
[ 0.55486559, 0.57349717, 0.57940478, 0.57843897, 0.57463271,
0.56963449, 0.5643922 , 0.55095598, 0.54452534, 0.53762606],
[ 0.53529358, 0.56254991, 0.57328105, 0.57409218, 0.57066168,
0.5654082 , 0.55956853, 0.5432474 , 0.53501127, 0.52601203],
[ 0.50110483, 0.54004071, 0.55800178, 0.56173719, 0.55894846,
0.55328279, 0.54642887, 0.52598388, 0.51533094, 0.50354147]])
# create a list values with regular interval for the mesh grid
x = np.array([10 + i * (150.-10.)/9 for i in range(10)])
y = np.array([50 + i * (100.-50.)/4 for i in range(4)])
# create arrays with values to be displayed as ticks
xlist = np.array([10., 20., 30., 40., 50., 60., 70., 100., 120., 150.])
ylist = np.array([50, 70, 90, 100])
plot_s(data, x, y, xlist, ylist)
if __name__ == '__main__':
main()
voilĂ :
Here with the meshgrid in the background to show the deformation/mapping:
Below is essentially the same but a slightly more compact version of what has been proposed by snake_charmer. However, I am not sure if I understood your question correctly. If your points in xlist and ylist are not too irregularly spaced, a more elegant solution could be to keep the irregular grid but to highlight the location of data points using ax.grid(). This depends on what exactly you want to show in the figure, though.
import numpy as np
from matplotlib import pyplot as plt
def plot_s(data, xlist, ylist):
fig, ax = plt.subplots()
x = np.arange(len(xlist))
y = np.arange(len(ylist))
X, Y = np.meshgrid(x, y)
CS = ax.contour(X, Y, data, colors='k')
ax.clabel(CS, inline = 1, fontsize=10)
ax.set_xlabel('x list')
ax.set_ylabel('y list')
ax.set_xticks(x)
ax.set_yticks(y)
ax.set_xticklabels(xlist)
ax.set_yticklabels(ylist)
jmax, imax = np.unravel_index(np.argmax(data), data.shape)
ax.plot(imax, jmax, 'ro')
ax.set_title('Contour plot')
plt.show()
def main():
data = np.array([[ 0.56555019, 0.57933922, 0.58266252, 0.58067285,
0.57660236, 0.57185625, 0.56711252, 0.55557035,
0.55027705, 0.54480605],
[ 0.55486559, 0.57349717, 0.57940478, 0.57843897,
0.57463271, 0.56963449, 0.5643922 , 0.55095598,
0.54452534, 0.53762606],
[ 0.53529358, 0.56254991, 0.57328105, 0.57409218,
0.57066168, 0.5654082 , 0.55956853, 0.5432474 ,
0.53501127, 0.52601203],
[ 0.50110483, 0.54004071, 0.55800178, 0.56173719,
0.55894846, 0.55328279, 0.54642887, 0.52598388,
0.51533094, 0.50354147]])
xlist = [10., 20., 30., 40., 50., 60., 70., 100., 120., 150.]
ylist = [50, 70, 90, 100]
plot_s(data, xlist, ylist)
if __name__ == '__main__':
main()

Categories