Code
library(dplyr)
library(tidyr)
library(knitr)
Ce qui suit est un premier draft réalisé en 2023, encore très perfectible, mais j’avais conscience depuis plusieurs années que l’absence d’une partie dédiée aux manipulations des données biographiques était problématique. Ce qui suit ne peut pas couvrir toutes les problématiques que l’on peut rencontrer lorsqu’on met en forme ce type de données: le format de mise à disposition, l’extraction de l’information pertinente correspondant à la question analysée, voire le logiciel utilisé, rend quasiment impossible la production d’un guide clé en main.
Quelques conseils
Le format des données utilisé dans cette section
Programmation
Le service SMS de l’Ined a mis en place récemment un support de programmation dédié principalement à R [Lien]. Ce support est construit sur l’idée d’une liste de fiches thématiques qui trouvent leur origine, mais pas que, dans des demandes d’assistances de programmation de la part de chercheur.e.s, (post)doctorant.e.s ou stagiaires. Bien que cela ne soit pas pour tout de suite, il est prévu d’alimenter ce support sur la question des manipulations des données biographiques avec: d’autres exemples, des alternatives aux codes proposés plus loin, ou l’application des exemples traités ici mais avec un format de mise à disposition des données différents.
… Ceci au mieux pour l’été 2024.
Packages utilisés:
library(dplyr)
library(tidyr)
library(knitr)
On partira de la base individus-séquences suivante:
= data.frame(id = c(1, 1, 1, 2),
df deb = c(2020, 2023, 2024, 2022),
fin = c(2021, 2024, 2025, NA),
x = c(1,2,1,2))
kable(df)
id | deb | fin | x |
---|---|---|---|
1 | 2020 | 2021 | 1 |
1 | 2023 | 2024 | 2 |
1 | 2024 | 2025 | 1 |
2 | 2022 | NA | 2 |
On supposera que l’année de collecte, pour toutes les observations, est 2025 2.
Si cela n’est pas donné dans le module biographique, il peut être intéressant de construire les numéros de séquences des trajectoires.
$nseq = 1
df= df %>% group_by(id) %>% mutate(nseq = cumsum(nseq))
df
kable(df)
id | deb | fin | x | nseq |
---|---|---|---|---|
1 | 2020 | 2021 | 1 | 1 |
1 | 2023 | 2024 | 2 | 2 |
1 | 2024 | 2025 | 1 | 3 |
2 | 2022 | NA | 2 | 1 |
Exemple 1 : durée de séjour de la première séquence observée
Supposons que x traduit un type de relation/union, par exemple x=1 est une relation non cohabitante et x=2 est une relation cohabitante. On s’intéresse à la durée de la première relation, sans distinction entre 1 et 2. Il suffit de séléctionner la première séquence.
= filter(df, nseq==1) df
La variable de fin va permettre de repérer les informations censurées, et de générer la variable d’évènement. A ce niveau il est donc important de ne pas encore remplacer la date de censure par sa valeur.
$e = ifelse(is.na(df$fin), 0,1)
df
kable(df)
id | deb | fin | x | nseq | e |
---|---|---|---|---|---|
1 | 2020 | 2021 | 1 | 1 | 1 |
2 | 2022 | NA | 2 | 1 | 0 |
Pour la variable de durée 3, une repéré les observations censurées, elle est calculée directement avec les variables fin et deb.
$dur = ifelse(df$e==1, df$fin - df$deb + 1, 2025 - df$deb + 1)
df
kable(df)
id | deb | fin | x | nseq | e | dur |
---|---|---|---|---|---|---|
1 | 2020 | 2021 | 1 | 1 | 1 | 2 |
2 | 2022 | NA | 2 | 1 | 0 | 4 |
Exemple 2 : changement de métrique temporelle
Toujours avec le même exemple, mais en ajoutant une observation, supposons que l’on dispose également de l’information sur les mois. Sur les mois où l’évènement à eu lieu, mais également sur les mois où l’enquête a été réalisée.
= data.frame(id = c(1, 1, 1, 2,3),
df2 deb = c(2020, 2023, 2024, 2022, 2021),
debm = c(2,5,3,10,9),
fin = c(2021, 2024, 2025, NA,2021),
finm = c(4,2,12,NA,11),
x = c(1,2,1,2,1),
enq = c(2025,2025,2025,2025,2025),
enqm = c(4,4,4,5,4))
$nseq = 1
df2= df2 %>% group_by(id) %>% mutate(nseq = cumsum(nseq))
df2
kable(df2)
id | deb | debm | fin | finm | x | enq | enqm | nseq |
---|---|---|---|---|---|---|---|---|
1 | 2020 | 2 | 2021 | 4 | 1 | 2025 | 4 | 1 |
1 | 2023 | 5 | 2024 | 2 | 2 | 2025 | 4 | 2 |
1 | 2024 | 3 | 2025 | 12 | 1 | 2025 | 4 | 3 |
2 | 2022 | 10 | NA | NA | 2 | 2025 | 5 | 1 |
3 | 2021 | 9 | 2021 | 11 | 1 | 2025 | 4 | 1 |
On remarque que la nouvelle observation (id=3) a connu l’évènement, ici la fin de la relation, la même année qu’au début d’exposition (le début de la relation)…. mais au bout de 2,6,11 mois???? Commeon dispose de l’information sur les mois de début et de fin cela peut être intéressant de l’exploite. De la même manière si l’enquête a été réalisée la même année, les entretiens n’ont pas eu lieu le même mois. On aura besoin de cette information pour les observations censurées.
De nouveau on sélectionne la première séquence, et pour la lisibilité de la base on retire les informations qui ne seront pas ou plus exploitées (nseq, x).
= filter(df2,nseq==1)
df2 = select(df2, -c(x,nseq))
df2
kable(df2)
id | deb | debm | fin | finm | enq | enqm |
---|---|---|---|---|---|---|
1 | 2020 | 2 | 2021 | 4 | 2025 | 4 |
2 | 2022 | 10 | NA | NA | 2025 | 5 |
3 | 2021 | 9 | 2021 | 11 | 2025 | 4 |
On génère la variable censure/évènement (toujours à faire avant la variable de durée) de la même manière que pour l’exemple 1.
$e = ifelse(is.na(df2$fin), 0, 1)
df2
kable(df2)
id | deb | debm | fin | finm | enq | enqm | e |
---|---|---|---|---|---|---|---|
1 | 2020 | 2 | 2021 | 4 | 2025 | 4 | 1 |
2 | 2022 | 10 | NA | NA | 2025 | 5 | 0 |
3 | 2021 | 9 | 2021 | 11 | 2025 | 4 | 1 |
Pour la variable de durée, le principe est de multiplié par 12 la différence entre l’année de fin et l’année de début et d’ajouter la différence entre le mois de fin et le mois de début.
Pour les observations censurées, ici l’année de fin est identique mais les mois varient. En terme de programmation, surtout si avec R on utilise ifelse
, il est préférable d’y aller doucement en créant une durée pour les observations qui ont connu l’évènement et une durée pour les observations censurées. Puis de regrouper les deux cas. C’est ce qui est fait dans le code qui suit.
Durée selon les valeurs de e:
$dur1 = ifelse(df2$e==1, 12*(df2$fin - df2$deb) + (df2$finm - df2$debm), 0)
df2$dur0 = ifelse(df2$e==0, 12*(2025 - df2$deb) + (df2$enqm - df2$debm), 0)
df2
kable(df2)
id | deb | debm | fin | finm | enq | enqm | e | dur1 | dur0 |
---|---|---|---|---|---|---|---|---|---|
1 | 2020 | 2 | 2021 | 4 | 2025 | 4 | 1 | 14 | 0 |
2 | 2022 | 10 | NA | NA | 2025 | 5 | 0 | 0 | 31 |
3 | 2021 | 9 | 2021 | 11 | 2025 | 4 | 1 | 2 | 0 |
On regroupe par simple sommation (le else étant 0).
$dur = df2$dur1 + df2$dur0
df2
= select(df2, -c(dur1,dur0))
df2
kable(df2)
id | deb | debm | fin | finm | enq | enqm | e | dur |
---|---|---|---|---|---|---|---|---|
1 | 2020 | 2 | 2021 | 4 | 2025 | 4 | 1 | 14 |
2 | 2022 | 10 | NA | NA | 2025 | 5 | 0 | 31 |
3 | 2021 | 9 | 2021 | 11 | 2025 | 4 | 1 | 2 |
On dispose ainsi des éléments nécessaire pour faire une analyse de durée avec une métrique mensuelle 4.
Exemple 3 : importation d’un début d’expositon externe
On repart de la première base
id | deb | fin | x | nseq |
---|---|---|---|---|
1 | 2020 | 2021 | 1 | 1 |
1 | 2023 | 2024 | 2 | 2 |
1 | 2024 | 2025 | 1 | 3 |
2 | 2022 | NA | 2 | 1 |
On suppose maintenant que x traduit des situations sur le marché du travail. Par exemple x=1 est un emploi en CDD et x=2 un emploi en CDI. On s’intéresse à la durée entre la fin des études et le premier emploi, quel que soit sont type.
= data.frame(id = c(1,2,3), fin_etude = c(2020,2021,2023))
etude kable(etude)
id | fin_etude |
---|---|
1 | 2020 |
2 | 2021 |
3 | 2023 |
Une nouvelle observation (id=3) apparaît. Au moment de l’enquête, elle n’a pas (encore) trouvé un emploi depuis la fin de ces études. On a donc une observation qui sera censurée.
Certaines bases biographiques peuvent être structurées avec des trajectoires strictement continue, l’année (l’âge) de fin étant l’année (l’âge) de début de la trajectoire suivante. Dans ce cas, l’information serait immédiatement disponible, avec la présence d’un nombre de séquences plus important dans la base.
On va devoir:
Avant la fusion, on peut conserver seulement les informations nécessaires (id, deb). La variable deb va changer également de statut en devenant l’année de fin de la période de recherche d’emploi.
= filter(df, nseq==1)
df = select(df, -c(fin,x,nseq))
df
= rename(df, fin = deb)
df kable(df)
id | fin |
---|---|
1 | 2020 |
2 | 2022 |
Après la fusion:
= full_join(etude, df, by = c('id'))
df
= rename(df, deb = fin_etude)
df
kable(df)
id | deb | fin |
---|---|---|
1 | 2020 | 2020 |
2 | 2021 | 2022 |
3 | 2023 | NA |
On a toutes les informations pour générer la variable censure/évènement et la variable de durée:
$e = ifelse(is.na(df$fin),0,1)
df
$dur = ifelse(df$e, df$fin - df$deb + 1, 2025 - df$deb + 1)
dfkable(df)
id | deb | fin | e | dur |
---|---|---|---|---|
1 | 2020 | 2020 | 1 | 1 |
2 | 2021 | 2022 | 1 | 2 |
3 | 2023 | NA | 0 | 3 |
On repart de la première base, avec les numéros de séquence.
id | deb | fin | x | nseq |
---|---|---|---|---|
1 | 2020 | 2021 | 1 | 1 |
1 | 2023 | 2024 | 2 | 2 |
1 | 2024 | 2025 | 1 | 3 |
2 | 2022 | NA | 2 | 1 |
Pour apparier des informations de plusieurs modules biographiques, on doit transformer les bases en format individus-séquences en format individus-périodes (ici individus années).
Pourquoi ne pas utiliser la simple différence entre la fin et le début ?
Durée (fin - début) et allongement de la base:
On ne génère pas des variables d’analyse, on aurait besoin de l’information sur l’année de l’enquête pour les informations censurées.
$fin[is.na(df$fin)] = 2025
df
kable(df)
id | deb | fin | x | nseq |
---|---|---|---|---|
1 | 2020 | 2021 | 1 | 1 |
1 | 2023 | 2024 | 2 | 2 |
1 | 2024 | 2025 | 1 | 3 |
2 | 2022 | 2025 | 2 | 1 |
Allongement de la base:
= df
df1 $dur1 = df1$fin - df1$deb
df1
$dur1b = df1$dur1 # uncount supprime la variable d'origine
df1= uncount(df1,dur1b)
df1
kable(df1)
id | deb | fin | x | nseq | dur1 |
---|---|---|---|---|---|
1 | 2020 | 2021 | 1 | 1 | 1 |
1 | 2023 | 2024 | 2 | 2 | 1 |
1 | 2024 | 2025 | 1 | 3 | 1 |
2 | 2022 | 2025 | 2 | 1 | 3 |
2 | 2022 | 2025 | 2 | 1 | 3 |
2 | 2022 | 2025 | 2 | 1 | 3 |
Pour générer la variable période (année), on a besoin d’un compteur qui sera associé à la variable deb. On doit bien contrôler l’opération par identifiant et numéro de séquence.
$c = 1
df1= df1 %>% group_by(id,nseq) %>% mutate(year = deb + cumsum(c))
df1
kable(df1)
id | deb | fin | x | nseq | dur1 | c | year |
---|---|---|---|---|---|---|---|
1 | 2020 | 2021 | 1 | 1 | 1 | 1 | 2021 |
1 | 2023 | 2024 | 2 | 2 | 1 | 1 | 2024 |
1 | 2024 | 2025 | 1 | 3 | 1 | 1 | 2025 |
2 | 2022 | 2025 | 2 | 1 | 3 | 1 | 2023 |
2 | 2022 | 2025 | 2 | 1 | 3 | 1 | 2024 |
2 | 2022 | 2025 | 2 | 1 | 3 | 1 | 2025 |
Problème: les années de début ne sont pas correncte: 2021 au lieu de 2020 pour la première séquence de id=1 par exemple.
On doit donc impérativement augmenter la différence entre la fin et le début par +1 pour que l’ensemble des périodes (années) soit couvertes.
On reprend donc les opérations précédentes mais avec durée = fin - debut + 1
= df
df2 $dur2 = df2$fin - df2$deb + 1
df2
$dur2b = df2$dur2 # uncount supprime la variable d'origine
df2= uncount(df2,dur2b)
df2
kable(df2)
id | deb | fin | x | nseq | dur2 |
---|---|---|---|---|---|
1 | 2020 | 2021 | 1 | 1 | 2 |
1 | 2020 | 2021 | 1 | 1 | 2 |
1 | 2023 | 2024 | 2 | 2 | 2 |
1 | 2023 | 2024 | 2 | 2 | 2 |
1 | 2024 | 2025 | 1 | 3 | 2 |
1 | 2024 | 2025 | 1 | 3 | 2 |
2 | 2022 | 2025 | 2 | 1 | 4 |
2 | 2022 | 2025 | 2 | 1 | 4 |
2 | 2022 | 2025 | 2 | 1 | 4 |
2 | 2022 | 2025 | 2 | 1 | 4 |
$c = 1
df2= df2 %>% group_by(id,nseq) %>% mutate(year = deb + cumsum(c) - 1)
df2
= select(df2, -c(deb,fin,dur2))
df2
kable(df2)
id | x | nseq | c | year |
---|---|---|---|---|
1 | 1 | 1 | 1 | 2020 |
1 | 1 | 1 | 1 | 2021 |
1 | 2 | 2 | 1 | 2023 |
1 | 2 | 2 | 1 | 2024 |
1 | 1 | 3 | 1 | 2024 |
1 | 1 | 3 | 1 | 2025 |
2 | 2 | 1 | 1 | 2022 |
2 | 2 | 1 | 1 | 2023 |
2 | 2 | 1 | 1 | 2024 |
2 | 2 | 1 | 1 | 2025 |
Les années sont toutes couvertes….mais un peu trop. En effet, lorsque les trajectoires sont continues soit lorsque l’année de fin d’une séquence est identique à l’année de début de la suivante, les années vont être doublonnées. On doit dont supprimer ce doublon.
De nouveaux on doit faire un choix, soit on priviligie l’année de fin, soit on privilégie l’année de début. Les applications ont des fonctions qui permettent de supprimer les doublons5. On peut le faire manuellement en regardant pour chaque personnes-années le nombre de doublon. Cela se fait facilement à l’aide d’un compteur, ici la variable nyear.
= df2 %>% group_by(id,year) %>% mutate(nyear = cumsum(c))
df2
kable(df2)
id | x | nseq | c | year | nyear |
---|---|---|---|---|---|
1 | 1 | 1 | 1 | 2020 | 1 |
1 | 1 | 1 | 1 | 2021 | 1 |
1 | 2 | 2 | 1 | 2023 | 1 |
1 | 2 | 2 | 1 | 2024 | 1 |
1 | 1 | 3 | 1 | 2024 | 2 |
1 | 1 | 3 | 1 | 2025 | 1 |
2 | 2 | 1 | 1 | 2022 | 1 |
2 | 2 | 1 | 1 | 2023 | 1 |
2 | 2 | 1 | 1 | 2024 | 1 |
2 | 2 | 1 | 1 | 2025 | 1 |
Si on souhaite garder l’année de fin on filtre les observations en conservant celles dont nyear=1. Si on souhaite privilégier les années de début on foltre les observations en conservant celles dont nyear=2. Si on souhaite conserver les années de fin de séquence:
= filter(df2, nyear==1)
df2
= select(df2, -c(nseq,c,nyear))
df2
kable(df2)
id | x | year |
---|---|---|
1 | 1 | 2020 |
1 | 1 | 2021 |
1 | 2 | 2023 |
1 | 2 | 2024 |
1 | 1 | 2025 |
2 | 2 | 2022 |
2 | 2 | 2023 |
2 | 2 | 2024 |
2 | 2 | 2025 |
Pour commencer par un exemple plutôt simple, on note que pour id=1 l’année 2022 n’est pas renseignée (trajectoire non continue). Si on reprend un exemple précédent (relations de couple), cette année pourrait être identifiée comme une période sans relation. Une façon simple de boucher ce type “trous”, est d’utiliser les années de naissances des individus, et de créer une base individus-périodes qui couvre toutes les années de vie de l’individu jusqu’à l’enquête. On remontera jusque là, mais on va par exemple considérer que pour id=1 et id=2 ce début de tout est en 2018.
= data.frame(id = c(1, 2),
dftout t0 = c(2018, 2018))
kable(dftout)
id | t0 |
---|---|
1 | 2018 |
2 | 2018 |
$tmax = 2025
dftout
$dur = dftout$tmax - dftout$t0 + 1
dftout
= uncount(dftout,dur)
dftout
$c = 1
dftout= dftout %>% group_by(id) %>% mutate(year = t0 + cumsum(c) - 1)
dftout
= select(dftout, -c(t0,tmax,c))
dftout
kable(dftout)
id | year |
---|---|
1 | 2018 |
1 | 2019 |
1 | 2020 |
1 | 2021 |
1 | 2022 |
1 | 2023 |
1 | 2024 |
1 | 2025 |
2 | 2018 |
2 | 2019 |
2 | 2020 |
2 | 2021 |
2 | 2022 |
2 | 2023 |
2 | 2024 |
2 | 2025 |
On peut maintenant apparier cette couverture de toutes les années de vie jusqu’à l’enquête à la base biographique:
= full_join(df2, dftout, by = c("id","year"))
df2
= arrange(df2, id, year)
df2 kable(df2)
id | x | year |
---|---|---|
1 | NA | 2018 |
1 | NA | 2019 |
1 | 1 | 2020 |
1 | 1 | 2021 |
1 | NA | 2022 |
1 | 2 | 2023 |
1 | 2 | 2024 |
1 | 1 | 2025 |
2 | NA | 2018 |
2 | NA | 2019 |
2 | NA | 2020 |
2 | NA | 2021 |
2 | 2 | 2022 |
2 | 2 | 2023 |
2 | 2 | 2024 |
2 | 2 | 2025 |
Pour supprimer les informations qui précèdent la première séquence de la biographie, on peut générer un compteur sur la variable x après avoir remplacer ses valeurs manquantes par des 0. On gardera les lignes pour lesquels ce compteur est supérieur à 1.
$x[is.na(df2$x)] = 0
df2
= df2 %>% group_by(id) %>% mutate(nx = cumsum(x))
df2
kable(df2)
id | x | year | nx |
---|---|---|---|
1 | 0 | 2018 | 0 |
1 | 0 | 2019 | 0 |
1 | 1 | 2020 | 1 |
1 | 1 | 2021 | 2 |
1 | 0 | 2022 | 2 |
1 | 2 | 2023 | 4 |
1 | 2 | 2024 | 6 |
1 | 1 | 2025 | 7 |
2 | 0 | 2018 | 0 |
2 | 0 | 2019 | 0 |
2 | 0 | 2020 | 0 |
2 | 0 | 2021 | 0 |
2 | 2 | 2022 | 2 |
2 | 2 | 2023 | 4 |
2 | 2 | 2024 | 6 |
2 | 2 | 2025 | 8 |
On supprime les lignes lorsque nx=0.
= filter(df2, nx>0)
df2
= select(df2, -c(nx))
df2
kable(df2)
id | x | year |
---|---|---|
1 | 1 | 2020 |
1 | 1 | 2021 |
1 | 0 | 2022 |
1 | 2 | 2023 |
1 | 2 | 2024 |
1 | 1 | 2025 |
2 | 2 | 2022 |
2 | 2 | 2023 |
2 | 2 | 2024 |
2 | 2 | 2025 |
On peut être amené à fusionner plusieurs modules biographique. Jusqu’à présent, une même année, tous les individus ne pouvaient être que dans une situation, par exemple une seul emploi, un seul lieu de résidence etc… Pour certains phénomènes, une même années ou pendant une période plus longue on peut observer simultanément plusieurs états différent, ou plus classiquement observer une somme d’un même état. On parle ici d’overlapping. Ce type de situation est typiquement celle qu’on observe avec le nombre d’enfants.
Supposons que le base ci-dessous traduit la naissance et potentiellement le décès des enfants.
= data.frame(id = c(1, 2, 2),
dfy deb = c(2022, 2019, 2023),
fin = c(NA, 2024,NA),
nseq = c(1,1,2))
kable(dfy)
id | deb | fin | nseq |
---|---|---|---|
1 | 2022 | NA | 1 |
2 | 2019 | 2024 | 1 |
2 | 2023 | NA | 2 |
Si on reprend les manipulations précédentes jusqu’à la création de la variable year:
$fin[is.na(dfy$fin)] = 2025
dfy$dur = dfy$fin - dfy$deb + 1
dfy
$durb = dfy$dur # Uncount supprime la variable d'origine
dfy= uncount(dfy,durb)
dfy
$c = 1
dfy= dfy %>% group_by(id,nseq) %>% mutate(year = deb + cumsum(c) - 1) dfy
La variable year est bien renseignée 2 fois pour les années 2023 et 2024.
On peut s’intéresser au fait d’avoir ou non un enfant, ou de manière plus générale au nombre d’enfant. En créant cette information, on se donne également le moyen de corriger cet overlapping:
= dfy %>% group_by(id,year) %>% mutate(ny = cumsum(c))
dfy = dfy %>% group_by(id,year) %>% mutate(tot_y = sum(c))
dfy
kable(dfy)
id | deb | fin | nseq | dur | c | year | ny | tot_y |
---|---|---|---|---|---|---|---|---|
1 | 2022 | 2025 | 1 | 4 | 1 | 2022 | 1 | 1 |
1 | 2022 | 2025 | 1 | 4 | 1 | 2023 | 1 | 1 |
1 | 2022 | 2025 | 1 | 4 | 1 | 2024 | 1 | 1 |
1 | 2022 | 2025 | 1 | 4 | 1 | 2025 | 1 | 1 |
2 | 2019 | 2024 | 1 | 6 | 1 | 2019 | 1 | 1 |
2 | 2019 | 2024 | 1 | 6 | 1 | 2020 | 1 | 1 |
2 | 2019 | 2024 | 1 | 6 | 1 | 2021 | 1 | 1 |
2 | 2019 | 2024 | 1 | 6 | 1 | 2022 | 1 | 1 |
2 | 2019 | 2024 | 1 | 6 | 1 | 2023 | 1 | 2 |
2 | 2019 | 2024 | 1 | 6 | 1 | 2024 | 1 | 2 |
2 | 2023 | 2025 | 2 | 3 | 1 | 2023 | 2 | 2 |
2 | 2023 | 2025 | 2 | 3 | 1 | 2024 | 2 | 2 |
2 | 2023 | 2025 | 2 | 3 | 1 | 2025 | 1 | 1 |
Il ne reste plus qu’à supprimer les lignes où ny>1
= filter(dfy, ny==1)
dfy = select(dfy, -c(ny,deb, fin, dur, nseq, c))
dfy
kable(dfy)
id | year | tot_y |
---|---|---|
1 | 2022 | 1 |
1 | 2023 | 1 |
1 | 2024 | 1 |
1 | 2025 | 1 |
2 | 2019 | 1 |
2 | 2020 | 1 |
2 | 2021 | 1 |
2 | 2022 | 1 |
2 | 2023 | 2 |
2 | 2024 | 2 |
2 | 2025 | 1 |
Avec une ligne par année, on peut la fusionner avec une autre base biographique en format individus-années (même principe qu’avec la fusion avec la base sur toutes les années de vie).
= full_join(dfy, df2, by = c("id","year"))
df2y
= arrange(df2y, id,year)
df2y
= select(df2y, c(id,year,x,tot_y))
df2y
$tot_y[is.na(df2y$tot_y)] = 0
df2y$x[is.na(df2y$x)] = 0
df2y
kable(df2y)
id | year | x | tot_y |
---|---|---|---|
1 | 2020 | 1 | 0 |
1 | 2021 | 1 | 0 |
1 | 2022 | 0 | 1 |
1 | 2023 | 2 | 1 |
1 | 2024 | 2 | 1 |
1 | 2025 | 1 | 1 |
2 | 2019 | 0 | 1 |
2 | 2020 | 0 | 1 |
2 | 2021 | 0 | 1 |
2 | 2022 | 2 | 1 |
2 | 2023 | 2 | 2 |
2 | 2024 | 2 | 2 |
2 | 2025 | 2 | 1 |
= data.frame(id = c( 1, 1, 1, 2, 3, 3, 4),
df deb = c(2018, 2022, 2024, 2019, 2023, 2024, 2023),
fin = c(2021, 2024, 2025, NA, 2024, NA, NA),
y = c(1, 2, 1, 2, 3, 2, 1),
nseq = c(1, 2, 3, 1, 1, 2, 1)
)
kable(df)
id | deb | fin | y | nseq |
---|---|---|---|---|
1 | 2018 | 2021 | 1 | 1 |
1 | 2022 | 2024 | 2 | 2 |
1 | 2024 | 2025 | 1 | 3 |
2 | 2019 | NA | 2 | 1 |
3 | 2023 | 2024 | 3 | 1 |
3 | 2024 | NA | 2 | 2 |
4 | 2023 | NA | 1 | 1 |
On va s’intéresser à la durée jusqu’à l’occurence de la séquence de type 2 ou 3 (variable y). On considéra que le début de l’exposition est donné par la variable deb sur la première séquence.
Recupération de l’année de l’évènement
On peut repérer la présence d’une des deux séquences d’intérêt avec une indicatrice.
$e = ifelse(df$y==2 | df$y==3,1,0)
df
kable(df)
id | deb | fin | y | nseq | e |
---|---|---|---|---|---|
1 | 2018 | 2021 | 1 | 1 | 0 |
1 | 2022 | 2024 | 2 | 2 | 1 |
1 | 2024 | 2025 | 1 | 3 | 0 |
2 | 2019 | NA | 2 | 1 | 1 |
3 | 2023 | 2024 | 3 | 1 | 1 |
3 | 2024 | NA | 2 | 2 | 1 |
4 | 2023 | NA | 1 | 1 | 0 |
De nouveau l’utilisation d’un compteur sur cette variable indicatrice, peut s’avérer utile pour repérer le moment de l’occurence.
= df %>% group_by(id) %>% mutate(n = cumsum(e))
df
kable(df)
id | deb | fin | y | nseq | e | n |
---|---|---|---|---|---|---|
1 | 2018 | 2021 | 1 | 1 | 0 | 0 |
1 | 2022 | 2024 | 2 | 2 | 1 | 1 |
1 | 2024 | 2025 | 1 | 3 | 0 | 1 |
2 | 2019 | NA | 2 | 1 | 1 | 1 |
3 | 2023 | 2024 | 3 | 1 | 1 | 1 |
3 | 2024 | NA | 2 | 2 | 1 | 2 |
4 | 2023 | NA | 1 | 1 | 0 | 0 |
Pour id=(2,3,4), ce compteur permet d’obtenir l’information souhaitée, à savoir n=0 en situation d’attente/séjour/survie et n=1 l’année de l’évènement. Pour id=1 cependant, l’alternance en y=1 et y=(2,3) ne permet pas de récupérer l’année d’occurence (première fois en 2 ou 3). Cela peut être fait, en faisant un compteur sur le compteur précédent:
= df %>% group_by(id) %>% mutate(nn = cumsum(n))
df
kable(df)
id | deb | fin | y | nseq | e | n | nn |
---|---|---|---|---|---|---|---|
1 | 2018 | 2021 | 1 | 1 | 0 | 0 | 0 |
1 | 2022 | 2024 | 2 | 2 | 1 | 1 | 1 |
1 | 2024 | 2025 | 1 | 3 | 0 | 1 | 2 |
2 | 2019 | NA | 2 | 1 | 1 | 1 | 1 |
3 | 2023 | 2024 | 3 | 1 | 1 | 1 | 1 |
3 | 2024 | NA | 2 | 2 | 1 | 2 | 3 |
4 | 2023 | NA | 1 | 1 | 0 | 0 | 0 |
Récupération des information censurée
Pour récupérer l’information sur les observations qui seront censurée, on peut faire un total sur la variable n ou e: si n=0, l’individu n’aura pas connu l’évènement.
= df %>% group_by(id) %>% mutate(N = sum(n))
df
kable(df)
id | deb | fin | y | nseq | e | n | nn | N |
---|---|---|---|---|---|---|---|---|
1 | 2018 | 2021 | 1 | 1 | 0 | 0 | 0 | 2 |
1 | 2022 | 2024 | 2 | 2 | 1 | 1 | 1 | 2 |
1 | 2024 | 2025 | 1 | 3 | 0 | 1 | 2 | 2 |
2 | 2019 | NA | 2 | 1 | 1 | 1 | 1 | 1 |
3 | 2023 | 2024 | 3 | 1 | 1 | 1 | 1 | 3 |
3 | 2024 | NA | 2 | 2 | 1 | 2 | 3 | 3 |
4 | 2023 | NA | 1 | 1 | 0 | 0 | 0 | 0 |
Pour id=4, N est bien égal à 0.
Récupération du début de l’exposition
Le début de l’exposition étant ici l’année de début de la première séquence. On peut facilement récupérer cette sur toute les lignes en la repérant (ici en générant une nouvelle variable avec la fonction ifelse
), et en sommant sa valeur sur les autres lignes (=0).
id | deb | fin | y | nseq | e | n | nn | N | debexp |
---|---|---|---|---|---|---|---|---|---|
1 | 2018 | 2021 | 1 | 1 | 0 | 0 | 0 | 2 | 2018 |
1 | 2022 | 2024 | 2 | 2 | 1 | 1 | 1 | 2 | 2018 |
1 | 2024 | 2025 | 1 | 3 | 0 | 1 | 2 | 2 | 2018 |
2 | 2019 | NA | 2 | 1 | 1 | 1 | 1 | 1 | 2019 |
3 | 2023 | 2024 | 3 | 1 | 1 | 1 | 1 | 3 | 2023 |
3 | 2024 | NA | 2 | 2 | 1 | 2 | 3 | 3 | 2023 |
4 | 2023 | NA | 1 | 1 | 0 | 0 | 0 | 0 | 2023 |
Mise en forme finale de la base
On peut maintenant conserver les lignes qui nous intéresse à savoir celle où nn=1 (évènement) ou N=0 (censure).
= filter(df, nn==1 | N==0)
df
kable(df)
id | deb | fin | y | nseq | e | n | nn | N | debexp |
---|---|---|---|---|---|---|---|---|---|
1 | 2022 | 2024 | 2 | 2 | 1 | 1 | 1 | 2 | 2018 |
2 | 2019 | NA | 2 | 1 | 1 | 1 | 1 | 1 | 2019 |
3 | 2023 | 2024 | 3 | 1 | 1 | 1 | 1 | 3 | 2023 |
4 | 2023 | NA | 1 | 1 | 0 | 0 | 0 | 0 | 2023 |
On dispose déjà de la variable d’évènement/censure (e ou n = (0,1), on finit donc par la variable de durée.
$fin[is.na(df$fin)] = 2025
df
$dur = ifelse(df$e==1, df$deb - df$debexp + 1, df$fin - df$debexp + 1)
df
= select(df, c(id,e,dur))
df
kable(df)
id | e | dur |
---|---|---|
1 | 1 | 5 |
2 | 1 | 1 |
3 | 1 | 1 |
4 | 0 | 3 |
Ces informations sont suffisantes pour estimer une fonction de séjour et on peut ajouter, si elles ne sont pas présentes, des covariables fixes issues du fichier des caractéristiques générales. Pour l’ajout de covariables dynamiques, leur ajout n’est pas forcément difficile pour une analyse en durée discrète 6. Pour les analyses type Cox, selon la nature de la variable dynamique, l’opération (quel que soit le logiciel utilisé) risque d’être plus ou moins compliquée.
En première ou deuxième analyse, on peut également voir s’intéresser à la durée de séjour dans l’état précédent. Par exemple, si l’analyse précédent consistait à regarder la durée de séjour dans le premier emploi, on pourrait regarder ensuite la durée jusqu’à sa reprise.
Cela va un peu (voir plus) se compliquer. On va repartir de la base de départ précédente en ajoutant une observation.
= data.frame(id = c( 1, 1, 1, 2, 3, 3, 4, 5, 5, 5 , 5),
df deb = c(2018, 2022, 2024, 2019, 2023, 2024, 2023, 2019, 2021, 2023, 2024),
fin = c(2021, 2024, 2025, NA, 2024, NA, NA, 2021, 2023, 2024, NA),
y = c(1, 2, 1, 2, 3, 2, 1, 1, 2, 1,3),
nseq = c(1, 2, 3, 1, 1, 2, 1, 1, 2, 3, 4)
)
kable(df)
id | deb | fin | y | nseq |
---|---|---|---|---|
1 | 2018 | 2021 | 1 | 1 |
1 | 2022 | 2024 | 2 | 2 |
1 | 2024 | 2025 | 1 | 3 |
2 | 2019 | NA | 2 | 1 |
3 | 2023 | 2024 | 3 | 1 |
3 | 2024 | NA | 2 | 2 |
4 | 2023 | NA | 1 | 1 |
5 | 2019 | 2021 | 1 | 1 |
5 | 2021 | 2023 | 2 | 2 |
5 | 2023 | 2024 | 1 | 3 |
5 | 2024 | NA | 3 | 4 |
Filtrage des observations hors champs
On peut déjà supprimer les observations hors champs, à savoir ici id=4 qui n’a pas connu l’évènement dont on analyse la durée.
id | deb | fin | y | nseq | e23 | n23 |
---|---|---|---|---|---|---|
1 | 2022 | 2024 | 2 | 2 | 1 | 1 |
1 | 2024 | 2025 | 1 | 3 | 0 | 1 |
2 | 2019 | NA | 2 | 1 | 1 | 1 |
3 | 2023 | 2024 | 3 | 1 | 1 | 1 |
3 | 2024 | NA | 2 | 2 | 1 | 2 |
5 | 2021 | 2023 | 2 | 2 | 1 | 1 |
5 | 2023 | 2024 | 1 | 3 | 0 | 1 |
5 | 2024 | NA | 3 | 4 | 1 | 2 |
Récupération de l’évènement analysé
Ici l’évènement sera un retour dans l’état y=1. Il y a de nouveau une possibilité de censure à droite si une observation reste dans l’état 2 ou 3 jusqu’au moment de l’enquête.
Il peut être utile d’utiliser des variables décalées pour repérer les changements d’état d’une séquence à une autre. Ces décalages sont appelées lead ou lag:
On va utilise ici des lead et donc pouvoir repérer les changements d’état d’une séquence à une autre. Comme on s’intéresse au retour à l’état 1:
id | deb | fin | y | nseq | e23 | n23 | e | diff_e |
---|---|---|---|---|---|---|---|---|
1 | 2022 | 2024 | 2 | 2 | 1 | 1 | 0 | -1 |
1 | 2024 | 2025 | 1 | 3 | 0 | 1 | 1 | NA |
2 | 2019 | NA | 2 | 1 | 1 | 1 | 0 | NA |
3 | 2023 | 2024 | 3 | 1 | 1 | 1 | 0 | 0 |
3 | 2024 | NA | 2 | 2 | 1 | 2 | 0 | NA |
5 | 2021 | 2023 | 2 | 2 | 1 | 1 | 0 | -1 |
5 | 2023 | 2024 | 1 | 3 | 0 | 1 | 1 | 1 |
5 | 2024 | NA | 3 | 4 | 1 | 2 | 0 | NA |
Pour chaque dernière séquence la valeur du lag est une valeur manquante. On repère l’évènement avec une valeur de -1 (transition de 0 à 1). On ne peut pas encore filtrer les informations car il va falloir récupérer la fin de la séquence, mais on peut déjà construire l’information.
$e = ifelse(df$diff_e==-1,1,0)
df$e[is.na(df$e)] = 0
df= df %>% group_by(id) %>% mutate(e = sum(e))
df
kable(df)
id | deb | fin | y | nseq | e23 | n23 | e | diff_e |
---|---|---|---|---|---|---|---|---|
1 | 2022 | 2024 | 2 | 2 | 1 | 1 | 1 | -1 |
1 | 2024 | 2025 | 1 | 3 | 0 | 1 | 1 | NA |
2 | 2019 | NA | 2 | 1 | 1 | 1 | 0 | NA |
3 | 2023 | 2024 | 3 | 1 | 1 | 1 | 0 | 0 |
3 | 2024 | NA | 2 | 2 | 1 | 2 | 0 | NA |
5 | 2021 | 2023 | 2 | 2 | 1 | 1 | 1 | -1 |
5 | 2023 | 2024 | 1 | 3 | 0 | 1 | 1 | 1 |
5 | 2024 | NA | 3 | 4 | 1 | 2 | 1 | NA |
Récupération de l’année final avec succesion d’états de même type
La difficulté ici est apportée seulement par id=3. Jusqu’à 2025, on a successivement l’état 2 puis 3. Il va donc falloir récupérer cette dernière année de succession de 2 et 3, jusqu’à la censure ou jusqu’à un retour dans l’état 1. S’il n’y avait pas ce genre de situation, l’utilisation de la variable diff_e aurait été suffisante pour récupérer l’année de fin lorsqu’on a plusieurs séquences (situations pour id=1,5).
On va de nouveau utiliser un lead, mais sur la variable e23.
id | deb | fin | y | e23 | n23 | e | lead_e23 | idem |
---|---|---|---|---|---|---|---|---|
1 | 2022 | 2024 | 2 | 1 | 1 | 1 | 0 | 0 |
1 | 2024 | 2025 | 1 | 0 | 1 | 1 | NA | 0 |
2 | 2019 | NA | 2 | 1 | 1 | 0 | NA | 0 |
3 | 2023 | 2024 | 3 | 1 | 1 | 0 | 1 | 1 |
3 | 2024 | NA | 2 | 1 | 2 | 0 | NA | 1 |
5 | 2021 | 2023 | 2 | 1 | 1 | 1 | 0 | 0 |
5 | 2023 | 2024 | 1 | 0 | 1 | 1 | 1 | 0 |
5 | 2024 | NA | 3 | 1 | 2 | 1 | NA | 0 |
On doit maintenant récupérer la dernière année de fin des situations où idem>0, et la placer sur la première.
ifelse
. A tester avec if_else
ou case_when
.
id | deb | fin | n23 | e | truefin |
---|---|---|---|---|---|
1 | 2022 | 2024 | 1 | 1 | 0 |
1 | 2024 | 2025 | 1 | 1 | 0 |
2 | 2019 | 2025 | 1 | 0 | 0 |
3 | 2023 | 2025 | 1 | 0 | 2025 |
3 | 2024 | 2025 | 2 | 0 | 2025 |
5 | 2021 | 2023 | 1 | 1 | 0 |
5 | 2023 | 2024 | 1 | 1 | 0 |
5 | 2024 | 2025 | 2 | 1 | 0 |
On peut [enfin] sélectionner et conserver une seule ligne par individu et générer la variable de durée
= select(df,-truefin)
df
= df %>% group_by(id) %>% mutate(nn23 = cumsum(n23))
df = filter(df, n23==nn23)
df
$dur= df$fin - df$deb + 1
df
= select(df, -c(n23,nn23))
df
kable(df)
id | deb | fin | e | dur |
---|---|---|---|---|
1 | 2022 | 2024 | 1 | 3 |
2 | 2019 | 2025 | 0 | 7 |
3 | 2023 | 2025 | 0 | 3 |
5 | 2021 | 2023 | 1 | 3 |
Des éléments de manipulation/programmation pour un exemple volontairement très compliqué sont donnés dans méthodes => notes méthodologiques. Ayant été fait en 2015, le code pour R est largement out of date↩︎
Ici on a une enquête réalisée une même année pour toute les observations, ce n’est pas toujours le cas. De même au lieu de l’année, si les datations avaient été données par l’âge, au moment de l’enquête l’âge varierait d’une personne à une autre. Ces datations différentes (année ou âge) peuvent être présentes dans chaque module biographique d’une enquête, ou dans le fichier des caractéristiques fixes. Dans ce cas l’information devra être récupérée↩︎
La mesure est ici discrète/groupée, il me semble toujours préférable d’allonger les durées à +1. On démarre donc toujours un premier janvier pour terminer un 31 décembre sur l’information est donnée par des année. Ici t=1 représente la première année après la sortie des études. Une personne qui aura eu un emploi durant cette année, l’aura eu durant cette première année, que ce soit 2 semaines après ou 11 mois après. Si on disposait des mois, cela pourrait être intéressant de modifier cette métrique temporelle. Voir exemple 3↩︎
Contrairement au durée annuelle je n’ai pas ajouté 1 à chaque durée, ce qui est de nouveau envisageable par exemple si on veut explicitement indiquer les évènements qui ont lieu le premier mois. Pour id=3 la relation a t-elle durée du 1er septembre au 30 novembre, ou du 30 septembre au 1er novembre?? On a toujours un problème de précision, mais ici d’une trentaine de jours↩︎
avec R par exemple la fonction unique
de dplyr↩︎
En conservant l’information sur les années, on transformera la base en format individu-période et on procédera à une fusion des informations↩︎