I have an algorithm that can be controlled by two parameters so now I want to plot the runtime of the algorithm depending on these parameters.
My Code:
from matplotlib import pyplot
import pylab
from mpl_toolkits.mplot3d import Axes3D
fig = pylab.figure()
ax = Axes3D(fig)
sequence_containing_x_vals = [5,5,5,5,10,10,10,10,15,15,15,15,20,20,20,20]
sequence_containing_y_vals = [1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4]
sequence_containing_z_vals = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
ax.scatter(sequence_containing_x_vals, sequence_containing_y_vals, sequence_containing_z_vals)
pyplot.show()
This will plot all the points in the space but I want them connected and have something like this:
(The coloring would be nice but not necessary)
To plot the surface you need to use plot_surface, and have the data as a regular 2D array (that reflects the 2D geometry of the x-y plane). Usually meshgrid is used for this, but since your data already has the x and y values repeated appropriately, you just need to reshape them. I did this with numpy reshape.
from matplotlib import pyplot, cm
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
fig = pyplot.figure()
ax = Axes3D(fig)
sequence_containing_x_vals = np.array([5,5,5,5,10,10,10,10,15,15,15,15,20,20,20,20])
X = sequence_containing_x_vals.reshape((4,4))
sequence_containing_y_vals = np.array([1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4])
Y = sequence_containing_y_vals.reshape((4,4))
sequence_containing_z_vals = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
Z = sequence_containing_z_vals.reshape((4,4))
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.hot)
pyplot.show()
Note that X, Y = np.meshgrid([1,2,3,4], [5, 10, 15, 20]) will give the same X and Y as above but more easily.
Of course, the surface shown here is just a plane since your data is consistent with z = x + y - -5, but this method will work with generic surfaces, as can be seen in the many matplotlib surface examples.
Related
I have a basic graph example and I am trying to make all the points be on some sort of curved line. I have an idea on how to about this but am not sure how to implement it or if it is even possible. Below I have a picture of the graph that I have made with the following code:
import matplotlib.pyplot as plt
import numpy as np
# original data
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
y = [2, 7, 3, 4, 5, 1, 6, 9, 4, 6]
# quadratic regression
for i in range(int((len(x) + len(y)) / 2)):
sub_x = x[i:i+3]
sub_y = y[i:i+3]
model = np.poly1d(np.polyfit(sub_x, sub_y, 2))
polyline = np.linspace(min(sub_x), max(sub_x), 200)
plt.plot(polyline, model(polyline), color="#6D34D6", linestyle='dashed')
# plot lines
plt.scatter(x, y, color='#FF3FAF')
plt.plot(x, y, color='#FF3FAF', linestyle='solid')
plt.show()
Here is the picture graph that is produced:
The question that I have is how do I make all the dotted lines connect seamlessly? I had an idea about averaging each two line segments that contain the same points but I don't know how to go around doing so. Another idea that I had was making some sort of bezier curve that connects all the points but that sounds unnecessarily complicated.
Something like the green line should be the output (sorry for the poorly drawn line):
You can use scipy.interpolate.interp1d to apply a quadratic interpolation to expand the number of points to, say, 300 length, and then plot a smooth curve.
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.interpolate import interp1d
# original data
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
y = [2, 7, 3, 4, 5, 1, 6, 9, 4, 6]
# quadratic regression
for i in range(int((len(x) + len(y)) / 2)):
sub_x = x[i:i+3]
sub_y = y[i:i+3]
model = np.poly1d(np.polyfit(sub_x, sub_y, 2))
polyline = np.linspace(min(sub_x), max(sub_x), 200)
plt.plot(polyline, model(polyline), color="#6D34D6", linestyle='dashed')
#Interpolate
x_new = np.linspace(min(x), max(x), 300) #<----
f = interp1d(x, y, kind='quadratic') #<----
# plot lines
plt.scatter(x, y, color='#FF3FAF')
plt.plot(x_new, f(x_new), color='#FF3FAF', linestyle='solid') #<----
plt.show()
When I plot data using matplotlib I always have 5-9 ticks on my x-axis independent of the range I plot, and if I zoom on the x-axis the tick spacing decreases, so I still see 5-9 ticks.
however, I would like 20-30 ticks on my x-axis!
I can achieve this with the following:
from matplotlib import pyplot as plt
import numpy as np
x = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
y = [1, 4, 3, 2, 7, 6, 9, 8, 10, 5]
number_of_ticks_on_x_axis = 20
plt.plot(x, y)
plt.xticks(np.arange(min(x), max(x)+1, (max(x) - min(x))/number_of_ticks_on_x_axis))
plt.show()
If I now zoom on the x-axis, no new ticks appear between the existing ones. I would like to still have ~20 ticks however much I zoom.
Assuming that you want to fix the no. of ticks on the X axis
...
from matplotlib.ticker import MaxNLocator
...
fig, ax = plt.subplots()
ax.xaxis.set_major_locator(MaxNLocator(15, min_n_ticks=15))
...
Please look at the docs for MaxNLocator
Example
In [36]: import numpy as np
...: import matplotlib.pyplot as plt
In [37]: from matplotlib.ticker import MaxNLocator
In [38]: fig, ax = plt.subplots(figsize=(10,4))
In [39]: ax.grid()
In [40]: ax.xaxis.set_major_locator(MaxNLocator(min_n_ticks=15))
In [41]: x = np.linspace(0, 1, 51)
In [42]: y = x*(1-x)
In [43]: plt.plot(x, y)
Out[43]: [<matplotlib.lines.Line2D at 0x7f9eab409e10>]
gives
and when I zoom into the maximum of the curve I get
You can link a callback function to an event in the canvas. In you case you can trigger a function that updates the axis when a redraw occurs.
from matplotlib import pyplot as plt
import numpy as np
x = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
y = [1, 4, 3, 2, 7, 6, 9, 8, 10, 5]
n = 20
plt.plot(x, y)
plt.xticks(np.arange(min(x), max(x)+1, (max(x) - min(x))/n), rotation=90)
def on_zoom(event):
ax = plt.gca()
fig = plt.gcf()
x_min, x_max = ax.get_xlim()
ax.set_xticks(np.linspace(x_min, x_max, n))
# had to add flush_events to get the ticks to redraw on the last update.
fig.canvas.flush_events()
fig = plt.gcf()
fig.canvas.mpl_disconnect(cid)
cid = fig.canvas.mpl_connect('draw_event', on_zoom)
This question has probably a totally simple solution but I just can't find it. I'd like to plot a contourf plot where the one part of my data varies in steps of order 1 and the other part varies with steps of order 100.
Now I tried to just give contour levels like this:
contour_levels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 200, 300, 400]
However this leads to the result that the fist 11 levels all have the same color as matplotlib is somehow normalizing this to the maximum value. How can I make every level equally important in terms of my color map?
Thanks a lot HYRY, your answer solved my problem. This is what the plots look like bevore and after the implementation (I adjusted the levels a bit; data from the GOZCARDS team/NASA):
Use colors argument:
import pylab as pl
import numpy as np
x, y = np.mgrid[-1:1:100j, 0:1:100j]
z = ... # your function
contour_levels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 200, 300, 400]
cmap = pl.cm.BuPu
colors = cmap(np.linspace(0, 1, len(contour_levels)))
pl.contour(x, y, z, levels=contour_levels, colors=colors)
I am a little wary of HYRY's solution as the mapping between the colors level can become arbitrary. I would suggest using LogNorm instead which maps your values -> colors with a log.
import pylab as pl
import numpy as np
x, y = np.mgrid[-1:1:100j, 0:1:100j]
z = ... # your function
contour_levels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 200, 300, 400]
cmap = pl.cm.BuPu
pl.contourf(x, y, z, levels=contour_levels, norm=matplotlib.colors.LogNorm)
If you also use vmin and vmax you can explicitly control the limits of the normalization and ensure that the color scales match between graphs independent of what levels you use.
So, I thought this was going to be really simple, but I've been having a lot of difficult finding exactly what I'm looking for in a comprehensible example.
Basically I want to make phase plots, so assuming I have a 2d array, how can I get matplotlib to convert this to a plot that I can attach titles, axes, and legends (color bars) to.
I'm looking for an extremely simple bare bones solution that only uses what is required that will work with any 2D array.
I'm certain this is simple and I'm just being thick somehow, but I'm really having a lot of trouble with this.
I have been tooling with the examples, but they don't seem well suited to what I'm trying to do: I like the general appearance of this graph, I'd just like to be able to pass in a 2dArray and have this same result:
import numpy as np
import matplotlib as ml
import matplotlib.pyplot as plt
H = [[1,2,3,4][5,6,7,8][9,10,11,12][13,14,15,16]]
fig = plt.figure(figsize=(6, 3.2))
ax = fig.add_subplot(111)
ax.set_title('colorMap')
X,Y = np.meshgrid(xedges, yedges)
plt.pcolormesh(X, Y, H)
ax.set_aspect('equal')
cax = fig.add_axes([0.12, 0.1, 0.78, 0.8])
cax.get_xaxis().set_visible(False)
cax.get_yaxis().set_visible(False)
cax.patch.set_alpha(0)
cax.set_frame_on(False)
plt.colorbar(orientation='vertical')
plt.show()
I'm afraid your posted example is not working, since X and Y aren't defined. So instead of pcolormesh let's use imshow:
import numpy as np
import matplotlib.pyplot as plt
H = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]]) # added some commas and array creation code
fig = plt.figure(figsize=(6, 3.2))
ax = fig.add_subplot(111)
ax.set_title('colorMap')
plt.imshow(H)
ax.set_aspect('equal')
cax = fig.add_axes([0.12, 0.1, 0.78, 0.8])
cax.get_xaxis().set_visible(False)
cax.get_yaxis().set_visible(False)
cax.patch.set_alpha(0)
cax.set_frame_on(False)
plt.colorbar(orientation='vertical')
plt.show()
Here is the simplest example that has the key lines of code:
import numpy as np
import matplotlib.pyplot as plt
H = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]])
plt.imshow(H, interpolation='none')
plt.show()
EDIT: (1/2023) Still working with Python 3.9.13
I want to make a 3D Graph with Matplotlib. The graph window appears, but no data is shown. What am I doing wrong?
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = [0, 10, 20, 40, 100]
y = [1, 4, 8, 60, 200]
z = [4, 5, 6, 7, 8]
ax.plot_surface(x, y, z)
plt.show()
plot_surface expects 2D inputs (doc). It is not plotting anything because you did not give it a valid surface to draw.
See this example.
X, Y and Z needs to be 2D-arrays :
Surface plots Axes3D.plot_surface(X, Y, Z, *args, **kwargs) Create a
surface plot.
Argument Description
X, Y, Z Data values as 2D arrays
However I do not understand the logic behind it : check this SO post for more info.