dfm-python 0.5.1__py3-none-any.whl → 0.5.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dfm_python/__init__.py +1 -1
- dfm_python/lightning/scaling.py +11 -5
- dfm_python/models/base.py +15 -41
- dfm_python/models/ddfm.py +108 -0
- dfm_python/models/dfm.py +114 -2
- dfm_python/ssm/em.py +1 -1
- {dfm_python-0.5.1.dist-info → dfm_python-0.5.2.dist-info}/METADATA +22 -22
- {dfm_python-0.5.1.dist-info → dfm_python-0.5.2.dist-info}/RECORD +10 -10
- {dfm_python-0.5.1.dist-info → dfm_python-0.5.2.dist-info}/WHEEL +0 -0
- {dfm_python-0.5.1.dist-info → dfm_python-0.5.2.dist-info}/licenses/LICENSE +0 -0
dfm_python/__init__.py
CHANGED
|
@@ -65,7 +65,7 @@ Note: DFMConfig and SeriesConfig are internal implementation details.
|
|
|
65
65
|
For detailed documentation, see the README.md file and the tutorial notebooks/scripts.
|
|
66
66
|
"""
|
|
67
67
|
|
|
68
|
-
__version__ = "0.5.
|
|
68
|
+
__version__ = "0.5.2"
|
|
69
69
|
|
|
70
70
|
# ============================================================================
|
|
71
71
|
# PUBLIC API DEFINITION
|
dfm_python/lightning/scaling.py
CHANGED
|
@@ -124,7 +124,10 @@ class NoScalingStrategy:
|
|
|
124
124
|
# Scaler Factory
|
|
125
125
|
# ============================================================================
|
|
126
126
|
|
|
127
|
-
def _create_scaler_transformer(
|
|
127
|
+
def _create_scaler_transformer(
|
|
128
|
+
scaler_type: Optional[str],
|
|
129
|
+
use_robust_as_default: bool = False
|
|
130
|
+
) -> Any:
|
|
128
131
|
"""Create a raw sklearn scaler for unified scaling.
|
|
129
132
|
|
|
130
133
|
Returns a raw sklearn scaler that can be used directly in TransformerPipeline.
|
|
@@ -160,10 +163,13 @@ def _create_scaler_transformer(scaler_type: Optional[str]) -> Any:
|
|
|
160
163
|
)
|
|
161
164
|
|
|
162
165
|
if scaler_type is None or scaler_type == 'none':
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
166
|
+
if use_robust_as_default:
|
|
167
|
+
scaler_type_lower = 'robust'
|
|
168
|
+
else:
|
|
169
|
+
# Passthrough: no scaling
|
|
170
|
+
return FunctionTransformer(func=lambda x: x, inverse_func=lambda x: x)
|
|
171
|
+
else:
|
|
172
|
+
scaler_type_lower = scaler_type.lower()
|
|
167
173
|
|
|
168
174
|
if scaler_type_lower == 'standard':
|
|
169
175
|
return StandardScaler()
|
dfm_python/models/base.py
CHANGED
|
@@ -590,11 +590,14 @@ class BaseFactorModel(pl.LightningModule):
|
|
|
590
590
|
"""
|
|
591
591
|
pass
|
|
592
592
|
|
|
593
|
+
@abstractmethod
|
|
593
594
|
def update(
|
|
594
595
|
self,
|
|
595
596
|
X_std: np.ndarray,
|
|
596
597
|
*,
|
|
597
|
-
|
|
598
|
+
history: Optional[int] = None,
|
|
599
|
+
kalman_filter: Optional[Any] = None,
|
|
600
|
+
scaler: Optional[Any] = None
|
|
598
601
|
) -> 'BaseFactorModel':
|
|
599
602
|
"""Update factor state with standardized data.
|
|
600
603
|
|
|
@@ -608,8 +611,16 @@ class BaseFactorModel(pl.LightningModule):
|
|
|
608
611
|
Standardized data array (T x N), where T is number of time periods
|
|
609
612
|
and N is number of series. Data should already be standardized using
|
|
610
613
|
result.Mx and result.Wx.
|
|
614
|
+
history : int, optional
|
|
615
|
+
Number of recent periods to use for factor state update. If None, uses
|
|
616
|
+
all provided data (default). If specified (e.g., 60), uses only the most
|
|
617
|
+
recent N periods. Initial state (Z_0, V_0) is always estimated from
|
|
618
|
+
full training data, but the update uses only recent history for efficiency.
|
|
611
619
|
kalman_filter : Any, optional
|
|
612
620
|
Kalman filter instance. If None, uses default or model's kalman filter.
|
|
621
|
+
scaler : Any, optional
|
|
622
|
+
If provided, attach/replace the model's scaler (e.g., refit on new regime).
|
|
623
|
+
When None, keep the existing scaler from training.
|
|
613
624
|
|
|
614
625
|
Returns
|
|
615
626
|
-------
|
|
@@ -620,48 +631,11 @@ class BaseFactorModel(pl.LightningModule):
|
|
|
620
631
|
--------
|
|
621
632
|
>>> # Update state with new data, then predict
|
|
622
633
|
>>> model.update(X_std).predict(horizon=1)
|
|
623
|
-
>>> # Or update
|
|
624
|
-
>>> model.update(X_std)
|
|
634
|
+
>>> # Or update with only recent 12 periods
|
|
635
|
+
>>> model.update(X_std, history=12)
|
|
625
636
|
>>> forecast = model.predict(horizon=6)
|
|
626
637
|
"""
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
result = self.result # Use property which ensures non-None after _check_trained()
|
|
630
|
-
|
|
631
|
-
# Validate input shape
|
|
632
|
-
if not isinstance(X_std, np.ndarray):
|
|
633
|
-
X_std = np.asarray(X_std)
|
|
634
|
-
if X_std.ndim != 2:
|
|
635
|
-
raise ValueError(
|
|
636
|
-
f"{self.__class__.__name__} update(): X_std must be 2D array (T x N), "
|
|
637
|
-
f"got shape {X_std.shape}"
|
|
638
|
-
)
|
|
639
|
-
|
|
640
|
-
# Handle NaN/Inf values
|
|
641
|
-
X_std = np.where(np.isfinite(X_std), X_std, np.nan)
|
|
642
|
-
|
|
643
|
-
# Update factor state (model-specific)
|
|
644
|
-
if hasattr(self, 'encoder') and self.encoder is not None:
|
|
645
|
-
# DDFM: Extract factors via encoder, then use Kalman filter
|
|
646
|
-
Z_last_updated = self._update_factor_state_ddfm(
|
|
647
|
-
X_std, result, kalman_filter
|
|
648
|
-
)
|
|
649
|
-
else:
|
|
650
|
-
# DFM: Use Kalman filter directly on standardized data
|
|
651
|
-
Z_last_updated = self._update_factor_state_dfm(
|
|
652
|
-
X_std, result, kalman_filter or getattr(self, 'kalman', None)
|
|
653
|
-
)
|
|
654
|
-
|
|
655
|
-
# Update result.Z[-1, :] permanently
|
|
656
|
-
if Z_last_updated is not None:
|
|
657
|
-
result.Z[-1, :] = Z_last_updated
|
|
658
|
-
else:
|
|
659
|
-
_logger.warning(
|
|
660
|
-
f"{self.__class__.__name__} update(): Failed to update factor state, "
|
|
661
|
-
f"keeping current state"
|
|
662
|
-
)
|
|
663
|
-
|
|
664
|
-
return self
|
|
638
|
+
pass
|
|
665
639
|
|
|
666
640
|
def get_result(self) -> BaseResult:
|
|
667
641
|
"""Extract result from trained model.
|
dfm_python/models/ddfm.py
CHANGED
|
@@ -1388,12 +1388,120 @@ class DDFM(BaseFactorModel):
|
|
|
1388
1388
|
Mx=Mx
|
|
1389
1389
|
)
|
|
1390
1390
|
|
|
1391
|
+
# Convert to numpy (handles torch inputs) and attempt scaler inverse_transform if available
|
|
1392
|
+
X_forecast = np.asarray(
|
|
1393
|
+
X_forecast.detach().cpu().numpy() if hasattr(X_forecast, "detach") else X_forecast
|
|
1394
|
+
)
|
|
1395
|
+
scaler = getattr(self, "scaler", None)
|
|
1396
|
+
if scaler is not None and hasattr(scaler, "inverse_transform"):
|
|
1397
|
+
try:
|
|
1398
|
+
X_forecast = scaler.inverse_transform(X_forecast)
|
|
1399
|
+
except Exception as e:
|
|
1400
|
+
_logger.warning(
|
|
1401
|
+
f"DDFM prediction: scaler.inverse_transform failed, returning unstandardized values. "
|
|
1402
|
+
f"error={e}"
|
|
1403
|
+
)
|
|
1404
|
+
|
|
1391
1405
|
if return_series and return_factors:
|
|
1392
1406
|
return X_forecast, Z_forecast
|
|
1393
1407
|
if return_series:
|
|
1394
1408
|
return X_forecast
|
|
1395
1409
|
return Z_forecast
|
|
1396
1410
|
|
|
1411
|
+
def update(
|
|
1412
|
+
self,
|
|
1413
|
+
X_std: np.ndarray,
|
|
1414
|
+
*,
|
|
1415
|
+
history: Optional[int] = None,
|
|
1416
|
+
kalman_filter: Optional[Any] = None,
|
|
1417
|
+
scaler: Optional[Any] = None
|
|
1418
|
+
) -> 'DDFM':
|
|
1419
|
+
"""Update factor state with standardized data.
|
|
1420
|
+
|
|
1421
|
+
This method permanently updates the last factor state (result.Z[-1, :])
|
|
1422
|
+
using the provided standardized data. Users should handle all preprocessing
|
|
1423
|
+
(masking, imputation, standardization) before calling this method.
|
|
1424
|
+
|
|
1425
|
+
Parameters
|
|
1426
|
+
----------
|
|
1427
|
+
X_std : np.ndarray
|
|
1428
|
+
Standardized data array (T x N), where T is number of time periods
|
|
1429
|
+
and N is number of series. Data should already be standardized using
|
|
1430
|
+
result.Mx and result.Wx.
|
|
1431
|
+
history : int, optional
|
|
1432
|
+
Number of recent periods to use for factor state update. If None, uses
|
|
1433
|
+
all provided data (default). If specified (e.g., 60), uses only the most
|
|
1434
|
+
recent N periods. Initial state (Z_0, V_0) is always estimated from
|
|
1435
|
+
full training data, but the update uses only recent history for efficiency.
|
|
1436
|
+
kalman_filter : Any, optional
|
|
1437
|
+
Kalman filter instance. If None, uses default or model's kalman filter.
|
|
1438
|
+
|
|
1439
|
+
Returns
|
|
1440
|
+
-------
|
|
1441
|
+
DDFM
|
|
1442
|
+
Self for method chaining
|
|
1443
|
+
|
|
1444
|
+
Examples
|
|
1445
|
+
--------
|
|
1446
|
+
>>> # Update state with new data, then predict
|
|
1447
|
+
>>> model.update(X_std).predict(horizon=1)
|
|
1448
|
+
>>> # Or update with only recent 12 periods
|
|
1449
|
+
>>> model.update(X_std, history=12)
|
|
1450
|
+
>>> forecast = model.predict(horizon=6)
|
|
1451
|
+
"""
|
|
1452
|
+
self._check_trained()
|
|
1453
|
+
|
|
1454
|
+
# Optionally replace scaler (e.g., if refit on new regime)
|
|
1455
|
+
if scaler is not None:
|
|
1456
|
+
self.scaler = scaler
|
|
1457
|
+
|
|
1458
|
+
result = self.result # Use property which ensures non-None after _check_trained()
|
|
1459
|
+
|
|
1460
|
+
# Validate input shape
|
|
1461
|
+
if not isinstance(X_std, np.ndarray):
|
|
1462
|
+
X_std = np.asarray(X_std)
|
|
1463
|
+
if X_std.ndim != 2:
|
|
1464
|
+
raise ValueError(
|
|
1465
|
+
f"DDFM update(): X_std must be 2D array (T x N), "
|
|
1466
|
+
f"got shape {X_std.shape}"
|
|
1467
|
+
)
|
|
1468
|
+
|
|
1469
|
+
# Handle NaN/Inf values
|
|
1470
|
+
X_std = np.where(np.isfinite(X_std), X_std, np.nan)
|
|
1471
|
+
|
|
1472
|
+
# Filter to recent history if specified
|
|
1473
|
+
# Note: Initial state (Z_0, V_0) from result is estimated from full training data,
|
|
1474
|
+
# but we use only recent history for the update step
|
|
1475
|
+
if history is not None and history > 0:
|
|
1476
|
+
if X_std.shape[0] > history:
|
|
1477
|
+
X_recent = X_std[-history:, :]
|
|
1478
|
+
_logger.debug(
|
|
1479
|
+
f"DDFM update(): Using {history} most recent periods out of {X_std.shape[0]} total periods"
|
|
1480
|
+
)
|
|
1481
|
+
else:
|
|
1482
|
+
X_recent = X_std
|
|
1483
|
+
_logger.debug(
|
|
1484
|
+
f"DDFM update(): history={history} specified but data has only {X_std.shape[0]} periods, using all data"
|
|
1485
|
+
)
|
|
1486
|
+
else:
|
|
1487
|
+
X_recent = X_std
|
|
1488
|
+
|
|
1489
|
+
# Update factor state using encoder to extract factors, then Kalman filter
|
|
1490
|
+
Z_last_updated = self._update_factor_state_ddfm(
|
|
1491
|
+
X_recent, result, kalman_filter
|
|
1492
|
+
)
|
|
1493
|
+
|
|
1494
|
+
# Update result.Z[-1, :] permanently
|
|
1495
|
+
if Z_last_updated is not None:
|
|
1496
|
+
result.Z[-1, :] = Z_last_updated
|
|
1497
|
+
else:
|
|
1498
|
+
_logger.warning(
|
|
1499
|
+
f"DDFM update(): Failed to update factor state, "
|
|
1500
|
+
f"keeping current state"
|
|
1501
|
+
)
|
|
1502
|
+
|
|
1503
|
+
return self
|
|
1504
|
+
|
|
1397
1505
|
@property
|
|
1398
1506
|
def result(self) -> DDFMResult:
|
|
1399
1507
|
"""Get model result from training state.
|
dfm_python/models/dfm.py
CHANGED
|
@@ -89,6 +89,7 @@ class DFM(BaseFactorModel):
|
|
|
89
89
|
max_iter: int = 100,
|
|
90
90
|
nan_method: int = 2,
|
|
91
91
|
nan_k: int = 3,
|
|
92
|
+
tent_weights_dict: Optional[dict] = None,
|
|
92
93
|
**kwargs
|
|
93
94
|
):
|
|
94
95
|
"""Initialize DFM instance.
|
|
@@ -119,6 +120,8 @@ class DFM(BaseFactorModel):
|
|
|
119
120
|
self.max_iter = max_iter
|
|
120
121
|
self.nan_method = nan_method
|
|
121
122
|
self.nan_k = nan_k
|
|
123
|
+
# Optional tent weights to control aggregation (e.g., disable tent by passing {'q':[1.0]})
|
|
124
|
+
self.tent_weights_dict = tent_weights_dict
|
|
122
125
|
|
|
123
126
|
# Determine number of factors
|
|
124
127
|
if num_factors is None:
|
|
@@ -200,7 +203,7 @@ class DFM(BaseFactorModel):
|
|
|
200
203
|
nQ=0,
|
|
201
204
|
i_idio=None,
|
|
202
205
|
clock=getattr(self.config, 'clock', 'm'),
|
|
203
|
-
tent_weights_dict=
|
|
206
|
+
tent_weights_dict=self.tent_weights_dict,
|
|
204
207
|
frequencies=None,
|
|
205
208
|
idio_chain_lengths=None,
|
|
206
209
|
config=self.config
|
|
@@ -280,7 +283,7 @@ class DFM(BaseFactorModel):
|
|
|
280
283
|
nQ=0,
|
|
281
284
|
i_idio=torch.ones(y.shape[0], device=y.device, dtype=y.dtype),
|
|
282
285
|
blocks=self.blocks.to(y.device),
|
|
283
|
-
tent_weights_dict=
|
|
286
|
+
tent_weights_dict=self.tent_weights_dict,
|
|
284
287
|
clock=getattr(self.config, 'clock', 'm'),
|
|
285
288
|
frequencies=None,
|
|
286
289
|
idio_chain_lengths=torch.zeros(y.shape[0], device=y.device, dtype=y.dtype),
|
|
@@ -832,6 +835,21 @@ class DFM(BaseFactorModel):
|
|
|
832
835
|
f"(max deviation: {max_deviation:.1f} std devs). "
|
|
833
836
|
f"Possible numerical instability."
|
|
834
837
|
)
|
|
838
|
+
|
|
839
|
+
# Convert to numpy (handles torch inputs) and attempt scaler inverse_transform if available
|
|
840
|
+
X_forecast = np.asarray(
|
|
841
|
+
X_forecast.detach().cpu().numpy() if hasattr(X_forecast, "detach") else X_forecast
|
|
842
|
+
)
|
|
843
|
+
scaler = getattr(self, "scaler", None)
|
|
844
|
+
if scaler is not None and hasattr(scaler, "inverse_transform"):
|
|
845
|
+
try:
|
|
846
|
+
X_forecast = scaler.inverse_transform(X_forecast)
|
|
847
|
+
except Exception as e:
|
|
848
|
+
_logger.warning(
|
|
849
|
+
f"DFM prediction: scaler.inverse_transform failed, returning unstandardized values. "
|
|
850
|
+
f"error={e}"
|
|
851
|
+
)
|
|
852
|
+
|
|
835
853
|
if return_factors and np.any(~np.isfinite(Z_forecast)):
|
|
836
854
|
nan_count = np.sum(~np.isfinite(Z_forecast))
|
|
837
855
|
raise ValueError(
|
|
@@ -846,6 +864,100 @@ class DFM(BaseFactorModel):
|
|
|
846
864
|
return X_forecast
|
|
847
865
|
return Z_forecast
|
|
848
866
|
|
|
867
|
+
def update(
|
|
868
|
+
self,
|
|
869
|
+
X_std: np.ndarray,
|
|
870
|
+
*,
|
|
871
|
+
history: Optional[int] = None,
|
|
872
|
+
kalman_filter: Optional[Any] = None,
|
|
873
|
+
scaler: Optional[Any] = None
|
|
874
|
+
) -> 'DFM':
|
|
875
|
+
"""Update factor state with standardized data.
|
|
876
|
+
|
|
877
|
+
This method permanently updates the last factor state (result.Z[-1, :])
|
|
878
|
+
using the provided standardized data. Users should handle all preprocessing
|
|
879
|
+
(masking, imputation, standardization) before calling this method.
|
|
880
|
+
|
|
881
|
+
Parameters
|
|
882
|
+
----------
|
|
883
|
+
X_std : np.ndarray
|
|
884
|
+
Standardized data array (T x N), where T is number of time periods
|
|
885
|
+
and N is number of series. Data should already be standardized using
|
|
886
|
+
result.Mx and result.Wx.
|
|
887
|
+
history : int, optional
|
|
888
|
+
Number of recent periods to use for factor state update. If None, uses
|
|
889
|
+
all provided data (default). If specified (e.g., 60), uses only the most
|
|
890
|
+
recent N periods. Initial state (Z_0, V_0) is always estimated from
|
|
891
|
+
full training data, but the update uses only recent history for efficiency.
|
|
892
|
+
kalman_filter : Any, optional
|
|
893
|
+
Kalman filter instance. If None, uses default or model's kalman filter.
|
|
894
|
+
|
|
895
|
+
Returns
|
|
896
|
+
-------
|
|
897
|
+
DFM
|
|
898
|
+
Self for method chaining
|
|
899
|
+
|
|
900
|
+
Examples
|
|
901
|
+
--------
|
|
902
|
+
>>> # Update state with new data, then predict
|
|
903
|
+
>>> model.update(X_std).predict(horizon=1)
|
|
904
|
+
>>> # Or update with only recent 12 periods
|
|
905
|
+
>>> model.update(X_std, history=12)
|
|
906
|
+
>>> forecast = model.predict(horizon=6)
|
|
907
|
+
"""
|
|
908
|
+
self._check_trained()
|
|
909
|
+
|
|
910
|
+
# Optionally replace scaler (e.g., if refit on new regime)
|
|
911
|
+
if scaler is not None:
|
|
912
|
+
self.scaler = scaler
|
|
913
|
+
|
|
914
|
+
result = self.result # Use property which ensures non-None after _check_trained()
|
|
915
|
+
|
|
916
|
+
# Validate input shape
|
|
917
|
+
if not isinstance(X_std, np.ndarray):
|
|
918
|
+
X_std = np.asarray(X_std)
|
|
919
|
+
if X_std.ndim != 2:
|
|
920
|
+
raise ValueError(
|
|
921
|
+
f"DFM update(): X_std must be 2D array (T x N), "
|
|
922
|
+
f"got shape {X_std.shape}"
|
|
923
|
+
)
|
|
924
|
+
|
|
925
|
+
# Handle NaN/Inf values
|
|
926
|
+
X_std = np.where(np.isfinite(X_std), X_std, np.nan)
|
|
927
|
+
|
|
928
|
+
# Filter to recent history if specified
|
|
929
|
+
# Note: Initial state (Z_0, V_0) from result is estimated from full training data,
|
|
930
|
+
# but we use only recent history for the update step
|
|
931
|
+
if history is not None and history > 0:
|
|
932
|
+
if X_std.shape[0] > history:
|
|
933
|
+
X_recent = X_std[-history:, :]
|
|
934
|
+
_logger.debug(
|
|
935
|
+
f"DFM update(): Using {history} most recent periods out of {X_std.shape[0]} total periods"
|
|
936
|
+
)
|
|
937
|
+
else:
|
|
938
|
+
X_recent = X_std
|
|
939
|
+
_logger.debug(
|
|
940
|
+
f"DFM update(): history={history} specified but data has only {X_std.shape[0]} periods, using all data"
|
|
941
|
+
)
|
|
942
|
+
else:
|
|
943
|
+
X_recent = X_std
|
|
944
|
+
|
|
945
|
+
# Update factor state using Kalman filter directly on standardized data
|
|
946
|
+
Z_last_updated = self._update_factor_state_dfm(
|
|
947
|
+
X_recent, result, kalman_filter or getattr(self, 'kalman', None)
|
|
948
|
+
)
|
|
949
|
+
|
|
950
|
+
# Update result.Z[-1, :] permanently
|
|
951
|
+
if Z_last_updated is not None:
|
|
952
|
+
result.Z[-1, :] = Z_last_updated
|
|
953
|
+
else:
|
|
954
|
+
_logger.warning(
|
|
955
|
+
f"DFM update(): Failed to update factor state, "
|
|
956
|
+
f"keeping current state"
|
|
957
|
+
)
|
|
958
|
+
|
|
959
|
+
return self
|
|
960
|
+
|
|
849
961
|
@property
|
|
850
962
|
def result(self) -> DFMResult:
|
|
851
963
|
"""Get model result from training state.
|
dfm_python/ssm/em.py
CHANGED
|
@@ -570,7 +570,7 @@ class EMAlgorithm(nn.Module):
|
|
|
570
570
|
resNaN = x_clean.clone()
|
|
571
571
|
resNaN[indNaN] = torch.nan
|
|
572
572
|
|
|
573
|
-
# Determine tent kernel size (pC) for
|
|
573
|
+
# Determine tent kernel size (pC) for slower-frequency aggregation
|
|
574
574
|
pC = 5 # Default: quarterly to monthly uses 5 periods [1,2,3,2,1]
|
|
575
575
|
if R_mat is not None:
|
|
576
576
|
pC = R_mat.shape[1]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dfm-python
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
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
|
|
@@ -59,25 +59,25 @@ Description-Content-Type: text/markdown
|
|
|
59
59
|
[](https://pypi.org/project/dfm-python/)
|
|
60
60
|
[](https://www.python.org/downloads/)
|
|
61
61
|
|
|
62
|
-
A comprehensive Python implementation of **Dynamic Factor Models (DFM)** and **Deep Dynamic Factor Models (DDFM)** for forecasting high-dimensional time series. Built with PyTorch Lightning
|
|
62
|
+
A comprehensive Python implementation of **Dynamic Factor Models (DFM)** and **Deep Dynamic Factor Models (DDFM)** for forecasting high-dimensional time series. Built with PyTorch Lightning.
|
|
63
63
|
|
|
64
64
|
## Features
|
|
65
65
|
|
|
66
66
|
### Core Capabilities
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
67
|
+
- **Mixed-frequency data**: Monthly, quarterly, semi-annual, annual series in one model
|
|
68
|
+
- **Clock-based framework**: All factors evolve at a common clock frequency
|
|
69
|
+
- **Block structure**: Flexible factor organization (global + sector-specific factors)
|
|
70
|
+
- **Idiosyncratic components**: Per-series state augmentation for better fit
|
|
71
|
+
- **Preprocessed data**: Users handle preprocessing (imputation, scaling) using sktime or other tools
|
|
72
|
+
- **Forecasting**: Generate predictions for any horizon
|
|
73
|
+
- **Nowcasting**: Estimate current period values using incomplete data
|
|
74
|
+
- **Deep DFM (DDFM)**: Nonlinear encoder with PyTorch for capturing complex factor structures
|
|
75
75
|
|
|
76
76
|
### Technical Features
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
77
|
+
- **PyTorch Lightning**: Standard training interface with DataModule and Trainer
|
|
78
|
+
- **Multiple configuration methods**: YAML files, Python dictionaries, or Hydra
|
|
79
|
+
- **Advanced numerical stability**: Adaptive regularization, spectral radius capping, variance floors
|
|
80
|
+
- **Error handling**: Comprehensive error handling, extensive testing, well-documented
|
|
81
81
|
|
|
82
82
|
## Installation
|
|
83
83
|
|
|
@@ -391,17 +391,17 @@ pytest src/test/test_trainer.py -v
|
|
|
391
391
|
## Project Status
|
|
392
392
|
|
|
393
393
|
**Version**: 0.5.1
|
|
394
|
-
**Status**: Stable
|
|
395
|
-
**Python**: 3.10+
|
|
394
|
+
**Status**: Stable
|
|
395
|
+
**Python**: 3.10+
|
|
396
396
|
|
|
397
397
|
### What's New in 0.5.1
|
|
398
398
|
|
|
399
|
-
-
|
|
400
|
-
-
|
|
401
|
-
-
|
|
402
|
-
-
|
|
403
|
-
-
|
|
404
|
-
-
|
|
399
|
+
- **New `update()` method**: Replaces legacy `nowcast()` with flexible `update().predict()` pattern
|
|
400
|
+
- **Improved API**: Users now control all preprocessing (masking, imputation, standardization)
|
|
401
|
+
- **Code cleanup**: Removed legacy code and overengineering (~200+ lines removed)
|
|
402
|
+
- **Bug fixes**: Fixed DDFM `update()` method for single-factor models
|
|
403
|
+
- **Updated tutorials**: All 4 tutorials rewritten with new pattern and VAR(1) configuration
|
|
404
|
+
- **VAR(1) only**: Simplified to VAR(1) factor dynamics throughout
|
|
405
405
|
|
|
406
406
|
## License
|
|
407
407
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
dfm_python/__init__.py,sha256=
|
|
1
|
+
dfm_python/__init__.py,sha256=NU9ZxIsniiU9q1IVL8AuGVej9_ROM7nyIFD5dh4MfT4,5683
|
|
2
2
|
dfm_python/config/__init__.py,sha256=NrTwjNzQRcYlk5Ht0hTF8o-6CCl5e4S_JxQAp1mNyCs,1507
|
|
3
3
|
dfm_python/config/adapter.py,sha256=X-mjWlg6ebWp-VAxjAUssgHnmr8AXOvSxhTTVSHOIIk,27475
|
|
4
4
|
dfm_python/config/results.py,sha256=hzXL6MwaIxzA4uzouh6Bc_57lbpedphN9ErTMgfkpeE,15967
|
|
@@ -14,19 +14,19 @@ dfm_python/encoder/pca.py,sha256=FsvCqXD0AuASxymV2-5oAM2QVv_syhSyL3CmxVOXxgM,105
|
|
|
14
14
|
dfm_python/encoder/vae.py,sha256=nKgt327-oQ1KT6q3hdABI9z_1K26cKSLVXC1Criy0KA,21250
|
|
15
15
|
dfm_python/lightning/__init__.py,sha256=_2DFZnMX3BOybuZZ76VJJ8gmMLZ7tFyO3yyN-A4_ECI,1247
|
|
16
16
|
dfm_python/lightning/data_module.py,sha256=cZuBdGbk6meaeiw9ZPGkcsUW4v25ZP_km-u0Pkoh5bg,34815
|
|
17
|
-
dfm_python/lightning/scaling.py,sha256
|
|
17
|
+
dfm_python/lightning/scaling.py,sha256=-8zYrBm3y4_Ke0ODh1-SJnC0pWef3ZJT6GRv8owo2o8,16997
|
|
18
18
|
dfm_python/logger/__init__.py,sha256=2GwtfOG2ttbtJ5CrO6338R6GIao3YuY0HLiBeCoC0Ns,1066
|
|
19
19
|
dfm_python/logger/inference_logger.py,sha256=TmXiWl703boJ3mIR-WgGpy6hfeF8a68OW9vrZppTTCE,10091
|
|
20
20
|
dfm_python/logger/logger.py,sha256=1YzsUnvAnAK9CTcfttdSIMtgFvylO13LjQPMEsSs9xc,5187
|
|
21
21
|
dfm_python/logger/train_logger.py,sha256=9VYhzwNYH1Rn7IQipKTY5wmVJcX1n6HDgnUZH0hChPY,17732
|
|
22
22
|
dfm_python/models/__init__.py,sha256=3JsYZOqDGkl1eG8Zrc4-5gf7Sw47PzzDg2XBhsudB04,724
|
|
23
|
-
dfm_python/models/base.py,sha256
|
|
24
|
-
dfm_python/models/ddfm.py,sha256=
|
|
25
|
-
dfm_python/models/dfm.py,sha256=
|
|
23
|
+
dfm_python/models/base.py,sha256=WIx1d38bsaRkpfA4iYVG0_x7R1N8zVfi922LAlXqsEg,29261
|
|
24
|
+
dfm_python/models/ddfm.py,sha256=4mGsMJ_2P_EOnOItLIrs7_by6EF25NX2dldrG1O5GYY,63758
|
|
25
|
+
dfm_python/models/dfm.py,sha256=BgHqCiiUprS3rtOOaj31LXJ8RnzESVdSOTZ-sxReNwc,38287
|
|
26
26
|
dfm_python/models/mcmc.py,sha256=QXBVxeBHREkc9K_xCGR1Y4sIFUkmX_RbRcgNktojuEc,23978
|
|
27
27
|
dfm_python/models/utils.py,sha256=LAtvBqUYxnnl4dIaWl6MC-NHSuoyeNkHZppfWB1lCxQ,20193
|
|
28
28
|
dfm_python/ssm/__init__.py,sha256=dFpt8_3jb_foISDNhma_JUAzch0VrKZcaeWP9zwpwoM,1161
|
|
29
|
-
dfm_python/ssm/em.py,sha256=
|
|
29
|
+
dfm_python/ssm/em.py,sha256=sUiwuBVLTQBNHlBJ9YwemVY0wJYMTUqkuhyjPohENNw,63429
|
|
30
30
|
dfm_python/ssm/kalman.py,sha256=KC6Q71VxM2NSdBVB5107QsNUm-_kE59MiyqrzImWz7E,45931
|
|
31
31
|
dfm_python/ssm/utils.py,sha256=SYsu4E2zHAXXLfR2iQWNX-ywuzmwZj5kT4owHlyfvjU,13637
|
|
32
32
|
dfm_python/trainer/__init__.py,sha256=gc8jRjM-y2XN911LldwYUYHAAuFEvzUJJvXhG6B3Sv4,25654
|
|
@@ -38,7 +38,7 @@ dfm_python/utils/diagnostics.py,sha256=UwrJfnn8VACATfQEkTcWw9eEJlTik-VCgTZ1dJABZ
|
|
|
38
38
|
dfm_python/utils/helpers.py,sha256=iOVwkP5pti-q-HKOAkp6bUPAp7BPV7BBgh8jL76g9go,13049
|
|
39
39
|
dfm_python/utils/statespace.py,sha256=P-GQiCP9HzI2d9PgYst4nmRuocEXr9gG1yzGYsYWkMg,44159
|
|
40
40
|
dfm_python/utils/time.py,sha256=o6NRPuk4Ynk8kPaUnzR6xq3pPAO6aLUTR9N_3hn_N88,35651
|
|
41
|
-
dfm_python-0.5.
|
|
42
|
-
dfm_python-0.5.
|
|
43
|
-
dfm_python-0.5.
|
|
44
|
-
dfm_python-0.5.
|
|
41
|
+
dfm_python-0.5.2.dist-info/METADATA,sha256=FC2_7glZDt_csfDlluuCi0tlOh-cpwwjPg4MkScIU3k,12018
|
|
42
|
+
dfm_python-0.5.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
43
|
+
dfm_python-0.5.2.dist-info/licenses/LICENSE,sha256=6e87br6LCDHjy2hdIR1-sdBQy351qdmSEbOg6jQ7ZXY,1063
|
|
44
|
+
dfm_python-0.5.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|