def include_tf_idf(local_repr, scores, concept_alignments, tf_idf):
    """
    Given a matrix of attributes for nodes/edges, incorporate TF-IDF counts as a new attribute.
    The attribute is binirized in 4 points: -4.5, 0, 3, 7.5; as these were, respectively,
    the minimum (using a maximum of 1.5 interquartile range), 1st quartile, 2nd quartile and 3rd quartile
    values in our training set.
    This function also adds another feature indicating if the concept is in the larger corpus used to
    calculate the DF values.

    Parameters:
        local_repr (DataFrame): Local representations (attribute matrix) for each node/edge.
        scores (Counter): TF-IDF values for each concept.
        concept_alignments (dict): Mapping from concepts to words.
        tf_idf (CountVectorizer): CountVectorizer used to calculate the TF-IDF values.
                                  Used to get vocabulary mappings into indices.
    
    Returns:
        DataFrame: `local_repr` extended with the TF-IDF values for each row.
    """
    for idx, feats in local_repr.iterrows():
        concept = feats['concept']

        if concept in scores:
            score = scores[feats['concept']]
        else:
            score = -float('inf')  # Will set all threasholds to 0

        # Binarize the value (4 additional features)
        for t in [-4.5, 0, 3, 7.5]:
            if score >= t:
                local_repr.loc[[idx], f'tf_idf_{t}'] = 1.0
            else:
                local_repr.loc[[idx], f'tf_idf_{t}'] = 0.0

        # Indicate if the concept (any word to which it is aligned) is present in the larger vocabulary.
        in_vocab = False
        if concept in concept_alignments:
            for w in concept_alignments[concept]:
                if w in tf_idf.vocabulary_:
                    in_vocab = True
                    break
        if in_vocab:
            local_repr.loc[[idx], 'larger_corpus'] = 1.0
        else:
            local_repr.loc[[idx], 'larger_corpus'] = 0.0
    return local_repr