0
# Coefficient Manipulation
1
2
Utilities for converting between different coefficient representations, packing/unpacking coefficients, and working with coefficient arrays for efficient processing and storage.
3
4
## Capabilities
5
6
### Coefficient Array Conversion
7
8
Functions for converting between coefficient lists and single arrays for efficient processing.
9
10
```python { .api }
11
def coeffs_to_array(coeffs, padding: int = 0, axes=None):
12
"""
13
Pack wavelet coefficients into a single array.
14
15
Parameters:
16
- coeffs: Coefficient list from wavedec, wavedec2, or wavedecn
17
- padding: Additional padding between coefficient arrays
18
- axes: Axes information (for nD coefficients)
19
20
Returns:
21
(coeff_arr, coeff_slices) tuple where:
22
- coeff_arr: 1D array containing all coefficients
23
- coeff_slices: List of slice objects for extracting individual coefficients
24
"""
25
26
def array_to_coeffs(arr, coeff_slices, output_format: str = 'wavedecn'):
27
"""
28
Unpack single array back to coefficient format.
29
30
Parameters:
31
- arr: 1D coefficient array from coeffs_to_array
32
- coeff_slices: Slice information from coeffs_to_array
33
- output_format: Output format ('wavedec', 'wavedec2', 'wavedecn')
34
35
Returns:
36
Coefficient list in specified format
37
"""
38
```
39
40
#### Usage Examples
41
42
```python
43
import pywt
44
import numpy as np
45
import matplotlib.pyplot as plt
46
47
# Create test signal and perform multilevel DWT
48
np.random.seed(42)
49
signal = np.sin(2 * np.pi * 5 * np.linspace(0, 1, 1024)) + 0.3 * np.random.randn(1024)
50
coeffs = pywt.wavedec(signal, 'db4', level=6)
51
52
print(f"Original coefficient format: {len(coeffs)} arrays")
53
print(f"Coefficient array lengths: {[len(c) for c in coeffs]}")
54
print(f"Total coefficients: {sum(len(c) for c in coeffs)}")
55
56
# Pack coefficients into single array
57
coeff_arr, coeff_slices = pywt.coeffs_to_array(coeffs, padding=0)
58
print(f"\nPacked array shape: {coeff_arr.shape}")
59
print(f"Number of slices: {len(coeff_slices)}")
60
61
# Show slice information
62
for i, slice_info in enumerate(coeff_slices):
63
coeff_type = "Approximation" if i == 0 else f"Detail {len(coeffs)-i}"
64
print(f"{coeff_type:>15}: {slice_info}")
65
66
# Verify packing/unpacking
67
reconstructed_coeffs = pywt.array_to_coeffs(coeff_arr, coeff_slices, 'wavedec')
68
print(f"\nReconstruction verification:")
69
for i, (orig, recon) in enumerate(zip(coeffs, reconstructed_coeffs)):
70
error = np.max(np.abs(orig - recon))
71
print(f"Coefficient {i}: max error = {error:.2e}")
72
73
# Demonstrate coefficient processing in array format
74
def process_coeffs_in_array(coeff_arr, coeff_slices, processing_func):
75
"""Process coefficients while in array format."""
76
processed_arr = coeff_arr.copy()
77
78
# Skip approximation coefficients (first slice)
79
for i in range(1, len(coeff_slices)):
80
slice_obj = coeff_slices[i]
81
detail_coeffs = processed_arr[slice_obj]
82
processed_arr[slice_obj] = processing_func(detail_coeffs)
83
84
return processed_arr
85
86
# Example: Soft thresholding in array format
87
def soft_threshold(x, threshold=0.1):
88
return np.sign(x) * np.maximum(np.abs(x) - threshold, 0)
89
90
processed_arr = process_coeffs_in_array(coeff_arr, coeff_slices,
91
lambda x: soft_threshold(x, 0.05))
92
93
# Convert back and reconstruct
94
processed_coeffs = pywt.array_to_coeffs(processed_arr, coeff_slices, 'wavedec')
95
reconstructed_signal = pywt.waverec(processed_coeffs, 'db4')
96
97
# Visualization
98
plt.figure(figsize=(12, 8))
99
100
plt.subplot(3, 1, 1)
101
plt.plot(signal, 'b-', label='Original signal')
102
plt.title('Original Signal')
103
plt.legend()
104
plt.grid(True)
105
106
plt.subplot(3, 1, 2)
107
plt.plot(coeff_arr, 'g-', label='Packed coefficients')
108
plt.title('Coefficients in Array Format')
109
plt.xlabel('Coefficient Index')
110
plt.legend()
111
plt.grid(True)
112
113
# Mark boundaries between coefficient types
114
boundary_indices = [0]
115
for slice_obj in coeff_slices:
116
boundary_indices.append(slice_obj.stop)
117
118
for boundary in boundary_indices[1:-1]:
119
plt.axvline(x=boundary, color='r', linestyle='--', alpha=0.7)
120
121
plt.subplot(3, 1, 3)
122
plt.plot(signal, 'b-', alpha=0.7, label='Original')
123
plt.plot(reconstructed_signal, 'r-', label='Processed & reconstructed')
124
plt.title('Processed Signal (Soft Thresholding)')
125
plt.legend()
126
plt.grid(True)
127
128
plt.tight_layout()
129
plt.show()
130
131
print(f"Processing SNR improvement: {10 * np.log10(np.var(signal) / np.var(signal - reconstructed_signal)):.2f} dB")
132
```
133
134
### Coefficient Raveling
135
136
Functions for converting coefficients to 1D arrays while preserving shape information.
137
138
```python { .api }
139
def ravel_coeffs(coeffs, axes=None):
140
"""
141
Ravel coefficients to 1D array with shape preservation.
142
143
Parameters:
144
- coeffs: Coefficient list from multilevel decomposition
145
- axes: Axes information (for nD coefficients)
146
147
Returns:
148
(coeff_arr, coeff_slices, coeff_shapes) tuple where:
149
- coeff_arr: 1D raveled coefficient array
150
- coeff_slices: Slice information for extraction
151
- coeff_shapes: Original shapes of coefficient arrays
152
"""
153
154
def unravel_coeffs(arr, coeff_slices, coeff_shapes, output_format: str = 'wavedecn'):
155
"""
156
Unravel 1D array back to coefficient format with proper shapes.
157
158
Parameters:
159
- arr: 1D raveled coefficient array
160
- coeff_slices: Slice information from ravel_coeffs
161
- coeff_shapes: Shape information from ravel_coeffs
162
- output_format: Output format specification
163
164
Returns:
165
Coefficient list with proper shapes
166
"""
167
```
168
169
#### Usage Examples
170
171
```python
172
import pywt
173
import numpy as np
174
175
# 2D image example
176
image = np.random.randn(128, 128)
177
coeffs_2d = pywt.wavedec2(image, 'db2', level=4)
178
179
print("2D Coefficient Structure:")
180
print(f"Approximation shape: {coeffs_2d[0].shape}")
181
for i, (cH, cV, cD) in enumerate(coeffs_2d[1:]):
182
level = len(coeffs_2d) - 1 - i
183
print(f"Level {level} details: H{cH.shape}, V{cV.shape}, D{cD.shape}")
184
185
# Ravel all coefficients
186
coeff_arr, coeff_slices, coeff_shapes = pywt.ravel_coeffs(coeffs_2d)
187
print(f"\nRaveled array shape: {coeff_arr.shape}")
188
print(f"Number of coefficient groups: {len(coeff_slices)}")
189
print(f"Coefficient shapes: {coeff_shapes}")
190
191
# Process in raveled format (e.g., apply ML model)
192
def simulate_ml_processing(arr):
193
"""Simulate machine learning processing of coefficients."""
194
# Example: Apply learned transform (simulated with random matrix)
195
np.random.seed(123)
196
# In practice, this would be a trained model
197
transform_matrix = np.eye(len(arr)) + 0.01 * np.random.randn(len(arr), len(arr))
198
return transform_matrix @ arr
199
200
processed_arr = simulate_ml_processing(coeff_arr)
201
202
# Unravel back to coefficient format
203
processed_coeffs = pywt.unravel_coeffs(processed_arr, coeff_slices, coeff_shapes, 'wavedec2')
204
processed_image = pywt.waverec2(processed_coeffs, 'db2')
205
206
print(f"\nImage reconstruction error: {np.max(np.abs(image - processed_image)):.6f}")
207
208
# 3D volume example
209
volume = np.random.randn(32, 32, 32)
210
coeffs_3d = pywt.wavedecn(volume, 'haar', level=3)
211
212
print(f"\n3D Volume Processing:")
213
print(f"Original volume shape: {volume.shape}")
214
print(f"Number of 3D coefficient groups: {len(coeffs_3d)}")
215
216
# Ravel 3D coefficients
217
coeff_arr_3d, coeff_slices_3d, coeff_shapes_3d = pywt.ravel_coeffs(coeffs_3d)
218
print(f"3D raveled array shape: {coeff_arr_3d.shape}")
219
220
# Simple processing: scale down high-frequency components
221
processed_arr_3d = coeff_arr_3d.copy()
222
# Assume first group is approximation, rest are details
223
total_coeffs = len(coeff_arr_3d)
224
approx_end = coeff_slices_3d[1].start if len(coeff_slices_3d) > 1 else total_coeffs
225
processed_arr_3d[approx_end:] *= 0.8 # Reduce detail coefficients
226
227
# Unravel and reconstruct
228
processed_coeffs_3d = pywt.unravel_coeffs(processed_arr_3d, coeff_slices_3d, coeff_shapes_3d, 'wavedecn')
229
processed_volume = pywt.waverecn(processed_coeffs_3d, 'haar')
230
231
print(f"3D volume reconstruction error: {np.max(np.abs(volume - processed_volume)):.6f}")
232
```
233
234
### Coefficient Shape Analysis
235
236
Utilities for analyzing coefficient structures without computing transforms.
237
238
```python { .api }
239
def wavedecn_shapes(shape, wavelet, mode: str = 'symmetric', level: int = None, axes=None):
240
"""
241
Get shapes of coefficient arrays without computing the transform.
242
243
Parameters:
244
- shape: Input data shape
245
- wavelet: Wavelet specification
246
- mode: Signal extension mode
247
- level: Decomposition level (default: maximum possible)
248
- axes: Axes for transform (default: all axes)
249
250
Returns:
251
List of coefficient shapes that would result from wavedecn
252
"""
253
254
def wavedecn_size(shapes):
255
"""
256
Calculate total number of coefficients from shapes.
257
258
Parameters:
259
- shapes: List of coefficient shapes from wavedecn_shapes
260
261
Returns:
262
Total number of coefficients
263
"""
264
```
265
266
#### Usage Examples
267
268
```python
269
import pywt
270
import numpy as np
271
272
# Analyze coefficient structures for different input sizes
273
input_shapes = [(1024,), (512, 512), (64, 64, 64), (32, 32, 32, 100)]
274
wavelet = 'db4'
275
276
print("Coefficient Structure Analysis:")
277
print("=" * 60)
278
279
for shape in input_shapes:
280
ndim = len(shape)
281
max_level = pywt.dwtn_max_level(shape, wavelet) if ndim > 1 else pywt.dwt_max_level(shape[0], pywt.Wavelet(wavelet).dec_len)
282
283
print(f"\nInput shape: {shape}")
284
print(f"Maximum decomposition level: {max_level}")
285
286
# Analyze different decomposition levels
287
for level in range(1, min(max_level + 1, 6)): # Limit to 5 levels for display
288
if ndim == 1:
289
# Use regular DWT functions for 1D
290
test_coeffs = pywt.wavedec(np.zeros(shape), wavelet, level=level)
291
coeff_shapes = [c.shape for c in test_coeffs]
292
total_coeffs = sum(c.size for c in test_coeffs)
293
else:
294
# Use shape analysis for nD
295
coeff_shapes = pywt.wavedecn_shapes(shape, wavelet, level=level)
296
total_coeffs = pywt.wavedecn_size(coeff_shapes)
297
298
compression_ratio = np.prod(shape) / total_coeffs
299
print(f" Level {level}: {len(coeff_shapes)} groups, {total_coeffs} total coeffs, compression ratio: {compression_ratio:.3f}")
300
301
# Memory usage analysis
302
def analyze_memory_usage(shape, wavelet, level):
303
"""Analyze memory usage for different coefficient representations."""
304
305
# Original data size
306
original_size = np.prod(shape) * 8 # 8 bytes per float64
307
308
if len(shape) == 1:
309
coeffs = pywt.wavedec(np.zeros(shape), wavelet, level=level)
310
coeff_sizes = [c.size for c in coeffs]
311
else:
312
coeff_shapes = pywt.wavedecn_shapes(shape, wavelet, level=level)
313
coeff_sizes = [np.prod(s) if isinstance(s, tuple) else sum(np.prod(s) for s in s.values()) for s in coeff_shapes]
314
315
# Coefficient list memory
316
coeffs_size = sum(coeff_sizes) * 8
317
318
# Array format memory (coeffs_to_array)
319
array_size = sum(coeff_sizes) * 8 # Same as coeffs but contiguous
320
321
# Slice overhead (approximate)
322
slice_overhead = len(coeff_sizes) * 64 # Rough estimate
323
324
return {
325
'original': original_size,
326
'coeffs': coeffs_size,
327
'array': array_size + slice_overhead,
328
'compression': original_size / coeffs_size
329
}
330
331
# Memory analysis example
332
test_shape = (1024, 1024)
333
test_level = 5
334
335
memory_info = analyze_memory_usage(test_shape, 'db4', test_level)
336
337
print(f"\nMemory Usage Analysis for {test_shape} at level {test_level}:")
338
print(f"Original data: {memory_info['original'] / 1024**2:.2f} MB")
339
print(f"Coefficient lists: {memory_info['coeffs'] / 1024**2:.2f} MB")
340
print(f"Array format: {memory_info['array'] / 1024**2:.2f} MB")
341
print(f"Compression ratio: {memory_info['compression']:.3f}")
342
343
# Coefficient distribution analysis
344
def analyze_coefficient_distribution(coeffs):
345
"""Analyze the distribution of coefficient values."""
346
if isinstance(coeffs[0], np.ndarray):
347
# 1D coefficients
348
all_coeffs = np.concatenate(coeffs[1:]) # Skip approximation
349
else:
350
# 2D/nD coefficients - extract all detail coefficients
351
all_coeffs = []
352
for level_coeffs in coeffs[1:]:
353
if isinstance(level_coeffs, tuple):
354
# 2D case: (cH, cV, cD)
355
all_coeffs.extend([level_coeffs[0].ravel(), level_coeffs[1].ravel(), level_coeffs[2].ravel()])
356
else:
357
# nD case: dictionary
358
for key, coeff in level_coeffs.items():
359
all_coeffs.append(coeff.ravel())
360
all_coeffs = np.concatenate(all_coeffs)
361
362
return {
363
'mean': np.mean(all_coeffs),
364
'std': np.std(all_coeffs),
365
'min': np.min(all_coeffs),
366
'max': np.max(all_coeffs),
367
'zeros': np.sum(all_coeffs == 0) / len(all_coeffs) * 100,
368
'small': np.sum(np.abs(all_coeffs) < 0.01) / len(all_coeffs) * 100
369
}
370
371
# Test coefficient distribution
372
test_signal = np.sin(2 * np.pi * 5 * np.linspace(0, 1, 1024)) + 0.1 * np.random.randn(1024)
373
test_coeffs = pywt.wavedec(test_signal, 'db8', level=6)
374
dist_info = analyze_coefficient_distribution(test_coeffs)
375
376
print(f"\nCoefficient Distribution Analysis:")
377
print(f"Mean: {dist_info['mean']:.6f}")
378
print(f"Std: {dist_info['std']:.6f}")
379
print(f"Range: [{dist_info['min']:.6f}, {dist_info['max']:.6f}]")
380
print(f"Exact zeros: {dist_info['zeros']:.2f}%")
381
print(f"Small coefficients (|x| < 0.01): {dist_info['small']:.2f}%")
382
```
383
384
### Advanced Coefficient Manipulation
385
386
Advanced utilities for coefficient processing and analysis.
387
388
#### Usage Examples
389
390
```python
391
import pywt
392
import numpy as np
393
import matplotlib.pyplot as plt
394
395
def coefficient_energy_analysis(coeffs):
396
"""Analyze energy distribution across coefficient levels."""
397
if isinstance(coeffs[0], np.ndarray):
398
# 1D case
399
energies = [np.sum(c**2) for c in coeffs]
400
labels = ['Approx'] + [f'Detail {len(coeffs)-i}' for i in range(1, len(coeffs))]
401
else:
402
# 2D case
403
energies = [np.sum(coeffs[0]**2)] # Approximation
404
labels = ['Approximation']
405
406
for i, (cH, cV, cD) in enumerate(coeffs[1:]):
407
level = len(coeffs) - 1 - i
408
energies.extend([np.sum(cH**2), np.sum(cV**2), np.sum(cD**2)])
409
labels.extend([f'H{level}', f'V{level}', f'D{level}'])
410
411
total_energy = sum(energies)
412
energy_percentages = [e/total_energy * 100 for e in energies]
413
414
return labels, energies, energy_percentages
415
416
# Test energy analysis
417
test_image = np.random.randn(256, 256)
418
coeffs_2d = pywt.wavedec2(test_image, 'db4', level=4)
419
420
labels, energies, percentages = coefficient_energy_analysis(coeffs_2d)
421
422
print("Energy Distribution Analysis:")
423
for label, energy, pct in zip(labels, energies, percentages):
424
print(f"{label:>12}: {energy:>10.2f} ({pct:>5.2f}%)")
425
426
# Visualize energy distribution
427
plt.figure(figsize=(12, 6))
428
plt.subplot(1, 2, 1)
429
plt.bar(range(len(labels)), percentages)
430
plt.xticks(range(len(labels)), labels, rotation=45)
431
plt.ylabel('Energy Percentage')
432
plt.title('Coefficient Energy Distribution')
433
plt.grid(True, alpha=0.3)
434
435
# Cumulative energy
436
cumulative = np.cumsum(percentages)
437
plt.subplot(1, 2, 2)
438
plt.plot(range(len(labels)), cumulative, 'o-')
439
plt.xticks(range(len(labels)), labels, rotation=45)
440
plt.ylabel('Cumulative Energy Percentage')
441
plt.title('Cumulative Energy Distribution')
442
plt.grid(True, alpha=0.3)
443
plt.axhline(y=90, color='r', linestyle='--', alpha=0.7, label='90% Energy')
444
plt.axhline(y=95, color='r', linestyle='--', alpha=0.7, label='95% Energy')
445
plt.legend()
446
447
plt.tight_layout()
448
plt.show()
449
450
# Find coefficient threshold for energy retention
451
def find_energy_threshold(coeffs, energy_target=0.9):
452
"""Find threshold that retains specified fraction of energy."""
453
# Collect all detail coefficients
454
all_details = []
455
if isinstance(coeffs[0], np.ndarray):
456
# 1D case
457
all_details = np.concatenate(coeffs[1:])
458
else:
459
# 2D case
460
for cH, cV, cD in coeffs[1:]:
461
all_details.extend([cH.ravel(), cV.ravel(), cD.ravel()])
462
all_details = np.concatenate(all_details)
463
464
# Sort by absolute value (descending)
465
sorted_coeffs = np.sort(np.abs(all_details))[::-1]
466
467
# Find threshold for energy retention
468
total_energy = np.sum(sorted_coeffs**2)
469
target_energy = energy_target * total_energy
470
471
cumulative_energy = np.cumsum(sorted_coeffs**2)
472
threshold_idx = np.argmax(cumulative_energy >= target_energy)
473
threshold = sorted_coeffs[threshold_idx]
474
475
return threshold, threshold_idx / len(sorted_coeffs)
476
477
threshold_90, coeff_fraction = find_energy_threshold(coeffs_2d, 0.9)
478
print(f"\nFor 90% energy retention:")
479
print(f"Threshold: {threshold_90:.6f}")
480
print(f"Coefficients kept: {coeff_fraction:.1%}")
481
```
482
483
## Types
484
485
```python { .api }
486
# Coefficient array formats
487
CoeffArray = np.ndarray # 1D array containing packed coefficients
488
CoeffSlices = List[slice] # Slice objects for extracting individual coefficients
489
CoeffShapes = List[Union[tuple, Dict[str, tuple]]] # Shape information for reconstruction
490
491
# Output format specifications
492
OutputFormat = Literal['wavedec', 'wavedec2', 'wavedecn']
493
494
# Coefficient analysis results
495
EnergyAnalysis = Tuple[List[str], List[float], List[float]] # (labels, energies, percentages)
496
```