-1

I am trying to use plt.specgram

But I found something weird in the x-axis setting.

My data is as below..

Date       Value
2018-01-01  0.1
2018-01-02  0.4
...
2020-02-27  0.7
2020-02-28  0.3

The length of data is 821. (The dates I wrote are temporary values)

When I plot the spectrogram as below:

Pxx, freqs, bins, im=ax.specgram(df, NFFT=256, Fs=1.0, noverlap=220, cmap='inferno', mode='psd')
mesh=ax.pcolormesh(bins, freqs, Pxx, norm=LogNorm(), cmap='inferno')

the x-axis only shows values below 700.

  1. When I set noverlap = 75, the x-axis shows values over 700.

  2. When I set nfft = 256, the x-axis shows values up to about 220.

  3. When I set nfft = 256*3, the x-axis becomes up to about 256*2.

If I want to the x-axis to show values up to 821, how do I set the parameters of specgram?
(I want to set nfft = 256, and noverlap = 220)


This is the fig1

enter image description here

This is the fig2

enter image description here

This is the fig3

enter image description here

Jdip
  • 5,980
  • 3
  • 7
  • 29

2 Answers2

0

Short answer

You can't.


EDIT

  • To answer your question as I understand it, which is

    how can I get the x-axis to span the full length of my data ?

    As @Hilmar mentioned, one way to achieve this is to have windows of length 1, i.e nfft = 1 and overlap = 0 (note, in Matlab, the spectrogram function won't let you input windows of length less than 2). With nfft = 256, the further down the x-axis you'll get is by setting overlap = 68. To understand why that is, and how a spectrogram is displayed, in particular the x-axis, you can go on to the "Long answer" below.

  • A slightly different and in my opinion more interesting question is:

    With a fixed window length and overlap, how do I make sure every sample in my data has been windowed and transformed ?

    The answer to that is to make sure that the length $N$ of the input data satisfies $$\left(\frac{N-r}{L-r}\right) = K$$

    $K\in1,2,3,\dots$

    To ensure this condition, choose

    $$K = \bigg\lceil \frac{N_o-r}{L-r} \bigg\rceil$$ where $\lceil\rceil$ denotes the ceil function and $N_o$ is the length of your actual data.

    You can then derive $N$ as $$N = K(L-r) + r$$ and zero-pad your input data with $N-N_o$ zeros.

    For example, taking your question's requirements, $N_o = 821, L = 256, r = 220$, we get $$K = 17, \quad N = 832$$ and you'd add $N-N_o = 11$ zeros to your input data before computing the spectrogram.


Long answer

Spectrogram display

  1. The number of x-axis indices corresponds to the number of time frames of your spectrogram. $$N_{frames} = \bigg\lfloor\frac{N-r}{L-r}\bigg\rfloor$$ where $N$ is the length of the data, $r$ the overlap and $L$ the window length.

  2. Each x-axis index is at the center of a window of length $L$:

$$x_{center}[k] = k \cdot (L-r) + L/2 $$

$k = 0,\dots, N_{frames}-1 $

$\quad \quad$ And the frame edges are $$\left[x_{center}[k]-\frac{L-r}{2},\, x_{center}[k]+\frac{L-r}{2}\right]$$

Verification

For example, taking your values:

  • nfft = 256, noverlap = 220: $N_{frames} = \bigg\lfloor\frac{821-220}{256-220}\bigg\rfloor = 16$.

The last frame has center $15\cdot(256-220)+128 = 668$ and edges $$[668-(256-220)/2, 668+(256-220)/2]$$ $$= [650, 686]$$ which you can verify on your Fig2 plot.

  • nfft = 256, noverlap = 75: $N_{frames} = 4$

The last frame has center $671$ and edges $$[580.5, 761.5]$$ which you can verify on your Fig3 plot.

  • nfft = 256*3, noverlap = 256*2: $N_{frames} = 1$

That's not a short time Fourier transform anymore, just an FFT on 768 data samples. You can see there is only one frame on Fig3. Still, the expected center and edges are verified: center at $384$, edges at $[256, 512]$

Jdip
  • 5,980
  • 3
  • 7
  • 29
  • Is this answer saying 821 output time frames isn't doable, or it's not doable with given noverlap? Padding? – OverLordGoldDragon Nov 17 '22 at 15:52
  • @OverLordGoldDragon The latter. I understood the OP was confused as to why the x-axis never spans the full length 821, which, you are right, is doable with FFT sizes of 1, as Hilmar mentioned in his answer, but not much use for obvious reasons. Another possibility would be to have the overlap be one sample less than the window length, which is, as Hilmar also mentioned, redundant. You are correct though, I guess if the window length and overlap are fixed, the original data could be padded so that the last window “fits” perfectly! – Jdip Nov 17 '22 at 16:34
  • I’ll edit my answer based on this comment. Thanks for the input! I’ll do that later today :) – Jdip Nov 17 '22 at 16:35
  • hop_size=1, while indeed very redundant and generally undesired, has its uses, and is certainly worth mentioning, especially with highly time-localized windows. In yet other cases we have no choice, e.g. one-integral inversion. – OverLordGoldDragon Nov 17 '22 at 18:09
  • @OverLordGoldDragon I've edited my answer with your suggestion. However, I haven't mentioned the 1 sample hop size, because there are plenty of resources on this site that deal with window size and overlap for STFT, and because with a L-length window and 1 sample overlap, the last frame displayed by a spectrogram would actually have its right edge at L/2, so does not address the OP's question. I did add the more interesting (IMO) consideration on zero-padding (which doesn't really answer the OP either, but maybe that's actually what he had in mind and framed his question poorly?) – Jdip Nov 17 '22 at 22:48
  • "does not address this particular question" why not? I don't see OP write against this. I'm also unsure how "plenty of resources on this site" is an obstacle, there's plenty of repetition everywhere. Also unsure of your take on padding, but it's the standard in many (if not most) applications, and the default in scipy and librosa. Also "just FFT on 768 data samples" is true only with a flat window, though here we're close. – OverLordGoldDragon Nov 17 '22 at 23:04
  • Please either change "There is only one way" to "One way" or edit the answer per our discussion. Former wouldn't do justice but would fix a serious issue with the answer (putting aside the fact that what it says isn't "per OLGD"). – OverLordGoldDragon Apr 17 '23 at 13:05
  • Also, a "fully valid" STFT, assuming a time-limited input, will pad and not unpad; consider the conservation of energy of transform for a unit impulse (Addendum). I don't know where the "don't pad" convention comes from but I find it frankly ridiculous. Attenuation of information for not admitting a prior. It doesn't even have a numerically stable inverse for float32 near edges. – OverLordGoldDragon Apr 17 '23 at 15:22
-1

If I want to the xaxis set as 821

This doesn't make a lot of sense since you are running afoul of the "uncertainty principle of the Fourier Transform". The shorter a signal is in time, the less frequency resolution has your spectrum.

There are ways to get "one spectrum per sample" but the results are typically not meaningful. Adjacent spectrums will be almost identical and all you are doing is interpolating without gaining any new information.

The easiest way would be set the FFT size to 1, but then each spectrum is just a single value, which is probably not what you want.

Hilmar
  • 44,604
  • 1
  • 32
  • 63
  • running afoul of the "uncertainty principle of the Fourier Transform" Heisenberg has nothing to do with this, and "results are typically not meaningful" is also false. – OverLordGoldDragon Apr 17 '23 at 12:57