Whenever I try to run this code the plot lines don't show up. Does anyone have any ideas as to why?This is a picture of my code too for reference
import numpy as np
import matplotlib.pyplot as plt
p1 = 1*(10**-3)
p2 = list(range(1,101))
newlist = []
for i in range(1,100):
db = 10*np.log10(p2[i]/p1)
plt.figure(1)
plt.plot (db, p2[i], 'c')
plt.title('Log-Linear Plot')
plt.ylabel('Power')
plt.xlabel('db')
plt.grid
plt.show()
plt.grid(True)
for i in range(1,100):
plt.figure(2)
plt.semilogy(db, p2[i], 'r')
plt.title('Linear Plot')
plt.ylabel('Power in Watts')
plt.xlabel('Power in Decibels')
plt.show()
plt.grid(True)
I think this is probably what you're after.
You were using the loop to update the array, but also had plot and show in the loop, which would create many separate plots. There's no need to call plot point by point, so that came out of the loop.
Also, though, using Numpy's vectorization, you don't even need to do the calculation in the loop, so I took that out as well, which left no loop at all.
p1 = 1*(10**-3)
p2 = np.arange(1,101)
newlist = []
db = 10*np.log10(p2/p1)
plt.plot (db, p2, 'c')
plt.title('Log-Linear Plot')
plt.ylabel('Power')
plt.xlabel('db')
plt.grid(True)
plt.show()
plt.figure(2)
plt.semilogy(db, p2, 'r')
plt.title('Linear Plot')
plt.ylabel('Power in Watts')
plt.xlabel('Power in Decibels')
plt.grid(True)
plt.show()
Related
I want to see how a plot varies with different values using a loop. I want to see it on the same plot. But i do not want to remains of the previous plot in the figure. In MATLAB this is possible by creating a figure and just plotting over the same figure. Closing it when the loop ends.
Like,
fh = figure();
%for loop here
%do something with x and y
subplot(211), plot(x);
subplot(212), plot(y);
pause(1)
%loop done
close(fh);
I am not able to find the equivalent of this in matplotlib. Usually all the questions are related to plotting different series on the same plot, which seems to come naturally on matplotlib, by plotting several series using plt.plot() and then showing them all finally using plt.show(). But I want to refresh the plot.
There are essentially two different ways to create animations in matplotlib
interactive mode
Turning on interactive more is done using plt.ion(). This will create a plot even though show has not yet been called. The plot can be updated by calling plt.draw() or for an animation, plt.pause().
import matplotlib.pyplot as plt
x = [1,1]
y = [1,2]
fig, (ax1,ax2) = plt.subplots(nrows=2, sharex=True, sharey=True)
line1, = ax1.plot(x)
line2, = ax2.plot(y)
ax1.set_xlim(-1,17)
ax1.set_ylim(-400,3000)
plt.ion()
for i in range(15):
x.append(x[-1]+x[-2])
line1.set_data(range(len(x)), x)
y.append(y[-1]+y[-2])
line2.set_data(range(len(y)), y)
plt.pause(0.1)
plt.ioff()
plt.show()
FuncAnimation
Matplotlib provides an animation submodule, which simplifies creating animations and also allows to easily save them. The same as above, using FuncAnimation would look like:
import matplotlib.pyplot as plt
import matplotlib.animation
x = [1,1]
y = [1,2]
fig, (ax1,ax2) = plt.subplots(nrows=2, sharex=True, sharey=True)
line1, = ax1.plot(x)
line2, = ax2.plot(y)
ax1.set_xlim(-1,18)
ax1.set_ylim(-400,3000)
def update(i):
x.append(x[-1]+x[-2])
line1.set_data(range(len(x)), x)
y.append(y[-1]+y[-2])
line2.set_data(range(len(y)), y)
ani = matplotlib.animation.FuncAnimation(fig, update, frames=14, repeat=False)
plt.show()
An example to animate a sine wave with changing frequency and its power spectrum would be the following:
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
x = np.linspace(0,24*np.pi,512)
y = np.sin(x)
def fft(x):
fft = np.abs(np.fft.rfft(x))
return fft**2/(fft**2).max()
fig, (ax1,ax2) = plt.subplots(nrows=2)
line1, = ax1.plot(x,y)
line2, = ax2.plot(fft(y))
ax2.set_xlim(0,50)
ax2.set_ylim(0,1)
def update(i):
y = np.sin((i+1)/30.*x)
line1.set_data(x,y)
y2 = fft(y)
line2.set_data(range(len(y2)), y2)
ani = matplotlib.animation.FuncAnimation(fig, update, frames=60, repeat=True)
plt.show()
If you call plt.show() inside the loop you will see the plot for each element on the loop as long as you close the window containing the figure. The process, will be plot for the first element, then if you close the window you will see the plot for the second element in the loop, etc
I want to select 4 points on a matplotlib graph and operate on those points as soon as 4 four points are clicked on.
The code below will indeed store the 4 points in the variable points but does not wait for the four points to be selected. I tried adding a for loop and tried threading here but neither option worked. How can I solve this problem?
fig = plt.figure()
ax = fig.add_subplot(111)
image = np.load('path-to-file.npy')
tfig = ax.imshow(image)
points = []
def onclick(event):
global points
points.append((event.xdata, event.ydata))
cid = fig.canvas.mpl_connect('button_press_event', onclick)
# this line will cause an error because the four points haven't been
# selected yet
firstPoint = points[0]
In this case, you might consider using plt.ginput instead of "rolling your own".
As a quick example:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set(title='Select 4 points')
xy = plt.ginput(4)
x, y = zip(*xy)
ax.fill(x, y, color='lightblue')
ax.plot(x, y, ls='', mfc='red', marker='o')
plt.show()
Here's some code that does scatter plot of a number of different series using matplotlib and then adds the line y=x:
import numpy as np, matplotlib.pyplot as plt, matplotlib.cm as cm, pylab
nseries = 10
colors = cm.rainbow(np.linspace(0, 1, nseries))
all_x = []
all_y = []
for i in range(nseries):
x = np.random.random(12)+i/10.0
y = np.random.random(12)+i/5.0
plt.scatter(x, y, color=colors[i])
all_x.extend(x)
all_y.extend(y)
# Could I somehow do the next part (add identity_line) if I haven't been keeping track of all the x and y values I've seen?
identity_line = np.linspace(max(min(all_x), min(all_y)),
min(max(all_x), max(all_y)))
plt.plot(identity_line, identity_line, color="black", linestyle="dashed", linewidth=3.0)
plt.show()
In order to achieve this I've had to keep track of all the x and y values that went into the scatter plot so that I know where identity_line should start and end. Is there a way I can get y=x to show up even if I don't have a list of all the points that I plotted? I would think that something in matplotlib can give me a list of all the points after the fact, but I haven't been able to figure out how to get that list.
You don't need to know anything about your data per se. You can get away with what your matplotlib Axes object will tell you about the data.
See below:
import numpy as np
import matplotlib.pyplot as plt
# random data
N = 37
x = np.random.normal(loc=3.5, scale=1.25, size=N)
y = np.random.normal(loc=3.4, scale=1.5, size=N)
c = x**2 + y**2
# now sort it just to make it look like it's related
x.sort()
y.sort()
fig, ax = plt.subplots()
ax.scatter(x, y, s=25, c=c, cmap=plt.cm.coolwarm, zorder=10)
Here's the good part:
lims = [
np.min([ax.get_xlim(), ax.get_ylim()]), # min of both axes
np.max([ax.get_xlim(), ax.get_ylim()]), # max of both axes
]
# now plot both limits against eachother
ax.plot(lims, lims, 'k-', alpha=0.75, zorder=0)
ax.set_aspect('equal')
ax.set_xlim(lims)
ax.set_ylim(lims)
fig.savefig('/Users/paul/Desktop/so.png', dpi=300)
Et voilĂ
In one line:
ax.plot([0,1],[0,1], transform=ax.transAxes)
No need to modify the xlim or ylim.
Starting with matplotlib 3.3 this has been made very simple with the axline method which only needs a point and a slope. To plot x=y:
ax.axline((0, 0), slope=1)
You don't need to look at your data to use this because the point you specify (i.e. here (0,0)) doesn't actually need to be in your data or plotting range.
If you set scalex and scaley to False, it saves a bit of bookkeeping. This is what I have been using lately to overlay y=x:
xpoints = ypoints = plt.xlim()
plt.plot(xpoints, ypoints, linestyle='--', color='k', lw=3, scalex=False, scaley=False)
or if you've got an axis:
xpoints = ypoints = ax.get_xlim()
ax.plot(xpoints, ypoints, linestyle='--', color='k', lw=3, scalex=False, scaley=False)
Of course, this won't give you a square aspect ratio. If you care about that, go with Paul H's solution.
I have some code:
import matplotlib.pyplot as plt
def print_fractures(fractures):
xpairs = []
ypairs = []
plt.figure(2)
plt.subplot(212)
for i in range(len(fractures)):
xends = [fractures[i][1][0], fractures[i][2][0]]
yends = [fractures[i][1][1], fractures[i][2][1]]
xpairs.append(xends)
ypairs.append(yends)
for xends,yends in zip(xpairs,ypairs):
plt.plot(xends, yends, 'b-', alpha=0.4)
plt.show()
def histogram(spacings):
plt.figure(1)
plt.subplot(211)
plt.hist(spacings, 100)
plt.xlabel('Spacing (m)', fontsize=15)
plt.ylabel('Frequency (count)', fontsize=15)
plt.show()
histogram(spacings)
print_fractures(fractures)
This code will produce the following output:
My questions are:
1) Why are two separate figures being created? I thought the subplot command would combine them into one figure. I thought it might be the multiple plt.show() commands, but I tried commenting those out and only calling it once from outside my functions and I still got 2 windows.
2) How can I combine them into 1 figure properly? Also, I would want figure 2 axes to have the same scale (i.e. so 400 m on the x axis is the same length as 400 m on the y-axis). Similarly, I'd like to stretch the histogram vertically as well - how is this accomplished?
As you observed already, you cannot call figure() inside each function if you intend to use only one figure (one Window). Instead, just call subplot() without calling show() inside the function. The show() will force pyplot to create a second figure IF you are in plt.ioff() mode. In plt.ion() mode you can keep the plt.show() calls inside the local context (inside the function).
To achieve the same scale for the x and y axes, use plt.axis('equal'). Below you can see an illustration of this prototype:
from numpy.random import random
import matplotlib.pyplot as plt
def print_fractures():
plt.subplot(212)
plt.plot([1,2,3,4])
def histogram():
plt.subplot(211)
plt.hist(random(1000), 100)
plt.xlabel('Spacing (m)', fontsize=15)
plt.ylabel('Frequency (count)', fontsize=15)
histogram()
print_fractures()
plt.axis('equal')
plt.show()
I have a scatter plot that is composed of different calls for scatter:
import matplotlib.pyplot as plt
import numpy as np
def onpick3(event):
index = event.ind
print '--------------'
print index
artist = event.artist
print artist
fig_handle = plt.figure()
x,y = np.random.rand(10),np.random.rand(10)
x1,y1 = np.random.rand(10),np.random.rand(10)
axes_size = 0.1,0.1,0.9,0.9
ax = fig_handle.add_axes(axes_size)
p = ax.scatter (x,y, marker='*', s=60, color='r', picker=True, lw=2)
p1 = ax.scatter (x1,y1, marker='*', s=60, color='b', picker=True, lw=2)
fig_handle.canvas.mpl_connect('pick_event', onpick3)
plt.show()
I'd like the points to be clickable, and get the x,y of the selected indexes.
However since scatter is being called more than once, I get the same indexes twice, so I cant use x[index] inside the onpick3 method
Is there a straightforward way to get the points?
It seems that event.artist gives back the same PathCollection that is given back from scatter (p and p1 in this case).
But I couldn't find any way to use it to extract the x,y of the selected indexes
Tried using event.artist.get_paths() - but it doesn't seem to be giving back all the scatter points, but only the one that I clicked on..so I'm really not sure what event.artist is giving back and what are the event.artist.get_paths() function is giving back
EDIT
it seems that event.artist._offsets gives an array with the relevant offsets, but for some reason when trying to use event.artist.offsetsI get
AttributeError: 'PathCollection' object has no attribute 'offsets'
(although if I understand the docs, it should be there)
To get the x, y coordinates for the collection that scatter returns, use event.artist.get_offsets() (Matplotlib has explicit getters and setters for mostly historical reasons. All get_offsets does is return self._offsets, but the public interface is through the "getter".).
So, to complete your example:
import matplotlib.pyplot as plt
import numpy as np
def onpick3(event):
index = event.ind
xy = event.artist.get_offsets()
print '--------------'
print xy[index]
fig, ax = plt.subplots()
x, y = np.random.random((2, 10))
x1, y1 = np.random.random((2, 10))
p = ax.scatter(x, y, marker='*', s=60, color='r', picker=True)
p1 = ax.scatter(x1, y1, marker='*', s=60, color='b', picker=True)
fig.canvas.mpl_connect('pick_event', onpick3)
plt.show()
However, if you're not varying things by a 3rd or 4th variable, you may not want to use scatter to plot points. Use plot instead. scatter returns a collection that's much more difficult to work with than the Line2D that plot returns. (If you do go the route of using plot, you'd use x, y = artist.get_data().)
Finally, not to plug my own project too much, but if you might find mpldatacursor useful. It abstracts away a lot of you're doing here.
If you decide to go that route, your code would look similar to: