-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Add MLE estimation for Markov chains #658
Conversation
All help getting this merged is appreciated! CC @mmcky @Smit-create |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same functionality in QuantEcon.jl
is called estimate_mc_discrete
. Use the same name?
Another approach is to accept a series of state values (rather than indices) and let X = ['a', 'b', 'b', 'd', 'a']
state_values, indices = np.unique(X, return_inverse=True)
# state_values
# array(['a', 'b', 'd'], dtype='<U1')
# indices
# array([0, 1, 1, 2, 0]) Series of vector state values: X = [[0, 0], [1, 2], [1, 2], [3, 3]]
state_values, indices = np.unique(X, return_inverse=True, axis=0)
# state_values
# array([[0, 0],
# [1, 2],
# [3, 3]])
# indices
# array([0, 1, 1, 2]) So the code would look like: def estimate_P(X):
X = np.asarray(X)
axis = 0 if X.ndim > 1 else None
state_values, indices = np.unique(X, return_inverse=True, axis=axis)
n = len(state_values)
P = np.zeros((n, n)) # dtype=float to modify in place
_count_transition_frequencies(indices, P)
P /= P.sum(1)[:, np.newaxis]
mc = MarkovChain(P, state_values=state_values)
return mc |
Just to recall (I had completely forgotten it), there is a thread of interesting discussions here in QuantEcon/QuantEcon.jl#161 from five years ago. |
Thanks @oyamad for the thoughts. I've changed the function name as you suggested. The discussion you mention was interesting. We have the same issue in #640 --- the need to estimate a discrete MC from continuous data. I suggest that we merge this as is and then open issues for adding functionality, including the option you mention of passing in state values rather than indices. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks @jstac. I will merge this in 24 hours pending any further public review.
quantecon/markov/estimate.py
Outdated
from .core import MarkovChain | ||
|
||
def estimate_mc_discrete(index_series, state_values=None): | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
""" | |
r""" |
quantecon/markov/estimate.py
Outdated
new_n = len(state_values) | ||
P = np.empty((new_n, new_n)) | ||
|
||
# Normalize | ||
row_sum = np.sum(trans_counter, axis=1) | ||
for i in range(new_n): | ||
P[i, :] = trans_counter[i, :] / row_sum[i] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for
loop should be avoided when possible:
new_n = len(state_values) | |
P = np.empty((new_n, new_n)) | |
# Normalize | |
row_sum = np.sum(trans_counter, axis=1) | |
for i in range(new_n): | |
P[i, :] = trans_counter[i, :] / row_sum[i] | |
P = trans_counter / trans_counter.sum(1)[:, np.newaxis] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Register
estimate_mc_discrete
tomarkov/__init__.py
. - Do
python qe_apidoc.py
in thedocs
directory.
We might ignore this case, but if |
a95015c
to
b96f317
Compare
Many thanks @Smit-create ! All looks good to me. It will be great to get this merged some we can finish up #640 |
But the second implementation is x1.5 ~ x2 slower (seemingly due to sorting inside rng = np.random.default_rng(281334440973545911265024757027747152088)
n = 1_000
T = 1_000_000
X = rng.integers(n, size=T)
%timeit qe.markov.estimate_mc_discrete(X) Version 1:
Version 2:
|
@oyamad Many thanks for these proposals. Number 1 is a clear improvement and should be adopted. Regarding 2, there is a speed trade-off but I am still in favor because, at least for macro models, these discretizations tend to happen once at the start, rather than repeatedly. One small point on proposal 2: In most instances, when people generate a vector time series, they will generate it as a matrix where columns are observations, as in https://quanteconpy.readthedocs.io/en/latest/tools/lss.html#quantecon.lss.simulate_linear_model. Should we handle that case? |
I think you are right. I'm in favor of all these proposed changes. |
I propose that we change If not, then we should change it to |
That makes sense.
(I have no strong preference over these. The shorter the better?) |
In my mind, "fit" sounds like we are fitting continuous data to a discrete model. Hence it's appropriate for #640 but not here. So, following your principle that shorter is better, let's go for |
Changed the function name to (" |
Yes, good point. You are right @oyamad . Perhaps redundancy is sometimes useful to provide emphasis. (Information theory says that redundancy is the key to communication in a noisy channel...) |
If we go with Version 2 in #658 (comment), we can merge |
Agreed, and thanks. Please go ahead @oyamad or @Smit-create |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ready to merge.
Adds a straightforward implementation of MLE estimation for Markov transition matrices.
Estimation is from a single time series. States that are never visited in the data are removed from state before being added to the
MarkovChain
object (since otherwise the matrix is not stochastic).This PR solves one part of the problem in #640, which is more naturally separated out.