aaren / wavelets

Python implementation of the wavelet analysis found in Torrence and Compo (1998)
296 stars 111 forks source link

Can't accurately compute C_d for an arbitrary wavelet. #1

Open aaren opened 11 years ago

aaren commented 11 years ago

Example code:

from wavelets import WaveletAnalysis
wa = WaveletAnalysis()

# hard coded Morlet value
print wa.C_d
print wa.wavelet.C_d

# override hard coding
wa.wavelet.C_d = None
# Now C_d is explicitly computed
print wa.C_d

The explicitly computed version is giving me a value of (0.13708 + 0j), rather than the 0.776 that we expect for a Morlet with w0 = 6 (which is the default).

aaren commented 10 years ago

@nabobalis @cadair see section 3.i of Torrence and Compo for the maths. I've implemented the function in wavelets.WaveletAnalysis.compute_Cdelta here but maybe I've got something wrong.

nabobalis commented 10 years ago

From what I can see either, the value in the paper is wrong or either Y_00 or W_D are returning the wrong values.

For the morlet, is the frequency_rep function calculating the fourier transform of the Morlet wavelet as is given http://en.wikipedia.org/wiki/Morlet_wavelet ?

aaren commented 10 years ago

It is implemented as in Table 1 of TC98 (paper here if you need it. ) I'm not sure how this maps to the wikipedia version.

nabobalis commented 10 years ago

Yeah, it is the same as far as I can tell as the paper version. I can't find an issue with the code.

aaren commented 10 years ago

Are we talking about the same thing? I'm comparing this code, i.e.

def frequency_rep(self, w, s=1.0):
        """Frequency representation of morlet.

        s - scale
        w - angular frequency
        """
        x = w * s
        # heaviside mock
        Hw = 0.5 * (np.sign(x) + 1)
        return np.pi ** -.25 * Hw * np.exp((-(x - self.w0) ** 2) / 2)

with the fourier transform on wikipedia:

wikipedia version

What I can't see is how the heaviside function is represented on wikipedia.

nabobalis commented 10 years ago

Yeah, I do mean that. The last part of the equation vanishes for sigma greater than 5. So it does simplify. I guess I shouldn't be going to wikipedia anyway.

Regardless, the code follows the paper. So I'm not sure what the problem is at the moment.

On 31 August 2014 13:51, aaren notifications@github.com wrote:

Are we talking about the same thing? I'm comparing this code https://github.com/aaren/wavelets/blob/master/wavelets/wavelets.py#L207, i.e.

def frequency_rep(self, w, s=1.0): """Frequency representation of morlet. s - scale w - angular frequency """ x = w * s

heaviside mock

    Hw = 0.5 \* (np.sign(x) + 1)
    return np.pi *\* -.25 \* Hw \* np.exp((-(x - self.w0) *\* 2) / 2)

with the fourier transform on wikipedia:

[image: wikipedia version] https://camo.githubusercontent.com/98674993726b55c71cecd0dab15e2eb5131a4ebc/687474703a2f2f75706c6f61642e77696b696d656469612e6f72672f6d6174682f342f342f662f34346666363266356232343461396663653363353662396566653164313432302e706e67

What I can't see is how the heaviside function is represented on wikipedia.

— Reply to this email directly or view it on GitHub https://github.com/aaren/wavelets/issues/1#issuecomment-53986979.

aaren commented 9 years ago

I made a mistake in the code. I'm still not getting the exact same value as TC98, but at least it's a lot closer (getting 0.724 now).

aaren commented 9 years ago

The actual computation of Cdelta is a double summation of the fourier wavelet over scale and frequency. This is sensitive to the values of N, dj and s0 (dt cancels out).

It looks like N just needs to be big enough (>1000), whereas dj and s0 can be a bit smaller than default.

s0 is the smallest scale. The default choice for s0 is to find where this corresponds to the equivalent fourier period being equal to 2*dt, which comes out as ~1.9. Setting s0=1 improves the Cdelta calculation a lot, giving us Cdelta = 0.778 (compared with 0.776 in TC98).

aaren commented 9 years ago

08baf66 nearly fixes the issue, giving Cdelta = 0.776 with s0 = 1. However setting s0 to lower than the default (based on the equivalent fourier period) changes the wavelet variance (TC98 equation 14), meaning that variance (energy) is no longer conserved through the wavelet transform.

The wavelet variance is dependent on the value of s0 for two reasons:

1) s0 sets the scaling for all of the scales 2) s0 sets the uppermost scale sJ via J = log2(N dt / s0) / dj

The scales are defined as sj = s0 * 2 ^ (j * dj), j=0, 1, ..., J (TC98 eq 9-10).

We are told to choose s0 such that the equivalent fourier period is about 2 * dt.

paulthebaker commented 7 years ago

I started coding up my own implementation of an inverse continuous wavelet transform. While searching for my own solution to this same problem, I found your repo.

I think the number quoted in the T&C paper is "wrong".

The actual computation of Cdelta is a double summation of the fourier wavelet over scale and frequency. This is sensitive to the values of N, dj and s0 (dt cancels out).

As you say, in the real, live, finite sample size case Cdelta is dependent on N, dj, and s0. If you use T&C eq. (5) to set up your w_k's, dt will affect the numerical integration step size (in the sum), even though it explicitly cancels. To reproduce the T&C result you need to use the exact settings they used.

If you look in their fortran code, you'll see they set:

N = 504
dt = 1/4
dj = 1/4
s0 = 1/4
jtot = 44

These are not the same values stated in T&C section 3.f. They also comment:

Note: for accurate reconstruction and wavelet-derived variance do not pad with zeroes, set s0=dt (for Paul set s0=dt/4), and use a large "jtot" (even though the extra scales will be within the cone of influence). For plotting purposes, it is only necessary to use s0=2dt (for Morlet) and "jtot" from Eqn(10) Torrence&Compo(1998).

It appears that in practice T&C are over computing for numerical stability.

Anyways, using their values, I can reproduce Cdelta = 0.77574... for the w0=6 Morlet. Following thier comment and setting s0 = 1/16 for the m=4 Paul wavelet, I get Cdelta = 1.1302.... Actually, I used jtot = 35, because my scale generator sets that. I assume that's the cause of my small difference with T&C's Table 2.

The main point is that T&C's quoted numbers are not general. I think it's best to compute Cdelta and any other empirically determined constants for a particular wavelet basis as needed and ignore the T&C values in their table 2.

shixun22 commented 7 years ago

Thanks for the nice code! Here I wish to share what I figured out about Cdelta after some struggling and frustration.

The TC98 Cdelta values are indeed resolution dependent. Idealised values of Cdelta can be computed with

def phi_lnk(lnk): k = np.exp(lnk) return self.wavelet.frequency(k).real

Cd = scipy.integrate.quad(phi_lnk, -10, 10)[0] / self.wavelet.time(0).real * (2.*np.pi)**0.5 / np.log(2.)

The ln(2) factor arises from the summation of the 2-based self.scales in the Cdelta computation, which has not been normalised in the Cdelta definition in TC98 or this code.

For complex wavelet functions whose Fourier transform is specified to be 0 for k<=0, such as Morlet, the resulting value needs to be further divided by 2.

The resulting Cdelta value for Morlet is 0.7784..., for Marr/Ricker is 3.6163..., both slightly larger from their TC98 value.

Hope this helps!