Skip to main content

Multi-Frequency Quantile Regression, Quantile Autoregression & Uncertain QAR — a unified Python toolkit for frequency-quantile econometrics.

Project description

frequencyquantile

Python 3.9+ License: MIT PyPI

A unified Python toolkit for Multi-Frequency Quantile Econometrics — implementing three families of quantile methods from the academic literature in a single, interoperable library.

Paper Method Module
Li, Li & Tsai (2015, JASA) QCOR, QPCOR, QAR, QPACF, QACF, Box-Jenkins quantile_correlation, qar
Adebayo (2026, Applied Economics) MFQR, MFQGC mfqr, mfqgc
Shi & Sheng (2025, Comm. Stat.) Uncertain QAR uncertain_qar

Author: Dr Merwan Roudane · merwanroudane920@gmail.com
GitHub: github.com/merwanroudane/frequencyquantile


Installation

pip install frequencyquantile

From source:

git clone https://github.com/merwanroudane/frequencyquantile.git
cd frequencyquantile
pip install -e .

Dependencies: numpy, scipy, pandas, statsmodels, matplotlib, seaborn, PyEMD, tabulate


Quick Start

import numpy as np
import frequencyquantile as fq

# Generate sample data
np.random.seed(42)
y = np.cumsum(np.random.randn(500)) + 100
x = np.cumsum(np.random.randn(500)) + 50

# === Paper 1: QAR with modified Box-Jenkins ===
model = fq.three_stage_procedure(y, tau=0.05, K=10)

# === Paper 2: Multi-Frequency Quantile Regression ===
mfqr = fq.MFQR(n_imfs=5).fit(y, x, y_name="GDP", x_name="Energy")

# === Paper 2: Multi-Frequency Granger Causality ===
mfqgc = fq.MFQGC(n_imfs=5).fit(y, x, y_name="CO2", x_name="REG")

# === Paper 3: Uncertain QAR ===
data = [(v - 0.05, v + 0.05) for v in y[:30]]
uqar = fq.UncertainQAR(order=3).fit(data)

# === Visualisation ===
plotter = fq.FrequencyQuantilePlotter()
plotter.plot_mfqr_heatmap(mfqr)
plotter.plot_mfqgc_heatmap(mfqgc)

Detailed API Reference

1. Quantile Correlation (Paper 1: Li, Li & Tsai 2015)

fq.qcov(Y, X, tau) — Quantile Covariance

# Eq. (2.1) numerator: qcov_τ{Y, X} = E[ψ_τ(Y − Q_τ,Y)(X − EX)]
val = fq.qcov(Y, X, tau=0.25)
Parameter Type Description
Y array (n,) Response variable
X array (n,) Predictor variable
tau float Quantile level ∈ (0,1)
Returns float Quantile covariance value

fq.qcor(Y, X, tau) — Quantile Correlation

# Eq. (2.1): normalised by √[(τ−τ²)·σ²_X]
r = fq.qcor(Y, X, tau=0.5)       # scalar in [-1, 1]

fq.sample_qcor(Y, X, tau) — With Inference (Theorem 1)

result = fq.sample_qcor(Y, X, tau=0.25)
# result = {'qcor': 0.312, 'se': 0.045, 'z_stat': 6.93, 'p_value': 0.0000}

fq.qpcor(Y, X, Z, tau) — Quantile Partial Correlation

# Eq. (2.4): controls for Z when measuring Y-X association at quantile τ
r_partial = fq.qpcor(Y, X, Z, tau=0.75)
Parameter Type Description
Z array (n, q) Covariates to partial out

fq.sample_qpcor(Y, X, Z, tau) — With Bootstrap SE (Theorem 2)

result = fq.sample_qpcor(Y, X, Z, tau=0.75)
# result = {'qpcor': -0.187, 'se': 0.063, 'z_stat': -2.97, 'p_value': 0.003}

2. Quantile Autoregressive Model (Paper 1)

fq.qpacf(y, tau, max_lag) — Quantile PACF for Model Identification

pacf = fq.qpacf(y, tau=0.05, max_lag=15, bandwidth_scale=0.6)
# pacf['lag']       → array([1, 2, ..., 15])
# pacf['qpacf']     → array of φ̂_{kk,τ} values
# pacf['se']        → asymptotic standard errors
# pacf['ci_lower']  → lower 95% confidence band
# pacf['ci_upper']  → upper 95% confidence band
Parameter Type Default Description
y array Time series
tau float Quantile level
max_lag int 15 Maximum lag to compute
bandwidth_scale float 0.6 Bofinger bandwidth multiplier

Key property (Lemma 2): QPACF has a cut-off at lag p — φ_{kk,τ} = 0 for k > p.

