【问题标题】:Find closest value between vectors in data frame by group逐组查找数据帧中向量之间的最接近值
【发布时间】:2019-11-01 17:50:14
【问题描述】:

我有一个数据集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,在该列中我发现每个人中最接近 visit_idtrue_visit。结果如下所示:

# 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 是个人 a 的 3,因为它是唯一的 true_visitclosest_visit 是第七行的 1,因为 0(该行的 visit_id)比 7(该参与者的 true_visits)更接近 1,依此类推。

我尝试查看hereherehere。他们是一个好的开始,但并不是我想要的。有任何想法吗?

【问题讨论】:

  • 为什么第七行的closest_visit 是1?另外,为什么每个 id b 有 3 个 closest_visit 值?
  • rep(letters[1:3], each = 3) 避免使用sort
  • @tmfmnk 为第七行(id c),abs(0 - c(1,7)) = c(1, 7),其中最小的是1,对应true_visit == 1。对于idb,他们的每一次真实访问都匹配一个不同的访问ID,比如abs(5 - c(0,5,10)) = c(5, 0, 5),最小的是0,对应true_visit == 5。这有意义/回答你的问题吗?
  • 并非如此。不是要为每个 ID 找到一个最接近的值吗?还有,为什么第九排是7?
  • 第 9 行有 7,因为 10(该行的访问 id)比 1(该个人的真实访问)更接近 7

标签: r


【解决方案1】:

可以去:

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

【讨论】:

    【解决方案2】:

    一个选项是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
    

    【讨论】:

    • 谢谢!您介意告诉我您使用的是什么版本的 tidyr 吗?我正在使用 tidyr 版本 0.8.3,.direction 的唯一选项是 c("up", "down")
    • @FelixT。我用packageVersion('dplyr') [1] ‘0.8.3’ &gt; packageVersion('tidyr') [1] ‘1.0.0’
    【解决方案3】:

    这不是最漂亮的方式,但它适用于您的示例:

    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))]
          }
      }
    

    【讨论】:

      猜你喜欢
      • 2019-06-01
      • 2023-03-06
      • 1970-01-01
      • 1970-01-01
      • 2021-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多