text: import sys
  from math import erf,sqrt
  from ..utils.autoconv import autoconv
  
  class Mixture:
      ID = "mixture"
  
      def __init__(self, n_components, cutoff):
          self.n_components = n_components
          self.cutoff = cutoff
          self.gmms    = None
  
      @staticmethod
      def register(parser):
          parser.add_argument("--" + Mixture.ID, nargs = 2, metavar = ("n_subpops", "threshold"),
                              help = "Use a gaussian mixture model, reporting values whose probability is " +
                              "below the threshold, as predicted by a model of the data comprised of n_subpops "+
                              "gaussians. Suggested values: 2, 0.3.")
  
      @staticmethod
      def from_parse(params):
          return Mixture(*map(autoconv, params))
  
      @staticmethod
      def mahalanobis(x, gmm, component):
          mean = gmm.means_[component]
          covar = gmm.covars_[component]
          u = x - mean
          v = u.transpose()
  
          return sqrt(v.dot(((1 / covar) * u).transpose()))
  
      def make_gmm(self, to_fit):
          from sklearn import mixture
          gmm = mixture.GMM(n_components = self.n_components)
          gmm.fit(to_fit)
          return gmm
  
      def fit(self, Xs, analyzer):
          from matplotlib import pyplot
  
          correlations = zip(*(X[0] for X in Xs))
          self.gmms = [self.make_gmm(to_fit) for to_fit in correlations]
  
          # TODO: add command line option to show graph
          # lp, resp = self.gmms[i].score_samples(to_fit)
          # ps = [self.test_one(x, i) for x in to_fit]
          # pyplot.hist(ps, bins = 30)
          # pyplot.show()
  
      def test_one(self, xi, gmm_pos):
          from numpy import argmax
          gmm = self.gmms[gmm_pos]
          _, resp = gmm.score_samples([xi])
          component = argmax(resp)
          distance = Mixture.mahalanobis(xi, gmm, component)
          return component, gmm.weights_[component] * (1-erf(distance / sqrt(2)))
  
      def find_discrepancies(self, X, index):
          correlations = X[0]
          discrepancies = []
  
          for id, (correlation, gmm_pos, cutoff) in enumerate(zip(correlations, range(len(self.gmms)), range(len(self.gmms)))):
              _, probability = self.test_one(correlation, gmm_pos)
              if probability < self.cutoff:
                  discrepancies.append(((0, id),))
  
          return discrepancies
  
      def format_ndarray(self, array):
          return '(' +  ', '.join('{:.2e}'.format(x) for x in array) +  ')'
  
      def more_info(self, discrepancy, description, X, indent = "", pipe = sys.stdout):
          field_id, gmm_pos = discrepancy[0]
          outlier = X[field_id][gmm_pos]
          component, prob = self.test_one(outlier, gmm_pos)
          gmm = self.gmms[gmm_pos]
          mean, covar, weight = gmm.means_[component], gmm.covars_[component], gmm.weights_[component]
  
          pipe.write(indent + '• Best match Gaussian component:\n')
          pipe.write(indent + indent + 'weight = {:.2e}\n'.format(weight))
          pipe.write(indent + indent + 'mean = {}\n'.format( self.format_ndarray(mean)))
          pipe.write(indent + indent + 'covariance matrix = {}\n'.format( self.format_ndarray(covar)))
  
          pipe.write(indent + '• Probability of being generated by best match Gaussian: {:.2e} (threshold: {})\n'.format(prob, self.cutoff))
  