Spaces:
Runtime error
Runtime error
File size: 6,111 Bytes
341869a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | """
Data Preprocessing Module
Handles text cleaning, tokenization, lemmatization, and class balancing.
"""
import re
import string
import pandas as pd
import numpy as np
from collections import Counter
import nltk
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer
from textblob import TextBlob
# Download required NLTK data
def download_nltk_data():
resources = ['stopwords', 'wordnet', 'omw-1.4', 'punkt', 'averaged_perceptron_tagger']
for r in resources:
try:
nltk.download(r, quiet=True)
except Exception:
pass
download_nltk_data()
# βββββββββββββββββββββββββββββββββββββββββββββ
# Label Mapping
# βββββββββββββββββββββββββββββββββββββββββββββ
# Map dataset labels β unified 7-class schema
LABEL_MAP = {
"Normal": "Normal",
"Anxiety": "Anxiety",
"Depression": "Depression",
"Suicidal": "Suicidal",
"Bipolar": "Bipolar",
"Stress": "Stress",
"Personality disorder": "Personality Disorder",
# Aliases (in case of variations)
"PTSD": "Stress",
"BPD": "Personality Disorder",
"Borderline": "Personality Disorder",
}
LABEL2ID = {
"Normal": 0,
"Anxiety": 1,
"Depression": 2,
"Suicidal": 3,
"Bipolar": 4,
"Stress": 5,
"Personality Disorder": 6,
}
ID2LABEL = {v: k for k, v in LABEL2ID.items()}
NUM_LABELS = len(LABEL2ID)
# βββββββββββββββββββββββββββββββββββββββββββββ
# Text Cleaning
# βββββββββββββββββββββββββββββββββββββββββββββ
def clean_text(text: str) -> str:
"""
Thorough text cleaning pipeline:
- Lowercase
- Remove URLs, mentions, hashtags
- Remove special characters / digits
- Normalize whitespace
"""
if not isinstance(text, str):
return ""
# Lowercase
text = text.lower()
# Remove URLs
text = re.sub(r"http\S+|www\S+|https\S+", "", text, flags=re.MULTILINE)
# Remove Reddit-style mentions and subreddits
text = re.sub(r"@\w+|r/\w+|u/\w+", "", text)
# Remove HTML entities
text = re.sub(r"&[a-z]+;", " ", text)
# Remove digits
text = re.sub(r"\d+", "", text)
# Remove punctuation (keep apostrophes for contractions)
text = text.translate(str.maketrans("", "", string.punctuation.replace("'", "")))
# Remove extra whitespace
text = re.sub(r"\s+", " ", text).strip()
return text
def lemmatize_text(text: str) -> str:
"""Lemmatize tokens and remove stopwords."""
lemmatizer = WordNetLemmatizer()
stop_words = set(stopwords.words("english"))
tokens = text.split()
tokens = [lemmatizer.lemmatize(t) for t in tokens if t not in stop_words and len(t) > 2]
return " ".join(tokens)
def get_sentiment_features(text: str) -> dict:
"""
Extract TextBlob sentiment features:
- polarity (-1 to 1)
- subjectivity (0 to 1)
- sentiment_label: Positive / Neutral / Negative
"""
blob = TextBlob(text)
polarity = blob.sentiment.polarity
subjectivity = blob.sentiment.subjectivity
if polarity > 0.05:
label = "Positive"
elif polarity < -0.05:
label = "Negative"
else:
label = "Neutral"
return {
"polarity": polarity,
"subjectivity": subjectivity,
"sentiment_label": label,
}
def full_preprocess(text: str, lemmatize: bool = True) -> str:
"""Full preprocessing pipeline for inference."""
text = clean_text(text)
if lemmatize:
text = lemmatize_text(text)
return text
# βββββββββββββββββββββββββββββββββββββββββββββ
# Dataset Loading
# βββββββββββββββββββββββββββββββββββββββββββββ
def load_dataset(csv_path: str, max_len: int = 512) -> pd.DataFrame:
"""
Load and preprocess the CSV dataset.
Expected columns: (index), statement, status
Returns a clean DataFrame with columns: text, label, label_id, sentiment_*
"""
print(f"[DATA] Loading dataset from: {csv_path}")
df = pd.read_csv(csv_path, index_col=0)
# Rename columns
df.columns = [c.strip().lower() for c in df.columns]
if "statement" in df.columns:
df.rename(columns={"statement": "text", "status": "label"}, inplace=True)
# Drop nulls
df.dropna(subset=["text", "label"], inplace=True)
# Normalize labels
df["label"] = df["label"].str.strip().map(LABEL_MAP)
df.dropna(subset=["label"], inplace=True)
# Map to numeric IDs
df["label_id"] = df["label"].map(LABEL2ID)
# Clean text
print("[DATA] Cleaning text...")
df["text_clean"] = df["text"].apply(clean_text)
# Filter too-short texts
df = df[df["text_clean"].str.len() > 5].reset_index(drop=True)
# Sentiment features
print("[DATA] Extracting sentiment features...")
sentiment = df["text"].apply(get_sentiment_features).apply(pd.Series)
df = pd.concat([df, sentiment], axis=1)
print(f"[DATA] Dataset loaded: {len(df)} samples")
print(f"[DATA] Label distribution:\n{df['label'].value_counts()}")
return df
def compute_class_weights(label_ids: np.ndarray) -> np.ndarray:
"""
Compute inverse-frequency class weights for weighted loss.
Returns array of shape [num_classes].
"""
counts = Counter(label_ids)
total = sum(counts.values())
weights = np.zeros(NUM_LABELS, dtype=np.float32)
for cls_id, count in counts.items():
weights[cls_id] = total / (NUM_LABELS * count)
return weights
|