tempdisagg 0.2.5__tar.gz → 0.2.7__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.
- tempdisagg-0.2.7/PKG-INFO +308 -0
- tempdisagg-0.2.7/README.md +285 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/pyproject.toml +3 -2
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/__init__.py +14 -13
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/model/models_handler.py +1 -44
- tempdisagg-0.2.7/tempdisagg/model/tempdisagg_base.py +92 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/model/tempdisagg_core.py +22 -81
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/model/tempdisagg_ensemble.py +15 -71
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/model/tempdisagg_fitter.py +17 -17
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/model/tempdisagg_model.py +23 -130
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/model/tempdisagg_visualizer.py +18 -14
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/preprocessing/disagg_input_preparer.py +16 -14
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/preprocessing/timeseries_completer.py +36 -32
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/utils/__init__.py +2 -0
- tempdisagg-0.2.7/tempdisagg/utils/retropolarizer.py +78 -0
- tempdisagg-0.2.7/tempdisagg.egg-info/PKG-INFO +308 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg.egg-info/SOURCES.txt +1 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg.egg-info/requires.txt +1 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tests/test_prediction.py +9 -0
- tempdisagg-0.2.5/PKG-INFO +0 -241
- tempdisagg-0.2.5/README.md +0 -219
- tempdisagg-0.2.5/tempdisagg/model/tempdisagg_base.py +0 -120
- tempdisagg-0.2.5/tempdisagg.egg-info/PKG-INFO +0 -241
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/LICENSE +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/setup.cfg +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/core/__init__.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/core/numeric_utils.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/core/rho_optimizer.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/core/temporal_aggregator.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/model/__init__.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/model/tempdisagg_adjuster.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/model/tempdisagg_summary.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/postprocessing/__init__.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/postprocessing/ensemble_prediction.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/postprocessing/post_estimation.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/preprocessing/__init__.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/preprocessing/conversion_matrix_builder.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/preprocessing/input_preprocessor.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg/utils/logging_utils.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg.egg-info/dependency_links.txt +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tempdisagg.egg-info/top_level.txt +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tests/test_numeric_utils.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tests/test_rho_optimizer.py +0 -0
- {tempdisagg-0.2.5 → tempdisagg-0.2.7}/tests/test_summary_visualize.py +0 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tempdisagg
|
|
3
|
+
Version: 0.2.7
|
|
4
|
+
Summary: Temporal disaggregation models in Python
|
|
5
|
+
Author-email: Jaime Vera-Jaramillo <jaimevera1107@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/jaimevera1107/tempdisagg
|
|
8
|
+
Keywords: temporal disaggregation,time series,econometrics,interpolation
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.8
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: numpy>=1.21
|
|
18
|
+
Requires-Dist: pandas>=1.3
|
|
19
|
+
Requires-Dist: scipy>=1.7
|
|
20
|
+
Requires-Dist: matplotlib>=3.4
|
|
21
|
+
Requires-Dist: scikit-learn<2.0,>=1.6
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# ⚡️ **tempdisagg**
|
|
25
|
+
|
|
26
|
+
>### **Temporal Disaggregation Models in Python**
|
|
27
|
+
|
|
28
|
+
*High-Frequency Estimation from Low-Frequency Data — Modular · Robust · Ready for Production*
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+

|
|
32
|
+

|
|
33
|
+

|
|
34
|
+

