| import argparse |
| from pathlib import Path |
|
|
| import pandas as pd |
|
|
| from utils import clean_title, ensure_dir, normalize_label, save_json |
|
|
|
|
| def load_input(path: str | Path) -> pd.DataFrame: |
| path = Path(path) |
| if path.suffix.lower() == ".csv": |
| return pd.read_csv(path) |
| if path.suffix.lower() == ".jsonl": |
| return pd.read_json(path, lines=True) |
| raise ValueError(f"Unsupported input format: {path.suffix}") |
|
|
|
|
| def preprocess_frame(dataframe: pd.DataFrame, min_title_length: int) -> tuple[pd.DataFrame, dict]: |
| stats = {"before_rows": int(len(dataframe))} |
| if "title" not in dataframe.columns or "label" not in dataframe.columns: |
| raise ValueError("Input data must contain `title` and `label` columns.") |
|
|
| processed = dataframe.copy() |
| processed["title"] = processed["title"].map(clean_title) |
| processed = processed[processed["title"].str.len() >= min_title_length].copy() |
| stats["after_title_filter"] = int(len(processed)) |
|
|
| processed["label_id"] = processed["label"].map(normalize_label) |
| processed["label"] = processed["label_id"].map({0: "non-clickbait", 1: "clickbait"}) |
| processed = processed.drop_duplicates(subset=["title"]).reset_index(drop=True) |
| stats["after_deduplicate"] = int(len(processed)) |
|
|
| processed["node_id"] = processed.index.astype(int) |
| preferred_columns = ["node_id", "id", "title", "label", "label_id", "source", "category", "publish_datetime", "url"] |
| existing_columns = [column for column in preferred_columns if column in processed.columns] |
| remaining_columns = [column for column in processed.columns if column not in existing_columns] |
| processed = processed[existing_columns + remaining_columns] |
| stats["label_distribution"] = processed["label"].value_counts().to_dict() |
| return processed, stats |
|
|
|
|
| def main() -> None: |
| parser = argparse.ArgumentParser(description="Preprocess ViClickbait dataset.") |
| parser.add_argument("--input", required=True, help="Path to raw CSV or JSONL.") |
| parser.add_argument("--output", required=True, help="Path to write processed CSV.") |
| parser.add_argument("--label_map", default=None, help="Optional label_map.json path.") |
| parser.add_argument("--stats_path", default=None, help="Optional preprocess_stats.json path.") |
| parser.add_argument("--min_title_length", type=int, default=4) |
| args = parser.parse_args() |
|
|
| frame = load_input(args.input) |
| processed, stats = preprocess_frame(frame, min_title_length=args.min_title_length) |
|
|
| output_path = Path(args.output) |
| ensure_dir(output_path.parent) |
| processed.to_csv(output_path, index=False) |
|
|
| label_map_path = Path(args.label_map) if args.label_map else output_path.parent / "label_map.json" |
| save_json({"label_to_id": {"non-clickbait": 0, "clickbait": 1}}, label_map_path) |
|
|
| stats_path = Path(args.stats_path) if args.stats_path else output_path.parent / "preprocess_stats.json" |
| save_json(stats, stats_path) |
|
|
| print(f"Saved processed data to {output_path}") |
| print(f"Rows after preprocessing: {len(processed)}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|