Как заменить выбросы на NA, имеющие определенный диапазон значений в R?

#r #replace #range #lapply #outliers

#r #заменить #диапазон #lapply #выбросы

Вопрос:

У меня есть климатические данные, и я пытаюсь заменить выбросы на NA . Я не использую boxplot(x)$out , потому что у меня есть диапазон значений, которые необходимо учитывать для вычисления выбросов.

 temp_range <- c(-15, 45)
wind_range <- c(0, 15)
humidity_range <- c(0, 100)
  

Мой фрейм данных выглядит следующим образом

df с выбросами

(Я выделил значения, которые следует заменить на NA в соответствии с диапазонами.)

Итак, temp1 и temp2 выбросы должны быть заменены на NA в соответствии с temp_range , wind выбросы должны быть заменены на NA в соответствии с wind_range и, наконец, humidity выбросы должны быть заменены на NA в соответствии с humidity_range .

Вот что у меня получилось:

 df <- read.csv2("http://pastebin.com/raw/vwqBu2M5", stringsAsFactors = FALSE)

df[,2:5] = apply(df[,2:5], 2, function(x) as.numeric(x))

#Ranges
temp_range <- c(-15, 45)
wind_range <- c(0, 15)
humidity_range <- c(0, 100)

#Function to detect outlier
in_interval <- function(x, interval){
  stopifnot(length(interval) == 2L)
  interval[1] <= x amp; x <= interval[2]
}


#Replace outliers according to temp_range
cols <- c('temp1', 'temp2')
df[, cols] <- lapply(df[, cols], function(x) {

  x[in_interval(x, temp_range)==FALSE] <- NA
  x
})
  

Я делаю последнюю часть кода (замену) для каждого диапазона. Есть ли способ упростить это, чтобы я мог избежать большого количества повторений?

Напоследок, допустим, cols <- c('wind') это выдает мне предупреждение и заменяет всю колонку wind константой.

 Warning message:
In `[<-.data.frame`(`*tmp*`, , cols, value = list(23.88, 23.93,  :
  provided 10 variables to replace 1 variables
  

Есть предложения?

Ответ №1:

Чтобы сделать это более динамично, используйте dictionnary: фрейм данных со значением выбросов, связанным с каждой переменной.

Здесь я создаю его в R, но было бы практичнее иметь его в csv, чтобы вы могли легко редактировать его.

 df <- read.csv2("http://pastebin.com/raw/vwqBu2M5", stringsAsFactors = FALSE)

df[,2:5] = apply(df[,2:5], 2, function(x) as.numeric(x))


df_dict <- data.frame(variable = c("temp1", "temp2", "wind", "humidity"), 
                       out_low = c(-15, -15, 0, 0), 
                       out_high =c(45, 45, 15, 100))

for (var in df_dict$variable) {

  df[[var]][df[[var]] < df_dict[df_dict$variable == var, ]$out_low | df[[var]] > df_dict[df_dict$variable == var, ]$out_high] <- NA

}
  

Ответ №2:

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

 df <- read.csv2("http://pastebin.com/raw/vwqBu2M5", stringsAsFactors = FALSE)

df[,2:5] = apply(df[,2:5], 2, function(x) as.numeric(x))

#Ranges
temp_range <- c(-15, 45)
wind_range <- c(0, 15)
humidity_range <- c(0, 100)

df$temp1[df$temp1 < temp_range[1] | df$temp1 > temp_range[2]] <- NA
df$temp2[df$temp2 < temp_range[1] | df$temp2 > temp_range[2]] <- NA
df$wind[df$wind < wind_range[1] | df$wind > wind_range[2]] <- NA
df$humidity[df$humidity < humidity_range[1] | df$humidity > humidity_range[2]] <- NA
  

По сути, все, что вы делаете, это берете переменную, создаете логический вектор, который выбирает только значения за пределами вашего диапазона, и заменяете эти значения на NA

Это даст вам следующее (что не совсем соответствует вашему изображению, но цифры кажутся правильными на основе ваших диапазонов):

                   time temp2 wind humidity temp1
1  2006-11-22 22:00:00    NA 0.00    56.95 23.88
2  2006-11-22 23:00:00  15.5 0.00    58.21 23.93
3  2006-11-23 00:00:00    NA   NA    62.95 23.81
4  2006-11-23 01:00:00  12.0 0.30    70.15    NA
5  2006-11-23 02:00:00  35.0 0.07    76.46 21.63
6  2006-11-23 03:00:00  12.0 0.79       NA 21.81
7  2006-11-23 04:00:00  35.0 0.50    69.11 21.04
8  2006-11-23 05:00:00  14.0 0.37    71.86 20.32
9  2006-11-23 06:00:00  -9.0 0.26    70.97 20.50
10 2006-11-23 07:00:00    NA 0.03    78.02    NA
  

Комментарии:

1. Я обновил изображение, результаты должны выглядеть как ваш вывод. Возможно ли выполнить первую и вторую строки замены в lapply? Они выглядят довольно похоже, и я опубликовал только пример, у меня больше столбцов на основе temp_range , так что количество строк замены будет расти, и я хочу сделать это более динамично.

Ответ №3:

Вы можете определить функцию,

 check_inRange <- function(col, range) {
   df[col] >= range[1] amp; df[col] <= range[2]
}
  

и затем для каждого столбца вы можете вызвать эту функцию как

 df[!check_inRange("temp1", temp_range), "temp1"] <- NA
df[!check_inRange("temp2", temp_range), "temp2"] <- NA
df[!check_inRange("wind", wind_range), "wind"] <- NA
df[!check_inRange("humidity", humidity_range), "humidity"] <- NA
  

Это заменило бы все значения в соответствующих столбцах, которые находятся вне диапазона, на NA

Комментарии:

1. В check_inRange функции условие должно быть df[col] >= range[1] amp; df[col] <= range[2] таким, чтобы 0 не заменялось, поскольку это не выброс в столбце wind (см. прикрепленное изображение)

2. @Martin хорошо .. обновил ответ. Также на изображении в temp2 столбце 35 не должно быть выделено. Поскольку это находится в диапазоне. (-15, 45) Не так ли?

3. Вы правы. Обновлено. Кстати, хорошее решение. Эта функция работает как шарм, но выглядит немного жестко, если у меня будет другой фрейм данных. Я буду работать над этим, поскольку я мог бы использовать ту же функцию, чтобы сначала отобразить выбросы и обусловить цвета, а после этого заменить их на NA.

4. Что ж, если у вас разные фреймы данных, вы также можете передать фрейм данных в параметр функции. Что-то вроде check_inRange <- function(col, range, df)