or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-reconciliation.mddata-utilities.mdevaluation.mdindex.mdprobabilistic-methods.mdreconciliation-methods.mdvisualization.md

reconciliation-methods.mddocs/

0

# Reconciliation Methods

1

2

Hierarchical reconciliation methods that ensure coherent forecasts across all levels of a hierarchy. Each method implements different approaches to resolving inconsistencies between forecasts at different aggregation levels, with both dense and sparse matrix variants for scalability.

3

4

## Capabilities

5

6

### Base Reconciler Interface

7

8

All reconciliation methods inherit from the `HReconciler` base class, providing a consistent interface across different algorithms.

9

10

```python { .api }

11

class HReconciler:

12

"""Abstract base class for all reconciliation methods."""

13

14

def fit(self, *args, **kwargs):

15

"""Fit the reconciliation method to data."""

16

17

def fit_predict(self, *args, **kwargs):

18

"""Fit and predict in one step."""

19

20

def predict(self, S, y_hat, level=None):

21

"""Generate reconciled predictions."""

22

23

def sample(self, num_samples: int):

24

"""Generate coherent samples from fitted model."""

25

26

def __call__(self, *args, **kwargs):

27

"""Alias for fit_predict."""

28

29

# Key attributes

30

fitted: bool # Whether model is fitted

31

is_sparse_method: bool # Whether method supports sparse matrices

32

insample: bool # Whether method requires historical data

33

```

34

35

### Bottom-Up Reconciliation

36

37

Bottom-up reconciliation aggregates forecasts from the most disaggregated level upward, ensuring perfect coherence by construction.

38

39

```python { .api }

40

class BottomUp:

41

"""

42

Bottom-up reconciliation method.

43

44

Aggregates forecasts from leaf nodes upward through the hierarchy.

45

This is the simplest reconciliation method that maintains perfect coherence.

46

"""

47

48

def __init__(self):

49

"""Initialize BottomUp reconciler."""

50

51

def fit(

52

self,

53

S: np.ndarray,

54

y_hat: np.ndarray,

55

idx_bottom: np.ndarray,

56

y_insample: Optional[np.ndarray] = None,

57

y_hat_insample: Optional[np.ndarray] = None,

58

sigmah: Optional[np.ndarray] = None,

59

intervals_method: Optional[str] = None,

60

num_samples: Optional[int] = None,

61

seed: Optional[int] = None,

62

tags: Optional[dict[str, np.ndarray]] = None

63

):

64

"""

65

Fit the bottom-up reconciliation method.

66

67

Parameters:

68

- S: summing matrix DataFrame

69

- y_hat: base forecasts array

70

- idx_bottom: indices of bottom-level series

71

- y_insample: historical data (not used for BottomUp)

72

- y_hat_insample: in-sample forecasts (not used)

73

- sigmah: forecast standard deviations

74

- intervals_method: method for generating intervals

75

- num_samples: number of samples for probabilistic reconciliation

76

- seed: random seed

77

- tags: hierarchy tags dictionary

78

"""

79

80

def fit_predict(

81

self,

82

S: np.ndarray,

83

y_hat: np.ndarray,

84

idx_bottom: np.ndarray,

85

**kwargs

86

) -> np.ndarray:

87

"""

88

Fit and generate bottom-up reconciled forecasts.

89

90

Returns:

91

Reconciled forecasts array with coherent predictions

92

"""

93

94

class BottomUpSparse:

95

"""Sparse matrix version of BottomUp for large hierarchies."""

96

# Same interface as BottomUp but optimized for sparse matrices

97

```

98

99

### Top-Down Reconciliation

100

101

Top-down reconciliation starts from the top level and disaggregates forecasts downward using historical proportions.

102

103

```python { .api }

104

class TopDown:

105

"""

106

Top-down reconciliation method.

107

108

Disaggregates forecasts from the root level downward using proportions

109

derived from historical data or base forecasts.

110

"""

111

112

def __init__(self, method: str = 'forecast_proportions'):

113

"""

114

Initialize TopDown reconciler.

115

116

Parameters:

117

- method: str, disaggregation method

118

'forecast_proportions': Use base forecast proportions

119

'average_proportions': Use average historical proportions

120

'proportion_averages': Use proportions of historical averages

121

"""

122

123

def fit(

124

self,

125

S: np.ndarray,

126

y_hat: np.ndarray,

127

y_insample: np.ndarray,

128

y_hat_insample: Optional[np.ndarray] = None,

129

sigmah: Optional[np.ndarray] = None,

130

intervals_method: Optional[str] = None,

131

num_samples: Optional[int] = None,

132

seed: Optional[int] = None,

133

tags: Optional[dict[str, np.ndarray]] = None,

134

idx_bottom: Optional[np.ndarray] = None

135

):

136

"""

137

Fit the top-down reconciliation method.

138

139

Parameters:

140

- S: summing matrix DataFrame

141

- y_hat: base forecasts array

142

- y_insample: historical data (required for proportion calculation)

143

- y_hat_insample: in-sample forecasts

144

- sigmah: forecast standard deviations

145

- intervals_method: method for generating intervals

146

- num_samples: number of samples

147

- seed: random seed

148

- tags: hierarchy tags dictionary

149

- idx_bottom: bottom-level series indices

150

"""

151

152

def fit_predict(

153

self,

154

S: np.ndarray,

155

y_hat: np.ndarray,

156

tags: dict[str, np.ndarray],

157

idx_bottom: Optional[np.ndarray] = None,

158

y_insample: Optional[np.ndarray] = None,

159

**kwargs

160

) -> np.ndarray:

161

"""

162

Fit and generate top-down reconciled forecasts.

163

164

Returns:

165

Reconciled forecasts array using top-down approach

166

"""

167

168

class TopDownSparse:

169

"""Sparse matrix version of TopDown for large hierarchies."""

170

# Same interface as TopDown

171

```

