Заменить значения в одном столбце на основе вектора, условно совпадающего с другим столбцом - PullRequest
0 голосов
/ 29 августа 2018

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

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

  badData <- c(296:310, 330:335, 350:565)

  df <- data.frame(wavelength = seq(300,360,5.008667),
                  reflectance = seq(-1,-61,-5.008667))

df 

   wavelength reflectance
   300.0000   -1.000000
   305.0087   -6.008667
   310.0173  -11.017334
   315.0260  -16.026001
   320.0347  -21.034668
   325.0433  -26.043335
   330.0520  -31.052002
   335.0607  -36.060669
   340.0693  -41.069336
   345.0780  -46.078003
   350.0867  -51.086670
   355.0953  -56.095337

Я пытался

   Data2 <- df %>% 
  mutate(reflectance = replace(reflectance,wavelength %in% badData, NA))

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

Выходной набор данных может быть из-за того, что длины волн 300.000 и 305.0087 лежат между 296 и 310, длина волны 330.05620 между 330 и 335 и 350.0867 и 355.0953 падают между 350: 565.

 wavelength reflectance
   300.0000   NA
   305.0087   NA
   310.0173  -11.017334
   315.0260  -16.026001
   320.0347  -21.034668
   325.0433  -26.043335
   330.0520  NA
   335.0607  -36.060669
   340.0693  -41.069336
   345.0780  -46.078003
   350.0867  NA
   355.0953  NA

Ответы [ 4 ]

0 голосов
/ 29 августа 2018

Как насчет dplyr::between()?

library(dplyr)

df %>% 
  mutate(
    reflectance = case_when(
      between(wavelength, 296, 310) ~ NA_real_,
      between(wavelength, 330, 335) ~ NA_real_,
      between(wavelength, 350, 565) ~ NA_real_,
      TRUE                          ~ reflectance
    )
  )
0 голосов
/ 29 августа 2018

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

badData <- list(c(296,310), c(330,335), c(350,565))

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

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

within_ranges <- function(x, lims)  {
  Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <= lim[2]))
}

Чтобы понять, что это делает, давайте отладим его, позвоним и посмотрим, что происходит.

debugonce(within_ranges)
within_ranges(df$wavelength, badData)
# debugging in: within_ranges(df$wavelength, badData)
# debug at #1: {
#     Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <= 
#         lim[2]))
# }

Давайте просто запустим эту внутреннюю часть:

# Browse[2]> 
lapply(lims, function(lim) lim[1] <= x & x <= lim[2])
# [[1]]
#  [1]  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [[2]]
#  [1] FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
# [[3]]
#  [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE

Таким образом, первый элемент (T, T, F, F, ...) состоит в том, попадают ли значения (x) в первую пару чисел (от 296 до 310); второй элемент со второй парой (от 330 до 335); и т.д.

Часть Reduce( вызывает первый аргумент, функцию, для первых двух аргументов, сохраняет возвращаемое значение, а затем выполняет ту же функцию для возврата и третьего аргумента. Он сохраняет его, затем выполняет ту же функцию для возврата и четвертого аргумента (если существует). Это повторяется по всей длине предоставленного списка.

В этом примере функция - это литерал | (экранированный, поскольку он особенный), поэтому он "ИЛИ" использует вектор [[1]] с вектором [[2]]. Вы действительно можете увидеть, что происходит, если вы добавите accumulate=TRUE:

# Browse[2]> 
Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <= lim[2]), accumulate=TRUE)
# [[1]]
#  [1]  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [[2]]
#  [1]  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
# [[3]]
#  [1]  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE  TRUE

Первый возврат - первый вектор, без изменений. Второй элемент - это оригинальный [[2]] вектор ИЛИ с предыдущим возвратом, который является этим [[1]] вектором (который совпадает с исходным [[1]]). Третий элемент - это оригинальный [[3]] вектор ИЛИ с предыдущим возвратом, который равен this [[2]]. Это приводит к трем группам TRUE (1, 2, 7, 11, 12), которые вы ожидаете. Итак, нам нужен элемент [[3]], который мы получаем без накопления:

# Browse[2]> 
Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <= lim[2]))
#  [1]  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE  TRUE

Хорошо, так что давайте Q выйдем из отладчика и дадим ему полный ход:

within_ranges(df$wavelength, badData)
#  [1]  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE  TRUE

Этот вывод выглядит знакомым.

