# Autocorrelation for a categorical time series

I am having trouble to compute the autocorrelation for different lags in a categorical time series. For instance, consider 3 possible categories: classA, classB and classC, and a vector x representing the series:

x <- c('classA', 'classB', 'classA', 'classC', 'classC', ...)


R contains the beautiful acf function, that computes the autocorrelation for different lags in a vector. Nevertheless the acf computes the Pearson correlation, which is (as far as I know) only suitable for continuous variables.

My first idea is to use the Cramer's V measure (available here) to compute the autocorrelation using something like cv.test(x, lag(x,1)), where the lag(x,1) function lags the x vector by 1, and then repeat the process for different lags. The problem is that I am not sure if this is the best approach, or even if this idea is correct.

Could you folks give-me some guidance about how to compute this autocorrelation?

Thanks

• You might be interested in this text books.google.com.eg/books/about/… Nov 27, 2019 at 22:19
• Thanks @AhmedAli Nov 29, 2019 at 17:18

After reading the book (An Introduction to Discrete-Valued Time Series) suggested by @Ahmed Ali in the comments, I believe that the Cramer's V is the best answer to the problem.

For those who are interested in a R function to compute the autocorrelation using Cramer's V, here follow an adaptation of the R code that i found in the book.

data <- series#load your categorical series here. e.g. scan(file.choose(), what="character")
Tlen <- length(data)

states <- unique(data)
nostates <- length(states)
datanum <- match(data, states)

#Binarization
bincodes <- diag(1,nostates)
databin <- bincodes[datanum,]

#relative frequencies
hatpi <- colMeans(databin)

maxlag <- 100
hatbivprob <- array(0,c(nostates,nostates,maxlag))

for(k in c(1:maxlag)){#for each lag
for(i in c(1:nostates)){#for each lagged vector representing a category
for(j in c(1:nostates)){#for each vector representing a category
hatbivprob[i,j,k] <- mean(databin[(k+1):Tlen,i]*databin[1:(Tlen-k),j])
}
}
}
#compare
indprob <- hatpi %*% t(hatpi)

#Cramer's v
cramer <- rep(0,maxlag)
for(k in c(1:maxlag)){
cramer[k] <- sqrt(sum((hatbivprob[,,k]-indprob)^2/indprob)/(nostates-1))
}
plot(cramer, type="h", xlab = "k", ylab = "Cramer's   v(k)", lwd=4, ylim=c(-1,1))


The ACF plot is indeed meaningless for categorical time series (CTS) since Pearson's correlation coefficient is designed for continuous variables.

When dealing with CTS, the concept of autocorrelation is replaced by serial dependence. Different measures can be used to calculate it: Cramer's V, as well as Goodman and Kruskal’s tau, are measures for unsigned serial dependence, while Cohen's kappa accounts for signed serial dependence.

Reference: Weiß, Christian H. An Introduction to Discrete-Valued Time Series. John Wiley & Sons, 2018

• I would cast this as a Markov state transition model, e.g. this. Sep 16, 2023 at 20:33
• "Pearson's coefficient assumptions apply to continuous variables that are normally distributed" has been frequently stated on poor Web sites, but when understood as only applicable in those cases -- which appears to be your intention -- it flat out isn't true.
– whuber
Sep 16, 2023 at 22:01
• @whuber Hi there! Skipping the irrelevant poor part of your comment, please elaborate a bit more so maybe I can modify my answer if it is inaccurate. Pearson corr coef is meaningful for continuous r.vs. So I assume that you object with the normally distributed part of the answer. I believe you are right if this is the case. I am modifying my answer because it is indeed misleading, it is like I say that Pearson correlation coefficient can be applied only in data with Gaussian characteristics. If this is not what you meant, looking forward for your answer. Sep 18, 2023 at 7:50
• Vicky, The reason for characterizing such sites as "poor" is to head off possible objections that refer to such Web sites as authorities. We have a popular thread on the topic at stats.stackexchange.com/questions/3730/….
– whuber
Sep 18, 2023 at 14:27