dfm-python 0.2.0__py3-none-any.whl → 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
dfm_python/__init__.py CHANGED
@@ -50,7 +50,7 @@ Example (Low-level API - For advanced usage):
50
50
  For detailed documentation, see the README.md file and the tutorial notebooks/scripts.
51
51
  """
52
52
 
53
- __version__ = "0.2.0"
53
+ __version__ = "0.2.2"
54
54
 
55
55
  from .config import DFMConfig, SeriesConfig, BlockConfig, Params, DEFAULT_GLOBAL_BLOCK_NAME
56
56
  from .config import (
@@ -69,7 +69,7 @@ from .kalman import run_kf, skf, fis, miss_data
69
69
  from .news import update_nowcast, news_dfm, para_const
70
70
 
71
71
  # Import high-level API (extends core DFM with convenience methods)
72
- from .dfm_api import DFM, _dfm_instance, from_yaml, from_spec, from_spec_df, from_dict
72
+ from .api import DFM, _dfm_instance, from_yaml, from_spec, from_spec_df, from_dict
73
73
 
74
74
  # Module-level API: expose singleton instance methods and properties
75
75
  # This allows: import dfm_python as dfm; dfm.load_config(...); dfm.result
@@ -1,7 +1,8 @@
1
- """High-level API for Dynamic Factor Model.
1
+ """High-level API for Dynamic Factor Model (convenience layer).
2
2
 
3
- This module provides an object-oriented interface for DFM estimation.
4
- Use this for a simpler, more intuitive API compared to the functional interface.
3
+ This module provides an object-oriented interface for DFM estimation
4
+ on top of the core `DFM` in `dfm.py`. Use this for a simpler, more
5
+ intuitive workflow:
5
6
 
6
7
  Example:
7
8
  >>> import dfm_python as dfm
@@ -16,7 +17,7 @@ Example:
16
17
  >>> factors = result.Z
17
18
  """
18
19
 
19
- from typing import Optional, Union, Dict, Any
20
+ from typing import Optional, Union, Dict, Any, Tuple
20
21
  from pathlib import Path
21
22
  import numpy as np
22
23
  import pandas as pd
@@ -29,6 +30,7 @@ from .config import (
29
30
  MergedConfigSource,
30
31
  )
31
32
  from .dfm import DFM as _DFMCore, DFMResult
33
+ from .core.helpers import safe_get_method, safe_get_attr
32
34
 
33
35
 
34
36
  class DFM(_DFMCore):
@@ -84,53 +86,8 @@ class DFM(_DFMCore):
84
86
  ) -> 'DFM':
85
87
  """Load configuration from various sources.
86
88
 
87
- This method provides a unified interface for loading configurations
88
- from YAML files, dictionaries, spec CSV files, or Hydra configs.
89
- It also supports merging configurations (e.g., YAML base + Spec CSV series).
90
-
91
- Parameters
92
- ----------
93
- source : str, Path, dict, DFMConfig, or ConfigSource, optional
94
- Configuration source. If DFMConfig, used directly.
95
- If str/Path, treated as YAML file path.
96
- If dict, treated as dictionary config.
97
- If ConfigSource, used directly.
98
- yaml : str or Path, optional
99
- Explicit YAML file path
100
- mapping : dict, optional
101
- Explicit dictionary config
102
- spec : str or Path, optional
103
- Explicit spec CSV file path (series definitions only)
104
- hydra : DictConfig or dict, optional
105
- Explicit Hydra config
106
- base : str, Path, dict, or ConfigSource, optional
107
- Base configuration (for merging). Used with `override`.
108
- override : str, Path, dict, or ConfigSource, optional
109
- Override configuration (for merging). Used with `base`.
110
- Series from override replace base series.
111
-
112
- Returns
113
- -------
114
- DFM
115
- Self for method chaining.
116
-
117
- Examples
118
- --------
119
- >>> # From YAML file
120
- >>> dfm.load_config('config/default.yaml')
121
- >>>
122
- >>> # From dictionary
123
- >>> dfm.load_config({'series': [...], 'clock': 'm'})
124
- >>>
125
- >>> # Explicit keyword arguments
126
- >>> dfm.load_config(yaml='config/default.yaml')
127
- >>> dfm.load_config(spec='data/spec.csv')
128
- >>>
129
- >>> # Merge YAML base + Spec CSV series
130
- >>> dfm.load_config(base='config/default.yaml', override='data/spec.csv')
131
- >>>
132
- >>> # Direct DFMConfig object
133
- >>> dfm.load_config(DFMConfig(...))
89
+ Unified interface for YAML files, dictionaries, spec CSV, or Hydra configs.
90
+ Supports merging configurations (e.g., YAML base + Spec CSV series).
134
91
  """
