Giter Site home page Giter Site logo

helske / seqhmm Goto Github PK

View Code? Open in Web Editor NEW
91.0 9.0 30.0 14.68 MB

Multivariate and Multichannel Discrete Hidden Markov Models for Categorical Sequences

R 51.82% C++ 13.24% TeX 34.51% C 0.42%
hidden-markov-models mixture-markov-models em-algorithm hmm r categorical-data time-series

seqhmm's Introduction

R-CMD-check Codecov test coverage cran version downloads

seqHMM: Hidden Markov Models for Life Sequences and Other Multivariate, Multichannel Categorical Time Series

The seqHMM package is designed for fitting hidden (or latent) Markov models (HMMs) and mixture hidden Markov models (MHMMs) for social sequence data and other categorical time series. Also some more restricted versions of these type of models are now available: Markov models, mixture Markov models, and latent class models.

The package supports models for one or multiple subjects with one or multiple parallel sequences (channels). External covariates can be added to explain cluster membership in mixture models. The package provides functions for evaluating and comparing models, as well as functions for easy plotting of multichannel sequence data and hidden Markov models.

Maximum likelihood estimation via EM algorithm and direct numerical maximization with analytical gradients is supported. All main algorithms are written in C++ with parallel computation support via OpenMP.

When using the package, please cite:

Helske, Satu and Helske, Jouni (2019). Mixture hidden Markov models for sequence data: the seqHMM package in R. Journal of Statistical Software, 88(3). doi:10.18637/jss.v088.i03

If you find bugs, please add a new issue here in GitHub. You can also contact Satu Helske ([email protected]) or Jouni Helske ([email protected]). We would be happy to hear your feedback.

The package is available on CRAN. Install it via

install.packages("seqHMM")

If you want to try the development version of the seqHMM package, install it from Github using the devtools package:

install.packages("devtools")
library(devtools)
install_github("helske/seqHMM")

Preview of the seqHMM package

This example uses the biofam data from the TraMineR package. The data consist of a sample of 2000 individuals born between 1909 and 1972 constructed from the Swiss Household Panel (SHP) survey in 2002. The sequences consist of family life states from age 15 to 30 (in columns 10 to 25).

  • 0 = "living with parents"
  • 1 = "left home"
  • 2 = "married"
  • 3 = "left home+married"
  • 4 = "child"
  • 5 = "left home+child"
  • 6 = "left home+married+child"
  • 7 = "divorced"

For the functions of the seqHMM package, sequence data is given as a state sequence object (stslist) using the seqdef function in the TraMineR package. To show a more complex example, the original data is split into three separate channels. This data is pre-generated and stored as biofam3c. It contains a list of three sequence data sets and a data frame including the covariates. Find more information and the code for the conversion by typing help(biofam3c).

library(seqHMM)
data(biofam3c)

# Building sequence objects (starting at age 15)
marr.seq <- seqdef(biofam3c$married, start = 15)
child.seq <- seqdef(biofam3c$children, start = 15)
left.seq <- seqdef(biofam3c$left, start = 15)

# Choosing colours for states
attr(marr.seq, "cpal") <- c("#AB82FF", "#E6AB02", "#E7298A")
attr(child.seq, "cpal") <- c("#66C2A5", "#FC8D62")
attr(left.seq, "cpal") <- c("#A6CEE3", "#E31A1C")

Plotting multichannel sequence data

3 Multichannel sequence data are easily plotted using the ssplot function (ssplot for Stacked Sequence Plot). It can plot two types of plots: sequences themselves (index plots) or state distributions at each time point. Plotting index plots with large sequence data can take time, especially if the sequences are also sorted at the same time, so by default the function plots states distributions.

# Plotting state distribution plots of observations
ssplot(
  list(marr.seq, child.seq, left.seq), title = "State distribution plots")

ssp1

Multiple ssp objects can also be plotted together in a grid.

# Preparing plots for women's state distributions
# Sorting by scores from multidimensional scaling
ssp_f2 <- ssp(
  list(marr.seq[biofam3c$covariates$sex == "woman",], 
       child.seq[biofam3c$covariates$sex == "woman",],
       left.seq[biofam3c$covariates$sex == "woman",]),
  type = "d", plots = "obs", border = NA, withlegend = FALSE,
  title = "State distributions for women", title.n = FALSE,
  ylab = c("Married", "Parenthood", "Left home"), ylab.pos = c(1, 2, 1),
  xlab = "Age", xtlab = 15:30)

# Same plot, but sequences instead of state distributions
ssp_f3 <- update(
  ssp_f2, type = "I", sortv = "mds.obs", title = "Sequences for women")

# State distributions with men's data
ssp_m2 <- update(
  ssp_f2, x = list(marr.seq[biofam3c$covariates$sex == "man",], 
                   child.seq[biofam3c$covariates$sex == "man",], 
                   left.seq[biofam3c$covariates$sex == "man",]),
  title = "State distributions for men")

# Men's sequences
ssp_m3 <- update(
  ssp_m2, type = "I", sortv = "mds.obs", title = "Sequences for men")

# Plotting state distributions and index plots of observations for women and men 
gridplot(
  list(ssp_f2, ssp_f3, ssp_m2, ssp_m3), ncol = 2, byrow = TRUE, 
  row.prop = c(0.42, 0.42, 0.16), legend.pos2 = "top")

gridplot

Building models

A model is first constructed using an appropriate build function. There are several such functions available: build_hmm for hidden Markov models, build_mhmm for mixture hidden Markov models, build_mm for Markov models, build_mmm for mixture Markov models, and build_lcm for latent class models.

When estimating hidden Markov models (HMMs) or mixture hidden Markov models, you may either

  1. choose random starting values by giving the number of hidden states with the n_states argument (recommended for simple models) or
  2. give user defined starting values for initial, transition, and emission matrices for faster estimation process (recommended for more complex models).

See the Examples and tips for estimating Markovian models with seqHMM vignette for tips and suggestions on model estimation and setting starting values.

Build functions check that the data and matrices are of the right form and create an object of class hmm (for HMMs and MMs) or mhmm (for MHMMs, MMMs, and LCMs). For the latter, covariates can be omitted or added with the usual formula argument using symbolic formulas familiar from e.g. the lm function. Even though missing observations are allowed in sequence data, covariates must be completely observed.

Estimating hidden Markov models

When estimating Hidden Markov models (HMMs), starting values for initial, transition, and emission probabilities are given in the build_hmm function (either random starting values with the n_states argument or user-defined starting values with transition_probs, emission_probs, and initial_probs). After that, parameters are estimated with the fit_model function.

Options for estimation methods

The fitting function provides three estimation steps: 1) EM algorithm, 2) global optimization, and 3) local optimization. By default, only the EM step is performed. The results from a former step are used as starting values in a latter. In order to reduce the risk of being trapped in a poor local maximum, a large number of initial values should be tested.

If global step is chosen, by default the fit_model function uses the multilevel single-linkage method (MLSL) with the LDS modification. The MLSL draws multiple random starting values and performs local optimization (L-BFGS by default) from each starting point. The LDS modification uses low-discrepancy sequences instead of random numbers as starting points and should improve convergence rate.

