<commit_before>import numpy as np
from scipy.stats import linregress
from sklearn.cross_validation import cross_val_score, KFold, train_test_split
from sklearn.externals import joblib as pickle

def cross_validate(model, cv_set, cv_target, n = 10, shuffle=True, n_jobs = 1):
    """Perform cross validation of model using provided data

    Parameters
    ----------
        model: object
            Model to be tested

        cv_set: array-like of shape = [n_samples, n_features]
            Estimated target values.

        cv_target: array-like of shape = [n_samples] or [n_samples, n_outputs]
            Estimated target values.

        n: integer (default = 10)
            How many folds to be created from dataset

        shuffle: bool (default = True)
            Should data be shuffled before folding.

        n_jobs: integer (default = 1)
            How many CPUs to use during cross validation

    Returns
    -------
        r2: array of shape = [n]
            R^2 score for each of generated folds
    """
    if shuffle:
        cv = KFold(len(cv_target), n_folds=n, shuffle=True)
    else:
        cv = n
    return cross_val_score(model, cv_set, cv_target, cv = cv, n_jobs = n_jobs)

### FIX ### If possible make ensemble scorer lazy, for now it consumes all ligands
class scorer(object):
    def __init__(self, model_instances, descriptor_generator_instances, score_title = 'score'):
        """Scorer class is parent class for scoring functions. It's capable of using multiple models and/or multiple descriptors.
        If multiple models and multiple descriptors are used they should be aligned, since no permutation of such is made.

        Parameters
        ----------
            model_instances: array of models
                An array of medels compatible with sklearn API (fit, predict and score methods)

            descriptor_generator_instances: array of descriptors
                An array of descriptor objects

            score_title: string
                Title of score to be used.
        """
        self.model = model_instances
        if type(model_instances) is list:
            self.single_model = False
        else:
            self.single_model = True

        self.descriptor_generator = descriptor_generator_instances
        if type(descriptor_generator_instances) is list:
            if len(descriptor_generator_instances) == len(model_instances):
                raise ValueError, "Length of models list doesn't equal descriptors list"
            self.single_descriptor = False
        else:
            self.single_descriptor = True
        self.score_title = score_title

    def fit(self, ligands, target, *args, **kwargs):
        """Trains model on supplied ligands and target values

        Parameters
        ----------
            ligands: array-like of ligands
                Ground truth (correct) target values.

            target: array-like of shape = [n_samples] or [n_samples, n_outputs]
                Estimated target values.
        """
        if self.single_descriptor:
            self.train_descs = self.descriptor_generator.build(ligands)
        else:
            self.train_descs = [desc_gen.build(ligands) for desc_gen in self.descriptor_generator]
        self.train_target = target

        if self.single_model and self.single_descriptor:
            return model.fit(self.train_descs,target, *args, **kwargs)
        elif self.single_model and not self.single_descriptor:
            return [model.fit(desc,target, *args, **kwargs) for desc in self.train_descs]
        else:
            return [model.fit(self.train_descs[n],target, *args, **kwargs) for n, model in enumerate(self.model)]

    def predict(self, ligands, *args, **kwargs):
        """Predicts values (eg. affinity) for supplied ligands

        Parameters
        ----------
            ligands: array-like of ligands
                Ground truth (correct) target values.

            target: array-like of shape = [n_samples] or [n_samples, n_outputs]
                Estimated target values.

        Returns
        -------
            predicted: np.array or array of np.arrays of shape = [n_ligands]
                Predicted scores for ligands
        """
        if self.single_model and self.single_descriptor:
            descs = self.descriptor_generator.build(ligands)
            return self.model.predict(descs)
        elif self.single_model and not self.single_descriptor:
            return [self.model.predict(descs, *args, **kwargs) for desc in self.train_descs]
        else:
            descs = [desc_gen.build(ligands) for desc_gen in self.descriptor_generator]
            return [model.predict(descs[n],target, *args, **kwargs) for n, model in enumerate(self.model)]

    def score(self, ligands, target, *args, **kwargs):
        """Methods estimates the quality of prediction as squared correlation coefficient (R^2)

        Parameters
        ----------
            ligands: array-like of ligands
                Ground truth (correct) target values.

            target: array-like of shape = [n_samples] or [n_samples, n_outputs]
                Estimated target values.

        Returns
        -------
            r2: float
                Squared correlation coefficient (R^2) for prediction
        """
        if self.single_model and self.single_descriptor:
            descs = self.descriptor_generator.build(ligands)
            return self.model.score(descs, *args, **kwargs)
        elif self.single_model and not self.single_descriptor:
            return [self.model.score(descs, *args, **kwargs) for desc in self.train_descs]
        else:
            descs = [desc_gen.build(ligands) for desc_gen in self.descriptor_generator]
            return [model.score(descs[n],target, *args, **kwargs) for n, model in enumerate(self.model)]

    def predict_ligand(self, ligand):
        """Local method to score one ligand and update it's scores.

        Parameters
        ----------
            ligand: oddt.toolkit.Molecule object
                Ligand to be scored

        Returns
        -------
            ligand: oddt.toolkit.Molecule object
                Scored ligand with updated scores
        """
        score = self.predict([ligand])[0]
        ligand.data.update({self.score_title: score})
        return ligand

    def predict_ligands(self, ligands):
        """Method to score ligands lazily

        Parameters
        ----------
            ligands: iterable of oddt.toolkit.Molecule objects
                Ligands to be scored

        Returns
        -------
            ligand: iterator of oddt.toolkit.Molecule objects
                Scored ligands with updated scores
        """
        # make lazy calculation
        for lig in ligands:
            yield self.predict_ligand(lig)

    def set_protein(self, protein):
        """Proxy method to update protein in all relevant places.

        Parameters
        ----------
            protein: oddt.toolkit.Molecule object
                New default protein

        """
        self.protein = protein
        if self.single_descriptor:
            if hasattr(self.descriptor_generator, 'set_protein'):
                self.descriptor_generator.set_protein(protein)
            else:
                self.descriptor_generator.protein = protein
        else:
            for desc in self.descriptor_generator:
                if hasattr(desc, 'set_protein'):
                    desc.set_protein(protein)
                else:
                    desc.protein = protein

    def save(self, filename):
        """Saves scoring function to a pickle file.

        Parameters
        ----------
            filename: string
                Pickle filename
        """
        self.protein = None
        if self.single_descriptor:
            self.descriptor_generator.protein = None
        else:
            for desc in self.descriptor_generator:
                desc.protein = None
        return pickle.dump(self, filename, compress=9)[0]

    @classmethod
    def load(self, filename):
        """Loads scoring function from a pickle file.

        Parameters
        ----------
            filename: string
                Pickle filename

        Returns
        -------
            sf: scorer-like object<commit_msg>Remove redundant code from scorer. For multi-model and mulit-desc functionality use appropriate ensemble_* classes<commit_after>import numpy as np
from scipy.stats import linregress
from sklearn.cross_validation import cross_val_score, KFold, train_test_split
from sklearn.externals import joblib as pickle

def cross_validate(model, cv_set, cv_target, n = 10, shuffle=True, n_jobs = 1):
    """Perform cross validation of model using provided data

    Parameters
    ----------
        model: object
            Model to be tested

        cv_set: array-like of shape = [n_samples, n_features]
            Estimated target values.

        cv_target: array-like of shape = [n_samples] or [n_samples, n_outputs]
            Estimated target values.

        n: integer (default = 10)
            How many folds to be created from dataset

        shuffle: bool (default = True)
            Should data be shuffled before folding.

        n_jobs: integer (default = 1)
            How many CPUs to use during cross validation

    Returns
    -------
        r2: array of shape = [n]
            R^2 score for each of generated folds
    """
    if shuffle:
        cv = KFold(len(cv_target), n_folds=n, shuffle=True)
    else:
        cv = n
    return cross_val_score(model, cv_set, cv_target, cv = cv, n_jobs = n_jobs)

### FIX ### If possible make ensemble scorer lazy, for now it consumes all ligands
class scorer(object):
    def __init__(self, model_instance, descriptor_generator_instance, score_title = 'score'):
        """Scorer class is parent class for scoring functions.

        Parameters
        ----------
            model_instance: model
                Medel compatible with sklearn API (fit, predict and score methods)

            descriptor_generator_instance: array of descriptors
                Descriptor generator object

            score_title: string
                Title of score to be used.
        """
        self.model = model_instance
        self.descriptor_generator = descriptor_generator_instance
        self.score_title = score_title

    def fit(self, ligands, target, *args, **kwargs):
        """Trains model on supplied ligands and target values

        Parameters
        ----------
            ligands: array-like of ligands
                Ground truth (correct) target values.

            target: array-like of shape = [n_samples] or [n_samples, n_outputs]
                Estimated target values.
        """
        self.train_descs = self.descriptor_generator.build(ligands)
        return model.fit(self.train_descs,target, *args, **kwargs)

    def predict(self, ligands, *args, **kwargs):
        """Predicts values (eg. affinity) for supplied ligands

        Parameters
        ----------
            ligands: array-like of ligands
                Ground truth (correct) target values.

            target: array-like of shape = [n_samples] or [n_samples, n_outputs]
                Estimated target values.

        Returns
        -------
            predicted: np.array or array of np.arrays of shape = [n_ligands]
                Predicted scores for ligands
        """
        descs = self.descriptor_generator.build(ligands)
        return self.model.predict(descs)

    def score(self, ligands, target, *args, **kwargs):
        """Methods estimates the quality of prediction as squared correlation coefficient (R^2)

        Parameters
        ----------
            ligands: array-like of ligands
                Ground truth (correct) target values.

            target: array-like of shape = [n_samples] or [n_samples, n_outputs]
                Estimated target values.

        Returns
        -------
            r2: float
                Squared correlation coefficient (R^2) for prediction
        """
        descs = self.descriptor_generator.build(ligands)
        return self.model.score(descs, *args, **kwargs)

    def predict_ligand(self, ligand):
        """Local method to score one ligand and update it's scores.

        Parameters
        ----------
            ligand: oddt.toolkit.Molecule object
                Ligand to be scored

        Returns
        -------
            ligand: oddt.toolkit.Molecule object
                Scored ligand with updated scores
        """
        score = self.predict([ligand])[0]
        ligand.data.update({self.score_title: score})
        return ligand

    def predict_ligands(self, ligands):
        """Method to score ligands lazily

        Parameters
        ----------
            ligands: iterable of oddt.toolkit.Molecule objects
                Ligands to be scored

        Returns
        -------
            ligand: iterator of oddt.toolkit.Molecule objects
                Scored ligands with updated scores
        """
        # make lazy calculation
        for lig in ligands:
            yield self.predict_ligand(lig)

    def set_protein(self, protein):
        """Proxy method to update protein in all relevant places.

        Parameters
        ----------
            protein: oddt.toolkit.Molecule object
                New default protein

        """
        self.protein = protein
        if hasattr(self.descriptor_generator, 'set_protein'):
            self.descriptor_generator.set_protein(protein)
        else:
            self.descriptor_generator.protein = protein


    def save(self, filename):
        """Saves scoring function to a pickle file.

        Parameters
        ----------
            filename: string
                Pickle filename
        """
        self.set_protein(None)
        return pickle.dump(self, filename, compress=9)[0]

    @classmethod
    def load(self, filename):
        """Loads scoring function from a pickle file.

        Parameters
        ----------
            filename: string
                Pickle filename

        Returns
        -------
            sf: scorer-like object
{'fraction_non_alphanumeric': 0.08803436861750827, 'fraction_numerical': 0.0019719698570321853, 'mean_word_length': 1.8704265211239135, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3274056', 'n_tokens_mistral': 3741, 'n_tokens_neox': 3511, 'n_words': 1164}