CoolFace
Apppublic

rajtheman/support-integrity-auditor

sourceHugging Facemitupdated 4mo agoView on Hugging Face
1likes
data_prep.py120 linesDownload Raw Back to src
1"""2Phase 1 — Data preparation for the Support Integrity Auditor.3 4Responsibilities5----------------61. Load the raw CRM ticket CSV.72. Extract the *real issue sentence* from each description (the dataset pads a8   genuine leading sentence with random faker words — see README §Data).93. Build the text the classifier sees + structured metadata features.104. Create a FIXED, stratified train/test split (held-out evaluation set) that is11   independent of any pseudo-label, so labels can never leak into the split.12 13Run:  python3 src/data_prep.py14Out:  artifacts/data/processed.parquet  (+ a printed summary)15"""16from __future__ import annotations17import os, re, sys18import pandas as pd19 20# make `from src import config` work when run as a plain script21sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))22from src import config as C23 24from sklearn.model_selection import train_test_split25 26# --------------------------------------------------------------------------- #27# Text extraction28# --------------------------------------------------------------------------- #29_GREETING = re.compile(r"^\s*(hi|hello|hey|dear)\b[^,.:;!?]*[,:]?\s*", re.IGNORECASE)30_FIRST_SENT = re.compile(r"(.+?[.?!])(?:\s|$)")31 32def extract_leading_sentence(desc: str) -> str:33    """Strip the greeting and return the first sentence (the genuine issue).34 35    The trailing faker words form a second 'sentence'; we deliberately drop them36    because raw keyword counts over the filler are misleading and are exactly the37    surface an adversarial ticket would attack.38    """39    t = str(desc).strip()40    t = _GREETING.sub("", t, count=1)41    m = _FIRST_SENT.search(t)42    lead = (m.group(1) if m else t).strip()43    return lead or t44 45def build_model_text(row: pd.Series) -> str:46    """Text fed to DeBERTa: assigned priority + structured tags + the real issue.47 48    Including the *assigned priority* is intentional and correct — the task is to49    judge whether THAT priority disagrees with the ticket's content. The inferred50    severity / mismatch label is never shown to the model.51    """52    tier = C.domain_tier(row[C.COL_EMAIL])53    return (54        f"priority: {row[C.COL_PRIORITY]} | "55        f"channel: {row[C.COL_CHANNEL]} | "56        f"category: {row[C.COL_CATEGORY]} | "57        f"tier: {tier} | "58        f"{str(row[C.COL_SUBJECT]).strip()}. {row['lead_sentence']}"59    )60 61# --------------------------------------------------------------------------- #62# Pipeline63# --------------------------------------------------------------------------- #64def load_raw() -> pd.DataFrame:65    df = pd.read_csv(C.RAW_CSV)66    df.columns = [c.strip() for c in df.columns]67    return df68 69def build_features(df: pd.DataFrame) -> pd.DataFrame:70    df = df.copy()71    df[C.COL_RES_HRS] = pd.to_numeric(df[C.COL_RES_HRS], errors="coerce")72    df[C.COL_SAT] = pd.to_numeric(df[C.COL_SAT], errors="coerce")73 74    df["lead_sentence"] = df[C.COL_DESC].map(extract_leading_sentence)75    df["domain_tier"] = df[C.COL_EMAIL].map(C.domain_tier)76    df["priority_score"] = df[C.COL_PRIORITY].map(C.PRIORITY_TO_SCORE)77    df["category_prior"] = df[C.COL_CATEGORY].map(C.CATEGORY_SEVERITY_PRIOR)78    df["model_text"] = df.apply(build_model_text, axis=1)79    return df80 81def make_split(df: pd.DataFrame) -> pd.DataFrame:82    df = df.copy()83    idx_train, idx_test = train_test_split(84        df.index,85        test_size=C.TRAIN["test_size"],86        random_state=C.SEED,87        stratify=df[C.COL_PRIORITY],     # stable strata, independent of labels88    )89    df["split"] = "train"90    df.loc[idx_test, "split"] = "test"91    return df92 93def main() -> pd.DataFrame:94    df = load_raw()95    print(f"[load] {len(df):,} rows x {df.shape[1]} cols")96    df = build_features(df)97    df = make_split(df)98 99    out = C.PROC_DIR / "processed.parquet"100    df.to_parquet(out, index=False)101 102    # summary103    print(f"[split] train={int((df['split']=='train').sum()):,}  "104          f"test={int((df['split']=='test').sum()):,}")105    print("[priority dist]")106    print(df[C.COL_PRIORITY].value_counts(normalize=True).round(3).to_string())107    print("[domain tier dist]")108    print(df["domain_tier"].value_counts(normalize=True).round(3).to_string())109    print("\n[sample lead-sentence extraction]")110    for _, r in df.head(4).iterrows():111        print(f"  RAW : {r[C.COL_DESC][:90]}")112        print(f"  LEAD: {r['lead_sentence']}")113    print("\n[sample model_text]")114    print("  " + df.iloc[1]["model_text"])115    print(f"\n[saved] {out}")116    return df117 118if __name__ == "__main__":119    main()120