Замена повторяющихся значений с помощью нескольких условий в r

Я новичок в R, и у меня есть следующие данные (пример) в виде csv-файла, и я хочу заменить любые повторяющиеся значения, если они произошли в последовательные дни в течение аналогичного года и месяца нулем или буквой. Мне нужно только сохранить один средний.

Year    Month   Day Average
2013    8       28   2.3
2013    8       29   2.3
2013    8       30   1.7
2013    8       31   1.7
2014    8       7    3
2014    8       6    3
2014    8       8    3
2014    8       9    3
2014    9       11   5.8
2014    9       12   5.8
2014    9       13   5.8

Результат, который я ожидаю, что-то вроде этого

Year    Month   Day Average
2013    8       28   2.3
2013    8       29   0
2013    8       30   1.7
2013    8       31   0
2014    8       7    3
2014    8       6    0
2014    8       8    0
2014    8       9    0
2014    9       11   5.8
2014    9       12   0
2014    9       13   0

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

Year    Month   Day Average
2013    8       28   2.3
2013    8       30   1.7
2014    8       7    3
2014    9       11   5.8

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

Заранее спасибо!!

Метки

2 ответа

  1. Использование dplyr для данных.манипуляция кадром, lubridate для даты
    манипуляция и diffпоиск последовательных повторяющихся значений.

    Обратите внимание, что я также отсортировал даты, чтобы сохранить самый ранний, что делает его не совсем совпадают с примером решения.

    library(dplyr)
    
    ## 
    ## Attaching package: 'dplyr'
    
    ## The following objects are masked from 'package:stats':
    ## 
    ##     filter, lag
    
    ## The following objects are masked from 'package:base':
    ## 
    ##     intersect, setdiff, setequal, union
    
    library(lubridate)
    
    ## 
    ## Attaching package: 'lubridate'
    
    ## The following object is masked from 'package:base':
    ## 
    ##     date
    
    df1 <- read.table(
      text = "
        Year    Month   Day Average
        2013    8       28   2.3
        2013    8       29   2.3
        2013    8       30   1.7
        2013    8       31   1.7
        2014    8       7    3
        2014    8       6    3
        2014    8       8    3
        2014    8       9    3
        2014    9       11   5.8
        2014    9       12   5.8
        2014    9       13   5.8",
    header = T)
    
    df2 <- read.table(
      text = "
        Year    Month   Day Average
        2013    8       28   2.3
        2013    8       29   0
        2013    8       30   1.7
        2013    8       31   0
        2014    8       7    3
        2014    8       6    0
        2014    8       8    0
        2014    8       9    0
        2014    9       11   5.8
        2014    9       12   0
        2014    9       13   0",
    header = T)
    
    df3 <- read.table(
      text = "
        Year    Month   Day Average
        2013    8       28   2.3
        2013    8       30   1.7
        2014    8       7    3
        2014    9       11   5.8",
      header = T)
    
    df2 <- df1 %>%
      mutate(date = ymd(paste(Year, Month, Day, sep = "-"))) %>%
      arrange(date) %>%
      mutate(is_consecutive_average = c(FALSE, diff(Average) == 0)) %>%
      mutate(is_consecutive_day = c(FALSE, diff(date) == 1)) %>%
      mutate(Average = Average * !(is_consecutive_average & is_consecutive_day)) %>%
      select(-is_consecutive_average, -is_consecutive_day, -date)
    
    df2
    
    ##    Year Month Day Average
    ## 1  2013     8  28     2.3
    ## 2  2013     8  29     0.0
    ## 3  2013     8  30     1.7
    ## 4  2013     8  31     0.0
    ## 5  2014     8   6     3.0
    ## 6  2014     8   7     0.0
    ## 7  2014     8   8     0.0
    ## 8  2014     8   9     0.0
    ## 9  2014     9  11     5.8
    ## 10 2014     9  12     0.0
    ## 11 2014     9  13     0.0
    
    df3 <- df2 %>%
      filter(Average != 0)
    
    df3
    
    ##   Year Month Day Average
    ## 1 2013     8  28     2.3
    ## 2 2013     8  30     1.7
    ## 3 2014     8   6     3.0
    ## 4 2014     9  11     5.8
    
  2. Вот data.tableрешение:

    Читать в данных

    data <- readr::read_csv(
        text,
        col_names = TRUE,
        trim_ws = TRUE
    )
    
    library( data.table )
    setDT( data )
    

    Преобразование значений даты в более приятный формат и сортировка

    data[ , date := as.Date( paste0( Year, "-", Month, "-", Day ) ) ]
    setorder( data, date )
    

    Создание новых столбцов для предыдущих дат и средних значений

    data[ , prev.date := shift( date, 1L, type = "lag" ) ]
    data[ , prev.average := shift( Average, 1L, type = "lag" ) ]
    

    Отметьте точки, где должна быть создана новая «группа», исходя из ваших критериев. Также отметьте самую первую запись как начало новой группы, так как мы можем предположить, что это так.

    data[ , group := 0L
          ][ as.integer( date - prev.date ) > 1L |
             Average != prev.average, group := 1L 
             ][ 1L, group := 1L ]
    

    Получите первый желаемый результат, заменив определенные значения нулями

    data[ group != 1L, Average := 0 ]
    first.output <- data[ , .( date, Average ) ]
    head( first.output, 3 )
    
             date Average
    1: 2013-08-28     2.3
    2: 2013-08-29     0.0
    3: 2013-08-30     1.7
    

    Теперь отметьте группы как уникальные номера

    data[ , group := cumsum( group ) ]
    

    И получите свой второй выход, суммируя до максимального «среднего» значения (которое будет единственным, не равным нулю), и минимального значения «даты» (первое в этой группе):

    second.output <- data[ , .( date = min( date ),
                                Average = max( Average ) ), 
                           by = group ][ , .( date, Average ) ]
    
    head( second.output, 3 )
             date Average
    1: 2013-08-28     2.3
    2: 2013-08-30     1.7
    3: 2014-08-06     3.0
    

    Примечание: Вы , вероятно, могли бы получитьsecond.output, просто удалив строки с нулевым «средним» значением изfirst.output, Но это удалило бы любые группы, где «среднее» действительно равно нулю, поэтому я думаю, что этот метод безопаснее.