CoolFace
Apppublic

biplobgon/product-recommendation-system

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes
user_features.py120 linesDownload Raw Back to features
1"""2features/user_features.py3--------------------------4Build user-level features from the events DataFrame.5 6EDA context7-----------8- Most visitors have only 1–3 events (highly sparse user history).9- Three event types: view (>95%), addtocart (<3%), transaction (<0.5%).10- Peak activity hours: 17:00–21:00.11- Session boundary: ~1 hour of inactivity.12"""13from __future__ import annotations14 15import pandas as pd16import numpy as np17 18from utils.logger import get_logger19 20logger = get_logger(__name__)21 22# Implicit feedback weights derived from EDA funnel analysis23EVENT_WEIGHTS = {"view": 1, "addtocart": 5, "transaction": 10}24 25 26def build_user_features(events: pd.DataFrame) -> pd.DataFrame:27    """Compute per-visitor aggregate features.28 29    Parameters30    ----------31    events:32        Raw events DataFrame with columns:33        [timestamp, visitorid, event, itemid, transactionid].34 35    Returns36    -------37    pd.DataFrame38        One row per visitor with columns:39        - visitorid40        - n_views, n_addtocart, n_transactions41        - n_unique_items42        - weighted_interaction_score43        - active_days44        - preferred_hour  (mode hour of activity)45        - conversion_rate  (transactions / views)46        - is_cold_start    (True if only 1 interaction)47    """48    logger.info("Building user features from %d events …", len(events))49 50    df = events.copy()51    df["datetime"] = pd.to_datetime(df["timestamp"], unit="ms")52    df["hour"] = df["datetime"].dt.hour53    df["date"] = df["datetime"].dt.date54    df["weight"] = df["event"].map(EVENT_WEIGHTS).fillna(1)55 56    agg = df.groupby("visitorid").agg(57        n_views=("event", lambda x: (x == "view").sum()),58        n_addtocart=("event", lambda x: (x == "addtocart").sum()),59        n_transactions=("event", lambda x: (x == "transaction").sum()),60        n_unique_items=("itemid", "nunique"),61        weighted_interaction_score=("weight", "sum"),62        active_days=("date", "nunique"),63        preferred_hour=("hour", lambda x: x.mode()[0] if not x.empty else -1),64        first_seen=("datetime", "min"),65        last_seen=("datetime", "max"),66    ).reset_index()67 68    agg["conversion_rate"] = (69        agg["n_transactions"] / agg["n_views"].replace(0, np.nan)70    ).fillna(0.0)71 72    agg["is_cold_start"] = (73        agg["n_views"] + agg["n_addtocart"] + agg["n_transactions"]74    ) <= 175 76    logger.info(77        "User features built: %d users, %.1f%% cold-start.",78        len(agg),79        agg["is_cold_start"].mean() * 100,80    )81    return agg82 83 84def build_user_item_matrix(85    events: pd.DataFrame,86    event_weights: dict[str, int] | None = None,87) -> pd.DataFrame:88    """Build a sparse-friendly user × item interaction matrix.89 90    Returns a DataFrame in COO-style format (visitorid, itemid, score)91    suitable for passing to implicit ALS or SVD models.92 93    Parameters94    ----------95    events:96        Raw events DataFrame.97    event_weights:98        Override default EVENT_WEIGHTS mapping.99 100    Returns101    -------102    pd.DataFrame with columns [visitorid, itemid, score].103    """104    weights = event_weights or EVENT_WEIGHTS105    df = events.copy()106    df["score"] = df["event"].map(weights).fillna(1)107 108    matrix = (109        df.groupby(["visitorid", "itemid"])["score"]110        .sum()111        .reset_index()112    )113    logger.info(114        "User-item matrix: %d interactions across %d users × %d items.",115        len(matrix),116        matrix["visitorid"].nunique(),117        matrix["itemid"].nunique(),118    )119    return matrix120