#r
#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
У меня должно быть два файла, в одном из которых повторяющиеся значения заменены на ноль или букву, а в другом — только средние значения без повторяющихся значений.
Заранее благодарю вас!!
Комментарии:
1. Пожалуйста, рассмотрите возможность использования
dput
или чего-то подобного для обмена вашими данными, это значительно упрощает помощь.2. В течение двух последовательных дней, если данные отличаются, то это имеет смысл, но если они округляются до одного и того же числа, вы отбрасываете их? Я не знаю базовых данных, но похоже, что вы будете выбрасывать потенциально хорошие данные. Кроме того, имеет ли значение порядок? Вы сохраняете более раннее среднее значение для всех, кроме 2014/8/6.
Ответ №1:
Используя dplyr для обработки данных.frame, 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 amp; 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
Комментарии:
1. Моя ошибка исправлена.
Ответ №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
, но это приведет к удалению любых групп, где «среднее» действительно равно нулю, поэтому я думаю, что этот метод безопаснее.