fq.QARModel — Quantile Autoregressive Model

model = fq.QARModel(tau=0.05)
model.fit(y, lags=[1, 2, 8, 11])    # explicit lags
# OR
model.fit(y, max_lag=5)              # use lags 1..5

# Attributes after fitting:
model.intercept         # φ_0(τ)
model.coefficients      # {lag: φ_j(τ)} dict
model.residuals         # quantile residuals ê_{t,τ}
model.se                # {lag: std_error} dict
model.n_obs             # effective sample size

# Summary table
model.summary()
#   Variable    Coef   Std.Err   z-stat   P>|z|   Sig
#   intercept  -0.747   0.029   -25.76   0.0000   ***
#   y(t-2)      0.118   0.051     2.31   0.0209   **
#   ...

# Forecast
forecasts = model.predict(y, steps=5)

fq.qacf(residuals, tau, max_lag) — Residual QACF for Diagnostics

ac = fq.qacf(model.residuals, tau=0.05, max_lag=15)
# Same structure as qpacf output

fq.qbp_test(residuals, tau, K, p) — Quantile Box-Pierce Test

bp = fq.qbp_test(model.residuals, tau=0.05, K=15, p=4)
# bp = {'Q_BP': 12.34, 'df': 11, 'p_value': 0.338}
# p_value > 0.05 → model adequate ✓

fq.three_stage_procedure(y, tau, K, alpha) — Full Box-Jenkins

model = fq.three_stage_procedure(y, tau=0.05, K=15, alpha=0.05, verbose=True)
# [Stage 1] QPACF suggests QAR(11) at τ=0.05.
#           Significant lags: [2, 8, 11]
# [Stage 2] Estimated QAR(11) with lags [1,2,...,11].
# [Stage 3] Removing lag 1 (p=0.7234).
# [Stage 3] Removing lag 9 (p=0.5891).
# ...
# [Stage 3] Final lags: [2, 4, 10, 11]
#           Q_BP(15) = 14.21, p-value = 0.320
#           ✓  Model passes Q_BP diagnostic.

3. EEMD Decomposition

fq.eemd_decompose(series, n_imfs, ...)

dec = fq.eemd_decompose(y, n_imfs=5, noise_width=0.05, n_trials=100)
# dec['imfs']      → (5, n) array of IMFs (high→low frequency)
# dec['residual']  → (n,) residual trend component
# dec['n_imfs']    → 5
# dec['labels']    → ['IMF1', 'IMF2', ..., 'IMF5', 'Residual']

4. Multi-Frequency Quantile Regression — MFQR (Paper 2: Adebayo 2026)

fq.MFQR Class

mfqr = fq.MFQR(
    n_imfs=5,                          # EEMD decomposition levels
    quantiles=[0.05, 0.25, 0.50, 0.75, 0.95],  # quantile grid
    noise_width=0.05,                  # EEMD noise
    n_trials=100,                      # EEMD ensemble size
)

# Fit: decomposes both series then runs QR at each (frequency, quantile)
mfqr.fit(y, x, y_name="CO2_Residential", x_name="CPU")

# Full summary table
mfqr.summary()
#   Frequency                         Quantile   β(CPU)   Std.Error  t-stat  p-value  Sig
#   IMF1 (High freq / Short-term)       0.05     0.0312    0.0451    0.692   0.4890
#   IMF1 (High freq / Short-term)       0.25     0.0189    0.0234    0.808   0.4191
#   ...
#   IMF5 (Low freq / Long-term)         0.95    -0.2341    0.0782   -2.994   0.0028   ***

# Coefficient matrix (pivot: quantiles × frequencies)
mfqr.coefficient_matrix()
#   Level       1        2        3        4        5
#   Quantile
#   0.05     0.031    0.018   -0.045   -0.121   -0.234
#   0.25     0.019    0.023   -0.038   -0.098   -0.189
#   ...

# Significance stars matrix
mfqr.significance_matrix()

5. Multi-Frequency Quantile Granger Causality — MFQGC (Paper 2)

fq.MFQGC Class

mfqgc = fq.MFQGC(
    n_imfs=5,
    quantiles=[0.05, 0.10, 0.25, 0.50, 0.75, 0.90, 0.95],
    max_lag=8,        # max lag for VAR
)

# Fit: EEMD → quantile series → Granger test at each combination
mfqgc.fit(y, x, y_name="RECEM", x_name="EPU")

# Summary
mfqgc.summary()
#   Frequency        Quantile  Lag  F-stat  p-value   Sig   Causal
#   IMF1 (High freq)   0.05     2   1.234   0.2934          No
#   IMF3 (Medium freq) 0.50     3   4.567   0.0034   ***    Yes
#   IMF5 (Low freq)    0.95     1   8.901   0.0001   ***    Yes