135
92
  # Handle direct DFMConfig
136
93
  if isinstance(source, DFMConfig):
@@ -171,29 +128,7 @@ class DFM(_DFMCore):
171
128
  data_path: Optional[Union[str, Path]] = None,
172
129
  data: Optional[np.ndarray] = None,
173
130
  **kwargs) -> 'DFM':
174
- """Load data from file or use provided array.
175
-
176
- Parameters
177
- ----------
178
- data_path : str or Path, optional
179
- Path to data file (CSV). If None, must provide `data`.
180
- data : np.ndarray, optional
181
- Data matrix directly (T x N). If None, must provide `data_path`.
182
- **kwargs
183
- Additional arguments passed to `load_data()`:
184
- - sample_start: Start date for data sampling
185
- - sample_end: End date for data sampling
186
-
187
- Returns
188
- -------
189
- DFM
190
- Self for method chaining.
191
-
192
- Example:
193
- >>> dfm.load_data('data/sample_data.csv')
194
- >>> # or
195
- >>> dfm.load_data(data=X, sample_start=pd.Timestamp('2000-01-01'))
196
- """
131
+ """Load data from file or use provided array."""
197
132
  if self._config is None:
198
133
  raise ValueError("Configuration must be loaded before loading data. "
199
134
  "Call load_config() first.")
@@ -203,14 +138,12 @@ class DFM(_DFMCore):
203
138
  data_path, self._config, **kwargs
204
139
  )
205
140
  elif data is not None:
206
- # If data is provided directly, we still need to transform it
207
- # For now, assume it's already in the right format
208
- # In the future, we might want to add transform_data() call here
141
+ # If data is provided directly, we still need a time index
209
142
  self._data = data
210
143
  if 'time' in kwargs:
211
144
  self._time = kwargs['time']
212
145
  else:
213
- # Generate default time index
146
+ # Generate default monthly-end index
214
147
  self._time = pd.date_range(
215
148
  start='2000-01-01',
216
149
  periods=len(data),
@@ -226,25 +159,7 @@ class DFM(_DFMCore):
226
159
  threshold: Optional[float] = None,
227
160
  max_iter: Optional[int] = None,
228
161
  **kwargs) -> 'DFM':
229
- """Train the DFM model.
230
-
231
- Parameters
232
- ----------
233
- threshold : float, optional
234
- EM convergence threshold. If None, uses config.threshold.
235
- max_iter : int, optional
236
- Maximum EM iterations. If None, uses config.max_iter.
237
- **kwargs
238
- Additional arguments (for future extensibility).
239
-
240
- Returns
241
- -------
242
- DFM
243
- Self for method chaining.
244
-
245
- Example:
246
- >>> dfm.train(threshold=1e-4, max_iter=100)
247
- """
162
+ """Train the DFM model using EM algorithm."""
248
163
  if self._config is None:
249
164
  raise ValueError("Configuration must be loaded before training. "
250
165
  "Call load_config() first.")
@@ -262,23 +177,16 @@ class DFM(_DFMCore):
262
177
  # Attach metadata for convenient OOP access
263
178
  if self._time is not None:
264
179
  self._result.time_index = self._time
265
- if hasattr(self._config, 'get_series_ids'):
266
- self._result.series_ids = self._config.get_series_ids()
267
- if hasattr(self._config, 'block_names'):
268
- self._result.block_names = self._config.block_names
180
+ series_ids = safe_get_method(self._config, 'get_series_ids')
181
+ if series_ids is not None:
182
+ self._result.series_ids = series_ids
183
+ block_names = safe_get_attr(self._config, 'block_names')
184
+ if block_names is not None:
185
+ self._result.block_names = block_names
269
186
  return self
270
187
 
271
188
  def reset(self) -> 'DFM':
272
- """Reset all state (config, data, results).
273
-
274
- Returns
275
- -------
276
- DFM
277
- Self for method chaining.
278
-
279
- Example:
280
- >>> dfm.reset() # Clear everything
281
- """
189
+ """Reset all state (config, data, results)."""
282
190
  super().__init__()
283
191
  self._time = None
284
192
  self._original_data = None
@@ -287,24 +195,29 @@ class DFM(_DFMCore):
287
195
  # ---------------------------------------------------------------------
288
196
  # Inference API
289
197
  # ---------------------------------------------------------------------
290
- def predict(self, horizon: int = 12, *, return_series: bool = True, return_factors: bool = True):
198
+ def predict(self, horizon: int = 12, *, return_series: bool = True, return_factors: bool = True) -> Union[Tuple[np.ndarray, np.ndarray], np.ndarray]:
291
199
  """Forecast series and/or factors for a given horizon using the trained model.
292
200
 
293
201
  Parameters
294
202
  ----------
295
203
  horizon : int
296
- Number of periods to forecast.
204
+ Number of periods ahead to forecast (default: 12)
297
205
  return_series : bool
298
- If True, return forecasted observed series (denormalized).
206
+ Whether to return forecasted series (default: True)
299
207
  return_factors : bool
300
- If True, return forecasted latent factors.
301
-
208
+ Whether to return forecasted factors (default: True)
209
+
302
210
  Returns
303
211
  -------
304
- tuple | np.ndarray
305
- - If both True: (X_forecast, Z_forecast)
306
- - If only factors: Z_forecast
307
- - If only series: X_forecast
212
+ If both return_series and return_factors are True:
213
+ Tuple[np.ndarray, np.ndarray]
214
+ (X_forecast, Z_forecast) where X_forecast is (horizon x N) and Z_forecast is (horizon x m)
215
+ If only return_series is True:
216
+ np.ndarray
217
+ X_forecast (horizon x N)
218
+ If only return_factors is True:
219
+ np.ndarray
220
+ Z_forecast (horizon x m)
308
221
  """
309
222
  if self._result is None:
310
223
  raise ValueError("Model must be trained before calling predict(). Call train() first.")
@@ -335,26 +248,7 @@ class DFM(_DFMCore):
335
248
 
336
249
  def plot(self, *, kind: str = 'factor', factor_index: int = 0, forecast_horizon: Optional[int] = None,
337
250
  save_path: Optional[Union[str, Path]] = None, show: bool = False):
338
- """Quick plotting utility for common visualizations.
339
-
340
- Parameters
341
- ----------
342
- kind : str
343
- What to plot. Supported: 'factor' (single factor time series).
344
- factor_index : int
345
- Which factor index to plot (default: 0 = first/global).
346
- forecast_horizon : int, optional
347
- If provided, overlay deterministic factor forecast.
348
- save_path : str or Path, optional
349
- If provided, save the figure to this path.
350
- show : bool
351
- If True, display the figure (useful in notebooks).
352
-
353
- Returns
354
- -------
355
- Path | None
356
- Save path if saved, else None.
357
- """
251
+ """Quick plotting utility for common visualizations."""
358
252
  if self._result is None:
359
253
  raise ValueError("No results to plot. Train the model first.")
360
254
  try:
@@ -379,7 +273,7 @@ class DFM(_DFMCore):
379
273
  Z_fore = self.predict(forecast_horizon, return_series=False, return_factors=True)
380
274
  # Build forecast dates
381
275
  last_date = time_hist[-1]
382
- # Use same monthly spacing; if freq missing, fallback to monthly
276
+ # Use same monthly-end spacing; if freq missing, fallback to monthly-end
383
277
  try:
384
278
  forecast_dates = pd.date_range(start=last_date + pd.tseries.frequencies.to_offset('ME'),
385
279
  periods=forecast_horizon, freq='ME')
@@ -391,7 +285,6 @@ class DFM(_DFMCore):
391
285
 
392
286
  title_block = None
393
287
  if hasattr(self._result, 'block_names') and self._result.block_names:
394
- # factor grouping is block-wise; use index 0 as 'Global' heuristic
395
288
  title_block = self._result.block_names[0] if factor_index == 0 else None
396
289
  plt.title(f"Factor {factor_index}" + (f" ({title_block})" if title_block else ""))
397
290
  plt.xlabel("Date")
@@ -428,13 +321,7 @@ def load_config(
428
321
  base: Optional[Union[str, Path, Dict[str, Any], ConfigSource]] = None,
429
322
  override: Optional[Union[str, Path, Dict[str, Any], ConfigSource]] = None,
430
323
  ) -> DFM:
