Tip

This page can also be run or downloaded as a Jupyter notebook.

  • Run in a new tab using JupyterLite[1]:

  • Download:

Wavelet Packets#

import pywt and construct a helper function#

import pywt

This helper function can format arrays in a consistent manner across different systems. Please note that this function is just for the purpose of this example and is not part of the PyWavelets library, and it is not necessary or required to use it in your own code:

def format_array(a):
    """Consistent array representation across different systems"""
    import numpy
    a = numpy.where(numpy.abs(a) < 1e-5, 0, a)
    return numpy.array2string(a, precision=5, separator=' ', suppress_small=True)

Create Wavelet Packet structure#

Okay, let’s create a sample instance of the WaveletPacket class:

x = [1, 2, 3, 4, 5, 6, 7, 8]
wp = pywt.WaveletPacket(data=x, wavelet='db1', mode='symmetric')

The input data and decomposition coefficients are stored in the WaveletPacket.data attribute:

print(wp.data)
[1, 2, 3, 4, 5, 6, 7, 8]

Nodes are identified by their paths, i.e., Node.path. For the root node, the path is '' and the decomposition level is 0.

print(repr(wp.path))
''
print(wp.level)
0

The maxlevel attribute, if not given as param in the constructor, is automatically computed:

print(wp['ad'].maxlevel)
3

Traversing the WP tree#

Accessing subnodes#

x = [1, 2, 3, 4, 5, 6, 7, 8]
wp = pywt.WaveletPacket(data=x, wavelet='db1', mode='symmetric')

First check what is the maximum level of decomposition:

print(wp.maxlevel)
3

and try accessing subnodes of the WP tree.

1st level:

print(wp['a'].data)
[ 2.12132034  4.94974747  7.77817459 10.60660172]
print(wp['a'].path)
a

2nd level:

print(wp['aa'].data)
[ 5. 13.]
print(wp['aa'].path)
aa

3rd level:

print(wp['aaa'].data)
[12.72792206]
print(wp['aaa'].path)
aaa

Oops, we have reached the maximum level of decomposition and received an IndexError:

print(wp['aaaa'].data)
Traceback (most recent call last):
...
IndexError: Path length is out of range.

Now, try an invalid path:

print(wp['ac'])
Traceback (most recent call last):
...
ValueError: Subnode name must be in ['a', 'd'], not 'c'.

which just yielded a ValueError.

Accessing Node’s attributes#

The WaveletPacket object is a tree data structure, which evaluates to a set of Node objects. WaveletPacket is just a special subclass of the Node class (which in turn inherits from the BaseNode class).

Tree nodes can be accessed using the obj[x] (Node.__getitem__) operator.

Each tree node has a set of attributes: data, path, node_name, parent, level, maxlevel and mode.

x = [1, 2, 3, 4, 5, 6, 7, 8]
wp = pywt.WaveletPacket(data=x, wavelet='db1', mode='symmetric')
print(wp['ad'].data)
[-2. -2.]
print(wp['ad'].path)
ad
print(wp['ad'].node_name)
d
print(wp['ad'].parent.path)
a
print(wp['ad'].level)
2
print(wp['ad'].maxlevel)
3
print(wp['ad'].mode)
symmetric

Collecting nodes#

x = [1, 2, 3, 4, 5, 6, 7, 8]
wp = pywt.WaveletPacket(data=x, wavelet='db1', mode='symmetric')

We can get all nodes on the particular level either in natural order:

print([node.path for node in wp.get_level(3, 'natural')])
['aaa', 'aad', 'ada', 'add', 'daa', 'dad', 'dda', 'ddd']

or sorted based on the band frequency (freq):

print([node.path for node in wp.get_level(3, 'freq')])
['aaa', 'aad', 'add', 'ada', 'dda', 'ddd', 'dad', 'daa']

Note that WaveletPacket.get_level() also performs automatic decomposition until it reaches the specified level.

Reconstructing data from Wavelet Packets#

x = [1, 2, 3, 4, 5, 6, 7, 8]
wp = pywt.WaveletPacket(data=x, wavelet='db1', mode='symmetric')

Now create a new instance of the WaveletPacket class and set its nodes with some data.

new_wp = pywt.WaveletPacket(data=None, wavelet='db1', mode='symmetric')
new_wp['aa'] = wp['aa'].data
new_wp['ad'] = [-2., -2.]

For convenience, Node.data gets automatically extracted from the Node object:

new_wp['d'] = wp['d']

And reconstruct the data from the aa, ad and d packets.

print(new_wp.reconstruct(update=False))
[1. 2. 3. 4. 5. 6. 7. 8.]

If the update param in the reconstruct method is set to False, the node’s Node.data will not be updated.

print(new_wp.data)
None

Otherwise, the Node.data attribute will be set to the reconstructed value.

print(new_wp.reconstruct(update=True))
[1. 2. 3. 4. 5. 6. 7. 8.]
print(new_wp.data)
[1. 2. 3. 4. 5. 6. 7. 8.]
print([n.path for n in new_wp.get_leaf_nodes(False)])
['aa', 'ad', 'd']
print([n.path for n in new_wp.get_leaf_nodes(True)])
['aaa', 'aad', 'ada', 'add', 'daa', 'dad', 'dda', 'ddd']

Removing nodes from Wavelet Packet tree#

Let’s create some sample data:

x = [1, 2, 3, 4, 5, 6, 7, 8]
wp = pywt.WaveletPacket(data=x, wavelet='db1', mode='symmetric')

First, let’s start with a tree decomposition at level 2. The leaf nodes in the tree are:

dummy = wp.get_level(2)
for n in wp.get_leaf_nodes(False):
    print(n.path, format_array(n.data))
aa [ 5. 13.]
ad [-2. -2.]
da [-1. -1.]
dd [0. 0.]
node = wp['ad']
print(node)
ad: [-2. -2.]

To remove a node from the WP tree, use Python’s del obj[x] (Node.__delitem__):

del wp['ad']

The leaf nodes that left in the tree are:

for n in wp.get_leaf_nodes():
    print(n.path, format_array(n.data))
aa [ 5. 13.]
da [-1. -1.]
dd [0. 0.]

And the reconstruction is:

print(wp.reconstruct())
[2. 3. 2. 3. 6. 7. 6. 7.]

Now, restore the deleted node value.

wp['ad'].data = node.data

Printing leaf nodes and tree reconstruction confirms the original state of the tree:

for n in wp.get_leaf_nodes(False):
    print(n.path, format_array(n.data))
aa [ 5. 13.]
ad [-2. -2.]
da [-1. -1.]
dd [0. 0.]
print(wp.reconstruct())
[1. 2. 3. 4. 5. 6. 7. 8.]

Lazy evaluation#

Note

This section is for demonstration of PyWavelets’ internal purposes only. Do not rely on the attribute access to nodes as presented in this example.

x = [1, 2, 3, 4, 5, 6, 7, 8]
wp = pywt.WaveletPacket(data=x, wavelet='db1', mode='symmetric')

At first the wp’s attribute a is None:

print(wp.a)
None

Remember that you should not rely on the attribute access.

At the first attempt to access the node, it is computed via the decomposition of its parent node (which is the wp object itself).

print(wp['a'])
a: [ 2.12132034  4.94974747  7.77817459 10.60660172]

Now wp.a is set to the newly created node:

print(wp.a)
a: [ 2.12132034  4.94974747  7.77817459 10.60660172]

And so is wp.d:

print(wp.d)
d: [-0.70710678 -0.70710678 -0.70710678 -0.70710678]