排除两个变量之间的距离最近的重复行

时间:2019-02-06 10:14:06

标签: r data.table

我有重复的患者ID,对于一个变量(年龄),其行是相同的。但是,还有一个年龄检查变量,它说明哪一行可能是正确的。年龄最接近“年龄检查”的行是我要保留的行。因此,对于id = 3,值31比28更接近30。因此,我想删除包含age_check == 28的行。我想在R中使用data.table。

id <- c(1,2,3,3,4,5)
age <- c(20,20,30,30,35,40)
age_check <- c(20,20,31,28,35,40)
dat <- data.table(id,age,age_check) #Create the data.table I used

   id age age_check
1:  1  20        20
2:  2  20        20
3:  3  30        31
4:  3  30        28
5:  4  35        35
6:  5  40        40

#ID 3 contains a duplicate for which I'd like to keep row 3

输出应为:

   id age age_check
1:  1  20        20
2:  2  20        20
3:  3  30        31
5:  4  35        35
6:  5  40        40

我已经尝试/开始使用roll =功能和以下代码:

res <-  unique(dat[, .(id)])
res[, w := dat[c(.SD, age = age_check), on =.(id, age), roll= "nearest",   which=TRUE]]

该想法已经在较早的文章中提供,但不适用于一行中的值。 谢谢!

4 个答案:

答案 0 :(得分:1)

应该不言自明:

dat[, .SD[which.min(abs(age - age_check))], by = .(id, age)]
#   id age age_check
#1:  1  20        20
#2:  2  20        20
#3:  3  30        31
#4:  4  35        35
#5:  5  40        40

答案 1 :(得分:0)

您不必合并。您可以只从age中减去age_check,然后使该行保持最小值。 tidyverse解决方案是

library(tidyverse)

dat %>% 
 mutate(new = abs(age - age_check)) %>% 
 group_by(id) %>% 
 slice(which.min(new)) %>% 
 select(-new)

给出,

# A tibble: 5 x 3
# Groups:   id [5]
     id   age age_check
  <dbl> <dbl>     <dbl>
1     1    20        20
2     2    20        20
3     3    30        31
4     4    35        35
5     5    40        40

答案 2 :(得分:0)

您可以执行以下操作(假设对于所有非重复行,age_check等于年龄):

dat[, min_dist := abs(age-age_check) == min(abs(age-age_check)), by = id]
dat <- dat[min_dist == T][, min_dist := NULL]

> dat
   id age age_check
1:  1  20        20
2:  2  20        20
3:  3  30        31
4:  4  35        35
5:  5  40        40

如果未重复行的年龄检查并不总是等于年龄,则可以执行以下操作:

dat[, dup_id := duplicated(id) | duplicated(id, fromLast = T)] #find duplicates
dat[, min_dist := abs(age-age_check) == min(abs(age-age_check)), by = id]
dat <- dat[dup_id == F | min_dist == T][, c("dup_id", "min_dist") := NULL]

答案 3 :(得分:0)

使用tidyverse,如果没有重复的最近值(因为filter()返回具有给定值的所有行),则可以使用:

dat %>%
 group_by(id) %>%
 filter(abs(age-age_check) == min(abs(age-age_check)))

     id   age age_check
  <dbl> <dbl>     <dbl>
1    1.   20.       20.
2    2.   20.       20.
3    3.   30.       31.
4    4.   35.       35.
5    5.   40.       40.

如果还存在重复的最接近值,则可以尝试:

dat %>%
 mutate(temp = abs(age-age_check)) %>%
 group_by(id) %>%
 top_n(-1) %>%
 select(-temp)

或者,如果您更喜欢基数R:

do.call(rbind, by(dat, dat$id, function(x) x[which.min(abs(x$age-x$age_check)), ]))

   id age age_check
1:  1  20        20
2:  2  20        20
3:  3  30        31
4:  4  35        35
5:  5  40        40