按照特定顺序向量对数据框行进行排序

215

有没有更简单的方法,可以确保数据帧按照我在下面示例中实现的“目标”向量进行排序?

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
6个回答

298

尝试使用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 但是它效果不好。 - Yulong
@Yulong:我认为在触发match()之前,您必须明确确保重复项已被删除。我想到的是duplicated()unique()或其他一些自定义程序,可以“保留”所需元素并丢弃其他元素。希望对您有所帮助。 - Rappster
@Edward 这是一个不错的解决方案。但是,它也改变了索引。我如何保持它们按升序(1、2、3、4)排列? - QuestionEverything
4
不确定这是否是最简洁的方法,但只使用“基本”函数,如果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

54

我们可以根据 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)))

5
在我看来,最佳解决方案是... - stevec
1
对我来说,最好和最简单的解决方案。 - Matt_B
2
只有当“目标”向量包含与“名称”向量相同的元素时,才适用;否则,顺序会被打乱。 - Surya

30

每当我需要匹配数据时,我更喜欢使用***_joindplyr中。下面是一种可能的尝试。

left_join(data.frame(name=target),df,by="name")
请注意,***_join的输入需要tbls或data.frame。

是的,dplyr 中的 *_join 函数确实非常好用。现在我也经常使用它们。 - Rappster
在这种情况下,建议将目标顺序声明为tibble,以避免将data.frame()转换为因子。target <- tibble(name = c("b", "c", "a", "d")) - Nettle
2
使用管道语法:df %>% right_join(tibble(name = target), by = "name") - Frank
3
请注意,自dplyr 1.0.0以来,这已不再可能。这是一个重大变更。请参阅changelog - damianooldoni
df %>% right_join(target, ., by = "name") - Saskia Schirmer

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
4
@Reilstein的代码setDT(df)[, name := factor(name, levels = target)],将data.frame转换为data.table后,按照指定的顺序对名称列进行因子化处理。然后可以在此链接中查看两个使用data.table的答案:https://dev59.com/H3M_5IYBdhLWcg3wn0vT。 - Henrik

4

如果您不想使用任何库,并且数据中有重复项,您也可以使用whichsapply

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

2

对于情况相似的系统,您可能有一个要排序的变量,但最初希望按照一个辅助变量的顺序排序,以便根据辅助变量首次出现的顺序对其进行排序。

在下面的函数中,初始排序变量称为order_by,辅助变量称为order_along——即“沿着初始顺序根据此变量排序”。

library(dplyr, warn.conflicts = FALSE)
df <- structure(
  list(
    msoa11hclnm = c(
      "Bewbush", "Tilgate", "Felpham",
      "Selsey", "Brunswick", "Ratton", "Ore", "Polegate", "Mile Oak",
      "Upperton", "Arundel", "Kemptown"
    ),
    lad20nm = c(
      "Crawley", "Crawley",
      "Arun", "Chichester", "Brighton and Hove", "Eastbourne", "Hastings",
      "Wealden", "Brighton and Hove", "Eastbourne", "Arun", "Brighton and Hove"
    ),
    shape_area = c(
      1328821, 3089180, 3540014, 9738033, 448888, 10152663, 5517102,
      7036428, 5656430, 2653589, 72832514, 826151
    )
  ),
  row.names = c(NA, -12L), class = "data.frame"
)

这不能满足我的需求:
df %>% 
  dplyr::arrange(shape_area, lad20nm)
#>    msoa11hclnm           lad20nm shape_area
#> 1    Brunswick Brighton and Hove     448888
#> 2     Kemptown Brighton and Hove     826151
#> 3      Bewbush           Crawley    1328821
#> 4     Upperton        Eastbourne    2653589
#> 5      Tilgate           Crawley    3089180
#> 6      Felpham              Arun    3540014
#> 7          Ore          Hastings    5517102
#> 8     Mile Oak Brighton and Hove    5656430
#> 9     Polegate           Wealden    7036428
#> 10      Selsey        Chichester    9738033
#> 11      Ratton        Eastbourne   10152663
#> 12     Arundel              Arun   72832514

这里是一个函数:

order_along <- function(df, order_along, order_by) {
  cols <- colnames(df)
  
  df <- df %>%
    dplyr::arrange({{ order_by }})
  
  df %>% 
    dplyr::select({{ order_along }}) %>% 
    dplyr::distinct() %>% 
    dplyr::full_join(df) %>% 
    dplyr::select(dplyr::all_of(cols))
  
}

order_along(df, lad20nm, shape_area)
#> Joining, by = "lad20nm"
#>    msoa11hclnm           lad20nm shape_area
#> 1    Brunswick Brighton and Hove     448888
#> 2     Kemptown Brighton and Hove     826151
#> 3     Mile Oak Brighton and Hove    5656430
#> 4      Bewbush           Crawley    1328821
#> 5      Tilgate           Crawley    3089180
#> 6     Upperton        Eastbourne    2653589
#> 7       Ratton        Eastbourne   10152663
#> 8      Felpham              Arun    3540014
#> 9      Arundel              Arun   72832514
#> 10         Ore          Hastings    5517102
#> 11    Polegate           Wealden    7036428
#> 12      Selsey        Chichester    9738033

2021年1月12日由reprex package (v0.3.0)创建


我有27个观测值(A1到A38),这些值在总共7938行数据中重复出现。我想根据这些值对数据进行排序,代码如下:target <- c("A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A15", "A16", "A17", "A18", "A24", "A25", "A26", "A27", "A28", "A29", "A30", "A31", "A32", "A33", "A34", "A35", "A36", "A37", "A38") - Mohammed Toufiq

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接