R:如何根据另一个变量中的值选择dplyr::distinct()保留的行?

R:如何根据另一个变量中的值选择dplyr::distinct()保留的行?,r,dplyr,unique,distinct-values,R,Dplyr,Unique,Distinct Values,现实生活中的问题:我的受试者有核磁共振扫描数据。其中一些已被扫描多次(单独的行)。其中一些人每次都按照不同的协议进行扫描。我想按主题ID保留所有唯一的行,如果一个主题是在两种不同的协议下扫描的,我希望它更喜欢其中一种 玩具示例: library(dplyr) df <- tibble( id = c("A", "A", "B", "C", "C", "D"), protocol = c("X", "Y", "X", "X", "X", "Y"),

现实生活中的问题:我的受试者有核磁共振扫描数据。其中一些已被扫描多次(单独的行)。其中一些人每次都按照不同的协议进行扫描。我想按主题ID保留所有唯一的行,如果一个主题是在两种不同的协议下扫描的,我希望它更喜欢其中一种

玩具示例:

library(dplyr)  
df <- tibble(
        id = c("A", "A", "B", "C", "C", "D"), 
        protocol = c("X", "Y", "X", "X", "X", "Y"),
        date = c(seq(as.Date("2018-01-01"), as.Date("2018-01-06"), 
                 by="days")),
        var = 1:6)
如果我有多个协议,并且希望分配一个选择它们的顺序,我可以创建一个新变量,在这个变量中,我按照优先顺序为协议分配一个整数,然后使用@joran的建议

df %>% group_by(id) %>% arrange(desc(protocol),var) %>% slice(1)  

谢谢

可能有一种更快的方法(几乎可以肯定是使用data.table),但这将是dplyr中天真的直接方法,我认为:

df %>% group_by(id) %>% arrange(desc(protocol),var) %>% do(head(.,1))

正如@Gregor在下面提到的(现已删除),
slice(1)
对于
do(head(,1))
来说可能是一个更好的习惯用法,如果您希望输出是一个不是分组数据的TIBLE,则无需使用
group_by()

df %>% arrange(id, desc(protocol)) %>% distinct(id, .keep_all = TRUE)

您可以将此过程分为两个步骤:获取必备项,获取其他ID的任何内容,然后合并

distinct_y <- df %>%
  filter(protocol == "Y") %>%
  distinct(id, .keep_all = TRUE)

distinct_other <- df %>%
  anti_join(distinct_y, "id") %>%
  distinct(id, .keep_all = TRUE)

distinct_combined <- rbind(distinct_y, distinct_other)

按字母顺序排列在所述的简单情况下有效,但如果需要,可以添加一个
protocol\u preference
变量,以便在
Y
不可用的情况下给出您希望选择的顺序,并选择“Y”,即使按字母顺序排序时它不是最后一个协议值

基于@davechilders answer和@Nathan Werth基于“重要性顺序”向量创建因子的想法


在这种情况下,按字母顺序对协议变量进行排序是很方便的,但是在更一般的情况下,例如,如果有一个“Z”协议,但仍然需要选择“Y”,则可以执行
df%>%arrange(id,desc(protocol='Y'))%%>%distinct(id,.keep_all=TRUE)
@RobJensen,这是最简单的答案(到目前为止)。如果你把它作为一个答案发布,我可以选择它。谢谢!当我拿出desc()时,它起了作用。我的真实数据有多个不同的协议和不同的名称。我可以做的是创建另一个变量,在该变量中,我根据偏好为每个协议分配一个数字,然后根据该数字进行排序,并对第一个协议进行切片。感谢!+1在您的第一篇文章中发布了一个清晰描述的问题和期望的结果,以及一个小示例和您尝试过的代码那就发帖吧!干杯。
df %>% arrange(id, desc(protocol)) %>% distinct(id, .keep_all = TRUE)
distinct_y <- df %>%
  filter(protocol == "Y") %>%
  distinct(id, .keep_all = TRUE)

distinct_other <- df %>%
  anti_join(distinct_y, "id") %>%
  distinct(id, .keep_all = TRUE)

distinct_combined <- rbind(distinct_y, distinct_other)
# Only difference is the best protocol for C will now be Z.
df2 <- tibble(
  id = c("A", "A", "B", "C", "C", "D"),
  protocol = c("X", "Y", "X", "X", "Z", "Y"),
  date = c(seq(as.Date("2018-01-01"), as.Date("2018-01-06"),
               by="days")),
  var = 1:6
)

order_of_importance <- c("Y", "Z", "X")

df2 %>%
  mutate(protocol = factor(protocol, order_of_importance)) %>%
  group_by(id) %>%
  arrange(protocol) %>%
  slice(1)
# # A tibble: 4 x 4
# # Groups: id [4]
#   id    protocol date         var
#   <chr> <fctr>   <date>     <int>
# 1 A     Y        2018-01-02     2
# 2 B     X        2018-01-03     3
# 3 C     Z        2018-01-05     5
# 4 D     Y        2018-01-06     6
order_of_importance <- c("Y", "Z", "X")

    df2 %>%
      mutate(protocol = factor(protocol, order_of_importance)) %>%
      arrange(id, protocol) %>%
      distinct(id, .keep_all = TRUE)
df %>% 
    arrange(id, desc(protocol == 'Y')) %>% 
    distinct(id, .keep_all = TRUE)