Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR for 0.0.1 release #3

Merged
merged 8 commits into from
Aug 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions feval/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ def gw(
mean_residuals = np.mean(residuals, axis=1)
S = T * (1 - np.mean(mean_residuals**2))
else: # Multistep
omega = compute_covariance(reg, covar_style, kernel, bw, kernel_kwargs)
omega = compute_covariance(
reg, covar_style, kernel=kernel, bw=bw, kernel_kwargs=kernel_kwargs
)
zbar = reg.mean().T
S = T * zbar.T @ np.linalg.pinv(omega) @ zbar

Expand Down Expand Up @@ -102,7 +104,7 @@ def mgw(
it reduces to multivariate Diebold-Mariano (MDM) (Mariano and Preve, 2012)

References:
- Borup, Daniel and Eriksen, Jonas Nygaard and Kjær, Mads Markvart and Thyrsgaard, Martin,
- Borup, Daniel and Eriksen, Jonas Nygaard and Kjaer, Mads Markvart and Thyrsgaard, Martin,
Predicting Bond Return Predictability. Available at http://dx.doi.org/10.2139/ssrn.3513340
- Diebold, F.X., and R.S. Mariano (1995) ‘Comparing Predictive Accuracy,’ Journal
of Business and Economic Statistics 13, 253–263.
Expand Down Expand Up @@ -149,7 +151,9 @@ def mgw(
reg = np.array([np.kron(h, d) for h, d in zip(H, D)])

Dbar = np.mean(reg, axis=0)
omega = compute_covariance(reg, Dbar, covar_style, kernel, bw, kernel_kwargs)
omega = compute_covariance(
reg, covar_style, Dbar=Dbar, kernel=kernel, bw=bw, kernel_kwargs=kernel_kwargs
)

dof = H.shape[1] * p
S = (T * Dbar @ np.linalg.pinv(omega) @ Dbar.T).item()
Expand All @@ -172,7 +176,7 @@ def cmcs(L: np.array, H: Optional[np.array] = None, alpha: float = 0.05, **kwarg
Borup (https://sites.google.com/view/danielborup/research)

References:
- Borup, Daniel and Eriksen, Jonas Nygaard and Kjær, Mads Markvart and Thyrsgaard, Martin,
- Borup, Daniel and Eriksen, Jonas Nygaard and Kjaer, Mads Markvart and Thyrsgaard, Martin,
Predicting Bond Return Predictability. Available at http://dx.doi.org/10.2139/ssrn.3513340
- Hansen, P. R., Lunde, A., & Nason, J. M. (2011). The model confidence set. Econometrica, 79(2), 453-497.

Expand All @@ -193,23 +197,20 @@ def cmcs(L: np.array, H: Optional[np.array] = None, alpha: float = 0.05, **kwarg
removed: (1xk) np.array where a column represents an algorithm cycle.
That way, we can see which model index was removed at which iteration.
"""
# Initialize
T = L.shape[0]
k = L.shape[1]
if H is None:
H = np.ones((T, 1))
T, k = L.shape

H = np.ones((T, 1)) if H is None else H

# Init loop
S, cval, pval = np.inf, 1, 1
mcs = np.ones((1, k))
removed = np.zeros((1, k))

j = 0
while S > cval:
# Create L_to_use, the losses of models still in MCS
L_to_use = L[:, (mcs == 1)[0]]
L_to_use = L[:, mcs[0].astype(bool)]

if L_to_use.shape[1] == 1: # Only 1 model left in set
# If only one model left in the set, exit loop
if L_to_use.shape[1] == 1:
break

# Perform MGW
Expand All @@ -234,7 +235,7 @@ def elim_rule(L: np.array, mcs: np.array, H: Optional[np.array] = None):
Borup (https://sites.google.com/view/danielborup/research)

References:
- Borup, Daniel and Eriksen, Jonas Nygaard and Kjær, Mads Markvart and Thyrsgaard, Martin,
- Borup, Daniel and Eriksen, Jonas Nygaard and Kjaer, Mads Markvart and Thyrsgaard, Martin,
Predicting Bond Return Predictability. Available at http://dx.doi.org/10.2139/ssrn.3513340
- Hansen, P. R., Lunde, A., & Nason, J. M. (2011). The model confidence set. Econometrica, 79(2), 453-497.

Expand Down Expand Up @@ -311,12 +312,18 @@ def validate_args(L, covar_style, kernel, bw):
raise ValueError(f"{bw=} incompatible with {covar_style=}.")
if L.shape[1] < 2:
raise ValueError(f"Not enough columns for matrix of losses {L.shape[1]=}.")
if not isinstance(L, np.ndarray):
raise TypeError(
f"Only np.ndarray is supported for the loss (currently {type(L)})."
)
if np.isnan(L).sum() > 0:
raise ValueError("Ensure there are no NaN in your loss matrix.")


def compute_covariance(
reg: np.array,
Dbar: np.array,
covar_style: str,
Dbar: Optional[np.array] = None,
kernel: Optional[Union[str, Callable]] = None,
bw: Optional[int] = None,
kernel_kwargs: Optional[dict] = None,
Expand Down
19 changes: 13 additions & 6 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def setUp(self):
self.Dbar = np.mean(self.reg, axis=0)

def test_sample_covariance(self):
cov_matrix = compute_covariance(self.reg, self.Dbar, covar_style="sample")
cov_matrix = compute_covariance(self.reg, Dbar=self.Dbar, covar_style="sample")
expected_cov = (
(self.reg - self.Dbar).T @ (self.reg - self.Dbar) / (len(self.reg) - 1)
)
Expand All @@ -32,7 +32,7 @@ def test_sample_covariance(self):
def test_string_kernel(self):
kernel_name = "Bartlett"
cov_matrix = compute_covariance(
self.reg, self.Dbar, covar_style="hac", kernel=kernel_name, bw=2
self.reg, Dbar=self.Dbar, covar_style="hac", kernel=kernel_name, bw=2
)

kerfunc = getattr(kernels, kernel_name)
Expand All @@ -46,18 +46,22 @@ def custom_kernel(data, **kwargs):
return np.cov(data, rowvar=False)

cov_matrix = compute_covariance(
self.reg, self.Dbar, covar_style="hac", kernel=custom_kernel
self.reg, Dbar=self.Dbar, covar_style="hac", kernel=custom_kernel
)
expected_cov = np.cov(self.reg, rowvar=False)
np.testing.assert_array_almost_equal(cov_matrix, expected_cov)

def test_unsupported_kernel_type(self):
with self.assertRaises(NotImplementedError):
compute_covariance(self.reg, self.Dbar, covar_style="hac", kernel=12345)
compute_covariance(
self.reg, Dbar=self.Dbar, covar_style="hac", kernel=12345
)

def test_unsupported_covar_style(self):
with self.assertRaises(ValueError):
compute_covariance(self.reg, self.Dbar, covar_style="unsupported_style")
compute_covariance(
self.reg, Dbar=self.Dbar, covar_style="unsupported_style"
)


class GWTests(unittest.TestCase):
Expand Down Expand Up @@ -238,7 +242,7 @@ def test_mcs_uncond_reject(self):
self.assertAlmostEqual(S, 0.16085093233763528)
self.assertAlmostEqual(pval, 0.6883742895715335) # not rejecting, as expected

def test_mcs_order(self):
def test_mcs_order_011(self):
"""
Traditional MCS with MGW, testing ordering.
"""
Expand All @@ -251,20 +255,23 @@ def test_mcs_order(self):
removed, np.array([[0, 0, 0]])
) # Could lead to unexpected behavior

def test_mcs_order_101(self):
F = np.vstack([data.f1, data.f2 + 0.4, data.f3]).T
L_ae = helpers.ae(data.y, F)
mcs, S, cval, pval, removed = cmcs(L_ae)

npt.assert_array_equal(mcs, np.array([[1, 0, 1]]))
npt.assert_array_equal(removed, np.array([[1, 0, 0]]))

def test_mcs_order_110(self):
F = np.vstack([data.f1, data.f2, data.f3 + 0.4]).T
L_ae = helpers.ae(data.y, F)
mcs, S, cval, pval, removed = cmcs(L_ae)

npt.assert_array_equal(mcs, np.array([[1, 1, 0]]))
npt.assert_array_equal(removed, np.array([[2, 0, 0]]))

def test_mcs_order_100(self):
F = np.vstack([data.f1, data.f2 + 0.4, data.f3 + 0.4]).T
L_ae = helpers.ae(data.y, F)
mcs, S, cval, pval, removed = cmcs(L_ae)
Expand Down