or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

arma-methods.mdcorrelation-signal.mdeigenvalue-methods.mdindex.mdlinear-prediction.mdmath-tools.mdnonparametric-methods.mdparametric-ar.mdwindow-functions.md

nonparametric-methods.mddocs/

0

# Non-parametric Methods

1

2

Classical spectral estimation techniques that do not assume an underlying parametric model. These methods are based on Fourier analysis and provide robust spectral estimates suitable for a wide range of signals and applications.

3

4

## Capabilities

5

6

### Periodogram Methods

7

8

Direct Fourier-based power spectral density estimation with various windowing and detrending options.

9

10

```python { .api }

11

def speriodogram(x, NFFT=None, detrend=True, sampling=1., scale_by_freq=True, window='hamming', axis=0):

12

"""

13

Simple periodogram PSD estimation.

14

15

Parameters:

16

- x: array-like, input time series data

17

- NFFT: int, FFT length (None uses len(x))

18

- detrend: bool or str, detrending option (True, False, 'mean', 'linear')

19

- sampling: float, sampling frequency

20

- scale_by_freq: bool, scale PSD by frequency

21

- window: str, window function name ('hamming', 'hanning', 'blackman', etc.)

22

- axis: int, axis along which to compute PSD

23

24

Returns:

25

array: Power spectral density values

26

"""

27

28

class Periodogram(data, sampling=1., NFFT=None, window='hamming', detrend='mean', scale_by_freq=True):

29

"""

30

Basic periodogram class for PSD estimation.

31

32

Parameters:

33

- data: array-like, input time series

34

- sampling: float, sampling frequency

35

- NFFT: int, FFT length for PSD computation

36

- window: str, window function ('hanning', 'hamming', 'blackman', etc.)

37

- detrend: str, detrending method ('mean', 'linear', None)

38

- scale_by_freq: bool, scale PSD by frequency

39

"""

40

```

41

42

### Welch's Method

43

44

Averaged periodogram method that reduces variance by averaging multiple overlapping segments.

45

46

```python { .api }

47

def WelchPeriodogram(data, NFFT=None, sampling=1., **kargs):

48

"""

49

Welch's averaged periodogram method.

50

51

Parameters:

52

- data: array-like, input time series data

53

- NFFT: int, segment length for averaging

54

- sampling: float, sampling frequency

55

- **kargs: additional parameters (window, overlap, detrend)

56

57

Returns:

58

tuple: (frequencies array, PSD values array)

59

"""

60

```

61

62

### Daniell Periodogram

63

64

Periodogram with frequency domain smoothing using Daniell kernel for variance reduction.

65

66

```python { .api }

67

def DaniellPeriodogram(data, P, NFFT=None, detrend='mean', sampling=1., window_params={}):

68

"""

69

Daniell's periodogram with smoothing.

70

71

Parameters:

72

- data: array-like, input time series data

73

- P: int, smoothing parameter (Daniell kernel width)

74

- NFFT: int, FFT length

75

- detrend: str, detrending method

76

- sampling: float, sampling frequency

77

- window_params: dict, window function parameters

78

79

Returns:

80

tuple: (frequencies array, smoothed PSD array)

81

"""

82

83

class pdaniell(data, P, sampling=1., NFFT=None, detrend='mean', scale_by_freq=True):

84

"""

85

Daniell periodogram class.

86

87

Parameters:

88

- data: array-like, input time series

89

- P: int, Daniell smoothing parameter

90

- sampling: float, sampling frequency

91

- NFFT: int, FFT length for PSD computation

92

- detrend: str, detrending method

93

- scale_by_freq: bool, scale PSD by frequency

94

"""

95

```

96

97

### Correlogram Method

98

99

Blackman-Tukey method using windowed autocorrelation function for spectral estimation.

100

101

```python { .api }

102

103

class pcorrelogram(data, sampling=1., lag=-1, window='hamming', NFFT=None, scale_by_freq=True, detrend=None):

104

"""

105

Correlogram PSD estimation class.

106

107

Parameters:

108

- data: array-like, input time series

109

- sampling: float, sampling frequency

110

- lag: int, maximum correlation lag

111

- window: str, lag window function

112

- NFFT: int, FFT length for PSD computation

113

- scale_by_freq: bool, scale PSD by frequency

114

- detrend: str, detrending method

115

"""

116

```

117

118

### Multi-Taper Method

119

120

Thomson's multi-taper method using discrete prolate spheroidal sequences for optimal spectral estimation.

121

122

