dfm-python 0.3.0__tar.gz → 0.3.1__tar.gz
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-0.3.1/.git +1 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/PKG-INFO +190 -27
- {dfm_python-0.3.0 → dfm_python-0.3.1}/README.md +184 -26
- dfm_python-0.3.1/config/experiment/ddfm_example.yaml +26 -0
- dfm_python-0.3.1/data/mdfred_snapshot_monthly.csv +780 -0
- dfm_python-0.3.1/data/mdfred_snapshot_quarterly.csv +262 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/full_tutorial_example.ipynb +223 -181
- {dfm_python-0.3.0 → dfm_python-0.3.1}/pyproject.toml +6 -2
- {dfm_python-0.3.0 → dfm_python-0.3.1}/src/dfm_python/__init__.py +91 -18
- dfm_python-0.3.1/src/dfm_python/api/__init__.py +44 -0
- dfm_python-0.3.0/src/dfm_python/api.py → dfm_python-0.3.1/src/dfm_python/api/model_api.py +348 -271
- dfm_python-0.3.1/src/dfm_python/api/session_api.py +402 -0
- dfm_python-0.3.1/src/dfm_python/config/__init__.py +35 -0
- dfm_python-0.3.0/src/dfm_python/config_sources.py → dfm_python-0.3.1/src/dfm_python/config/io.py +9 -9
- dfm_python-0.3.0/src/dfm_python/config.py → dfm_python-0.3.1/src/dfm_python/config/schema.py +36 -39
- dfm_python-0.3.1/src/dfm_python/core/__init__.py +139 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/src/dfm_python/core/diagnostics.py +22 -23
- {dfm_python-0.3.0 → dfm_python-0.3.1}/src/dfm_python/core/em.py +10 -9
- dfm_python-0.3.0/src/dfm_python/dfm.py → dfm_python-0.3.1/src/dfm_python/core/estimation.py +22 -432
- {dfm_python-0.3.0 → dfm_python-0.3.1}/src/dfm_python/core/helpers.py +10 -9
- dfm_python-0.3.1/src/dfm_python/core/results.py +293 -0
- dfm_python-0.3.0/src/dfm_python/core/numeric.py → dfm_python-0.3.1/src/dfm_python/core/state_space.py +630 -269
- dfm_python-0.3.0/src/dfm_python/core/utils.py → dfm_python-0.3.1/src/dfm_python/core/structure.py +6 -14
- dfm_python-0.3.1/src/dfm_python/dataloader/__init__.py +30 -0
- dfm_python-0.3.0/src/dfm_python/data.py → dfm_python-0.3.1/src/dfm_python/dataloader/loader.py +18 -18
- dfm_python-0.3.1/src/dfm_python/dataloader/preprocess.py +11 -0
- dfm_python-0.3.1/src/dfm_python/models/__init__.py +19 -0
- dfm_python-0.3.1/src/dfm_python/models/base.py +98 -0
- dfm_python-0.3.1/src/dfm_python/models/ddfm.py +978 -0
- dfm_python-0.3.1/src/dfm_python/models/dfm.py +213 -0
- dfm_python-0.3.1/src/dfm_python/nowcasting/__init__.py +13 -0
- {dfm_python-0.3.0/src/dfm_python → dfm_python-0.3.1/src/dfm_python/nowcasting}/nowcast.py +303 -325
- dfm_python-0.3.1/src/dfm_python/utils/__init__.py +38 -0
- dfm_python-0.3.1/src/dfm_python/utils/convert.py +342 -0
- dfm_python-0.3.1/src/dfm_python/utils/loss.py +184 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/src/test/test_api.py +3 -3
- {dfm_python-0.3.0 → dfm_python-0.3.1}/src/test/test_core.py +1 -1
- dfm_python-0.3.1/src/test/test_ddfm.py +645 -0
- dfm_python-0.3.1/src/test/test_dfm_regression.py +172 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/src/test/test_kalman.py +2 -2
- {dfm_python-0.3.0 → dfm_python-0.3.1}/src/test/test_nowcasting.py +102 -8
- {dfm_python-0.3.0 → dfm_python-0.3.1}/tutorial/basic_tutorial.py +68 -19
- {dfm_python-0.3.0 → dfm_python-0.3.1}/uv.lock +482 -85
- dfm_python-0.3.0/src/dfm_python/core/__init__.py +0 -15
- dfm_python-0.3.0/src/dfm_python/kalman.py +0 -467
- {dfm_python-0.3.0 → dfm_python-0.3.1}/.gitignore +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/.python-version +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/LICENSE +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/MANIFEST.in +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/blocks/default.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/blocks/sample_spec.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/blocks/sample_spec2.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/default.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/experiment/exp1.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/experiment/exp2.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/experiment/exp3.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/blocks/spec_20251118_194140_025b98_blocks.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/blocks/spec_20251118_194248_82a2a3_blocks.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/blocks/spec_20251118_194423_013beb_blocks.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/blocks/spec_20251118_194532_190712_blocks.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/blocks/spec_20251118_195001_ef69d1_blocks.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/blocks/spec_20251118_195110_eb12bc_blocks.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/blocks/spec_20251118_195439_12eea1_blocks.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/blocks/spec_20251118_195547_0ecebd_blocks.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/blocks/spec_20251118_200209_8b6463_blocks.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/blocks/spec_20251118_200319_c62fea_blocks.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/blocks/spec_20251118_200722_7d6a26_blocks.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/blocks/spec_20251118_200838_320d08_blocks.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/blocks/spec_20251118_200934_65c460_blocks.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/blocks/spec_20251118_201046_8e7482_blocks.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/series/spec_20251118_194140_025b98_series.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/series/spec_20251118_194248_82a2a3_series.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/series/spec_20251118_194423_013beb_series.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/series/spec_20251118_194532_190712_series.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/series/spec_20251118_195001_ef69d1_series.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/series/spec_20251118_195110_eb12bc_series.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/series/spec_20251118_195439_12eea1_series.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/series/spec_20251118_195547_0ecebd_series.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/series/spec_20251118_200209_8b6463_series.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/series/spec_20251118_200319_c62fea_series.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/series/spec_20251118_200722_7d6a26_series.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/series/spec_20251118_200838_320d08_series.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/series/spec_20251118_200934_65c460_series.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/series/spec_20251118_201046_8e7482_series.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/spec_20251118_194140_025b98.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/spec_20251118_194248_82a2a3.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/spec_20251118_194423_013beb.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/spec_20251118_194532_190712.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/spec_20251118_195001_ef69d1.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/spec_20251118_195110_eb12bc.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/spec_20251118_195439_12eea1.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/spec_20251118_195547_0ecebd.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/spec_20251118_200209_8b6463.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/spec_20251118_200319_c62fea.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/spec_20251118_200722_7d6a26.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/spec_20251118_200838_320d08.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/spec_20251118_200934_65c460.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/generated/spec_20251118_201046_8e7482.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/series/default.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/series/sample_spec.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/config/series/sample_spec2.yaml +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/data/sample_data.csv +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/data/sample_spec.csv +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/src/adapters/__init__.py +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/src/adapters/data_view.py +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/src/adapters/database.py +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/src/adapters/model_result.py +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/src/dfm_python/core/time.py +0 -0
- {dfm_python-0.3.0 → dfm_python-0.3.1}/src/test/test_integration.py +0 -0
dfm_python-0.3.1/.git
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gitdir: ../../../../../kaggle_projects/hull-tactical/.git/worktrees/qui/modules/dfm-python
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dfm-python
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Dynamic Factor Model (DFM) estimation and nowcasting in Python
|
|
5
5
|
Project-URL: Homepage, https://github.com/yourusername/dfm-python
|
|
6
6
|
Project-URL: Documentation, https://github.com/yourusername/dfm-python#readme
|
|
@@ -29,11 +29,16 @@ Provides-Extra: all
|
|
|
29
29
|
Requires-Dist: hydra-core>=1.3.0; extra == 'all'
|
|
30
30
|
Requires-Dist: omegaconf>=2.3.0; extra == 'all'
|
|
31
31
|
Requires-Dist: pytest>=7.0.0; extra == 'all'
|
|
32
|
+
Requires-Dist: scikit-learn>=1.0.0; extra == 'all'
|
|
32
33
|
Requires-Dist: sqlalchemy>=2.0.0; extra == 'all'
|
|
34
|
+
Requires-Dist: torch>=2.0.0; extra == 'all'
|
|
33
35
|
Provides-Extra: db
|
|
34
36
|
Requires-Dist: sqlalchemy>=2.0.0; extra == 'db'
|
|
37
|
+
Provides-Extra: deep
|
|
38
|
+
Requires-Dist: torch>=2.0.0; extra == 'deep'
|
|
35
39
|
Provides-Extra: dev
|
|
36
40
|
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
41
|
+
Requires-Dist: scikit-learn>=1.0.0; extra == 'dev'
|
|
37
42
|
Provides-Extra: hydra
|
|
38
43
|
Requires-Dist: hydra-core>=1.3.0; extra == 'hydra'
|
|
39
44
|
Requires-Dist: omegaconf>=2.3.0; extra == 'hydra'
|
|
@@ -68,6 +73,7 @@ A Dynamic Factor Model is a powerful statistical framework that:
|
|
|
68
73
|
- ✅ **Robust missing data**: Spline interpolation preprocessing + Kalman filter handling during estimation
|
|
69
74
|
- ✅ **News decomposition**: Attribute forecast changes to specific data releases
|
|
70
75
|
- ✅ **Nowcasting & Forecasting**: Generate predictions for any horizon
|
|
76
|
+
- ✅ **Deep Dynamic Factor Models (DDFM)**: Nonlinear encoder with PyTorch for capturing complex factor structures (optional, requires `dfm-python[deep]`)
|
|
71
77
|
|
|
72
78
|
### Technical Features
|
|
73
79
|
- ✅ **Multiple configuration methods**: YAML files, CSV specs, Python dictionaries, or Hydra
|
|
@@ -95,6 +101,7 @@ pip install dfm-python
|
|
|
95
101
|
Optional dependencies:
|
|
96
102
|
- `hydra-core` (for Hydra configuration management)
|
|
97
103
|
- `matplotlib` (for plotting)
|
|
104
|
+
- `torch>=2.0.0` (for Deep Dynamic Factor Models - install with `pip install dfm-python[deep]`)
|
|
98
105
|
|
|
99
106
|
## Quick Start
|
|
100
107
|
|
|
@@ -131,7 +138,41 @@ smoothed_data = result.X_sm # (100 × 10) Smoothed series
|
|
|
131
138
|
forecasts = result.forecast(horizon=None) # Default: 1 year of periods based on clock frequency
|
|
132
139
|
```
|
|
133
140
|
|
|
134
|
-
### Example 2:
|
|
141
|
+
### Example 2: Deep Dynamic Factor Model (DDFM)
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
import dfm_python as dfm
|
|
145
|
+
from dfm_python import create_model, DFMConfig, SeriesConfig, BlockConfig
|
|
146
|
+
import numpy as np
|
|
147
|
+
|
|
148
|
+
# Generate or load your data
|
|
149
|
+
X = np.random.randn(100, 10) # 100 months, 10 series
|
|
150
|
+
|
|
151
|
+
# Create configuration
|
|
152
|
+
series = [
|
|
153
|
+
SeriesConfig(series_id=f'series_{i}', frequency='m', transformation='lin', blocks=[1])
|
|
154
|
+
for i in range(10)
|
|
155
|
+
]
|
|
156
|
+
config = DFMConfig(
|
|
157
|
+
series=series,
|
|
158
|
+
blocks=[BlockConfig(block_name='Global', factors=1)],
|
|
159
|
+
clock='m',
|
|
160
|
+
factors_per_block=[1],
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Create and fit DDFM model
|
|
164
|
+
ddfm = create_model('ddfm', encoder_layers=[64, 32], num_factors=1, epochs=100)
|
|
165
|
+
result = ddfm.fit(X, config)
|
|
166
|
+
|
|
167
|
+
# Access results (same structure as DFM)
|
|
168
|
+
factors = result.Z # (100 × 1) Extracted factors
|
|
169
|
+
loadings = result.C # (10 × 1) Factor loadings
|
|
170
|
+
X_forecast, Z_forecast = ddfm.predict(horizon=12)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Note**: DDFM requires PyTorch. Install with `pip install dfm-python[deep]`.
|
|
174
|
+
|
|
175
|
+
### Example 3: Mixed-Frequency Data (Monthly + Quarterly)
|
|
135
176
|
|
|
136
177
|
```python
|
|
137
178
|
import dfm_python as dfm
|
|
@@ -597,7 +638,7 @@ Generate datasets for model evaluation across multiple periods:
|
|
|
597
638
|
from datetime import datetime
|
|
598
639
|
|
|
599
640
|
# Define evaluation periods
|
|
600
|
-
from dfm_python.
|
|
641
|
+
from dfm_python.engine.time import datetime_range
|
|
601
642
|
periods = datetime_range(start=datetime(2020, 1, 1), end=datetime(2023, 12, 31), freq='QE')
|
|
602
643
|
|
|
603
644
|
# Generate evaluation dataset
|
|
@@ -916,6 +957,132 @@ Complete reference for `DFMResult`:
|
|
|
916
957
|
6. **Flexible configuration**: Multiple input methods (YAML, CSV, Dict, Hydra)
|
|
917
958
|
7. **Backward compatibility**: Old code continues to work, new features are opt-in
|
|
918
959
|
|
|
960
|
+
## Deep Dynamic Factor Models (DDFM)
|
|
961
|
+
|
|
962
|
+
The package includes support for **Deep Dynamic Factor Models (DDFM)**, which use nonlinear encoders to extract factors from observed data. DDFM is particularly useful when the relationship between observed variables and latent factors is nonlinear.
|
|
963
|
+
|
|
964
|
+
**Note**: DDFM has a **separate high-level API class** (`dfm_python.DDFM`) from the linear DFM class (`dfm_python.DFM`). Both classes share the same interface (load_config, load_data, train, predict, plot) but use different underlying models.
|
|
965
|
+
|
|
966
|
+
### When to Use DDFM vs Linear DFM
|
|
967
|
+
|
|
968
|
+
- **Use Linear DFM** when:
|
|
969
|
+
- The relationship between factors and observations is approximately linear
|
|
970
|
+
- You need interpretable factor loadings
|
|
971
|
+
- Computational speed is important
|
|
972
|
+
- You have limited data
|
|
973
|
+
|
|
974
|
+
- **Use DDFM** when:
|
|
975
|
+
- The relationship between factors and observations is nonlinear
|
|
976
|
+
- You suspect complex interactions between variables
|
|
977
|
+
- You have sufficient data for training neural networks
|
|
978
|
+
- You want to capture non-Gaussian factor structures
|
|
979
|
+
|
|
980
|
+
### DDFM Architecture
|
|
981
|
+
|
|
982
|
+
The DDFM implementation uses:
|
|
983
|
+
- **Nonlinear Encoder**: Multi-layer perceptron (MLP) to extract factors from observations
|
|
984
|
+
- **Linear Decoder**: Linear transformation from factors back to observations (for interpretability)
|
|
985
|
+
- **Linear Dynamics**: VAR(1) or VAR(2) dynamics for factors (estimated via OLS)
|
|
986
|
+
- **Idiosyncratic Modeling**: AR(1) dynamics for residual components (optional)
|
|
987
|
+
- **Kalman Smoothing**: Final smoothing step using Kalman filter with full state-space (factors + idio)
|
|
988
|
+
|
|
989
|
+
**Key Features:**
|
|
990
|
+
- **VAR(2) Support**: Can model factor dynamics with up to 2 lags using companion form
|
|
991
|
+
- **Idiosyncratic Components**: Models residual components with AR(1) dynamics for better fit
|
|
992
|
+
- **Decoder Parameter Extraction**: Directly extracts observation matrix from trained decoder (no OLS re-estimation)
|
|
993
|
+
- **Full State-Space**: Complete state-space model with factors and idio in state vector
|
|
994
|
+
|
|
995
|
+
### DDFM Examples
|
|
996
|
+
|
|
997
|
+
#### Example 1: Basic DDFM Usage
|
|
998
|
+
|
|
999
|
+
```python
|
|
1000
|
+
from dfm_python import create_model, DFMConfig, SeriesConfig, BlockConfig
|
|
1001
|
+
import numpy as np
|
|
1002
|
+
|
|
1003
|
+
# Create data
|
|
1004
|
+
X = np.random.randn(200, 15) # 200 time periods, 15 series
|
|
1005
|
+
|
|
1006
|
+
# Create config
|
|
1007
|
+
series = [
|
|
1008
|
+
SeriesConfig(series_id=f'series_{i}', frequency='m', transformation='lin', blocks=[1])
|
|
1009
|
+
for i in range(15)
|
|
1010
|
+
]
|
|
1011
|
+
config = DFMConfig(
|
|
1012
|
+
series=series,
|
|
1013
|
+
blocks={'Block_Global': BlockConfig(factors=2, ar_lag=1, clock='m')},
|
|
1014
|
+
clock='m',
|
|
1015
|
+
)
|
|
1016
|
+
|
|
1017
|
+
# Create DDFM model
|
|
1018
|
+
ddfm = create_model(
|
|
1019
|
+
'ddfm',
|
|
1020
|
+
encoder_layers=[64, 32, 16], # Encoder architecture
|
|
1021
|
+
num_factors=2, # Number of factors
|
|
1022
|
+
activation='tanh', # Activation function
|
|
1023
|
+
epochs=100, # Training epochs
|
|
1024
|
+
batch_size=32, # Batch size
|
|
1025
|
+
learning_rate=0.001, # Learning rate
|
|
1026
|
+
)
|
|
1027
|
+
|
|
1028
|
+
# Fit model
|
|
1029
|
+
result = ddfm.fit(X, config)
|
|
1030
|
+
|
|
1031
|
+
# Use results (same API as linear DFM)
|
|
1032
|
+
factors = result.Z
|
|
1033
|
+
loadings = result.C
|
|
1034
|
+
X_forecast, Z_forecast = ddfm.predict(horizon=12)
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
### Testing DDFM
|
|
1038
|
+
|
|
1039
|
+
The package includes synthetic DGP tests for validating DDFM:
|
|
1040
|
+
|
|
1041
|
+
```python
|
|
1042
|
+
from dfm_python.engine.synthetic_dgp import SyntheticDGP
|
|
1043
|
+
from dfm_python import create_model
|
|
1044
|
+
|
|
1045
|
+
# Create synthetic data with known factors
|
|
1046
|
+
dgp = SyntheticDGP(seed=42, n=10, r=1, poly_degree=2) # Nonlinear factors
|
|
1047
|
+
X = dgp.simulate(200)
|
|
1048
|
+
|
|
1049
|
+
# Fit DDFM
|
|
1050
|
+
ddfm = create_model('ddfm', encoder_layers=[64, 32], num_factors=1)
|
|
1051
|
+
result = ddfm.fit(X, config)
|
|
1052
|
+
|
|
1053
|
+
# Evaluate factor recovery
|
|
1054
|
+
r2 = dgp.evaluate(result.Z) # Trace R² metric
|
|
1055
|
+
print(f"Factor recovery R²: {r2:.3f}")
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1058
|
+
## Package Structure
|
|
1059
|
+
|
|
1060
|
+
The package is organized into clear layers for better maintainability and extensibility:
|
|
1061
|
+
|
|
1062
|
+
- **`engine/`**: Pure mathematical/algorithmic code shared by all factor models
|
|
1063
|
+
- EM algorithm, Kalman filter, numerical utilities
|
|
1064
|
+
- Time series utilities, diagnostics, synthetic DGP
|
|
1065
|
+
- Model-agnostic low-level engine code
|
|
1066
|
+
|
|
1067
|
+
- **`models/`**: Factor model implementations
|
|
1068
|
+
- `base.py`: BaseFactorModel interface
|
|
1069
|
+
- `dfm.py`: Linear Dynamic Factor Model (EM-based)
|
|
1070
|
+
- `ddfm.py`: Deep Dynamic Factor Model (PyTorch-based, fully integrated)
|
|
1071
|
+
|
|
1072
|
+
**Note**: The original DDFM implementation (TensorFlow/Keras) has been archived to `archive/DDFM_original/` for reference. The current implementation is fully integrated in `src/dfm_python/models/ddfm.py`.
|
|
1073
|
+
|
|
1074
|
+
- **`nowcasting/`**: Nowcasting and news decomposition domain logic
|
|
1075
|
+
- `nowcast.py`: Nowcast class and core logic
|
|
1076
|
+
- News decomposition and backtest functionality
|
|
1077
|
+
|
|
1078
|
+
- **`api.py` / `__init__.py`**: Public high-level API
|
|
1079
|
+
- `DFM` class, `create_model()` factory
|
|
1080
|
+
- Module-level convenience functions
|
|
1081
|
+
|
|
1082
|
+
- **`core/`**: Backward compatibility layer (re-exports from `engine/`)
|
|
1083
|
+
- Maintains old import paths for existing code
|
|
1084
|
+
- New code should use `engine.*` directly
|
|
1085
|
+
|
|
919
1086
|
## Testing
|
|
920
1087
|
|
|
921
1088
|
Run the full test suite:
|
|
@@ -928,15 +1095,19 @@ Run specific test categories:
|
|
|
928
1095
|
|
|
929
1096
|
```bash
|
|
930
1097
|
# Core DFM estimation
|
|
931
|
-
pytest src/test/
|
|
1098
|
+
pytest src/test/test_api.py -v
|
|
932
1099
|
|
|
933
1100
|
# Kalman filter/smoother
|
|
934
1101
|
pytest src/test/test_kalman.py -v
|
|
935
1102
|
|
|
936
|
-
#
|
|
937
|
-
pytest src/test/
|
|
1103
|
+
# DDFM tests (requires PyTorch)
|
|
1104
|
+
pytest src/test/test_ddfm.py -v
|
|
1105
|
+
|
|
1106
|
+
# Regression tests (ensure backward compatibility)
|
|
1107
|
+
pytest src/test/test_dfm_regression.py -v
|
|
938
1108
|
|
|
939
|
-
#
|
|
1109
|
+
# Integration tests
|
|
1110
|
+
pytest src/test/test_integration.py -v
|
|
940
1111
|
pytest src/test/test_dfm.py -k "idio" -v
|
|
941
1112
|
```
|
|
942
1113
|
|
|
@@ -1042,26 +1213,18 @@ DATA_VIEW_SOURCE=file # or database
|
|
|
1042
1213
|
**Python**: 3.10+
|
|
1043
1214
|
**PyPI**: https://pypi.org/project/dfm-python/
|
|
1044
1215
|
|
|
1045
|
-
### Recent Improvements (v0.3.
|
|
1046
|
-
|
|
1047
|
-
- ✅ **Polars
|
|
1048
|
-
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
- ✅ **
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
- Automatic error handling and failed step tracking
|
|
1058
|
-
- ✅ **Enhanced Nowcast API**:
|
|
1059
|
-
- `NowcastResult` and `NewsDecompResult` dataclasses for structured results
|
|
1060
|
-
- `return_result` parameter in `__call__` method for full metadata
|
|
1061
|
-
- Improved date parsing and error handling
|
|
1062
|
-
- ✅ **Module Reorganization**: `news.py` functionality integrated into `nowcast.py` for clearer module structure
|
|
1063
|
-
- All nowcasting functions now available via `from dfm_python.nowcast import ...`
|
|
1064
|
-
- `news.py` module removed (functionality fully migrated to `nowcast.py`)
|
|
1216
|
+
### Recent Improvements (v0.3.1)
|
|
1217
|
+
|
|
1218
|
+
- ✅ **Polars-backed Data Views + Kalman Cache**
|
|
1219
|
+
`create_data_view()` now applies release-date masks via Polars, avoiding per-view NumPy copies. `Nowcast.backtest()` reuses cached Kalman filter/smoother states across successive data views, giving large speed-ups for 12-step pseudo real-time tests.
|
|
1220
|
+
- ✅ **Backtest Trajectory Plot**
|
|
1221
|
+
`BacktestResult.plot_trajectory()` draws 이전 vs. 업데이트 nowcast curves plus monthly actuals along a relative week axis (`-12주 … 현재`). The tutorial simply calls this helper—no more hand-built plotting code.
|
|
1222
|
+
- ✅ **Higher-Frequency Backsteps**
|
|
1223
|
+
`Nowcast.backtest(..., backward_steps=12, higher_freq=True)` now emits weekly snapshots even when the model clock is monthly, so you can inspect 12주 worth of “pseudo releases” immediately.
|
|
1224
|
+
- ✅ **Reload & Reuse**
|
|
1225
|
+
`DFM.load_pickle()` restores config, time index, and cached Polars data views so you can call `predict()`/`nowcast()` right after loading without re-reading CSVs.
|
|
1226
|
+
- ✅ **API + Docs Refresh**
|
|
1227
|
+
README/tutorial sections now highlight the faster backtests, new trajectory plots, and guidelines for interpreting flattened Q-matrix values or “strange” news decomposition outputs.
|
|
1065
1228
|
|
|
1066
1229
|
### Previous Improvements (v0.2.9)
|
|
1067
1230
|
|
|
@@ -27,6 +27,7 @@ A Dynamic Factor Model is a powerful statistical framework that:
|
|
|
27
27
|
- ✅ **Robust missing data**: Spline interpolation preprocessing + Kalman filter handling during estimation
|
|
28
28
|
- ✅ **News decomposition**: Attribute forecast changes to specific data releases
|
|
29
29
|
- ✅ **Nowcasting & Forecasting**: Generate predictions for any horizon
|
|
30
|
+
- ✅ **Deep Dynamic Factor Models (DDFM)**: Nonlinear encoder with PyTorch for capturing complex factor structures (optional, requires `dfm-python[deep]`)
|
|
30
31
|
|
|
31
32
|
### Technical Features
|
|
32
33
|
- ✅ **Multiple configuration methods**: YAML files, CSV specs, Python dictionaries, or Hydra
|
|
@@ -54,6 +55,7 @@ pip install dfm-python
|
|
|
54
55
|
Optional dependencies:
|
|
55
56
|
- `hydra-core` (for Hydra configuration management)
|
|
56
57
|
- `matplotlib` (for plotting)
|
|
58
|
+
- `torch>=2.0.0` (for Deep Dynamic Factor Models - install with `pip install dfm-python[deep]`)
|
|
57
59
|
|
|
58
60
|
## Quick Start
|
|
59
61
|
|
|
@@ -90,7 +92,41 @@ smoothed_data = result.X_sm # (100 × 10) Smoothed series
|
|
|
90
92
|
forecasts = result.forecast(horizon=None) # Default: 1 year of periods based on clock frequency
|
|
91
93
|
```
|
|
92
94
|
|
|
93
|
-
### Example 2:
|
|
95
|
+
### Example 2: Deep Dynamic Factor Model (DDFM)
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
import dfm_python as dfm
|
|
99
|
+
from dfm_python import create_model, DFMConfig, SeriesConfig, BlockConfig
|
|
100
|
+
import numpy as np
|
|
101
|
+
|
|
102
|
+
# Generate or load your data
|
|
103
|
+
X = np.random.randn(100, 10) # 100 months, 10 series
|
|
104
|
+
|
|
105
|
+
# Create configuration
|
|
106
|
+
series = [
|
|
107
|
+
SeriesConfig(series_id=f'series_{i}', frequency='m', transformation='lin', blocks=[1])
|
|
108
|
+
for i in range(10)
|
|
109
|
+
]
|
|
110
|
+
config = DFMConfig(
|
|
111
|
+
series=series,
|
|
112
|
+
blocks=[BlockConfig(block_name='Global', factors=1)],
|
|
113
|
+
clock='m',
|
|
114
|
+
factors_per_block=[1],
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Create and fit DDFM model
|
|
118
|
+
ddfm = create_model('ddfm', encoder_layers=[64, 32], num_factors=1, epochs=100)
|
|
119
|
+
result = ddfm.fit(X, config)
|
|
120
|
+
|
|
121
|
+
# Access results (same structure as DFM)
|
|
122
|
+
factors = result.Z # (100 × 1) Extracted factors
|
|
123
|
+
loadings = result.C # (10 × 1) Factor loadings
|
|
124
|
+
X_forecast, Z_forecast = ddfm.predict(horizon=12)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Note**: DDFM requires PyTorch. Install with `pip install dfm-python[deep]`.
|
|
128
|
+
|
|
129
|
+
### Example 3: Mixed-Frequency Data (Monthly + Quarterly)
|
|
94
130
|
|
|
95
131
|
```python
|
|
96
132
|
import dfm_python as dfm
|
|
@@ -556,7 +592,7 @@ Generate datasets for model evaluation across multiple periods:
|
|
|
556
592
|
from datetime import datetime
|
|
557
593
|
|
|
558
594
|
# Define evaluation periods
|
|
559
|
-
from dfm_python.
|
|
595
|
+
from dfm_python.engine.time import datetime_range
|
|
560
596
|
periods = datetime_range(start=datetime(2020, 1, 1), end=datetime(2023, 12, 31), freq='QE')
|
|
561
597
|
|
|
562
598
|
# Generate evaluation dataset
|
|
@@ -875,6 +911,132 @@ Complete reference for `DFMResult`:
|
|
|
875
911
|
6. **Flexible configuration**: Multiple input methods (YAML, CSV, Dict, Hydra)
|
|
876
912
|
7. **Backward compatibility**: Old code continues to work, new features are opt-in
|
|
877
913
|
|
|
914
|
+
## Deep Dynamic Factor Models (DDFM)
|
|
915
|
+
|
|
916
|
+
The package includes support for **Deep Dynamic Factor Models (DDFM)**, which use nonlinear encoders to extract factors from observed data. DDFM is particularly useful when the relationship between observed variables and latent factors is nonlinear.
|
|
917
|
+
|
|
918
|
+
**Note**: DDFM has a **separate high-level API class** (`dfm_python.DDFM`) from the linear DFM class (`dfm_python.DFM`). Both classes share the same interface (load_config, load_data, train, predict, plot) but use different underlying models.
|
|
919
|
+
|
|
920
|
+
### When to Use DDFM vs Linear DFM
|
|
921
|
+
|
|
922
|
+
- **Use Linear DFM** when:
|
|
923
|
+
- The relationship between factors and observations is approximately linear
|
|
924
|
+
- You need interpretable factor loadings
|
|
925
|
+
- Computational speed is important
|
|
926
|
+
- You have limited data
|
|
927
|
+
|
|
928
|
+
- **Use DDFM** when:
|
|
929
|
+
- The relationship between factors and observations is nonlinear
|
|
930
|
+
- You suspect complex interactions between variables
|
|
931
|
+
- You have sufficient data for training neural networks
|
|
932
|
+
- You want to capture non-Gaussian factor structures
|
|
933
|
+
|
|
934
|
+
### DDFM Architecture
|
|
935
|
+
|
|
936
|
+
The DDFM implementation uses:
|
|
937
|
+
- **Nonlinear Encoder**: Multi-layer perceptron (MLP) to extract factors from observations
|
|
938
|
+
- **Linear Decoder**: Linear transformation from factors back to observations (for interpretability)
|
|
939
|
+
- **Linear Dynamics**: VAR(1) or VAR(2) dynamics for factors (estimated via OLS)
|
|
940
|
+
- **Idiosyncratic Modeling**: AR(1) dynamics for residual components (optional)
|
|
941
|
+
- **Kalman Smoothing**: Final smoothing step using Kalman filter with full state-space (factors + idio)
|
|
942
|
+
|
|
943
|
+
**Key Features:**
|
|
944
|
+
- **VAR(2) Support**: Can model factor dynamics with up to 2 lags using companion form
|
|
945
|
+
- **Idiosyncratic Components**: Models residual components with AR(1) dynamics for better fit
|
|
946
|
+
- **Decoder Parameter Extraction**: Directly extracts observation matrix from trained decoder (no OLS re-estimation)
|
|
947
|
+
- **Full State-Space**: Complete state-space model with factors and idio in state vector
|
|
948
|
+
|
|
949
|
+
### DDFM Examples
|
|
950
|
+
|
|
951
|
+
#### Example 1: Basic DDFM Usage
|
|
952
|
+
|
|
953
|
+
```python
|
|
954
|
+
from dfm_python import create_model, DFMConfig, SeriesConfig, BlockConfig
|
|
955
|
+
import numpy as np
|
|
956
|
+
|
|
957
|
+
# Create data
|
|
958
|
+
X = np.random.randn(200, 15) # 200 time periods, 15 series
|
|
959
|
+
|
|
960
|
+
# Create config
|
|
961
|
+
series = [
|
|
962
|
+
SeriesConfig(series_id=f'series_{i}', frequency='m', transformation='lin', blocks=[1])
|
|
963
|
+
for i in range(15)
|
|
964
|
+
]
|
|
965
|
+
config = DFMConfig(
|
|
966
|
+
series=series,
|
|
967
|
+
blocks={'Block_Global': BlockConfig(factors=2, ar_lag=1, clock='m')},
|
|
968
|
+
clock='m',
|
|
969
|
+
)
|
|
970
|
+
|
|
971
|
+
# Create DDFM model
|
|
972
|
+
ddfm = create_model(
|
|
973
|
+
'ddfm',
|
|
974
|
+
encoder_layers=[64, 32, 16], # Encoder architecture
|
|
975
|
+
num_factors=2, # Number of factors
|
|
976
|
+
activation='tanh', # Activation function
|
|
977
|
+
epochs=100, # Training epochs
|
|
978
|
+
batch_size=32, # Batch size
|
|
979
|
+
learning_rate=0.001, # Learning rate
|
|
980
|
+
)
|
|
981
|
+
|
|
982
|
+
# Fit model
|
|
983
|
+
result = ddfm.fit(X, config)
|
|
984
|
+
|
|
985
|
+
# Use results (same API as linear DFM)
|
|
986
|
+
factors = result.Z
|
|
987
|
+
loadings = result.C
|
|
988
|
+
X_forecast, Z_forecast = ddfm.predict(horizon=12)
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
### Testing DDFM
|
|
992
|
+
|
|
993
|
+
The package includes synthetic DGP tests for validating DDFM:
|
|
994
|
+
|
|
995
|
+
```python
|
|
996
|
+
from dfm_python.engine.synthetic_dgp import SyntheticDGP
|
|
997
|
+
from dfm_python import create_model
|
|
998
|
+
|
|
999
|
+
# Create synthetic data with known factors
|
|
1000
|
+
dgp = SyntheticDGP(seed=42, n=10, r=1, poly_degree=2) # Nonlinear factors
|
|
1001
|
+
X = dgp.simulate(200)
|
|
1002
|
+
|
|
1003
|
+
# Fit DDFM
|
|
1004
|
+
ddfm = create_model('ddfm', encoder_layers=[64, 32], num_factors=1)
|
|
1005
|
+
result = ddfm.fit(X, config)
|
|
1006
|
+
|
|
1007
|
+
# Evaluate factor recovery
|
|
1008
|
+
r2 = dgp.evaluate(result.Z) # Trace R² metric
|
|
1009
|
+
print(f"Factor recovery R²: {r2:.3f}")
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
## Package Structure
|
|
1013
|
+
|
|
1014
|
+
The package is organized into clear layers for better maintainability and extensibility:
|
|
1015
|
+
|
|
1016
|
+
- **`engine/`**: Pure mathematical/algorithmic code shared by all factor models
|
|
1017
|
+
- EM algorithm, Kalman filter, numerical utilities
|
|
1018
|
+
- Time series utilities, diagnostics, synthetic DGP
|
|
1019
|
+
- Model-agnostic low-level engine code
|
|
1020
|
+
|
|
1021
|
+
- **`models/`**: Factor model implementations
|
|
1022
|
+
- `base.py`: BaseFactorModel interface
|
|
1023
|
+
- `dfm.py`: Linear Dynamic Factor Model (EM-based)
|
|
1024
|
+
- `ddfm.py`: Deep Dynamic Factor Model (PyTorch-based, fully integrated)
|
|
1025
|
+
|
|
1026
|
+
**Note**: The original DDFM implementation (TensorFlow/Keras) has been archived to `archive/DDFM_original/` for reference. The current implementation is fully integrated in `src/dfm_python/models/ddfm.py`.
|
|
1027
|
+
|
|
1028
|
+
- **`nowcasting/`**: Nowcasting and news decomposition domain logic
|
|
1029
|
+
- `nowcast.py`: Nowcast class and core logic
|
|
1030
|
+
- News decomposition and backtest functionality
|
|
1031
|
+
|
|
1032
|
+
- **`api.py` / `__init__.py`**: Public high-level API
|
|
1033
|
+
- `DFM` class, `create_model()` factory
|
|
1034
|
+
- Module-level convenience functions
|
|
1035
|
+
|
|
1036
|
+
- **`core/`**: Backward compatibility layer (re-exports from `engine/`)
|
|
1037
|
+
- Maintains old import paths for existing code
|
|
1038
|
+
- New code should use `engine.*` directly
|
|
1039
|
+
|
|
878
1040
|
## Testing
|
|
879
1041
|
|
|
880
1042
|
Run the full test suite:
|
|
@@ -887,15 +1049,19 @@ Run specific test categories:
|
|
|
887
1049
|
|
|
888
1050
|
```bash
|
|
889
1051
|
# Core DFM estimation
|
|
890
|
-
pytest src/test/
|
|
1052
|
+
pytest src/test/test_api.py -v
|
|
891
1053
|
|
|
892
1054
|
# Kalman filter/smoother
|
|
893
1055
|
pytest src/test/test_kalman.py -v
|
|
894
1056
|
|
|
895
|
-
#
|
|
896
|
-
pytest src/test/
|
|
1057
|
+
# DDFM tests (requires PyTorch)
|
|
1058
|
+
pytest src/test/test_ddfm.py -v
|
|
1059
|
+
|
|
1060
|
+
# Regression tests (ensure backward compatibility)
|
|
1061
|
+
pytest src/test/test_dfm_regression.py -v
|
|
897
1062
|
|
|
898
|
-
#
|
|
1063
|
+
# Integration tests
|
|
1064
|
+
pytest src/test/test_integration.py -v
|
|
899
1065
|
pytest src/test/test_dfm.py -k "idio" -v
|
|
900
1066
|
```
|
|
901
1067
|
|
|
@@ -1001,26 +1167,18 @@ DATA_VIEW_SOURCE=file # or database
|
|
|
1001
1167
|
**Python**: 3.10+
|
|
1002
1168
|
**PyPI**: https://pypi.org/project/dfm-python/
|
|
1003
1169
|
|
|
1004
|
-
### Recent Improvements (v0.3.
|
|
1005
|
-
|
|
1006
|
-
- ✅ **Polars
|
|
1007
|
-
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
- ✅ **
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
- Automatic error handling and failed step tracking
|
|
1017
|
-
- ✅ **Enhanced Nowcast API**:
|
|
1018
|
-
- `NowcastResult` and `NewsDecompResult` dataclasses for structured results
|
|
1019
|
-
- `return_result` parameter in `__call__` method for full metadata
|
|
1020
|
-
- Improved date parsing and error handling
|
|
1021
|
-
- ✅ **Module Reorganization**: `news.py` functionality integrated into `nowcast.py` for clearer module structure
|
|
1022
|
-
- All nowcasting functions now available via `from dfm_python.nowcast import ...`
|
|
1023
|
-
- `news.py` module removed (functionality fully migrated to `nowcast.py`)
|
|
1170
|
+
### Recent Improvements (v0.3.1)
|
|
1171
|
+
|
|
1172
|
+
- ✅ **Polars-backed Data Views + Kalman Cache**
|
|
1173
|
+
`create_data_view()` now applies release-date masks via Polars, avoiding per-view NumPy copies. `Nowcast.backtest()` reuses cached Kalman filter/smoother states across successive data views, giving large speed-ups for 12-step pseudo real-time tests.
|
|
1174
|
+
- ✅ **Backtest Trajectory Plot**
|
|
1175
|
+
`BacktestResult.plot_trajectory()` draws 이전 vs. 업데이트 nowcast curves plus monthly actuals along a relative week axis (`-12주 … 현재`). The tutorial simply calls this helper—no more hand-built plotting code.
|
|
1176
|
+
- ✅ **Higher-Frequency Backsteps**
|
|
1177
|
+
`Nowcast.backtest(..., backward_steps=12, higher_freq=True)` now emits weekly snapshots even when the model clock is monthly, so you can inspect 12주 worth of “pseudo releases” immediately.
|
|
1178
|
+
- ✅ **Reload & Reuse**
|
|
1179
|
+
`DFM.load_pickle()` restores config, time index, and cached Polars data views so you can call `predict()`/`nowcast()` right after loading without re-reading CSVs.
|
|
1180
|
+
- ✅ **API + Docs Refresh**
|
|
1181
|
+
README/tutorial sections now highlight the faster backtests, new trajectory plots, and guidelines for interpreting flattened Q-matrix values or “strange” news decomposition outputs.
|
|
1024
1182
|
|
|
1025
1183
|
### Previous Improvements (v0.2.9)
|
|
1026
1184
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Example DDFM (Deep Dynamic Factor Model) Configuration
|
|
2
|
+
# This configuration demonstrates all DDFM-specific parameters
|
|
3
|
+
|
|
4
|
+
# Standard DFM parameters
|
|
5
|
+
ar_lag: 1
|
|
6
|
+
threshold: 1e-5
|
|
7
|
+
max_iter: 5000
|
|
8
|
+
nan_method: 2
|
|
9
|
+
nan_k: 3
|
|
10
|
+
clock: 'm'
|
|
11
|
+
|
|
12
|
+
# DDFM-specific parameters
|
|
13
|
+
ddfm_encoder_layers: [64, 32, 16] # Hidden layer dimensions for encoder
|
|
14
|
+
ddfm_num_factors: 2 # Number of factors (optional, inferred from blocks if None)
|
|
15
|
+
ddfm_activation: 'tanh' # Activation function: 'tanh', 'relu', or 'sigmoid'
|
|
16
|
+
ddfm_use_batch_norm: true # Use batch normalization in encoder
|
|
17
|
+
ddfm_learning_rate: 0.001 # Learning rate for Adam optimizer
|
|
18
|
+
ddfm_epochs: 100 # Number of training epochs
|
|
19
|
+
ddfm_batch_size: 32 # Batch size for training
|
|
20
|
+
ddfm_factor_order: 2 # VAR lag order for factor dynamics (1 or 2)
|
|
21
|
+
ddfm_use_idiosyncratic: true # Model idiosyncratic components with AR(1) dynamics
|
|
22
|
+
ddfm_min_obs_idio: 5 # Minimum observations for idio AR(1) estimation
|
|
23
|
+
|
|
24
|
+
# Note: Series and blocks configuration should be provided separately
|
|
25
|
+
# via series config files or spec CSV, or merged with this config
|
|
26
|
+
|