# Path: simpleai/machine_learning/models.py
# class Classifier(object):
#     """
#     Base of all classifiers.
#     This specifies the classifier API.
# 
#     Each classifier holds at least a dataset and a ClassificationProblem.
#     """
# 
#     def __init__(self, dataset, problem):
#         self.dataset = dataset
#         self.problem = problem
#         self.learn()
# 
#     def learn(self):
#         """
#         Does the training. Returns nothing.
#         """
#         raise NotImplementedError()
# 
#     @property
#     def attributes(self):
#         """
#         The attributes of the problem.
#         A list of callable objects.
#         """
#         return self.problem.attributes
# 
#     @property
#     def target(self):
#         """
#         The problem's target.
#         A callable that takes an observation and returns the correct
#         classification for it.
#         """
#         return self.problem.target
# 
#     def classify(self, example):
#         """
#         Returns the classification for example.
#         """
#         raise NotImplementedError()
# 
#     def save(self, filepath):
#         """
#         Pickles the tree and saves it into `filepath`
#         """
# 
#         if not filepath or not isinstance(filepath, str):
#             raise ValueError("Invalid filepath")
# 
#         # Removes dataset so is not saved in the pickle
#         self.dataset = None
#         with open(filepath, "wb") as filehandler:
#             pickle.dump(self, filehandler)
# 
#     def distance(self, a, b):
#         """
#         Custom distance between `a` and `b`.
#         """
#         raise NotImplementedError()
# 
#     @classmethod
#     def load(cls, filepath):
#         """
#         Loads a pickled version of the classifier saved in `filepath`
#         """
#         with open(filepath, "rb") as filehandler:
#             classifier = pickle.load(filehandler)
# 
#         if not isinstance(classifier, Classifier):
#             raise ValueError("Pickled object is not a Classifier")
# 
#         return classifier
# 
# Path: simpleai/machine_learning/metrics.py
# class Counter(defaultdict):
#     """
#     Counter of examples. Counts the total of examples added
#     and also the times that the target of that example appears.
# 
#     To add an example use the `add` method and to check the
#     values use it like a dictionary.
#     """
# 
#     def __init__(self, target):
#         super(Counter, self).__init__(int)
#         self.target = target
#         self.total = 0
# 
#     def add(self, example):
#         value = self.target(example)
#         self[value] += 1
#         self.total += 1
# 
# class OnlineInformationGain(object):
#     def __init__(self, attribute, target):
#         self.attribute = attribute
#         self.H = OnlineEntropy(target)
#         self.G = defaultdict(lambda: OnlineEntropy(target))
# 
#     def add(self, example):
#         self.H.add(example)
#         value = self.attribute(example)
#         self.G[value].add(example)
# 
#     def get_target_class_counts(self):
#         return self.H
# 
#     def get_branches(self):
#         return list(self.G.items())
# 
#     def get_gain(self):
#         H1 = self.H.get_entropy()
#         H2 = 0.0
#         for G in list(self.G.values()):
#             w = G.total / float(self.H.total)
#             H2 += w * G.get_entropy()
#         return H1 - H2
# 
# class OnlineLogProbability(object):
#     def __init__(self):
#         self.d = defaultdict(int)
#         self._logtotal = None
# 
#     def add(self, x):
#         if self._logtotal is not None:
#             raise ValueError("OnlineLogProbability is frozen since first read")
#         self.d[x] += 1
# 
#     def __getitem__(self, x):
#         if x not in self:
#             raise KeyError(x)
#         if self._logtotal is None:
#             self._logtotal = numpy.log(sum(self.d.values()))
#         return numpy.log(self.d[x]) - self._logtotal
# 
#     def __contains__(self, x):
#         return x in self.d
# 
#     def __iter__(self):
#         return iter(self.d)
# 
#     def __len__(self):
#         return len(self.d)
# 
#     def iteritems(self):
#         for x in self.d:
#             yield x, self[x]


import numpy
    import cPickle as pickle
    import pickle
from collections import defaultdict
from simpleai.machine_learning.models import Classifier
from simpleai.machine_learning.metrics import Counter, OnlineInformationGain, \
                                              OnlineLogProbability
        return tree

    def importance(self, attribute, examples):
        """
        AIMA implies that importance should be information gain.
        Since AIMA only defines it for binary features this implementation
        was based on the wikipedia article:
        http://en.wikipedia.org/wiki/Information_gain_in_decision_trees
        """
        gain_counter = OnlineInformationGain(attribute, self.target)
        for example in examples:
            gain_counter.add(example)
        return gain_counter.get_gain()

    def classify(self, example):
        node = walk_to_leaf(self.root, example)
        return node.result


