2017-09-27 4 views
3

J'ai un jeu de données d'échantillons qui suit la trajectoire d'un vélo vers différentes stations. Mon objectif est de trouver les intervalles que le vélo reste à une station particulière avec difftime(), dans ce cas, la station B.Référencement des données dans les rangées principales

> test 
    bikeid start_station   starttime end_station    endtime 
1  1    A 2017-09-25 01:00:00   B 2017-09-25 01:30:00 
2  1    B 2017-09-25 07:30:00   C 2017-09-25 08:00:00 
3  1    C 2017-09-25 10:00:00   A 2017-09-25 10:30:00 
4  1    A 2017-09-25 13:00:00   C 2017-09-25 13:30:00 
5  1    C 2017-09-25 15:30:00   B 2017-09-25 16:00:00 
6  1    B 2017-09-25 18:00:00   B 2017-09-25 18:30:00 
7  1    B 2017-09-25 19:00:00   A 2017-09-25 19:30:00 
8  1    А 2017-09-25 20:00:00   B 2017-09-25 20:30:00 
9  1    C 2017-09-25 22:00:00   C 2017-09-25 22:30:00 
10  1    B 2017-09-25 23:00:00   C 2017-09-25 23:30:00 

Parfois, les vélos ne commencent pas à la même station qu'ils ont fini, et ces cas devrait être ignoré. Dans l'ensemble de données ci-dessus, nous pouvons voir que 360 ​​minutes se sont écoulées entre 01:30:00 et 07:30:00, que 120 minutes se sont écoulées entre 16:00:00 et 18:00:00, et que 30 minutes se sont écoulées entre 18:30:00 et 19:00:00. Les rangs 8 et 10 sont ignorés car la moto ne démarre pas à la même station que celle où elle s'est terminée. Par conséquent, le vecteur de sortie doit être:

[1] 360 120 30 

Le code suivant à l'aide ne produit pas la sortie désirée:

sapply(test$starttime[test$end_station == "B"], function(x, et) difftime(et[x < et][1], x, units = "mins"), et = test$endtime[test$start_station == "B"]) 

Comment peut-on prendre en compte la ligne suivante et calculer difftime() que lorsque le end_station et start_station dans la rangée suivante sont égaux? En utilisant lead() dans dplyr? Toute suggestion serait appréciée

Voici les données de l'échantillon:

> dput(test) 
structure(list(bikeid = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), start_station = c("A", 
"B", "C", "A", "C", "B", "B", "А", "C", "B"), starttime = structure(c(1506315600, 
1506339000, 1506348000, 1506358800, 1506367800, 1506376800, 1506380400, 
1506384000, 1506391200, 1506394800), class = c("POSIXct", "POSIXt" 
), tzone = ""), end_station = c("B", "C", "A", "C", "B", "B", 
"A", "B", "C", "C"), endtime = structure(c(1506317400, 1506340800, 
1506349800, 1506360600, 1506369600, 1506378600, 1506382200, 1506385800, 
1506393000, 1506396600), class = c("POSIXct", "POSIXt"), tzone = "")), .Names = c("bikeid", 
"start_station", "starttime", "end_station", "endtime"), row.names = c(NA, 
-10L), class = "data.frame") 

Répondre

5

Remodeler as suggested la dernière fois ...

library(data.table) 

mtest = melt(setDT(test), id="bikeid", 
    meas = patterns("_station", "time"), 
    variable.name = "event", 
    value.name = c("station", "time")) 
mtest[.(factor(1:2), c("start", "end")), on=.(event), event := i.V2] 
setkey(mtest, bikeid, time) 

Puis retour à large pour les sorts tandis que le vélo est au repos .. .

idleDT = dcast(mtest[-c(1,.N)][, g := rep(1:.N, each=2, length.out=.N)], 
    g ~ rowid(g), value.var=c("station", "time")) 

    g station_1 station_2    time_1    time_2 
1: 1   B   B 2017-09-25 01:30:00 2017-09-25 07:30:00 
2: 2   C   C 2017-09-25 08:00:00 2017-09-25 10:00:00 
3: 3   A   A 2017-09-25 10:30:00 2017-09-25 13:00:00 
4: 4   C   C 2017-09-25 13:30:00 2017-09-25 15:30:00 
5: 5   B   B 2017-09-25 16:00:00 2017-09-25 18:00:00 
6: 6   B   B 2017-09-25 18:30:00 2017-09-25 19:00:00 
7: 7   A <U+0410> 2017-09-25 19:30:00 2017-09-25 20:00:00 
8: 8   B   C 2017-09-25 20:30:00 2017-09-25 22:00:00 
9: 9   C   B 2017-09-25 22:30:00 2017-09-25 23:00:00 

Ensuite, rejoindre ou filtre et compute ...

idleDT[.("B", "B"), on=.(station_1, station_2), time_2 - time_1 ] 

Time differences in mins 
[1] 360 120 30 

Commentaire

Je devrais probablement expliquer pourquoi je préfère le format long mtest sur test de l'OP, même si je vais au retour grand format pour l'analyse (grâce @Henrik) ...

  • Les stations pourraient/devraient être un facteur, et si vous le divisez sur deux colonnes dans les données de base, il est important de s'assurer que les deux facteurs ont les mêmes niveaux.
  • Les données sont probablement enregistrées en termes d'événements (comme "le vélo est parti" et "le vélo est arrivé"), pas en termes de voyages. Si quelqu'un vole le vélo ou le perd, par exemple, le endtime et le end_station devraient logiquement être manquants, mais il est plus facile d'en suivre le long format, à mon avis.
  • Les données mesurées pourraient même avoir deux événements «le vélo est arrivé» dans une rangée, même si cela n'a pas de sens logique, tout ce qui peut mal tourner avec les données va mal tourner, selon mon expérience. Si cela se produisait, vous auriez du mal à trouver comment l'enregistrer en format large en termes de voyages.

En général, je suis juste appliquer mon (peut-être trop zélés ou mal) comprendre tidy data, riffs sur la plainte de Hadley dans le lien sur les mises en page de données où « [c] les en-têtes OLONNE sont des valeurs, pas des noms de variables. »

+0

Je suis 100% sûr que vous avez une bonne raison d'utiliser '' melt' & dcast' pour créer "idleDT" , au lieu de quelque chose comme 'test [,' ': =' '(prev_endstation = shift (end_station), prev_endtime = shift (endtime))]', mais malheureusement mon état actuel du cerveau ne me permet pas de le repérer. Pardon. Plz envoyer l'explication. ;) – Henrik

+1

@Henrik J'ai édité dans une longue explication et je suis d'accord qu'une explication était nécessaire. J'espère que cela a du sens, même si je ne suis pas sûr que ce sera convaincant pour tout le monde. Merci :) – Frank

1

Une solution dplyr:

library(dplyr) 
df %>% 
    mutate(lag_end_station = lag(end_station), 
     lag_end_time = lag(endtime)) %>% 
    filter(start_station == "B" & lag_end_station == "B") %>% 
    transmute(interval = difftime(starttime, lag_end_time)) 

Résultat:

interval 
1 360 mins 
2 120 mins 
3 30 mins