根据向量以特定顺序对数据帧行进行排序


158

是否有一种更简单的方法来确保数据框的行根据我在下面的简短示例中实现的“目标”矢量进行排序?

df <- data.frame(name = letters[1:4], value = c(rep(TRUE, 2), rep(FALSE, 2)))

df
#   name value
# 1    a  TRUE
# 2    b  TRUE
# 3    c FALSE
# 4    d FALSE

target <- c("b", "c", "a", "d")

这似乎有点“复杂”,无法完成工作:

idx <- sapply(target, function(x) {
    which(df$name == x)
})
df <- df[idx,]
rownames(df) <- NULL

df 
#   name value
# 1    b  TRUE
# 2    c FALSE
# 3    a  TRUE
# 4    d FALSE

Answers:


232

尝试match

df <- data.frame(name=letters[1:4], value=c(rep(TRUE, 2), rep(FALSE, 2)))
target <- c("b", "c", "a", "d")
df[match(target, df$name),]

  name value
2    b  TRUE
3    c FALSE
1    a  TRUE
4    d FALSE

只要您target包含与完全相同的元素df$name并且都不包含重复的值,它将起作用。

来自?match

match returns a vector of the positions of (first) matches of its first argument 
in its second.

因此,match找到与target元素匹配的行号,然后我们df以该顺序返回。


太好了,这更像是我想要的东西!非常感谢
Rappster

1
一个问题,如果我要匹配的列具有重复值怎么办?喜欢b,c,a,d,b,c,a,d。我尝试过,match但效果不佳。
育龙2013年

@Yulong:我认为您必须明确确保在触发前已删除重复项match()。什么想到的是duplicated()unique()或其他自定义例程“保持”所需的元素,而丢掉了别人。HTH
Rappster

@Edward这是一个不错的解决方案。但是,它也会更改索引。我还如何将它们保持升序(1、2、3、4)?
哈桑·伊克巴尔

2
不确定这是最干净的方法,但是只有“基本”功能,如果您在df中有重复项,这应该可以工作:df <- data.frame(name=letters[c(1:4, 1:4)], value=c(rep(TRUE, 2), rep(FALSE, 2),rep(TRUE, 2), rep(FALSE, 2) )) target <- c("b", "c", "a", "d") df[order(unlist(sapply(df$name, function(x) which(target == x)))),]
Erica Fary

21

我更喜欢***_joindplyr需要匹配数据时使用。一种可能的尝试

left_join(data.frame(name=target),df,by="name")

请注意,***_joinrequire tbls或data.frame 的输入


是的,* _ join函数dplyr确实很棒。到现在
为止

在这种情况下,建议将目标顺序声明为小标题,以避免data.frame()转换为因子。target <- tibble(name = c("b", "c", "a", "d"))
荨麻

2
并使用管道语法:df %>% right_join(tibble(name = target), by = "name")
Frank

18

这种方法有些不同,它为我提供了比以前的答案更大的灵活性。通过将其设置为有序因子,您可以在arrange此类应用中很好地使用它。我使用了gdata包中的reorder.factor 。

df <- data.frame(name=letters[1:4], value=c(rep(TRUE, 2), rep(FALSE, 2)))
target <- c("b", "c", "a", "d")

require(gdata)
df$name <- reorder.factor(df$name, new.order=target)

接下来,使用现在已订购的事实:

require(dplyr)
df %>%
  arrange(name)
    name value
1    b  TRUE
2    c FALSE
3    a  TRUE
4    d FALSE

如果要返回到原始(字母顺序)排序,只需使用as.character()将其恢复到原始状态即可。


2
有人知道这个的data.table版本吗?
Reilstein's

2
@Reilstein setDT(df)[ , name := factor(name, levels = target)]。然后在这里查看两个data.table答案
Henrik

4

我们可以根据调整因子水平target并将其用于arrange

library(dplyr)
df %>% arrange(factor(name, levels = target))

#  name value
#1    b  TRUE
#2    c FALSE
#3    a  TRUE
#4    d FALSE

order将其用于slice

df %>% slice(order(factor(name, levels = target)))

2
IMO的最佳解决方案
stevec

1
对我来说最好的和最简单的解决方案。
Matt_B

0

如果您不想使用任何库并且数据中重复出现,则也可以使用whichwith sapply

new_order <- sapply(target, function(x,df){which(df$name == x)}, df=df)
df        <- df[new_order,]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.