Criar função para frequência relativa de duas colunas no data frame

Pessoal, boa tarde.

Eu estou montando o meu projeto do curso de pacotes no R e preciso montar uma função que compute a frequência relativa de duas colunas no data frame.

Exemplo:
coluna_1<- c(X,X,X,A,A,A,A,C,C,C)
coluna_2<- c(X,X,A,A,A,A,A,A,C,C)

df<- data.frame(coluna_1, coluna_2)

Eu consigo fazer fazer isso para uma coluna, mas para duas eu não consigo.
O que eu preciso para o meu retorno é o seguinte:
freq_col_1 freq_col_2
0,3 0,2
0,4 0,6
0,3 0,2

Ou seja, eu preciso criar uma função que vai receber o dataframe e as duas colunas com as variáveis categóricas.

Obrigado

Henrique,

tem algumas formas de se chegar ao seu objetivo. Uma questão que tenho é se o dataframe de entrada, como explicou no texto, sempre terá as mesmas categorias nas variáveis? Talvez seria importante transformá-las em fator? Bom, imaginando que não serão iguais e nem serão fatores, penso na seguinte alternativa:

coluna_1<- c(“X”,“X”,“X”,“A”,“A”,“A”,“A”,“C”,“C”,“C”)
coluna_2<- c(“X”,“X”,“A”,“A”,“A”,“A”,“A”,“A”,“C”,“C”)

df<- data.frame(coluna_1, coluna_2)
library(janitor)
library(purrr)

map(df, ~tabyl(.x))

Obviamente você teria que dar uma refinada na lista pra extrair as colunas que deseja.

Oi Denis,
infelizmente isso nao resolver o meu problema.
eu preciso de uma tabela com essas duas frequencias, o que voce me entraga sao duas tabelas.

obrigado

O que eu gostaria seria jogar esta funcao:

df |>
tabyl(coluna_1, coluna_2) |>
select(percent)

e sair uma tabela com as frequencias relativas. O problema e que este codigo so funciona se eu colocar so uma coluna, se der duas da erro

Henrique,

Se eu entendi direito, a solução do @denis-or resolve grande parte do seu problema. Você precisaria usar um join para juntar as tabelas no final, mas a questão das frequências relativas estava feita.

Abaixo eu apresento 3 possíveis soluções: uma adaptada do Denis (usando a tabyl, que eu nunca tinha visto antes :astonished:), uma usando só funções do tidyverse e uma mais direta que não depende nem do purrr.

# Tabela exemplo
df <- data.frame(
  coluna_1 = c("X", "X", "X", "A", "A", "A", "A", "C", "C", "C"),
  coluna_2 = c("X", "X", "A", "A", "A", "A", "A", "A", "C", "C")
)

### SOLUÇÃO 1

# Adaptação da resposta do Denis, juntando tabelas no final
df |> 
  purrr::map(janitor::tabyl) |> 
  purrr::reduce(dplyr::inner_join, ".x[[i]]", suffix = c("_1", "_2")) |> 
  dplyr::select(dplyr::starts_with("percent"))
#>  percent_1 percent_2
#>        0.4       0.6
#>        0.3       0.2
#>        0.3       0.2

### SOLUÇÃO 2

# Função auxiliar
freq_col <- function(col) {
  col |> 
    tibble::as_tibble() |> 
    dplyr::count(value) |> 
    dplyr::mutate(freq_col = n / sum(n)) |> 
    dplyr::select(-n)
}

# Operar em cada coluna e juntar depois
df |> 
  purrr::map(freq_col) |> 
  purrr::reduce(dplyr::inner_join, "value", suffix = c("_1", "_2"))
#> # A tibble: 3 × 3
#>   value freq_col_1 freq_col_2
#>   <chr>      <dbl>      <dbl>
#> 1 A            0.4        0.6
#> 2 C            0.3        0.2
#> 3 X            0.3        0.2

### SOLUÇÃO 3

# Aplicar a função da solução 2 sem usar map()
freq_1 <- freq_col(df$coluna_1)
freq_2 <- freq_col(df$coluna_2)

# Juntar as duas tabelas sem precisar de reduce()
dplyr::inner_join(freq_1, freq_2, "value", suffix = c("_1", "_2"))
#> # A tibble: 3 × 3
#>   value freq_col_1 freq_col_2
#>   <chr>      <dbl>      <dbl>
#> 1 A            0.4        0.6
#> 2 C            0.3        0.2
#> 3 X            0.3        0.2