class NaiveBayes(Classifier):
    """
    Implements a classifier that uses the Bayes' theorem.
    """

    def learn(self):
        # Frequency count of target classes
        self.C = OnlineLogProbability()
        # Frequency count of P(Fi|C):
        self.Fi = defaultdict(lambda:  # For each class,
                      defaultdict(lambda:  # For each attribute,

# Path: simpleai/machine_learning/models.py
# class Classifier(object):
#     """
#     Base of all classifiers.
#     This specifies the classifier API.
# 
#     Each classifier holds at least a dataset and a ClassificationProblem.
#     """
# 
#     def __init__(self, dataset, problem):
#         self.dataset = dataset
#         self.problem = problem
#         self.learn()
# 
#     def learn(self):
#         """
#         Does the training. Returns nothing.
#         """
#         raise NotImplementedError()
# 
#     @property
#     def attributes(self):
#         """
#         The attributes of the problem.
#         A list of callable objects.
#         """
#         return self.problem.attributes
# 
#     @property
#     def target(self):
#         """
#         The problem's target.
#         A callable that takes an observation and returns the correct
#         classification for it.
#         """
#         return self.problem.target
# 
#     def classify(self, example):
#         """
#         Returns the classification for example.
#         """
#         raise NotImplementedError()
# 
#     def save(self, filepath):
#         """
#         Pickles the tree and saves it into `filepath`
#         """
# 
#         if not filepath or not isinstance(filepath, str):
#             raise ValueError("Invalid filepath")
# 
#         # Removes dataset so is not saved in the pickle
#         self.dataset = None
#         with open(filepath, "wb") as filehandler:
#             pickle.dump(self, filehandler)
# 
#     def distance(self, a, b):
#         """
#         Custom distance between `a` and `b`.
#         """
#         raise NotImplementedError()
# 
#     @classmethod
#     def load(cls, filepath):
#         """
#         Loads a pickled version of the classifier saved in `filepath`
#         """
#         with open(filepath, "rb") as filehandler:
#             classifier = pickle.load(filehandler)
# 
#         if not isinstance(classifier, Classifier):
#             raise ValueError("Pickled object is not a Classifier")
# 
#         return classifier
# 
# Path: simpleai/machine_learning/metrics.py
# class Counter(defaultdict):
#     """
#     Counter of examples. Counts the total of examples added
#     and also the times that the target of that example appears.
# 
#     To add an example use the `add` method and to check the
#     values use it like a dictionary.
#     """
# 
#     def __init__(self, target):
#         super(Counter, self).__init__(int)
#         self.target = target
#         self.total = 0
# 
#     def add(self, example):
#         value = self.target(example)
#         self[value] += 1
#         self.total += 1
# 
# class OnlineInformationGain(object):
#     def __init__(self, attribute, target):
#         self.attribute = attribute
#         self.H = OnlineEntropy(target)
#         self.G = defaultdict(lambda: OnlineEntropy(target))
# 
#     def add(self, example):
#         self.H.add(example)
#         value = self.attribute(example)
#         self.G[value].add(example)
# 
#     def get_target_class_counts(self):
#         return self.H
# 
#     def get_branches(self):
#         return list(self.G.items())
# 
#     def get_gain(self):
#         H1 = self.H.get_entropy()
#         H2 = 0.0
#         for G in list(self.G.values()):
#             w = G.total / float(self.H.total)
#             H2 += w * G.get_entropy()
#         return H1 - H2
# 
# class OnlineLogProbability(object):
#     def __init__(self):
#         self.d = defaultdict(int)
#         self._logtotal = None
# 
#     def add(self, x):
#         if self._logtotal is not None:
#             raise ValueError("OnlineLogProbability is frozen since first read")
#         self.d[x] += 1
# 
#     def __getitem__(self, x):
#         if x not in self:
#             raise KeyError(x)
#         if self._logtotal is None:
#             self._logtotal = numpy.log(sum(self.d.values()))
#         return numpy.log(self.d[x]) - self._logtotal
# 
#     def __contains__(self, x):
#         return x in self.d
# 
#     def __iter__(self):
#         return iter(self.d)
# 
#     def __len__(self):
#         return len(self.d)
# 
#     def iteritems(self):
#         for x in self.d:
#             yield x, self[x]


# Path: simpleai/machine_learning/classifiers.py
import numpy
    import cPickle as pickle
    import pickle
from collections import defaultdict
from simpleai.machine_learning.models import Classifier
from simpleai.machine_learning.metrics import Counter, OnlineInformationGain, \
                                              OnlineLogProbability

        return tree

    def importance(self, attribute, examples):
        """
        AIMA implies that importance should be information gain.
        Since AIMA only defines it for binary features this implementation
        was based on the wikipedia article:
        http://en.wikipedia.org/wiki/Information_gain_in_decision_trees
        """
        gain_counter = OnlineInformationGain(attribute, self.target)
        for example in examples:
            gain_counter.add(example)
        return gain_counter.get_gain()

    def classify(self, example):
        node = walk_to_leaf(self.root, example)
        return node.result


class NaiveBayes(Classifier):
    """
    Implements a classifier that uses the Bayes' theorem.
    """

    def learn(self):
        # Frequency count of target classes
        self.C = OnlineLogProbability()
        # Frequency count of P(Fi|C):
        self.Fi = defaultdict(lambda:  # For each class,
                      defaultdict(lambda:  # For each attribute,

                          OnlineLogProbability()))  # For each value, count it