SAUDAÇÕES!

Seja bem vindo à página do professor Pedro Albuquerque. Para saber mais sobre meu currículo, disciplinas ministradas e interesses de pesquisa, navegue no menu disponível no topo da página.

segunda-feira, 16 de outubro de 2017

Orientação a objetos no R - S4 Classes e Métodos.


Uma das principais atividades e, também uma boa prática na programação usando o R é a utilização da abordagem de programação orientada a objetos. Essa proposta é útil na construção de pacotes no R bem como na organização e estabilidade das bibliotecas criadas. Nesse tutorial iremos trabalhar com a classe S4 de objetos do R.

Suponha que desejamos criar um pacote que nos ajude a calcular a menção de alunos de uma turma. Nesse caso, o sistema de classes S4 pode ser definido criando-se o objeto aluno:

setClass(
  "Aluno",
  representation(nome="character", idade="numeric"),
  prototype(nome=character(0), idade=numeric(0))
)

Esse objeto contêm, inicialmente, dois slots: a idade do aluno e seu nome. O nome é considerado nesse modelo uma variável caracter e a idade uma variável numérica. O comando prototype define quais devem ser os valores iniciais para esses dois atributos.

Entretanto, na universidade nós temos diversos tipos de aluno, eles podem ser: alunos da graduação, pós-graduação, extensão, aluno especial, ouvinte, etc. Suponha que tenhamos dois tipos de alunos apenas:

#Define as subclasses de alunos
setClass("Graduacao",
  representation(nota1="numeric",nota2="numeric",nota3="numeric"),
  contains="Aluno")

setClass("Posgraduacao",
  representation(artigo="character",nota1="numeric",nota2="numeric"),
  contains="Aluno")

Na primeira subclasse donominada Graduação o aluno faz três provas representadas pelos slots: nota1, nota2 e nota3. Já na segunda subclasse o aluno é um aluno de Pós-graduação e ele é avaliado de maneira diferente, tem que preparar um artigo o qual pode ser armazenado no slot artigo e faz duas provas cujas notas são armazenadas em nota1 e nota 2. Note que nesse caso, ambos são Alunos e por isso herdam os atributos de nome e idade.

Além do mais, na criação dessas subclasses não atribuímos os valores iniciais desses atributos por opção. A boa prática, no entanto, recomenda que todos esses atributos sejam definidos os valores de inicialização através do método prototype.

Cada tipo de aluno terá sua nota calculada de maneira diferente: para os alunos de graduação a nota final será a média das três notas nas provas, já os alunos de Pós-graduação a nota será 50% da média das duas provas e 50% se ele fez o artigo e zero se não fez. Nesse caso, usaremos a propriedade de Mutabilidade (Mutability) dos sistemas de programação orientada a objetos. Através dessa propriedade um cômputo diferente será realizado para cada tipo de objeto. O primeiro passo é a criação de um método genérico:

#Cria um método generico denominado calculaMencao
#o qual é aplicado para o tipo de objeto de entrada.
setGeneric(
  "calculaMencao",
  function(object) {
    standardGeneric("calculaMencao")
  }
)

Em seguida, vamos definir um cálculo de função para cada tipo de aluno:

#Para alunos de graduação
setMethod(
  "calculaMencao",
  signature("Graduacao"),
  function(object) {
    #Calcula a média
    media<-(object@nota1+object@nota2+object@nota3)/3
    #Retorna o resultado
    return(media)
  }
)

#Para alunos de pós-graduação
setMethod(
  "calculaMencao",
  signature("Posgraduacao"),
  function(object) {
    #Calcula a média
    media<-(object@nota1+object@nota2)/2
    #Fez o artigo ?
    artigo<-ifelse(object@artigo == "",0,10)
    #Calcula a nota final
    nota<-media*0.5+artigo*0.5
    #Retorna o resultado
    return(nota)
  }
)
Note que em ambas as definições dos métodos incluímos o método genérico calculaMencao. O que muda entre eles é a assinatura (signature) e, consequentemente, a maneira como a função é calculada. Vamos agora criar alguns alunos seguindo a classes S4 formada:
#Aluno de Graduação
joao <- new("Graduacao",
              nome="João Silva",
              idade=22,
              nota1=2.75,
              nota2=8.78,
              nota3=5.72)

#Aluno de Pós-Graduação
maria <- new("Posgraduacao",
            nome="Maria Silva",
            idade=27,
            nota1=8.54,
            nota2=3.21,
            artigo="Como programar em R")

#Aluno de Pós-Graduação
alex <- new("Posgraduacao",
             nome="Alexandre Silva",
             idade=33,
             nota1=2.15,
             nota2=5.48,
             artigo="")
Para não ter que repetir o mesmo código toda vez que desejarmos criar um objeto, podemos construir os constructors:
#Cria o constructor:
Graduacao <- function(nome, idade, nota1, nota2, nota3){
  new("Graduacao", nome=nome, idade=idade, nota1=nota1, nota2=nota2, nota3=nota3)
}

#Cria o constructor:
Posgraduacao <- function(nome, idade, nota1, nota2, artigo){
  new("Graduacao", nome=nome, idade=idade, nota1=nota1, nota2=nota2, artigo=artigo)
}
Podemos calcular a menção deles fazendo:
#Calcula a nota do João
resultadoJoao<-calculaMencao(joao)
resultadoJoao

#Calcula a nota da Maria
resultadoMaria<-calculaMencao(maria)
resultadoMaria

#Calcula a nota do Alex
resultadoAlex<-calculaMencao(alex)
resultadoAlex
Podemos ainda ter objetos que são oriundos de mais de uma classe, por exemplo, considere um aluno que faz tanto Graduação quanto Pós-graduação, podemos criar uma classe específica para ele:
setClass("Maluco",
         contains=c("Graduacao","Posgraduacao"))
A criação dos objetos e cálculo das notas é feito da seguinte forma:
#Registro do Sergio na graduação
sergioGraduacao <- new("Maluco",
                   nome="Sergio Silva",
                   idade=22,
                   nota1=8.36,
                   nota2=9.12,
                   nota3=5.64)

#Registro do Sergio na pós-graduação
sergioPosgraduacao <- new("Posgraduacao",
                      nome="Sergio Silva",
                      idade=22,
                      nota1=3.45,
                      nota2=8.34,
                      artigo="Não sei nada")


#Calcula da nota do Sergio
resultadoSergio1<-calculaMencao(sergioGraduacao)
resultadoSergio1

resultadoSergio2<-calculaMencao(sergioPosgraduacao)
resultadoSergio2
É comum ainda criar para os métodos alguns pontos de validação dos valores de entrada, por exemplo, nenhuma nota pode ser negativa ou maior do que 10. Podemos incluir essas restrições fazendo:
setClass("Graduacao",
         representation(
           nota1="numeric",
           nota2="numeric",
           nota3="numeric"),
         validity = function(object) { 
           if (object@nota1<0 | object@nota1>10 ) 
           { 
             stop("A nota na prova 1 tem que ser válida.") 
           } 
           if (object@nota2<0 | object@nota2>10 ) 
           { 
             stop("A nota na prova 2 tem que ser válida.") 
           } 
           if (object@nota3<0 | object@nota3>10) 
           { 
             stop("A nota na prova 3 tem que ser válida.") 
           } 
           return(TRUE) 
         },
         contains="Aluno"
         )
Nas classes S4 de objetos temos algumas funções especiais:
  • slotNames: fornece o nome dos slots.
  • getSlots: fornece o nome dos slots e seus tipos.
  • getClass: fornece o nome dos slots, seus tipos e das classes antecessoras.




Um comentário: