dfm-python 0.3.1__py3-none-any.whl → 0.4.0__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 +103 -60
- dfm_python/config/__init__.py +36 -6
- dfm_python/config/params.py +202 -0
- dfm_python/{core → config}/results.py +96 -71
- dfm_python/config/schema.py +260 -331
- dfm_python/{core → config}/structure.py +1 -1
- dfm_python/config/utils.py +93 -0
- dfm_python/encoder/__init__.py +33 -0
- dfm_python/encoder/base.py +0 -0
- dfm_python/encoder/pca.py +157 -0
- dfm_python/encoder/vae.py +291 -0
- dfm_python/lightning/__init__.py +48 -0
- dfm_python/lightning/callbacks.py +0 -0
- dfm_python/lightning/data_module.py +364 -0
- dfm_python/lightning/ddfm_module.py +553 -0
- dfm_python/lightning/dfm_module.py +466 -0
- dfm_python/lightning/utils.py +0 -0
- dfm_python/logger/__init__.py +51 -0
- dfm_python/logger/inference_logger.py +270 -0
- dfm_python/logger/logger.py +110 -0
- dfm_python/logger/train_logger.py +309 -0
- dfm_python/models/__init__.py +21 -4
- dfm_python/models/base.py +24 -28
- dfm_python/models/ddfm.py +862 -720
- dfm_python/models/dfm.py +698 -101
- dfm_python/models/estimation.py +352 -0
- dfm_python/nowcast/__init__.py +28 -0
- dfm_python/nowcast/dataview.py +143 -0
- dfm_python/nowcast/helpers.py +554 -0
- dfm_python/{nowcasting → nowcast}/nowcast.py +57 -536
- dfm_python/nowcast/utils.py +290 -0
- dfm_python/ssm/__init__.py +47 -0
- dfm_python/ssm/em.py +488 -0
- dfm_python/ssm/kalman.py +604 -0
- dfm_python/ssm/utils.py +379 -0
- dfm_python/trainer/__init__.py +20 -0
- dfm_python/trainer/ddfm.py +196 -0
- dfm_python/trainer/dfm.py +153 -0
- dfm_python/transformations/__init__.py +35 -0
- dfm_python/transformations/sktime.py +40 -0
- dfm_python/transformations/transformers.py +320 -0
- dfm_python/transformations/utils.py +339 -0
- dfm_python/utils/__init__.py +189 -26
- dfm_python/utils/data.py +706 -0
- dfm_python/{core → utils}/diagnostics.py +11 -87
- dfm_python/utils/helpers.py +533 -0
- dfm_python/utils/statespace.py +1326 -0
- dfm_python/{core → utils}/time.py +373 -0
- {dfm_python-0.3.1.dist-info → dfm_python-0.4.0.dist-info}/METADATA +11 -9
- dfm_python-0.4.0.dist-info/RECORD +53 -0
- dfm_python/api/__init__.py +0 -44
- dfm_python/api/model_api.py +0 -1148
- dfm_python/api/session_api.py +0 -402
- dfm_python/core/__init__.py +0 -139
- dfm_python/core/em.py +0 -1385
- dfm_python/core/estimation.py +0 -553
- dfm_python/core/helpers.py +0 -1338
- dfm_python/core/state_space.py +0 -993
- dfm_python/dataloader/__init__.py +0 -30
- dfm_python/dataloader/loader.py +0 -763
- dfm_python/dataloader/preprocess.py +0 -11
- dfm_python/nowcasting/__init__.py +0 -13
- dfm_python/utils/convert.py +0 -342
- dfm_python/utils/loss.py +0 -184
- dfm_python-0.3.1.dist-info/RECORD +0 -32
- {dfm_python-0.3.1.dist-info → dfm_python-0.4.0.dist-info}/WHEEL +0 -0
- {dfm_python-0.3.1.dist-info → dfm_python-0.4.0.dist-info}/licenses/LICENSE +0 -0
dfm_python/__init__.py
CHANGED
|
@@ -26,23 +26,39 @@ Key Features:
|
|
|
26
26
|
|
|
27
27
|
Example (High-level API - Recommended):
|
|
28
28
|
>>> import dfm_python as dfm
|
|
29
|
+
>>> from dfm_python.lightning import DFMDataModule
|
|
30
|
+
>>> from sktime.transformations.compose import ColumnTransformer
|
|
31
|
+
>>>
|
|
29
32
|
>>> # Linear DFM
|
|
30
33
|
>>> model = dfm.DFM()
|
|
31
34
|
>>> model.load_config('config/default.yaml')
|
|
32
|
-
>>>
|
|
33
|
-
>>>
|
|
35
|
+
>>>
|
|
36
|
+
>>> # Create transformer (user must provide)
|
|
37
|
+
>>> transformer = ColumnTransformer([...]) # User-defined sktime transformer
|
|
38
|
+
>>>
|
|
39
|
+
>>> # Create DataModule
|
|
40
|
+
>>> data_module = DFMDataModule(config=model.config, transformer=transformer, data_path='data/sample_data.csv')
|
|
41
|
+
>>> data_module.setup()
|
|
42
|
+
>>>
|
|
43
|
+
>>> # Train
|
|
44
|
+
>>> model.train(data_module, max_iter=100)
|
|
34
45
|
>>> Xf, Zf = model.predict(horizon=6)
|
|
35
46
|
>>>
|
|
36
47
|
>>> # Or use DDFM (separate class)
|
|
37
48
|
>>> ddfm_model = dfm.DDFM(encoder_layers=[64, 32], num_factors=2)
|
|
38
49
|
>>> ddfm_model.load_config('config/default.yaml')
|
|
39
|
-
>>>
|
|
40
|
-
>>>
|
|
50
|
+
>>>
|
|
51
|
+
>>> # Create DataModule for DDFM
|
|
52
|
+
>>> data_module_ddfm = DFMDataModule(config=ddfm_model.config, transformer=transformer, data_path='data/sample_data.csv')
|
|
53
|
+
>>> data_module_ddfm.setup()
|
|
54
|
+
>>>
|
|
55
|
+
>>> # Train
|
|
56
|
+
>>> ddfm_model.train(data_module_ddfm, epochs=100)
|
|
41
57
|
>>> Xf, Zf = ddfm_model.predict(horizon=6)
|
|
42
58
|
|
|
43
59
|
Example (Low-level API - For advanced usage):
|
|
44
60
|
>>> from dfm_python import DFM, DFMConfig, SeriesConfig
|
|
45
|
-
>>> from dfm_python.
|
|
61
|
+
>>> from dfm_python.transformations.utils import read_data # Preferred import
|
|
46
62
|
>>> # Option 1: Load from YAML
|
|
47
63
|
>>> config = load_config('config.yaml')
|
|
48
64
|
>>> # Option 2: Create directly
|
|
@@ -50,15 +66,25 @@ Example (Low-level API - For advanced usage):
|
|
|
50
66
|
... series=[SeriesConfig(frequency='m', transformation='lin', blocks=[1], series_id='series1')],
|
|
51
67
|
... block_names=['Global']
|
|
52
68
|
... )
|
|
53
|
-
>>>
|
|
69
|
+
>>> from dfm_python.lightning import DFMDataModule
|
|
70
|
+
>>> from sktime.transformations.compose import ColumnTransformer
|
|
71
|
+
>>>
|
|
72
|
+
>>> # Create transformer (user must provide)
|
|
73
|
+
>>> transformer = ColumnTransformer([...]) # User-defined
|
|
74
|
+
>>>
|
|
75
|
+
>>> # Create DataModule
|
|
76
|
+
>>> data_module = DFMDataModule(config=config, transformer=transformer, data_path='data.csv')
|
|
77
|
+
>>> data_module.setup()
|
|
78
|
+
>>>
|
|
79
|
+
>>> # Fit model
|
|
54
80
|
>>> model = DFM()
|
|
55
|
-
>>> result = model.fit(
|
|
81
|
+
>>> result = model.fit(data_module, config)
|
|
56
82
|
>>> factors = result.Z # Extract estimated factors
|
|
57
83
|
|
|
58
84
|
For detailed documentation, see the README.md file and the tutorial notebooks/scripts.
|
|
59
85
|
"""
|
|
60
86
|
|
|
61
|
-
__version__ = "0.
|
|
87
|
+
__version__ = "0.4.0"
|
|
62
88
|
|
|
63
89
|
# ============================================================================
|
|
64
90
|
# PUBLIC API DEFINITION
|
|
@@ -84,45 +110,61 @@ from .config import (
|
|
|
84
110
|
)
|
|
85
111
|
|
|
86
112
|
# Data utilities
|
|
87
|
-
|
|
113
|
+
# Note: transform_data and DFMScaler have been removed - users must provide their own sktime transformers to DFMDataModule
|
|
88
114
|
|
|
89
115
|
# Results
|
|
90
|
-
from .
|
|
116
|
+
from .config.results import DFMResult, DDFMResult, BaseResult
|
|
91
117
|
|
|
92
|
-
#
|
|
93
|
-
from .
|
|
94
|
-
from .
|
|
95
|
-
# DFMCore is
|
|
118
|
+
# Utilities (from utils/ subpackage)
|
|
119
|
+
from .utils.diagnostics import diagnose_series, print_series_diagnosis
|
|
120
|
+
from .utils.time import calculate_rmse
|
|
121
|
+
# DFMCore is an alias for DFMLinear (backward compatibility)
|
|
96
122
|
from .models.dfm import DFMLinear
|
|
97
123
|
DFMCore = DFMLinear
|
|
98
124
|
|
|
99
|
-
#
|
|
100
|
-
|
|
125
|
+
# PyTorch Lightning modules (recommended)
|
|
126
|
+
try:
|
|
127
|
+
from .lightning import (
|
|
128
|
+
DFMLightningModule,
|
|
129
|
+
DDFMLightningModule,
|
|
130
|
+
DFMDataModule,
|
|
131
|
+
KalmanFilter, # New module class
|
|
132
|
+
EMAlgorithm, # New module class
|
|
133
|
+
)
|
|
134
|
+
_has_lightning = True
|
|
135
|
+
except ImportError:
|
|
136
|
+
_has_lightning = False
|
|
137
|
+
DFMLightningModule = None # type: ignore
|
|
138
|
+
DDFMLightningModule = None # type: ignore
|
|
139
|
+
DFMDataModule = None # type: ignore
|
|
140
|
+
KalmanFilter = None # type: ignore
|
|
141
|
+
EMAlgorithm = None # type: ignore
|
|
142
|
+
|
|
143
|
+
# Nowcasting (from nowcast/ subpackage)
|
|
144
|
+
from .nowcast import (
|
|
101
145
|
Nowcast,
|
|
102
|
-
para_const,
|
|
103
146
|
NowcastResult,
|
|
147
|
+
)
|
|
148
|
+
from .nowcast.helpers import (
|
|
149
|
+
para_const,
|
|
104
150
|
NewsDecompResult,
|
|
105
151
|
BacktestResult,
|
|
106
152
|
)
|
|
107
153
|
|
|
108
|
-
# High-level API (from api/ subpackage)
|
|
109
|
-
from .api import (
|
|
110
|
-
DFM, _dfm_instance, from_yaml, from_spec, from_spec_df, from_dict,
|
|
111
|
-
load_config, load_data, load_pickle, train, predict, plot, reset, create_model
|
|
112
|
-
)
|
|
113
|
-
|
|
114
154
|
# Model implementations
|
|
115
155
|
from .models.base import BaseFactorModel
|
|
116
|
-
from .models.dfm import DFMLinear
|
|
156
|
+
from .models.dfm import DFMLinear, DFM
|
|
157
|
+
from .models.dfm import (
|
|
158
|
+
from_yaml, from_spec, from_spec_df, from_dict,
|
|
159
|
+
load_config, load_pickle, train, predict, plot, reset, create_model
|
|
160
|
+
)
|
|
161
|
+
# Note: load_data has been removed - use DFMDataModule instead
|
|
117
162
|
|
|
118
163
|
# DDFM high-level API and low-level model (both optional, requires PyTorch)
|
|
119
164
|
try:
|
|
120
|
-
from .
|
|
121
|
-
from .
|
|
122
|
-
from .models.ddfm import DDFM as DDFMModel
|
|
165
|
+
from .models.ddfm import DDFM, DDFMModel
|
|
166
|
+
from .models.ddfm import load_config_ddfm, load_data_ddfm, train_ddfm, predict_ddfm, plot_ddfm, reset_ddfm
|
|
123
167
|
_has_ddfm = True
|
|
124
|
-
# Export high-level API as DDFM
|
|
125
|
-
DDFM = DDFMAPI
|
|
126
168
|
except ImportError:
|
|
127
169
|
_has_ddfm = False
|
|
128
170
|
DDFM = None # type: ignore
|
|
@@ -133,30 +175,7 @@ except ImportError:
|
|
|
133
175
|
predict_ddfm = None # type: ignore
|
|
134
176
|
plot_ddfm = None # type: ignore
|
|
135
177
|
reset_ddfm = None # type: ignore
|
|
136
|
-
|
|
137
|
-
# Expose properties as module-level attributes
|
|
138
|
-
# Use property-like access via functions or direct attribute access
|
|
139
|
-
# Since 'config' conflicts with the config module, we'll use a different approach
|
|
140
|
-
# Users can access: dfm.get_config(), dfm.get_data(), dfm.get_result()
|
|
141
|
-
def get_config():
|
|
142
|
-
"""Get current configuration."""
|
|
143
|
-
return _dfm_instance.config
|
|
144
|
-
|
|
145
|
-
def get_data():
|
|
146
|
-
"""Get current data matrix."""
|
|
147
|
-
return _dfm_instance.data
|
|
148
|
-
|
|
149
|
-
def get_time():
|
|
150
|
-
"""Get current time index."""
|
|
151
|
-
return _dfm_instance.time
|
|
152
|
-
|
|
153
|
-
def get_result():
|
|
154
|
-
"""Get training result."""
|
|
155
|
-
return _dfm_instance.result
|
|
156
|
-
|
|
157
|
-
def get_original_data():
|
|
158
|
-
"""Get original (untransformed) data matrix."""
|
|
159
|
-
return _dfm_instance.original_data
|
|
178
|
+
# DDFM module-level functions removed - create DDFM() instance directly
|
|
160
179
|
|
|
161
180
|
__all__ = [
|
|
162
181
|
# Core classes
|
|
@@ -172,22 +191,46 @@ __all__ = [
|
|
|
172
191
|
'MergedConfigSource', 'make_config_source',
|
|
173
192
|
# High-level API (module-level - recommended)
|
|
174
193
|
'load_config',
|
|
175
|
-
'
|
|
176
|
-
'get_config', 'get_data', 'get_time', 'get_result', 'get_original_data',
|
|
194
|
+
'load_pickle', 'train', 'predict', 'plot', 'reset', 'create_model',
|
|
177
195
|
# Convenience constructors (cleaner API)
|
|
178
196
|
'from_yaml', 'from_spec', 'from_spec_df', 'from_dict',
|
|
179
197
|
# Low-level API (functional interface - advanced usage)
|
|
180
|
-
'
|
|
181
|
-
'
|
|
182
|
-
'run_kf', 'skf', 'fis', 'miss_data',
|
|
183
|
-
'para_const', # Internal utility, kept for backward compatibility
|
|
198
|
+
'BaseResult', 'DFMResult', 'DDFMResult', 'calculate_rmse', 'diagnose_series', 'print_series_diagnosis',
|
|
199
|
+
'para_const', # Internal utility for nowcasting
|
|
184
200
|
]
|
|
185
201
|
|
|
186
202
|
# Add DDFM high-level API and convenience functions if available
|
|
187
203
|
if _has_ddfm:
|
|
188
204
|
__all__.extend([
|
|
189
205
|
'DDFM', # High-level API class
|
|
190
|
-
'
|
|
206
|
+
'DDFMModel', # Low-level implementation
|
|
207
|
+
'load_config_ddfm', 'train_ddfm',
|
|
191
208
|
'predict_ddfm', 'plot_ddfm', 'reset_ddfm'
|
|
209
|
+
# Note: load_data_ddfm has been removed - use DFMDataModule instead
|
|
210
|
+
])
|
|
211
|
+
|
|
212
|
+
# Add Lightning modules if available
|
|
213
|
+
if _has_lightning:
|
|
214
|
+
__all__.extend([
|
|
215
|
+
'DFMLightningModule',
|
|
216
|
+
'DDFMLightningModule',
|
|
217
|
+
'DFMDataModule',
|
|
218
|
+
'KalmanFilter', # New module class
|
|
219
|
+
'EMAlgorithm', # New module class
|
|
220
|
+
])
|
|
221
|
+
|
|
222
|
+
# Add Trainer classes if available
|
|
223
|
+
try:
|
|
224
|
+
from .trainer import DFMTrainer, DDFMTrainer
|
|
225
|
+
_has_trainers = True
|
|
226
|
+
except ImportError:
|
|
227
|
+
_has_trainers = False
|
|
228
|
+
DFMTrainer = None # type: ignore
|
|
229
|
+
DDFMTrainer = None # type: ignore
|
|
230
|
+
|
|
231
|
+
if _has_trainers:
|
|
232
|
+
__all__.extend([
|
|
233
|
+
'DFMTrainer',
|
|
234
|
+
'DDFMTrainer',
|
|
192
235
|
])
|
|
193
236
|
|
dfm_python/config/__init__.py
CHANGED
|
@@ -6,10 +6,12 @@ This subpackage provides:
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from .schema import (
|
|
9
|
-
DFMConfig, SeriesConfig, BlockConfig,
|
|
9
|
+
BaseModelConfig, DFMConfig, DDFMConfig, SeriesConfig, BlockConfig,
|
|
10
10
|
DEFAULT_GLOBAL_BLOCK_NAME,
|
|
11
|
-
validate_frequency, validate_transformation,
|
|
12
11
|
)
|
|
12
|
+
from .params import Params, FitParams
|
|
13
|
+
from .results import BaseResult, DFMResult, DDFMResult, DFMParams
|
|
14
|
+
from .utils import validate_frequency, validate_transformation
|
|
13
15
|
from .io import (
|
|
14
16
|
ConfigSource,
|
|
15
17
|
YamlSource,
|
|
@@ -18,18 +20,46 @@ from .io import (
|
|
|
18
20
|
MergedConfigSource,
|
|
19
21
|
make_config_source,
|
|
20
22
|
from_spec,
|
|
21
|
-
_load_config_from_dataframe,
|
|
22
|
-
|
|
23
|
+
# Note: Private functions (_load_config_from_dataframe, _write_series_blocks_yaml)
|
|
24
|
+
# are available from io module but not exported as public API
|
|
25
|
+
)
|
|
26
|
+
from .structure import (
|
|
27
|
+
FREQUENCY_HIERARCHY,
|
|
28
|
+
PERIODS_PER_YEAR,
|
|
29
|
+
get_periods_per_year,
|
|
30
|
+
compute_idio_chain_lengths,
|
|
31
|
+
get_tent_weights_for_pair,
|
|
32
|
+
generate_tent_weights,
|
|
33
|
+
generate_R_mat,
|
|
34
|
+
get_aggregation_structure,
|
|
35
|
+
group_series_by_frequency,
|
|
23
36
|
)
|
|
24
37
|
|
|
25
38
|
__all__ = [
|
|
26
39
|
# Schema
|
|
27
|
-
'DFMConfig', '
|
|
40
|
+
'BaseModelConfig', 'DFMConfig', 'DDFMConfig', 'SeriesConfig', 'BlockConfig',
|
|
28
41
|
'DEFAULT_GLOBAL_BLOCK_NAME',
|
|
42
|
+
# Parameters
|
|
43
|
+
'Params', 'FitParams',
|
|
44
|
+
# Results
|
|
45
|
+
'BaseResult', 'DFMResult', 'DDFMResult', 'DFMParams',
|
|
46
|
+
# Utilities
|
|
29
47
|
'validate_frequency', 'validate_transformation',
|
|
30
48
|
# IO
|
|
31
49
|
'ConfigSource', 'YamlSource', 'DictSource',
|
|
32
50
|
'HydraSource', 'MergedConfigSource', 'make_config_source',
|
|
33
|
-
'from_spec',
|
|
51
|
+
'from_spec',
|
|
52
|
+
# Structure utilities
|
|
53
|
+
'FREQUENCY_HIERARCHY',
|
|
54
|
+
'PERIODS_PER_YEAR',
|
|
55
|
+
'get_periods_per_year',
|
|
56
|
+
'compute_idio_chain_lengths',
|
|
57
|
+
'get_tent_weights_for_pair',
|
|
58
|
+
'generate_tent_weights',
|
|
59
|
+
'generate_R_mat',
|
|
60
|
+
'get_aggregation_structure',
|
|
61
|
+
'group_series_by_frequency',
|
|
62
|
+
# Note: Private functions (_load_config_from_dataframe, _write_series_blocks_yaml)
|
|
63
|
+
# are available from io module but not exported as public API
|
|
34
64
|
]
|
|
35
65
|
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"""Parameter dataclasses for DFM models.
|
|
2
|
+
|
|
3
|
+
This module contains parameter configuration classes:
|
|
4
|
+
- Params: Global estimation parameters with defaults
|
|
5
|
+
- FitParams: Optional parameter overrides for model fitting
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from typing import Optional
|
|
10
|
+
from .utils import validate_frequency
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class Params:
|
|
15
|
+
"""Global estimation parameters for DFM (main settings only).
|
|
16
|
+
|
|
17
|
+
This dataclass contains only the global knobs that control the EM algorithm
|
|
18
|
+
and numerical stability. It does NOT include series or block definitions.
|
|
19
|
+
Use this with spec CSV files to provide main settings while the spec CSV
|
|
20
|
+
defines all series and their block memberships.
|
|
21
|
+
|
|
22
|
+
All parameters have defaults matching the standard YAML configuration.
|
|
23
|
+
|
|
24
|
+
Attributes
|
|
25
|
+
----------
|
|
26
|
+
ar_lag : int
|
|
27
|
+
AR lag for factor transition equation (typically 1)
|
|
28
|
+
threshold : float
|
|
29
|
+
EM convergence threshold (default: 1e-5)
|
|
30
|
+
max_iter : int
|
|
31
|
+
Maximum EM iterations (default: 5000)
|
|
32
|
+
nan_method : int
|
|
33
|
+
Missing data handling method (1-5, default: 2 = spline interpolation).
|
|
34
|
+
This preprocessing step is followed by Kalman Filter-based missing data
|
|
35
|
+
handling during DFM estimation, following standard practice in state-space
|
|
36
|
+
models (Mariano & Murasawa 2003, Harvey 1989).
|
|
37
|
+
nan_k : int
|
|
38
|
+
Spline parameter for NaN interpolation (default: 3 for cubic spline)
|
|
39
|
+
clock : str
|
|
40
|
+
Base frequency for all latent factors (default: 'm' for monthly)
|
|
41
|
+
clip_ar_coefficients : bool
|
|
42
|
+
Enable AR coefficient clipping for stationarity (default: True)
|
|
43
|
+
ar_clip_min : float
|
|
44
|
+
Minimum AR coefficient (default: -0.99)
|
|
45
|
+
ar_clip_max : float
|
|
46
|
+
Maximum AR coefficient (default: 0.99)
|
|
47
|
+
warn_on_ar_clip : bool
|
|
48
|
+
Warn when AR coefficients are clipped (default: True)
|
|
49
|
+
clip_data_values : bool
|
|
50
|
+
Enable clipping of extreme data values (default: True)
|
|
51
|
+
data_clip_threshold : float
|
|
52
|
+
Clip values beyond this many standard deviations (default: 100.0)
|
|
53
|
+
warn_on_data_clip : bool
|
|
54
|
+
Warn when data values are clipped (default: True)
|
|
55
|
+
use_regularization : bool
|
|
56
|
+
Enable regularization for numerical stability (default: True)
|
|
57
|
+
regularization_scale : float
|
|
58
|
+
Scale factor for ridge regularization (default: 1e-5)
|
|
59
|
+
min_eigenvalue : float
|
|
60
|
+
Minimum eigenvalue for positive definite matrices (default: 1e-8)
|
|
61
|
+
max_eigenvalue : float
|
|
62
|
+
Maximum eigenvalue cap (default: 1e6)
|
|
63
|
+
warn_on_regularization : bool
|
|
64
|
+
Warn when regularization is applied (default: True)
|
|
65
|
+
use_damped_updates : bool
|
|
66
|
+
Enable damped updates when likelihood decreases (default: True)
|
|
67
|
+
damping_factor : float
|
|
68
|
+
Damping factor: 0.8 = 80% new, 20% old (default: 0.8)
|
|
69
|
+
warn_on_damped_update : bool
|
|
70
|
+
Warn when damped updates are used (default: True)
|
|
71
|
+
augment_idio : bool
|
|
72
|
+
Enable state augmentation with idiosyncratic components (default: True)
|
|
73
|
+
augment_idio_slow : bool
|
|
74
|
+
Enable tent-length chains for slower-frequency series (default: True)
|
|
75
|
+
idio_rho0 : float
|
|
76
|
+
Initial AR coefficient for idiosyncratic components (default: 0.1)
|
|
77
|
+
idio_min_var : float
|
|
78
|
+
Minimum variance for idiosyncratic innovation covariance (default: 1e-8)
|
|
79
|
+
|
|
80
|
+
Examples
|
|
81
|
+
--------
|
|
82
|
+
>>> from dfm_python.config import Params
|
|
83
|
+
>>> params = Params(max_iter=100, threshold=1e-4)
|
|
84
|
+
>>> dfm.from_spec('data/spec.csv', params=params)
|
|
85
|
+
"""
|
|
86
|
+
# Estimation parameters
|
|
87
|
+
ar_lag: int = 1
|
|
88
|
+
threshold: float = 1e-5
|
|
89
|
+
max_iter: int = 5000
|
|
90
|
+
nan_method: int = 2
|
|
91
|
+
nan_k: int = 3
|
|
92
|
+
clock: str = 'm'
|
|
93
|
+
|
|
94
|
+
# Numerical stability - AR clipping
|
|
95
|
+
clip_ar_coefficients: bool = True
|
|
96
|
+
ar_clip_min: float = -0.99
|
|
97
|
+
ar_clip_max: float = 0.99
|
|
98
|
+
warn_on_ar_clip: bool = True
|
|
99
|
+
|
|
100
|
+
# Numerical stability - Data clipping
|
|
101
|
+
clip_data_values: bool = True
|
|
102
|
+
data_clip_threshold: float = 100.0
|
|
103
|
+
warn_on_data_clip: bool = True
|
|
104
|
+
|
|
105
|
+
# Numerical stability - Regularization
|
|
106
|
+
use_regularization: bool = True
|
|
107
|
+
regularization_scale: float = 1e-5
|
|
108
|
+
min_eigenvalue: float = 1e-8
|
|
109
|
+
max_eigenvalue: float = 1e6
|
|
110
|
+
warn_on_regularization: bool = True
|
|
111
|
+
|
|
112
|
+
# Numerical stability - Damped updates
|
|
113
|
+
use_damped_updates: bool = True
|
|
114
|
+
damping_factor: float = 0.8
|
|
115
|
+
warn_on_damped_update: bool = True
|
|
116
|
+
|
|
117
|
+
# Idiosyncratic component augmentation
|
|
118
|
+
augment_idio: bool = True # Enable state augmentation with idiosyncratic components (default: True)
|
|
119
|
+
augment_idio_slow: bool = True # Enable tent-length chains for slower-frequency series (default: True)
|
|
120
|
+
idio_rho0: float = 0.1 # Initial AR coefficient for idiosyncratic components (default: 0.1)
|
|
121
|
+
idio_min_var: float = 1e-8 # Minimum variance for idiosyncratic innovation covariance (default: 1e-8)
|
|
122
|
+
|
|
123
|
+
def __post_init__(self):
|
|
124
|
+
"""Validate parameters."""
|
|
125
|
+
self.clock = validate_frequency(self.clock)
|
|
126
|
+
if self.threshold <= 0:
|
|
127
|
+
raise ValueError(f"threshold must be positive, got {self.threshold}")
|
|
128
|
+
if self.max_iter < 1:
|
|
129
|
+
raise ValueError(f"max_iter must be at least 1, got {self.max_iter}")
|
|
130
|
+
if self.ar_clip_min >= self.ar_clip_max:
|
|
131
|
+
raise ValueError(f"ar_clip_min ({self.ar_clip_min}) must be < ar_clip_max ({self.ar_clip_max})")
|
|
132
|
+
if not (-1 < self.ar_clip_min < 1):
|
|
133
|
+
raise ValueError(f"ar_clip_min must be in (-1, 1), got {self.ar_clip_min}")
|
|
134
|
+
if not (-1 < self.ar_clip_max < 1):
|
|
135
|
+
raise ValueError(f"ar_clip_max must be in (-1, 1), got {self.ar_clip_max}")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@dataclass
|
|
139
|
+
class FitParams:
|
|
140
|
+
"""Parameters for fitting DFM models.
|
|
141
|
+
|
|
142
|
+
This dataclass groups all optional parameters that can override
|
|
143
|
+
config values during model fitting. This reduces method signature
|
|
144
|
+
complexity and improves code readability.
|
|
145
|
+
|
|
146
|
+
All parameters are optional. If None, the corresponding value
|
|
147
|
+
from DFMConfig will be used during parameter resolution.
|
|
148
|
+
"""
|
|
149
|
+
# Convergence parameters
|
|
150
|
+
threshold: Optional[float] = None
|
|
151
|
+
max_iter: Optional[int] = None
|
|
152
|
+
|
|
153
|
+
# Model structure
|
|
154
|
+
ar_lag: Optional[int] = None
|
|
155
|
+
num_factors: Optional[int] = None
|
|
156
|
+
|
|
157
|
+
# Missing data handling
|
|
158
|
+
nan_method: Optional[int] = None
|
|
159
|
+
nan_k: Optional[int] = None
|
|
160
|
+
|
|
161
|
+
# AR coefficient clipping
|
|
162
|
+
clip_ar_coefficients: Optional[bool] = None
|
|
163
|
+
ar_clip_min: Optional[float] = None
|
|
164
|
+
ar_clip_max: Optional[float] = None
|
|
165
|
+
|
|
166
|
+
# Data clipping
|
|
167
|
+
clip_data_values: Optional[bool] = None
|
|
168
|
+
data_clip_threshold: Optional[float] = None
|
|
169
|
+
|
|
170
|
+
# Regularization
|
|
171
|
+
use_regularization: Optional[bool] = None
|
|
172
|
+
regularization_scale: Optional[float] = None
|
|
173
|
+
min_eigenvalue: Optional[float] = None
|
|
174
|
+
max_eigenvalue: Optional[float] = None
|
|
175
|
+
|
|
176
|
+
# Damping
|
|
177
|
+
use_damped_updates: Optional[bool] = None
|
|
178
|
+
damping_factor: Optional[float] = None
|
|
179
|
+
|
|
180
|
+
def to_dict(self) -> dict:
|
|
181
|
+
"""Convert to dictionary, excluding None values."""
|
|
182
|
+
return {k: v for k, v in self.__dict__.items() if v is not None}
|
|
183
|
+
|
|
184
|
+
@classmethod
|
|
185
|
+
def from_kwargs(cls, **kwargs) -> 'FitParams':
|
|
186
|
+
"""Create FitParams from keyword arguments.
|
|
187
|
+
|
|
188
|
+
Filters kwargs to only include valid parameter names,
|
|
189
|
+
ignoring any extra arguments.
|
|
190
|
+
"""
|
|
191
|
+
valid_params = {
|
|
192
|
+
'threshold', 'max_iter', 'ar_lag', 'num_factors',
|
|
193
|
+
'nan_method', 'nan_k',
|
|
194
|
+
'clip_ar_coefficients', 'ar_clip_min', 'ar_clip_max',
|
|
195
|
+
'clip_data_values', 'data_clip_threshold',
|
|
196
|
+
'use_regularization', 'regularization_scale',
|
|
197
|
+
'min_eigenvalue', 'max_eigenvalue',
|
|
198
|
+
'use_damped_updates', 'damping_factor'
|
|
199
|
+
}
|
|
200
|
+
filtered = {k: v for k, v in kwargs.items() if k in valid_params}
|
|
201
|
+
return cls(**filtered)
|
|
202
|
+
|