(Кстати: внутри нашей функции мы могли бы также использовать

rowSums(sapply(lims, ...)) > 0

и это сработало бы так же хорошо. Для этого, однако, вы должны понимать, что sapply должен возвращать matrix с таким количеством столбцов, сколько у нас есть строк данных в df, странно, если вы не знакомы.)

Теперь мы можем NA, если нам нужно, либо dplyr:

df %>%
  mutate(
    reflectance = if_else(within_ranges(wavelength, badData), NA_real_, reflectance)
  )
#    wavelength reflectance
# 1    300.0000          NA
# 2    305.0087          NA
# 3    310.0173   -11.01733
# 4    315.0260   -16.02600
# 5    320.0347   -21.03467
# 6    325.0433   -26.04333
# 7    330.0520          NA
# 8    335.0607   -36.06067
# 9    340.0693   -41.06934
# 10   345.0780   -46.07800
# 11   350.0867          NA
# 12   355.0953          NA

Редактировать : или другой dplyr, используя вашу первую мысль о replace (не моя первая по привычке, без причины):

df %>%
  mutate(
    reflectance = replace(reflectance, within_ranges(wavelength, badData), NA_real_)
  )

или основание R:

df$reflectance <- ifelse(within_ranges(df$wavelength, badData), NA_real_, df$reflectance)
df
#    wavelength reflectance
# 1    300.0000          NA
# 2    305.0087          NA
# 3    310.0173   -11.01733
# 4    315.0260   -16.02600
# 5    320.0347   -21.03467
# 6    325.0433   -26.04333
# 7    330.0520          NA
# 8    335.0607   -36.06067
# 9    340.0693   -41.06934
# 10   345.0780   -46.07800
# 11   350.0867          NA
# 12   355.0953          NA

Примечания:

  • Я специально использую NA_real, как для ясности (знаете ли вы, что существуют разные типы NA?), Так и отчасти потому, что при использовании dplyr::if_else он будет жаловаться / не работать, если классы аргументы "истина" и "ложь" не совпадают (NA технически logical, а не numeric, как ваш reflectance);
  • Я использую dplyr::if_else для первого примера, так как вы уже используете dplyr, но в случае, если вы решите отказаться от dplyr (или кто-то еще), тогда работает base-R ifelse, тоже. (У него есть свои обязательства, но, похоже, он отлично работает здесь.)
0 голосов
/ 29 августа 2018

Вот решение, основанное на создании кадра данных для badData и tidyr::crossing. Используя crossing, мы можем получить все комбинации между двумя фреймами данных.

badData <- data.frame(start= c(296,330,350),end=c(310.01,335,565))

library(dplyr)
library(tidyr)
library(data.table)

df %>% crossing(badData) %>% 
       mutate(Flag=ifelse(data.table::between(wavelength,start,end),1,0)) %>% 
       arrange(wavelength,desc(Flag)) %>% #Make sure 1 'if exist' at the 1st row for each wavelength before run distinct
       distinct(wavelength,.keep_all=T) %>%
       mutate(reflectance_upd=ifelse(Flag==1,NA,reflectance))

    wavelength reflectance start    end Flag reflectance_upd
1    300.0000   -1.000000   296 310.01    1              NA
2    305.0087   -6.008667   296 310.01    1              NA
3    310.0173  -11.017334   296 310.01    0       -11.01733
4    315.0260  -16.026001   296 310.01    0       -16.02600
5    320.0347  -21.034668   296 310.01    0       -21.03467
6    325.0433  -26.043335   296 310.01    0       -26.04333
7    330.0520  -31.052002   330 335.00    1              NA
8    335.0607  -36.060669   296 310.01    0       -36.06067
9    340.0693  -41.069336   296 310.01    0       -41.06934
10   345.0780  -46.078003   296 310.01    0       -46.07800
11   350.0867  -51.086670   350 565.00    1              NA
12   355.0953  -56.095337   350 565.00    1              NA
0 голосов
/ 29 августа 2018

Я думаю, это поможет.

    library(TeachingDemos)
    df$reflectance <- ifelse(296 %<% df$wavelength %<% 310 | 330 %<% df$wavelength %<% 335 | 350 %<% df$wavelength %<% 565, NA, df$reflectance) 

> df
   wavelength reflectance
1    300.0000          NA
2    305.0087          NA
3    310.0173   -11.01733
4    315.0260   -16.02600
5    320.0347   -21.03467
6    325.0433   -26.04333
7    330.0520          NA
8    335.0607   -36.06067
9    340.0693   -41.06934
10   345.0780   -46.07800
11   350.0867          NA
12   355.0953          NA
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...