1
votes

Conserver avant et après la date d'une liste externe

Ayant ce dataframe:

 id       date   name     text_sth
1 2008-10-31 Google another text
1 2008-10-31  Yahoo        other
1 2008-11-02 Google         test
1 2008-11-02  Yahoo     text_sth
1 2008-11-05 Amazon    text here
1 2008-11-02 Google another text
2 2008-10-31 Amazon          etc
2 2008-11-01 Google         test
2 2008-11-02 Amazon another text
2 2008-11-03 Google    text here

Et ce second:

library(data.table)
library(tidyverse)
library(reshape2)

dframe1 = data.table(dframe1)
dframe1[, date := as.Date(date)]

dframe1_first = dframe1[, .(date = min(date)), .(id, name)] %>% 
    mutate(date_pre = date - 1,
           date_after = date + 1)

req_rows = dframe2 %>%
    merge(dframe1_first %>%
              rename(id = id),
          by = "id") %>%
    filter(date >= date_pre,
           date <= date_after,
           date != date) %>%
    mutate(period = ifelse(date<date, '1-day-pre', '1-day-after'))

En utilisant les résultats de dframe1 comment est-il possible d'éviter dataframe2 les lignes qui ont le même nom pour chaque id que dframe1 mais une date avant et après la date d'enregistrement de dframe1?

Voici ce que j'ai essayé

    dframe2 <- structure(list(id = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 2L, 2L, 2L, 2L, 2L, 2L), date = c("2008-11-01", "2008-11-01", 
"2008-11-04", "2008-10-31", "2008-10-31", "2008-11-02", "2008-11-02", 
"2008-11-02", "2008-11-05", "2008-11-02", "2008-11-03", "2008-10-31", 
"2008-11-01", "2008-11-01", "2008-11-02", "2008-11-02", "2008-11-03"
), name = c("Google", "Yahoo", "Amazon", "Google", "Yahoo", "Amazon", 
"Google", "Yahoo", "Amazon", "Google", "Yahoo", "Amazon", "Google", 
"Amazon", "Google", "Amazon", "Google"), text_sth = c("test", 
"text_sth", "text here", "another text", "other", "another one", 
"test", "text_sth", "text here", "another text", "other", "etc", 
"test", "text_sth", "text here", "another text", "text here")), class = "data.frame", row.names = c(NA, 
-17L))

Résultat attendu:

dframe1 <- structure(list(id = c(1L, 1L, 1L, 2L, 2L), name = c("Google", 
"Yahoo", "Amazon", "Amazon", "Google"), date = c("2008-11-01", 
"2008-11-01", "2008-11-04", "2008-11-01", "2008-11-02")), class = "data.frame", row.names = c(NA, 
-5L))

r

0 commentaires

3 Réponses :


1
votes

Une approche pourrait consister à développer l'ensemble de données dframe1 et à inclure des lignes avec a +1 et -1 date pour chaque id et nom . Nous supprimons les lignes d'origine de dframe1 et faisons une inner_join avec dframe2.

dframe1 %>%
   mutate(date = as.Date(date), date1 = date) %>%
   group_by(id, name) %>%
   tidyr::complete(date1 = seq(date1 - 1, date1 + 1, by = "1 day")) %>%
   filter(date1 != date | is.na(date)) %>%
   select(-date) %>%
   mutate(col = c("before", "after")) %>%
   rename(date = 3) %>%
   inner_join(dframe2 %>% mutate(date = as.Date(date)))  

Pour ajouter un nouveau colonnes, nous pouvons ajouter une autre instruction mutate .

library(dplyr)

dframe1 %>%
  mutate(date = as.Date(date), date1 = date) %>%
  group_by(id, name) %>%
  tidyr::complete(date1 = seq(date1 - 1, date1 + 1, by = "1 day")) %>%
  filter(date1 != date | is.na(date)) %>%
  select(-date) %>%
  rename(date = 3) %>%
  inner_join(dframe2 %>% mutate(date = as.Date(date)))

