Plotting a dot that moves along side a dispersive wave? - python-3.x

How would I go on about plotting a dot that moves along a wave pack/superposition. I saw this on the website and wanted to try for myself.https://blog.soton.ac.uk/soundwaves/further-concepts/2-dispersive-waves/. So I know how to animate a superpositon of two sine waves. But how would I plot a dot that moves along it? I won't post my entire code, but it looks somewhat like this
import matplotlib.pyplot as plt
import numpy as np
N = 1000
x = np.linspace(0,100,N)
wave1 = np.sin(2*x)
wave2 = np.sin(3*x)
sWave = wave1+wave2
plt.plot(x,sWave)
plt.ion()
for t in np.arange(0,400):
sWave.set_ydata(sWave)
plt.draw()
plt.pause(.1)
plt.ioff()
plt.show()
Note that this is just a quick draft of my original code.

You can add a scatter and update its data in a loop by using .set_offsets().
import matplotlib.pyplot as plt
import numpy as np
N = 1000
x = np.linspace(0, 100, N)
wave1 = np.sin(2*x)
wave2 = np.sin(3*x)
sWave = wave1 + wave2
fig, ax = plt.subplots()
ax.plot(x, sWave)
scatter = ax.scatter([], [], facecolor="red") # Initialize an empty scatter.
for t in range(N):
scatter.set_offsets((x[t], sWave[t])) # Modify that scatter's data.
fig.canvas.draw()
plt.pause(.001)

Related

Updating a plot in Python

