2017-05-22 6 views
0

J'ai vu beaucoup de questions sur la vectorisation des boucles, mais je n'ai trouvé aucune question impliquant la vectorisation d'une boucle for pour remplir une cellule en fonction de la valeur d'une cellule dans une rangée ci-dessous (Excuses si je suis juste aveugle si ...). J'ai un dataframe avec 1,6 million de lignes de salaires et la date à laquelle chaque personne a commencé à gagner ce salaire. Chaque personne peut avoir plusieurs salaires, et donc plusieurs lignes, chacune avec une date différente qu'elle a été mise à jour.Opération itérative vectorisée sur les lignes

Code pour un jeu de données factice est la suivante:

df1 <- data.frame("id" = c(1,1,2,2,3,3,4,4,5,5,6,6), 
        "salary" = c(15456,16594, 
           17364,34564, 
           34525,33656, 
           23464,23467, 
           16794,27454, 
           40663,42743), 
        "start_date" = sample(seq(as.Date('2016/01/01'),as.Date(Sys.Date()), by="day"), 12)) 

df1 <- df1[order(df1$id,df1$start_date),] 

Je veux créer une colonne avec une date de fin pour chaque salaire, qui est calculé comme le jour précédant l'entrée de salaire ultérieure. S'il n'y a pas d'entrée de salaire subséquente, alors il est fixé comme date du jour. Ceci est mon code, y compris une boucle, pour le faire:

df1$end_date <- Sys.Date() 

for (i in 1:(nrow(df1)-1)){ 
    if(df1[i,1]== df1[i+1,1]){ 
    df1[i,4] <- df1[i+1,3]-1 
    } 
    print(i) 
} 

Cependant, je sais que for boucles ne sont pas la façon la plus efficace, mais comment pourrais-je aller sur ce vectorisation?

Répondre

1

Vous pouvez utiliser library(data.table):

setDT(df1)[, end_date := shift(start_date, type = "lead", fill = Sys.Date()), id][] 
+0

Cela fonctionne pour la première ligne pour chaque id, mais ensembles la dernière rangée pour chaque id est égale au jour avant la date de la première rangée, alors que je suis après la date du jour. – cstaff91

+0

ah, j'ai mal lu "aujourd'hui". Pas de problème, voir l'édition. – BigDataScientist

+0

Bang, merci! – cstaff91

0

Avec data.table et déplacement, vous pouvez utiliser ci-dessous:

df1 <- data.table("id" = c(1,1,2,2,3,3,4,4,5,5,6,6), 
        "salary" = c(15456,16594, 
           17364,34564, 
           34525,33656, 
           23464,23467, 
           16794,27454, 
           40663,42743), 
        "start_date" = sample(seq(as.Date('2016/01/01'),as.Date(Sys.Date()), by="day"), 12)) 

df1 <- df1[order(id,start_date),] 

df1[, EndDate := shift(start_date, type="lead"), id] 
df1[is.na(EndDate), EndDate := Sys.Date()] 
0

Si je comprends bien votre question, le code de base R suivant fonctionnera.

df1$end <- ave(df1$start_date, df1$id, FUN=function(x) c(tail(x, -1) - 1, Sys.Date())) 

ave est utilisé pour effectuer l'opération au niveau du groupe. La fonction effectuée prend la seconde à la date finale et soustrait 1. Ceci est concaténé avec la date finale.

Ce retour

df1 
    id salary start_date  end 
1 1 15456 2016-03-20 2016-12-06 
2 1 16594 2016-12-07 2017-05-22 
3 2 17364 2016-10-17 2016-07-27 
4 2 34564 2016-07-28 2017-05-22 
5 3 34525 2016-05-26 2016-05-01 
6 3 33656 2016-05-02 2017-05-22 
7 4 23464 2017-04-17 2016-01-19 
8 4 23467 2016-01-20 2017-05-22 
9 5 16794 2016-09-12 2016-05-06 
10 5 27454 2016-05-07 2017-05-22 
11 6 40663 2016-10-03 2016-03-28 
12 6 42743 2016-03-29 2017-05-22 
+0

voir le commentaire sur ma réponse, il veut 'Sys.Date()' pour 'end_date' si le' start_date' est le dernier par id, ... j'ai également couru dans le même piège. – BigDataScientist

+0

Aha. Merci pour la clarification. – lmo

1

Utilisation du package dplyr, vous pouvez faire:

library(dplyr) 
df1 %>% 
group_by(id) %>% 
mutate(end_date=lead(start_date-1,default=Sys.Date())) 

qui retourne:

id salary start_date end_date 
    <dbl> <dbl>  <date>  <date> 
1  1 15456 2016-02-14 2016-03-02 
2  1 16594 2016-03-03 2017-05-22 
3  2 17364 2016-01-17 2016-11-28 
4  2 34564 2016-11-29 2017-05-22 
5  3 33656 2016-08-17 2016-11-25 
6  3 34525 2016-11-26 2017-05-22 
7  4 23464 2016-01-20 2017-05-05 
8  4 23467 2017-05-06 2017-05-22 
9  5 27454 2016-02-29 2016-12-15 
10  5 16794 2016-12-16 2017-05-22 
11  6 42743 2016-03-14 2017-01-29 
12  6 40663 2017-01-30 2017-05-22