Skip to main content

signal processing - Extracting information from the result of ContinuousWaveletTransform


I'm trying to analysis a signal and identify some time-frequency information of it. For example, around which time the specific frequency arrives. It appears that Mathematica has very powerful wavelet analysis functions builtin suitable for my job. I've being reading the documentation of them, but still can't get it. In particular I don't really understand what the functions ContinuousWaveletTransform and WaveletScalogram do.


For example, for this example in the document,


data = Table[
Piecewise[{{Sin[2 π 10 t], 0 <= t < 1/4}, {Sin[2 π 25 t],
1/4 <= t < 1/2}, {Sin[2 π 50 t],
1/2 <= t < 3/4}, {Sin[2 π 100 t], 3/4 <= t <= 1}}], {t, 0,
1, 1/1023}];
ListLinePlot[data, AspectRatio -> 0.2]


enter image description here


cwd = ContinuousWaveletTransform[data, 
DGaussianWavelet[5], {Automatic, 12}];
WaveletScalogram[cwd, ColorFunction -> "RustTones"]

enter image description here


The above example shows the maps of a 1D time signal into a 2D time-frequency representation.


So my questions are:



  1. In the documentation, it says WaveletScalogram "plots wavelet vector coefficients", what that really mean? What is the x and y axis represent? In my understanding the time-frequency 2d plot is kind of like a musical score where the time axis is horizontal and the frequencies (notes) are plotted on a vertical axis. It this understanding correct?


  2. Is there a way to see the identify frequencies in the signal from the 2D plot? For example, we can see four difference frequencies from the 2d plot, can we read off their frequencies from the corresponding y value?



Answer



Yes, you are right. WaveletScalogram produces a plot that is very similar in behaviour to that used in music. Here, the octave axis is also logarithmic: -Log[2,b], meaning that the frequency at the next octave is doubled. We can illustrate this by a simple example - Consider a signal with a freq = 440Hz and a signal with double that freq = 880Hz. Now, knowing that higher frequencies are resolved at lower octaves and lower frequencies at higher octaves we can do the following calculation:


N[Log[440]/Log[2]]


 8.78136

N[Log[880]/Log[2]]



 9.78136

What this shows us is that two signals with frequencies respectively ω and 2ω are resolved at octaves next to each other.


And yes, you can identify a certain frequency from the WaveletScalogram. You have the following relation between the scales used in the wavelet transform,ContinuousWaveletTransform or DiscreteWaveletTransform, and the specific frequency: $$F_{a}=\frac{F_{c}}{a \Delta }$$



  • a is a scale

  • Δ is the SampleRate

  • Fc is the center frequency of a wavelet in Hz


  • Fa is the frequency corresponding to the scale a, in Hz


For ease of use we will give Fc = 1. For a better localisation search the Internet for the exact centre frequency corresponding to the wavelet family you are using. Now, all we need is the SampleRate which by default is equal to 8000Hz and the scales used in the wavelet transform you gave as an example.


