docs: def predict_to_dataframe(
              self,
              peptides,
              alleles=None,
              allele=None,
              throw=True,
              include_individual_model_predictions=False,
              include_percentile_ranks=True,
              include_confidence_intervals=True,
              centrality_measure=DEFAULT_CENTRALITY_MEASURE):
          """
          Predict nM binding affinities. Gives more detailed output than `predict`
          method, including 5-95% prediction intervals.
          
          If multiple predictors are available for an allele, the predictions are
          the geometric means of the individual model predictions.
          
          One of 'allele' or 'alleles' must be specified. If 'allele' is specified
          all predictions will be for the given allele. If 'alleles' is specified
          it must be the same length as 'peptides' and give the allele
          corresponding to each peptide. 
          
          Parameters
          ----------
          peptides : `EncodableSequences` or list of string
          alleles : list of string
          allele : string
          throw : boolean
              If True, a ValueError will be raised in the case of unsupported
              alleles or peptide lengths. If False, a warning will be logged and
              the predictions for the unsupported alleles or peptides will be NaN.
          include_individual_model_predictions : boolean
              If True, the predictions of each individual model are included as
              columns in the result dataframe.
          include_percentile_ranks : boolean, default True
              If True, a "prediction_percentile" column will be included giving the
              percentile ranks. If no percentile rank information is available,
              this will be ignored with a warning.
          centrality_measure : string or callable
              Measure of central tendency to use to combine predictions in the
              ensemble. Options include: mean, median, robust_mean.
  
          Returns
          -------
          `pandas.DataFrame` of predictions
          """
          if isinstance(peptides, string_types):
              raise TypeError("peptides must be a list or array, not a string")
          if isinstance(alleles, string_types):
              raise TypeError("alleles must be a list or array, not a string")
          if allele is None and alleles is None:
              raise ValueError("Must specify 'allele' or 'alleles'.")
  
          peptides = EncodableSequences.create(peptides)
          df = pandas.DataFrame({
              'peptide': peptides.sequences
          }, copy=False)
  
          if allele is not None:
              if alleles is not None:
                  raise ValueError("Specify exactly one of allele or alleles")
              df["allele"] = allele
              normalized_allele = mhcnames.normalize_allele_name(allele)
              df["normalized_allele"] = normalized_allele
              unique_alleles = [normalized_allele]
          else:
              df["allele"] = numpy.array(alleles)
              df["normalized_allele"] = df.allele.map(
                  mhcnames.normalize_allele_name)
              unique_alleles = df.normalized_allele.unique()
  
          if len(df) == 0:
              # No predictions.
              logging.warning("Predicting for 0 peptides.")
              empty_result = pandas.DataFrame(
                  columns=[
                      'peptide',
                      'allele',
                      'prediction',
                      'prediction_low',
                      'prediction_high'
                  ])
              return empty_result
  
          (min_peptide_length, max_peptide_length) = (
              self.supported_peptide_lengths)
  
          if (peptides.min_length < min_peptide_length or
                  peptides.max_length > max_peptide_length):
              # Only compute this if needed
              all_peptide_lengths_supported = False
              sequence_length = df.peptide.str.len()
              df["supported_peptide_length"] = (
                  (sequence_length >= min_peptide_length) &
                  (sequence_length <= max_peptide_length))
              if (~df.supported_peptide_length).any():
                  msg = (
                      "%d peptides have lengths outside of supported range [%d, %d]: "
                      "%s" % (
                          (~df.supported_peptide_length).sum(),
                          min_peptide_length,
                          max_peptide_length,
                          str(df.ix[~df.supported_peptide_length].peptide.unique())))
                  logging.warning(msg)
                  if throw:
                      raise ValueError(msg)
          else:
              # Handle common case efficiently.
              df["supported_peptide_length"] = True
              all_peptide_lengths_supported = True
  
          num_pan_models = len(self.class1_pan_allele_models)
          max_single_allele_models = max(
              len(self.allele_to_allele_specific_models.get(allele, []))
              for allele in unique_alleles
          )
          predictions_array = numpy.zeros(
              shape=(df.shape[0], num_pan_models + max_single_allele_models),
              dtype="float64")
          predictions_array[:] = numpy.nan
  
          if self.class1_pan_allele_models:
              unsupported_alleles = [
                  allele for allele in
                  df.normalized_allele.unique()
                  if allele not in self.allele_to_fixed_length_sequence
              ]
              if unsupported_alleles:
                  msg = (
                      "No sequences for allele(s): %s.\n"
                      "Supported alleles: %s" % (
                          " ".join(unsupported_alleles),
                          " ".join(sorted(self.allele_to_fixed_length_sequence))))
                  logging.warning(msg)
                  if throw:
                      raise ValueError(msg)
              mask = df.supported_peptide_length
              if mask.sum() > 0:
                  masked_allele_encoding = AlleleEncoding(
                      df.loc[mask].normalized_allele,
                      allele_to_fixed_length_sequence=self.allele_to_fixed_length_sequence)
                  masked_peptides = peptides.sequences[mask]
                  for (i, model) in enumerate(self.class1_pan_allele_models):
                      predictions_array[mask, i] = model.predict(
                          masked_peptides,
                          allele_encoding=masked_allele_encoding)
  
          if self.allele_to_allele_specific_models:
              unsupported_alleles = [
                  allele for allele in unique_alleles
                  if not self.allele_to_allele_specific_models.get(allele)
              ]
              if unsupported_alleles:
                  msg = (
                      "No single-allele models for allele(s): %s.\n"
                      "Supported alleles are: %s" % (
                          " ".join(unsupported_alleles),
                          " ".join(sorted(self.allele_to_allele_specific_models))))
                  logging.warning(msg)
                  if throw:
                      raise ValueError(msg)
  
              for allele in unique_alleles:
                  models = self.allele_to_allele_specific_models.get(allele, [])
                  if len(unique_alleles) == 1 and all_peptide_lengths_supported:
                      mask = None
                  else:
                      mask = (
                          (df.normalized_allele == allele) &
                          df.supported_peptide_length).values
                  if mask is None or mask.all():
                      # Common case optimization
                      for (i, model) in enumerate(models):
                          predictions_array[:, num_pan_models + i] = (
                              model.predict(peptides))
                  elif mask.sum() > 0:
                      peptides_for_allele = EncodableSequences.create(
                          df.ix[mask].peptide.values)
                      for (i, model) in enumerate(models):
                          predictions_array[
                              mask,
                              num_pan_models + i,
                          ] = model.predict(peptides_for_allele)
  
          if callable(centrality_measure):
              centrality_function = centrality_measure
          else:
              centrality_function = CENTRALITY_MEASURES[centrality_measure]
  
          logs = numpy.log(predictions_array)
          log_centers = centrality_function(logs)
          df["prediction"] = numpy.exp(log_centers)
  
          if include_confidence_intervals:
              df["prediction_low"] = numpy.exp(numpy.nanpercentile(logs, 5.0, axis=1))
              df["prediction_high"] = numpy.exp(numpy.nanpercentile(logs, 95.0, axis=1))
  
          if include_individual_model_predictions:
              for i in range(num_pan_models):
                  df["model_pan_%d" % i] = predictions_array[:, i]
  
              for i in range(max_single_allele_models):
                  df["model_single_%d" % i] = predictions_array[
                      :, num_pan_models + i
                  ]
  
          if include_percentile_ranks:
              if self.allele_to_percent_rank_transform:
                  df["prediction_percentile"] = self.percentile_ranks(
                      df.prediction,
                      alleles=df.normalized_allele.values,
                      throw=throw)
              else:
                  warnings.warn("No percentile rank information available.")
  
          del df["supported_peptide_length"]
          del df["normalized_allele"]
          return df