11 votes

sélectionner la dernière observation dans les données longitudinales

Je dispose d'un ensemble de données comportant plusieurs évaluations temporelles pour chaque participant. Je souhaite sélectionner la dernière évaluation pour chaque participant. Mon jeu de données ressemble à ceci :

ID  week  outcome
1   2   14
1   4   28
1   6   42
4   2   14
4   6   46
4   9   64
4   9   71
4  12   85
9   2   14
9   4   28
9   6   51
9   9   66
9  12   84

Je veux sélectionner uniquement la dernière observation/évaluation pour chaque participant, mais je n'ai que le nombre de semaines comme indicateur pour chaque participant. Comment est-ce possible dans R (ou excel ?)?

Merci d'avance,

niki

11voto

Josh O'Brien Points 68397

Voici une approche de base-R :

do.call("rbind", 
        by(df, INDICES=df$ID, FUN=function(DF) DF[which.max(DF$week), ]))
  ID week outcome
1  1    6      42
4  4   12      85
9  9   12      84

Alternativement, le data.table offre un langage succinct et expressif pour effectuer ce type de manipulations sur les bases de données :

library(data.table)
dt <- data.table(df, key="ID")

dt[, .SD[which.max(outcome), ], by=ID] 
#      ID week outcome
# [1,]  1    6      42
# [2,]  4   12      85
# [3,]  9   12      84

# Same but much faster. 
# (Actually, only the same as long as there are no ties for max(outcome)..)
dt[ dt[,outcome==max(outcome),by=ID][[2]] ]   # same, but much faster.

# If there are ties for max(outcome), the following will still produce
# the same results as the method using .SD, but will be faster
i1 <- dt[,which.max(outcome), by=ID][[2]]
i2 <- dt[,.N, by=ID][[2]]
dt[i1 + cumsum(i2) - i2,]

Enfin, voici un plyr -Une solution basée sur l'utilisation de l'Internet

library(plyr)

ddply(df, .(ID), function(X) X[which.max(X$week), ])
#   ID week outcome
# 1  1    6      42
# 2  4   12      85
# 3  9   12      84

10voto

user1234357 Points 319

Si vous cherchez simplement la dernière observation par identifiant de personne, un simple code de deux lignes devrait suffire. Je suis toujours en faveur d'une solution de base simple lorsque c'est possible, mais il est toujours bon d'avoir plus d'une façon de résoudre un problème.

dat[order(dat$ID,dat$Week),]  # Sort by ID and week
dat[!duplicated(dat$ID, fromLast=T),] # Keep last observation per ID

   ID Week Outcome
3   1    6      42
8   4   12      85
13  9   12      84

2voto

jbaums Points 14228

Une autre option dans la base : df[rev(rownames(df)),][match(unique(df$ID), rev(df$ID)), ]

2voto

Bryan Goodrich Points 701

Je peux jouer à ce jeu. J'ai effectué quelques tests sur les différences entre lapply , sapply y par entre autres. Il me semble que plus vous contrôlez les types de données et plus l'opération est basique, plus elle est rapide (par exemple, lapply est généralement plus rapide que sapply, et as.numeric(lapply(...)) sera également plus rapide). En gardant cela à l'esprit, cette opération produit les mêmes résultats que précédemment et peut être plus rapide que les autres.

df[cumsum(as.numeric(lapply(split(df$week, df$id), which.max))), ]

Explication : nous voulons seulement quel.max sur la semaine pour chaque identifiant. Cela permet de gérer le contenu de lapply . Nous n'avons besoin que du vecteur de ces points relatifs, il faut donc le rendre numérique. Le résultat est le vecteur (3, 5, 5). Nous devons ajouter les positions des maxima antérieurs. Cette opération est réalisée à l'aide de cumsum .

Il convient de noter que cette solution n'est pas générale lorsque j'utilise cumsum . Il peut être nécessaire, avant l'exécution, de trier la base de données en fonction de l'identifiant et de la semaine. J'espère que vous comprenez pourquoi (et que vous savez comment utiliser la fonction with(df, order(id, week)) dans l'index des lignes pour y parvenir). Dans tous les cas, cela peut toujours échouer si nous n'avons pas un max unique, parce que which.max ne prend que le premier. Par conséquent, ma solution pose un peu de questions, mais cela va de soi. Nous essayons d'extraire des informations très spécifiques pour un exemple très spécifique. Nos solutions ne peuvent pas être générales (même si les méthodes sont importantes à comprendre de manière générale).

Je laisse à trinker le soin de mettre à jour ses comparaisons !

2voto

msmith Points 21

Cette réponse utilise le package data.table. Elle devrait être très rapide, même avec de grands ensembles de données.

setkey(DT, ID, week)              # Ensure it's sorted.
DT[DT[, .I[.N], by = ID][, V1]]

Explication : .I est un vecteur entier contenant les emplacements des lignes du groupe (dans ce cas, le groupe est ID ). .N est un vecteur entier de longueur 1 contenant le nombre de lignes du groupe. Ce que nous faisons ici, c'est extraire l'emplacement de la dernière ligne de chaque groupe, en utilisant la méthode "inner" DT[.] en utilisant le fait que les données sont triées en fonction de ID y week . Ensuite, nous l'utilisons pour subdiviser la partie "extérieure" de l'échantillon. DT[.] .

À titre de comparaison (parce qu'il n'est pas affiché ailleurs), voici comment vous pouvez générer les données d'origine afin de pouvoir exécuter le code :

DT <- 
  data.table(
    ID = c(rep(1, 3), rep(4, 5), rep(9, 5)),
    week = c(2,4,6, 2,6,9,9,12, 2,4,6,9,12), 
    outcome = c(14,28,42, 14,46,64,71,85, 14,28,51,66,84))

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X