dfm-python 0.2.7__py3-none-any.whl → 0.2.9__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 CHANGED
@@ -50,22 +50,17 @@ Example (Low-level API - For advanced usage):
50
50
  For detailed documentation, see the README.md file and the tutorial notebooks/scripts.
51
51
  """
52
52
 
53
- __version__ = "0.2.7"
53
+ __version__ = "0.2.9"
54
54
 
55
- from .config import DFMConfig, SeriesConfig, BlockConfig, Params, DEFAULT_GLOBAL_BLOCK_NAME
56
55
  from .config import (
57
- ConfigSource,
58
- YamlSource,
59
- DictSource,
60
- SpecCSVSource,
61
- HydraSource,
62
- MergedConfigSource,
63
- make_config_source,
56
+ DFMConfig, SeriesConfig, BlockConfig, Params, DEFAULT_GLOBAL_BLOCK_NAME,
57
+ ConfigSource, YamlSource, DictSource, SpecCSVSource, HydraSource,
58
+ MergedConfigSource, make_config_source,
64
59
  )
65
60
  from .data import transform_data
66
61
  from .dfm import DFMResult
67
62
  from .core import calculate_rmse, diagnose_series, print_series_diagnosis
68
- from .dfm import DFM as _DFMCore # Core DFM class from dfm.py
63
+ from .dfm import DFMCore # Core DFM class from dfm.py
69
64
  from .kalman import run_kf, skf, fis, miss_data
70
65
  from .news import update_nowcast, news_dfm, para_const
71
66
 
@@ -100,27 +95,9 @@ def get_original_data():
100
95
  """Get original (untransformed) data matrix."""
101
96
  return _dfm_instance.original_data
102
97
 
103
- # For direct attribute access, we'll use __getattr__ but handle config module conflict
104
-
105
- def __getattr__(name):
106
- """Allow access to DFM instance properties at module level."""
107
- # Handle special cases that conflict with submodules
108
- if name == 'config' and _dfm_instance.config is not None:
109
- return _dfm_instance.config
110
- elif name == 'data':
111
- return _dfm_instance.data
112
- elif name == 'time':
113
- return _dfm_instance.time
114
- elif name == 'result':
115
- return _dfm_instance.result
116
- elif name == 'original_data':
117
- return _dfm_instance.original_data
118
- # For other attributes, try normal import
119
- raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
120
-
121
98
  __all__ = [
122
99
  # Core classes
123
- 'DFMConfig', 'SeriesConfig', 'BlockConfig', 'Params', 'DFM',
100
+ 'DFMConfig', 'SeriesConfig', 'BlockConfig', 'Params', 'DFM', 'DFMCore',
124
101
  # Constants
125
102
  'DEFAULT_GLOBAL_BLOCK_NAME',
126
103
  # Config sources
dfm_python/api.py CHANGED
@@ -29,11 +29,11 @@ from .config import (
29
29
  ConfigSource,
30
30
  MergedConfigSource,
31
31
  )
32
- from .dfm import DFM as _DFMCore, DFMResult
32
+ from .dfm import DFMCore, DFMResult
33
33
  from .core.helpers import safe_get_method, safe_get_attr
34
34
 
35
35
 
36
- class DFM(_DFMCore):
36
+ class DFM(DFMCore):
37
37
  """High-level API for Dynamic Factor Model estimation.
38
38
 
39
39
  This class provides a simple, object-oriented interface for DFM operations.
@@ -368,29 +368,48 @@ def from_yaml(yaml_path: Union[str, Path]) -> DFM:
368
368
  return _dfm_instance.load_config(yaml=yaml_path)
369
369
 
370
370
 
371
- def from_spec(spec_path: Union[str, Path], params: Optional[Params] = None) -> DFM:
372
- """Load configuration from spec CSV file (convenience constructor)."""
373
- from .config import SpecCSVSource
371
+ def from_spec(
372
+ csv_path: Union[str, Path],
373
+ output_dir: Optional[Union[str, Path]] = None,
374
+ series_filename: Optional[str] = None,
375
+ blocks_filename: Optional[str] = None
376
+ ) -> Tuple[Path, Path]:
377
+ """Convert spec CSV file to YAML configuration files.
374
378
 
375
- if params is None:
376
- params = Params()
379
+ This function reads a spec CSV file and generates two YAML files:
380
+ - config/series/{basename}.yaml - series definitions
381
+ - config/blocks/{basename}.yaml - block definitions
377
382
 
378
- # Load config from spec CSV using ConfigSource pattern
379
- spec_source = SpecCSVSource(spec_path)
380
- config = spec_source.load()
381
-
382
- # Override estimation parameters from params
383
- for key, value in params.__dict__.items():
384
- if hasattr(config, key):
385
- setattr(config, key, value)
386
-
387
- _dfm_instance._config = config
388
- return _dfm_instance
383
+ Parameters
384
+ ----------
385
+ csv_path : str or Path
386
+ Path to the spec CSV file
387
+ output_dir : str or Path, optional
388
+ Output directory for YAML files. Defaults to config/ directory relative to CSV.
389
+ series_filename : str, optional
390
+ Custom filename for series YAML (without .yaml extension).
391
+ Defaults to CSV basename.
392
+ blocks_filename : str, optional
393
+ Custom filename for blocks YAML (without .yaml extension).
394
+ Defaults to CSV basename.
395
+
396
+ Returns
397
+ -------
398
+ Tuple[Path, Path]
399
+ Paths to generated series YAML and blocks YAML files
400
+
401
+ Examples
402
+ --------
403
+ >>> series_path, blocks_path = from_spec('data/sample_spec.csv')
404
+ >>> # Creates config/series/sample_spec.yaml and config/blocks/sample_spec.yaml
405
+ """
406
+ from .config_sources import from_spec as _from_spec
407
+ return _from_spec(csv_path, output_dir, series_filename, blocks_filename)
389
408
 
