dfm-python 0.1.0__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.0/.cursor/worktrees.json +7 -0
- dfm_python-0.1.0/.github/workflows/database.yaml +67 -0
- dfm_python-0.1.0/.github/workflows/nowcast.yaml +69 -0
- dfm_python-0.1.0/.github/workflows/train.yaml +78 -0
- dfm_python-0.1.0/.gitignore +33 -0
- dfm_python-0.1.0/.python-version +1 -0
- dfm_python-0.1.0/MANIFEST.in +32 -0
- dfm_python-0.1.0/PKG-INFO +271 -0
- dfm_python-0.1.0/README.md +230 -0
- dfm_python-0.1.0/adapters/__init__.py +30 -0
- dfm_python-0.1.0/adapters/adapter_database.py +1280 -0
- dfm_python-0.1.0/assets/initialization.py +319 -0
- dfm_python-0.1.0/assets/test_initialization.py +404 -0
- dfm_python-0.1.0/config/default.yaml +33 -0
- dfm_python-0.1.0/config/series/default.yaml +133 -0
- dfm_python-0.1.0/config/series/test_series.yaml +134 -0
- dfm_python-0.1.0/config/test.yaml +33 -0
- dfm_python-0.1.0/database/__init__.py +113 -0
- dfm_python-0.1.0/database/client.py +80 -0
- dfm_python-0.1.0/database/db_utils.py +633 -0
- dfm_python-0.1.0/database/helpers.py +599 -0
- dfm_python-0.1.0/database/models.py +133 -0
- dfm_python-0.1.0/database/operations.py +1728 -0
- dfm_python-0.1.0/database/series.py +172 -0
- dfm_python-0.1.0/database/settings.py +324 -0
- dfm_python-0.1.0/database/spec_manager.py +323 -0
- dfm_python-0.1.0/migrations/001_initial_schema.sql +509 -0
- dfm_python-0.1.0/migrations/002_add_blocks.sql +179 -0
- dfm_python-0.1.0/pyproject.toml +57 -0
- dfm_python-0.1.0/scripts/__init__.py +2 -0
- dfm_python-0.1.0/scripts/ingest_api.py +659 -0
- dfm_python-0.1.0/scripts/nowcast_dfm.py +354 -0
- dfm_python-0.1.0/scripts/train_dfm.py +348 -0
- dfm_python-0.1.0/scripts/utils.py +194 -0
- dfm_python-0.1.0/services/__init__.py +20 -0
- dfm_python-0.1.0/services/api/__init__.py +15 -0
- dfm_python-0.1.0/services/api/base.py +99 -0
- dfm_python-0.1.0/services/api/bok_client.py +317 -0
- dfm_python-0.1.0/services/api/kosis_client.py +368 -0
- dfm_python-0.1.0/services/ingestion/__init__.py +14 -0
- dfm_python-0.1.0/services/ingestion/base.py +389 -0
- dfm_python-0.1.0/services/ingestion/bok.py +297 -0
- dfm_python-0.1.0/services/ingestion/kosis.py +298 -0
- dfm_python-0.1.0/services/ingestion/orchestrator.py +526 -0
- dfm_python-0.1.0/src/__init__.py +43 -0
- dfm_python-0.1.0/src/dfm_python/__init__.py +28 -0
- dfm_python-0.1.0/src/dfm_python/config.py +435 -0
- dfm_python-0.1.0/src/dfm_python/data_loader.py +500 -0
- dfm_python-0.1.0/src/dfm_python/dfm.py +2356 -0
- dfm_python-0.1.0/src/dfm_python/kalman.py +403 -0
- dfm_python-0.1.0/src/dfm_python/news.py +557 -0
- dfm_python-0.1.0/src/test/__init__.py +2 -0
- dfm_python-0.1.0/src/test/test_dfm.py +717 -0
- dfm_python-0.1.0/src/test/test_kalman.py +312 -0
- dfm_python-0.1.0/src/test/test_news.py +225 -0
- dfm_python-0.1.0/src/utils/__init__.py +12 -0
- dfm_python-0.1.0/src/utils/data_utils.py +148 -0
- dfm_python-0.1.0/tests/__init__.py +0 -0
- dfm_python-0.1.0/tests/test_adapters_database.py +184 -0
- dfm_python-0.1.0/tests/test_database_connection.py +213 -0
- dfm_python-0.1.0/tests/test_database_ingestion.py +508 -0
- dfm_python-0.1.0/tests/test_full_workflow.py +248 -0
- dfm_python-0.1.0/tests/test_nowcast_dfm.py +230 -0
- dfm_python-0.1.0/tests/test_nowcasting_dfm.py +148 -0
- dfm_python-0.1.0/tests/test_train_dfm.py +251 -0
- dfm_python-0.1.0/uv.lock +1247 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
name: Data Ingestion
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
spec_file:
|
|
7
|
+
description: 'Spec CSV file path (relative to src/spec/)'
|
|
8
|
+
required: false
|
|
9
|
+
default: '001_initial_spec.csv'
|
|
10
|
+
type: string
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
ingest:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
timeout-minutes: 60
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout repository
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: '3.12'
|
|
25
|
+
cache: 'pip'
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: |
|
|
29
|
+
pip install uv
|
|
30
|
+
uv venv .venv
|
|
31
|
+
source .venv/bin/activate
|
|
32
|
+
uv sync
|
|
33
|
+
|
|
34
|
+
- name: Load environment variables
|
|
35
|
+
env:
|
|
36
|
+
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
|
|
37
|
+
SUPABASE_SECRET_KEY: ${{ secrets.SUPABASE_SECRET_KEY }}
|
|
38
|
+
BOK_API_KEY: ${{ secrets.BOK_API_KEY }}
|
|
39
|
+
KOSIS_API_KEY: ${{ secrets.KOSIS_API_KEY }}
|
|
40
|
+
run: |
|
|
41
|
+
echo "SUPABASE_URL=$SUPABASE_URL" >> $GITHUB_ENV
|
|
42
|
+
echo "SUPABASE_SECRET_KEY=$SUPABASE_SECRET_KEY" >> $GITHUB_ENV
|
|
43
|
+
echo "BOK_API_KEY=$BOK_API_KEY" >> $GITHUB_ENV
|
|
44
|
+
echo "KOSIS_API_KEY=$KOSIS_API_KEY" >> $GITHUB_ENV
|
|
45
|
+
|
|
46
|
+
- name: Run data ingestion
|
|
47
|
+
env:
|
|
48
|
+
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
|
|
49
|
+
SUPABASE_SECRET_KEY: ${{ secrets.SUPABASE_SECRET_KEY }}
|
|
50
|
+
BOK_API_KEY: ${{ secrets.BOK_API_KEY }}
|
|
51
|
+
KOSIS_API_KEY: ${{ secrets.KOSIS_API_KEY }}
|
|
52
|
+
GITHUB_WORKFLOW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
53
|
+
run: |
|
|
54
|
+
source .venv/bin/activate
|
|
55
|
+
python scripts/ingest_api.py
|
|
56
|
+
continue-on-error: false
|
|
57
|
+
|
|
58
|
+
- name: Upload logs on failure
|
|
59
|
+
if: failure()
|
|
60
|
+
uses: actions/upload-artifact@v4
|
|
61
|
+
with:
|
|
62
|
+
name: ingestion-logs
|
|
63
|
+
path: |
|
|
64
|
+
*.log
|
|
65
|
+
outputs/**/*.log
|
|
66
|
+
retention-days: 7
|
|
67
|
+
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: Nowcast DFM
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
config_path:
|
|
7
|
+
description: 'Model config CSV path (relative to src/spec/)'
|
|
8
|
+
required: false
|
|
9
|
+
default: 'src/spec/001_initial_spec.csv'
|
|
10
|
+
type: string
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
nowcast:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
timeout-minutes: 60
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout repository
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: '3.12'
|
|
25
|
+
cache: 'pip'
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: |
|
|
29
|
+
pip install uv
|
|
30
|
+
uv venv .venv
|
|
31
|
+
source .venv/bin/activate
|
|
32
|
+
uv sync
|
|
33
|
+
|
|
34
|
+
- name: Download model weights (optional)
|
|
35
|
+
uses: actions/download-artifact@v4
|
|
36
|
+
with:
|
|
37
|
+
name: model-weights
|
|
38
|
+
path: .
|
|
39
|
+
continue-on-error: true
|
|
40
|
+
|
|
41
|
+
- name: Load environment variables
|
|
42
|
+
env:
|
|
43
|
+
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
|
|
44
|
+
SUPABASE_SECRET_KEY: ${{ secrets.SUPABASE_SECRET_KEY }}
|
|
45
|
+
run: |
|
|
46
|
+
echo "SUPABASE_URL=$SUPABASE_URL" >> $GITHUB_ENV
|
|
47
|
+
echo "SUPABASE_SECRET_KEY=$SUPABASE_SECRET_KEY" >> $GITHUB_ENV
|
|
48
|
+
|
|
49
|
+
- name: Run nowcasting
|
|
50
|
+
env:
|
|
51
|
+
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
|
|
52
|
+
SUPABASE_SECRET_KEY: ${{ secrets.SUPABASE_SECRET_KEY }}
|
|
53
|
+
run: |
|
|
54
|
+
source .venv/bin/activate
|
|
55
|
+
python scripts/nowcast_dfm.py \
|
|
56
|
+
"model.config_path=${{ inputs.config_path }}" \
|
|
57
|
+
"data.use_database=true" \
|
|
58
|
+
"data.strict_mode=false"
|
|
59
|
+
|
|
60
|
+
- name: Upload logs on failure
|
|
61
|
+
if: failure()
|
|
62
|
+
uses: actions/upload-artifact@v4
|
|
63
|
+
with:
|
|
64
|
+
name: nowcast-logs
|
|
65
|
+
path: |
|
|
66
|
+
*.log
|
|
67
|
+
outputs/**/*.log
|
|
68
|
+
retention-days: 7
|
|
69
|
+
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
name: Train DFM Model
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
config_path:
|
|
7
|
+
description: 'Model config CSV path (relative to src/spec/)'
|
|
8
|
+
required: false
|
|
9
|
+
default: 'src/spec/001_initial_spec.csv'
|
|
10
|
+
type: string
|
|
11
|
+
max_iter:
|
|
12
|
+
description: 'Maximum EM iterations for training'
|
|
13
|
+
required: false
|
|
14
|
+
default: '100'
|
|
15
|
+
type: string
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
train:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
timeout-minutes: 120
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- name: Checkout repository
|
|
24
|
+
uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: '3.12'
|
|
30
|
+
cache: 'pip'
|
|
31
|
+
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: |
|
|
34
|
+
pip install uv
|
|
35
|
+
uv venv .venv
|
|
36
|
+
source .venv/bin/activate
|
|
37
|
+
uv sync
|
|
38
|
+
|
|
39
|
+
- name: Load environment variables
|
|
40
|
+
env:
|
|
41
|
+
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
|
|
42
|
+
SUPABASE_SECRET_KEY: ${{ secrets.SUPABASE_SECRET_KEY }}
|
|
43
|
+
run: |
|
|
44
|
+
echo "SUPABASE_URL=$SUPABASE_URL" >> $GITHUB_ENV
|
|
45
|
+
echo "SUPABASE_SECRET_KEY=$SUPABASE_SECRET_KEY" >> $GITHUB_ENV
|
|
46
|
+
|
|
47
|
+
- name: Train DFM model
|
|
48
|
+
env:
|
|
49
|
+
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
|
|
50
|
+
SUPABASE_SECRET_KEY: ${{ secrets.SUPABASE_SECRET_KEY }}
|
|
51
|
+
run: |
|
|
52
|
+
source .venv/bin/activate
|
|
53
|
+
python scripts/train_dfm.py \
|
|
54
|
+
"model.config_path=${{ inputs.config_path }}" \
|
|
55
|
+
"data.use_database=true" \
|
|
56
|
+
"data.strict_mode=false" \
|
|
57
|
+
"dfm.max_iter=${{ inputs.max_iter }}"
|
|
58
|
+
|
|
59
|
+
- name: Upload model weights
|
|
60
|
+
if: success()
|
|
61
|
+
uses: actions/upload-artifact@v4
|
|
62
|
+
with:
|
|
63
|
+
name: model-weights
|
|
64
|
+
path: |
|
|
65
|
+
ResDFM.pkl
|
|
66
|
+
*.pkl
|
|
67
|
+
retention-days: 30
|
|
68
|
+
|
|
69
|
+
- name: Upload logs on failure
|
|
70
|
+
if: failure()
|
|
71
|
+
uses: actions/upload-artifact@v4
|
|
72
|
+
with:
|
|
73
|
+
name: train-logs
|
|
74
|
+
path: |
|
|
75
|
+
*.log
|
|
76
|
+
outputs/**/*.log
|
|
77
|
+
retention-days: 7
|
|
78
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
.env
|
|
2
|
+
.env.local
|
|
3
|
+
.env.*
|
|
4
|
+
*.env
|
|
5
|
+
|
|
6
|
+
.vscode/
|
|
7
|
+
|
|
8
|
+
# Secrets and credentials
|
|
9
|
+
secrets/
|
|
10
|
+
credentials/
|
|
11
|
+
*.key
|
|
12
|
+
*.pem
|
|
13
|
+
*.p12
|
|
14
|
+
*.pfx
|
|
15
|
+
|
|
16
|
+
# Excel files
|
|
17
|
+
*.xls
|
|
18
|
+
*.xlsx
|
|
19
|
+
|
|
20
|
+
# MATLAB files
|
|
21
|
+
*.mat
|
|
22
|
+
data/**/*.xls
|
|
23
|
+
data/**/*.xlsx
|
|
24
|
+
matlab/**/*.xls
|
|
25
|
+
*.m
|
|
26
|
+
|
|
27
|
+
*.txt
|
|
28
|
+
*.pyc
|
|
29
|
+
|
|
30
|
+
outputs/PR_REVIEW.md
|
|
31
|
+
PR_DESCRIPTION.md
|
|
32
|
+
|
|
33
|
+
outputs/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Include only the DFM module (src/nowcasting)
|
|
2
|
+
# Exclude all application-specific code (adapters, database, scripts, config, etc.)
|
|
3
|
+
|
|
4
|
+
include README.md
|
|
5
|
+
include LICENSE
|
|
6
|
+
include pyproject.toml
|
|
7
|
+
|
|
8
|
+
# Include only the nowcasting package
|
|
9
|
+
recursive-include src/nowcasting *.py
|
|
10
|
+
|
|
11
|
+
# Exclude application-specific directories
|
|
12
|
+
prune adapters
|
|
13
|
+
prune database
|
|
14
|
+
prune scripts
|
|
15
|
+
prune services
|
|
16
|
+
prune config
|
|
17
|
+
prune migrations
|
|
18
|
+
prune tests
|
|
19
|
+
prune assets
|
|
20
|
+
|
|
21
|
+
# Include README (already specified above, but be explicit)
|
|
22
|
+
include README.md
|
|
23
|
+
|
|
24
|
+
# Exclude other application-specific files (but keep README.md)
|
|
25
|
+
exclude *.yaml
|
|
26
|
+
exclude *.yml
|
|
27
|
+
exclude *.csv
|
|
28
|
+
exclude *.sql
|
|
29
|
+
exclude *.lock
|
|
30
|
+
exclude .gitignore
|
|
31
|
+
exclude .env*
|
|
32
|
+
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dfm-python
|
|
3
|
+
Version: 0.1.0
|
|
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
|
+
Keywords: dfm,dynamic-factor-model,econometrics,forecasting,nowcasting,time-series
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Requires-Dist: numpy>=1.24.0
|
|
20
|
+
Requires-Dist: pandas>=2.0.0
|
|
21
|
+
Requires-Dist: scipy>=1.10.0
|
|
22
|
+
Provides-Extra: all
|
|
23
|
+
Requires-Dist: httpx>=0.24.0; extra == 'all'
|
|
24
|
+
Requires-Dist: hydra-core>=1.3.0; extra == 'all'
|
|
25
|
+
Requires-Dist: omegaconf>=2.3.0; extra == 'all'
|
|
26
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == 'all'
|
|
27
|
+
Requires-Dist: pydantic-settings>=2.11.0; extra == 'all'
|
|
28
|
+
Requires-Dist: pytest>=7.0.0; extra == 'all'
|
|
29
|
+
Requires-Dist: supabase>=2.0.0; extra == 'all'
|
|
30
|
+
Provides-Extra: database
|
|
31
|
+
Requires-Dist: httpx>=0.24.0; extra == 'database'
|
|
32
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == 'database'
|
|
33
|
+
Requires-Dist: pydantic-settings>=2.11.0; extra == 'database'
|
|
34
|
+
Requires-Dist: supabase>=2.0.0; extra == 'database'
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
37
|
+
Provides-Extra: hydra
|
|
38
|
+
Requires-Dist: hydra-core>=1.3.0; extra == 'hydra'
|
|
39
|
+
Requires-Dist: omegaconf>=2.3.0; extra == 'hydra'
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
|
|
42
|
+
# dfm-python
|
|
43
|
+
|
|
44
|
+
A generic Python implementation of Dynamic Factor Models (DFM) for nowcasting and forecasting.
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
- **Generic DFM Estimation**: EM algorithm for estimating dynamic factor models
|
|
49
|
+
- **Mixed-Frequency Data**: Handle data with different frequencies (daily, monthly, quarterly)
|
|
50
|
+
- **Missing Data Handling**: Robust handling of missing observations
|
|
51
|
+
- **News Decomposition**: Compare forecasts between vintages and attribute changes
|
|
52
|
+
- **Flexible Configuration**: Support for YAML and CSV configuration files
|
|
53
|
+
- **Optional Hydra Integration**: Use Hydra for experiment management (optional dependency)
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install dfm-python
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Optional Dependencies
|
|
62
|
+
|
|
63
|
+
For Hydra configuration support:
|
|
64
|
+
```bash
|
|
65
|
+
pip install dfm-python[hydra]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
For database integration (application-specific):
|
|
69
|
+
```bash
|
|
70
|
+
pip install dfm-python[database]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Install all optional dependencies:
|
|
74
|
+
```bash
|
|
75
|
+
pip install dfm-python[all]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Quick Start
|
|
79
|
+
|
|
80
|
+
### Basic Usage
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from dfm_python import load_config, load_data, dfm
|
|
84
|
+
import pandas as pd
|
|
85
|
+
|
|
86
|
+
# Load configuration from YAML or CSV
|
|
87
|
+
config = load_config('config.yaml')
|
|
88
|
+
|
|
89
|
+
# Load data from CSV file
|
|
90
|
+
X, Time, Z = load_data('data.csv', config, sample_start=pd.Timestamp('2000-01-01'))
|
|
91
|
+
|
|
92
|
+
# Estimate DFM
|
|
93
|
+
Res = dfm(X, config, threshold=1e-4)
|
|
94
|
+
|
|
95
|
+
# Access results
|
|
96
|
+
factors = Res.Z # Extracted factors
|
|
97
|
+
loadings = Res.C # Factor loadings
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Configuration
|
|
101
|
+
|
|
102
|
+
The DFM module uses a configuration object (`DFMConfig`) that defines:
|
|
103
|
+
|
|
104
|
+
- **Series**: Each time series with frequency, transformation, and block membership
|
|
105
|
+
- **Blocks**: Factor blocks with number of factors per block
|
|
106
|
+
- **Estimation Parameters**: EM algorithm settings (threshold, max_iter, etc.)
|
|
107
|
+
|
|
108
|
+
#### YAML Configuration Example
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
# config.yaml
|
|
112
|
+
model:
|
|
113
|
+
series:
|
|
114
|
+
series_1:
|
|
115
|
+
frequency: "m"
|
|
116
|
+
transformation: "pch"
|
|
117
|
+
blocks: [Global]
|
|
118
|
+
series_2:
|
|
119
|
+
frequency: "q"
|
|
120
|
+
transformation: "pca"
|
|
121
|
+
blocks: [Global, Investment]
|
|
122
|
+
|
|
123
|
+
blocks:
|
|
124
|
+
Global:
|
|
125
|
+
factors: 1
|
|
126
|
+
Investment:
|
|
127
|
+
factors: 1
|
|
128
|
+
|
|
129
|
+
dfm:
|
|
130
|
+
ar_lag: 1
|
|
131
|
+
threshold: 1e-5
|
|
132
|
+
max_iter: 5000
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
#### CSV Configuration Example
|
|
136
|
+
|
|
137
|
+
```csv
|
|
138
|
+
SeriesID,Frequency,Transformation,Block_Global,Block_Investment
|
|
139
|
+
series_1,m,pch,1,0
|
|
140
|
+
series_2,q,pca,1,1
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Data Format
|
|
144
|
+
|
|
145
|
+
CSV data files should have:
|
|
146
|
+
- First column: Date (YYYY-MM-DD format)
|
|
147
|
+
- Subsequent columns: One per series (matching SeriesID from config)
|
|
148
|
+
- Column names: Match SeriesID from configuration
|
|
149
|
+
|
|
150
|
+
Example:
|
|
151
|
+
```csv
|
|
152
|
+
Date,series_1,series_2
|
|
153
|
+
2000-01-01,100.0,50.0
|
|
154
|
+
2000-02-01,101.0,51.0
|
|
155
|
+
...
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Core Components
|
|
159
|
+
|
|
160
|
+
### DFMConfig
|
|
161
|
+
|
|
162
|
+
Configuration dataclass that defines the model structure:
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from dfm_python import DFMConfig, SeriesConfig
|
|
166
|
+
|
|
167
|
+
config = DFMConfig(
|
|
168
|
+
series=[
|
|
169
|
+
SeriesConfig(
|
|
170
|
+
series_id="gdp",
|
|
171
|
+
frequency="q",
|
|
172
|
+
transformation="pca",
|
|
173
|
+
blocks=[1, 0] # Binary: loads on block 0 (Global)
|
|
174
|
+
)
|
|
175
|
+
],
|
|
176
|
+
block_names=["Global", "Investment"],
|
|
177
|
+
factors_per_block=[1, 1],
|
|
178
|
+
ar_lag=1,
|
|
179
|
+
threshold=1e-5,
|
|
180
|
+
max_iter=5000
|
|
181
|
+
)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### DFM Estimation
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from dfm_python import dfm
|
|
188
|
+
|
|
189
|
+
# Estimate DFM model
|
|
190
|
+
result = dfm(X, config, threshold=1e-4, max_iter=1000)
|
|
191
|
+
|
|
192
|
+
# Access results
|
|
193
|
+
factors = result.Z # Factor estimates (T x r)
|
|
194
|
+
loadings = result.C # Factor loadings (N x r)
|
|
195
|
+
transition = result.A # Transition matrix
|
|
196
|
+
covariance = result.Q # Factor covariance
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### News Decomposition
|
|
200
|
+
|
|
201
|
+
Compare forecasts between two vintages:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from dfm_python import update_nowcast
|
|
205
|
+
|
|
206
|
+
# Compare old vs new vintage
|
|
207
|
+
news_df, forecast_df = update_nowcast(
|
|
208
|
+
X_old, X_new, Time, config, result,
|
|
209
|
+
series="gdp",
|
|
210
|
+
vintage_old="2024-01-01",
|
|
211
|
+
vintage_new="2024-02-01"
|
|
212
|
+
)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## API Reference
|
|
216
|
+
|
|
217
|
+
### Main Functions
|
|
218
|
+
|
|
219
|
+
- `load_config(file)`: Load configuration from YAML or CSV
|
|
220
|
+
- `load_data(file, config)`: Load and transform data from CSV
|
|
221
|
+
- `dfm(X, config)`: Estimate DFM model using EM algorithm
|
|
222
|
+
- `update_nowcast(...)`: Perform news decomposition between vintages
|
|
223
|
+
|
|
224
|
+
### Configuration
|
|
225
|
+
|
|
226
|
+
- `DFMConfig`: Main configuration class
|
|
227
|
+
- `SeriesConfig`: Individual series configuration
|
|
228
|
+
|
|
229
|
+
### Results
|
|
230
|
+
|
|
231
|
+
- `DFMResult`: Result object with factors, loadings, and model parameters
|
|
232
|
+
|
|
233
|
+
## Architecture
|
|
234
|
+
|
|
235
|
+
The DFM module is designed to be **generic and application-agnostic**:
|
|
236
|
+
|
|
237
|
+
- **Core Module** (`dfm_python`): Generic DFM estimation logic
|
|
238
|
+
- **Adapters** (application-specific): Database integration, API clients, etc.
|
|
239
|
+
|
|
240
|
+
This separation allows the core module to be used in any context while keeping application-specific code separate.
|
|
241
|
+
|
|
242
|
+
## Requirements
|
|
243
|
+
|
|
244
|
+
- Python >= 3.12
|
|
245
|
+
- numpy >= 1.24.0
|
|
246
|
+
- pandas >= 2.0.0
|
|
247
|
+
- scipy >= 1.10.0
|
|
248
|
+
|
|
249
|
+
## License
|
|
250
|
+
|
|
251
|
+
MIT License
|
|
252
|
+
|
|
253
|
+
## Contributing
|
|
254
|
+
|
|
255
|
+
Contributions are welcome! Please ensure that:
|
|
256
|
+
- Core module remains generic (no application-specific code)
|
|
257
|
+
- All optional dependencies are properly handled
|
|
258
|
+
- Tests pass for the core functionality
|
|
259
|
+
|
|
260
|
+
## Citation
|
|
261
|
+
|
|
262
|
+
If you use this package in your research, please cite:
|
|
263
|
+
|
|
264
|
+
```bibtex
|
|
265
|
+
@software{dfm-python,
|
|
266
|
+
title = {dfm-python: Dynamic Factor Models for Nowcasting},
|
|
267
|
+
author = {DFM Python Contributors},
|
|
268
|
+
year = {2024},
|
|
269
|
+
url = {https://github.com/yourusername/dfm-python}
|
|
270
|
+
}
|
|
271
|
+
```
|