In order to reduce computation time spent on non-global optima, the convergence tolerance of the local optimizer is set relatively large. At step 3, a local optimization (again L-BFGS by default) is run with a lower tolerance to find the optimum with high precision.

There are some theoretical guarantees that the MLSL method shoud find all local optima in a finite number of local optimizations. Of course, it might not always succeed in a reasonable time. The EM algorithm can help in finding good boundaries for the search, especially with good starting values, but in some cases it can mislead. A good strategy is to try a couple of different fitting options with different combinations of the methods: e.g. all steps, only global and local steps, and a few evaluations of EM followed by global and local optimization.

It is also possible to run the EM algorithm several times with random starting values. This is done by setting the value restarts in the control_em argument. Although not done by default, this method seems to perform very well as EM algorithm is relatively fast compared to direct numerical estimation.

Single-channel data and random starting values

# Initializing an HMM with 4 hidden states, random starting values                   
init_hmm_mvad1 <- build_hmm(observations = mvad_seq, n_states = 4)

# Estimating model parameters using the EM algorithm with 50 restarts 
# Randomized starting values for transition and emission probabilities
fit_hmm_mvad <- fit_model(init_hmm_mvad, control_em = list(restart = list(times = 50)))

# Saving the HMM
hmm_mvad <- fit_hmm_mvad$model

Multichannel data and user-defined starting values

# Initial values for emission matrices
emiss_marr <- matrix(NA, nrow=4, ncol=3)
emiss_marr[1,] <- seqstatf(marr.seq[, 1:4])[, 2] + 0.1
emiss_marr[2,] <- seqstatf(marr.seq[, 5:8])[, 2] + 0.1
emiss_marr[3,] <- seqstatf(marr.seq[, 9:12])[, 2] + 0.1
emiss_marr[4,] <- seqstatf(marr.seq[, 13:16])[, 2] + 0.1
emiss_marr <- emiss_marr / rowSums(emiss_marr)

emiss_child <- matrix(NA, nrow=4, ncol=2)
emiss_child[1,] <- seqstatf(child.seq[, 1:4])[, 2] + 0.1
emiss_child[2,] <- seqstatf(child.seq[, 5:8])[, 2] + 0.1
emiss_child[3,] <- seqstatf(child.seq[, 9:12])[, 2] + 0.1
emiss_child[4,] <- seqstatf(child.seq[, 13:16])[, 2] + 0.1
emiss_child <- emiss_child / rowSums(emiss_child)

emiss_left <- matrix(NA, nrow=4, ncol=2)
emiss_left[1,] <- seqstatf(left.seq[, 1:4])[, 2] + 0.1
emiss_left[2,] <- seqstatf(left.seq[, 5:8])[, 2] + 0.1
emiss_left[3,] <- seqstatf(left.seq[, 9:12])[, 2] + 0.1
emiss_left[4,] <- seqstatf(left.seq[, 13:16])[, 2] + 0.1
emiss_left <- emiss_left / rowSums(emiss_left)

# Initial values for transition matrix
trans <- matrix(
  c(0.90, 0.06, 0.03, 0.01,
       0, 0.90, 0.07, 0.03,
       0,    0, 0.90, 0.10,
       0,    0,    0,    1), 
  nrow = 4, ncol = 4, byrow = TRUE)

# Initial values for initial state probabilities
initial_probs <- c(0.9, 0.07, 0.02, 0.01)

# Building the hidden Markov model with initial parameter values 
bhmm <- build_hmm(
  observations = list(marr.seq, child.seq, left.seq),
  initial_probs = initial_probs, transition_probs = trans, 
  emission_probs = list(emiss_marr, emiss_child, emiss_left),
  channel_names = c("Marriage", "Parenthood", "Residence"))

# Fitting the HMM:
# step 1) EM algorithm
hmm <- fit_model(bhmm)
hmm$logLik
# -16854.16

# EM + 50 restarts with random starting values for emission probabilities
hmm2 <- fit_model(bhmm, 
  control_em = list(restart = list(times = 50, transition = FALSE, emission = TRUE)))
hmm2$logLik
# -16854.16

# Using all three steps:
# step 1) EM algorithm
# step 2) global optimization (default: MLSL_LDS with LBFGS as local optimizer)
# step 3) local optimization (default: LBFGS) for "final polishing"
# Note: By default, estimation time limited to 60 seconds in step 2.
# Setting 3000 evaluations with unlimited time
hmm3 <- fit_model(bhmm, global_step = TRUE, local_step = TRUE, control_global = list(maxeval = 3000, maxtime = 0))
hmm3$logLik
# -16854.16

# Only global optimization (3000 iterations, unlimited time)
hmm4 <- fit_model(bhmm, em_step = FALSE, global_step = TRUE, local_step = FALSE,
                control_global = list(maxeval = 3000, maxtime = 0))
hmm4$logLik
# -16856.78


Plotting hidden Markov models

A simple plot method is used to show an hmm object as a graph. It shows hidden states as pie charts (vertices), with emission probabilities as slices and transition probabilities as arrows (edges). By default, initial probabilities are shown below the pies.

# Plot HMM
plot(hmm$model)

HMMdefault


# A prettier version
plot(
  hmm$model,
  # larger vertices
  vertex.size = 45,
  # varying curvature of edges
  edge.curved = c(0, -0.7, 0.6, 0, -0.7, 0),
  # legend with two columns and less space
  ncol.legend = 2, legend.prop = 0.4,
  # new label for combined slice
  combined.slice.label = "States with probability < 0.05")

HMM

The ssplot function can also be used for plotting the observed states and/or the most probable paths of hidden states of a HMM.

# Plotting observations and hidden states
ssplot(hmm$model, plots = "both", type = "I")

sspboth_default

# Prettier version
ssplot(
  hmm$model, type = "I", plots = "both",
  # Sorting subjects according to multidimensional
  # scaling scores of the most probable hidden state paths
  sortv = "mds.hidden", 
  # Naming the channels
  ylab = c("Children", "Married", "Residence"), 
  # Title for the plot
  title = "Observed sequences and the 
most probable paths of hidden states",
  # Labels for hidden states (most common states)
  hidden.states.labels = c("1: Childless single, with parents", 
                           "2: Childless single, left home",
                           "3: Married without children",
                           "4: Married parent, left home"),
  # Colours for hidden states
  hidden.states.colors = c("olivedrab", "bisque", "plum", "indianred"),
  # Labels for x axis
  xtlab = 15:30, xlab = "Age",
  # Proportion for legends
  legend.prop = 0.45)

sspboth

Computing likelihood and BIC

The logLik and BIC functions are used for model comparison with the log-likelihood or the Bayesian information criterion (BIC).

# Likelihood
logLik(hmm$model)
# 'log Lik.' -16854.16 (df=25)

# BIC
BIC(hmm$model)
# 33967.66

Trimming HMMs

The trim_hmm function can be used to trim models by setting small probabilities to zero. Here the trimmed model led to model with slightly improved likelihood, so probabilities less than 0.001 could be set to zero.

trimmed_hmm <- trim_model(hmm$model, maxit = 100, zerotol = 1e-03)
# "1 iteration(s) used."
# "Trimming improved log-likelihood, ll_trim-ll_orig = 4.28e-05"

