Я хочу вставить строки между двумя датами по группам. Мой способ сделать это настолько сложен, что я вставляю пропущенные значения при последнем наблюдении, переносе вперед и затем слиянии. Мне было интересно, есть ли более простой способ достичь этого.

# sample data 
user<-c("A","A","B","B","B") 
dummy<-c(1,1,1,1,1)
date<-as.Date(c("2017/1/3","2017/1/6","2016/5/1","2016/5/3","2016/5/5"))
dt<-data.frame(user,dummy,date) 

  user dummy       date
1    A     1 2017-01-03
2    A     1 2017-01-06
3    B     1 2016-05-01
4    B     1 2016-05-03
5    B     1 2016-05-05

Желаемый результат

enter image description here

4
News_is_Selection_Bias 1 Сен 2017 в 04:16

6 ответов

Лучший ответ

Используя dplyr и tidyr:) (однострочное решение)

library(dplyr)
library(tidyr)
dt %>% group_by(user) %>% complete(date=full_seq(date,1),fill=list(dummy=0))
# A tibble: 9 x 3
# Groups:   user [2]
    user       date dummy
  <fctr>     <date> <dbl>
1      A 2017-01-03     1
2      A 2017-01-04     0
3      A 2017-01-05     0
4      A 2017-01-06     1
5      B 2016-05-01     1
6      B 2016-05-02     0
7      B 2016-05-03     1
8      B 2016-05-04     0
9      B 2016-05-05     1
8
BEN_YO 1 Сен 2017 в 01:41

Я нашел самый простой способ сделать это с помощью библиотеки padr.

library(padr)
dt_padded <- pad(dt, group = "user", by = "date") %>%
  replace_na(list(dummy=0))
2
roarkz 1 Сен 2017 в 01:52

Мы можем использовать tidyverse для решения этой задачи.

library(tidyverse)

dt2 <- dt %>%
  group_by(user) %>%
  do(date = seq(from = min(.$date), to = max(.$date), by = 1)) %>%
  unnest() %>%
  left_join(dt, by = c("user", "date")) %>%
  replace_na(list(dummy = 0)) %>%
  select(colnames(dt))

dt2
# A tibble: 9 x 3
    user dummy       date
  <fctr> <dbl>     <date>
1      A     1 2017-01-03
2      A     0 2017-01-04
3      A     0 2017-01-05
4      A     1 2017-01-06
5      B     1 2016-05-01
6      B     0 2016-05-02
7      B     1 2016-05-03
8      B     0 2016-05-04
9      B     1 2016-05-05
2
www 1 Сен 2017 в 01:37

Ты можешь попробовать это

library(data.table)
setDT(dt)
tmp <- dt[, .(date = seq.Date(min(date), max(date), by = '1 day')), by = 
'user']
dt <- merge(tmp, dt, by = c('user', 'date'), all.x = TRUE)
dt[, dummy := ifelse(is.na(dummy), 0, dummy)]
2
myincas 1 Сен 2017 в 01:35

Решение Base R (не совсем элегантное):

# Data
user<-c("A","A","B","B","B") 
dummy<-c(1,1,1,1,1)
date<-as.Date(c("2017/1/3","2017/1/6","2016/5/1","2016/5/3","2016/5/5"))
df1 <-data.frame(user,dummy,date)

# Solution
do.call(rbind, lapply(split(df1, df1$user), function(df) {
  dff <- data.frame(user=df$user[1], dummy=0, date=seq.Date(min(df$date), max(df$date), 'day'))
  dff[dff$date %in% df$date, "dummy"] <- df$dummy[1]
  dff
}))


# user dummy date      
# A    1     2017-01-03
# A    0     2017-01-04
# A    0     2017-01-05
# A    1     2017-01-06
# B    1     2016-05-01
# B    0     2016-05-02
# B    1     2016-05-03
# B    0     2016-05-04
# B    1     2016-05-05
2
Abdou 1 Сен 2017 в 02:23

Предполагая, что ваши данные называются df1, и вы хотите добавить даты между двумя днями, попробуйте это:

library(dplyr)
df2 <- seq.Date(as.Date("2015-01-03"), as.Date("2015-01-06"), by ="day")
left_join(df2, df1)

Если вы просто пытаетесь добавить новую запись, я предлагаю использовать rbind.

rbind()
1
Joel Alcedo 1 Сен 2017 в 01:40