столбец мутирования dplyr с ближайшим значением во внешнем списке - PullRequest
0 голосов
/ 16 ноября 2018

Я пытаюсь изменить столбец и заполнить его точными совпадениями из списка, если они есть, а если нет, то наиболее близкое совпадение.

Мой фрейм данных выглядит так:

index <- seq(1, 10, 1)
blockID <- c(100, 120, 132, 133, 201, 207, 210, 238, 240, 256)
df <- as.data.frame(cbind(index, blockID))

   index blockID
1      1     100
2      2     120
3      3     132
4      4     133
5      5     201
6      6     207
7      7     210
8      8     238
9      9     240
10    10     256

Я хочу mutate новый столбец, который проверяет, есть ли blockID в списке. Если да, то следует просто сохранить значение blockID. Если нет, он должен вернуть ближайшее значение в blocklist:

blocklist <- c(100, 120, 130, 150, 201, 205, 210, 238, 240, 256) 

, поэтому дополнительный столбец должен содержать

100 (match), 
120 (match), 
130 (no match for 132--nearest value is 130), 
130 (no match for 133--nearest value is 130), 
201, 
205 (no match for 207--nearest value is 205), 
210, 
238, 
240, 
256 

Вот что я пробовал:

df2 <- df %>% mutate(blockmatch = ifelse(blockID %in% blocklist, blockID, ifelse(match.closest(blockID, blocklist, tolerance = Inf), "missing")))

Я просто вставил "missing", чтобы завершить операторы ifelse(), но на самом деле его нигде не нужно возвращать, поскольку предыдущие значения будут выполняться для каждого значения blockID. Однако полученный df2 просто «отсутствует» во всех ячейках, где он должен был заменить ближайшее число. Я знаю, что есть базовые R альтернативы match.closest, но я не уверен, что это проблема. Есть идеи?

1 Ответ

0 голосов
/ 17 ноября 2018

Вам не нужно if..else. Ваше правило можно упростить, сказав, что мы всегда получаем элемент blocklist с наименьшей абсолютной разницей по сравнению с blockID. Если значения совпадают, то абсолютная разница равна 0 (что всегда будет наименьшим).

С этим вот простое базовое решение R -

df$blockmatch <- sapply(df$blockID, function(x) blocklist[order(abs(x - blocklist))][1])

   index blockID blockmatch
1      1     100        100
2      2     120        120
3      3     132        130
4      4     133        130
5      5     201        201
6      6     207        205
7      7     210        210
8      8     238        238
9      9     240        240
10    10     256        256

Вот несколько способов с dplyr -

df %>% 
  rowwise() %>% 
  mutate(
    blockmatch = blocklist[order(abs(blockID - blocklist))][1]
  )

df %>% 
  mutate(
    blockmatch = sapply(blockID, function(x) blocklist[order(abs(x - blocklist))][1])
  )

Благодаря @Onyambu, вот более быстрый способ -

df$blockmatch <- blocklist[max.col(-abs(sapply(blocklist, '-', df$blockID)))]
...