freq = (#1[[1]] -> 8000/#1[[2]] &) /@ cwd["Scales"]


  {{1, 1} -> 20230.3, {1, 2} -> 19094.9, {1, 3} -> 18023.2, {1, 4} -> 17011.6,
{1, 5} -> 16056.8, {1, 6} -> 15155.6, {1, 7} -> 14305., {1, 8} -> 13502.1,
{1, 9} -> 12744.3, {1, 10} -> 12029., {1, 11} -> 11353.9, {1, 12} -> 10716.6,
{2, 1} -> 10115.2, {2, 2} -> 9547.44, {2, 3} -> 9011.58, {2, 4} -> 8505.8,

{2, 5} -> 8028.41, {2, 6} -> 7577.81, {2, 7} -> 7152.5, {2, 8} -> 6751.06,
{2, 9} -> 6372.15, {2, 10} -> 6014.51, {2, 11} -> 5676.94, {2, 12} -> 5358.32, ... }

We need to filter the frequency we are interested in and we will be able to say where we can find it in the scalogram. Say we are interested in the ω = 50Hz


Cases[freq, u_ /; 49 <= Last[u] <= 51]


{{9, 9} -> 49.7824}

Look back at the scalogram and locate octave 9 and voice 9 - and there it is !



At first I thought that the logical step to perform was to scale the data before passing it to the WaveletScalogram. And since we are using -Log scale you need to scale by a factor 2. I am still trying to figure out how to do this in WaveletScalogram. If that isn't something you need really fast you can use the following approach:


ListPlot[Abs@Reverse[Last /@ cwd[All]], PlotRange -> All]


Linear scale scalogram



Or optionally in 3D:


ListPlot3D[Abs@Reverse[Last /@ cwd[All]], PlotRange -> All, 
Mesh -> None, Boxed -> False, ColorFunction -> "DeepSeaColors",
AxesLabel -> {"time", "octaves", "magnitude"}]



Wavelet Scalogram in 3D





Edit by xslittlegrass


Sometimes we need to plot the wavelet transform scalogram in unit of frequencies rather than scales. The following shows how to do that (Thanks @Rojo and @MichaelE2 for discussion).


sampleRate=1023;
data = Table[
Piecewise[{{Sin[2 π 10 t], 0 <= t < 1/4}, {Sin[2 π 25 t],

1/4 <= t < 1/2}, {Sin[2 π 50 t],
1/2 <= t < 3/4}, {Sin[2 π 100 t], 3/4 <= t <= 1}}], {t, 0, 1,
1/sampleRate}];
cwd = ContinuousWaveletTransform[data,
DGaussianWavelet[5], {Automatic, 12}, SampleRate -> sampleRate]

Note that the frequencies in wavelet plot is characterized by pairs of numbers {octave, voice}. A octave means the frequency is doubled, and voices are further divisions of a octave. For example, if f1==2*f2, then f1 is one octave above f2. These wavelet scales can be converted into frequencies easily in a very clean way thanks to the "Scales" and "FourierFactor" properties of the wavelet transform data.


This calculates the frequency (in Hz) of each octave (corresponding to {1,1}, {2,1}, ... in the {octave,voice} notation.)


freq = (cwd["SampleRate"]/(#*cwd["Wavelet"]["FourierFactor"])) & /@
(Thread[{Range[cwd["Octaves"]], 1}] /. cwd["Scales"]);


this gives expression for ticks at each octave


ticks = Transpose[{Range[Length[freq]], freq}];

this display the wavelet scalogram in the frequency.


WaveletScalogram[cwd, Frame -> True, FrameTicks -> {{ticks, Automatic}, Automatic}, 
FrameLabel -> {"Time", "Frequency(Hz)"},
ColorFunction -> "RustTones"]

enter image description here



From above plots, one can see that there are frequencies at around 10Hz, 25Hz, 50Hz and 100Hz as we have in the signal.


Comments

Popular posts from this blog

front end - keyboard shortcut to invoke Insert new matrix

I frequently need to type in some matrices, and the menu command Insert > Table/Matrix > New... allows matrices with lines drawn between columns and rows, which is very helpful. I would like to make a keyboard shortcut for it, but cannot find the relevant frontend token command (4209405) for it. Since the FullForm[] and InputForm[] of matrices with lines drawn between rows and columns is the same as those without lines, it's hard to do this via 3rd party system-wide text expanders (e.g. autohotkey or atext on mac). How does one assign a keyboard shortcut for the menu item Insert > Table/Matrix > New... , preferably using only mathematica? Thanks! Answer In the MenuSetup.tr (for linux located in the $InstallationDirectory/SystemFiles/FrontEnd/TextResources/X/ directory), I changed the line MenuItem["&New...", "CreateGridBoxDialog"] to read MenuItem["&New...", "CreateGridBoxDialog", MenuKey["m", Modifiers-...

How to thread a list

I have data in format data = {{a1, a2}, {b1, b2}, {c1, c2}, {d1, d2}} Tableform: I want to thread it to : tdata = {{{a1, b1}, {a2, b2}}, {{a1, c1}, {a2, c2}}, {{a1, d1}, {a2, d2}}} Tableform: And I would like to do better then pseudofunction[n_] := Transpose[{data2[[1]], data2[[n]]}]; SetAttributes[pseudofunction, Listable]; Range[2, 4] // pseudofunction Here is my benchmark data, where data3 is normal sample of real data. data3 = Drop[ExcelWorkBook[[Column1 ;; Column4]], None, 1]; data2 = {a #, b #, c #, d #} & /@ Range[1, 10^5]; data = RandomReal[{0, 1}, {10^6, 4}]; Here is my benchmark code kptnw[list_] := Transpose[{Table[First@#, {Length@# - 1}], Rest@#}, {3, 1, 2}] &@list kptnw2[list_] := Transpose[{ConstantArray[First@#, Length@# - 1], Rest@#}, {3, 1, 2}] &@list OleksandrR[list_] := Flatten[Outer[List, List@First[list], Rest[list], 1], {{2}, {1, 4}}] paradox2[list_] := Partition[Riffle[list[[1]], #], 2] & /@ Drop[list, 1] RM[list_] := FoldList[Transpose[{First@li...

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...