Dplyr::arrange não ordena dentro de um loop for

Bom dia pessoal,
Tenho um data.frame com várias colunas e gostaria de ordenar os valores de cada uma delas de forma decrescente e recuperar apenas os maiores valores de cada. Como são muitas colunas, resolvi fazer através de um for, mas o arrange não está funcionando. Para ordenar eu utilizo o iterador (que no caso é o nome das minhas colunas) e acredito que aí que está o problema. Quando rodo fora do for, fornecendo o nome da coluna (sem as aspas " ") ele ordena de forma correta. O que posso fazer para conseguir fazer funcionar dentro do for?

Segue um exemplo reprodutível:

library(tidyverse)

df <- data.frame (tc_i_br_a4  = c(0.00501, 0.00458, 0.16035, 0.37084),
                  tc_ii_yc6 = c(0.00343, 0.00699, 0.01166, 0.24595),
                  tc_vi_clb = c(0.00316, 0.00780, 0.27390, 0.00152))

list <- vector(mode = "list")

i = 1

for (i in 1:ncol(df)) {
    temp <- df %>% select(i) %>% arrange(desc(i)) %>% head(3)
    list <- c(list, temp)
}
#> Note: Using an external vector in selections is ambiguous.
#> i Use `all_of(i)` instead of `i` to silence this message.
#> i See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
#> This message is displayed once per session.
#> Note: Using an external vector in selections is ambiguous.
#> i Use `all_of(i)` instead of `i` to silence this message.
#> i See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
#> This message is displayed once per session.

list
#> $tc_i_br_a4
#> [1] 0.00501 0.00458 0.16035
#> 
#> $tc_ii_yc6
#> [1] 0.00343 0.00699 0.01166
#> 
#> $tc_vi_clb
#> [1] 0.00316 0.00780 0.27390
#> $tc_i_br_a4
#> [1] 0.00501 0.00458 0.16035
#> 
#> $tc_ii_yc6
#> [1] 0.00343 0.00699 0.01166
#> 
#> $tc_vi_clb
#> [1] 0.00316 0.00780 0.27390

Created on 2022-04-06 by the reprex package (v2.0.1)

Olá Laila,
usando o R base é assim que fica

df <- data.frame(tc_i_br_a4  = c(0.00501, 0.00458, 0.16035, 0.37084),
                 tc_ii_yc6 = c(0.00343, 0.00699, 0.01166, 0.24595),
                 tc_vi_clb = c(0.00316, 0.00780, 0.27390, 0.00152))

df_final <- head(df,3)

for (i in 1:ncol(df)) {
  df_final[,i] <- head(sort(df[,i], decreasing = T),3)
}

Humberto

1 curtida

Laila,

O @hal-delaserna já deu uma resposta que parece resolver o problema, mas, como você está usando o tidyverse, vou deixar aqui uma alternativa só por completude.

O seu código na verdade tem 2 problemas:

  1. No geral, o tidyverse não funciona bem com números de colunas. Basta ver o quanto o select() está reclamando por você ter usado um número de coluna dentro dele… O tidyverse foi criado pensando em nomes e por isso precisamos avisar quando estivermos indexando de outra forma. No select() precisaríamos escrever select(all_of(i)) (como sugerido pelo alerta), mas no arrange() seria arrange(desc(across(i))). Para saber mais sobre o across(), dê uma olhada no nosso livro: 7.2 O pacote dplyr | Ciência de Dados em R.

  2. Como você começa o seu loop selecionando a i-ésima coluna, não faz sentido você ordenar a própria i-ésima; você precisa ordenar a primeira coluna (a única coluna, no caso).

Abaixo segue minha versão do código com o mínimo possível de alterações:

library(tidyverse)

df <- data.frame (tc_i_br_a4  = c(0.00501, 0.00458, 0.16035, 0.37084),
                  tc_ii_yc6 = c(0.00343, 0.00699, 0.01166, 0.24595),
                  tc_vi_clb = c(0.00316, 0.00780, 0.27390, 0.00152))

list <- vector(mode = "list")

i = 1

for (i in 1:ncol(df)) {
    temp <- df %>% select(all_of(i)) %>% arrange(desc(across(1))) %>% head(3)
    list <- c(list, temp)
}

list
#> $tc_i_br_a4
#> [1] 0.37084 0.16035 0.00501
#> 
#> $tc_ii_yc6
#> [1] 0.24595 0.01166 0.00699
#> 
#> $tc_vi_clb
#> [1] 0.27390 0.00780 0.00316

Created on 2022-04-06 by the reprex package (v2.0.1)

1 curtida

Se você quiser uma versão ainda mais tidyverse do código, eu sugiro dar uma olhada no pacote purrr: Capítulo 10 Programação funcional (purrr) | Ciência de Dados em R.

Acho que o programa abaixo fica até mais fácil de entender! O map(), neste caso, vai aplicar uma função em cada coluna (primeiro sort(decreasing = TRUE) e depois head(3)), retornando sempre uma lista com os resultados.

library(magrittr)

df <- tibble::tibble(
  tc_i_br_a4 = c(0.00501, 0.00458, 0.16035, 0.37084),
  tc_ii_yc6 = c(0.00343, 0.00699, 0.01166, 0.24595),
  tc_vi_clb = c(0.00316, 0.00780, 0.27390, 0.00152)
)

df %>%
  purrr::map(sort, decreasing = TRUE) %>%
  purrr::map(head, 3)
#> $tc_i_br_a4
#> [1] 0.37084 0.16035 0.00501
#> 
#> $tc_ii_yc6
#> [1] 0.24595 0.01166 0.00699
#> 
#> $tc_vi_clb
#> [1] 0.27390 0.00780 0.00316

Created on 2022-04-06 by the reprex package (v2.0.1)

1 curtida

Muito obrigada meninos. Todas as opções postadas funcionaram. :grinning:

1 curtida