Мне нужно создать фиктивную переменную между двумя датами, равными 1

#r #dataframe #dplyr

#r #dataframe #dplyr

Вопрос:

У меня есть данные следующего типа:

 group<-as.character(c("A","A","A","A","B","B","B","B"))
rain_start<-c(1,0,0,0,0,1,0,0)
rain_end<-c(0,0,1,0,0,1,0,0)
day<-c(1,2,3,4,1,2,3,4)
data<-as.data.frame(cbind(group,rain_start,rain_end,day))
  

которое производит:

  -------- ------------- ----------- ----- -- 
|        |             |           |     |  |
 -------- ------------- ----------- ----- -- 
| group  | rain_start  | rain_end  | day |  |
| A      | 1           | 0         | 1   |  |
| A      | 0           | 0         | 2   |  |
| A      | 0           | 1         | 3   |  |
| A      | 0           | 0         | 4   |  |
| B      | 0           | 0         | 1   |  |
| B      | 1           | 0         | 2   |  |
| B      | 0           | 1         | 3   |  |
| B      | 0           | 0         | 4   |  |
 -------- ------------- ----------- ----- -- 
  

Теперь я хотел бы, для каждой группы, чтобы в одном столбце было указано, шел дождь или нет. Итак:

 rain<-c(1,1,1,0,1,1,0,0)

data2<-as.data.frame(cbind(group,rain,day))
data2
  

которое производит:

  ------- ------ ------ -- -- 
| group | rain |  day |  |  |
 ------- ------ ------ -- -- 
| A     |    1 |    1 |  |  |
| A     |    1 |    2 |  |  |
| A     |    1 |    3 |  |  |
| A     |    0 |    4 |  |  |
| B     |    1 |    1 |  |  |
| B     |    1 |    2 |  |  |
| B     |    0 |    3 |  |  |
| B     |    0 |    4 |  |  |
 ------- ------ ------ -- -- 
  

Я пробовал mutate() и ifelse в dplyr, но есть проблема с несовпадением векторов.

Комментарии:

1. Как у вас есть 1 во второй строке, если начало и конец равны нулю???

2. Просто примечание — as.data.frame(cbind(... это не очень хорошая практика, поскольку она преобразует все в character . Попробуйте data.frame(group,rain_start... вместо этого сохранить класс, предназначенный для каждого столбца.

3. @Duck — Я думаю, что смысл в том, чтобы заполнить промежуток между начальной и конечной строками.

4. Но в случае B первая строка в этой группе равна двойному нулю!

5. Это имеет смысл, но я все еще не понимаю, почему B , 1 сегодня дождливый день. Дождь не начался и не закончился в этот день, и не начался в любой предыдущий день для этой группы.

Ответ №1:

Предполагая, что ваш rain_start начинается с 1-го дня для группы B, вы можете сделать это следующим образом :

 library(dplyr)

data %>%
  group_by(group) %>%
  mutate(rain = as.integer(between(row_number(), 
                           match(1, rain_start), match(1, rain_end)))) %>%
  select(group, rain, day)

#  group  rain   day
#  <chr> <int> <dbl>
#1 A         1     1
#2 A         1     2
#3 A         1     3
#4 A         0     4
#5 B         1     1
#6 B         1     2
#7 B         0     3
#8 B         0     4
  

Мы преобразуем все значения в 1, где row_number() находится между первым значением, где rain_start = 1 и первым значением, где rain_end = 1 .

Комментарии:

1. Идеально! Большое вам спасибо. Я понятия не имел о команде match.

Ответ №2:

Следуя действительному предложению от @andrew_reece, вот решение с использованием условных выражений и tidyverse функций:

 library(tidyverse)
#Code 1
data %>% group_by(group) %>%
  mutate(Rain=ifelse(rain_start==1|rain_end==1,1,NA)) %>%
  fill(Rain,.direction = 'up') %>%
  replace(is.na(.),0)
  

Вывод:

 # A tibble: 8 x 5
# Groups:   group [2]
  group rain_start rain_end   day  Rain
  <fct>      <dbl>    <dbl> <dbl> <dbl>
1 A              1        0     1     1
2 A              0        0     2     1
3 A              0        1     3     1
4 A              0        0     4     0
5 B              1        0     1     1
6 B              0        1     2     1
7 B              0        0     3     0
8 B              0        0     4     0
  

Некоторые используемые данные:

 #Data
data <- structure(list(group = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 
2L), .Label = c("A", "B"), class = "factor"), rain_start = c(1, 
0, 0, 0, 1, 0, 0, 0), rain_end = c(0, 0, 1, 0, 0, 1, 0, 0), day = c(1, 
2, 3, 4, 1, 2, 3, 4)), class = "data.frame", row.names = c(NA, 
-8L))
  

Ответ №3:

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

 group <- rep(c("A","B","C"), c(4,4,8))
rain_start <- c(1,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0)
rain_end <- c(0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,1)
day <- sequence(c(4,4,8))

data <- data.frame(group, rain_start, rain_end, day)
  

Затем код для пометки дождливых дней:

 library(data.table)
setDT(data)
data[, rain := as.integer(!(cumsum(rain_start) - 
               rleid(rev(cumsum(rev(rain_end)))))), by=group]

#    group rain_start rain_end day rain
# 1:     A          1        0   1    1
# 2:     A          0        0   2    1
# 3:     A          0        1   3    1
# 4:     A          0        0   4    0
# 5:     B          0        0   1    0
# 6:     B          1        1   2    1
# 7:     B          0        0   3    0
# 8:     B          0        0   4    0
# 9:     C          0        0   1    0
#10:     C          1        0   2    1
#11:     C          0        0   3    1
#12:     C          0        1   4    1
#13:     C          0        0   5    0
#14:     C          1        0   6    1
#15:     C          0        0   7    1
#16:     C          0        1   8    1
  

Ответ №4:

Вот решение, использующее group_by и group_modify , которое выполняет итерацию по каждому из них rain_state в каждой группе и переключает, идет ли дождь в виде значений в новом столбце, raining .

 data %>%
  group_by(group) %>%
  group_modify(function(d, grp) {
    rain_state <- 0 
    d$raining <- 0

    for (i in 1:nrow(d)) {

      if (d$rain_start[i] == 1) {
        rain_state <- 1
        d$raining[i] <- 1

      } else if (d$rain_end[i] == 1) {
        rain_state <- 0
        d$raining[i] <- 1

      } else if (rain_state == 1) d$raining[i] <- 1
    }
    return (d %>% select(raining, day))
  })

# A tibble: 8 x 3
# Groups:   group [2]
  group raining day  
  <fct>   <dbl> <fct>
1 A           1 1    
2 A           1 2    
3 A           1 3    
4 A           0 4    
5 B           1 1    
6 B           1 2    
7 B           0 3    
8 B           0 4  
  

Примечание: Я предполагаю, что rain_start на самом деле есть опечатка, учитывая ваш ожидаемый результат, и что вы предполагали, что в день 1 группы B начнется дождь, вот так:

 rain_start<-c(1,0,0,0,1,0,0,0)
  

Ответ №5:

Базовое решение R:

 do.call("rbind", lapply(split(type.convert(df), df$group), function(x) {
  within(x, rain <- ifelse(rain_end == 1 amp; rain_start == 1, TRUE,
                      c(as.logical(rain_start[1]), diff(cumsum(rain_start) -
                                    c(0, rain_end[-nrow(x)])) == 0)))
    }
  )
)