My situation is this: I am developing a Jupyter-lab notebook to exemplify engineering topics. I find myself in the need of plotting something in an axes object within a figure, and then using a slider interact changing a value to update the plot.
Here is a MWE (or at least a shorter Working Example):
import ipywidgets as widgets
from ipywidgets import interact
import numpy as np
import matplotlib.pyplot as plt
global ax1
global fig
fig, (ax1) = plt.subplots(ncols=1, subplot_kw=dict(projection='polar'))
RAD = np.array([0.85, 0.85, 0.85])
ANG = np.array([np.pi/2, np.pi*(2/3+1/2), np.pi*(1/2-2/3)])
c = ax1.scatter(ANG, RAD)
ax1.set_ylim([0, 1])
ax1.set_yticklabels([])
def h(rh):
RADp = np.array([rh, rh, rh])
ANGp = np.array([-np.pi/2, np.pi*(2/3-1/2), np.pi*(-1/2-2/3)])
cp = ax1.scatter(ANGp, RADp)
ax1.add_artist(cp)
plt.show()
return (rh)
interact(h, rh = widgets.FloatSlider(min=0, max=1, step=0.001, value=1));
In this example I create the figure fig and its axes ax1 declared as global variables (so that they will be available within function h. Then using RAD and ANG I create a scatter plot c.
Afterwards using the interact widget I would like to have three crosses change position along the r axis by changing the value of rh with the slider.
I don't get any error, but neither get I any crosses at all.
In the actual code I use pcolormesh instead of scatter.
I hope I made myself clear. I had got ti working by creating the figure and ax1 each time the function is called, but then I added some more suff thath don't need to be plotted each time.
Thanks for taking the time to read!
A very limited answer is that you function should return fig not rh.
Also note that you don't need the lines with global, and plt.show()
import ipywidgets as widgets
from ipywidgets import interact
import numpy as np
import matplotlib.pyplot as plt
fig, (ax1) = plt.subplots(ncols=1, subplot_kw=dict(projection='polar'))
RAD = np.array([0.85, 0.85, 0.85])
ANG = np.array([np.pi/2, np.pi*(2/3+1/2), np.pi*(1/2-2/3)])
c = ax1.scatter(ANG, RAD)
ax1.set_ylim([0, 1])
ax1.set_yticklabels([])
def h(rh):
RADp = np.array([rh, rh, rh])
ANGp = np.array([-np.pi/2, np.pi*(2/3-1/2), np.pi*(-1/2-2/3)])
cp = ax1.scatter(ANGp, RADp)
ax1.add_artist(cp)
# plt.show()
return fig
interact(h, rh = widgets.FloatSlider(min=0, max=1, step=0.001, value=1));
I say limited because I think you want to update rather than add point?
A version which is hopefully more in line with what you want
the key point being the use of set_offsets method to update the positions.
import ipywidgets as widgets
from ipywidgets import interact
import numpy as np
import matplotlib.pyplot as plt
fig, (ax1) = plt.subplots(ncols=1, subplot_kw=dict(projection='polar'))
RAD = np.array([0.85, 0.85, 0.85])
ANG = np.array([np.pi/2, np.pi*(2/3+1/2), np.pi*(1/2-2/3)])
c = ax1.scatter(ANG, RAD)
ax1.set_ylim([0, 1])
ax1.set_yticklabels([])
def h(rh):
new = [
[-np.pi/2, rh],
[np.pi*(2/3-1/2), rh],
[np.pi*(-1/2-2/3), rh],
]
c.set_offsets(new)
return fig
interact(h, rh = widgets.FloatSlider(min=0, max=1, step=0.001, value=1));

Matplotlib get_ylim() changing data transformation result

"get_ylim()" is changing the result of the transformation from data to display coordinates in matplotlib (I'm using version 3.2.1). Is it supposed to change axis properties? It's the same effect using "get_xlim()".
Here is my code:
import matplotlib.pyplot as plt
import numpy as np
dpi = 80
plt.rcParams.update({'font.size': 12})
fig, ax = plt.subplots(figsize=(1280/dpi, 720/dpi), dpi=dpi)
x = np.arange(200)
y = - 0.1 * x
ax.plot(x, y)
points = ax.transData.transform(np.vstack((x, y)).T).astype(int)
print(points[:5])
ax.get_ylim()
points = ax.transData.transform(np.vstack((x, y)).T).astype(int)
print(points[:5])
Both prints output different results only with the ax.get_ylim() in place.

Matplotlib get all axes artist objects for ArtistAnimation?

I am trying to make an animation using ArtistAnimation like this:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
ims = []
for i in range(60):
x = np.linspace(0,i,1000)
y = np.sin(x)
im = ax.plot(x,y, color='black')
ims.append(im)
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True,
repeat_delay=1000)
plt.show()
This animates a sine wave growing across the figure. Currently I'm just adding the Lines2D object returned by ax.plot() to ims. However, I would like to potentially draw multiple overlapping plots on the Axes and adjust the title, legend and x-axis range for each frame. How do I get an object that I can add to ims after plotting and making all the changes I want for each frame?
The list you supply to ArtistAnimation should be a list of lists of artists, one list per frame.
artist_list = [[line1a, line1b, title1], [line2a, line2b, title2], ...]
where the first list is shown in the first frame, the second list in the second frame etc.
The reason your code works is that ax.plot returns a list of lines (in your case only a list of a single line).
In any case, the following might be a more understandable version of your code where an additional text is animated.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
artist_list = []
for i in range(60):
x = np.linspace(0,i,1000)
y = np.sin(x)
line, = ax.plot(x,y, color='black')
text = ax.text(i,0,i)
artist_list.append([line, text])
ani = animation.ArtistAnimation(fig, artist_list, interval=50, blit=True,
repeat_delay=1000)
plt.show()
In general, it will be hard to animate changing axes limits with ArtistAnimation, so if that is an ultimate goal consider using a FuncAnimation instead.

Axis label missing

I am trying to create a 3D plot but I am having trouble with the z-axis label. It simply doesn't appear in the graph. How do I amend this? The code is as follows
# Gamma vs Current step 2
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
h = np.arange(0.1,5.1,0.1)
gamma = np.arange(0.1,5.1,0.1)
sigmaz_hgam = np.array([.009998,.03988,.08878,.15403
,.230769,.312854,.394358,.4708311,.539697879,.6,.6518698
,.696033486,.73345752165,.7651390123,.792,.814845635
,.8343567,.851098499,.865535727,.8780487,.8889486,.89848986
,.906881,.914295,.9208731,.9267338,.93197569,.93668129
,.9409202379,.94475138,.951383,.9542629,.956895,.959309
,.961526,.9635675,.96545144,.9671934,.968807,.97030539
,.9716983,.972995,.974206,.975337,.97639567,.977387,.978318
,.97919266,.98,.9807902])
mu = 1
sigmaz_hgam = mu*sigmaz_hgam
# creates an empty list for current values to be stored in
J1 = []
for i in range(sigmaz_hgam.size):
expec_sz = sigmaz_hgam[i]
J = 4*gamma[i]*(mu-expec_sz)
J1.append(J.real)
#print(J)
This part of the code is what is used to graph out which is where the problem lies
mpl.rcParams['legend.fontsize'] = 10
fig = plt.figure()
ax = fig.gca(projection='3d')
x = h
y = gamma
z = J1
ax.plot(x, y, z, label='Dephasing Model')
ax.legend()
ax.set_xlabel('h', fontsize=10)
ax.set_ylabel('$\gamma$')
ax.yaxis._axinfo['label']['space_factor'] = 3.0
for t in ax.zaxis.get_major_ticks(): t.label.set_fontsize(10)
# disable auto rotation
ax.zaxis.set_rotate_label(False)
ax.set_zlabel('J', fontsize=10, rotation = 0)
plt.show()
On my version of Matplotlib (2.0.2), on a Mac, I see the label (which is there – most of it is just being cropped out of the image in your case).
You could try to reduce the padding between the ticks and the label:
ax.zaxis.labelpad = 0

How can I add a normal distribution curve to multiple histograms?

With the following code I create four histograms:
import numpy as np
import pandas as pd
data = pd.DataFrame(np.random.normal((1, 2, 3 , 4), size=(100, 4)))
data.hist(bins=10)
I want the histograms to look like this:
I know how to make it one graph at the time, see here
But how can I do it for multiple histograms without specifying each single one? Ideally I could use 'pd.scatter_matrix'.
Plot each histogram seperately and do the fit to each histogram as in the example you linked or take a look at the hist api example here. Essentially what should be done is
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
fig = plt.figure()
ax1 = fig.add_subplot(221)
ax2 = fig.add_subplot(222)
ax3 = fig.add_subplot(223)
ax4 = fig.add_subplot(224)
for ax in [ax1, ax2, ax3, ax4]:
n, bins, patches = ax.hist(**your_data_here**, 50, normed=1, facecolor='green', alpha=0.75)
bincenters = 0.5*(bins[1:]+bins[:-1])
y = mlab.normpdf( bincenters, mu, sigma)
l = ax.plot(bincenters, y, 'r--', linewidth=1)
plt.show()

Resources