# Hierarchy of states in TraMineR

I am considering using TraMineR to analyze my data. However, before I do, I would like to know if it can handle hierarchical states, and I could not find the answer to this question in the documentation or online.

By "hierarchical states", I mean the following. My data describe professional trajectories through the sequence of held positions. I have some sort of ontology defined, for instance the state "working in news industry" subsumes "journalist" and "tv anchor". I would like this information to be taken into account when comparing sequences. If two persons are journalists during the same period, their sequences are considered as more similar than if they just work in the news industry, but the latter is still better than not working in the same sector at all.

Can I do this with TraMineR? If it is the case, can someone please point me towards the relevant functions?

TraMineR has no automatic way to handle such hierarchy of states.

However, most dissimilarity measures provided by TraMineR can account for differences in state dissimilarities. For example, the optimal matching (OM) method can account for user provided substitution costs. So, by setting a lower cost for a substitution between 'journalist' and 'tv anchor' than between any other professions or between 'journalist' and any other profession you should get the expected result.

I illustrate with a small example with the 4 states JL journalist, TV TV anchor, ED eductaion, and BK bank. I define a cost matrix costs with a substitution cost of 1 between JL and TV, and of 2 for all other substitution. The indel cost is set as half the maximum substitution cost.

library(TraMineR)
alphabet <- c("TV","JL","BK","ED")
costs <- matrix(c(0, 1, 2, 2,
1, 0, 2, 2,
2, 2, 0, 2,
2, 2, 2, 0), nrow=4, byrow=TRUE)

sdat <- matrix( c("ED","ED","BK","BK","JL",
"ED","ED","BK","BK","TV",
"ED","ED","BK","BK","BK",
"ED","BK","BK","JL","JL",
"JL","JL","JL","JL","JL",
"TV","TV","TV","TV","TV"
), nrow=6, byrow=TRUE)
(seq <- seqdef(sdat, alphabet=alphabet))

#   Sequence
# 1 ED-ED-BK-BK-JL
# 2 ED-ED-BK-BK-TV
# 3 ED-ED-BK-BK-BK
# 4 ED-BK-BK-JL-JL
# 5 JL-JL-JL-JL-JL
# 6 TV-TV-TV-TV-TV

seqdist(seq, method="OM", sm=costs, indel=.5*max(costs))

#      [,1] [,2] [,3] [,4] [,5] [,6]
# [1,]    0    1    2    2    8    9
# [2,]    1    0    2    3    9    8
# [3,]    2    2    0    4   10   10
# [4,]    2    3    4    0    6    8
# [5,]    8    9   10    6    0    5
# [6,]    9    8   10    8    5    0


We see that sequence 1 is closer from seq 2 than from seq 3. Likewise, although seq 3 and 5 do not share any common state with seq 6, the latter is closer from seq 5 than from seq 3.

• Defining such a custom distance function was indeed my backup plan in case there was no straightforward solution... but I hadn't seriously thought about how to do so, concretely. Now I can use your code pretty much directly, so thanks Gilbert! Nov 27, 2018 at 21:10