172

173

### Middle-Out Reconciliation

174

175

Middle-out reconciliation anchors at a specified middle level, applying bottom-up above and top-down below that level.

176

177

```python { .api }

178

class MiddleOut:

179

"""

180

Middle-out reconciliation method.

181

182

Anchors reconciliation at a middle level of the hierarchy, applying

183

bottom-up reconciliation above and top-down reconciliation below.

184

"""

185

186

def __init__(

187

self,

188

middle_level: str,

189

top_down_method: str = 'forecast_proportions'

190

):

191

"""

192

Initialize MiddleOut reconciler.

193

194

Parameters:

195

- middle_level: str, name of the middle level to anchor at

196

- top_down_method: str, method for top-down portion

197

"""

198

199

def fit_predict(

200

self,

201

S: np.ndarray,

202

y_hat: np.ndarray,

203

tags: dict[str, np.ndarray],

204

y_insample: Optional[np.ndarray] = None,

205

level: Optional[list[int]] = None,

206

intervals_method: Optional[str] = None,

207

**kwargs

208

) -> np.ndarray:

209

"""

210

Generate middle-out reconciled forecasts.

211

212

Returns:

213

Reconciled forecasts using middle-out approach

214

"""

215

216

class MiddleOutSparse:

217

"""Sparse matrix version of MiddleOut for large hierarchies."""

218

# Same interface as MiddleOut

219

```

220

221

### Minimum Trace Reconciliation

222

223

MinTrace uses generalized least squares to find reconciled forecasts that minimize the trace of the covariance matrix.

224

225

```python { .api }

226

class MinTrace:

227

"""

228

Minimum trace reconciliation using generalized least squares.

229

230

Finds reconciled forecasts that minimize the trace of the forecast

231

error covariance matrix subject to aggregation constraints.

232

"""

233

234

def __init__(

235

self,

236

method: str = 'ols',

237

nonnegative: bool = False,

238

mint_shr_ridge: float = 2e-8,

239

num_threads: int = 1

240

):

241

"""

242

Initialize MinTrace reconciler.

243

244

Parameters:

245

- method: str, estimation method

246

'ols': Ordinary least squares (identity covariance)

247

'wls_struct': Weighted least squares with structural scaling

248

'wls_var': Weighted least squares with variance scaling

249

'mint_cov': Use sample covariance matrix

250

'mint_shrink': Use shrinkage covariance estimator

251

- nonnegative: bool, enforce non-negative constraints

252

- mint_shr_ridge: float, ridge parameter for shrinkage estimator

253

- num_threads: int, number of threads for optimization

254

"""

255

256

def fit(

257

self,

258

S: np.ndarray,

259

y_hat: np.ndarray,

260

y_insample: Optional[np.ndarray] = None,

261

y_hat_insample: Optional[np.ndarray] = None,

262

sigmah: Optional[np.ndarray] = None,

263

intervals_method: Optional[str] = None,

264

num_samples: Optional[int] = None,

265

seed: Optional[int] = None,

266

tags: Optional[dict[str, np.ndarray]] = None,

267

idx_bottom: Optional[np.ndarray] = None

268

):

269

"""Fit the MinTrace reconciliation method."""

270

271

def fit_predict(

272

self,

273

S: np.ndarray,

274

y_hat: np.ndarray,

275

idx_bottom: Optional[np.ndarray] = None,

276

y_insample: Optional[np.ndarray] = None,

277

y_hat_insample: Optional[np.ndarray] = None,

278

**kwargs

279

) -> np.ndarray:

280

"""

281

Generate MinTrace reconciled forecasts.

282

283

Returns:

284

Reconciled forecasts using minimum trace approach

285

"""

286

287

class MinTraceSparse:

288

"""Sparse matrix version of MinTrace for large hierarchies."""

289

# Same interface as MinTrace

290

```

291

292

### Optimal Combination

293

294

