import json import logging import re import unicodedata from pathlib import Path import pandas as pd import yaml from sklearn.model_selection import train_test_split # ============================================================ # LOGGING # ============================================================ logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" ) logger = logging.getLogger(__name__) # ============================================================ # PARAMS # ============================================================ def load_params(): with open("params.yaml", "r") as f: return yaml.safe_load(f) # ============================================================ # TRANSFORMER PREPROCESSOR # ============================================================ class TransformerTextPreprocessor: def __init__(self): self.url_re = re.compile( r"https?://\S+|www\.\S+" ) self.mention_re = re.compile( r"@\w+" ) self.html_re = re.compile( r"<[^>]+>" ) self.amp_re = re.compile( r"&|<|>|"|'" ) self.repeat_re = re.compile( r"(.)\1{3,}" ) self.multi_space = re.compile( r"\s+" ) def preprocess(self, text): if not isinstance(text, str): return "" if not text.strip(): return "" text = unicodedata.normalize( "NFKC", text ) text = self.html_re.sub( " ", text ) text = self.amp_re.sub( " ", text ) text = self.mention_re.sub( "@user", text ) text = self.url_re.sub( "http", text ) text = self.repeat_re.sub( r"\1\1", text ) text = self.multi_space.sub( " ", text ).strip() return text def preprocess_batch(self, texts): return [ self.preprocess(t) for t in texts ] # ============================================================ # STRATIFIED SPLIT # ============================================================ def stratified_split( df, label_col, test_size, val_size, seed ): train_val_df, test_df = train_test_split( df, test_size=test_size, stratify=df[label_col], random_state=seed ) val_ratio = val_size / ( 1 - test_size ) train_df, val_df = train_test_split( train_val_df, test_size=val_ratio, stratify=train_val_df[label_col], random_state=seed ) logger.info( f"Train: {len(train_df):,}" ) logger.info( f"Val: {len(val_df):,}" ) logger.info( f"Test: {len(test_df):,}" ) return ( train_df.reset_index(drop=True), val_df.reset_index(drop=True), test_df.reset_index(drop=True) ) # ============================================================ # SAVE OUTPUTS # ============================================================ def save_outputs( train_df, val_df, test_df, output_dir ): output_dir = Path(output_dir) output_dir.mkdir( parents=True, exist_ok=True ) train_path = ( output_dir / "train.parquet" ) val_path = ( output_dir / "val.parquet" ) test_path = ( output_dir / "test.parquet" ) report_path = ( output_dir / "preprocessing_report.json" ) train_df.to_parquet( train_path, index=False ) val_df.to_parquet( val_path, index=False ) test_df.to_parquet( test_path, index=False ) report = { "train_rows": int( len(train_df) ), "val_rows": int( len(val_df) ), "test_rows": int( len(test_df) ), "train_distribution": train_df["Sentiment"] .value_counts() .to_dict(), "val_distribution": val_df["Sentiment"] .value_counts() .to_dict(), "test_distribution": test_df["Sentiment"] .value_counts() .to_dict() } with open( report_path, "w" ) as f: json.dump( report, f, indent=4 ) logger.info( "Saved processed datasets." ) # ============================================================ # MAIN # ============================================================ def main(): params = load_params() cfg = params[ "data_preprocessing" ] logger.info( "Loading cleaned dataset..." ) df = pd.read_parquet( cfg["input_path"] ) logger.info( f"Loaded: {df.shape}" ) train_df, val_df, test_df = ( stratified_split( df=df, label_col="Sentiment", test_size=cfg["test_size"], val_size=cfg["val_size"], seed=cfg["random_state"] ) ) logger.info( "Applying TransformerTextPreprocessor..." ) preprocessor = ( TransformerTextPreprocessor() ) train_df["CommentText"] = ( preprocessor.preprocess_batch( train_df["CommentText"] ) ) val_df["CommentText"] = ( preprocessor.preprocess_batch( val_df["CommentText"] ) ) test_df["CommentText"] = ( preprocessor.preprocess_batch( test_df["CommentText"] ) ) save_outputs( train_df, val_df, test_df, cfg["output_dir"] ) logger.info( "Stage 2 completed successfully." ) if __name__ == "__main__": main()