dfm-python 0.1.5__py3-none-any.whl → 0.1.6__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 +3 -3
- dfm_python/dfm.py +255 -11
- {dfm_python-0.1.5.dist-info → dfm_python-0.1.6.dist-info}/METADATA +2 -2
- {dfm_python-0.1.5.dist-info → dfm_python-0.1.6.dist-info}/RECORD +6 -6
- {dfm_python-0.1.5.dist-info → dfm_python-0.1.6.dist-info}/WHEEL +0 -0
- {dfm_python-0.1.5.dist-info → dfm_python-0.1.6.dist-info}/licenses/LICENSE +0 -0
dfm_python/__init__.py
CHANGED
|
@@ -30,13 +30,13 @@ Example:
|
|
|
30
30
|
For detailed documentation, see the README.md file.
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
|
-
__version__ = "0.1.
|
|
33
|
+
__version__ = "0.1.6"
|
|
34
34
|
|
|
35
35
|
from .config import DFMConfig
|
|
36
36
|
from .data_loader import (
|
|
37
37
|
load_config, load_config_from_yaml, load_config_from_csv, load_data, transform_data
|
|
38
38
|
)
|
|
39
|
-
from .dfm import DFMResult, dfm, calculate_rmse
|
|
39
|
+
from .dfm import DFMResult, dfm, calculate_rmse, diagnose_series, print_series_diagnosis
|
|
40
40
|
from .kalman import run_kf, skf, fis, miss_data
|
|
41
41
|
from .news import update_nowcast, news_dfm, para_const
|
|
42
42
|
|
|
@@ -51,7 +51,7 @@ __all__ = [
|
|
|
51
51
|
'ModelConfig', # Alias for DFMConfig
|
|
52
52
|
'load_config', 'load_config_from_yaml', 'load_config_from_csv',
|
|
53
53
|
'load_data', 'transform_data',
|
|
54
|
-
'DFMResult', 'dfm', 'calculate_rmse',
|
|
54
|
+
'DFMResult', 'dfm', 'calculate_rmse', 'diagnose_series', 'print_series_diagnosis',
|
|
55
55
|
'run_kf', 'skf', 'fis', 'miss_data',
|
|
56
56
|
'update_nowcast', 'news_dfm', 'para_const',
|
|
57
57
|
# Deprecated aliases
|
dfm_python/dfm.py
CHANGED
|
@@ -646,6 +646,20 @@ class DFMResult:
|
|
|
646
646
|
how many factors are in each block structure.
|
|
647
647
|
p : int
|
|
648
648
|
Number of lags in the autoregressive structure of factors. Typically p=1.
|
|
649
|
+
rmse : float, optional
|
|
650
|
+
Overall RMSE on original scale (averaged across all series).
|
|
651
|
+
rmse_per_series : np.ndarray, optional
|
|
652
|
+
RMSE per series on original scale (N,).
|
|
653
|
+
rmse_std : float, optional
|
|
654
|
+
Overall RMSE on standardized scale (averaged across all series).
|
|
655
|
+
rmse_std_per_series : np.ndarray, optional
|
|
656
|
+
RMSE per series on standardized scale (N,).
|
|
657
|
+
converged : bool, optional
|
|
658
|
+
Whether EM algorithm converged.
|
|
659
|
+
num_iter : int, optional
|
|
660
|
+
Number of EM iterations performed.
|
|
661
|
+
loglik : float, optional
|
|
662
|
+
Final log-likelihood value.
|
|
649
663
|
|
|
650
664
|
Examples
|
|
651
665
|
--------
|
|
@@ -673,8 +687,10 @@ class DFMResult:
|
|
|
673
687
|
converged: bool = False # Whether EM algorithm converged
|
|
674
688
|
num_iter: int = 0 # Number of iterations completed
|
|
675
689
|
loglik: float = -np.inf # Final log-likelihood
|
|
676
|
-
rmse: Optional[float] = None #
|
|
677
|
-
rmse_per_series: Optional[np.ndarray] = None # RMSE per series (
|
|
690
|
+
rmse: Optional[float] = None # Overall RMSE (original scale)
|
|
691
|
+
rmse_per_series: Optional[np.ndarray] = None # RMSE per series (original scale)
|
|
692
|
+
rmse_std: Optional[float] = None # Overall RMSE (standardized scale)
|
|
693
|
+
rmse_std_per_series: Optional[np.ndarray] = None # RMSE per series (standardized scale)
|
|
678
694
|
|
|
679
695
|
|
|
680
696
|
def em_converged(loglik: float, previous_loglik: float, threshold: float = 1e-4,
|
|
@@ -1729,12 +1745,11 @@ def em_step(y: np.ndarray, A: np.ndarray, C: np.ndarray, Q: np.ndarray,
|
|
|
1729
1745
|
num_blocks = blocks.shape[1]
|
|
1730
1746
|
|
|
1731
1747
|
# Get config defaults if not provided
|
|
1748
|
+
# Create a minimal config object with defaults for backward compatibility
|
|
1749
|
+
# We use a simple object to avoid DFMConfig validation which requires series
|
|
1732
1750
|
if config is None:
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
config = DFMConfig(
|
|
1736
|
-
series=[],
|
|
1737
|
-
block_names=[],
|
|
1751
|
+
from types import SimpleNamespace
|
|
1752
|
+
config = SimpleNamespace(
|
|
1738
1753
|
clip_ar_coefficients=True,
|
|
1739
1754
|
ar_clip_min=-0.99,
|
|
1740
1755
|
ar_clip_max=0.99,
|
|
@@ -2953,6 +2968,12 @@ def dfm(X: np.ndarray, config: DFMConfig, threshold: Optional[float] = None, max
|
|
|
2953
2968
|
# Only use non-missing observations for comparison
|
|
2954
2969
|
rmse_overall, rmse_per_series = calculate_rmse(X, X_sm, mask=None)
|
|
2955
2970
|
|
|
2971
|
+
# Calculate standardized RMSE for better comparison across series
|
|
2972
|
+
# Standardize both actual and predicted using the same Wx and Mx
|
|
2973
|
+
X_std = (X - Mx_clean) / np.where(Wx_clean != 0, Wx_clean, 1.0)
|
|
2974
|
+
x_sm_std = x_sm # Already standardized
|
|
2975
|
+
rmse_std_overall, rmse_std_per_series = calculate_rmse(X_std, x_sm_std, mask=None)
|
|
2976
|
+
|
|
2956
2977
|
# Create result structure
|
|
2957
2978
|
Res = DFMResult(
|
|
2958
2979
|
x_sm=x_sm,
|
|
@@ -2972,7 +2993,9 @@ def dfm(X: np.ndarray, config: DFMConfig, threshold: Optional[float] = None, max
|
|
|
2972
2993
|
num_iter=num_iter,
|
|
2973
2994
|
loglik=loglik,
|
|
2974
2995
|
rmse=rmse_overall,
|
|
2975
|
-
rmse_per_series=rmse_per_series
|
|
2996
|
+
rmse_per_series=rmse_per_series,
|
|
2997
|
+
rmse_std=rmse_std_overall,
|
|
2998
|
+
rmse_std_per_series=rmse_std_per_series
|
|
2976
2999
|
)
|
|
2977
3000
|
|
|
2978
3001
|
# Display output tables (optional - can be disabled for cleaner output)
|
|
@@ -3099,22 +3122,243 @@ def _display_dfm_tables(Res: DFMResult, config, nQ: int) -> None:
|
|
|
3099
3122
|
# Table 8: Model Fit Statistics (RMSE)
|
|
3100
3123
|
if Res.rmse is not None and not np.isnan(Res.rmse):
|
|
3101
3124
|
print('Table 8: Model Fit Statistics')
|
|
3102
|
-
print(f' Overall RMSE (
|
|
3125
|
+
print(f' Overall RMSE (original scale): {Res.rmse:.6f}')
|
|
3126
|
+
if Res.rmse_std is not None and not np.isnan(Res.rmse_std):
|
|
3127
|
+
print(f' Overall RMSE (standardized scale): {Res.rmse_std:.6f}')
|
|
3103
3128
|
if Res.rmse_per_series is not None and len(Res.rmse_per_series) > 0:
|
|
3104
|
-
print('\n RMSE per Series:')
|
|
3129
|
+
print('\n RMSE per Series (Original Scale):')
|
|
3105
3130
|
try:
|
|
3106
3131
|
series_names = config.SeriesName if hasattr(config, 'SeriesName') else [f"Series_{i}" for i in range(len(Res.rmse_per_series))]
|
|
3107
3132
|
for i, (name, rmse_val) in enumerate(zip(series_names, Res.rmse_per_series)):
|
|
3108
3133
|
if not np.isnan(rmse_val):
|
|
3109
|
-
|
|
3134
|
+
# Calculate RMSE as percentage of mean if available
|
|
3135
|
+
mean_val = Res.Mx[i] if i < len(Res.Mx) else np.nan
|
|
3136
|
+
if not np.isnan(mean_val) and abs(mean_val) > 1e-6:
|
|
3137
|
+
pct = 100.0 * rmse_val / abs(mean_val)
|
|
3138
|
+
print(f' {name:40s}: {rmse_val:.6f} ({pct:.2f}% of mean)')
|
|
3139
|
+
else:
|
|
3140
|
+
print(f' {name:40s}: {rmse_val:.6f}')
|
|
3110
3141
|
except Exception:
|
|
3111
3142
|
# Fallback if series names not available
|
|
3112
3143
|
for i, rmse_val in enumerate(Res.rmse_per_series):
|
|
3113
3144
|
if not np.isnan(rmse_val):
|
|
3114
3145
|
print(f' Series {i:3d}: {rmse_val:.6f}')
|
|
3146
|
+
|
|
3147
|
+
# Show standardized RMSE per series
|
|
3148
|
+
if Res.rmse_std_per_series is not None and len(Res.rmse_std_per_series) > 0:
|
|
3149
|
+
print('\n RMSE per Series (Standardized Scale):')
|
|
3150
|
+
try:
|
|
3151
|
+
series_names = config.SeriesName if hasattr(config, 'SeriesName') else [f"Series_{i}" for i in range(len(Res.rmse_std_per_series))]
|
|
3152
|
+
for i, (name, rmse_std_val) in enumerate(zip(series_names, Res.rmse_std_per_series)):
|
|
3153
|
+
if not np.isnan(rmse_std_val):
|
|
3154
|
+
print(f' {name:40s}: {rmse_std_val:.6f} std dev')
|
|
3155
|
+
except Exception:
|
|
3156
|
+
# Fallback if series names not available
|
|
3157
|
+
for i, rmse_std_val in enumerate(Res.rmse_std_per_series):
|
|
3158
|
+
if not np.isnan(rmse_std_val):
|
|
3159
|
+
print(f' Series {i:3d}: {rmse_std_val:.6f} std dev')
|
|
3160
|
+
|
|
3161
|
+
# Diagnostic warnings for high RMSE
|
|
3162
|
+
if Res.rmse_per_series is not None and Res.Mx is not None:
|
|
3163
|
+
print('\n Diagnostic Warnings:')
|
|
3164
|
+
try:
|
|
3165
|
+
series_names = config.SeriesName if hasattr(config, 'SeriesName') else [f"Series_{i}" for i in range(len(Res.rmse_per_series))]
|
|
3166
|
+
warnings_count = 0
|
|
3167
|
+
for i, (name, rmse_val) in enumerate(zip(series_names, Res.rmse_per_series)):
|
|
3168
|
+
if not np.isnan(rmse_val) and i < len(Res.Mx):
|
|
3169
|
+
mean_val = Res.Mx[i]
|
|
3170
|
+
std_val = Res.Wx[i] if i < len(Res.Wx) else np.nan
|
|
3171
|
+
if not np.isnan(mean_val) and abs(mean_val) > 1e-6:
|
|
3172
|
+
pct_of_mean = 100.0 * rmse_val / abs(mean_val)
|
|
3173
|
+
# Warn if RMSE > 50% of mean or > 10 standard deviations
|
|
3174
|
+
if pct_of_mean > 50.0 or (not np.isnan(std_val) and rmse_val > 10.0 * std_val):
|
|
3175
|
+
warnings_count += 1
|
|
3176
|
+
if warnings_count <= 5: # Limit to first 5 warnings
|
|
3177
|
+
print(f' ⚠ {name:40s}: RMSE is {pct_of_mean:.1f}% of mean')
|
|
3178
|
+
if not np.isnan(std_val):
|
|
3179
|
+
print(f' (RMSE={rmse_val:.2e}, Mean={mean_val:.2e}, Std={std_val:.2e})')
|
|
3180
|
+
if warnings_count > 5:
|
|
3181
|
+
print(f' ... and {warnings_count - 5} more series with high RMSE')
|
|
3182
|
+
except Exception:
|
|
3183
|
+
pass
|
|
3115
3184
|
print('\n\n\n')
|
|
3116
3185
|
|
|
3117
3186
|
except Exception as e:
|
|
3118
3187
|
print(f'Error displaying tables: {e}')
|
|
3119
3188
|
pass
|
|
3120
3189
|
|
|
3190
|
+
|
|
3191
|
+
def diagnose_series(Res: DFMResult, config: DFMConfig, series_name: Optional[str] = None,
|
|
3192
|
+
series_idx: Optional[int] = None) -> Dict[str, Any]:
|
|
3193
|
+
"""Diagnose model fit issues for a specific series.
|
|
3194
|
+
|
|
3195
|
+
This function provides detailed diagnostics to help identify why a series
|
|
3196
|
+
may have high RMSE, including factor loadings, standardization values,
|
|
3197
|
+
and reconstruction errors.
|
|
3198
|
+
|
|
3199
|
+
Parameters
|
|
3200
|
+
----------
|
|
3201
|
+
Res : DFMResult
|
|
3202
|
+
DFM estimation results
|
|
3203
|
+
config : DFMConfig
|
|
3204
|
+
DFM configuration
|
|
3205
|
+
series_name : str, optional
|
|
3206
|
+
Name of the series to diagnose. If provided, series_idx is ignored.
|
|
3207
|
+
series_idx : int, optional
|
|
3208
|
+
Index of the series to diagnose (0-based). Used if series_name is not provided.
|
|
3209
|
+
|
|
3210
|
+
Returns
|
|
3211
|
+
-------
|
|
3212
|
+
dict
|
|
3213
|
+
Dictionary containing diagnostic information:
|
|
3214
|
+
- 'series_name': Name of the series
|
|
3215
|
+
- 'series_idx': Index of the series
|
|
3216
|
+
- 'rmse_original': RMSE on original scale
|
|
3217
|
+
- 'rmse_standardized': RMSE on standardized scale
|
|
3218
|
+
- 'mean': Series mean (Mx)
|
|
3219
|
+
- 'std': Series standard deviation (Wx)
|
|
3220
|
+
- 'rmse_pct_of_mean': RMSE as percentage of mean
|
|
3221
|
+
- 'rmse_in_std_devs': RMSE in standard deviations
|
|
3222
|
+
- 'factor_loadings': Factor loadings for this series
|
|
3223
|
+
- 'max_loading': Maximum absolute loading
|
|
3224
|
+
- 'loading_sum_sq': Sum of squared loadings
|
|
3225
|
+
- 'reconstruction_error_mean': Mean reconstruction error
|
|
3226
|
+
- 'reconstruction_error_std': Std of reconstruction error
|
|
3227
|
+
|
|
3228
|
+
Examples
|
|
3229
|
+
--------
|
|
3230
|
+
>>> Res = dfm(X, config)
|
|
3231
|
+
>>> diag = diagnose_series(Res, config, series_name="Real GDP")
|
|
3232
|
+
>>> print(f"Standardized RMSE: {diag['rmse_standardized']:.4f}")
|
|
3233
|
+
"""
|
|
3234
|
+
# Find series index
|
|
3235
|
+
if series_name is not None:
|
|
3236
|
+
try:
|
|
3237
|
+
series_names = config.SeriesName if hasattr(config, 'SeriesName') else []
|
|
3238
|
+
if series_name in series_names:
|
|
3239
|
+
series_idx = series_names.index(series_name)
|
|
3240
|
+
else:
|
|
3241
|
+
# Try case-insensitive match
|
|
3242
|
+
series_idx = next((i for i, name in enumerate(series_names)
|
|
3243
|
+
if name.lower() == series_name.lower()), None)
|
|
3244
|
+
if series_idx is None:
|
|
3245
|
+
raise ValueError(f"Series '{series_name}' not found in configuration")
|
|
3246
|
+
except (AttributeError, ValueError) as e:
|
|
3247
|
+
raise ValueError(f"Cannot find series '{series_name}': {e}")
|
|
3248
|
+
|
|
3249
|
+
if series_idx is None:
|
|
3250
|
+
raise ValueError("Must provide either series_name or series_idx")
|
|
3251
|
+
|
|
3252
|
+
if series_idx < 0 or series_idx >= Res.C.shape[0]:
|
|
3253
|
+
raise ValueError(f"Series index {series_idx} out of range [0, {Res.C.shape[0]})")
|
|
3254
|
+
|
|
3255
|
+
# Get series name
|
|
3256
|
+
try:
|
|
3257
|
+
series_names = config.SeriesName if hasattr(config, 'SeriesName') else []
|
|
3258
|
+
name = series_names[series_idx] if series_idx < len(series_names) else f"Series_{series_idx}"
|
|
3259
|
+
except (AttributeError, IndexError):
|
|
3260
|
+
name = f"Series_{series_idx}"
|
|
3261
|
+
|
|
3262
|
+
# Get RMSE values
|
|
3263
|
+
rmse_original = None
|
|
3264
|
+
rmse_standardized = None
|
|
3265
|
+
if Res.rmse_per_series is not None and series_idx < len(Res.rmse_per_series):
|
|
3266
|
+
rmse_original = Res.rmse_per_series[series_idx]
|
|
3267
|
+
if Res.rmse_std_per_series is not None and series_idx < len(Res.rmse_std_per_series):
|
|
3268
|
+
rmse_standardized = Res.rmse_std_per_series[series_idx]
|
|
3269
|
+
|
|
3270
|
+
# Get standardization values
|
|
3271
|
+
mean_val = Res.Mx[series_idx] if series_idx < len(Res.Mx) else np.nan
|
|
3272
|
+
std_val = Res.Wx[series_idx] if series_idx < len(Res.Wx) else np.nan
|
|
3273
|
+
|
|
3274
|
+
# Calculate RMSE metrics
|
|
3275
|
+
rmse_pct_of_mean = None
|
|
3276
|
+
rmse_in_std_devs = None
|
|
3277
|
+
if not np.isnan(rmse_original) and not np.isnan(mean_val) and abs(mean_val) > 1e-6:
|
|
3278
|
+
rmse_pct_of_mean = 100.0 * rmse_original / abs(mean_val)
|
|
3279
|
+
if not np.isnan(rmse_original) and not np.isnan(std_val) and std_val > 1e-6:
|
|
3280
|
+
rmse_in_std_devs = rmse_original / std_val
|
|
3281
|
+
|
|
3282
|
+
# Get factor loadings
|
|
3283
|
+
factor_loadings = Res.C[series_idx, :] if series_idx < Res.C.shape[0] else np.array([])
|
|
3284
|
+
max_loading = np.max(np.abs(factor_loadings)) if len(factor_loadings) > 0 else np.nan
|
|
3285
|
+
loading_sum_sq = np.sum(factor_loadings ** 2) if len(factor_loadings) > 0 else np.nan
|
|
3286
|
+
|
|
3287
|
+
# Calculate reconstruction errors (if we have original data)
|
|
3288
|
+
reconstruction_error_mean = None
|
|
3289
|
+
reconstruction_error_std = None
|
|
3290
|
+
# Note: We don't have access to original X here, so we can't calculate this
|
|
3291
|
+
# This would require passing X to the function or storing it in Res
|
|
3292
|
+
|
|
3293
|
+
return {
|
|
3294
|
+
'series_name': name,
|
|
3295
|
+
'series_idx': series_idx,
|
|
3296
|
+
'rmse_original': rmse_original,
|
|
3297
|
+
'rmse_standardized': rmse_standardized,
|
|
3298
|
+
'mean': mean_val,
|
|
3299
|
+
'std': std_val,
|
|
3300
|
+
'rmse_pct_of_mean': rmse_pct_of_mean,
|
|
3301
|
+
'rmse_in_std_devs': rmse_in_std_devs,
|
|
3302
|
+
'factor_loadings': factor_loadings,
|
|
3303
|
+
'max_loading': max_loading,
|
|
3304
|
+
'loading_sum_sq': loading_sum_sq,
|
|
3305
|
+
'reconstruction_error_mean': reconstruction_error_mean,
|
|
3306
|
+
'reconstruction_error_std': reconstruction_error_std
|
|
3307
|
+
}
|
|
3308
|
+
|
|
3309
|
+
|
|
3310
|
+
def print_series_diagnosis(Res: DFMResult, config: DFMConfig,
|
|
3311
|
+
series_name: Optional[str] = None,
|
|
3312
|
+
series_idx: Optional[int] = None) -> None:
|
|
3313
|
+
"""Print a formatted diagnosis report for a specific series.
|
|
3314
|
+
|
|
3315
|
+
Parameters
|
|
3316
|
+
----------
|
|
3317
|
+
Res : DFMResult
|
|
3318
|
+
DFM estimation results
|
|
3319
|
+
config : DFMConfig
|
|
3320
|
+
DFM configuration
|
|
3321
|
+
series_name : str, optional
|
|
3322
|
+
Name of the series to diagnose
|
|
3323
|
+
series_idx : int, optional
|
|
3324
|
+
Index of the series to diagnose
|
|
3325
|
+
|
|
3326
|
+
Examples
|
|
3327
|
+
--------
|
|
3328
|
+
>>> Res = dfm(X, config)
|
|
3329
|
+
>>> print_series_diagnosis(Res, config, series_name="Real GDP")
|
|
3330
|
+
"""
|
|
3331
|
+
diag = diagnose_series(Res, config, series_name=series_name, series_idx=series_idx)
|
|
3332
|
+
|
|
3333
|
+
print(f"\n{'='*70}")
|
|
3334
|
+
print(f"DIAGNOSTIC REPORT: {diag['series_name']}")
|
|
3335
|
+
print(f"{'='*70}\n")
|
|
3336
|
+
|
|
3337
|
+
print("RMSE Statistics:")
|
|
3338
|
+
print(f" Original scale: {diag['rmse_original']:.6e}" if diag['rmse_original'] is not None else " Original scale: N/A")
|
|
3339
|
+
print(f" Standardized scale: {diag['rmse_standardized']:.6f} std dev" if diag['rmse_standardized'] is not None else " Standardized scale: N/A")
|
|
3340
|
+
if diag['rmse_pct_of_mean'] is not None:
|
|
3341
|
+
print(f" As % of mean: {diag['rmse_pct_of_mean']:.2f}%")
|
|
3342
|
+
if diag['rmse_in_std_devs'] is not None:
|
|
3343
|
+
print(f" In std deviations: {diag['rmse_in_std_devs']:.2f}x")
|
|
3344
|
+
|
|
3345
|
+
print("\nStandardization Values:")
|
|
3346
|
+
print(f" Mean: {diag['mean']:.6e}" if not np.isnan(diag['mean']) else " Mean: N/A")
|
|
3347
|
+
print(f" Std: {diag['std']:.6e}" if not np.isnan(diag['std']) else " Std: N/A")
|
|
3348
|
+
|
|
3349
|
+
print("\nFactor Loadings:")
|
|
3350
|
+
if len(diag['factor_loadings']) > 0:
|
|
3351
|
+
print(f" Number of loadings: {len(diag['factor_loadings'])}")
|
|
3352
|
+
print(f" Max absolute: {diag['max_loading']:.6f}" if not np.isnan(diag['max_loading']) else " Max absolute: N/A")
|
|
3353
|
+
print(f" Sum of squares: {diag['loading_sum_sq']:.6f}" if not np.isnan(diag['loading_sum_sq']) else " Sum of squares: N/A")
|
|
3354
|
+
# Show top 5 loadings
|
|
3355
|
+
abs_loadings = np.abs(diag['factor_loadings'])
|
|
3356
|
+
top_indices = np.argsort(abs_loadings)[-5:][::-1]
|
|
3357
|
+
print(f" Top 5 loadings:")
|
|
3358
|
+
for idx in top_indices:
|
|
3359
|
+
print(f" Factor {idx:3d}: {diag['factor_loadings'][idx]:8.4f}")
|
|
3360
|
+
else:
|
|
3361
|
+
print(" No loadings available")
|
|
3362
|
+
|
|
3363
|
+
print(f"\n{'='*70}\n")
|
|
3364
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dfm-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
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
|
|
@@ -555,7 +555,7 @@ If you use this package in your research, please cite:
|
|
|
555
555
|
@software{dfm-python,
|
|
556
556
|
title = {dfm-python: Dynamic Factor Models for Nowcasting and Forecasting},
|
|
557
557
|
author = {DFM Python Contributors},
|
|
558
|
-
year = {
|
|
558
|
+
year = {2025},
|
|
559
559
|
url = {https://pypi.org/project/dfm-python/},
|
|
560
560
|
version = {0.1.5}
|
|
561
561
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
dfm_python/__init__.py,sha256=
|
|
1
|
+
dfm_python/__init__.py,sha256=f7MFOpB9MmPPjX58bvlLWB4ujuIAWmc_whPN1np3be4,2419
|
|
2
2
|
dfm_python/config.py,sha256=QI-kdEQIkiDahN7lwoGr6PUVeNXW4MiZQkOEr5xypzs,26283
|
|
3
3
|
dfm_python/data_loader.py,sha256=0pNH4YyOgGsgcYZJL60OhC4QA-iTvjFrsq8I-WEF1rk,20585
|
|
4
|
-
dfm_python/dfm.py,sha256=
|
|
4
|
+
dfm_python/dfm.py,sha256=zNkT-iVEqNvMyjVmfGdCn5Txa9w8ehGGhV2ujNkieps,155260
|
|
5
5
|
dfm_python/kalman.py,sha256=vaIpTiaV3EcZW3SIa0gxkKtYqboffu1wAr3-7yhKdIc,17308
|
|
6
6
|
dfm_python/news.py,sha256=AEs-Z7UoOzvw7TN22AKsglVnYlI4JgNB7ittEcdGU2M,31583
|
|
7
7
|
dfm_python/utils/__init__.py,sha256=nfFHSKJGUzXoeleoqrWqzuQriDU79o9xUhfG3VYy2sI,659
|
|
8
8
|
dfm_python/utils/aggregation.py,sha256=Z0gbGticxMs2GLxG4pD-eOR-7aHd-S5aeHo5S4OlrKc,10270
|
|
9
9
|
dfm_python/utils/data_utils.py,sha256=zUpIBD1_ydyZLXvVxl6OVW4AM43edEgGs3thE8687e0,6805
|
|
10
|
-
dfm_python-0.1.
|
|
11
|
-
dfm_python-0.1.
|
|
12
|
-
dfm_python-0.1.
|
|
13
|
-
dfm_python-0.1.
|
|
10
|
+
dfm_python-0.1.6.dist-info/METADATA,sha256=WFMfZB8RwXbmUKjBUBYVoAwM1zloKRjB6LxJmFtW7_0,19086
|
|
11
|
+
dfm_python-0.1.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
12
|
+
dfm_python-0.1.6.dist-info/licenses/LICENSE,sha256=6e87br6LCDHjy2hdIR1-sdBQy351qdmSEbOg6jQ7ZXY,1063
|
|
13
|
+
dfm_python-0.1.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|