OptimalCombination is equivalent to specific MinTrace variants, providing an alternative interface for classic optimal reconciliation.

295

296

```python { .api }

297

class OptimalCombination:

298

"""

299

Optimal combination method (equivalent to specific MinTrace variants).

300

301

Provides optimal linear combination of base forecasts to achieve coherence

302

while minimizing forecast error variance.

303

"""

304

305

def __init__(

306

self,

307

method: str = 'ols',

308

nonnegative: bool = False,

309

num_threads: int = 1

310

):

311

"""

312

Initialize OptimalCombination reconciler.

313

314

Parameters:

315

- method: str, combination method ('ols', 'wls_struct')

316

- nonnegative: bool, enforce non-negative constraints

317

- num_threads: int, number of threads for optimization

318

"""

319

320

# Same interface as MinTrace for fit/fit_predict methods

321

```

322

323

### Empirical Risk Minimization

324

325

ERM reconciliation minimizes empirical risk using in-sample errors, with options for regularization.

326

327

```python { .api }

328

class ERM:

329

"""

330

Empirical Risk Minimization reconciliation.

331

332

Minimizes in-sample reconciliation errors using various optimization

333

approaches including closed-form solutions and regularized methods.

334

"""

335

336

def __init__(

337

self,

338

method: str = 'closed',

339

lambda_reg: float = 1e-2

340

):

341

"""

342

Initialize ERM reconciler.

343

344

Parameters:

345

- method: str, optimization method

346

'closed': Closed-form solution

347

'reg': Regularized solution with lasso penalty

348

'reg_bu': Regularized bottom-up solution

349

- lambda_reg: float, regularization parameter for lasso methods

350

"""

351

352

def fit(

353

self,

354

S: pd.DataFrame,

355

y_hat: np.ndarray,

356

y_insample: pd.DataFrame,

357

y_hat_insample: np.ndarray,

358

sigmah: np.ndarray = None,

359

intervals_method: str = None,

360

num_samples: int = None,

361

seed: int = None,

362

tags: dict = None,

363

idx_bottom: np.ndarray = None

364

):

365

"""

366

Fit the ERM reconciliation method.

367

368

Note: ERM requires both historical data and in-sample forecasts.

369

"""

370

371

def fit_predict(

372

self,

373

S: np.ndarray,

374

y_hat: np.ndarray,

375

idx_bottom: Optional[np.ndarray] = None,

376

y_insample: Optional[np.ndarray] = None,

377

y_hat_insample: Optional[np.ndarray] = None,

378

**kwargs

379

) -> np.ndarray:

380

"""

381

Generate ERM reconciled forecasts.

382

383

Returns:

384

Reconciled forecasts using empirical risk minimization

385

"""

386

```

387

388

## Usage Examples

389

390

### Basic Method Usage

391

392

```python

393

from hierarchicalforecast.methods import BottomUp, TopDown, MinTrace

394

395

# Simple bottom-up reconciliation

396

bottom_up = BottomUp()

397

reconciled = bottom_up.fit_predict(S=summing_matrix, y_hat=forecasts, idx_bottom=bottom_indices)

398

399

# Top-down with forecast proportions

400

top_down = TopDown(method='forecast_proportions')

401

reconciled = top_down.fit_predict(

402

S=summing_matrix,

403

y_hat=forecasts,

404

tags=hierarchy_tags,

405

y_insample=historical_data

406

)

407

408

# MinTrace with shrinkage covariance

409

min_trace = MinTrace(method='mint_shrink', nonnegative=True)

410

reconciled = min_trace.fit_predict(

411

S=summing_matrix,

412

y_hat=forecasts,

413

y_insample=historical_data

414

)

415

```

416

417

### Using Sparse Methods for Large Hierarchies

418

419

```python

420

from hierarchicalforecast.methods import BottomUpSparse, MinTraceSparse

421

422

# For very large hierarchies, use sparse variants

423

bottom_up_sparse = BottomUpSparse()

424

min_trace_sparse = MinTraceSparse(method='ols')

425

426

# Same interface, but optimized for sparse matrices

427

reconciled = bottom_up_sparse.fit_predict(S=sparse_summing_matrix, y_hat=forecasts, idx_bottom=bottom_indices)

428

```

429

430

### Method Comparison

431

432

```python

433

# Compare multiple methods

434

methods = {

435

'BottomUp': BottomUp(),

436

'TopDown_FP': TopDown(method='forecast_proportions'),

437

'TopDown_AP': TopDown(method='average_proportions'),

438

'MinTrace_OLS': MinTrace(method='ols'),

439

'MinTrace_WLS': MinTrace(method='wls_struct'),

440

'ERM_Closed': ERM(method='closed')

441

}

442

443

results = {}

444

for name, method in methods.items():

445

results[name] = method.fit_predict(

446

S=summing_matrix,

447

y_hat=forecasts,

448

y_insample=historical_data,

449

y_hat_insample=insample_forecasts

450

)

451

```