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
x <- 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 geradosAlé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.
x <- tabfreq(x)
x # observe os dados gerados por meio desta funcao## 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”). FALSEindica 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:
mfreq <- function (x, details = FALSE, na.rm = FALSE, rounding = 2, grouped = TRUE) 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
}
rounding <- trunc(rounding) #truncado casas decimais no rounding, se houver
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
x <- tabfreq(x) # implementando tabfreq caso ela não tenhaPor 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
numchar <- is.numeric(x$estat$raw_data) # is.numeric detecta se um objeto é integralmente numérico
if (numchar == 0) { #se o valor retornou 0, quer dizer que há caracteres no meioPara 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
mo <- "The data set has no mode!" # resposta armazenada no 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
pos <- which(x$tabela$Fi == max(x$tabela$Fi)) #obtendo as posições da moda via Fi e armazenando em pos
# os valores da amostra pode ser acessado via objeto
mo <- x$tabela$Groups[pos] # retonando a moda via os valores que estao nas posicoes achadas em posO 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
pos <- which(x$tabela$Fi == max(x$tabela$Fi))
mo <- round(as.numeric(x$tabela$Groups[pos]), rounding) #usando o rounding dentro da funcao round
}
}
resume <- list(mode = mo, table = x$tabela, rawdata = x$estat$raw_data) #lista de valores 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
pos <- which(x$tabela$Fi == max(x$tabela$Fi)) #obtendo as posições da moda
compos <- length(pos) #obtendo o número de modas via lenght
mo <- vector(mode = "integer", length = compos) #ajustando mo como vetor
j <- 1 #colocando j como 1
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
aux1 <- 0
}
else {
aux1 <- x$tabela$Fi[i - 1] # cálculo de delta 1
}
if (i == x$estat$Numero_de_classes) {# condicional para a moda estar na última classe
aux2 <- 0
}
else { # cálculo de delta 2
aux2 <- x$tabela$Fi[i + 1]
} # cálculo da moda
del1 <- x$tabela$Fi[i] - aux1
del2 <- x$tabela$Fi[i] - aux2
# colocando o resultado na posicao j do vetor mo
mo[j] <- x$estat$LI_classes[i] + (del1/(del1 + del2)) *
x$estat$Ampl_clas
j <- j + 1 #incrementando j para o próximo loop e assim colocar na posição seguinte de mo
} #imprimindo resultado como abordado anteriormente
mo <- round(mo, rounding)
resume <- list(mode = mo, table = x$tabela, rawdata = x$estat$raw_data)
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 <- 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)
if (all(x$tabela$Fi == x$tabela$Fi[1])) { #mesmo procedimento da discreta
mo <- "The data set has no mode!"
}
else {
pos <- which(x$tabela$Fi == max(x$tabela$Fi))
mo <- round(x$tabela$Groups[pos], rounding)
}
resume <- list(mode = mo, table = x$tabela, rawdata = x$estat$raw_data)
if (details) {
return(resume)
}
else {
return(mo)
}
}
}
} # fim da função