431
- """Load configuration (module-level convenience function).
432
-
433
- This is a convenience function that uses the global DFM instance.
434
- For multiple models, create separate DFM() instances.
435
-
436
- See DFM.load_config() for parameter documentation.
437
- """
324
+ """Load configuration (module-level convenience function)."""
438
325
  return _dfm_instance.load_config(
439
326
  source=source,
440
327
  yaml=yaml,
@@ -477,58 +364,12 @@ def reset() -> DFM:
477
364
 
478
365
  # Convenience constructors for cleaner API
479
366
  def from_yaml(yaml_path: Union[str, Path]) -> DFM:
480
- """Load configuration from YAML file (convenience constructor).
481
-
482
- This is a cleaner alternative to `load_config(yaml=...)`.
483
-
484
- Parameters
485
- ----------
486
- yaml_path : str or Path
487
- Path to YAML configuration file
488
-
489
- Returns
490
- -------
491
- DFM
492
- DFM instance for method chaining
493
-
494
- Examples
495
- --------
496
- >>> import dfm_python as dfm
497
- >>> dfm.from_yaml('config/default.yaml')
498
- >>> dfm.load_data('data/sample_data.csv')
499
- >>> dfm.train(max_iter=1)
500
- """
367
+ """Load configuration from YAML file (convenience constructor)."""
501
368
  return _dfm_instance.load_config(yaml=yaml_path)
502
369
 
503
370
 
504
371
  def from_spec(spec_path: Union[str, Path], params: Optional[Params] = None) -> DFM:
505
- """Load configuration from spec CSV file (convenience constructor).
506
-
507
- This is a cleaner alternative to `load_config(config=params, spec=...)`.
508
- The spec CSV defines all series and their block memberships.
509
- The params object (optional) provides main settings (threshold, max_iter, etc.).
510
-
511
- Parameters
512
- ----------
513
- spec_path : str or Path
514
- Path to spec CSV file (series definitions)
515
- params : Params, optional
516
- Global estimation parameters. If None, uses defaults from Params().
517
-
518
- Returns
519
- -------
520
- DFM
521
- DFM instance for method chaining
522
-
523
- Examples
524
- --------
525
- >>> from dfm_python.config import Params
526
- >>> import dfm_python as dfm
527
- >>> params = Params(max_iter=100, threshold=1e-4)
528
- >>> dfm.from_spec('data/spec.csv', params=params)
529
- >>> dfm.load_data('data/sample_data.csv')
530
- >>> dfm.train(max_iter=params.max_iter)
531
- """
372
+ """Load configuration from spec CSV file (convenience constructor)."""
532
373
  from .config import SpecCSVSource
533
374
 
534
375
  if params is None:
@@ -548,33 +389,7 @@ def from_spec(spec_path: Union[str, Path], params: Optional[Params] = None) -> D
548
389
 
549
390
 
550
391
  def from_spec_df(spec_df: pd.DataFrame, params: Optional[Params] = None) -> DFM:
551
- """Load configuration from spec DataFrame (convenience constructor).
552
-
553
- Same as `from_spec()` but accepts a pandas DataFrame instead of a CSV file path.
554
- Useful when you already have the spec data in memory.
555
-
556
- Parameters
557
- ----------
558
- spec_df : pd.DataFrame
559
- DataFrame containing series definitions (same format as spec CSV)
560
- params : Params, optional
561
- Global estimation parameters. If None, uses defaults from Params().
562
-
563
- Returns
564
- -------
565
- DFM
566
- DFM instance for method chaining
567
-
568
- Examples
569
- --------
570
- >>> import pandas as pd
571
- >>> from dfm_python.config import Params
572
- >>> import dfm_python as dfm
573
- >>> spec_df = pd.read_csv('data/spec.csv')
574
- >>> params = Params(max_iter=100)
575
- >>> dfm.from_spec_df(spec_df, params=params)
576
- >>> dfm.load_data('data/sample_data.csv')
577
- """
392
+ """Load configuration from spec DataFrame (convenience constructor)."""
578
393
  from .data_loader import _load_config_from_dataframe
579
394
 
580
395
  if params is None:
@@ -593,33 +408,7 @@ def from_spec_df(spec_df: pd.DataFrame, params: Optional[Params] = None) -> DFM:
593
408
 
594
409
 
595
410
  def from_dict(mapping: Dict[str, Any]) -> DFM:
596
- """Load configuration from dictionary (convenience constructor).
597
-
598
- This is a cleaner alternative to `load_config(mapping=...)`.
599
- Useful for programmatic configuration without YAML or CSV files.
600
-
601
- Parameters
602
- ----------
603
- mapping : dict
604
- Dictionary containing full DFM configuration (series, blocks, params)
605
-
606
- Returns
607
- -------
608
- DFM
609
- DFM instance for method chaining
610
-
611
- Examples
612
- --------
613
- >>> import dfm_python as dfm
614
- >>> config_dict = {
615
- ... 'clock': 'm',
616
- ... 'max_iter': 100,
617
- ... 'blocks': {'Block_Global': {'factors': 1, 'clock': 'm'}},
618
- ... 'series': [{'series_id': 's1', 'frequency': 'm', 'transformation': 'lin', 'blocks': [0]}]
619
- ... }
620
- >>> dfm.from_dict(config_dict)
621
- >>> dfm.load_data('data/sample_data.csv')
622
- """
411
+ """Load configuration from dictionary (convenience constructor)."""
623
412
  return _dfm_instance.load_config(mapping=mapping)
624
413
 
625
414
 
@@ -628,3 +417,4 @@ def from_dict(mapping: Dict[str, Any]) -> DFM:
628
417
  __all__ = ['DFM', 'load_config', 'load_data', 'train', 'reset',
629
418
  'from_yaml', 'from_spec', 'from_spec_df', 'from_dict']
630
419
 
420
+
dfm_python/config.py CHANGED
@@ -1050,7 +1050,28 @@ class DictSource:
1050
1050
  self.mapping = mapping
1051
1051
 
1052
1052
  def load(self) -> DFMConfig:
1053
- """Load configuration from dictionary."""
1053
+ """Load configuration from dictionary.
1054
+
1055
+ If the dictionary is partial (e.g., only max_iter, threshold),
1056
+ it will be merged with a minimal default config.
1057
+ """
1058
+ # Check if this is a partial config (missing series or blocks)
1059
+ has_series = 'series' in self.mapping and self.mapping['series']
1060
+ has_blocks = 'blocks' in self.mapping and self.mapping['blocks']
1061
+
1062
+ if not has_series or not has_blocks:
1063
+ # This is a partial config - create a minimal default and merge
1064
+ minimal_default = {
1065
+ 'series': [],
1066
+ 'blocks': {},
1067
+ 'clock': 'm',
1068
+ 'max_iter': 5000,
1069
+ 'threshold': 1e-5
1070
+ }
1071
+ # Merge: mapping takes precedence
1072
+ merged = {**minimal_default, **self.mapping}
1073
+ return DFMConfig.from_dict(merged)
1074
+
1054
1075
  return DFMConfig.from_dict(self.mapping)
1055
1076
 
1056
1077
 
@@ -1142,10 +1163,36 @@ class MergedConfigSource:
1142
1163
  from dataclasses import fields
1143
1164
 
1144
1165
  base_cfg = self.base.load()
1145
- override_cfg = self.override.load()
1166
+
1167
+ # Check if override is a partial config (DictSource with partial dict)
1168
+ override_is_partial = False
1169
+ if isinstance(self.override, DictSource):
1170
+ has_series = 'series' in self.override.mapping and self.override.mapping['series']
1171
+ has_blocks = 'blocks' in self.override.mapping and self.override.mapping['blocks']
1172
+ override_is_partial = not (has_series and has_blocks)
1173
+
1174
+ if override_is_partial:
1175
+ # Handle partial override: merge fields directly without loading full DFMConfig
1176
+ override_dict = self.override.mapping
1177
+ override_cfg = base_cfg # Use base as template
1178
+ else:
1179
+ override_cfg = self.override.load()
1146
1180
 
1147
1181
  # Merge blocks: override takes precedence
1148
- merged_blocks = {**base_cfg.blocks, **override_cfg.blocks}
1182
+ if override_is_partial and 'blocks' in override_dict:
1183
+ # Merge block dicts
1184
+ merged_blocks = {**base_cfg.blocks, **override_dict['blocks']}
1185
+ else:
1186
+ merged_blocks = {**base_cfg.blocks, **override_cfg.blocks}
1187
+
1188
+ # Use override's series if provided and non-empty, otherwise use base's series
1189
+ if override_is_partial:
1190
+ if 'series' in override_dict and override_dict['series']:
1191
+ merged_series = override_dict['series']
1192
+ else:
1193
+ merged_series = base_cfg.series
1194
+ else:
1195
+ merged_series = override_cfg.series if (override_cfg.series and len(override_cfg.series) > 0) else base_cfg.series
1149
1196
 
1150
1197
  # Get all config fields (excluding derived/computed fields)
1151
1198
  excluded_fields = {'series', 'blocks', 'block_names', 'factors_per_block', '_cached_blocks'}
@@ -1155,11 +1202,29 @@ class MergedConfigSource:
1155
1202
  if field.name not in excluded_fields
1156
1203
  }
