sábado, 7 de junho de 2008

Cálculo da variância com Ruby

No blog Kodumaro foi publicado uma comparação de cálculos estatísticos entre linguagens imperativas, como C, e linguagens funcionais, no caso o Lisp. Os cálculos utilizados como exemplificação foram os de variância da população e variância da amostra. Como estou estudando ruby, me empolguei e resolvi fazer a versão desses cálculos nessa linguagem.

Veja como o código ficou:
class Array
def acumula()
acc = 0
each { |a| acc += yield(a) }
acc
end
def media()
total = acumula { |a| a }
total / length
end
def numerador_de_variancia()
m = media
acumula { |a| (a - media)**2 }
end
def variancia_populacional()
numerador_de_variancia / length
end
def variancia_da_amostra()
numerador_de_variancia / (length-1)
end
end
O que fiz foi criar novos métodos na classe Array para fazer todos os cálculos necessários. O interessante é que não encontrei na documentação do Ruby nenhum método que iterasse sobre um array e retornasse um acumulo dos valores calculados por um bloco. Para suprir isso criei então o método acumula.

Apesar do código ter ficado um pouco mais verboso que o código em lisp exibido no Kodumaro, o uso ficou mais simples. Como são novos métodos, para obter a variância da amostra em um array basta por exemplo fazer [2,4,6].variancia_da_amostra. Veja abaixo um exemplo de uso desses métodos:
array = [2,4,6]
puts "Media: #{array.media}"
puts "Numerador de variancia: #{array.numerador_de_variancia}"
puts "Variancia populacional: #{array.variancia_populacional}"
puts "Variancia da amostra: #{array.variancia_da_amostra}"
Mas é importante ressaltar o perigo dessa alteração de classe. Veja que não faço nenhum tratamento sobre o tipo de dado do array. No caso, estou assumindo que todo array que vá utilizar esses métodos tenham sempre todos os indices preenchidos por numeros.

3 comentários:

  1. Muito legal, Tiago!

    Adoro linguagens que permitem alteração de classes pré-existentes, como Ruby, Python, Smalltalk e Lua. =)

    []'s
    Cacilhas, La Batalema

    ResponderExcluir
  2. Que tal o Enumerable#inject? ;-)

    http://ruby-doc.org/core/classes/Enumerable.html#M001147

    Que bom que tá gostando! Eu sabia! haha

    ResponderExcluir
  3. @Fabio Kung

    Boa Kung! Brigado pela dica, segue como fica o programa atualizado utilizando inject ao invés do método acumula que eu havia criado:

    class Array
    def media()
    total = inject { |total,atual| total += atual }
    total / length
    end
    def numerador_de_variancia()
    m = media
    inject { |total,atual| total += (atual - media)**2 }
    end
    def variancia_populacional()
    numerador_de_variancia / length
    end
    def variancia_da_amostra()
    numerador_de_variancia / (length-1)
    end
    end

    Mais simples ainda

    ResponderExcluir