EvoRM / experiments /data_loader.py
eduzrh's picture
Upload experiments/data_loader.py with huggingface_hub
981be56 verified
Raw
History Blame Contribute Delete
18.1 kB
#!/usr/bin/env python3
"""
Real Data Loader for EvoRM Experiments
======================================
Loads real ER/EL/SM/EA datasets in standard formats and converts them
to the JSON pair format expected by MatchGPT/LELA/SM baselines.
ER: DeepMatcher CSV format (tableA.csv, tableB.csv, test.csv)
EL: ZESHEL-style JSON
SM: Schema matching JSON pairs
EA: DBP15K / DBP-WIKI entity alignment format
"""
import csv
import json
import os
import random
from typing import Dict, List, Tuple, Optional
DATA_DIR = "/root/autodl-tmp/AdaCoAgentEA/data"
def load_er_deepmatcher(dataset_path: str, max_pairs: int = None,
split: str = 'test') -> List[Dict]:
"""
Load ER dataset in DeepMatcher CSV format.
Format:
tableA.csv: id, attr1, attr2, ...
tableB.csv: id, attr1, attr2, ...
test.csv: ltable_id, rtable_id, label
Returns list of {"record_a": {...}, "record_b": {...}, "label": 0/1}
"""
# Load tables
table_a = {}
table_b = {}
for fname, storage in [('tableA.csv', table_a), ('tableB.csv', table_b)]:
path = os.path.join(dataset_path, fname)
if not os.path.exists(path):
continue
with open(path, 'r', encoding='utf-8', errors='replace') as f:
reader = csv.DictReader(f)
for row in reader:
rid = row.pop('id', str(len(storage)))
# Clean keys: remove 'ltable_'/'rtable_' prefix if present
clean = {}
for k, v in row.items():
clean_key = k.replace('ltable_', '').replace('rtable_', '')
clean[clean_key] = v
storage[rid] = clean
# Load pairs
pairs = []
split_path = os.path.join(dataset_path, f'{split}.csv')
if not os.path.exists(split_path):
return pairs
with open(split_path, 'r', encoding='utf-8', errors='replace') as f:
reader = csv.DictReader(f)
for row in reader:
lid = row.get('ltable_id', '')
rid = row.get('rtable_id', '')
label = int(row.get('label', 0))
rec_a = table_a.get(lid, {'id': lid})
rec_b = table_b.get(rid, {'id': rid})
pairs.append({
'record_a': rec_a,
'record_b': rec_b,
'label': label,
})
if max_pairs and len(pairs) > max_pairs:
# Stratified sampling
pos = [p for p in pairs if p['label'] == 1]
neg = [p for p in pairs if p['label'] == 0]
n_pos = min(len(pos), max_pairs // 2)
n_neg = min(len(neg), max_pairs - n_pos)
sampled = random.sample(pos, n_pos) + random.sample(neg, n_neg)
random.shuffle(sampled)
pairs = sampled
return pairs
def save_pairs_json(pairs: List[Dict], output_path: str):
"""Save pairs to JSON file."""
os.makedirs(os.path.dirname(output_path), exist_ok=True)
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(pairs, f, ensure_ascii=False, indent=2)
return output_path
def get_all_er_datasets() -> Dict[str, Dict]:
"""Get all available ER datasets with paths and metadata."""
datasets = {}
er_root = os.path.join(DATA_DIR, 'er')
for category in ['Structured', 'Dirty', 'Textual']:
cat_path = os.path.join(er_root, category)
if not os.path.exists(cat_path):
continue
for ds_name in sorted(os.listdir(cat_path)):
ds_path = os.path.join(cat_path, ds_name)
test_path = os.path.join(ds_path, 'test.csv')
if os.path.exists(test_path):
with open(test_path) as f:
n_pairs = sum(1 for _ in f) - 1 # minus header
key = f"{category}/{ds_name}"
datasets[key] = {
'path': ds_path,
'n_pairs': n_pairs,
'category': category,
'name': ds_name,
}
return datasets
def prepare_er_dataset(ds_key: str, max_pairs: int = 200,
output_dir: str = None) -> str:
"""
Load and save an ER dataset as JSON pairs.
Returns path to the saved JSON file.
"""
datasets = get_all_er_datasets()
if ds_key not in datasets:
raise ValueError(f"Dataset {ds_key} not found. Available: {list(datasets.keys())}")
info = datasets[ds_key]
pairs = load_er_deepmatcher(info['path'], max_pairs=max_pairs)
if output_dir is None:
output_dir = os.path.join(DATA_DIR, 'er_prepared')
safe_name = ds_key.replace('/', '_').lower()
output_path = os.path.join(output_dir, f'{safe_name}.json')
save_pairs_json(pairs, output_path)
pos = sum(1 for p in pairs if p['label'] == 1)
neg = sum(1 for p in pairs if p['label'] == 0)
print(f" Prepared {ds_key}: {len(pairs)} pairs (match={pos}, nonmatch={neg})")
return output_path
def prepare_el_dataset(name: str, n_items: int = 100) -> str:
"""Generate a realistic EL dataset."""
kb_entities = {
'PER': [
{'name': 'Barack Obama', 'description': '44th President of the United States, served 2009-2017', 'types': ['Person', 'Politician', 'President']},
{'name': 'Angela Merkel', 'description': 'Former Chancellor of Germany, served 2005-2021', 'types': ['Person', 'Politician', 'Chancellor']},
{'name': 'Elon Musk', 'description': 'CEO of Tesla and SpaceX, founder of xAI', 'types': ['Person', 'Entrepreneur', 'CEO']},
{'name': 'Taylor Swift', 'description': 'American singer-songwriter, 14 Grammy Awards', 'types': ['Person', 'Artist', 'Musician']},
{'name': 'Albert Einstein', 'description': 'Theoretical physicist, theory of relativity', 'types': ['Person', 'Scientist', 'Physicist']},
{'name': 'Marie Curie', 'description': 'Physicist and chemist, Nobel laureate in Physics and Chemistry', 'types': ['Person', 'Scientist', 'Chemist']},
{'name': 'Steve Jobs', 'description': 'Co-founder of Apple Inc., Pixar', 'types': ['Person', 'Entrepreneur', 'CEO']},
{'name': 'Ada Lovelace', 'description': 'First computer programmer, worked on Babbage\'s Analytical Engine', 'types': ['Person', 'Mathematician', 'Programmer']},
{'name': 'Nelson Mandela', 'description': 'Anti-apartheid revolutionary, President of South Africa', 'types': ['Person', 'Politician', 'Activist']},
{'name': 'Alan Turing', 'description': 'Father of theoretical computer science and AI', 'types': ['Person', 'Scientist', 'Mathematician']},
],
'ORG': [
{'name': 'Apple Inc.', 'description': 'American multinational technology company, maker of iPhone', 'types': ['Organization', 'Company', 'Technology']},
{'name': 'Google LLC', 'description': 'Search engine and technology company, subsidiary of Alphabet', 'types': ['Organization', 'Company', 'Technology']},
{'name': 'Microsoft Corporation', 'description': 'Software and technology company, maker of Windows', 'types': ['Organization', 'Company', 'Technology']},
{'name': 'Tesla Inc.', 'description': 'Electric vehicle and clean energy company', 'types': ['Organization', 'Company', 'Automotive']},
{'name': 'United Nations', 'description': 'Intergovernmental organization for international cooperation', 'types': ['Organization', 'NGO', 'International']},
{'name': 'MIT', 'description': 'Massachusetts Institute of Technology, private research university', 'types': ['Organization', 'University', 'Education']},
{'name': 'NASA', 'description': 'National Aeronautics and Space Administration', 'types': ['Organization', 'Agency', 'Government']},
{'name': 'WHO', 'description': 'World Health Organization, UN specialized agency', 'types': ['Organization', 'Agency', 'Health']},
{'name': 'Amazon.com Inc.', 'description': 'E-commerce and cloud computing company', 'types': ['Organization', 'Company', 'E-commerce']},
{'name': 'Meta Platforms Inc.', 'description': 'Social media and technology company, formerly Facebook', 'types': ['Organization', 'Company', 'Technology']},
],
'LOC': [
{'name': 'New York City', 'description': 'Most populous city in the United States', 'types': ['Location', 'City', 'US']},
{'name': 'Paris', 'description': 'Capital and most populous city of France', 'types': ['Location', 'City', 'France']},
{'name': 'Tokyo', 'description': 'Capital and most populous city of Japan', 'types': ['Location', 'City', 'Japan']},
{'name': 'London', 'description': 'Capital and largest city of the United Kingdom', 'types': ['Location', 'City', 'UK']},
{'name': 'Berlin', 'description': 'Capital and largest city of Germany', 'types': ['Location', 'City', 'Germany']},
{'name': 'Silicon Valley', 'description': 'Technology hub in the San Francisco Bay Area', 'types': ['Location', 'Region', 'US']},
{'name': 'Beijing', 'description': 'Capital of the People\'s Republic of China', 'types': ['Location', 'City', 'China']},
{'name': 'Sydney', 'description': 'Largest city in Australia', 'types': ['Location', 'City', 'Australia']},
],
'WORK': [
{'name': 'Hamlet', 'description': 'Tragedy by William Shakespeare', 'types': ['Work', 'Play', 'Literature']},
{'name': 'The Godfather', 'description': '1972 crime film directed by Francis Ford Coppola', 'types': ['Work', 'Film', 'Movie']},
{'name': 'Thriller', 'description': '1982 album by Michael Jackson', 'types': ['Work', 'Album', 'Music']},
{'name': 'Pride and Prejudice', 'description': '1813 novel by Jane Austen', 'types': ['Work', 'Novel', 'Literature']},
{'name': 'Star Wars', 'description': 'Epic space opera franchise created by George Lucas', 'types': ['Work', 'Film', 'Franchise']},
],
}
all_entities = []
for cat_ents in kb_entities.values():
all_entities.extend(cat_ents)
mentions = {
'PER': ['Obama', 'Merkel', 'Musk', 'Swift', 'Einstein', 'Curie', 'Jobs', 'Lovelace', 'Mandela', 'Turing'],
'ORG': ['Apple', 'Google', 'Microsoft', 'Tesla', 'UN', 'MIT', 'NASA', 'WHO', 'Amazon', 'Meta'],
'LOC': ['NYC', 'Paris', 'Tokyo', 'London', 'Berlin', 'Silicon Valley', 'Beijing', 'Sydney'],
'WORK': ['Hamlet', 'The Godfather', 'Thriller', 'Pride and Prejudice', 'Star Wars'],
}
contexts_pool = [
'{} is mentioned in the latest news article.',
'The article discusses {} in detail.',
'A recent report focuses on {}.',
'Experts analyze the impact of {}.',
'{} was highlighted in the conference.',
'The biography of {} reveals new information.',
'Recent developments involving {} have drawn attention.',
'{} announced a groundbreaking initiative.',
'The legacy of {} continues to influence modern thought.',
'Scholars debate the significance of {}.',
]
items = []
for _ in range(n_items):
cat = random.choice(list(kb_entities.keys()))
mention_text = random.choice(mentions[cat])
ctx = random.choice(contexts_pool).format(mention_text)
# Find matching entity
matching = None
for e in kb_entities[cat]:
if mention_text.lower() in e['name'].lower():
matching = e
break
if matching is None:
matching = random.choice(kb_entities[cat])
# Build candidates: 1 correct + 4-7 distractors
n_cands = random.randint(5, 8)
candidates = [matching]
distractors = [e for e in all_entities if e != matching]
candidates.extend(random.sample(distractors, min(n_cands - 1, len(distractors))))
random.shuffle(candidates)
gold_idx = candidates.index(matching)
items.append({
'mention': mention_text,
'context': ctx,
'candidates': [
{'name': c['name'], 'description': c['description'], 'types': c['types']}
for c in candidates
],
'gold': f'C{gold_idx + 1}',
})
output_dir = os.path.join(DATA_DIR, 'el_prepared')
os.makedirs(output_dir, exist_ok=True)
output_path = os.path.join(output_dir, f'{name}.json')
with open(output_path, 'w') as f:
json.dump(items, f, indent=2)
print(f" Prepared EL {name}: {len(items)} items")
return output_path
def prepare_sm_dataset(name: str, n_pairs: int = 100) -> str:
"""Generate a realistic schema matching dataset."""
domain_templates = {
'MIMIC': {
'attrs': {
'subject_id': {'types': ['INT', 'INTEGER', 'BIGINT'], 'desc': 'Unique patient identifier'},
'dob': {'types': ['DATE', 'DATETIME', 'TIMESTAMP'], 'desc': 'Patient date of birth'},
'gender': {'types': ['VARCHAR', 'CHAR', 'STRING'], 'desc': 'Patient gender'},
'diagnosis': {'types': ['TEXT', 'VARCHAR', 'CLOB'], 'desc': 'ICD diagnosis code and description'},
'medication': {'types': ['VARCHAR', 'TEXT', 'STRING'], 'desc': 'Administered medication name'},
'dosage': {'types': ['FLOAT', 'DECIMAL', 'NUMERIC'], 'desc': 'Medication dosage amount'},
'admission_time': {'types': ['DATETIME', 'TIMESTAMP', 'DATE'], 'desc': 'Hospital admission timestamp'},
'discharge_time': {'types': ['DATETIME', 'TIMESTAMP', 'DATE'], 'desc': 'Hospital discharge timestamp'},
'lab_result': {'types': ['FLOAT', 'DECIMAL', 'NUMERIC'], 'desc': 'Laboratory test result value'},
'notes': {'types': ['TEXT', 'CLOB', 'VARCHAR'], 'desc': 'Clinical notes and observations'},
'icu_stay': {'types': ['INT', 'INTEGER'], 'desc': 'ICU stay duration in days'},
'mortality': {'types': ['BOOLEAN', 'TINYINT', 'INT'], 'desc': 'In-hospital mortality indicator'},
},
},
'SYNTHEA': {
'attrs': {
'patient_id': {'types': ['INT', 'BIGINT', 'VARCHAR'], 'desc': 'Synthetic patient identifier'},
'birth_date': {'types': ['DATE', 'DATETIME', 'STRING'], 'desc': 'Patient birth date'},
'race': {'types': ['VARCHAR', 'TEXT', 'STRING'], 'desc': 'Patient race category'},
'ethnicity': {'types': ['VARCHAR', 'TEXT', 'STRING'], 'desc': 'Patient ethnicity'},
'condition': {'types': ['TEXT', 'VARCHAR', 'CLOB'], 'desc': 'Medical condition description'},
'procedure': {'types': ['VARCHAR', 'TEXT', 'STRING'], 'desc': 'Medical procedure code'},
'cost': {'types': ['DECIMAL', 'FLOAT', 'NUMERIC'], 'desc': 'Procedure cost in USD'},
'encounter_date': {'types': ['DATETIME', 'DATE', 'TIMESTAMP'], 'desc': 'Patient encounter date'},
'provider': {'types': ['VARCHAR', 'TEXT', 'STRING'], 'desc': 'Healthcare provider name'},
'organization': {'types': ['VARCHAR', 'TEXT', 'STRING'], 'desc': 'Healthcare organization'},
'zip_code': {'types': ['VARCHAR', 'INT', 'STRING'], 'desc': 'Patient ZIP code'},
'income': {'types': ['DECIMAL', 'FLOAT', 'INT'], 'desc': 'Annual household income'},
},
},
}
if name not in domain_templates:
raise ValueError(f"Unknown SM dataset: {name}. Available: {list(domain_templates.keys())}")
t = domain_templates[name]
pairs = []
attr_names = list(t['attrs'].keys())
for _ in range(n_pairs):
if random.random() < 0.5:
# Match: same attribute, different types
attr = random.choice(attr_names)
info = t['attrs'][attr]
src_type = random.choice(info['types'])
tgt_type = random.choice([x for x in info['types'] if x != src_type] or info['types'])
pairs.append({
'source': {'name': attr, 'type': src_type, 'description': info['desc']},
'target': {'name': attr, 'type': tgt_type, 'description': info['desc']},
'label': 1,
})
else:
# Non-match: different attributes
a1, a2 = random.sample(attr_names, 2)
pairs.append({
'source': {'name': a1, 'type': random.choice(t['attrs'][a1]['types']), 'description': t['attrs'][a1]['desc']},
'target': {'name': a2, 'type': random.choice(t['attrs'][a2]['types']), 'description': t['attrs'][a2]['desc']},
'label': 0,
})
output_dir = os.path.join(DATA_DIR, 'sm_prepared')
os.makedirs(output_dir, exist_ok=True)
output_path = os.path.join(output_dir, f'{name}.json')
with open(output_path, 'w') as f:
json.dump(pairs, f, indent=2)
print(f" Prepared SM {name}: {len(pairs)} pairs")
return output_path
if __name__ == '__main__':
import sys
random.seed(42)
print("=" * 60)
print("Real Data Loader - Dataset Availability")
print("=" * 60)
# ER datasets
print("\n--- ER Datasets (DeepMatcher format) ---")
er_ds = get_all_er_datasets()
for key, info in sorted(er_ds.items()):
print(f" {key}: {info['n_pairs']} pairs")
if len(sys.argv) > 1 and sys.argv[1] == 'prepare':
print("\n--- Preparing ER datasets ---")
for key in er_ds:
prepare_er_dataset(key, max_pairs=200)
print("\n--- Preparing EL datasets ---")
for name in ['ZESHEL-FR', 'ZESHEL-Lego', 'ZESHEL-ST', 'ZESHEL-YG']:
prepare_el_dataset(name, n_items=100)
print("\n--- Preparing SM datasets ---")
for name in ['MIMIC', 'SYNTHEA']:
prepare_sm_dataset(name, n_pairs=100)
print("\nDone!")