Найти ближайшее значение между векторами в фрейме данных по группе - PullRequest
3 голосов
/ 01 ноября 2019

У меня есть набор данных df:

df <- tibble(
  id = sort(rep(letters[1:3], 3)),
  visit_id = rep(c(0, 5, 10), 3),
  true_visit = c(NA, 3, NA, 0, 5, 10, 1, 7, NA)
)
> df
# A tibble: 9 x 3
  id    visit_id true_visit
  <chr>    <dbl>      <dbl>
1 a            0         NA
2 a            5          3
3 a           10         NA
4 b            0          0
5 b            5          5
6 b           10         10
7 c            0          1
8 c            5          7
9 c           10         NA

Я пытаюсь создать новый столбец closest_visit, где я нахожу true_visit, ближайший к visit_id внутри каждого человека. Результат будет выглядеть так:

# A tibble: 9 x 4
  id    visit_id true_visit closest_visit
  <chr>    <dbl>      <dbl>         <dbl>
1 a            0         NA             3
2 a            5          3             3
3 a           10         NA             3
4 b            0          0             0
5 b            5          5             5
6 b           10         10            10
7 c            0          1             1
8 c            5          7             7
9 c           10         NA             7

Чтобы уточнить, closest_visit равен 3 для отдельного a, потому что это единственный true_visit. closest_visit равно 1 для седьмой строки, потому что 0 (visit_id для этой строки) ближе к 1, чем к 7 (true_visit s для этого участника) и т. Д.

Я пытался посмотреть здесь , здесь и здесь . Они были хорошим началом, но не совсем то, что я ищу. Есть идеи?

Ответы [ 3 ]

2 голосов
/ 01 ноября 2019

Может пойти на:

library(dplyr)

df %>%
  group_by(id) %>%
  mutate(
    closest_visit = case_when(
      visit_id == true_visit ~ true_visit,
      TRUE ~ true_visit[sapply(visit_id, 
                               function(x) which.min(abs(x - true_visit)))]
      )
    )

Вывод:

# A tibble: 9 x 4
# Groups:   id [3]
  id    visit_id true_visit closest_visit
  <chr>    <dbl>      <dbl>         <dbl>
1 a            0         NA             3
2 a            5          3             3
3 a           10         NA             3
4 b            0          0             0
5 b            5          5             5
6 b           10         10            10
7 c            0          1             1
8 c            5          7             7
9 c           10         NA             7
1 голос
/ 01 ноября 2019

Это не самый красивый способ, но он работает на вашем примере:

library(dplyr)

for (id in unique(df$id) ) {

  available_visit = na.omit(df[df$id == id ,'true_visit']) %>% pull()
  unique_id = unique(df$visit_id[df$id == id])

    for (visit_id in unique_id) {

      df[df$id == id & df$visit_id ==  visit_id, 'closest_visit' ] <- 
        available_visit[which.min(abs(available_visit-visit_id))]
      }
  }
1 голос
/ 01 ноября 2019

Один вариант - findInterval, а затем fill

library(dplyr)
library(tidyr)
df %>%
   group_by(id) %>% 
   mutate(closest_visit = na.omit(true_visit)[findInterval(true_visit, 
          visit_id)]) %>% 
   fill(closest_visit, .direction = "updown")
# A tibble: 9 x 4
# Groups:   id [3]
#  id    visit_id true_visit closest_visit
#  <chr>    <dbl>      <dbl>         <dbl>
#1 a            0         NA             3
#2 a            5          3             3
#3 a           10         NA             3
#4 b            0          0             0
#5 b            5          5             5
#6 b           10         10            10
#7 c            0          1             1
#8 c            5          7             7
#9 c           10         NA             7
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...