Construir um script em R que possa obter a moda de um conjunto de
dados com o auxílio do pacote de funções Leem
Diante do objetivo do relatório, ele será dissertado e explanado nas próximas seções:
O pacote Leem
está sendo desenvolvido na Universidade
Federal de São João del Rei e tem o propósito de ser uma ferramenta
auxiliar de estudo na disciplina de Estatística. Como ele está em
desenvolvimento, foi proposto aos discentes da disciplina de Estatística
Computacional que auxiliem em sua construção.
Este pacote traz consigo uma classe de objetos próprio:
leem
. Atualmente, apenas tais objetos podem ser utilizados
nos argumentos das funções deste pacote, logo a função de moda terá que
trabalhar com estes objetos.
Para conceder um objeto a classe de leem
, utiliza-se a
função new_leem
com a seguinte sintaxe:
library(leem) # carregando o pacote leem
<- rbinom(100, 20, 0.4) # gerando uma amostra de números discretos
x
<- new_leem(x, variable = "discrete") # o argumento "variable" define o tipo de conjunto de dados gerados x
Além da função new_leem
, é importante também destacar a
função tabfreq
, a qual gera uma tabela de dados para o
objeto, sendo que tal tabela contém informações como classes e
frequências - essenciais para o cálculo de moda.
<- tabfreq(x)
x
# observe os dados gerados por meio desta funcao x
## Groups Fi Fr Fac1 Fac2 Fp Fac1p Fac2p
## 1 4 2 0.02 2 100 2 2 100
## 2 5 8 0.08 10 98 8 10 98
## 3 6 8 0.08 18 90 8 18 90
## 4 7 15 0.15 33 82 15 33 82
## 5 8 22 0.22 55 67 22 55 67
## 6 9 22 0.22 77 45 22 77 45
## 7 10 15 0.15 92 23 15 92 23
## 8 11 4 0.04 96 8 4 96 8
## 9 12 2 0.02 98 4 2 98 4
## 10 14 2 0.02 100 2 2 100 2
O corpo da função pode ser encontrado neste link. Caso tenha dúvida sobre algo, refira-se a este script e o acompanhe pelo texto.
Ao construir a função, o primeiro (e, possivelmente o mais fundamenteal) passo é definir os argumentos da função. NO caso da moda, o primeiro argumento será o conjunto de dados em que se quer saber tal valor. Além dele, foi incluído os seguintes argumentos auxiliares:
details
: argumento lógico que definirá se aparecerá
mais dados nos resultados além da moda. FALSE
mostrará
apenas a moda nos resultados. TRUE
mostará a tabela inteira
apresentada em tabfreq
.na.rm
: argumento lógico que tratará de resultados
inválidos (“NA”). FALSE
indica que estes dados serão levados
em consideração; TRUE
indicará a função para os
desconsiderar na função (ainda não implementado).rouding
: argumento numérico para indicar o número de
casa decimais. grouped
: argumento lógico que indicará o
procedimento de cálculo para variáveis contínuas: TRUE
indicará para calcular a moda através dos intervalos de classe obtidos;
FALSE
indicará a função para tratar cada valor registrado
como um ponto discreto, sem os agrupar em intervalos (para dados gerados
aleatoriamente, tal procedimento costuma não gerar uma moda, visto que a
probabilidade de uma máquina obter o mesmo número de 8 ou mais casas
decimais em um sorteio é muito baixa, levando a todos os dados a terem
frequência igual a 1).Com isso, levando em consideração que os valores de argumentos impostos na declaração da função serão o padrão, podemos a declarar como:
<- function (x, details = FALSE, na.rm = FALSE, rounding = 2, grouped = TRUE) mfreq
Como estes argumentos precisam ter estes valores específicos para funcionar mas o usuário pode colocar qualquer tipo de objeto no argumento, devemos garantir que apenas tais valores possam ser inseridos na função, garantindo seu funcionamento correto.
if (!is.numeric(rounding) | rounding < 0) { #verificando se rounding é numérico e maior que zero
stop("The 'rounding' argument must be numeric and positive!",
call. = FALSE, domain = "R-leem") # o comando stop termina a função
}<- trunc(rounding) #truncado casas decimais no rounding, se houver
rounding if (!is.logical(details)) { #verificando se details é lógico
stop("The 'details' argument must be logical!",
call. = FALSE, domain = "R-leem")
}
if (!is.logical(grouped)) { #verificando se grouped é lógico
stop("The 'grouped' argument must be logical!",
call. = FALSE, domain = "R-leem")
}
if (!is.logical(na.rm)) { #verificando se na.rm é lógico
stop("The 'na.rm' argument must be logical!", call. = FALSE,
domain = "R-leem")
}
if (class(x) != "leem") { #verificando se o objeto da função tem classe leem
stop("Use the 'new_leem()' function to create an object of class leem!",
call. = FALSE)
}if (class(x) == "leem" & is.null(attr(x, "table"))) # identificando se a variaável tenha tabfreq
<- tabfreq(x) # implementando tabfreq caso ela não tenha x
Por fim, antes de começar os cálculos, é interessante usar os dados
obtidos via tabfreq
para facilitar a execução deste
processo. Por isso, antes de todos os cálculos, foi forçada o uso do
tabfreq
no argumento, garantindo que a variável já tenha
sido sujeita a esta função.
Como o leem
trabalha com variáveis qualitativas, elas
deverão ser levadas em consideração. Pela sua natureza, elas
naturalmente serão dados discretos e expressas por meio de caracteres.
Tendo isso em mente, para identificar objetos leem
carregando este tipo de dado, basta verificar se ele é discreto e possui
caracteres em seus dados brutos.
if (attr(x, "variable") == "discrete") { #verificando se a variável é discreta
<- is.numeric(x$estat$raw_data) # is.numeric detecta se um objeto é integralmente numérico
numchar if (numchar == 0) { #se o valor retornou 0, quer dizer que há caracteres no meio
Para obter a moda de uma amostra qualitativa, basta observar qual é o dado com maior frequência. Contudo, há duas possibilidades que devem ser levantadas ao se implementar um algoritmo deste tipo:
Como discutido brevemente ao descrever o argumento
grouped
, se todas os valores da amostra tiverem a mesma
frequência, o conjunto não apresentará moda, sendo classificado como
amodal;
Quando houver dois ou mais valores que possuem o mesmo valor de frequência e que ainda são os mais frequentes da amostra, o conjunto será multimodal.
Para conjuntos amodais, uma solução seria comparar a frequência de todos os valores contra um exemplo do mesmo conjunto. Caso todos eles sejam iguais a este valor de exemplo, então se trata de uma distribuição amodal.
A função lógica all
pode ser empregada a este fim: ela
verifica se todos os valores contidos em um objeto obedecem a relação
imposta no argumento. Para nosso caso, se for obtido o retorno
TRUE
, a variável auxiliar mo
- a qual será
usada para dispor o valor da moda - assumirá o valor de uma frase
indicando que o grupo de dados apresentados é amodal.
# dentro da condicional do numchar
{ # os valores de frequência dos valores estão anexados em objeto$tabela$Fi
if (all(x$tabela$Fi == x$tabela$Fi[1])) { #usando o all para comparar os valores da função entre si
<- "The data set has no mode!" # resposta armazenada no mo
mo }
Se o conjunto de dados passou nesta checagem,, ele prosseguirá para o
cálculo da moda. A este fim, fora empregado a função which
,
a qual retorna a posição de um valor dentro de um vetor. No caso, será
pedido a função para procurar a posição que apresenta o maior valor de
frequência. Como ela pode retornar mais de um valor de posição caso eles
sigam o que foi pedido, conjuntos multimodais poderão ser
detectados.
else { # condicional complementar do numchar
<- which(x$tabela$Fi == max(x$tabela$Fi)) #obtendo as posições da moda via Fi e armazenando em pos
pos # os valores da amostra pode ser acessado via objeto
<- x$tabela$Groups[pos] # retonando a moda via os valores que estao nas posicoes achadas em pos mo
O procedimento será semelhante para este tipo de variável. Contudo,
foi-se tomado um cuidado maior em relação ao retorno da função. Para
garantir que o valor da moda seja retornado como um número e que possa
ser trabalhada, fora empregada a função as.numeric
, a qual
transforma um objeto caractere em um número.
Além disso, como se trata de variáveis quantitativas, os argumentos
details
e rounding
serão implementados na hora
de dispor o resultado da função.
Para rounding
, ela será usada em conjunto com a função
nativa round
, usada para arredondamento de valores. Ela é
composta por dois argumentos: valor a ser arredondado e número de
dígitos, sendo que o valor será as modas e o número de dígitos será
rounding
.
details
apenas indicará para uma condicional se é para
imprimir o resultado completo via uma lista que será denominada
resume
com três vetores atômicos: mode
que
será a moda; table
sendo a tabela de dados e
rawdata
como os dados brutos.
else { # condicional complementar do discrete
<- which(x$tabela$Fi == max(x$tabela$Fi))
pos <- round(as.numeric(x$tabela$Groups[pos]), rounding) #usando o rounding dentro da funcao round
mo
}}
<- list(mode = mo, table = x$tabela, rawdata = x$estat$raw_data) #lista de valores resume
resume if (details) { #checando se foi pedido detalhes
return(resume) #imprime se pediu
}else {
return(mo) # retorna só mo se não
}
No caso de variáveis contínuas agrupadas, a moda se dá pela seguinte expressão:
\[ Mo = L_i \ + \frac{\Delta_1}{\Delta_1 + \Delta_2}A \] Em que: - \(Mo\) é a moda; - \(L_i\) é o valor do limite inferior da classe de maior frequência; - \(\Delta_1\) é a diferença entre a frequência da moda e a da classe anterior; - \(\Delta_2\) é a diferença entre a frequência da moda e a da próxima classe; - \(A\) é a amplitude da classe.
Assim como surgiu os casos de conjuntos amodais e multimodais, há peculiaridades que este cálculo impõe em seu algoritmo:
Com isso, deverá ser imposta uma condicional para detectar estes casos.
Neste ponto, como os resultados são calculados em vez de extraídos do
objeto como nas rotinas anteriores, deverá ser tomada uma medida
diferente para armazenar os resultados. Para este fim, mo
será antecipadamente ajustado como um vetor de comprimento igual ao
número de modas. Os resultados serão obtidos por meio de um loop
for
, o qual é acompanhado por uma variável auxiliar
j
que se encarregará de indicar onde no vetor se deve
armazenar a variável.
if (attr(x, "variable") == "continuous") {# detectando a variável contínua
if (grouped) {#condicional do grouped. Será falado posteriormente
<- which(x$tabela$Fi == max(x$tabela$Fi)) #obtendo as posições da moda
pos <- length(pos) #obtendo o número de modas via lenght
compos <- vector(mode = "integer", length = compos) #ajustando mo como vetor
mo <- 1 #colocando j como 1
j for(i in pos) { #fazendo um loop for para os valores das posições das frequências
if (i == 1) {# condicional para a moda estar na primeira classe
<- 0
aux1
}else {
<- x$tabela$Fi[i - 1] # cálculo de delta 1
aux1
}if (i == x$estat$Numero_de_classes) {# condicional para a moda estar na última classe
<- 0
aux2
}else { # cálculo de delta 2
<- x$tabela$Fi[i + 1]
aux2 # cálculo da moda
} <- x$tabela$Fi[i] - aux1
del1 <- x$tabela$Fi[i] - aux2
del2 # colocando o resultado na posicao j do vetor mo
<- x$estat$LI_classes[i] + (del1/(del1 + del2)) *
mo[j] $estat$Ampl_clas
x<- j + 1 #incrementando j para o próximo loop e assim colocar na posição seguinte de mo
j
#imprimindo resultado como abordado anteriormente
} <- round(mo, rounding)
mo <- list(mode = mo, table = x$tabela, rawdata = x$estat$raw_data)
resume if (details) {
return(resume)
Como mencionado na seção 2.1, haverá a diferença de
cálculo se grouped
for FALSE
. No caso de dados
desagrupados, foi discutido que eles serão tratados como dados
discretos. Por isso, o cálculo implementado para este caso é semelhante
ao cálculo de discretas.
Para poder usar o cálculo anterior, a função força o objeto a se
tornar um leem
discreto. Com isso, o cálculo é feito assim
como ocorreu nas seções anteriores a esta.
} else { # caso o usuário tenha colocado grouped como falso, o algoritmo virá para cá
<- x$estat$raw_data # ao recolocar os dados brutos na variável, ela perde a classe leem
x <- new_leem(x, 1) # colocando-a como leem de novo mas agora como discreta
x <- tabfreq(x)
x if (all(x$tabela$Fi == x$tabela$Fi[1])) { #mesmo procedimento da discreta
<- "The data set has no mode!"
mo
}else {
<- which(x$tabela$Fi == max(x$tabela$Fi))
pos <- round(x$tabela$Groups[pos], rounding)
mo
}<- list(mode = mo, table = x$tabela, rawdata = x$estat$raw_data)
resume if (details) {
return(resume)
}else {
return(mo)
}
}
}
} # fim da função