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))
3 Réponses :
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
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/...
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)]
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 .