Вычтите значение на основе первого экземпляра группы в другом столбце

#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