R:如何根据另一个变量中的值选择 dplyr::distinct() 保留哪一行?
R: How do I choose which row dplyr::distinct() keeps based on a value in another variable?
现实生活中的问题:我的受试者有 MRI 扫描数据。其中一些已被多次扫描(单独的行)。其中一些每次都根据不同的协议进行扫描。我想按主题 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)
我想 return 一个包含所有唯一主题的数据框(按 ID)。当涉及到重复值时,我希望它保留 "Y" 作为协议的条目,而不是自动保留第一个条目,如果它有这样的选择,但不要删除带有 [=35= 的行] 除此以外。
在示例中,它将保留第 2、3、4 和 6 行。
我更喜欢 dplyr,但我愿意接受其他建议。
我试过的任何东西都不起作用:
df %>% distinct(id, .keep_all = TRUE) #Nope!
df %>% distinct(id, protocol == "Y", .keep_all = TRUE) #Nope!
df$protocol <- factor(df$protocol, levels = c("Y", "X"))
df %>% distinct(id, .keep_all = TRUE) #Nope!
df %>% group_by(id) %>% filter(protocol == "Y") #Nope!
两个好的答案:
@RobJensen 建议
df %>% arrange(id, desc(protocol == 'Y')) %>% distinct(id, .keep_all = TRUE)
如果我有多个协议并希望分配它们的选择顺序,我可以创建一个新变量,在其中我按优先顺序为协议分配一个整数,然后使用@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))
的更好成语。
如果您希望输出不是 grouped_df 的小标题,则无需使用 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)
如果您想将其从 "one above all" 概括为值的排序,我建议将 protocol
作为一个因素。
例如,假设有三个协议:X、Y 和 Z。Y 是最好的,Z 比 X 好,如果没有更好的,你只想要 X。
# 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
按字母顺序排列在所述的简单情况下有效,但如果你愿意,你可以添加一个 protocol_preference
变量来给出你希望 selected 的顺序,如果 [=13] =] 不可用,并且 select "Y" 即使按字母顺序排序时它恰好不是最后一个协议值。
建立@davechilders 的答案和@Nathan Werth 基于"order of importance" 向量
创建因子的想法
order_of_importance <- c("Y", "Z", "X")
df2 %>%
mutate(protocol = factor(protocol, order_of_importance)) %>%
arrange(id, protocol) %>%
distinct(id, .keep_all = TRUE)
或者,如果您只想 select 'Y' 并且对 select 的内容没有偏好,如果 'Y' 不可用,您可以
df %>%
arrange(id, desc(protocol == 'Y')) %>%
distinct(id, .keep_all = TRUE)
现实生活中的问题:我的受试者有 MRI 扫描数据。其中一些已被多次扫描(单独的行)。其中一些每次都根据不同的协议进行扫描。我想按主题 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)
我想 return 一个包含所有唯一主题的数据框(按 ID)。当涉及到重复值时,我希望它保留 "Y" 作为协议的条目,而不是自动保留第一个条目,如果它有这样的选择,但不要删除带有 [=35= 的行] 除此以外。
在示例中,它将保留第 2、3、4 和 6 行。
我更喜欢 dplyr,但我愿意接受其他建议。
我试过的任何东西都不起作用:
df %>% distinct(id, .keep_all = TRUE) #Nope!
df %>% distinct(id, protocol == "Y", .keep_all = TRUE) #Nope!
df$protocol <- factor(df$protocol, levels = c("Y", "X"))
df %>% distinct(id, .keep_all = TRUE) #Nope!
df %>% group_by(id) %>% filter(protocol == "Y") #Nope!
两个好的答案: @RobJensen 建议
df %>% arrange(id, desc(protocol == 'Y')) %>% distinct(id, .keep_all = TRUE)
如果我有多个协议并希望分配它们的选择顺序,我可以创建一个新变量,在其中我按优先顺序为协议分配一个整数,然后使用@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))
的更好成语。
如果您希望输出不是 grouped_df 的小标题,则无需使用 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)
如果您想将其从 "one above all" 概括为值的排序,我建议将 protocol
作为一个因素。
例如,假设有三个协议:X、Y 和 Z。Y 是最好的,Z 比 X 好,如果没有更好的,你只想要 X。
# 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
按字母顺序排列在所述的简单情况下有效,但如果你愿意,你可以添加一个 protocol_preference
变量来给出你希望 selected 的顺序,如果 [=13] =] 不可用,并且 select "Y" 即使按字母顺序排序时它恰好不是最后一个协议值。
建立@davechilders 的答案和@Nathan Werth 基于"order of importance" 向量
创建因子的想法order_of_importance <- c("Y", "Z", "X")
df2 %>%
mutate(protocol = factor(protocol, order_of_importance)) %>%
arrange(id, protocol) %>%
distinct(id, .keep_all = TRUE)
或者,如果您只想 select 'Y' 并且对 select 的内容没有偏好,如果 'Y' 不可用,您可以
df %>%
arrange(id, desc(protocol == 'Y')) %>%
distinct(id, .keep_all = TRUE)