Tip
This page can also be run or downloaded as a Jupyter notebook.
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]