0

I have been having issues regarding signal processing. I am fairly new with programming so I may be completely wrong. I wrote code for a textbox widget in matplotlib. I want to be able to plot evenly-spaced markers on a graph. The number of markers that someone wants to add will be entered in the textbox. Right now I have a numpy linspace function, where it starts at 0 and stops at len(x_data) with num being equal to the number of markers I want to add. For some reason the markers plot on top of each other. So instead of plotting 10 markers on the graph it only plots 5 markers where the markers are plotted on top of each other. Am I doing something wrong?

def process_points(event):
    ax2.cla()
    
    number_of_points = int(text2_box1.text)
    marker_indices = np.linspace(0, len(x_data)-1, number_of_points, dtype=int)
    x_markers = x_data[marker_indices]
    y_markers = y_data[marker_indices]
    
    ax2.plot(x_data, y_data, color='blue')
    ax2.set_xlabel('Frequency (Hz)')
    ax2.set_ylabel('Amplitude')
    ax2.set_title('Data between lines')
    
    for x, y in zip(x_markers, y_markers):
        ax2.plot(x, y, '.', color='crimson')
    
    plt.show()

Example of what I mean:

Edit: Updated code:

def process_points(event):
     ax2.cla()

     number_of_points = int(text2_box1.text)
     x_markers = np.linspace(0, x_data[-1],number_of_points)

     ax2.semilogy(x_data, y_data, color='blue')
     ax2.set_xlabel('Frequency (Hz)')
     ax2.set_ylabel('Amplitude')
     ax2.set_title('Data between lines')

     y_markers = np.interp(x_markers, x_data, y_data)  
     ax2.scatter(x_markers, y_markers, color='crimson')  

     ax2.grid(True, which='both', axis='both')

     plt.show()

New graph from the updated code

Reinderien
  • 11,755
  • 5
  • 49
  • 77