```python { .api }

123

def pmtm(x, NW=None, k=None, NFFT=None, e=None, v=None, method="adapt", show=False):

124

"""

125

Multi-taper method PSD estimation.

126

127

Parameters:

128

- x: array-like, input time series data

129

- NW: float, time-bandwidth product (typically 2.5-4)

130

- k: int, number of tapers (typically 2*NW - 1)

131

- NFFT: int, FFT length

132

- e: array-like, pre-computed tapers (DPSS sequences)

133

- v: array-like, eigenvalues of tapers

134

- method: str, averaging method ('adapt', 'unity', 'eigen')

135

- show: bool, show intermediate plots

136

137

Returns:

138

tuple: (PSD array, frequencies array)

139

"""

140

141

def dpss(N, NW=None, k=None):

142

"""

143

Discrete prolate spheroidal sequences (Slepian tapers).

144

145

Parameters:

146

- N: int, sequence length

147

- NW: float, time-bandwidth product

148

- k: int, number of sequences to compute

149

150

Returns:

151

tuple: (tapers array, eigenvalues array)

152

"""

153

154

class MultiTapering(Spectrum):

155

"""

156

Multi-taper PSD estimation class.

157

158

Parameters:

159

- data: array-like, input time series

160

- NW: float, time-bandwidth product

161

- k: int, number of tapers

162

- NFFT: int, FFT length

163

- sampling: float, sampling frequency

164

"""

165

```

166

167

## Usage Examples

168

169

### Basic Periodogram Analysis

170

171

```python

172

import spectrum

173

import numpy as np

174

import matplotlib.pyplot as plt

175

176

# Generate test signal with multiple components

177

fs = 1000 # Sampling frequency

178

T = 1.0 # Duration

179

t = np.linspace(0, T, int(fs*T), False)

180

181

# Signal: two sinusoids + noise

182

f1, f2 = 50, 120 # Hz

183

signal = (np.sin(2*np.pi*f1*t) + 0.5*np.sin(2*np.pi*f2*t) +

184

0.2*np.random.randn(len(t)))

185

186

# Different periodogram methods

187

p_basic = spectrum.Periodogram(signal, sampling=fs)

188

p_welch = spectrum.WelchPeriodogram(signal, NFFT=256, sampling=fs)

189

p_daniell = spectrum.pdaniell(signal, P=5, sampling=fs)

190

191

# Plot comparison

192

plt.figure(figsize=(12, 10))

193

194

plt.subplot(3, 1, 1)

195

plt.plot(t[:200], signal[:200])

196

plt.title('Input Signal (first 200 samples)')

197

plt.xlabel('Time (s)')

198

plt.ylabel('Amplitude')

199

plt.grid(True)

200

201

plt.subplot(3, 1, 2)

202

freqs = np.linspace(0, fs/2, len(p_basic.psd))

203

plt.semilogy(freqs, p_basic.psd, label='Basic Periodogram')

204

plt.axvline(f1, color='r', linestyle='--', alpha=0.7, label=f'f1={f1}Hz')

205

plt.axvline(f2, color='r', linestyle='--', alpha=0.7, label=f'f2={f2}Hz')

206

plt.xlabel('Frequency (Hz)')

207

plt.ylabel('PSD')

208

plt.title('Basic Periodogram')

209

plt.legend()

210

plt.grid(True)

211

plt.xlim(0, 200)

212

213

plt.subplot(3, 1, 3)

214

freqs_welch, psd_welch = p_welch

215

plt.semilogy(freqs_welch, psd_welch, 'g-', label='Welch Method')

216

freqs_daniell = np.linspace(0, fs/2, len(p_daniell.psd))

217

plt.semilogy(freqs_daniell, p_daniell.psd, 'r-', label='Daniell Method')

218

plt.axvline(f1, color='k', linestyle='--', alpha=0.7)

219

plt.axvline(f2, color='k', linestyle='--', alpha=0.7)

220

plt.xlabel('Frequency (Hz)')

221

plt.ylabel('PSD')

222

plt.title('Welch vs Daniell Methods')

223

plt.legend()

224

plt.grid(True)

225

plt.xlim(0, 200)

226

227

plt.tight_layout()

228

plt.show()

229

```

230

231

### Multi-Taper Method Analysis

232

233

