J'ai un ensemble de données longitudinales qui enregistre la situation professionnelle de la personne chaque mois pendant 45 mois. J'aimerais pouvoir créer deux variables à ajouter à cet ensemble de données: 1) Durée totale que chaque personne a passée «au chômage» 2) Nombre de périodes de chômage
Idéalement, cela passerait également les AN sans interrompre la période
J'ai créé un exemple de jeu de données pour simplifier les choses:
ID spell_count duration 1 1 2 2 2 2 1 2 3 3 1 1 ... 10 10 1 2
3 Réponses :
Avec le package tidyverse
vous pouvez regrouper par une variable (ou plus) et résumer très facilement.
Avant d'agréger les données, je vais forcer la colonne date
à classer Date
et remplacer les chaînes de caractères "NA"
par les valeurs manquantes réelles, NA
.
res <- lapply(split(df, df$ID), function(DF){ i <- DF$act == "Unemployed" if(any(i, na.rm = TRUE)) duration <- difftime(max(DF$date[i], na.rm = TRUE), min(DF$date[i], na.rm = TRUE), units = "weeks") else duration <- 0 spell_count <- sum(i, na.rm = TRUE) data.frame(ID = DF$ID[1], spell_count, duration) }) res <- do.call(rbind, res) row.names(res) <- NULL res # ID spell_count duration #1 1 2 8.714286 weeks #2 2 2 4.428571 weeks #3 3 1 0.000000 weeks #4 4 2 4.428571 weeks #5 5 0 0.000000 weeks #6 6 0 0.000000 weeks #7 7 0 0.000000 weeks #8 8 0 0.000000 weeks #9 9 0 0.000000 weeks #10 10 2 8.714286 weeks
Le code ci-dessus ne donnera que les lignes où il y a au moins un act == "Unemployed" code >.
Si vous voulez toutes les lignes, la solution de base R suivante le fera.
library(tidyverse) is.na(df$act) <- df$act == "NA" df$date <- as.Date(df$date) df %>% group_by(ID, act) %>% summarise(spell_count = sum(act == "Unemployed", na.rm = TRUE), duration = difftime(last(date), first(date), units = "weeks")) %>% filter(act == "Unemployed") %>% select(-act) ## A tibble: 5 x 3 ## Groups: ID [5] # ID spell_count duration # <int> <int> <time> #1 1 2 8.714286 weeks #2 2 2 4.428571 weeks #3 3 1 0.000000 weeks #4 4 2 4.428571 weeks #5 10 2 8.714286 weeks
C'est vraiment en train d'y arriver, merci. Il y a cependant deux problèmes. La colonne de durée indique la durée globale plutôt que la durée du sort (par exemple, l'ID 5 doit avoir une durée de 0). De plus, l'ID 10 devrait avoir spell_count 1 plutôt que 2. Existe-t-il un moyen de le faire?
Quant au second, * ID 10 doit avoir spell_count 1 plutôt que 2 * les lignes numéro 10 et 30 ont toutes deux ID == 10
et act == "Unemployed"
. Cela devrait donc être 2
. Ou ai-je tort?
Vous avez tout à fait raison, je me demandais simplement s'il y avait un moyen d'ignorer la ligne numéro 20 qui est NA et ainsi en faire un seul sort plutôt que d'avoir la valeur NA diviser le sort en deux. Je suppose que je pourrais supprimer toutes les lignes contenant des NA et cela devrait s'en occuper.
na.rm = TRUE
s'en charge.
Ouais, mais si vous regardez par exemple l'ID 2, il devrait également avoir spell_count seulement 1 car il s'agit d'un sort continu (de 2 mois). Donc, en gros, ce que vous avez fourni comme colonne spell_count est en fait une colonne de durée en mois, ce qui est vraiment utile (merci) mais je ne sais toujours pas comment calculer le nombre de sorts.
Je n'utilise que votre premier bloc de code, puis pour Durée globale, je fais:
df_stats <- merge(df_duration,df_spell_count, by = "ID", all.x = TRUE,all.y = TRUE)
le nombre de périodes de chômage est un peu plus délicat:
df_spell_count = df[order(ID,date)] df_spell_count <- df_spell_count[!(is.na(act)|act=="NA")] df_spell_count[,previous_act := shift(act,1),by = ID] df_spell_count<-df_spell_count[act =="Unemployed" & (previous_act!="Unemployed" | is.na(previous_act))] df_spell_count<-df_spell_count[,.(spell_count =.N),by = ID]
Si vous souhaitez fusionner les deux choses, il suffit de:
library(data.table) setDT(df) df_duration = df[act=="Unemployed",.(duration = .N),by = ID]
Notez que ce df ne contient pas de lignes pour les utilisateurs sans périodes de chômage. p >
puis-je demander une petite extension de ce code? Disons que j'ai une autre variable qui m'indique la date de l'entrevue. En fonction du moment où une personne a été interviewée, j'aimerais inclure un délai différent dans lequel calculer la durée et le nombre. Comment ajuster le code pour ce faire? Par exemple, si interview_date est le 01/10/2007, je veux inclure les données du 01/09/2006 et du 01/10/2006, mais si la date_interview est le 01/11/2007, je veux inclure les données du 01/09/2006, 2006 -10-01 et 2006-11-01.
Donc si j'ai bien compris, vous avez une date d'entrevue et vous souhaitez baser vos statistiques sur les activités antérieures à cette date. Donc, je filtrerais simplement la base de données df en fonction de la date d'interview de chaque ID utilisateur. Si ces dates sont dans un df différent, interview_df, fusionnez-le avec le df original, par ID utilisateur, puis filtrez simplement la date <= interview_date. BTW, pourriez-vous voter ma réponse? Que toi!
Je vous remercie beaucoup pour votre aide. Je suis un nouvel utilisateur et je viens d'avoir mes privilèges de vote, d'où le retard mais j'ai voté maintenant. C'est une excellente réponse.
Voici une autre tentative d'utilisation du tidyverse. Les données sur les «périodes» sont une transformation courante des données de panel; dans l'approche tidyverse, l'astuce, je pense, est de générer une variable orthographique, comme la variable "run" dans le code d'origine de l'OP.
# libraries library(tidyverse) library(zoo) library(lubridate) # example dataset ID <- c(1:10, 1:10, 1:10) date <- c("2006-09-01", "2006-09-01", "2006-09-01", "2006-09-01", "2006-09-01", "2006-09-01", "2006-09-01", "2006-09-01", "2006-09-01", "2006-09-01", "2006-10-01", "2006-10-01", "2006-10-01", "2006-10-01", "2006-10-01", "2006-10-01", "2006-10-01", "2006-10-01", "2006-10-01", "2006-10-01", "2006-11-01", "2006-11-01", "2006-11-01", "2006-11-01", "2006-11-01", "2006-11-01", "2006-11-01", "2006-11-01", "2006-11-01", "2006-11-01") act <- c("Unemployed", "Employment", "Education", "Education", "Education", "Education", "Education", "Education", "Education", "Unemployed", "Education", "Unemployed", "Unemployed", "Unemployed", "Education", "Education", "Employment", "Education", "Education", "NA", "Unemployed", "Unemployed", "NA", "Unemployed", "Education", "Employment", "Employment", "NA", "Education", "Unemployed") df <- data.frame(ID, date, act) df[order(ID),] # convert types of some variables (in particular use zoo::yearmon instead of date, since these are actually yearmonth combos) df$act <- as.character(df$act) df$date <- lubridate::ymd(df$date) df$yearmon <- zoo::as.yearmon(df$date) df$act <- ifelse(df$act=='NA',NA,df$act) # construct "act2", which is act, except when an NA is surrounded by the SAME act before and after, it is replaced with that same act # e.g. Unemployed NA Unemployed -> Unemployed Unemployed Unemployed # e.g. Education NA Unemployed -> stays the same # (see note at the end of this discussion for more details on this) df <- df %>% arrange(ID,date) df <- df %>% group_by(ID) %>% mutate( act2 = ifelse(is.na(act) & (lag(act)==lead(act)), lead(act), act) ) # create "spell" variable, which is like the "run" variable in the example code # within ID this identifies the spell that is currently taken place # --- this is the most important part of the code --- df <- df %>% group_by(ID) %>% mutate( spell = cumsum(coalesce(is.na(act2) | act2!=lag(act2),FALSE)) + 1 ) # add yearmonth + 1 month, in order to do duration calculations # (I'm again exploiting the fact that your data is monthly. if this were not true, this variable could be lead(date), within ID. but then we'd have to figure out how to deal with ends of the panel, where lead(date) is NA) df$yearmonplusmonth <- df$yearmon + (1/12) # construct a dataset of ID-spell combinations spells <- df %>% group_by(ID,spell) %>% summarize( spelltype = first(act2), duration = (max(yearmonplusmonth) - min(yearmon))*12 ) # construct a dataset at the ID level, with desired summaries of spells spellsummary <- spells %>% group_by(ID,spelltype) %>% summarize( spell_count = n(), duration = sum(duration) ) # if there are no spells of a given spelltype, it doesn't appear in spellsummary # we need to fill out spellsummary with zeroes in ID-spelltype cases where there are no spells: temp <- expand.grid(ID = unique(spellsummary$ID), spelltype = unique(spellsummary$spelltype)) spellsummary <- full_join(spellsummary,temp,by=c('ID','spelltype')) spellsummary <- spellsummary %>% mutate_at(vars(spell_count,duration),funs(coalesce(as.numeric(.),0))) spellsummary <- spellsummary %>% mutate_at(vars(spell_count,duration),funs(round(.,0))) spellsummary <- spellsummary %>% arrange(ID,spelltype) # finally, we just want Unemployed spelltype summaries by ID: spellsummary %>% filter(spelltype=='Unemployed') # A tibble: 10 x 4 # Groups: ID [10] # ID spelltype spell_count duration # <int> <chr> <dbl> <dbl> # 1 1 Unemployed 2 2 # 2 2 Unemployed 1 2 # 3 3 Unemployed 1 1 # 4 4 Unemployed 1 2 # 5 5 Unemployed 0 0 # 6 6 Unemployed 0 0 # 7 7 Unemployed 0 0 # 8 8 Unemployed 0 0 # 9 9 Unemployed 0 0 # 10 10 Unemployed 1 3
Remarque: j'obtiens 3 pour la durée de la dernière ligne , plutôt que 2 dans la sortie souhaitée de l'OP. La raison en est que je suppose que Unemp NA Unemp est vraiment Unemp Unemp Unemp, à la fois pour les besoins de spell_count ET pour des raisons de durée. Le PO veut que ce soit le cas pour spell_count mais pas pour la durée. Pour y parvenir, une approche pourrait consister à utiliser la variable "act" pour les calculs de durée et la variable "act2" pour les calculs spell_count - je laisse cela au lecteur.
Vous dites que vous avez des données mensuelles. Ainsi, chaque mois, il était enregistré si la personne était au chômage ou non - et dans l'ensemble de données réel, vous auriez 45 lignes par ID? Dans ce cas, la durée du statut de chômage (en mois) pourrait être juste une question de compter
Act% en% "Chômeurs"
pour chaque ID ...@lebatsnok Oui, j'ai environ 7 000 identifiants et 45 valeurs d'emploi correspondant à 45 collectes de données mensuelles. Ainsi, dans un format large, ce serait 7 000 lignes avec 45 variables d'emploi et dans un format long, ce serait 315 000 lignes avec 1 variable d'emploi.