Salt
  • 1
  • 4
  • Instead of `len(x_data)-1` try `x_data[-1]`; in other words, give `linspace` the maximal x-value as its series end. – Reinderien Jul 10 '23 at 18:26
  • Also drop `dtype=int`; leave it to its default floating-point type – Reinderien Jul 10 '23 at 18:26
  • Does your signal repeat/fold back on itself? – jared Jul 10 '23 at 18:27
  • @Reinderien They're using the `linspace` to generate indices, so it has to be `int`. That's also why they're using the number of points, not the value of `x_data[-1]`. – jared Jul 10 '23 at 18:28
  • Sure, but they shouldn't. `y_markers` shouldn't exist at all, and `x_markers` should be the output of `linspace` directly with no indexing – Reinderien Jul 10 '23 at 18:28
  • OP, if the data was generated from an FFT, I'm wondering if you handled the mirrored result correctly. I'm thinking that the signal is folded back and you're seeing the points on top of each other because of that. – jared Jul 10 '23 at 18:29
  • Let me try these recommendations and Ill get back to you @Reinderien – Salt Jul 10 '23 at 18:30
  • @Reinderien It seems that their intention is to place the markers at the appropriate coordinates, in which case they would need to find the y-value associated with the `linspace`-generated points. Assuming the `x_data` is evenly spaced and monotonic, their indexing method seems appropriate. – jared Jul 10 '23 at 18:32
  • No the signal is a section of a much longer signal. Since I am not really interested in what is going on in the higher frequencies I defined a small sliding window. So the signal does not repeat @jared – Salt Jul 10 '23 at 18:32
  • @Salt So your `x_data` is definitely monotonic? You can check by doing `(np.diff(x_data) >= 0).all()`. If that is false, then it is not monotonic. – jared Jul 10 '23 at 18:33
  • @jared the folding could be the issue. I cant leave out that possibility, but since its merely a section of the entire FFT then it shouldn't be too much of issue. Correct me if I am wrong. I am fairly new at this. – Salt Jul 10 '23 at 18:36
  • The fact that your spectrum looks vaguely symmetric but the f-axis is not centred on 0 is deeply suspect. I share Jared's concerns and recommend that you share the code that generates this spectrum, as well as data for it to consume. – Reinderien Jul 10 '23 at 18:37
  • @jared let me try that and Ill get back to you. – Salt Jul 10 '23 at 18:38
  • @Reinderien yeah I know lol. It repeats like this because its supposed to be the end of a fiber cable. But the data is not symmetrical. – Salt Jul 10 '23 at 18:40
  • @Reinderien there is issues regarding the type of data. The data in the linspace should either be a boolean or integer type – Salt Jul 10 '23 at 18:44
  • See above ^: _y_markers shouldn't exist at all, and x_markers should be the output of linspace directly with no indexing_ – Reinderien Jul 10 '23 at 18:44
  • @Reinderien apologies. Let me fix that – Salt Jul 10 '23 at 18:51
  • @Reinderien is this what you mean? Apologies I am very new with coding and signal processing. def process_points(event): ax2.cla() number_of_points = int(text2_box1.text) x_markers = np.linspace(0, x_data[-1], number_of_points) ax2.plot(x_data, y_data, color='blue') ax2.set_xlabel('Frequency (Hz)') ax2.set_ylabel('Amplitude') ax2.set_title('Data between lines') ax2.plot(x_markers, '.', color='crimson') plt.show() – Salt Jul 10 '23 at 19:02
  • Let's please continue this in https://chat.stackoverflow.com/rooms/254426/evenly-spaced-marker-plotting-for-a-graph – Reinderien Jul 10 '23 at 19:03
  • @Reinderien i can see what you said in the chat but I cant talk because I dont have enough reputation to talk in the chat room – Salt Jul 10 '23 at 19:07
  • Sigh. Stack Overflow self-owns again. So be it: chat in the comments – Reinderien Jul 10 '23 at 19:08
  • @Reinderien ill try what you are referring to. Just give me some time. Ill change the tick frequency and update the y-axis so it is in a logarithmic scale. – Salt Jul 10 '23 at 19:10
  • Long story short, controlling your horizontal grid space and enabling gridline major rendering at the chosen frequency is a saner and more built-in way to do what you want to do; will be easier to read; and will not require that the reader's eyes search up and down once you render in semilog scale instead of linear scale. – Reinderien Jul 10 '23 at 19:10
  • @Reinderien cool thanks again for your help I appreciate it. Ill update the code and let you know if it is working. – Salt Jul 10 '23 at 19:11
  • @Reinderien is this what you mean? number_of_points = int(text2_box1.text) x_markers = np.linspace(0, x_data[-1], number_of_points) ax2.semilogx(x_data, y_data, color='blue') y_markers = np.interp(x_markers, x_data, y_data) ax2.scatter(x_markers, y_markers, color='crimson') ax2.grid(True, which='both', axis='both') plt.show() – Salt Jul 10 '23 at 19:19
  • Maybe. Edit your question and include a screenshot of the graph. That `semilogx` should be a `semilogy` – Reinderien Jul 10 '23 at 19:20
  • @Reinderien I am running it now. I have a large data set so it takes a while to process everything. Ill update with a graph in a bit. – Salt Jul 10 '23 at 19:21
  • @Reinderien I posed the graph from the updated code on the discussion. It seems as if all the markers are plotted at the start of the graph. – Salt Jul 10 '23 at 19:33
  • Your question is "clear enough" so I'm going to switch to writing an answer. – Reinderien Jul 10 '23 at 19:34
  • May I make a bit of self promotion? I have written a well received post on the subtle (and not so subtle) [issues that you find working with the discrete Fourier Transform](https://stackoverflow.com/a/27191172/2749397). E.g., you seem to ignore the existence of the Nyquist frequency. – gboffi Jul 14 '23 at 10:07

2 Answers2

0

Your data is clearly folded, meaning half the data is duplicated and your first and last points are the same. You have two options:

  1. You can remove the duplicate data and use your original method. The data is probably folded at the halfway point (you'll have to confirm this), so you can save x_data = x_data[:len(x_data)//2]. Same for the y_data. Then you should get your desired results.
  2. If you want to use @Reinderien's method, then instead of doing x_data[-1], which is simply returning the first value (because of the folding), do np.linspace(x_data.min(), x_data.max(), number_of_points).
jared
  • 4,165
  • 1
  • 8
  • 31
  • OP claims fold appearance because "its supposed to be the end of a fiber cable. But the data is not symmetrical." I tend not to believe this explanation, but with no proof available, there isn't a lot to be done. – Reinderien Jul 10 '23 at 19:40
  • @Reinderien Did they confirm my check about the data being monotonic? Either way, once I saw that `data[-1]` put all the points in the same location, I became even more sure that the data is folded/duplicated. OP will have to figure out why that is, but in the meantime, one of my two suggestions should do the trick. – jared Jul 10 '23 at 19:43
0

x_markers appears to be correct. interp might be choking because, again, @jared and I think that your frequency series is non-monotonic. Hint: perform a verification plot where the vertical axis is x_data and the horizontal axis is an arange. It should be a straight line.

The code to find y_markers appears to be reasonable but you should add a debug breakpoint and examine its value to make sure, because it seems only the first point is being drawn.

The point of adding the grid is so that you align it with your number_of_points and that hasn't been done. For this you need a set_xticks.

Reinderien
  • 11,755
  • 5
  • 49
  • 77