```python

234

import spectrum

235

import numpy as np

236

import matplotlib.pyplot as plt

237

238

# Generate signal with closely spaced frequencies

239

fs = 500

240

N = 500

241

t = np.arange(N) / fs

242

243

# Two closely spaced sinusoids + noise

244

f1, f2 = 100, 105 # 5 Hz apart

245

A1, A2 = 1.0, 0.8

246

noise_level = 0.3

247

248

signal = (A1*np.sin(2*np.pi*f1*t) + A2*np.sin(2*np.pi*f2*t) +

249

noise_level*np.random.randn(N))

250

251

# Multi-taper analysis with different parameters

252

NW_values = [2.5, 4.0, 6.0]

253

colors = ['blue', 'red', 'green']

254

255

plt.figure(figsize=(12, 10))

256

257

# Plot signal

258

plt.subplot(2, 2, 1)

259

plt.plot(t, signal)

260

plt.title(f'Signal: {f1}Hz + {f2}Hz + noise')

261

plt.xlabel('Time (s)')

262

plt.ylabel('Amplitude')

263

plt.grid(True)

264

265

# Compare with basic periodogram

266

plt.subplot(2, 2, 2)

267

p_period = spectrum.Periodogram(signal, sampling=fs)

268

freqs = np.linspace(0, fs/2, len(p_period.psd))

269

plt.semilogy(freqs, p_period.psd, 'k-', alpha=0.7, label='Periodogram')

270

plt.axvline(f1, color='r', linestyle=':', alpha=0.8)

271

plt.axvline(f2, color='r', linestyle=':', alpha=0.8)

272

plt.xlim(90, 115)

273

plt.xlabel('Frequency (Hz)')

274

plt.ylabel('PSD')

275

plt.title('Basic Periodogram')

276

plt.legend()

277

plt.grid(True)

278

279

# Multi-taper with different NW values

280

plt.subplot(2, 2, 3)

281

for i, NW in enumerate(NW_values):

282

k = int(2*NW - 1) # Number of tapers

283

psd_mt, freqs_mt = spectrum.pmtm(signal, NW=NW, k=k, NFFT=1024)

284

plt.semilogy(freqs_mt*fs, psd_mt, color=colors[i],

285

label=f'NW={NW}, k={k}', linewidth=2)

286

287

plt.axvline(f1, color='r', linestyle=':', alpha=0.8)

288

plt.axvline(f2, color='r', linestyle=':', alpha=0.8)

289

plt.xlim(90, 115)

290

plt.xlabel('Frequency (Hz)')

291

plt.ylabel('PSD')

292

plt.title('Multi-Taper Method (Different NW)')

293

plt.legend()

294

plt.grid(True)

295

296

# Taper visualization

297

plt.subplot(2, 2, 4)

298

NW = 4.0

299

k = int(2*NW - 1)

300

tapers, eigenvals = spectrum.dpss(N, NW=NW, k=k)

301

for i in range(min(4, k)): # Show first 4 tapers

302

plt.plot(tapers[:, i], label=f'Taper {i+1} (λ={eigenvals[i]:.3f})')

303

plt.xlabel('Sample')

304

plt.ylabel('Amplitude')

305

plt.title(f'DPSS Tapers (N={N}, NW={NW})')

306

plt.legend()

307

plt.grid(True)

308

309

plt.tight_layout()

310

plt.show()

311

312

# Print taper properties

313

print(f"Multi-taper parameters: NW={NW}, k={k}")

314

print(f"Frequency resolution: {2*NW/N*fs:.2f} Hz")

315

print(f"Eigenvalue concentration: {np.sum(eigenvals > 0.9)}/{k} tapers > 0.9")

316

```

317

318

### Correlogram Method

319

320

```python

321

import spectrum

322

import numpy as np

323

import matplotlib.pyplot as plt

324

325

# Generate AR signal for correlogram analysis

326

N = 256

327

# AR(2) coefficients creating spectral peak around 0.2-0.3 normalized freq

328

ar_coeffs = [1, -1.6, 0.81] # Poles near unit circle

329

noise = np.random.randn(N)

330

331

# Generate AR signal

332

from scipy import signal

333

ar_signal = signal.lfilter([1], ar_coeffs, noise)

334

335

# Different correlation lags for correlogram

336

lag_values = [20, 40, 80]

337

windows = ['rectangular', 'hamming', 'blackman']

338

339

plt.figure(figsize=(15, 10))

340

341

# Original signal

342

plt.subplot(2, 3, 1)

343

plt.plot(ar_signal)

344

plt.title('AR(2) Signal')

345

plt.xlabel('Sample')

346

plt.ylabel('Amplitude')

347

plt.grid(True)

348

349

# Autocorrelation function

350

plt.subplot(2, 3, 2)

351

max_lag = 50

352

autocorr = spectrum.CORRELATION(ar_signal, maxlags=max_lag, norm='unbiased')

353

lags = np.arange(-max_lag, max_lag+1)

354

plt.plot(lags, autocorr)

355

plt.title('Autocorrelation Function')

356

plt.xlabel('Lag')

357

plt.ylabel('Correlation')

358

plt.grid(True)

359

360

# Different lag lengths

361

plt.subplot(2, 3, 4)

362

for i, lag in enumerate(lag_values):

363

p_corr = spectrum.pcorrelogram(ar_signal, lag=lag, window='hamming')

364

freqs = np.linspace(0, 0.5, len(p_corr.psd))

365

plt.semilogy(freqs, p_corr.psd, label=f'Lag={lag}', linewidth=2)

366

plt.xlabel('Normalized Frequency')

367

plt.ylabel('PSD')

368

plt.title('Correlogram: Different Lag Lengths')

369

plt.legend()

370

plt.grid(True)

371

372

# Different windows

373

plt.subplot(2, 3, 5)

374

lag = 40

375

for i, window in enumerate(windows):

376

p_corr = spectrum.pcorrelogram(ar_signal, lag=lag, window=window)

377

freqs = np.linspace(0, 0.5, len(p_corr.psd))

378

plt.semilogy(freqs, p_corr.psd, label=f'{window.capitalize()}', linewidth=2)

379

plt.xlabel('Normalized Frequency')

380

plt.ylabel('PSD')

381

plt.title(f'Correlogram: Different Windows (lag={lag})')

382

plt.legend()

383

plt.grid(True)

384

385

# Comparison with parametric method

386

plt.subplot(2, 3, 6)

387

p_corr = spectrum.pcorrelogram(ar_signal, lag=40, window='hamming')

388

p_burg = spectrum.pburg(ar_signal, order=10)

389

freqs = np.linspace(0, 0.5, len(p_corr.psd))

390

plt.semilogy(freqs, p_corr.psd, 'b-', label='Correlogram', linewidth=2)

391

plt.semilogy(freqs, p_burg.psd, 'r--', label='Burg AR(10)', linewidth=2)

392

plt.xlabel('Normalized Frequency')

393

plt.ylabel('PSD')

394

plt.title('Correlogram vs Parametric')

395

plt.legend()

396

plt.grid(True)

397

398

plt.tight_layout()

399

plt.show()

400

```

