from __future__ import division

class NaiveBayes:
    
    """
    A naive Bayesian classifier. The observation vectors can be
    arbitrary iterable objecs with hashable values. The classes should
    also be hashable.
    """

    def __init__(self):
        self.prior = {}  # Frequency of each class
        self.total = {}  # Frequency of each (class, attr, value)-tuple
        self.count = 0   # Number of observations

    def add(self, cls, obs):
        'Adds an observation to the classifier'
        self.prior[cls] = self.prior.get(cls, 0) + 1
        for idx, val in enumerate(obs):
            key = cls, idx, val
            self.total[key] = self.total.get(key, 0) + 1
        self.count += 1

    def discr(self, cls, obs):
        'Bayesian discriminant. Proportional to posterior probability'
        result = self.prior[cls]/self.count
        for idx, val in enumerate(obs):
            freq = self.total.get((cls, idx, val), 0)
            result *= freq/self.prior[cls]
        return result

    def classify(self, obs):
        'Classifies an observation'
        candidates = [(self.discr(c, obs), c) for c in self.prior]
        return max(candidates)[1]