# Transition probabilities of the original HMM
hmm$model$emission_probs
# $Marriage
#            symbol_names
# state_names     divorced      married       single
#           1 0.000000e+00 3.026210e-22 1.000000e+00
#           2 1.793831e-50 2.301489e-09 1.000000e+00
#           3 4.713737e-02 9.528626e-01 6.419152e-14
#           4 1.747152e-02 9.497448e-01 3.278367e-02
# 
# $Parenthood
#            symbol_names
# state_names    childless     children
#           1 9.988180e-01 1.181965e-03
#           2 1.000000e+00 2.390547e-09
#           3 1.000000e+00 5.123136e-09
#           4 1.715309e-14 1.000000e+00
# 
# $`Residence`
#            symbol_names
# state_names    left home with parents
#           1 8.126227e-11 1.000000e+00
#           2 1.000000e+00 2.145277e-16
#           3 6.916852e-01 3.083148e-01
#           4 1.000000e+00 5.190911e-50

# Emission probabilities of the trimmed HMM
trimmed_hmm$emiss
# $Marriage
#            symbol_names
# state_names   divorced   married     single
#           1 0.00000000 0.0000000 1.00000000
#           2 0.00000000 0.0000000 1.00000000
#           3 0.04713737 0.9528626 0.00000000
#           4 0.01747154 0.9497448 0.03278367
# 
# $Parenthood
#            symbol_names
# state_names childless   children
#           1  0.998818 0.00118196
#           2  1.000000 0.00000000
#           3  1.000000 0.00000000
#           4  0.000000 1.00000000
# 
# $`Residence`
#            symbol_names
# state_names left home with parents
#           1 0.0000000    1.0000000
#           2 1.0000000    0.0000000
#           3 0.6916852    0.3083148
#           4 1.0000000    0.0000000

Converting multichannel to single channel models and data

The mc_to_sc function converts a multichannel model into a single channel representation. E.g. the plot function for hmm objects uses this type of conversion. The seqHMM package also includes a similar function mc_to_sc_data for merging multiple state sequence objects.

sc_hmm <- mc_to_sc(hmm$model)

ssplot(sc_hmm, plots = "both", type = "I", sortv = "from.end", sort.channel = 0, 
       xtlab = 15:30, legend.prop = 0.5)

scssp

Mixture hidden Markov models

A mixture hidden Markov model (MHMM) is, by definition, a mixture of HMMs that are estimated together. These are fitted and plotted with similar functions to ones presented before.

Starting values are given as a list consisting of the parameter values for each cluster. Similarly to HMMs, you may either

  1. choose random starting values by giving a vector for the number of hidden states in each cluster/submodel with the n_states argument (recommended for simple models) or
  2. give user defined starting values for initial, transition, and emission matrices for faster estimation process (recommended for more complex models).

The build_mhmm function checks that the model is properly constructed before estimating parameters with the fit_model function.

MHMM without covariates, random starting values

# Building the MHMM
init_mhmm_1 <- build_mhmm(
  observations = list(marr_seq, child_seq, left_seq), 
  channel_names = c("Marriage", "Parenthood", "Residence"),
  # Number of hidden states: 4 hidden states in clusters 1 and 2, 6 hidden states in cluster 3
  n_states = c(4, 4, 6))

# Estimating the parameters with the EM algorithm, 10 restarts with randomized starting values
mhmm_1 <- fit_model(init_mhmm_1, control_em = list(restart = list(times = 10)))

MHMM with covariates, user-defined starting values

# Starting values for emission probabilities

# Cluster 1
alphabet(child.seq) # Checking for the order of observed states
emiss_1_child <- matrix(
  c(0.99, 0.01, # High probability for childless
    0.99, 0.01,
    0.99, 0.01,
    0.99, 0.01), 
  nrow = 4, ncol = 2, byrow = TRUE)

alphabet(marr.seq)
emiss_1_marr <- matrix(
  c(0.01, 0.01, 0.98, # High probability for single
    0.01, 0.01, 0.98,
    0.01, 0.98, 0.01, # High probability for married
    0.98, 0.01, 0.01), # High probability for divorced
  nrow = 4, ncol = 3, byrow = TRUE)

alphabet(left.seq)
emiss_1_left <- matrix(
  c(0.01, 0.99, # High probability for living with parents
    0.99, 0.01, # High probability for having left home
    0.99, 0.01,
    0.99, 0.01), 
  nrow = 4, ncol = 2, byrow = TRUE)

# Cluster 2
emiss_2_child <- matrix(
  c(0.99, 0.01, # High probability for childless
    0.99, 0.01,
    0.99, 0.01,
    0.01, 0.99), 
  nrow = 4, ncol = 2, byrow = TRUE)

emiss_2_marr <- matrix(
  c(0.01, 0.01, 0.98, # High probability for single
    0.01, 0.01, 0.98,
    0.01, 0.98, 0.01, # High probability for married
    0.29, 0.7, 0.01), 
  nrow = 4, ncol = 3, byrow = TRUE)

emiss_2_left <- matrix(
  c(0.01, 0.99, # High probability for living with parents
    0.99, 0.01,
    0.99, 0.01,
    0.99, 0.01), 
  nrow = 4, ncol = 2, byrow = TRUE)

# Cluster 3
emiss_3_child <- matrix(
  c(0.99, 0.01, # High probability for childless
    0.99, 0.01,
    0.01, 0.99,
    0.99, 0.01,
    0.01, 0.99,
    0.01, 0.99), 
  nrow = 6, ncol = 2, byrow = TRUE)

emiss_3_marr <- matrix(
  c(0.01, 0.01, 0.98, # High probability for single
    0.01, 0.01, 0.98,
    0.01, 0.01, 0.98,
    0.01, 0.98, 0.01, # High probability for married
    0.01, 0.98, 0.01,
    0.98, 0.01, 0.01), # High probability for divorced
  nrow = 6, ncol = 3, byrow = TRUE)

emiss_3_left <- matrix(
  c(0.01, 0.99, # High probability for living with parents
    0.99, 0.01,
    0.50, 0.50,
    0.01, 0.99,
    0.99, 0.01,
    0.99, 0.01), 
  nrow = 6, ncol = 2, byrow = TRUE)

# Starting values for transition matrices
trans_1 <- matrix(
  c(0.80, 0.16, 0.03, 0.01,
       0, 0.90, 0.07, 0.03,
       0,    0, 0.90, 0.10,
       0,    0,    0,    1),
  nrow = 4, ncol = 4, byrow = TRUE)

trans_2 <- matrix(
  c(0.80, 0.10, 0.05, 0.03, 0.01, 0.01,
       0, 0.70, 0.10, 0.10, 0.05, 0.05,
       0,    0, 0.85, 0.01, 0.10, 0.04,
       0,    0,    0, 0.90, 0.05, 0.05,
       0,    0,    0,    0, 0.90,  0.1,
       0,    0,    0,    0,    0,    1),
  nrow = 6, ncol = 6, byrow = TRUE)

# Starting values for initial state probabilities
initial_probs_1 <- c(0.9, 0.07, 0.02, 0.01)
initial_probs_2 <- c(0.9, 0.04, 0.03, 0.01, 0.01, 0.01)

# Birth cohort
biofam3c$covariates$cohort <- cut(
  biofam3c$covariates$birthyr, c(1908, 1935, 1945, 1957))
