#r
Вопрос:
Если у кого-нибудь найдется минутка, чтобы помочь… Что я хотел бы сделать, так это следующее с приведенным ниже фреймом данных.
time look category
150 left B1
170 right B1
100 left B1
100 away A1
70 left A1
400 right A1
100 left A1
300 right A2
100 left A2
100 right A2
100 left B1
150 right B1
200 away B1
100 left B1
Я хотел бы создать новый фрейм данных, который:
- Удаляет стандартное произвольное значение, например 200, в столбце время
- Это вычитание происходит только один раз, начиная с первого экземпляра группы в категории
- Это происходит только для групп, начинающихся с
- Например, глядя на A1. Если бы мы удалили 200, это означало бы, что первые две строки A1 удаляются из фрейма данных, а 30 удаляются из 400. Обратите внимание на изменение в приведенном ниже фрейме данных.
- A2: удалите 200 из первого экземпляра A2 и времени, что означает, что 300 становится 100. Ни одна строка не была удалена, потому что время было 300.
- Главное, чтобы порядок оставался прежним.
Это должно выглядеть так:
time look category
150 left B1
170 right B1
100 left B1
370 right A1
100 left A1
100 right A2
100 left A2
100 right A2
100 left B1
150 right B1
200 away B1
100 left B1
Я понятия не имею, с чего начать, так что любое понимание было бы удивительным.
Правка № 1: Мы хотим вычесть это значение arb только из групп, которые начинаются с A. Таким образом, группы, начинающиеся с B, останутся неизменными.
Ответ №1:
Вы можете попробовать
library(dplyr)
library(data.table)
df %>%
group_by(data.table::rleid( category)) %>%
mutate(ctime = cumsum(time)) %>%
mutate(val1 = ifelse(startsWith(category, "A"),ctime - 200, ctime )) %>%
filter(val1>0) %>%
mutate(time = val1 - ifelse(is.na(lag(val1)), 0, lag(val1))) %>%
ungroup %>%
select(time, look, category)
time look category
<dbl> <chr> <chr>
1 150 left B1
2 170 right B1
3 100 left B1
4 370 right A1
5 100 left A1
6 100 right A2
7 100 left A2
8 100 right A2
9 100 left B1
10 150 right B1
11 200 away B1
12 100 left B1
Комментарии:
1. Привет, спасибо, что ответили! Мне было интересно, как вы могли бы изменить это так, чтобы оно вычиталось только из групп, начинающихся только с A?
2. @anonymous Одна вещь, которую я имею в виду, — это разделить данные, начиная с » А » и «не», применить приведенный выше код и снова объединить. Я попробую это сделать и отвечу еще раз
3. @anonymous Я редактирую свой код выше. Пожалуйста, проверьте это.
Ответ №2:
Подход tidyverse
:
library(tidyverse)
value <- 200
df %>%
separate(category, into = c("cat1", "cat2"), sep=1) %>%
group_by(cat1, cat2) %>%
mutate(aux = ifelse(cat1 == "A", cumsum(time)-value, time),
time = if_else(aux > 0 , pmin(time, aux), aux)) %>%
ungroup %>% filter(time > 0) %>%
unite(category, c(cat1,cat2), sep="") %>%
select(-aux)
#> # A tibble: 12 × 3
#> time look category
#> <dbl> <chr> <chr>
#> 1 150 left B1
#> 2 170 right B1
#> 3 100 left B1
#> 4 370 right A1
#> 5 100 left A1
#> 6 100 right A2
#> 7 100 left A2
#> 8 100 right A2
#> 9 100 left B1
#> 10 150 right B1
#> 11 200 away B1
#> 12 100 left B1
Это еще одно решение (без data.table
), которое использует функцию f
, которая , учитывая значение для вычитания и категорию, выполняет вычитание для этой категории. Функция reduce
выполняет итерацию по необходимым категориям.
library(tidyverse)
f <- function(df, value, cat)
{
df <- mutate(df,id=1:nrow(df))
df %>%
filter(category == cat) %>%
mutate(cvalue=c(value,rep(0,nrow(.)-1))) %>%
mutate(extra=cumsum(time-cvalue)) %>%
mutate(time = ifelse(extra <= 0,0,pmin(time,extra))) %>%
{mutate(df, time=replace(time, id %in% .$id, .$time))} %>%
filter(time > 0) %>%
select(!id)
}
a <- str_extract(df$category,regex("^A.*")) %>%
{unique(.[!is.na(.)])}
reduce(a,function(x,y) f(x,200,y), .init = df)
Вывод:
time look category
1 150 left B1
2 170 right B1
3 100 left B1
4 370 right A1
5 100 left A1
6 100 right A2
7 100 left A2
8 100 right A2
9 100 left B1
10 150 right B1
11 200 away B1
12 100 left B1