|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
`tempdisagg` is a production-ready Python library for **temporal disaggregation of time series** — transforming low-frequency data into high-frequency estimates while preserving consistency.
|
|
39
|
+
|
|
40
|
+
> It supports all major classical methods — **Chow-Lin**, **Litterman**, **Denton**, **Fernández**, **Uniform** — and provides a **clean modular architecture** inspired by R's `tempdisagg`, with modern additions:
|
|
41
|
+
|
|
42
|
+
- 📈 Regression + autoregressive adjustment
|
|
43
|
+
- 📉 Differencing & smoothing interpolators
|
|
44
|
+
- 🤖 Ensemble prediction engine
|
|
45
|
+
- 🧠 Intelligent padding & interpolation
|
|
46
|
+
- 🔧 Post-estimation fix for negative values
|
|
47
|
+
- 🔄 Optional retropolarization via regression
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 🔍 Why Temporal Disaggregation?
|
|
52
|
+
|
|
53
|
+
Official indicators often come in low frequency (e.g. yearly GDP), while economic analysis or forecasting needs monthly or quarterly granularity. `tempdisagg` fills this gap using consistent econometric techniques to create **granular estimates** that **respect original aggregates**.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 📚 Methods Implemented
|
|
58
|
+
|
|
59
|
+
| Method(s) | Description |
|
|
60
|
+
|-------------------------------------------------------------------------|---------------------------------------------------------------|
|
|
61
|
+
| `ols` | Ordinary Least Squares (baseline) |
|
|
62
|
+
| `denton`, `denton-opt` | Denton interpolation with differencing |
|
|
63
|
+
| `denton-colette` | Cholette smoother variant from Dagum & Cholette |
|
|
64
|
+
| `chow-lin`, `chow-lin-opt`, `chow-lin-ecotrim`, `chow-lin-quilis` | Regression + AR(1) residual modeling |
|
|
65
|
+
| `litterman`, `litterman-opt` | Random walk / AR(1) prior models |
|
|
66
|
+
| `fernandez` | Fixed-ρ Litterman (ρ = 0) |
|
|
67
|
+
| `fast` | Fast approximation of Denton-Cholette |
|
|
68
|
+
| `uniform` | Even distribution across subperiods |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 💾 Installation
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install tempdisagg
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 🚀 Quick Example
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from tempdisagg import TempDisaggModel
|
|
84
|
+
import pandas as pd
|
|
85
|
+
import numpy as np
|
|
86
|
+
|
|
87
|
+
# Sample input data (monthly disaggregation of yearly total)
|
|
88
|
+
df = pd.DataFrame({
|
|
89
|
+
"Index": [2020]*12 + [2021]*12,
|
|
90
|
+
"Grain": list(range(1, 13)) * 2,
|
|
91
|
+
"y": [1200] + [np.nan]*11 + [1500] + [np.nan]*11,
|
|
92
|
+
"X": np.linspace(100, 200, 24)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
# Fit model
|
|
96
|
+
model = TempDisaggModel(method="chow-lin-opt", conversion="sum")
|
|
97
|
+
model.fit(df)
|
|
98
|
+
|
|
99
|
+
# Predict high-frequency series
|
|
100
|
+
y_hat = model.predict()
|
|
101
|
+
|
|
102
|
+
# Adjust negatives (if any; OPTIONAL)
|
|
103
|
+
y_adj = model.adjust_output()
|
|
104
|
+
|
|
105
|
+
# Show results
|
|
106
|
+
model.summary()
|
|
107
|
+
model.plot()
|
|
108
|
+
````
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## ⚡ Example with Real Data
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
import statsmodels.api as sm
|
|
116
|
+
from tempdisagg import TempDisaggModel
|
|
117
|
+
|
|
118
|
+
# Load macroeconomic dataset (quarterly)
|
|
119
|
+
macro = sm.datasets.macrodata.load_pandas().data
|
|
120
|
+
macro["Index"] = macro["year"].astype(int)
|
|
121
|
+
macro["Grain"] = macro["quarter"].astype(int)
|
|
122
|
+
macro["X"] = macro["realcons"]
|
|
123
|
+
|
|
124
|
+
# Aggregate GDP to annual level
|
|
125
|
+
gdp_annual = macro.groupby("Index")["realgdp"].mean().reset_index()
|
|
126
|
+
gdp_annual.columns = ["Index", "y"]
|
|
127
|
+
|
|
128
|
+
# Merge back into full frame
|
|
129
|
+
df = macro.merge(gdp_annual, on="Index", how="left")[["Index", "Grain", "y", "X"]]
|
|
130
|
+
|
|
131
|
+
# Fit model and predict
|
|
132
|
+
model = TempDisaggModel(method="chow-lin-opt", conversion="average")
|
|
133
|
+
model.fit(df)
|
|
134
|
+
|
|
135
|
+
# Get high-frequency estimates
|
|
136
|
+
y_hat = model.predict(full=False)
|
|
137
|
+
|
|
138
|
+
# Optional: post-estimation adjustment
|
|
139
|
+
y_adj = model.adjust_output(full=False)
|
|
140
|
+
|
|
141
|
+
# Summary and plot
|
|
142
|
+
model.summary()
|
|
143
|
+
model.plot()
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 🤖 Ensemble Prediction
|
|
149
|
+
|
|
150
|
+
Run all models and let the library **find the optimal weighted combination**.
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
model = TempDisaggModel(method="ensemble", conversion="sum")
|
|
154
|
+
model.fit(df)
|
|
155
|
+
|
|
156
|
+
model.summary()
|
|
157
|
+
model.plot()
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Behind the scenes:
|
|
161
|
+
- Each method is fitted separately.
|
|
162
|
+
- Error metrics (e.g. MAE) are computed.
|
|
163
|
+
- Weights are optimized to minimize global error.
|
|
164
|
+
- Final prediction is a weighted average across models.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## 🚫 Negative Value Adjustment
|
|
169
|
+
|
|
170
|
+
When disaggregation outputs negatives (due to smoothing or regression noise), `tempdisagg` can correct them **without violating consistency**.
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
model.fit(df)
|
|
174
|
+
y_hat = model.adjust_output()
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Internally:
|
|
178
|
+
- Detects negatives in each group.
|
|
179
|
+
- Redistributes values proportionally.
|
|
180
|
+
- Ensures aggregate values match original data.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 🧠 Retropolarizer: Smart Interpolation
|
|
185
|
+
|
|
186
|
+
For missing values in the target (`y`), you can activate the **Retropolarizer**: a module that imputes via regression, proportions, or exponential smoothing.
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
from tempdisagg import Retropolarizer
|
|
190
|
+
|
|
191
|
+
retro = Retropolarizer(method="linear_regression")
|
|
192
|
+
df["y_imputed"] = retro.fit_transform(df, target_col="y", aux_col="X")
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Or use it inside any model:
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
model = TempDisaggModel(
|
|
199
|
+
method="chow-lin",
|
|
200
|
+
use_retropolarizer=True,
|
|
201
|
+
retro_method="linear_regression"
|
|
202
|
+
)
|
|
203
|
+
model.fit(df)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Available methods:
|
|
207
|
+
|
|
208
|
+
- 'proportion'
|
|
209
|
+
- 'linear_regression'
|
|
210
|
+
- 'polynomial_regression'
|
|
211
|
+
- 'exponential_smoothing'
|
|
212
|
+
- 'mlp_regression'
|
|
213
|
+
|
|
214
|
+
> **Note:** The Retropolarizer is only used to impute missing values in the `y` column. It is **not** intended for interpolating the `X` (indicator) variable.
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 📘 Input Format
|
|
220
|
+
|
|
221
|
+
Your data must be in long format:
|
|
222
|
+
|
|
223
|
+
| Column | Meaning |
|
|
224
|
+
|----------|------------------------------------------------|
|
|
225
|
+
| `Index` | Low-frequency group ID (e.g., year) |
|
|
226
|
+
| `Grain` | High-frequency unit (e.g., month number) |
|
|
227
|
+
| `y` | Target variable (repeated within group) |
|
|
228
|
+
| `X` | Indicator variable at high frequency |
|
|
229
|
+
|
|
230
|
+
```text
|
|
231
|
+
Index | Grain | y | X
|
|
232
|
+
------|-------|-------|-----
|
|
233
|
+
2020 | 1 | 1000 | 10.1
|
|
234
|
+
2020 | 2 | 1000 | 11.3
|
|
235
|
+
2020 | 3 | 1000 | 12.5
|
|
236
|
+
... | ... | ... | ...
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## 🧩 Modular Design
|
|
242
|
+
|
|
243
|
+
| Component | Role |
|
|
244
|
+
|------------------------|--------------------------------------------|
|
|
245
|
+
| `TempDisaggModel` | High-level interface |
|
|
246
|
+
| `DisaggInputPreparer` | Input validation + padding + interpolation |
|
|
247
|
+
| `ModelsHandler` | Implements disaggregation methods |
|
|
248
|
+
| `RhoOptimizer` | Optimizes AR(1) parameter |
|
|
249
|
+
| `PostEstimation` | Adjusts negative values |
|
|
250
|
+
| `EnsemblePrediction` | Combines multiple models |
|
|
251
|
+
| `Retropolarizer` | Regression-based imputer for `y` |
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## 🧪 Testing & Reliability
|
|
256
|
+
|
|
257
|
+
- ✅ Full test coverage
|
|
258
|
+
- ✅ Input validation & fallbacks
|
|
259
|
+
- ✅ Padding & missing data supported
|
|
260
|
+
- ✅ Consistency validation `C @ y_hat ≈ y_l`
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## 🔍 API Overview
|
|
265
|
+
|
|
266
|
+
| Method | Description |
|
|
267
|
+
|--------------------------------|-----------------------------------------------------|
|
|
268
|
+
| `.fit(df)` | Fit the model |
|
|
269
|
+
| `.predict(full=True)` | Predict disaggregated values |
|
|
270
|
+
| `.adjust_output(full=True)` | Fix negative predictions |
|
|
271
|
+
| `.summary(metric="mae")` | Print coefficients, rho, and errors |
|
|
272
|
+
| `.plot(use_adjusted=False)` | Visualize predictions |
|
|
273
|
+
| `.get_params()` / `.set_params()` | Get/set model config |
|
|
274
|
+
| `.to_dict()` | Export results |
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## 📦 Dependencies
|
|
279
|
+
|
|
280
|
+
`tempdisagg` relies on the following Python libraries:
|
|
281
|
+
|
|
282
|
+
- `pandas` – data manipulation
|
|
283
|
+
- `numpy` – numerical operations
|
|
284
|
+
- `matplotlib` – plotting
|
|
285
|
+
- `scipy` and `statsmodels` – regression and optimization
|
|
286
|
+
- `scikit-learn` – used in `Retropolarizer` (e.g., MLP imputation)
|
|
287
|
+
|
|
288
|
+
> These packages are automatically installed with `pip install tempdisagg`.
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## 📚 References
|
|
293
|
+
|
|
294
|
+
- Dagum & Cholette (2006), *Benchmarking, Temporal Distribution, and Reconciliation Methods*
|
|
295
|
+
- Denton (1971), *Adjustment of Monthly or Quarterly Series*
|
|
296
|
+
- Chow & Lin (1971), *Best Linear Unbiased Estimation of Missing Observations*
|
|
297
|
+
- Fernández (1981), *Methodological Note on a Monthly Indicator*
|
|
298
|
+
- Litterman (1983), *A Random Walk, Markov Model for Forecasting*
|
|
299
|
+
- tempdisagg (R package)
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## 📃 License
|
|
304
|
+
|
|
305
|
+
MIT License — See [LICENSE](./LICENSE) for details.
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
Developed and maintained by Jaime Vera-Jaramillo — Contributions are welcome ❤️.
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# ⚡️ **tempdisagg**
|
|
2
|
+
|
|
3
|
+
>### **Temporal Disaggregation Models in Python**
|
|
4
|
+
|
|
5
|
+
*High-Frequency Estimation from Low-Frequency Data — Modular · Robust · Ready for Production*
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+

|
|
9
|
+

|
|
10
|
+

|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
`tempdisagg` is a production-ready Python library for **temporal disaggregation of time series** — transforming low-frequency data into high-frequency estimates while preserving consistency.
|
|
16
|
+
|
|
17
|
+
> It supports all major classical methods — **Chow-Lin**, **Litterman**, **Denton**, **Fernández**, **Uniform** — and provides a **clean modular architecture** inspired by R's `tempdisagg`, with modern additions:
|
|
18
|
+
|
|
19
|
+
- 📈 Regression + autoregressive adjustment
|
|
20
|
+
- 📉 Differencing & smoothing interpolators
|
|
21
|
+
- 🤖 Ensemble prediction engine
|
|
22
|
+
- 🧠 Intelligent padding & interpolation
|
|
23
|
+
- 🔧 Post-estimation fix for negative values
|
|
24
|
+
- 🔄 Optional retropolarization via regression
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 🔍 Why Temporal Disaggregation?
|
|
29
|
+
|
|
30
|
+
Official indicators often come in low frequency (e.g. yearly GDP), while economic analysis or forecasting needs monthly or quarterly granularity. `tempdisagg` fills this gap using consistent econometric techniques to create **granular estimates** that **respect original aggregates**.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 📚 Methods Implemented
|
|
35
|
+
|
|
36
|
+
| Method(s) | Description |
|
|
37
|
+
|-------------------------------------------------------------------------|---------------------------------------------------------------|
|
|
38
|
+
| `ols` | Ordinary Least Squares (baseline) |
|
|
39
|
+
| `denton`, `denton-opt` | Denton interpolation with differencing |
|
|
40
|
+
| `denton-colette` | Cholette smoother variant from Dagum & Cholette |
|
|
41
|
+
| `chow-lin`, `chow-lin-opt`, `chow-lin-ecotrim`, `chow-lin-quilis` | Regression + AR(1) residual modeling |
|
|
42
|
+
| `litterman`, `litterman-opt` | Random walk / AR(1) prior models |
|
|
43
|
+
| `fernandez` | Fixed-ρ Litterman (ρ = 0) |
|
|
44
|
+
| `fast` | Fast approximation of Denton-Cholette |
|
|
45
|
+
| `uniform` | Even distribution across subperiods |
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 💾 Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install tempdisagg
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 🚀 Quick Example
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from tempdisagg import TempDisaggModel
|
|
61
|
+
import pandas as pd
|
|
62
|
+
import numpy as np
|
|
63
|
+
|
|
64
|
+
# Sample input data (monthly disaggregation of yearly total)
|
|
65
|
+
df = pd.DataFrame({
|
|
66
|
+
"Index": [2020]*12 + [2021]*12,
|
|
67
|
+
"Grain": list(range(1, 13)) * 2,
|
|
68
|
+
"y": [1200] + [np.nan]*11 + [1500] + [np.nan]*11,
|
|
69
|
+
"X": np.linspace(100, 200, 24)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
# Fit model
|
|
73
|
+
model = TempDisaggModel(method="chow-lin-opt", conversion="sum")
|
|
74
|
+
model.fit(df)
|
|
75
|
+
|
|
76
|
+
# Predict high-frequency series
|
|
77
|
+
y_hat = model.predict()
|
|
78
|
+
|
|
79
|
+
# Adjust negatives (if any; OPTIONAL)
|
|
80
|
+
y_adj = model.adjust_output()
|
|
81
|
+
|
|
82
|
+
# Show results
|
|
83
|
+
model.summary()
|
|
84
|
+
model.plot()
|
|
85
|
+
````
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## ⚡ Example with Real Data
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
import statsmodels.api as sm
|
|
93
|
+
from tempdisagg import TempDisaggModel
|
|
94
|
+
|
|
95
|
+
# Load macroeconomic dataset (quarterly)
|
|
96
|
+
macro = sm.datasets.macrodata.load_pandas().data
|
|
97
|
+
macro["Index"] = macro["year"].astype(int)
|
|
98
|
+
macro["Grain"] = macro["quarter"].astype(int)
|
|
99
|
+
macro["X"] = macro["realcons"]
|
|
100
|
+
|
|
101
|
+
# Aggregate GDP to annual level
|
|
102
|
+
gdp_annual = macro.groupby("Index")["realgdp"].mean().reset_index()
|
|
103
|
+
gdp_annual.columns = ["Index", "y"]
|
|
104
|
+
|
|
105
|
+
# Merge back into full frame
|
|
106
|
+
df = macro.merge(gdp_annual, on="Index", how="left")[["Index", "Grain", "y", "X"]]
|
|
107
|
+
|
|
108
|
+
# Fit model and predict
|
|
109
|
+
model = TempDisaggModel(method="chow-lin-opt", conversion="average")
|
|
110
|
+
model.fit(df)
|
|
111
|
+
|
|
112
|
+
# Get high-frequency estimates
|
|
113
|
+
y_hat = model.predict(full=False)
|
|
114
|
+
|
|
115
|
+
# Optional: post-estimation adjustment
|
|
116
|
+
y_adj = model.adjust_output(full=False)
|
|
117
|
+
|
|
118
|
+
# Summary and plot
|
|
119
|
+
model.summary()
|
|
120
|
+
model.plot()
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 🤖 Ensemble Prediction
|
|
126
|
+
|
|
127
|
+
Run all models and let the library **find the optimal weighted combination**.
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
model = TempDisaggModel(method="ensemble", conversion="sum")
|
|
131
|
+
model.fit(df)
|
|
132
|
+
|
|
133
|
+
model.summary()
|
|
134
|
+
model.plot()
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Behind the scenes:
|
|
138
|
+
- Each method is fitted separately.
|
|
139
|
+
- Error metrics (e.g. MAE) are computed.
|
|
140
|
+
- Weights are optimized to minimize global error.
|
|
141
|
+
- Final prediction is a weighted average across models.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 🚫 Negative Value Adjustment
|
|
146
|
+
|
|
147
|
+
When disaggregation outputs negatives (due to smoothing or regression noise), `tempdisagg` can correct them **without violating consistency**.
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
model.fit(df)
|
|
151
|
+
y_hat = model.adjust_output()
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Internally:
|
|
155
|
+
- Detects negatives in each group.
|
|
156
|
+
- Redistributes values proportionally.
|
|
157
|
+
- Ensures aggregate values match original data.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 🧠 Retropolarizer: Smart Interpolation
|
|
162
|
+
|
|
163
|
+
For missing values in the target (`y`), you can activate the **Retropolarizer**: a module that imputes via regression, proportions, or exponential smoothing.
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from tempdisagg import Retropolarizer
|
|
167
|
+
|
|
168
|
+
retro = Retropolarizer(method="linear_regression")
|
|
169
|
+
df["y_imputed"] = retro.fit_transform(df, target_col="y", aux_col="X")
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Or use it inside any model:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
model = TempDisaggModel(
|
|
176
|
+
method="chow-lin",
|
|
177
|
+
use_retropolarizer=True,
|
|
178
|
+
retro_method="linear_regression"
|
|
179
|
+
)
|
|
180
|
+
model.fit(df)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Available methods:
|
|
184
|
+
|
|
185
|
+
- 'proportion'
|
|
186
|
+
- 'linear_regression'
|
|
187
|
+
- 'polynomial_regression'
|
|
188
|
+
- 'exponential_smoothing'
|
|
189
|
+
- 'mlp_regression'
|
|
190
|
+
|
|
191
|
+
> **Note:** The Retropolarizer is only used to impute missing values in the `y` column. It is **not** intended for interpolating the `X` (indicator) variable.
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 📘 Input Format
|
|
197
|
+
|
|
198
|
+
Your data must be in long format:
|
|
199
|
+
|
|
200
|
+
| Column | Meaning |
|
|
201
|
+
|----------|------------------------------------------------|
|
|
202
|
+
| `Index` | Low-frequency group ID (e.g., year) |
|
|
203
|
+
| `Grain` | High-frequency unit (e.g., month number) |
|
|
204
|
+
| `y` | Target variable (repeated within group) |
|
|
205
|
+
| `X` | Indicator variable at high frequency |
|
|
206
|
+
|
|
207
|
+
```text
|
|
208
|
+
Index | Grain | y | X
|
|
209
|
+
------|-------|-------|-----
|
|
210
|
+
2020 | 1 | 1000 | 10.1
|
|
211
|
+
2020 | 2 | 1000 | 11.3
|
|
212
|
+
2020 | 3 | 1000 | 12.5
|
|
213
|
+
... | ... | ... | ...
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 🧩 Modular Design
|
|
219
|
+
|
|
220
|
+
| Component | Role |
|
|
221
|
+
|------------------------|--------------------------------------------|
|
|
222
|
+
| `TempDisaggModel` | High-level interface |
|
|
223
|
+
| `DisaggInputPreparer` | Input validation + padding + interpolation |
|
|
224
|
+
| `ModelsHandler` | Implements disaggregation methods |
|
|
225
|
+
| `RhoOptimizer` | Optimizes AR(1) parameter |
|
|
226
|
+
| `PostEstimation` | Adjusts negative values |
|
|
227
|
+
| `EnsemblePrediction` | Combines multiple models |
|
|
228
|
+
| `Retropolarizer` | Regression-based imputer for `y` |
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## 🧪 Testing & Reliability
|
|
233
|
+
|
|
234
|
+
- ✅ Full test coverage
|
|
235
|
+
- ✅ Input validation & fallbacks
|
|
236
|
+
- ✅ Padding & missing data supported
|
|
237
|
+
- ✅ Consistency validation `C @ y_hat ≈ y_l`
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## 🔍 API Overview
|
|
242
|
+
|
|
243
|
+
| Method | Description |
|
|
244
|
+
|--------------------------------|-----------------------------------------------------|
|
|
245
|
+
| `.fit(df)` | Fit the model |
|
|
246
|
+
| `.predict(full=True)` | Predict disaggregated values |
|
|
247
|
+
| `.adjust_output(full=True)` | Fix negative predictions |
|
|
248
|
+
| `.summary(metric="mae")` | Print coefficients, rho, and errors |
|
|
249
|
+
| `.plot(use_adjusted=False)` | Visualize predictions |
|
|
250
|
+
| `.get_params()` / `.set_params()` | Get/set model config |
|
|
251
|
+
| `.to_dict()` | Export results |
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## 📦 Dependencies
|
|
256
|
+
|
|
257
|
+
`tempdisagg` relies on the following Python libraries:
|
|
258
|
+
|
|
259
|
+
- `pandas` – data manipulation
|
|
260
|
+
- `numpy` – numerical operations
|
|
261
|
+
- `matplotlib` – plotting
|
|
262
|
+
- `scipy` and `statsmodels` – regression and optimization
|
|
263
|
+
- `scikit-learn` – used in `Retropolarizer` (e.g., MLP imputation)
|
|
264
|
+
|
|
265
|
+
> These packages are automatically installed with `pip install tempdisagg`.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## 📚 References
|
|
270
|
+
|
|
271
|
+
- Dagum & Cholette (2006), *Benchmarking, Temporal Distribution, and Reconciliation Methods*
|
|
272
|
+
- Denton (1971), *Adjustment of Monthly or Quarterly Series*
|
|
273
|
+
- Chow & Lin (1971), *Best Linear Unbiased Estimation of Missing Observations*
|
|
274
|
+
- Fernández (1981), *Methodological Note on a Monthly Indicator*
|
|
275
|
+
- Litterman (1983), *A Random Walk, Markov Model for Forecasting*
|
|
276
|
+
- tempdisagg (R package)
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## 📃 License
|
|
281
|
+
|
|
282
|
+
MIT License — See [LICENSE](./LICENSE) for details.
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
Developed and maintained by Jaime Vera-Jaramillo — Contributions are welcome ❤️.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tempdisagg"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.7"
|
|
8
8
|
description = "Temporal disaggregation models in Python"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -25,7 +25,8 @@ dependencies = [
|
|
|
25
25
|
"numpy>=1.21",
|
|
26
26
|
"pandas>=1.3",
|
|
27
27
|
"scipy>=1.7",
|
|
28
|
-
"matplotlib>=3.4"
|
|
28
|
+
"matplotlib>=3.4",
|
|
29
|
+
"scikit-learn>=1.6,<2.0"
|
|
29
30
|
]
|
|
30
31
|
|
|
31
32
|
[tool.setuptools.packages.find]
|
|
@@ -1,30 +1,31 @@
|
|
|
1
|
-
# tempdisagg/__init__.py
|
|
2
|
-
|
|
3
1
|
"""
|
|
4
2
|
tempdisagg: Temporal disaggregation of low-frequency time series using various statistical methods.
|
|
5
3
|
"""
|
|
6
4
|
|
|
7
5
|
# Public API
|
|
8
6
|
from .model.tempdisagg_model import TempDisaggModel
|
|
7
|
+
from .utils.retropolarizer import Retropolarizer
|
|
8
|
+
from .core.temporal_aggregator import TemporalAggregator
|
|
9
9
|
|
|
10
10
|
# Define what gets imported with `from tempdisagg import *`
|
|
11
11
|
__all__ = [
|
|
12
|
-
"
|
|
13
|
-
"ModelFitter",
|
|
14
|
-
"EnsemblePredictor",
|
|
15
|
-
"TempDisaggVisualizer",
|
|
16
|
-
"TempDisaggReporter",
|
|
17
|
-
"TempDisaggAdjuster",
|
|
12
|
+
"ConversionMatrixBuilder",
|
|
18
13
|
"DisaggInputPreparer",
|
|
14
|
+
"EnsemblePrediction",
|
|
15
|
+
"EnsemblePredictor",
|
|
19
16
|
"InputPreprocessor",
|
|
20
|
-
"TimeSeriesCompleter",
|
|
21
|
-
"ConversionMatrixBuilder",
|
|
22
17
|
"ModelsHandler",
|
|
23
|
-
"
|
|
18
|
+
"ModelFitter",
|
|
24
19
|
"NumericUtils",
|
|
25
20
|
"PostEstimation",
|
|
26
|
-
"
|
|
21
|
+
"Retropolarizer",
|
|
22
|
+
"RhoOptimizer",
|
|
23
|
+
"TempDisaggAdjuster",
|
|
24
|
+
"TempDisaggModel",
|
|
25
|
+
"TempDisaggReporter",
|
|
26
|
+
"TempDisaggVisualizer",
|
|
27
27
|
"TemporalAggregator",
|
|
28
|
+
"TimeSeriesCompleter",
|
|
28
29
|
]
|
|
29
30
|
|
|
30
|
-
__version__= "0.2.
|
|
31
|
+
__version__ = "0.2.7"
|