biofam3c$covariates$cohort <- factor(
  biofam3c$covariates$cohort, labels=c("1909-1935", "1936-1945", "1946-1957")
  )

# Build MHMM
init_mhmm_2 <- build_mhmm(
  observations = list(marr.seq, child.seq, left.seq),
  transition_probs = list(trans_1, trans_1, trans_2),
  emission_probs = list(list(emiss_1_marr, emiss_1_child, emiss_1_left), 
                        list(emiss_2_marr, emiss_2_child, emiss_2_left),
                        list(emiss_3_marr, emiss_3_child, emiss_3_left)),
  initial_probs = list(initial_probs_1, initial_probs_1, initial_probs_2),
  formula = ~sex * cohort, data = biofam3c$covariates, 
  cluster_names = c("Cluster 1", "Cluster 2", "Cluster 3"),
  channel_names = c("Marriage", "Parenthood", "Left home")
  )

mhmm_fit_2 <- fit_model(init_mhmm_2)

Summary of MHMM

The summary method computes summaries of the MHMM, e.g. standard errors for covariates and prior and posterior probabilities for subjects. A print method shows some summaries of these: estimates and standard errors for covariates, log-likelihood and BIC, information on most probable clusters and prior and posterior probabilities. Parameter estimates for transitions, emissions, and initial probabilities are omitted by default.

The classification table shows the mean probabilities of belonging to each cluster by the most probable cluster. The most probable cluster is determined by the posterior probabilities. A good model shoud have high proportions in the diagonal. Here, for individuals assigned to cluster 1, the average probability for cluster 1 is 0.84, 0.16 for cluster 2, and close to 0 for cluster 3. The highest probability for the assigned cluster is 0.93 for cluster 3.

summ_mhmm <- summary(mhmm_fit$model)

names(summ_mhmm)
# [1] "logLik"                          "BIC"                            
# [3] "most_probable_cluster"           "coefficients"                   
# [5] "vcov"                            "prior_cluster_probabilities"    
# [7] "posterior_cluster_probabilities" "classification_table"           
# [9] "model" 

summ_mhmm
# Covariate effects :
# Cluster 1 is the reference.
# 
# Cluster 2 :
#                           Estimate  Std. error
# (Intercept)                 1.1400       0.176
# sexwoman                   -0.2150       0.241
# cohort1936-1945             0.0829       0.239
# cohort1946-1957             0.1420       0.218
# sexwoman:cohort1936-1945    0.2960       0.329
# sexwoman:cohort1946-1957    0.0715       0.295
# 
# Cluster 3 :
#                           Estimate  Std. error
# (Intercept)                  0.430       0.197
# sexwoman                     0.149       0.263
# cohort1936-1945             -0.647       0.290
# cohort1946-1957             -0.899       0.269
# sexwoman:cohort1936-1945     0.212       0.387
# sexwoman:cohort1946-1957    -0.122       0.356
# 
# Log-likelihood: -12712.65   BIC: 26524.88 
# 
# Means of prior cluster probabilities :
# Cluster 1 Cluster 2 Cluster 3 
#     0.191     0.622     0.187 
# 
# Most probable clusters :
#             Cluster 1  Cluster 2  Cluster 3
# count             310       1364        326
# proportion      0.155      0.682      0.163
# 
# Classification table :
# Mean cluster probabilities (in columns) by the most probable cluster (rows)
# 
#           Cluster 1 Cluster 2 Cluster 3
# Cluster 1    0.8391    0.1606  0.000274
# Cluster 2    0.0848    0.8634  0.051819
# Cluster 3    0.0169    0.0499  0.933151

Plotting MHMMs

Also MHMMs are plotted with the plot function. The user can choose between an interactive mode (interactive = TRUE), where the model for each cluster is plotted separately, and a combined plot with all models at once.

# Plot mixture hidden Markov model
# Interactive plot, one cluster at a time
plot(mhmm_fit$model, interactive = TRUE)

mixHMM1 mixHMM2 mixHMM3

# Plotting observed sequences and most probable hidden states
# Interactive plot, one cluster at a time
mssplot(
  mhmm_fit$model, plots = "both", type = "I", sortv = "from.end", sort.channel = 1, 
  xtlab = 15:30, xlab = "Age")

mssplot1 mssplot2 mssplot3

seqhmm's People

Contributors

clayton-aldern avatar helske avatar satuhelske avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

seqhmm's Issues

Get likelihood of test observation of model fitted with train observation

Hello,
I have following problem. I have got fitted model (using some training sequence), and now i would like to measure likelihod of new test sequence.

According to this general tutorial https://hmmlearn.readthedocs.io/en/latest/tutorial.html# it is middle case "Given the model parameters and observed data, calculate the likelihood of the data".

As i can undestand library to calc likelihood i need to have model as well as observation sequence. When i am trying to build model all the time i have got some error.

My general emission matrix (8x18) looks like
emission_probs [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16] [,17] [,18] [1,] 0 0 0.0000000 0.75450149 0 0 0 0 0.2454985 0.000 0 0 0 0 0.0000000 0 0.0000000 0 [2,] 0 0 0.0000000 0.00000000 0 0 0 0 0.0000000 0.000 0 0 0 0 0.2330097 0 0.7669903 0 [3,] 0 0 0.9662826 0.03371737 0 0 0 0 0.0000000 0.000 0 0 0 0 0.0000000 0 0.0000000 0 [4,] 0 0 0.0000000 0.00000000 0 0 0 0 0.0000000 0.975 0 0 0 0 0.0250000 0 0.0000000 0 [5,] 0 0 0.0000000 0.00000000 0 0 0 0 0.0000000 0.000 0 0 0 0 0.0000000 1 0.0000000 0 [6,] 0 0 1.0000000 0.00000000 0 0 0 0 0.0000000 0.000 0 0 0 0 0.0000000 0 0.0000000 0 [7,] 0 0 0.0000000 0.37847369 0 0 0 0 0.6215263 0.000 0 0 0 0 0.0000000 0 0.0000000 0 [8,] 0 0 0.0000000 0.00000000 0 0 0 0 0.0000000 0.975 0 0 0 0 0.0250000 0 0.0000000 0 whereas observations looks like
Sequence [1] 3-9-10-16-17-16-15-16-17-16-10-4-3.

first error is Number of columns in emission_probs is not equal to the number of symbols.
So i tried to use only column that accure in observation. This solution works in some cases.

When i tried to evaluate another observations to "3-9-10-16-17") i got errror Emission probabilities in emission_probs do not sum to one.
What more i can make to force it works?

can this package support Multivariate Discrete HMM??

Hello

I'm trying to trying a HMM model with multiple observation sequences. For example, I have a matrix with N rows and M columns. each row means the time series, and columns represent different discrete/category observation. Can I training the HMM using seqHMM??

Thanks

Feature request: allow prior state assignment probabilities from covariates

Using covariates to create a prior cluster probability has been a powerful addition to my MHMMs. Is there any possibility to extend this functionality to prior state probability also? That way users could train models with informative starting state priors that vary with covariates, while still allowing for transitions between the states (allowing greater flexibility than the MHMM analogue).

will it work for multivariate time series prediction : different continues or/and discrete/category observation