# p-value matrix (pivot)
mfqgc.pvalue_matrix()

# Causality significance matrix
mfqgc.causality_matrix()

6. Uncertain Quantile Autoregressive Model (Paper 3: Shi & Sheng 2025)

fq.UncertainQAR Class

Input: Imprecise observations as (lower, upper) interval pairs.

# Data as interval-valued uncertain variables L(a, b)
data = [
    (2.26, 2.36), (3.18, 3.28), (4.00, 4.10), (4.40, 4.50),
    (4.37, 4.47), (4.07, 4.17), (3.91, 4.01), (3.83, 3.93),
    # ... more observations
]

uqar = fq.UncertainQAR(
    order=None,              # None = auto-select via cross-validation
    max_order=5,             # max order for CV search
    quantiles=[0.1, 0.3, 0.5, 0.7, 0.9],
    sig_level=0.05,          # hypothesis test significance
)

uqar.fit(data, verbose=True)
# Selecting order via cross-validation (ATE)...
#   k=1: ATE = 0.019600
#   k=2: ATE = 0.012300
#   k=3: ATE = 0.009900  ← smallest
#   → Selected order k = 3
#
# Quantile s = 0.5:
#   Coefficients: [0.5687, 0.9160, -0.0463, -0.0115]
#   ê_s = 0.0194, σ̂_s = 0.1386
#   Hypothesis test: ✓ PASSED
#   STE = 0.5231
#   Forecast = 4.2105, 95% CI = [3.9306, 4.4904]

# Summary table
uqar.summary()
#   Quantile  Order  ê_s     σ̂_s    H₀ Test  STE     Forecast  CI Lower  CI Upper  ĉ_s0    ĉ_s1    ĉ_s2    ĉ_s3
#   0.1       3      0.077   0.282  Pass     2.080   4.229     3.659     4.798     0.0933  0.3642  0.3290  0.2891
#   0.3       3      0.081   0.291  Pass     2.198   4.235     3.648     4.823     0.0862  0.3444  0.3303  0.3101
#   0.5       3      0.019   0.139  Pass     0.523   4.211     3.931     4.490     0.5687  0.9160 -0.0463 -0.0115
#   ...

# Get optimal quantile (min STE among passing tests)
best_s = uqar.optimal_quantile()  # → 0.5

Also works with precise data (auto-wrapped as zero-width intervals):

uqar = fq.UncertainQAR(order=2).fit(np.array([3.1, 3.5, 3.8, 4.2, ...]))

7. Visualisation

fq.FrequencyQuantilePlotter Class

plotter = fq.FrequencyQuantilePlotter(style="dark", figsize=(12, 6))
Method Description Paper
plot_qpacf(pacf, tau) QPACF lollipop with 95% CI bands 1
plot_qacf(acf, tau) Residual QACF lollipop 1
plot_multi_qpacf(y, taus) Side-by-side QPACF for multiple τ 1
plot_qar_summary(models) Coefficient functions φ(τ) across quantiles 1
plot_mfqr_heatmap(mfqr) Coefficient heatmap (quantile × frequency) 2
plot_mfqgc_heatmap(mfqgc) p-value heatmap with significance stars 2
plot_eemd(decomp) EEMD decomposition panel plot 2
plot_uncertain_forecast(uqar, data) Fan chart across quantiles 3
# All methods support saving:
plotter.plot_mfqr_heatmap(mfqr, title="CPU → RECEM", save="mfqr_cpu_recem.png")

8. Tables

tbl = fq.FrequencyQuantileTable(fmt="fancy_grid")  # or "latex", "html", "pipe"
tbl.qar_summary(model)
tbl.mfqr_summary(mfqr)
tbl.mfqgc_summary(mfqgc)
tbl.uncertain_qar_summary(uqar)

# LaTeX export
latex_str = tbl.to_latex(mfqr.summary(), caption="MFQR Results", label="tab:mfqr")

Full Workflow Example

import numpy as np
import pandas as pd
import frequencyquantile as fq

# ── Load data ──
df = pd.read_csv("data.csv", parse_dates=["date"])
y = df["CO2_residential"].values
x_cpu = df["CPU"].values
x_epu = df["EPU"].values
x_reg = df["REG"].values

# ── Step 1: EEMD Decomposition ──
plotter = fq.FrequencyQuantilePlotter()
dec = fq.eemd_decompose(y, n_imfs=5)
plotter.plot_eemd(dec, series_name="RECEM", save="eemd_recem.png")

