#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)))
}
)
)