great code thanks
may you clarify :
will it work for multivariate time series prediction both regression and classification

each row means the time series, and columns represent different continues or/and discrete/category observation.

1
where all values are continues values
weight height age target
1 56 160 34 1.2
2 77 170 54 3.5
3 87 167 43 0.7
4 55 198 72 0.5
5 88 176 32 2.3

2
or even will it work for multivariate time series where values are mixture of continues and categorical values
for example 2 dimensions have continues values and 3 dimensions are categorical values

color        weight     gender  height  age  target 

1 black 56 m 160 34 yes
2 white 77 f 170 54 no
3 yellow 87 m 167 43 yes
4 white 55 m 198 72 no
5 white 88 f 176 32 yes

ltext param in multichannel HMM plots?

Hi,

I am trying to plot a MC-HMM using the plot function from this package, and I want to add my custom labels to the legend (instead of the alphabet symbols).

I've tried the param "ltext" but I do not know how to associate each element of my custom legend with the combined state it represents.

Any idea of how this param works?

Thanks in advance!

ssplot comes out black

I have 50,000 sequences (one channel) with 18 states. I subset to a random sample of 2000 to reduce the dimensionality. To plot I tried to just view the first 20 columns. The plot is black but the legend on the right is OK.
attr(myseq,"cpal") <-colorpalette[[18]]
ssplot(myseq,xtlab1:20,legend.prop=0.4)

Thanks for your help

Fit mhmm with sequences of different lengths

Hi,

I'm a newbie to R programming and I'm now having some difficulties to fit a mhmm with sequences of different lengths.

In the 'biofam3c' example, all sequences are of the same length, but my data includes sequences with length vary from 20 to 200. And each seq has three different types of observations.

I'm wondering is it possible to model such data and what's the correct way to do that?

Thanks for your time to help in advance!

Error in build_hmm

Hi,

I was trying to implement single channel hmm following the reference manual. I get the error "Error in build_hmm(observations = mvad_seq, n_states = 4) : unused argument (n_states = 4)" when i try to build hmm with given states. The code i used:

data("mvad", package = "TraMineR")
mvad_alphabet <- c("employment", "FE", "HE", "joblessness", "school","training")
mvad_labels <- c("employment", "further education", "higher education","joblessness", "school", "training")
mvad_scodes <- c("EM", "FE", "HE", "JL", "SC", "TR")
mvad_seq <- seqdef(mvad, 17:86, alphabet = mvad_alphabet, states = mvad_scodes, labels = mvad_labels, xtstep = 6)
init_hmm_mvad1 <- build_hmm(observations = mvad_seq, n_states = 4)

Kindly suggest.

Thanks a million,

Underlying algorithm (math derivation) or paper reference for build_mmm?

Which algorithm does build_mmm (building mixture Markov model) implement? Is there a paper or math derivation for reference? According to this documentation section 2.3 on seqHMM, it looks like the mixture hidden Markov models implemented by seqHMM is based on the 1990 paper Mixed Markov Latent Class Models by van de Pol & Langeheine? I didn't find the online free version of this paper though, so it's not clear to me about the math or algorithm details of what build_mmm does?

Since the sequences that I'm trying to cluster are supposed to be independent from each other, and each sequence has different length. Hereby it would be necessary for me to know the underlying algorithm for build_mmm, such that I can decide if it's appropriate to call this function to solve my problem.

Thanks!

fit_model error for markov model with many states

Hello,
I have to build a Markov model with around 5000 states and 32000 sequences, and when I try to execute fit_model after building it, I experience a "Error : std::bad_alloc". But when I only keep 1000 sequences (818 states) I can run fit_model.
Any idea ?
Thanks
mikael

Transition Probabilities for each observation

I am trying to decompose the forward probabilities in each time period into their constituent terms: a_{t-1}(i)q_{ij}P(o_{t}|j)

in

[a_{t}(j)=\sum_{i=1}^{N}a_{t-1}(i)q_{ij}P(o_{t}|j)]

The main challenge is the calculation of P(o_{t}|j) in multivariate case. Sometimes the joint emission probability for each state is zero for a given observation. As a result, forward probabilities should all be NAs due to zero division. However, forward_backward gives me non-zero or non-NA estimate for these scenarios. What is the specific way seqHMM deals with this scenario so that I get an non-zero or non-NA estimate?

Thank you in advance for any direction you can give.

fit_mhmm: Improve warnings

Change "Initial values for coefficients of covariates resulted non-finite cluster probabilities." to warn from later values as well.

One channel full of NA's results in a seqfault

Hi,

Recently, I found this error when executing my code for training HMMs with sequence data.

 *** caught segfault ***
address (nil), cause 'unknown'

Traceback:
 1: .Call("seqHMM_EM", PACKAGE = "seqHMM", transitionMatrix, emissionArray,     initialProbs, obsArray, nSymbols, itermax, tol, trace, threads)
 2: EM(model$transition_probs, emissionArray, model$initial_probs,     obsArray, model$n_symbols, em.con$maxeval, em.con$reltol,     em.con$print_level, threads)
 3: fit_model(mc_init_mod, em_step = TRUE, control_em = control_em,     threads = threads)
 4: FUN(X[[i]], ...)
 5: lapply(pieces, .fun, ...)
 6: structure(lapply(pieces, .fun, ...), dim = dim(pieces))
 7: llply(nstates, function(J) {    mc_init <- hmmHelper.randomInitialVector(J)    mc_trans <- hmmHelper.randomTransitionMatrix.hmm(J)    if (!is.data.frame(seqData)) {        mc_emissions <- llply(seqData, function(channel) {            simulate_emission_probs(n_states = J, n_symbols = length(alphabet(channel)),                 n_clusters = 1)        })    }    else {        mc_emissions <- simulate_emission_probs(n_states = J,             n_clusters = 1, n_symbols = length(alphabet(seqData)))    }    mc_init_mod <- build_hmm(observations = seqData, initial_probs = mc_init,         transition_probs = mc_trans, emission_probs = mc_emissions,         channel_names = names(seqData))    mc_fit <- fit_model(mc_init_mod, em_step = TRUE, control_em = control_em,         threads = threads)    mc_fit$model}, .progress = ifelse(verbose, "text", "none"))
 8: CIIDSHelper.trainHMMs(seqData = notClassData, nstates = possibleStates,     control_em = control_em, threads = threads)
 9: FUN(X[[i]], ...)