# ── Step 2: QAR Analysis at key quantiles ──
taus = [0.05, 0.25, 0.50, 0.75, 0.95]
models = {}
for tau in taus:
    models[tau] = fq.three_stage_procedure(y, tau=tau, K=10, verbose=False)
plotter.plot_qar_summary(models, save="qar_coefficients.png")

# ── Step 3: MFQR for each predictor ──
tbl = fq.FrequencyQuantileTable()
for x, name in [(x_cpu, "CPU"), (x_epu, "EPU"), (x_reg, "REG")]:
    mfqr = fq.MFQR(n_imfs=5).fit(y, x, y_name="RECEM", x_name=name)
    tbl.mfqr_summary(mfqr)
    plotter.plot_mfqr_heatmap(mfqr, save=f"mfqr_{name}.png")

# ── Step 4: MFQGC Causality ──
for x, name in [(x_cpu, "CPU"), (x_epu, "EPU"), (x_reg, "REG")]:
    gc = fq.MFQGC(n_imfs=5).fit(y, x, y_name="RECEM", x_name=name)
    tbl.mfqgc_summary(gc)
    plotter.plot_mfqgc_heatmap(gc, save=f"mfqgc_{name}.png")

# ── Step 5: Uncertain QAR (if interval data) ──
intervals = [(v - 0.05, v + 0.05) for v in y[:30]]
uqar = fq.UncertainQAR(quantiles=[0.1, 0.3, 0.5, 0.7, 0.9]).fit(intervals)
tbl.uncertain_qar_summary(uqar)
plotter.plot_uncertain_forecast(uqar, data=intervals, save="uqar_forecast.png")

Library Architecture

frequencyquantile/
├── __init__.py              # Public API
├── utils.py                 # Check function, QR solver, density estimation
├── quantile_correlation.py  # QCOR, QPCOR (Paper 1, Sec. 2)
├── qar.py                   # QARModel, QPACF, QACF, Q_BP (Paper 1, Sec. 3)
├── decomposition.py         # EEMD wrapper (Wu & Huang 2009)
├── mfqr.py                  # MFQR (Paper 2)
├── mfqgc.py                 # MFQGC (Paper 2)
├── uncertain_qar.py         # Uncertain QAR (Paper 3)
├── visualization.py         # Dark-mode publication plots
└── tables.py                # Formatted summary tables

References

  1. Li G., Li Y., Tsai C-L. (2015). "Quantile Correlations and Quantile Autoregressive Modeling", Journal of the American Statistical Association, 110(509): 246–261.

  2. Adebayo T.S. (2026). "Response of sectoral CO₂ emissions to climate and economic policy uncertainties: a multi-frequency quantile analysis", Applied Economics, 58(20): 3922–3941.

  3. Shi Y., Sheng Y. (2025). "Uncertain quantile autoregressive model", Communications in Statistics - Simulation and Computation, 54(6): 1869–1889.

  4. Koenker R., Xiao Z. (2006). "Quantile Autoregression", JASA, 101: 980–990.

  5. Wu Z., Huang N.E. (2009). "Ensemble Empirical Mode Decomposition", Advances in Adaptive Data Analysis, 1(1): 1–41.

  6. Liu B. (2007). Uncertainty Theory, 2nd edn. Springer.


License

MIT License — Copyright (c) 2026 Dr Merwan Roudane


Citation

@software{roudane2026frequencyquantile,
  author  = {Roudane, Merwan},
  title   = {frequencyquantile: Multi-Frequency Quantile Econometrics Toolkit},
  year    = {2026},
  url     = {https://github.com/merwanroudane/frequencyquantile},
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

frequencyquantile-1.0.1.tar.gz (28.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

frequencyquantile-1.0.1-py3-none-any.whl (33.7 kB view details)

Uploaded Python 3

File details

Details for the file frequencyquantile-1.0.1.tar.gz.

File metadata

  • Download URL: frequencyquantile-1.0.1.tar.gz
  • Upload date:
  • Size: 28.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for frequencyquantile-1.0.1.tar.gz
Algorithm Hash digest
SHA256 0f79d098b3ed41509e1d20aa87c73493a5848e299861639c215f5ae2ce0e1900
MD5 134ac0acfe93af3182f82fe82c444bb4
BLAKE2b-256 582d5fb90a59b2bc36a09b38cc8b83de1dced9b2a8106895201f7d19b5fbec6a

See more details on using hashes here.

File details

Details for the file frequencyquantile-1.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for frequencyquantile-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1f53e1159a83c53c461bd68a40c7f6456d64a155488a4f57649b12cc9c4b4834
MD5 8ff895264853cc87a036bd5f91515fe7
BLAKE2b-256 bbe2ab9bdfc1fbf3e4cd0c543d3040f33e9b1816b2d7b89ae4354e7e9d0076dc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page