#Joining, by = c("id", "name", "date")
# A tibble: 10 x 4
# Groups:   id, name [5]
#      id name   date       text_sth    
#   <int> <chr>  <date>     <chr>       
# 1     1 Amazon 2008-11-05 text here   
# 2     1 Google 2008-10-31 another text
# 3     1 Google 2008-11-02 test        
# 4     1 Google 2008-11-02 another text
# 5     1 Yahoo  2008-10-31 other       
# 6     1 Yahoo  2008-11-02 text_sth    
# 7     2 Amazon 2008-10-31 etc         
# 8     2 Amazon 2008-11-02 another text
# 9     2 Google 2008-11-01 test        
#10     2 Google 2008-11-03 text here 


3 commentaires

Y a-t-il une possibilité d'ajouter une colonne qui montrera si c'est une date antérieure ou postérieure?


@Nathalie a mis à jour la réponse. J'ai ajouté c ("avant", "après") mais vous pouvez choisir tout ce que vous voulez.


Merci. Une autre question. Si le dframe2 n'avait pas la colonne de nom, comment puis-je effectuer ce processus stackoverflow.com/questions/58643662/...



2
votes

Si je comprends bien, l'OP souhaite trouver les entrées correspondantes sur id , nom et la veille ou le lendemain. Par conséquent, une jointure non équi n'aidera pas car elle inclura des correspondances le jour même.

Je suggère d'effectuer deux jointures internes, une pour la veille et une seconde pour le lendemain en utilisant lapply () . Par la suite, les résultats sont combinés avec rbindlist () qui ajoute également une nouvelle colonne matching_day comme demandé par le PO :

    matching_day id       date   name     text_sth
 1:       before  1 2008-10-31 Google another text
 2:       before  1 2008-10-31  Yahoo        other
 3:        after  1 2008-11-02 Google         test
 4:        after  1 2008-11-02 Google another text
 5:        after  1 2008-11-02  Yahoo     text_sth
 6:        after  1 2008-11-05 Amazon    text here
 7:       before  2 2008-10-31 Amazon          etc
 8:       before  2 2008-11-01 Google         test
 9:        after  2 2008-11-02 Amazon another text
10:        after  2 2008-11-03 Google    text here
library(data.table)
library(magrittr)
setDT(dframe1)[, date := as.Date(date)]
setDT(dframe2)[, date := as.Date(date)]

lapply(
  c(-1, +1), 
  function(x) dframe2[dframe1[, .(id, name, date = date + x)], on = .(id, name, date), nomatch = 0L]
) %>%
  set_names(c("before", "after")) %>% 
  rbindlist(idcol = "matching_day") %>% 
  .[order(id)]


0 commentaires

0
votes

Une méthode de base R pourrait être de transformer dframe1 en un bloc de données dframe1a qui comprend déjà les dates souhaitées et merge () avec dframe2.

dframe1a <- do.call(rbind, lapply(1:nrow(dframe1), function(m) 
  cbind(dframe1[m, -3], date=as.matrix(dframe1[m, "date"] + c(-1, 1)), row.names=NULL)))
dframe1a$date <- as.Date(as.numeric(as.character(dframe1a$date)), origin="1970-01-01")
merge(dframe2, dframe1a)
#    id       date   name     text_sth
# 1   1 2008-10-31 Google another text
# 2   1 2008-10-31  Yahoo        other
# 3   1 2008-11-02 Google another text
# 4   1 2008-11-02 Google         test
# 5   1 2008-11-02  Yahoo     text_sth
# 6   1 2008-11-05 Amazon    text here
# 7   2 2008-10-31 Amazon          etc
# 8   2 2008-11-01 Google         test
# 9   2 2008-11-02 Amazon another text
# 10  2 2008-11-03 Google    text here

Remarque: Bien sûr, vos dates d'origine doivent être formatées comme telles, par exemple dframe1 $ date .


0 commentaires