dfm-python 0.1.5__tar.gz → 0.1.8__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.1.5 → dfm_python-0.1.8}/CHANGELOG.md +35 -0
- dfm_python-0.1.8/PKG-INFO +343 -0
- dfm_python-0.1.8/README.md +300 -0
- dfm_python-0.1.8/config/blocks/default.yaml +55 -0
- dfm_python-0.1.8/config/default.yaml +69 -0
- dfm_python-0.1.8/config/series/default.yaml +699 -0
- dfm_python-0.1.8/data/sample_data.csv +490 -0
- dfm_python-0.1.8/data/sample_spec.csv +85 -0
- {dfm_python-0.1.5 → dfm_python-0.1.8}/pyproject.toml +2 -1
- dfm_python-0.1.8/src/__init__.py +4 -0
- dfm_python-0.1.8/src/dfm_python/__init__.py +146 -0
- dfm_python-0.1.8/src/dfm_python/config.py +746 -0
- dfm_python-0.1.8/src/dfm_python/data_loader.py +836 -0
- dfm_python-0.1.8/src/dfm_python/dfm.py +3815 -0
- dfm_python-0.1.8/src/dfm_python/dfm_api.py +328 -0
- {dfm_python-0.1.5 → dfm_python-0.1.8}/src/dfm_python/kalman.py +0 -12
- {dfm_python-0.1.5 → dfm_python-0.1.8}/src/dfm_python/news.py +28 -38
- {dfm_python-0.1.5 → dfm_python-0.1.8}/src/dfm_python/utils/__init__.py +0 -2
- {dfm_python-0.1.5 → dfm_python-0.1.8}/src/dfm_python/utils/aggregation.py +4 -11
- {dfm_python-0.1.5 → dfm_python-0.1.8}/src/dfm_python/utils/data_utils.py +20 -8
- dfm_python-0.1.8/src/test/test_dfm.py +373 -0
- dfm_python-0.1.8/src/test/test_kalman.py +184 -0
- dfm_python-0.1.8/src/test/test_news.py +100 -0
- dfm_python-0.1.8/trash/simple_api_example.py +41 -0
- dfm_python-0.1.8/trash/test_dfm_edge_cases.py +552 -0
- dfm_python-0.1.8/trash/test_dfm_stress.py +377 -0
- dfm_python-0.1.8/trash/test_synthetic.py +130 -0
- dfm_python-0.1.8/tutorial/basic_tutorial.ipynb +577 -0
- dfm_python-0.1.8/tutorial/basic_tutorial.py +342 -0
- {dfm_python-0.1.5 → dfm_python-0.1.8}/uv.lock +323 -1
- dfm_python-0.1.5/PKG-INFO +0 -572
- dfm_python-0.1.5/README.md +0 -530
- dfm_python-0.1.5/config/example_config.yaml +0 -149
- dfm_python-0.1.5/config/example_config_macro_data.yaml +0 -214
- dfm_python-0.1.5/data/example_macro_data.csv +0 -18
- dfm_python-0.1.5/data/sample_data.csv +0 -193
- dfm_python-0.1.5/scripts/inference.py +0 -226
- dfm_python-0.1.5/scripts/train.py +0 -282
- dfm_python-0.1.5/spec/example_spec.csv +0 -13
- dfm_python-0.1.5/src/__init__.py +0 -17
- dfm_python-0.1.5/src/dfm_python/__init__.py +0 -60
- dfm_python-0.1.5/src/dfm_python/config.py +0 -556
- dfm_python-0.1.5/src/dfm_python/data_loader.py +0 -540
- dfm_python-0.1.5/src/dfm_python/dfm.py +0 -3120
- dfm_python-0.1.5/src/test/test_dfm.py +0 -460
- dfm_python-0.1.5/src/test/test_kalman.py +0 -325
- dfm_python-0.1.5/src/test/test_news.py +0 -229
- dfm_python-0.1.5/src/test/test_synthetic.py +0 -135
- dfm_python-0.1.5/test_comprehensive.py +0 -510
- {dfm_python-0.1.5 → dfm_python-0.1.8}/.gitignore +0 -0
- {dfm_python-0.1.5 → dfm_python-0.1.8}/.python-version +0 -0
- {dfm_python-0.1.5 → dfm_python-0.1.8}/LICENSE +0 -0
- {dfm_python-0.1.5 → dfm_python-0.1.8}/MANIFEST.in +0 -0
- {dfm_python-0.1.5 → dfm_python-0.1.8}/src/test/__init__.py +0 -0
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.7] - 2025-01-XX
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- **BREAKING**: Removed CSV config loading - use YAML files or create `DFMConfig` objects directly
|
|
7
|
+
- Made package more generic by removing application-specific dependencies
|
|
8
|
+
- Improved code documentation and comments throughout codebase
|
|
9
|
+
- Refactored variable naming for clarity and consistency
|
|
10
|
+
- Updated README to be more concise and focused
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Direct `DFMConfig` object creation support in `load_config()`
|
|
14
|
+
- Application-specific adapter guidance for custom formats
|
|
15
|
+
- Database-backed data loading adapter interface documentation
|
|
16
|
+
- Enhanced function docstrings with detailed parameter descriptions
|
|
17
|
+
|
|
18
|
+
### Removed
|
|
19
|
+
- CSV configuration file support (deprecated, use YAML or direct object creation)
|
|
20
|
+
- `load_config_from_csv()` function (deprecated, kept for backward compatibility with warning)
|
|
21
|
+
- Spec file dependencies
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- Improved error messages for frequency constraint violations
|
|
25
|
+
- Enhanced type hints and documentation
|
|
26
|
+
|
|
27
|
+
## [0.1.6] - Previous release
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- Frequency generalization improvements
|
|
31
|
+
- Code cleanup and refactoring
|
|
32
|
+
|
|
33
|
+
## [0.1.5] - Previous release
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
- Additional improvements and bug fixes
|
|
37
|
+
|
|
3
38
|
## [0.1.4] - 2024-11-07
|
|
4
39
|
|
|
5
40
|
### Fixed
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dfm-python
|
|
3
|
+
Version: 0.1.8
|
|
4
|
+
Summary: Dynamic Factor Model (DFM) estimation and nowcasting in Python
|
|
5
|
+
Project-URL: Homepage, https://github.com/yourusername/dfm-python
|
|
6
|
+
Project-URL: Documentation, https://github.com/yourusername/dfm-python#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/yourusername/dfm-python
|
|
8
|
+
Project-URL: Issues, https://github.com/yourusername/dfm-python/issues
|
|
9
|
+
Author: DFM Python Contributors
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: dfm,dynamic-factor-model,econometrics,forecasting,nowcasting,time-series
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Requires-Dist: matplotlib>=3.10.7
|
|
21
|
+
Requires-Dist: numpy>=1.24.0
|
|
22
|
+
Requires-Dist: pandas>=2.0.0
|
|
23
|
+
Requires-Dist: scipy>=1.10.0
|
|
24
|
+
Provides-Extra: all
|
|
25
|
+
Requires-Dist: httpx>=0.24.0; extra == 'all'
|
|
26
|
+
Requires-Dist: hydra-core>=1.3.0; extra == 'all'
|
|
27
|
+
Requires-Dist: omegaconf>=2.3.0; extra == 'all'
|
|
28
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == 'all'
|
|
29
|
+
Requires-Dist: pydantic-settings>=2.11.0; extra == 'all'
|
|
30
|
+
Requires-Dist: pytest>=7.0.0; extra == 'all'
|
|
31
|
+
Requires-Dist: supabase>=2.0.0; extra == 'all'
|
|
32
|
+
Provides-Extra: database
|
|
33
|
+
Requires-Dist: httpx>=0.24.0; extra == 'database'
|
|
34
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == 'database'
|
|
35
|
+
Requires-Dist: pydantic-settings>=2.11.0; extra == 'database'
|
|
36
|
+
Requires-Dist: supabase>=2.0.0; extra == 'database'
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
39
|
+
Provides-Extra: hydra
|
|
40
|
+
Requires-Dist: hydra-core>=1.3.0; extra == 'hydra'
|
|
41
|
+
Requires-Dist: omegaconf>=2.3.0; extra == 'hydra'
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
|
|
44
|
+
# dfm-python: Dynamic Factor Models for Python
|
|
45
|
+
|
|
46
|
+
[](https://pypi.org/project/dfm-python/)
|
|
47
|
+
[](https://www.python.org/downloads/)
|
|
48
|
+
|
|
49
|
+
A Python implementation of **Dynamic Factor Models (DFM)** for nowcasting and forecasting high-dimensional time series. Implements clock-based synchronization and tent kernel aggregation for mixed-frequency data.
|
|
50
|
+
|
|
51
|
+
## Features
|
|
52
|
+
|
|
53
|
+
- **Mixed-frequency data**: Monthly, quarterly, semi-annual, annual (no daily/weekly)
|
|
54
|
+
- **Clock-based framework**: All factors evolve at a common clock frequency
|
|
55
|
+
- **Block structure**: Flexible factor organization (global, sector-specific)
|
|
56
|
+
- **Robust missing data**: Spline interpolation and Kalman filter handling
|
|
57
|
+
- **News decomposition**: Attribute forecast changes to data releases
|
|
58
|
+
- **Generic design**: YAML configs or direct object creation, extensible via adapters
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install dfm-python
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Requirements**: Python >= 3.12, numpy >= 1.24.0, pandas >= 2.0.0, scipy >= 1.10.0
|
|
67
|
+
|
|
68
|
+
## Quick Start
|
|
69
|
+
|
|
70
|
+
### Option 1: YAML Configuration
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from dfm_python import load_config, load_data, dfm
|
|
74
|
+
|
|
75
|
+
# Load configuration from YAML
|
|
76
|
+
config = load_config('config.yaml')
|
|
77
|
+
X, Time, _ = load_data('data.csv', config)
|
|
78
|
+
|
|
79
|
+
# Estimate model
|
|
80
|
+
result = dfm(X, config)
|
|
81
|
+
|
|
82
|
+
# Access results
|
|
83
|
+
factors = result.Z # (T × m) Smoothed factors
|
|
84
|
+
loadings = result.C # (N × m) Factor loadings
|
|
85
|
+
smoothed = result.X_sm # (T × N) Smoothed data
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Option 2: Direct Configuration
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from dfm_python import DFMConfig, SeriesConfig, load_data, dfm
|
|
92
|
+
|
|
93
|
+
# Create configuration programmatically
|
|
94
|
+
config = DFMConfig(
|
|
95
|
+
series=[
|
|
96
|
+
SeriesConfig(
|
|
97
|
+
series_id='gdp_real',
|
|
98
|
+
series_name='Real GDP',
|
|
99
|
+
frequency='q',
|
|
100
|
+
transformation='pca',
|
|
101
|
+
category='National Accounts',
|
|
102
|
+
units='Index',
|
|
103
|
+
blocks=[1] # Loads on first block (global)
|
|
104
|
+
),
|
|
105
|
+
SeriesConfig(
|
|
106
|
+
series_id='consumption',
|
|
107
|
+
series_name='Consumption',
|
|
108
|
+
frequency='m',
|
|
109
|
+
transformation='pch',
|
|
110
|
+
category='Consumption',
|
|
111
|
+
units='Index',
|
|
112
|
+
blocks=[1]
|
|
113
|
+
)
|
|
114
|
+
],
|
|
115
|
+
block_names=['Global'],
|
|
116
|
+
clock='m',
|
|
117
|
+
threshold=1e-5,
|
|
118
|
+
max_iter=5000
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Load data and estimate
|
|
122
|
+
X, Time, _ = load_data('data.csv', config)
|
|
123
|
+
result = dfm(X, config)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Configuration
|
|
127
|
+
|
|
128
|
+
### YAML Format
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
dfm:
|
|
132
|
+
clock: "m" # Clock frequency (base for all factors)
|
|
133
|
+
threshold: 1e-5 # EM convergence threshold
|
|
134
|
+
max_iter: 5000 # Maximum EM iterations
|
|
135
|
+
ar_lag: 1 # AR lag for factors
|
|
136
|
+
|
|
137
|
+
series:
|
|
138
|
+
gdp_real:
|
|
139
|
+
series_name: "Real GDP"
|
|
140
|
+
frequency: "q"
|
|
141
|
+
transformation: "pca"
|
|
142
|
+
category: "National Accounts"
|
|
143
|
+
units: "Index"
|
|
144
|
+
blocks: [Global]
|
|
145
|
+
|
|
146
|
+
consumption:
|
|
147
|
+
series_name: "Consumption"
|
|
148
|
+
frequency: "m"
|
|
149
|
+
transformation: "pch"
|
|
150
|
+
category: "Consumption"
|
|
151
|
+
units: "Index"
|
|
152
|
+
blocks: [Global]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Key Parameters
|
|
156
|
+
|
|
157
|
+
- **clock**: Base frequency for latent factors (typically "m" for monthly)
|
|
158
|
+
- **threshold**: EM convergence criterion (default: 1e-5)
|
|
159
|
+
- **max_iter**: Maximum EM iterations (default: 5000)
|
|
160
|
+
- **ar_lag**: Autoregressive lag for factors (default: 1)
|
|
161
|
+
|
|
162
|
+
### Numerical Stability
|
|
163
|
+
|
|
164
|
+
All numerical stability techniques are configurable and transparent:
|
|
165
|
+
|
|
166
|
+
```yaml
|
|
167
|
+
dfm:
|
|
168
|
+
clip_ar_coefficients: true # Ensure stationarity
|
|
169
|
+
clip_data_values: true # Clip extreme outliers
|
|
170
|
+
use_regularization: true # Prevent ill-conditioned matrices
|
|
171
|
+
use_damped_updates: true # Prevent likelihood decreases
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Warnings are logged when techniques are applied. All parameters can be disabled for research purposes.
|
|
175
|
+
|
|
176
|
+
## Data Format
|
|
177
|
+
|
|
178
|
+
### File-Based (CSV)
|
|
179
|
+
|
|
180
|
+
Data file with series as columns and dates as rows:
|
|
181
|
+
|
|
182
|
+
```csv
|
|
183
|
+
Date,gdp_real,consumption,investment
|
|
184
|
+
2000-01-01,,98.5,95.0
|
|
185
|
+
2000-02-01,,98.7,95.2
|
|
186
|
+
2000-03-01,100.5,99.0,95.5
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Requirements**:
|
|
190
|
+
- First column: `Date` (YYYY-MM-DD format)
|
|
191
|
+
- Column names: Must match `series_id` in configuration
|
|
192
|
+
- Missing values: Empty cells or NaN
|
|
193
|
+
- Mixed frequencies: Quarterly series only at quarter-end months
|
|
194
|
+
|
|
195
|
+
**Frequency Support**:
|
|
196
|
+
- **Supported**: Monthly (m), Quarterly (q), Semi-annual (sa), Annual (a)
|
|
197
|
+
- **Not supported**: Daily (d), Weekly (w) - raises ValueError
|
|
198
|
+
- All series must have frequency ≤ clock frequency
|
|
199
|
+
|
|
200
|
+
### Database-Backed
|
|
201
|
+
|
|
202
|
+
For production applications, implement adapters that return:
|
|
203
|
+
- `X`: Transformed data matrix (T × N)
|
|
204
|
+
- `Time`: Time index (pandas DatetimeIndex)
|
|
205
|
+
- `Z`: Original untransformed data (T × N)
|
|
206
|
+
|
|
207
|
+
## Usage Examples
|
|
208
|
+
|
|
209
|
+
### Basic Estimation
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
from dfm_python import load_config, load_data, dfm
|
|
213
|
+
|
|
214
|
+
config = load_config('config.yaml')
|
|
215
|
+
X, Time, _ = load_data('data.csv', config)
|
|
216
|
+
result = dfm(X, config)
|
|
217
|
+
|
|
218
|
+
# Extract common factor
|
|
219
|
+
common_factor = result.Z[:, 0]
|
|
220
|
+
|
|
221
|
+
# Model fit
|
|
222
|
+
rmse = result.rmse
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### News Decomposition
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
from dfm_python import news_dfm
|
|
229
|
+
|
|
230
|
+
# Estimate on old and new data
|
|
231
|
+
result_old = dfm(X_old, config)
|
|
232
|
+
result_new = dfm(X_new, config)
|
|
233
|
+
|
|
234
|
+
# Decompose news
|
|
235
|
+
y_old, y_new, singlenews, actual, forecast, weight, t_miss, v_miss, innov = news_dfm(
|
|
236
|
+
X_old, result_old, t_fcst=100, v_news=0
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# Forecast update
|
|
240
|
+
forecast_update = y_new - y_old
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## API Reference
|
|
244
|
+
|
|
245
|
+
### `load_config(configfile) -> DFMConfig`
|
|
246
|
+
|
|
247
|
+
Load configuration from YAML file or return existing DFMConfig object.
|
|
248
|
+
|
|
249
|
+
**Parameters**:
|
|
250
|
+
- `configfile`: Path to YAML file (.yaml, .yml) or `DFMConfig` object
|
|
251
|
+
|
|
252
|
+
**Returns**: `DFMConfig` object
|
|
253
|
+
|
|
254
|
+
### `load_data(datafile, config, sample_start=None) -> Tuple[np.ndarray, pd.DatetimeIndex, np.ndarray]`
|
|
255
|
+
|
|
256
|
+
Load and transform time series data.
|
|
257
|
+
|
|
258
|
+
**Parameters**:
|
|
259
|
+
- `datafile`: Path to data file (CSV supported)
|
|
260
|
+
- `config`: `DFMConfig` object
|
|
261
|
+
- `sample_start`: Optional start date (YYYY-MM-DD) to filter data
|
|
262
|
+
|
|
263
|
+
**Returns**: `(X, Time, Z)` where:
|
|
264
|
+
- `X`: Transformed data matrix (T × N)
|
|
265
|
+
- `Time`: Time index (DatetimeIndex)
|
|
266
|
+
- `Z`: Original untransformed data (T × N)
|
|
267
|
+
|
|
268
|
+
### `dfm(X, config, threshold=None, max_iter=None) -> DFMResult`
|
|
269
|
+
|
|
270
|
+
Estimate Dynamic Factor Model using EM algorithm.
|
|
271
|
+
|
|
272
|
+
**Parameters**:
|
|
273
|
+
- `X`: Data matrix (T × N) with possible missing values
|
|
274
|
+
- `config`: `DFMConfig` object
|
|
275
|
+
- `threshold`: Convergence threshold (default: from config)
|
|
276
|
+
- `max_iter`: Maximum iterations (default: from config)
|
|
277
|
+
|
|
278
|
+
**Returns**: `DFMResult` object
|
|
279
|
+
|
|
280
|
+
### `DFMResult` Object
|
|
281
|
+
|
|
282
|
+
Contains all estimation outputs:
|
|
283
|
+
|
|
284
|
+
- **Factors**: `Z` (T × m), `C` (N × m)
|
|
285
|
+
- **Parameters**: `A` (m × m), `Q` (m × m), `R` (N × N)
|
|
286
|
+
- **Smoothed Data**: `X_sm` (T × N), `x_sm` (T × N)
|
|
287
|
+
- **Initial Conditions**: `Z_0` (m,), `V_0` (m × m)
|
|
288
|
+
- **Standardization**: `Mx` (N,), `Wx` (N,)
|
|
289
|
+
- **Convergence**: `converged`, `num_iter`, `loglik`
|
|
290
|
+
- **Model Fit**: `rmse`, `rmse_per_series`
|
|
291
|
+
|
|
292
|
+
## Architecture
|
|
293
|
+
|
|
294
|
+
**Core Modules**:
|
|
295
|
+
- `config.py`: Configuration management
|
|
296
|
+
- `data_loader.py`: Data loading and transformation
|
|
297
|
+
- `dfm.py`: Core estimation (EM algorithm)
|
|
298
|
+
- `kalman.py`: Kalman filter and smoother
|
|
299
|
+
- `news.py`: News decomposition
|
|
300
|
+
|
|
301
|
+
**Design Principles**:
|
|
302
|
+
- Generic core (no application-specific code)
|
|
303
|
+
- Flexible configuration (YAML or direct object creation)
|
|
304
|
+
- Extensible via application-specific adapters
|
|
305
|
+
- Robust error handling and numerical stability
|
|
306
|
+
|
|
307
|
+
## Troubleshooting
|
|
308
|
+
|
|
309
|
+
**Convergence Issues**:
|
|
310
|
+
- Increase `max_iter` (try 10000)
|
|
311
|
+
- Relax `threshold` (try 1e-3)
|
|
312
|
+
- Check data quality and missing data patterns
|
|
313
|
+
|
|
314
|
+
**Dimension Mismatch**:
|
|
315
|
+
- Ensure `series_id` in config matches data column names
|
|
316
|
+
- Verify block structure consistency
|
|
317
|
+
- Check frequency codes are valid
|
|
318
|
+
|
|
319
|
+
**Numerical Instability**:
|
|
320
|
+
- Monitor warnings for stability techniques
|
|
321
|
+
- Adjust thresholds based on data characteristics
|
|
322
|
+
- Investigate data quality if techniques frequently needed
|
|
323
|
+
|
|
324
|
+
## License
|
|
325
|
+
|
|
326
|
+
MIT License
|
|
327
|
+
|
|
328
|
+
## Citation
|
|
329
|
+
|
|
330
|
+
```bibtex
|
|
331
|
+
@software{dfm-python,
|
|
332
|
+
title = {dfm-python: Dynamic Factor Models for Nowcasting and Forecasting},
|
|
333
|
+
author = {DFM Python Contributors},
|
|
334
|
+
year = {2025},
|
|
335
|
+
url = {https://pypi.org/project/dfm-python/}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
**Package Status**: Stable
|
|
342
|
+
**PyPI**: https://pypi.org/project/dfm-python/
|
|
343
|
+
**Python**: 3.12+
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# dfm-python: Dynamic Factor Models for Python
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/dfm-python/)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
|
|
6
|
+
A Python implementation of **Dynamic Factor Models (DFM)** for nowcasting and forecasting high-dimensional time series. Implements clock-based synchronization and tent kernel aggregation for mixed-frequency data.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Mixed-frequency data**: Monthly, quarterly, semi-annual, annual (no daily/weekly)
|
|
11
|
+
- **Clock-based framework**: All factors evolve at a common clock frequency
|
|
12
|
+
- **Block structure**: Flexible factor organization (global, sector-specific)
|
|
13
|
+
- **Robust missing data**: Spline interpolation and Kalman filter handling
|
|
14
|
+
- **News decomposition**: Attribute forecast changes to data releases
|
|
15
|
+
- **Generic design**: YAML configs or direct object creation, extensible via adapters
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install dfm-python
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Requirements**: Python >= 3.12, numpy >= 1.24.0, pandas >= 2.0.0, scipy >= 1.10.0
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### Option 1: YAML Configuration
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from dfm_python import load_config, load_data, dfm
|
|
31
|
+
|
|
32
|
+
# Load configuration from YAML
|
|
33
|
+
config = load_config('config.yaml')
|
|
34
|
+
X, Time, _ = load_data('data.csv', config)
|
|
35
|
+
|
|
36
|
+
# Estimate model
|
|
37
|
+
result = dfm(X, config)
|
|
38
|
+
|
|
39
|
+
# Access results
|
|
40
|
+
factors = result.Z # (T × m) Smoothed factors
|
|
41
|
+
loadings = result.C # (N × m) Factor loadings
|
|
42
|
+
smoothed = result.X_sm # (T × N) Smoothed data
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Option 2: Direct Configuration
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from dfm_python import DFMConfig, SeriesConfig, load_data, dfm
|
|
49
|
+
|
|
50
|
+
# Create configuration programmatically
|
|
51
|
+
config = DFMConfig(
|
|
52
|
+
series=[
|
|
53
|
+
SeriesConfig(
|
|
54
|
+
series_id='gdp_real',
|
|
55
|
+
series_name='Real GDP',
|
|
56
|
+
frequency='q',
|
|
57
|
+
transformation='pca',
|
|
58
|
+
category='National Accounts',
|
|
59
|
+
units='Index',
|
|
60
|
+
blocks=[1] # Loads on first block (global)
|
|
61
|
+
),
|
|
62
|
+
SeriesConfig(
|
|
63
|
+
series_id='consumption',
|
|
64
|
+
series_name='Consumption',
|
|
65
|
+
frequency='m',
|
|
66
|
+
transformation='pch',
|
|
67
|
+
category='Consumption',
|
|
68
|
+
units='Index',
|
|
69
|
+
blocks=[1]
|
|
70
|
+
)
|
|
71
|
+
],
|
|
72
|
+
block_names=['Global'],
|
|
73
|
+
clock='m',
|
|
74
|
+
threshold=1e-5,
|
|
75
|
+
max_iter=5000
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Load data and estimate
|
|
79
|
+
X, Time, _ = load_data('data.csv', config)
|
|
80
|
+
result = dfm(X, config)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Configuration
|
|
84
|
+
|
|
85
|
+
### YAML Format
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
dfm:
|
|
89
|
+
clock: "m" # Clock frequency (base for all factors)
|
|
90
|
+
threshold: 1e-5 # EM convergence threshold
|
|
91
|
+
max_iter: 5000 # Maximum EM iterations
|
|
92
|
+
ar_lag: 1 # AR lag for factors
|
|
93
|
+
|
|
94
|
+
series:
|
|
95
|
+
gdp_real:
|
|
96
|
+
series_name: "Real GDP"
|
|
97
|
+
frequency: "q"
|
|
98
|
+
transformation: "pca"
|
|
99
|
+
category: "National Accounts"
|
|
100
|
+
units: "Index"
|
|
101
|
+
blocks: [Global]
|
|
102
|
+
|
|
103
|
+
consumption:
|
|
104
|
+
series_name: "Consumption"
|
|
105
|
+
frequency: "m"
|
|
106
|
+
transformation: "pch"
|
|
107
|
+
category: "Consumption"
|
|
108
|
+
units: "Index"
|
|
109
|
+
blocks: [Global]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Key Parameters
|
|
113
|
+
|
|
114
|
+
- **clock**: Base frequency for latent factors (typically "m" for monthly)
|
|
115
|
+
- **threshold**: EM convergence criterion (default: 1e-5)
|
|
116
|
+
- **max_iter**: Maximum EM iterations (default: 5000)
|
|
117
|
+
- **ar_lag**: Autoregressive lag for factors (default: 1)
|
|
118
|
+
|
|
119
|
+
### Numerical Stability
|
|
120
|
+
|
|
121
|
+
All numerical stability techniques are configurable and transparent:
|
|
122
|
+
|
|
123
|
+
```yaml
|
|
124
|
+
dfm:
|
|
125
|
+
clip_ar_coefficients: true # Ensure stationarity
|
|
126
|
+
clip_data_values: true # Clip extreme outliers
|
|
127
|
+
use_regularization: true # Prevent ill-conditioned matrices
|
|
128
|
+
use_damped_updates: true # Prevent likelihood decreases
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Warnings are logged when techniques are applied. All parameters can be disabled for research purposes.
|
|
132
|
+
|
|
133
|
+
## Data Format
|
|
134
|
+
|
|
135
|
+
### File-Based (CSV)
|
|
136
|
+
|
|
137
|
+
Data file with series as columns and dates as rows:
|
|
138
|
+
|
|
139
|
+
```csv
|
|
140
|
+
Date,gdp_real,consumption,investment
|
|
141
|
+
2000-01-01,,98.5,95.0
|
|
142
|
+
2000-02-01,,98.7,95.2
|
|
143
|
+
2000-03-01,100.5,99.0,95.5
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Requirements**:
|
|
147
|
+
- First column: `Date` (YYYY-MM-DD format)
|
|
148
|
+
- Column names: Must match `series_id` in configuration
|
|
149
|
+
- Missing values: Empty cells or NaN
|
|
150
|
+
- Mixed frequencies: Quarterly series only at quarter-end months
|
|
151
|
+
|
|
152
|
+
**Frequency Support**:
|
|
153
|
+
- **Supported**: Monthly (m), Quarterly (q), Semi-annual (sa), Annual (a)
|
|
154
|
+
- **Not supported**: Daily (d), Weekly (w) - raises ValueError
|
|
155
|
+
- All series must have frequency ≤ clock frequency
|
|
156
|
+
|
|
157
|
+
### Database-Backed
|
|
158
|
+
|
|
159
|
+
For production applications, implement adapters that return:
|
|
160
|
+
- `X`: Transformed data matrix (T × N)
|
|
161
|
+
- `Time`: Time index (pandas DatetimeIndex)
|
|
162
|
+
- `Z`: Original untransformed data (T × N)
|
|
163
|
+
|
|
164
|
+
## Usage Examples
|
|
165
|
+
|
|
166
|
+
### Basic Estimation
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from dfm_python import load_config, load_data, dfm
|
|
170
|
+
|
|
171
|
+
config = load_config('config.yaml')
|
|
172
|
+
X, Time, _ = load_data('data.csv', config)
|
|
173
|
+
result = dfm(X, config)
|
|
174
|
+
|
|
175
|
+
# Extract common factor
|
|
176
|
+
common_factor = result.Z[:, 0]
|
|
177
|
+
|
|
178
|
+
# Model fit
|
|
179
|
+
rmse = result.rmse
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### News Decomposition
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from dfm_python import news_dfm
|
|
186
|
+
|
|
187
|
+
# Estimate on old and new data
|
|
188
|
+
result_old = dfm(X_old, config)
|
|
189
|
+
result_new = dfm(X_new, config)
|
|
190
|
+
|
|
191
|
+
# Decompose news
|
|
192
|
+
y_old, y_new, singlenews, actual, forecast, weight, t_miss, v_miss, innov = news_dfm(
|
|
193
|
+
X_old, result_old, t_fcst=100, v_news=0
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Forecast update
|
|
197
|
+
forecast_update = y_new - y_old
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## API Reference
|
|
201
|
+
|
|
202
|
+
### `load_config(configfile) -> DFMConfig`
|
|
203
|
+
|
|
204
|
+
Load configuration from YAML file or return existing DFMConfig object.
|
|
205
|
+
|
|
206
|
+
**Parameters**:
|
|
207
|
+
- `configfile`: Path to YAML file (.yaml, .yml) or `DFMConfig` object
|
|
208
|
+
|
|
209
|
+
**Returns**: `DFMConfig` object
|
|
210
|
+
|
|
211
|
+
### `load_data(datafile, config, sample_start=None) -> Tuple[np.ndarray, pd.DatetimeIndex, np.ndarray]`
|
|
212
|
+
|
|
213
|
+
Load and transform time series data.
|
|
214
|
+
|
|
215
|
+
**Parameters**:
|
|
216
|
+
- `datafile`: Path to data file (CSV supported)
|
|
217
|
+
- `config`: `DFMConfig` object
|
|
218
|
+
- `sample_start`: Optional start date (YYYY-MM-DD) to filter data
|
|
219
|
+
|
|
220
|
+
**Returns**: `(X, Time, Z)` where:
|
|
221
|
+
- `X`: Transformed data matrix (T × N)
|
|
222
|
+
- `Time`: Time index (DatetimeIndex)
|
|
223
|
+
- `Z`: Original untransformed data (T × N)
|
|
224
|
+
|
|
225
|
+
### `dfm(X, config, threshold=None, max_iter=None) -> DFMResult`
|
|
226
|
+
|
|
227
|
+
Estimate Dynamic Factor Model using EM algorithm.
|
|
228
|
+
|
|
229
|
+
**Parameters**:
|
|
230
|
+
- `X`: Data matrix (T × N) with possible missing values
|
|
231
|
+
- `config`: `DFMConfig` object
|
|
232
|
+
- `threshold`: Convergence threshold (default: from config)
|
|
233
|
+
- `max_iter`: Maximum iterations (default: from config)
|
|
234
|
+
|
|
235
|
+
**Returns**: `DFMResult` object
|
|
236
|
+
|
|
237
|
+
### `DFMResult` Object
|
|
238
|
+
|
|
239
|
+
Contains all estimation outputs:
|
|
240
|
+
|
|
241
|
+
- **Factors**: `Z` (T × m), `C` (N × m)
|
|
242
|
+
- **Parameters**: `A` (m × m), `Q` (m × m), `R` (N × N)
|
|
243
|
+
- **Smoothed Data**: `X_sm` (T × N), `x_sm` (T × N)
|
|
244
|
+
- **Initial Conditions**: `Z_0` (m,), `V_0` (m × m)
|
|
245
|
+
- **Standardization**: `Mx` (N,), `Wx` (N,)
|
|
246
|
+
- **Convergence**: `converged`, `num_iter`, `loglik`
|
|
247
|
+
- **Model Fit**: `rmse`, `rmse_per_series`
|
|
248
|
+
|
|
249
|
+
## Architecture
|
|
250
|
+
|
|
251
|
+
**Core Modules**:
|
|
252
|
+
- `config.py`: Configuration management
|
|
253
|
+
- `data_loader.py`: Data loading and transformation
|
|
254
|
+
- `dfm.py`: Core estimation (EM algorithm)
|
|
255
|
+
- `kalman.py`: Kalman filter and smoother
|
|
256
|
+
- `news.py`: News decomposition
|
|
257
|
+
|
|
258
|
+
**Design Principles**:
|
|
259
|
+
- Generic core (no application-specific code)
|
|
260
|
+
- Flexible configuration (YAML or direct object creation)
|
|
261
|
+
- Extensible via application-specific adapters
|
|
262
|
+
- Robust error handling and numerical stability
|
|
263
|
+
|
|
264
|
+
## Troubleshooting
|
|
265
|
+
|
|
266
|
+
**Convergence Issues**:
|
|
267
|
+
- Increase `max_iter` (try 10000)
|
|
268
|
+
- Relax `threshold` (try 1e-3)
|
|
269
|
+
- Check data quality and missing data patterns
|
|
270
|
+
|
|
271
|
+
**Dimension Mismatch**:
|
|
272
|
+
- Ensure `series_id` in config matches data column names
|
|
273
|
+
- Verify block structure consistency
|
|
274
|
+
- Check frequency codes are valid
|
|
275
|
+
|
|
276
|
+
**Numerical Instability**:
|
|
277
|
+
- Monitor warnings for stability techniques
|
|
278
|
+
- Adjust thresholds based on data characteristics
|
|
279
|
+
- Investigate data quality if techniques frequently needed
|
|
280
|
+
|
|
281
|
+
## License
|
|
282
|
+
|
|
283
|
+
MIT License
|
|
284
|
+
|
|
285
|
+
## Citation
|
|
286
|
+
|
|
287
|
+
```bibtex
|
|
288
|
+
@software{dfm-python,
|
|
289
|
+
title = {dfm-python: Dynamic Factor Models for Nowcasting and Forecasting},
|
|
290
|
+
author = {DFM Python Contributors},
|
|
291
|
+
year = {2025},
|
|
292
|
+
url = {https://pypi.org/project/dfm-python/}
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
**Package Status**: Stable
|
|
299
|
+
**PyPI**: https://pypi.org/project/dfm-python/
|
|
300
|
+
**Python**: 3.12+
|