10: lapply(pieces, .fun, ...)
11: structure(lapply(pieces, .fun, ...), dim = dim(pieces))
12: llply(unique(dataLabels), function(classLabel) {    if (!is.data.frame(seqData)) {        classData <- llply(seqData, function(channel) channel[dataLabels ==             classLabel, ])        notClassData <- llply(seqData, function(channel) channel[dataLabels !=             classLabel, ])    }    else {        classData <- seqData[dataLabels == classLabel, ]        notClassData <- seqData[dataLabels != classLabel, ]    }    if (verbose)         print(paste("Training HMMs for class", classLabel))    classHMMs <- CIIDSHelper.trainHMMs(seqData = classData, nstates = possibleStates,         control_em = control_em, threads = threads)    if (verbose)         print(paste("Training HMMs for NOT class", classLabel))    notClassHMMs <- CIIDSHelper.trainHMMs(seqData = notClassData,         nstates = possibleStates, control_em = control_em, threads = threads)    list(YES = classHMMs, NO = notClassHMMs)}, .progress = ifelse(verbose, "text", "none"))
13: CIIDSHelper.trainDualHMMClassifier(seqData = trainingData, dataLabels = DATA_LABELS[names(DATA_LABELS) !=     simulation], possibleStates = NSTATES, control_em = CONTROL_EM,     threads = THREADS)
14: .fun(piece, ...)
15: (function (i) {    piece <- pieces[[i]]    if (.inform) {        res <- try(.fun(piece, ...))        if (inherits(res, "try-error")) {            piece <- paste(utils::capture.output(print(piece)),                 collapse = "\n")            stop("with piece ", i, ": \n", piece, call. = FALSE)        }    }    else {        res <- .fun(piece, ...)    }    progress$step()    res})(1L)
16: .Call(loop_apply_, as.integer(n), f, env)
17: loop_apply(n, do.ply)
18: llply(names(DATA_LABELS), function(simulation) {    if (!is.data.frame(seqData)) {        testData <- llply(seqData, function(channel) channel[rownames(channel) %in%             simulation, ])        trainingData <- llply(seqData, function(channel) channel[!(rownames(channel) %in%             simulation), ])    }    else {        testData <- seqData[rownames(seqData) %in% simulation,             ]        trainingData <- seqData[!(rownames(seqData) %in% simulation),             ]    }    print(paste("###### Training Dual HMM Classifier ######"))    dualHMMClassifier <- CIIDSHelper.trainDualHMMClassifier(seqData = trainingData,         dataLabels = DATA_LABELS[names(DATA_LABELS) != simulation],         possibleStates = NSTATES, control_em = CONTROL_EM, threads = THREADS)    print(paste("###### Testing HMM Classifier with different configurations ######"))    result <- llply(names(dualHMMClassifier), function(classLabel) {        CIIDSHelper.seqHMM.llr(seq = testData, baseHMM = dualHMMClassifier[[classLabel]]$YES,             constrainedHMM = dualHMMClassifier[[classLabel]]$NO)    })    names(result) <- names(dualHMMClassifier)    result}, .progress = "text")
19: eval(expr, envir, enclos)
20: eval(ei, envir)
21: withVisible(eval(ei, envir))
22: source("src/CIIDS/5.3_bestConfigurationDHMMClasiffier.R")

I suppose it is something internal in the seqHMM_EM process, may be related to memory troubles. The sequence data object I am using comprises a total of 146 sequences with 2 channels, sized 12.3 Mb

Any idea of what's going on here?

Thanks in advance!

Mistake in the documentation - code example

Hello,

I found a mistake in the code examples of your documentation that makes the code stop working.

Your example:

