def _postprocess_output(
    self,
    noteScores: pd.DataFrame,
    userScores: pd.DataFrame,
    ratings: pd.DataFrame,
    noteStatusHistory: pd.DataFrame,
    userEnrollment: pd.DataFrame,
  ) -> Tuple[pd.DataFrame, pd.DataFrame]:
    """Prune noteScores and userScores and augment with any additional columns as necessary.

    Note that ratings, noteStatusHistory and userEnrollment are expected to be the *raw*
    versions which were supplied to "score", not the version output after filtering.
    Operating on the raw versions allows accurately computing statistics over the entire dataset
    (e.g. fraction of users from a modeling group).

    Args:
      noteScores (pd.DataFrame): scoring output for notes
      userScores (pd.DataFrame): scoirng output for users
      ratings (pd.DataFrame): preprocessed ratings
      noteStatusHistory (pd.DataFrame): one row per note; history of when note had each status
      userEnrollment (pd.DataFrame): one row per user specifying enrollment properties

    Returns:
      Tuple[pd.DataFrame, pd.DataFrame]:
        noteScores: note scoring output from _score_notes_and_users
        userScores: user scoring output from _score_notes_and_users
    """
    if self._captureThreshold is None:
      logger.info(f"Skipping postprocessing for {self.get_name()}: captureThreshold is None.")
      return noteScores, userScores
    # Identify notes with enough ratings from within the modeling group.
    logger.info(f"Postprocessing output for {self.get_name()}")
    assert self._includedGroups, "includedGroups must be set"
    userEnrollment = userEnrollment[[c.participantIdKey, c.modelingGroupKey]].rename(
      columns={c.participantIdKey: c.raterParticipantIdKey}
    )
    userEnrollment.loc[:, _IN_GROUP] = (
      userEnrollment[c.modelingGroupKey].isin(self._includedGroups).astype(pd.BooleanDtype())
    )
    ratings = ratings.merge(
      userEnrollment[[c.raterParticipantIdKey, _IN_GROUP]], on=c.raterParticipantIdKey, how="left"
    )
    ratings = ratings.fillna({_IN_GROUP: self._includeUnassigned})
    ratios = ratings[[c.noteIdKey, _IN_GROUP]].groupby(c.noteIdKey).mean().reset_index()
    logger.info(f"  Original noteScores length: {len(noteScores)}")
    noteScores = noteScores.merge(
      ratios[ratios[_IN_GROUP] >= self._captureThreshold][[c.noteIdKey]]
    )
    logger.info(f"  Final noteScores length: {len(noteScores)}")
    return noteScores, userScores
Prune noteScores and userScores and augment with any additional columns as necessary.

    Note that ratings, noteStatusHistory and userEnrollment are expected to be the *raw*
    versions which were supplied to "score", not the version output after filtering.
    Operating on the raw versions allows accurately computing statistics over the entire dataset
    (e.g. fraction of users from a modeling group).

    Args:
      noteScores (pd.DataFrame): scoring output for notes
      userScores (pd.DataFrame): scoirng output for users
      ratings (pd.DataFrame): preprocessed ratings
      noteStatusHistory (pd.DataFrame): one row per note; history of when note had each status
      userEnrollment (pd.DataFrame): one row per user specifying enrollment properties

    Returns:
      Tuple[pd.DataFrame, pd.DataFrame]:
        noteScores: note scoring output from _score_notes_and_users
        userScores: user scoring output from _score_notes_and_users
    
sourcecode/scoring/scorer.py