Download model-3/src/data.py from Jyo-K/PoPTard5: direct link, hf CLI and curl.
- Browser
- Download file 4.05 kB
-
https://huggingface.co/Jyo-K/PoPTard5/resolve/main/model-3/src/data.py
- Command line
-
hf download hf://Jyo-K/PoPTard5/model-3/src/data.py
-
curl -L -o data.py https://huggingface.co/Jyo-K/PoPTard5/resolve/main/model-3/src/data.py
4.05 kB
| """Loading, S1 augmentation with ground truth, sanity checks and splits. | |
| Unchanged from model-2 except: load_data() takes an explicit paths dict so the | |
| same code serves train and test, and test loading needs no ground truth. | |
| """ | |
| import os | |
| import numpy as np | |
| import pandas as pd | |
| S1_COLS = ["entity_id", "business_name", "business_address", "country", "country_norm", | |
| "name_norm", "name_core", "legal_suffix", "address_abbrev_norm", "address_numbers"] | |
| S23_COLS = S1_COLS | |
| def read_any(path, columns=None): | |
| if path.endswith(".parquet"): | |
| df = pd.read_parquet(path, columns=columns) | |
| else: | |
| df = pd.read_csv(path, sep="\t", dtype=str, keep_default_na=False, usecols=columns) | |
| for c in df.columns: | |
| df[c] = df[c].fillna("").astype(str) | |
| return df | |
| def split_ids(s): | |
| return [x for x in str(s).split(",") if x] | |
| def load_data(paths, with_gt=True): | |
| s1 = read_any(paths["source1"], S1_COLS) | |
| s2 = read_any(paths["source2"], S23_COLS) | |
| s3 = read_any(paths["source3"], S23_COLS) | |
| s23 = pd.concat([s2, s3], ignore_index=True) | |
| if not with_gt: | |
| return s1, s23, None | |
| gt = read_any(paths["ground_truth"]) | |
| return s1, s23, gt | |
| def augment_s1(s1, gt, out_path=None): | |
| """Attach ground-truth labels to Source 1 as column `matched_entity_ids`.""" | |
| assert s1["entity_id"].is_unique, "duplicate entity_id in source1" | |
| assert gt["source1_entity_id"].is_unique, "duplicate source1_entity_id in ground truth" | |
| s1 = s1.merge(gt.rename(columns={"source1_entity_id": "entity_id"}), on="entity_id", | |
| how="left", validate="one_to_one") | |
| missing = (s1["matched_entity_ids"].isna()).sum() | |
| assert missing == 0, f"{missing} source1 entities have no ground-truth row" | |
| s1["matched_entity_ids"] = s1["matched_entity_ids"].fillna("").astype(str) | |
| if out_path: | |
| s1.to_parquet(out_path, index=False) | |
| return s1 | |
| def sanity_checks(s1, s23, rng, sample=200_000): | |
| rep = {} | |
| rep["n_s1"] = len(s1) | |
| rep["n_s23"] = len(s23) | |
| rep["s23_ids_unique"] = bool(s23["entity_id"].is_unique) | |
| lists = s1["matched_entity_ids"].map(split_ids) | |
| n = lists.map(len) | |
| rep["singleton_rate"] = float((n == 0).mean()) | |
| rep["mean_matches"] = float(n.mean()) | |
| rep["max_matches"] = int(n.max()) | |
| flat = pd.Series([x for l in lists for x in l]) | |
| rep["n_matched_ids"] = int(len(flat)) | |
| rep["multi_owner_ids"] = int(flat.duplicated().sum()) | |
| idx = pd.Index(s23["entity_id"]) | |
| pos = idx.get_indexer(flat) | |
| rep["gt_ids_missing_in_s23"] = int((pos < 0).sum()) | |
| # same-country rate over a sample of true pairs | |
| owner = np.repeat(np.arange(len(s1)), n.values) | |
| take = rng.choice(len(flat), size=min(sample, len(flat)), replace=False) | |
| ok = pos[take] >= 0 | |
| q_c = s1["country_norm"].values[owner[take][ok]] | |
| c_c = s23["country_norm"].values[pos[take][ok]] | |
| rep["true_pairs_same_country_rate"] = float((q_c == c_c).mean()) if ok.any() else None | |
| rep["s1_country_counts"] = s1["country_norm"].value_counts().to_dict() | |
| return rep | |
| def make_split(s1, cfg, rng): | |
| """Return three disjoint arrays of S1 row positions: ce, stack, val.""" | |
| n_ce, n_st, n_va = cfg["n_s1_ce"], cfg["n_s1_stack"], cfg["n_s1_val"] | |
| if cfg["split"] == "A": | |
| perm = rng.permutation(len(s1)) | |
| need = n_ce + n_st + n_va | |
| if need > len(perm): | |
| raise ValueError(f"requested {need} S1 entities but only {len(perm)} exist") | |
| return perm[:n_ce], perm[n_ce:n_ce + n_st], perm[n_ce + n_st:need] | |
| if cfg["split"] == "B": | |
| c = s1["country_norm"].values | |
| tr = rng.permutation(np.where(c == cfg["split_b_train_country"])[0]) | |
| va = rng.permutation(np.where(c == cfg["split_b_val_country"])[0]) | |
| if n_ce + n_st > len(tr) or n_va > len(va): | |
| raise ValueError("not enough entities for split B with these sizes") | |
| return tr[:n_ce], tr[n_ce:n_ce + n_st], va[:n_va] | |
| raise ValueError(cfg["split"]) | |
| def ensure_dir(p): | |
| os.makedirs(p, exist_ok=True) | |
| return p | |