Упорядочить строки данных в соответствии с вектором с определенным порядком

Есть ли более простой способ убедиться, что строки фрейма данных упорядочены по "целевому" вектору, как я реализовал в коротком примере ниже?

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 ответов

Решение

Пытаться 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 в этой последовательности.

Мы можем корректировать уровни факторов на основе 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)))

Я предпочитаю использовать ***_join в dplyr всякий раз, когда мне нужно сопоставить данные. Одна из возможных попыток для этого

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

Обратите внимание, что вход для ***_join требуют таблицы или data.frame

Этот метод немного отличается, он предоставил мне немного больше гибкости, чем предыдущий ответ. Превратив его в упорядоченный фактор, вы можете использовать его в arrange и тому подобное. Я использовал reorder.factor из gdata пакет.

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() чтобы вернуть его в исходное состояние.

Если вы не хотите использовать какие-либо библиотеки и у вас есть повторения в ваших данных, вы можете использовать which с sapply также.

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

Вот аналогичная система для ситуации, когда у вас есть переменная, по которой вы хотите отсортировать сначала, но затем вы хотите отсортировать по вторичной переменной в соответствии с порядком, в котором эта вторичная переменная впервые появляется в начальной сортировке.

В приведенной ниже функции исходная переменная сортировки называется 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

Создано 12.01.2021 пакетом REPEX (v0.3.0)

Другие вопросы по тегам