EM + 50 restarts with random starting values for emission probabilities
hmm2 <- fit_model(bhmm, 
  control_em = list(restart = list(times = 50, transition = FALSE, emission = TRUE))

The final closing bracket is missing. You might want to correct that.
Besides that, nice package and good documentation!

HMM cluster assignments

Is there a method where one can get the probabilities of being in the clusters identified by HMM, I see this is available in the MHMM function but not in HMM. The only thing that I can see is the hidden paths.

plot function

Hi there,

A question about how the plot function is developed. I have a markov chain object (known initial probability, known emission probability and known transition matrix) and can I use the plot function in your package without fitting the hmm model?

how to find out optimal state numbers?

this is another general question about hmm:

how to find out the most optimal numbers of states?

This's because I don't know how many states should be applied and must be inferred from data. Any references would be very much welcomed.

Problems with BIC function

Hi,

I am applying the BIC function over a set of objects with class "hmm", obtained by apply the function fit_model of this package.

When I run my code in RStudio, everything works fine and the BIC values are computed, but when I try to run the code in a R shell, I encounter this error:

Error in log(object$K) : non-numeric argument to mathematical function

I suppose that the BIC function used is not the same depending on the R environment. What is the BIC function used in this package?

Thanks in advance!

ssplot creates two windows

I am trying to create an ssplot within Rmd and knit to pdf. It seems to be producing a blank window in addition to my plot and in the knitted pdf the blank plot window appears ruining formatting.

head(noh)
PID NoH1 NoH2 NoH3 NoH4 NoH5 NoH6 NoH7 NoH8 NoH9 NoH10 NoH11 NoH12
1 1001 1 0 1 0 1 0 1 0 0 0 0 0
2 1004 1 0 1 1 0 1 1 0 1 0 1 0
3 1005 0 0 0 0 0 0 0 0 1 0 0 0
4 1007 0 1 1 0 0 1 1 1 1 1 1 0
5 1011 0 0 0 0 0 0 0 0 0 0 0 1
6 1056 0 0 0 0 0 0 0 0 0 0 0 0
noh_seq<-seqdef(noh[,2:13],start=1, labels = c("Heavy","Not Heavy"))
attr(noh_seq, "cpal") <- c("violetred2", "darkgoldenrod2")
ssplot(noh_seq,
sortv = "from.start", sort.channel = 1, type = "I",
ylab = c("Patients"),
xtlab = 1:12, xlab = "Week", title = "All Patients")

Computing sequence (log)-likelihood

Hi,

I am not sure of how to compute the (log)-likelihood of a test sequence for a fitted model in this package, i.e, given a sequence o and a model H, calculate log[P(o|H)]

Is there a function to directly compute this? I have coded this (horrible) workaround, changing temporaly some of the model parameters and calling the package logLik function:

sequenceLikelihood <- function (hmm,seq) {
  aux <- hmm
  aux$observations <- seq
  aux$n_sequences <- 1
  if (!is.data.frame(seq)) {
    aux$length_of_sequences <- ncol(seq[[1]])
  } else {
    aux$length_of_sequences <- ncol(seq)
  }

  result <- seqHMM:::logLik.hmm(aux)

  if (is.nan(result)) -Inf
  else result
}

Is there any built-in function to do this better?

Thanks!

Error in gridPLT() : Figure region too small and/or viewport too large

Hi,

I have been following 4. Examples with life course data section of your documentation and I cannot plot the state distributions.

When I do:

ssplot(list("Marriage" = marr_seq, "Parenthood" = child_seq,
            "Residence" = left_seq))

I get:

Error in gridPLT() : Figure region too small and/or viewport too large

I have a big screen and I have tried to maximize the plot section, but it does not work.

No summary for latent class model?

There is no summary function for latent class model.

set.seed(123)
obs <- seqdef(rbind(
  matrix(sample(letters[1:3], 5000, TRUE, prob = c(0.1, 0.6, 0.3)), 500, 10),
  matrix(sample(letters[1:3], 2000, TRUE, prob = c(0.4, 0.4, 0.2)), 200, 10)))

# Initialize the model
set.seed(9087)
model <- build_lcm(obs, n_clusters = 2)

# Estimate model parameters
fit <- fit_model(model)
fit

Analytical hessian for all parameters?

This relates to #5. I think I should check if it would be possible to compute the analytical hessian with regards to all model parameters which could be used in vcov. This should be faster and perhaps more accurate than finite difference approach. As the hessian is computed at optimum, we could use natural parametrization here, which might make the calculations easier, and then the resulting standard errors for probabilities might be interesting as well.

Problem installing the package

Guys, I am having an issue installing the package. It keeps throwing this error at me. Could please help me out here. Thanks.

icpc -I/opt/apps/intel13_1/R/3.2.2/lib64/R/include -DNDEBUG -I/usr/local/include -I"/home/rajesh13/R/x86_64-pc-linux-gnu-library/3.2/Rcpp/include" -I"/home/rajesh13/R/x86_64-pc-linux-gnu-library/3.2/RcppArmadillo/include" -fopenmp -fpic -g -O2 -c viterbi.cpp -o viterbi.o
/home/rajesh13/R/x86_64-pc-linux-gnu-library/3.2/Rcpp/include/Rcpp/algorithm.h(153): warning #858: type qualifier on return type is meaningless
static inline RCPP_CONSTEXPR double ZERO() { return 0.0; }
^

/home/rajesh13/R/x86_64-pc-linux-gnu-library/3.2/Rcpp/include/Rcpp/algorithm.h(154): warning #858: type qualifier on return type is meaningless
static inline RCPP_CONSTEXPR double ONE() { return 1.0; }
^

/home/rajesh13/R/x86_64-pc-linux-gnu-library/3.2/Rcpp/include/Rcpp/algorithm.h(162): warning #858: type qualifier on return type is meaningless
static inline RCPP_CONSTEXPR int ZERO() { return 0; }
^

/home/rajesh13/R/x86_64-pc-linux-gnu-library/3.2/Rcpp/include/Rcpp/algorithm.h(163): warning #858: type qualifier on return type is meaningless
static inline RCPP_CONSTEXPR int ONE() { return 1; }
^

viterbi.cpp(40): error #140: too many arguments in function call
delta.col(obs.n_cols - 1).max(q(k, obs.n_cols - 1));
^

compilation aborted for viterbi.cpp (code 2)
make: *** [viterbi.o] Error 2
ERROR: compilation failed for package ‘seqHMM’

  • removing ‘/home/rajesh13/R/x86_64-pc-linux-gnu-library/3.2/seqHMM’

The downloaded source packages are in
‘/tmp/RtmpWmMkcT/downloaded_packages’
Warning message:
In install.packages("seqHMM") :
installation of package ‘seqHMM’ had non-zero exit status

Fit MHMM with Clusters of different rows

Hi,

In the R-documentation example of "build_mhmm" you
are fitting a model with the seqdef-object
"marr_seq" , "child_seq" , "left_seq" all three
seqdef-objects have the same nrow.

Now I'm trying to fit a MHMM with my life-course data.
In my previous analysis I have done an hierarchical clustering.
The classification of the clustering suggests a 5-cluster solution.
The problem is that this 5-cluster has a different number of observations.

So if I build the MHMM, I get following error:
"The number of subjects (rows) is not the same in all channels"
If I cut my list of oberservations to the same number of nrow
the "build_mhmm" function works.

Is there any possibility to model a MHMM with a different number
of rows in the cluster according to my classification?

Thank you and best regards

n_states is not ignored in build_hmm even if transition_probs, emission_probs, and initial_probs arguments are declared

In the help file for build_hmm, the n_states argument it says:

A scalar giving the number of hidden states (not used if starting values for model parameters are given with initial_probs, transition_probs, and emission_probs)

However, if all four arguments are declared (n_states, transition_probs, emission_probs, initial_probs), then it does use n_states and ignores transitions_probs and possibly the remaining two arguments.

I noticed this because structural zeroes in the matrix that I passed to the transition_probs argument were not preserved when n_states was also declared.

The solution, obviously, is to exclude n_states from the function call but it took me a long while to realise this as, based on the help file, I assumed it was ignored!

This same is true for build_mhmm.

Whether you correct the help file or the behaviour of build_hmm and fit_model is up to you, but currently they are not consistent.

Otherwise thanks for a great package - very helpful.

An example:

library("seqHMM")

data("mvad", package = "TraMineR")
mvad_alphabet <- c("employment", "FE", "HE", "joblessness", "school", "training")
mvad_labels <- c("employment", "further education", "higher education", "joblessness", "school", "training")
mvad_scodes <- c("EM", "FE", "HE", "JL", "SC", "TR")
mvad_seq <- seqdef(mvad, 17:86, alphabet = mvad_alphabet, states = mvad_scodes, labels = mvad_labels, xtstep = 6)

init_hmm_mvad1 <- build_hmm(observations = mvad_seq, n_states = 4)

emiss <- matrix(NA, nrow = 4, ncol = 6)
emiss[1,] <- seqstatf(mvad_seq[, 1:12])[, 2] + 1
emiss[2,] <- seqstatf(mvad_seq[, 13:24])[, 2] + 1
emiss[3,] <- seqstatf(mvad_seq[, 25:48])[, 2] + 1
emiss[4,] <- seqstatf(mvad_seq[, 49:70])[, 2] + 1
emiss <- emiss / rowSums(emiss)

# transition matrix with structural zeroes
tr <- matrix(
  c(0.80, 0.20, 0.00, 0.00,
    0.00, 0.80, 0.20, 0.00,
    0.00, 0.00, 0.80, 0.20,
    0.20, 0.00, 0.00, 0.80),
    nrow=4, ncol=4, byrow=TRUE)

init <- c(0.3, 0.3, 0.2, 0.2)

set.seed(123)

init_hmm1 <- build_hmm(
  observations = mvad_seq, 
  n_states = 4,
  transition_probs = tr,
  emission_probs = emiss,
  initial_probs = init
)

init_hmm2 <- build_hmm(
  observations = mvad_seq, 
  #n_states = 4,
  transition_probs = tr,
  emission_probs = emiss,
  initial_probs = init
)

model_hmm1 <- fit_model(init_hmm1)
model_hmm2 <- fit_model(init_hmm2)

# zeroes not preserved
model_hmm1$model$transition_probs
#>         to
#>from      State 1 State 2 State 3 State 4
#>  State 1  0.4239  0.5753  0.0008  0.0000
#>  State 2  0.8241  0.0642  0.0277  0.0840
#>  State 3  0.0091  0.0008  0.9699  0.0202
#>  State 4  0.0083  0.0000  0.0048  0.9870

attr(model_hmm1$model, "df")
#> 35



# zeroes preserved
model_hmm2$model$transition_probs 
#>         to
#>from      State 1 State 2 State 3 State 4
#>  State 1  0.9467  0.0533  0.0000  0.0000
#>  State 2  0.0000  0.9192  0.0808  0.0000
#>  State 3  0.0000  0.0000  0.9024  0.0976
#>  State 4  0.0066  0.0000  0.0000  0.9934

attr(model_hmm2$model, "df")
#> 27

How to compare MHMMs with different number of parameters?

Hi,

I am aware of the use of BIC for comparing single HMMs with different number of parameters. How does this apply for the use of MHMMs? I mean, how can I compare models with different number of clusters, and different number of states in the HMM of each cluster?

Best!

Seed-dependent model fitting success for LCM

Hi,
I am trying to cluster model-generated time series with seqHMM. I am importing a dataframe of continuous time series, discretizing it, and then running build_lcm and fit_model to get the clusters. For some random seeds, the fitting is successful, for others, it generates (a variety of) error messages. Log likelihood test indicates model is functional (and indeed for some seeds it runs fine). If you could review this and consider the attached files (R script and data file), I would be very grateful for any insight how to resolve this.

LCM_seed_steipatr.zip

specifying emission matrix

The build_hmm and build_mhmm functions ask me to specify emission and transition probabilities.
It has the nice feature, that values specified to 0 persist in the estimation as 0.
In order to deal with identifiability issues, I would additionally like to fix some values in the emission matrix to be equal.
As example, I would like to force emiss[1,1] = emiss[2,2] and emiss[1,4] = emiss[2,4]
emiss <- matrix(
c(0.6, 0, 0, 0.4,
0, 0.6, 0, 0.4,
0, 0, 1, 0),
nrow = 3, ncol = 4, byrow = TRUE)

Is there any trick how I could achieve this within build_hmm and build_mhmm?
Due to identifiability issues, it would be very important to be able to do this.
Best wishes, Ulrike

plot.mhmm uses global variables instead of variables passed into function if interactive = TRUE

While using the CRAN version of this package, if a call to the function plot.mhmm happens with interactive = TRUE is wrapped inside of a function, plot.mhmm looks for global variables for its input instead of the local variables in the function scope. Trying to use variables in the function scope results in the following error:

For example, running:

obs <- seqdef(sapply(1:10, function(i){
  rbinom(10, 2, prob=c(.5, .5))
}))


plot_wrapper <- function(wrapped_model) { 
  plot(wrapped_model, interactive = FALSE)
}          

plot_wrapper(fit_model(build_mmm(obs, n_clusters = 2))$model)

causes the following error:

9.
(function (x, which.plots = NULL, nrow = NA, ncol = NA, byrow = FALSE, 
    row.prop = "auto", col.prop = "auto", layout = "horizontal", 
    pie = TRUE, vertex.size = 40, vertex.label = "initial.probs", 
    vertex.label.dist = "auto", vertex.label.pos = "bottom",  ... 
8.
do.call(mHMMplotgrid, args) 
7.
plot.mhmm(wrapped_model, interactive = FALSE) 
6.
plot(wrapped_model, interactive = FALSE) at .active-rstudio-document#7
5.
plot_wrapper(fit_model(build_mmm(obs, n_clusters = 2))$model) at .active-rstudio-document#10
4.
eval(ei, envir) 
3.
eval(ei, envir) 
2.
withVisible(eval(ei, envir)) 
1.
source("~/.active-rstudio-document", echo = TRUE) 

However if we assign the input of the function to be a global variable, like this instead:

obs <- seqdef(sapply(1:10, function(i){
  rbinom(10, 2, prob=c(.5, .5))
}))


plot_wrapper <- function(wrapped_model) {
  temp <<- wrapped_model
  plot(temp, interactive = FALSE)
}          

plot_wrapper(fit_model(build_mmm(obs, n_clusters = 2))$model)

We get no error.

Obtaining the observations within each cluster for MHMM

I am using Mixture of Hidden Markov Model (MHMM) to cluster my data. To do so, I used Package "seqHMM" in R. My question is whether it is possible to obtain the actual observations within each cluster.For example, after my analysis, I have 3 clusters, and I want to find the exact observations within each cluster, is it possible?

Example:

At first, I created three HMMs with transition probabilities initial probabilities sc_init1, sc_init2, sc_init3, and sc_trans1, sc_trans2, sc_trans3, and finally with emission probabilities sc_emiss1, sc_emiss2, sc_emiss3 respectively. Then I combined them into MHMM with three clusters as the following:

mhmm_init <- list(sc_init1, sc_init2, sc_init3)

mhmm_trans <- list(sc_trans1, sc_trans2, sc_trans3)

mhmm_emiss <- list(sc_emiss1,sc_emiss2, sc_emiss3)

mhmm<- build_mhmm(observations=seq, transition_probs=mhmm_trans, emission_probs=mhmm_emission, initial_probs=mhmm_initial, cluster_names = c("Cluster 1", "Cluster 2", "Cluster 3”))
My data, seq, is longitudinal data. Now that the model is constructed, I estimated model parameters with the fit_model function as the following

set.seed(1011) #1011

mhmm_fit <- fit_model(mhmm, local_step = TRUE, threads = 1,
control_em = list(restart = list(times =10)))

mhmm_final <- mhmm_fit$model

By using mhmm_final, I can get several information about each of my three clusters such as transition probabilities, initial probabilities and emission probabilities. For example, if I want to get these estimations for cluster 1 I can easily get them with the following code:

mhmm_final$transition_probs$Cluster 1

mhmm_final$emission_probs$Cluster 1

mhmm_final$initial_probs$Cluster 1

My question is that how I can get observations in each cluster. There is a code available for observations as mhmm_final$observations but this line of code gives me all the observations in all three clusters. I want to find the exact observations within each cluster, in this case Cluster 1.

Let’s assume that I have 10 sequences (seq 1, seq 2, seq 3, seq 4, seq 5, seq 6, seq 7, seq 8, seq 9, seq 10), and I clustered them into three groups with this approach. I want to know that each of these sequences belongs to which cluster.

[Help request] How to test the model?

I've used seqHMM to create a Markov Model from some sequence data using build_mm(). How do I then pass test data to it? Or get a list of transition probabilities for each state so I can do look ups? e.g if state1 then state2 is 0.4.

Is there a predict function in R, or something I'm missing in the library?

Thanks!

plot.hmm: Is it possible to modify the arrow size based on the transition probability?

Hi,

I am plotting a HMM that looks like this:

image

As you can see, the arrow of the edge with the highest probability cannot be seen clearly, because of the width of the edge. I am setting edge.arrow.size to 1. I can fix this by setting edge.arrow.size to a higher value, but then the rest of the arrows are too big:

image

I would like to modify the arrow size in terms of the transition probability, something like edge.arrow.size = "auto". Is that possible?

Thanks!!

general question about subsequence extrac. with hmm

So nice that seqHMM can analyse sequences over multiple participants.

But as I'm new to hmm in general, wish to ask a silly question:
With the original sequence matrix and the hidden state matrix both available, how can I extract the most probable subsequences per state.

Any references would be very welcomed, thanks!

not able to install the package

Hi there,

I am trying to install the package on my macbook via Rstudio. Following the command, I got errors as following:

ld: warning: directory not found for option '-L/usr/local/lib/gcc/x86_64-apple-darwin13.0.0/4.8.2'
ld: library not found for -lgfortran
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [seqHMM.so] Error 1
ERROR: compilation failed for package ‘seqHMM’

  • removing ‘/Library/Frameworks/R.framework/Versions/3.2/Resources/library/seqHMM’
    Error: Command failed (1)

Any suggestion how I can solve it?

How to fix a model with warning: in fit_model, backward probabilities contain non-finite values,NLOPT_FAILURE

I have a single channel sequence. I built an HMM based on assumed 4 states, using arbitrary initial values, transition probabilities, emission probabilities. I fit the model using global_step=TRUE, local_step=TRUE.
The EM algorithm failed. The local optimization terminated. Is there a way to adjust the parameters to avoid the warning? Note that I was able to plot and print the hmm (maybe incorrect because of the warning?? )
Thanks for your help

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.