1157
1204
 
1158
- # Create merged config: base settings + override series + merged blocks
1205
+ # Override settings from override_cfg or override_dict
1206
+ if override_is_partial:
1207
+ override_settings = {
1208
+ field.name: override_dict.get(field.name, getattr(base_cfg, field.name))
1209
+ for field in fields(DFMConfig)
1210
+ if field.name not in excluded_fields
1211
+ }
1212
+ else:
1213
+ override_settings = {
1214
+ field.name: getattr(override_cfg, field.name)
1215
+ for field in fields(DFMConfig)
1216
+ if field.name not in excluded_fields
1217
+ and hasattr(override_cfg, field.name)
1218
+ }
1219
+
1220
+ # Merge settings: base + override (override takes precedence)
1221
+ merged_settings = {**base_settings, **override_settings}
1222
+
1223
+ # Create merged config: merged settings + merged series + merged blocks
1159
1224
  return DFMConfig(
1160
- series=override_cfg.series, # Override series
1161
- blocks=merged_blocks, # Merged blocks
1162
- **base_settings # All other settings from base
1225
+ series=merged_series,
1226
+ blocks=merged_blocks,
1227
+ **merged_settings
1163
1228
  )
1164
1229
 
1165
1230
 
@@ -0,0 +1,59 @@
1
+ """Core utilities for DFM estimation.
2
+
3
+ This package contains core functionality organized by domain:
4
+ - em.py: EM algorithm core (init_conditions, em_step, em_converged)
5
+ - numeric.py: Numerical utilities (matrix operations, regularization, clipping)
6
+ - diagnostics.py: Diagnostic functions and output formatting
7
+ - results.py: Result metrics (RMSE calculation)
8
+ - grouping.py: Frequency grouping utilities
9
+ - helpers.py: Common helper functions for code patterns
10
+ """
11
+
12
+ from .em import init_conditions, em_step, em_converged
13
+ from .numeric import (
14
+ _ensure_symmetric,
15
+ _compute_principal_components,
16
+ _clean_matrix,
17
+ _ensure_positive_definite,
18
+ _compute_regularization_param,
19
+ _clip_ar_coefficients,
20
+ _apply_ar_clipping,
21
+ _cap_max_eigenvalue,
22
+ _estimate_ar_coefficient,
23
+ _safe_divide,
24
+ _check_finite,
25
+ _ensure_square_matrix,
26
+ )
27
+ from .results import calculate_rmse
28
+ from .grouping import group_series_by_frequency
29
+ from .diagnostics import (
30
+ _display_dfm_tables,
31
+ diagnose_series,
32
+ print_series_diagnosis,
33
+ )
34
+ from .helpers import safe_get_method, safe_get_attr
35
+
36
+ __all__ = [
37
+ 'init_conditions',
38
+ 'em_step',
39
+ 'em_converged',
40
+ '_ensure_symmetric',
41
+ '_compute_principal_components',
42
+ '_clean_matrix',
43
+ '_ensure_positive_definite',
44
+ '_compute_regularization_param',
45
+ '_clip_ar_coefficients',
46
+ '_apply_ar_clipping',
47
+ '_cap_max_eigenvalue',
48
+ '_estimate_ar_coefficient',
49
+ '_safe_divide',
50
+ '_check_finite',
51
+ '_ensure_square_matrix',
52
+ 'calculate_rmse',
53
+ 'group_series_by_frequency',
54
+ '_display_dfm_tables',
55
+ 'diagnose_series',
56
+ 'print_series_diagnosis',
57
+ 'safe_get_method',
58
+ 'safe_get_attr',
59
+ ]