Created on 2022-12-03 with reprex v2.0.2

Um ponto importante é que, talvez, o formato ideal para sua entrada não seja data frame. Note que, para a operação que você quer fazer, não importa a ordem dos elementos de cada coluna; se a coluna_1 fosse C, C, C, A, A, A, A, X, X, X, o resultado final não mudaria. Vale a pena pensar se uma lista com 2 elementos ou 2 vetores completamente separados não seriam opções melhores de entrada.

1 curtida

Obrigado pessoal pela resposta.
Eu confesso que pensei bem em como responder e encontrei essa abordagem, achei ate mais simples:

df |>
select(coluna_1, coluna_2) |>
sapply(function(x) round(table(x) * 100 /nrow(df),2)) |>
tibble::as_tibble()

2 curtidas

Caio, eu terminei as funcoes do meu pacote. Agora e ver o segundo video e as possiveis duvidas dele.

Oi Henrique.

Quem bom que conseguiu uma solução legal. Parabéns!

Gostaria de aproveitar pra apontar alguns pontos, não apenas pra você, mas quem nos lê também.

Primeiro é que se você utilizar duas colunas juntas, no table() ou tabyl() do janitor eles cruzarão as variáveis, fazendo um tabela cruzada. Sua solução resolve esse problema.

Caso as categorias a serem calculadas sejam iguais, de fato, a sua solução é muito interessante. Eu ainda sugeria uma pequena alteração, incluindo a função prop.table() do base, que já faz os cálculos com os totais. É apenas detalhe.

df <- data.frame(
  coluna_1 = c("X", "X", "X", "A", "A", "A", "A", "C", "C", "C"),
  coluna_2 = c("X", "X", "A", "A", "A", "A", "A", "A", "C", "C")
)

df |>
  dplyr::select(coluna_1, coluna_2) |>
  sapply(\(x) prop.table(table(x)) * 100 |> round(2)) |>
  tibble::as_tibble()
#> # A tibble: 3 × 2
#>   coluna_1 coluna_2
#>      <dbl>    <dbl>
#> 1       40       60
#> 2       30       20
#> 3       30       20

Created on 2022-12-09 with reprex v2.0.2

Mas, se as categorias das colunas forem de tamanhos diferentes dá erro.

df_1 <- data.frame(
  coluna_1 = c("X", "X", "X", "A", "A", "A", "A", "C", "C", "C"),
  coluna_2 = c("C", "C", "Z", "X", "S", "S", "S", "S", "T", "T") # coluna com categorias e tamanho diferentes
)

df_1 |>
  dplyr::select(coluna_1, coluna_2) |>
  sapply(\(x) prop.table(table(x)) * 100 |> round(2)) |>
  tibble::as_tibble()
#> Error:
#> ! Tibble columns must have compatible sizes.
#> • Size 3: Column `coluna_1`.
#> • Size 5: Column `coluna_2`.
#> ℹ Only values of size one are recycled.

#> Backtrace:
#>     ▆
#>  1. ├─tibble::as_tibble(...)
#>  2. └─tibble:::as_tibble.list(...)
#>  3.   └─tibble:::lst_to_tibble(x, .rows, .name_repair, col_lengths(x))
#>  4.     └─tibble:::recycle_columns(x, .rows, lengths)
#>  5.       └─rlang::cnd_signal(...)

Created on 2022-12-09 with reprex v2.0.2

Outro ponto é se as categorias forem diferentes, mas de tamanhos iguais, a tabela final está mostrando adequadamente os dados?

df_2 <- data.frame(
  coluna_1 = c("X", "X", "X", "A", "A", "A", "A", "C", "C", "C"),
  coluna_2 = c("Z", "Z", "Z", "Y", "Y", "Y", "Y", "T", "T", "T")
  
)

df_2 |>
  dplyr::select(coluna_1, coluna_2) |>
  sapply(\(x) prop.table(table(x)) * 100 |> round(2)) |>
  tibble::as_tibble()
#> # A tibble: 3 × 2
#>   coluna_1 coluna_2
#>      <dbl>    <dbl>
#> 1       40       30
#> 2       30       40
#> 3       30       30

Created on 2022-12-09 with reprex v2.0.2

Apenas reflexões.

1 curtida