'Estimate the CSP decomposition on epochs.
Parameters
X : ndarray, shape (n_epochs, n_channels, n_times)
The data on which to estimate the CSP.
y : array, shape (n_epochs,)
The class for each epoch.
Returns
self : instance of CSP
Returns the modified instance.'

def fit(self, X, y):

    if (not isinstance(X, np.ndarray)):
        raise ValueError(('X    should    be    of    type    ndarray    (got    %s).' % type(X)))
    self._check_Xy(X, y)
    n_channels = X.shape[1]
    self._classes = np.unique(y)
    n_classes = len(self._classes)
    if (n_classes < 2):
        raise ValueError('n_classes    must    be    >=    2.')
    covs = np.zeros((n_classes, n_channels, n_channels))
    sample_weights = list()
    for (class_idx, this_class) in enumerate(self._classes):
        if (self.cov_est == 'concat'):
            class_ = np.transpose(X[(y == this_class)], [1, 0, 2])
            class_ = class_.reshape(n_channels, (-1))
            cov = _regularized_covariance(class_, reg=self.reg)
            weight = sum((y == this_class))
        elif (self.cov_est == 'epoch'):
            class_ = X[(y == this_class)]
            cov = np.zeros((n_channels, n_channels))
            for this_X in class_:
                cov += _regularized_covariance(this_X, reg=self.reg)
            cov /= len(class_)
            weight = len(class_)
        covs[class_idx] = cov
        if self.norm_trace:
            covs[class_idx] /= np.trace(cov)
        sample_weights.append(weight)
    if (n_classes == 2):
        (eigen_values, eigen_vectors) = linalg.eigh(covs[0], covs.sum(0))
        ix = np.argsort(np.abs((eigen_values - 0.5)))[::(-1)]
    else:
        (eigen_vectors, D) = _ajd_pham(covs)
        mean_cov = np.average(covs, axis=0, weights=sample_weights)
        eigen_vectors = eigen_vectors.T
        for ii in range(eigen_vectors.shape[1]):
            tmp = np.dot(np.dot(eigen_vectors[:, ii].T, mean_cov), eigen_vectors[:, ii])
            eigen_vectors[:, ii] /= np.sqrt(tmp)
        class_probas = [np.mean((y == _class)) for _class in self._classes]
        mutual_info = []
        for jj in range(eigen_vectors.shape[1]):
            (aa, bb) = (0, 0)
            for (cov, prob) in zip(covs, class_probas):
                tmp = np.dot(np.dot(eigen_vectors[:, jj].T, cov), eigen_vectors[:, jj])
                aa += (prob * np.log(np.sqrt(tmp)))
                bb += (prob * ((tmp ** 2) - 1))
            mi = (- (aa + ((3.0 / 16) * (bb ** 2))))
            mutual_info.append(mi)
        ix = np.argsort(mutual_info)[::(-1)]
    eigen_vectors = eigen_vectors[:, ix]
    self.filters_ = eigen_vectors.T
    self.patterns_ = linalg.pinv(eigen_vectors)
    pick_filters = self.filters_[:self.n_components]
    X = np.asarray([np.dot(pick_filters, epoch) for epoch in X])
    X = (X ** 2).mean(axis=2)
    self.mean_ = X.mean(axis=0)
    self.std_ = X.std(axis=0)
    return self
