0

I would like to plot four curves, to the left two temporal curves and to the rigth two FFTs based on the temporal curves. So for instance:

import matplotlib.pyplot as plt
import numpy as np
from scipy import signal


Fs=1024
t=np.arange(0,10,1/Fs)
F=np.arange(0,10,1/Fs)
x = np.sin(2 * 3.1416 * F *t )

plt.figure()
ax1 = plt.subplot(221)
plt.plot(t,x)
ax2 = plt.subplot(222)
f, Pxx_den = signal.periodogram(x, Fs)
line1, = plt.plot(f[:np.where(f>50)[0][0]],Pxx_den[:np.where(f>50)[0][0]] )

ax3 = plt.subplot(223, sharex=ax1)
plt.plot(t,x)
ax4 = plt.subplot(224)
f, Pxx_den = signal.periodogram(x, Fs)
line1, = plt.plot(f[:np.where(f>50)[0][0]],Pxx_den[:np.where(f>50)[0][0]] )


def on_xlims_change1(axes):
    lim = axes.get_xlim()
    f, Pxx_den = signal.periodogram(x[np.bitwise_and(t >lim[0] , t <lim[1])], Fs)
    ax2.clear()
    ax2.plot(f[:np.where(f>50)[0][0]],Pxx_den[:np.where(f>50)[0][0]] )

def on_xlims_change2(axes):
    lim = axes.get_xlim()
    f, Pxx_den = signal.periodogram(x[np.bitwise_and(t >lim[0] , t <lim[1])], Fs)
    ax4.clear()
    ax4.plot(f[:np.where(f>50)[0][0]],Pxx_den[:np.where(f>50)[0][0]] )

ax1.callbacks.connect('xlim_changed', on_xlims_change1)
ax3.callbacks.connect('xlim_changed', on_xlims_change2)


plt.show()

What I'm seeking for is a way to update the ax2 and ax4 when the x axis of the ax1 or ax3 is modified. each time the x axis of ax1 or ax3 is modified I would like to compute the FFT only on the range of the curve displayed. So I'm almost done to do this part. However because ax1 and ax3 have shared x axis. I was expecting that the two FFT plot should be updated But they don't.

enter image description here

So when I zoom in one temporal axis only the FFT directly to the rigth is updated and not all of them. I don't know where something is missing?

Community
  • 1
  • 1
ymmx
  • 4,769
  • 5
  • 32
  • 64
  • I edited the post because it is different from the duplicate post. – ymmx Feb 02 '18 at 15:52
  • I think you mean the post is different from the linked duplicate now because you edited it, not the other way around. ;-) In such cases best notify the person who closed the question (using the @ notation), otherwise it will not be reopened. – ImportanceOfBeingErnest Feb 02 '18 at 17:42

1 Answers1

2

The callback is only triggered for the axes of which the limits actually change externally. You may just use a single function to update both plots simultaneously.

def onlimschange(ax):
    on_xlims_change1(ax)
    on_xlims_change2(ax)

def on_xlims_change1(axes):
    lim = axes.get_xlim()
    f, Pxx_den = signal.periodogram(x[np.bitwise_and(t >lim[0] , t <lim[1])], Fs)
    ax2.clear()
    ax2.plot(f[:np.where(f>50)[0][0]],Pxx_den[:np.where(f>50)[0][0]] )

def on_xlims_change2(axes):
    lim = axes.get_xlim()
    f, Pxx_den = signal.periodogram(x[np.bitwise_and(t >lim[0] , t <lim[1])], Fs)
    ax4.clear()
    ax4.plot(f[:np.where(f>50)[0][0]],Pxx_den[:np.where(f>50)[0][0]] )

ax1.callbacks.connect('xlim_changed', onlimschange)
ax3.callbacks.connect('xlim_changed', onlimschange)

Amore general solution where arbitrary axes can be shared, could be to introduce a mapping from a source_axes to a target_axes and to loop over all shared axes to update the respective target axes according to the mapping.

import matplotlib.pyplot as plt
import numpy as np
from scipy import signal

Fs=1024.
t=np.arange(0,10,1/Fs)
F=np.arange(0,10,1/Fs)
x = np.sin(2 * 3.1416 * F *t )

plt.figure()
ax1 = plt.subplot(221)
plt.plot(t,x)
ax2 = plt.subplot(222)
f, Pxx_den = signal.periodogram(x, Fs)
line1, = plt.plot(f[:np.where(f>50)[0][0]],Pxx_den[:np.where(f>50)[0][0]] )

ax3 = plt.subplot(223, sharex=ax1)
plt.plot(t,x)
ax4 = plt.subplot(224)
f, Pxx_den = signal.periodogram(x, Fs)
line1, = plt.plot(f[:np.where(f>50)[0][0]],Pxx_den[:np.where(f>50)[0][0]] )

def func1(source_axes):
    lim = source_axes.get_xlim()
    f, Pxx_den = signal.periodogram(x[np.bitwise_and(t >lim[0] , t <lim[1])], Fs)
    X,Y = f[:np.where(f>50)[0][0]],Pxx_den[:np.where(f>50)[0][0]]
    return X,Y

mapping = {ax1 : [ax2, func1], ax3 : [ax4, func1]}

def onlimschange(source_axes):
    for source_ax in source_axes.get_shared_x_axes().get_siblings(source_axes):
        target_ax = mapping[source_ax][0]
        X,Y = mapping[source_ax][1](source_axes)
        target_ax.clear()
        target_ax.plot(X,Y)

ax1.callbacks.connect('xlim_changed', onlimschange)
ax3.callbacks.connect('xlim_changed', onlimschange)


plt.show()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Yes, that' would work. But In my complete code, the sharedx are done by the user and then could change over time. In the solution you proposed, every FFT will be done again, even if it not necessary. I'm wondering if i can make a test in the `onlimschange` fonction to know which axes have their xaxis changing? If not, I will use this solution. thank you. – ymmx Feb 03 '18 at 08:57
  • I added another solution. See if that is more what you are looking for. – ImportanceOfBeingErnest Feb 04 '18 at 09:45
  • Nice. Thank you. – ymmx Feb 05 '18 at 08:10