CoolFace
Apppublic

TwinklData/Community_Collections_App

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
shortlist.py96 linesDownload Raw Back to src
1import pandas as pd2 3def shortlist_applications(4    df: pd.DataFrame,5    k: int = None,6    threshold: float = None,7    weight_necessity: float = 0.55,8    weight_length: float = 0.1,9    weight_usage: float = 0.3510) -> pd.DataFrame:11    """12    Automatically shortlist grant applications by combining necessity index,13    application length (favoring longer submissions), and the specificity of the14    requested usage list.15 16    Args:17        df: Processed DataFrame including columns 'necessity_index', 'word_count', and 'Usage'.18        k: Number of top applications to select. Mutually exclusive with threshold.19        threshold: Score threshold above which to select applications. Mutually exclusive with k.20        weight_necessity: Weight for necessity_index (0 to 1).21        weight_length: Weight for length score (0 to 1).22        weight_usage: Weight for usage specificity (0 to 1).23 24    Returns:25        DataFrame of shortlisted applications sorted by descending combined score.26    """27    # Ensure exactly one of k or threshold is provided28    if (k is None and threshold is None) or (k is not None and threshold is not None):29        raise ValueError("Provide exactly one of k or threshold")30 31    # Normalize necessity_index (assumed already between 0 and 1)32    necessity = df['necessity_index']33 34    # Compute length score: longer applications score higher (more context is valued)35    word_counts = df['word_count']36    min_wc, max_wc = word_counts.min(), word_counts.max()37    if max_wc != min_wc:38        length_score = (word_counts - min_wc) / (max_wc - min_wc)39    else:40        length_score = pd.Series([0.5] * len(df), index=df.index)41 42    # Compute usage score based on *how many* concrete usage items were extracted43    # (previously this was a simple binary flag).  Longer lists are taken as a44    # signal of greater specificity → higher score.  We first count the number45    # of non‑empty items, then min‑max normalise the counts so the resulting46    # score is between 0 and 1 (mirroring the approach used for47    # `length_score`).48 49    def count_valid_usage(items):50        """Return the number of meaningful usage entries in *items*.51 52        The Usage column is expected to contain a list of strings (output of53        `extract_usage.extract_usage`). We treat empty strings and the literal54        "None" (case‑insensitive) as non‑entries.55        """56        if not isinstance(items, (list, tuple, set)):57            return 058        return sum(59            160            for item in items61            if isinstance(item, str) and item.strip() and item.strip().lower() != "none"62        )63 64    usage_counts = df["usage"].apply(count_valid_usage)65 66    min_uc, max_uc = usage_counts.min(), usage_counts.max()67    if max_uc != min_uc:68        usage_score = (usage_counts - min_uc) / (max_uc - min_uc)69    else:70        # If all rows have identical counts (e.g. all zero), assign a neutral 0.571        usage_score = pd.Series([0.5] * len(df), index=df.index)72 73    # Combine scores with normalized weights74    total_weight = weight_necessity + weight_length + weight_usage75    weights = {76        'necessity': weight_necessity / total_weight,77        'length': weight_length / total_weight,78        'usage': weight_usage / total_weight,79    }80    combined = (81        weights['necessity'] * necessity +82        weights['length'] * length_score +83        weights['usage'] * usage_score84    )85    df = df.copy()86    df['shortlist_score'] = combined87 88    # Select applications based on k or threshold89    df_sorted = df.sort_values('shortlist_score', ascending=False)90    if k is not None:91        result = df_sorted.head(k)92    else:93        result = df_sorted[df_sorted['shortlist_score'] >= threshold]94 95    return result96