390
409
 
391
410
  def from_spec_df(spec_df: pd.DataFrame, params: Optional[Params] = None) -> DFM:
392
411
  """Load configuration from spec DataFrame (convenience constructor)."""
393
- from .data import _load_config_from_dataframe
412
+ from .config_sources import _load_config_from_dataframe
394
413
 
395
414
  if params is None:
396
415
  params = Params()
dfm_python/config.py CHANGED
@@ -21,6 +21,7 @@ from config_sources for backward compatibility.
21
21
  """
22
22
 
23
23
  import numpy as np
24
+ import warnings
24
25
  from typing import List, Optional, Dict, Any, Union
25
26
  from dataclasses import dataclass, field
26
27
 
@@ -102,10 +103,14 @@ class SeriesConfig:
102
103
  Unique identifier (auto-generated if None)
103
104
  series_name : str, optional
104
105
  Human-readable name (defaults to series_id if None)
105
- units : str
106
- Units of measurement
107
- category : str
108
- Category/group name
106
+ units : str, optional
107
+ Units of measurement (optional metadata for display purposes only).
108
+ Used in news decomposition output for readability. Not used in model estimation.
109
+ release_date : int, optional
110
+ Release date information for pseudo real-time nowcasting.
111
+ - Positive value (1-31): Day of month when data is released
112
+ - Negative value: Days before end of previous month when data is released
113
+ Example: 25 = released on 25th of each month, -5 = released 5 days before end of previous month
109
114
  """
110
115
  # Required fields (no defaults)
111
116
  frequency: str
@@ -114,9 +119,8 @@ class SeriesConfig:
114
119
  # Optional fields (with defaults - must come after required fields)
115
120
  series_id: Optional[str] = None # Auto-generated if None: "series_0", "series_1", etc.
116
121
  series_name: Optional[str] = None # Optional metadata for display
117
- units: str = "" # Optional metadata
118
- category: str = "" # Optional metadata
119
- aggregate: Optional[str] = None # Aggregation method (deprecated: higher frequencies than clock are not supported)
122
+ units: Optional[str] = None # Optional metadata for display only (used in news.py output)
123
+ release_date: Optional[int] = None # Release date for pseudo real-time nowcasting
120
124
 
121
125
  def __post_init__(self):
122
126
  """Validate fields after initialization."""
@@ -716,9 +720,7 @@ class DFMConfig:
716
720
  blocks=series_blocks,
717
721
  series_id=get_list_value('SeriesID', i, None),
718
722
  series_name=get_list_value('SeriesName', i, None),
719
- units=str(get_list_value('Units', i, '')),
720
- category=str(get_list_value('Category', i, '')),
721
- aggregate=get_list_value('Aggregate', i, None)
723
+ units=get_list_value('Units', i, None) # Optional, for display only
722
724
  ))
723
725
 
724
726
  # Convert block_names to blocks dict
@@ -805,9 +807,7 @@ class DFMConfig:
805
807
  frequency=series_cfg.get('frequency', 'm'),
806
808
  transformation=series_cfg.get('transformation', 'lin'),
807
809
  blocks=series_blocks,
808
- units=series_cfg.get('units', ''),
809
- category=series_cfg.get('category', ''),
810
- aggregate=series_cfg.get('aggregate', None) # Deprecated
810
+ units=series_cfg.get('units', None) # Optional, for display only
811
811
  ))
812
812
 
813
813
  # Convert blocks_dict to BlockConfig dict
@@ -1031,7 +1031,6 @@ def validate_transformation(transformation: str) -> str:
1031
1031
  >>> validate_transformation('unknown') # Issues warning but returns value
1032
1032
  'unknown'
1033
1033
  """
1034
- import warnings
1035
1034
  if transformation not in _VALID_TRANSFORMATIONS:
1036
1035
  warnings.warn(f"Unknown transformation code: {transformation}. Will use untransformed data.")
1037
1036
  return transformation