401

402

### Method Comparison for Different Signal Types

403

404

```python

405

import spectrum

406

import numpy as np

407

import matplotlib.pyplot as plt

408

409

def compare_methods(signal, title, fs=1.0):

410

"""Compare different non-parametric methods on a given signal."""

411

412

# Method parameters

413

methods = {

414

'Periodogram': spectrum.Periodogram(signal, sampling=fs),

415

'Correlogram': spectrum.pcorrelogram(signal, lag=len(signal)//4,

416

window='hamming'),

417

'Multi-taper': None # Will compute separately

418

}

419

420

# Multi-taper method

421

NW = 3.0

422

k = int(2*NW - 1)

423

psd_mt, freqs_mt = spectrum.pmtm(signal, NW=NW, k=k, NFFT=512)

424

425

plt.figure(figsize=(12, 8))

426

427

# Plot signal

428

plt.subplot(2, 2, 1)

429

t = np.arange(len(signal)) / fs

430

plt.plot(t, signal)

431

plt.title(f'{title} - Time Domain')

432

plt.xlabel('Time (s)')

433

plt.ylabel('Amplitude')

434

plt.grid(True)

435

436

# Plot spectra

437

plt.subplot(2, 2, 2)

438

439

# Periodogram

440

freqs = np.linspace(0, fs/2, len(methods['Periodogram'].psd))

441

plt.semilogy(freqs, methods['Periodogram'].psd, 'b-',

442

label='Periodogram', linewidth=2)

443

444

# Correlogram

445

freqs_corr = np.linspace(0, fs/2, len(methods['Correlogram'].psd))

446

plt.semilogy(freqs_corr, methods['Correlogram'].psd, 'g--',

447

label='Correlogram', linewidth=2)

448

449

# Multi-taper

450

plt.semilogy(freqs_mt*fs, psd_mt, 'r:',

451

label='Multi-taper', linewidth=2)

452

453

plt.xlabel('Frequency (Hz)')

454

plt.ylabel('PSD')

455

plt.title(f'{title} - PSD Comparison')

456

plt.legend()

457

plt.grid(True)

458

459

plt.tight_layout()

460

plt.show()

461

462

# Test different signal types

463

fs = 100

464

465

# 1. Narrow-band signal

466

N1 = 300

467

t1 = np.arange(N1) / fs

468

signal1 = np.sin(2*np.pi*10*t1) + 0.3*np.random.randn(N1)

469

compare_methods(signal1, "Narrow-band Signal (10 Hz)", fs)

470

471

# 2. Wide-band signal

472

N2 = 400

473

signal2 = np.random.randn(N2) # White noise

474

# Add some color with filtering

475

from scipy import signal as sp_signal

476

b, a = sp_signal.butter(4, 0.3, 'low')

477

signal2 = sp_signal.lfilter(b, a, signal2)

478

compare_methods(signal2, "Wide-band Colored Noise", fs)

479

480

# 3. Multi-component signal

481

N3 = 500

482

t3 = np.arange(N3) / fs

483

signal3 = (np.sin(2*np.pi*5*t3) + 0.8*np.sin(2*np.pi*15*t3) +

484

0.6*np.sin(2*np.pi*25*t3) + 0.2*np.random.randn(N3))

485

compare_methods(signal3, "Multi-component Signal", fs)

486

```