File size: 11,824 Bytes
cc007c0 | 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 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 | #!/usr/bin/env python3
"""
Preprocess new datasets for EvoRM experiments:
- DBP-WIKI (EA): D-W-15K and D-W-100K
- ZESHEL (EL): 4 test domains
- MIMIC (SM): OMAP clinical schema matching
- Synthea (SM): OMAP clinical schema matching
- WDC (ER): product matching
"""
import os
import sys
import json
import random
from collections import defaultdict
DATA_DIR = '/root/autodl-tmp/AdaCoAgentEA/data'
random.seed(42)
# ==============================================================================
# 1. DBP-WIKI Entity Alignment
# ==============================================================================
def prepare_dbp_wiki():
"""Convert OpenEA D-W datasets to EA format for AdaCoAgentEA."""
src_dir = os.path.join(DATA_DIR, 'ea', 'dbp_wiki', 'OpenEA_dataset_v2.0')
out_dir = os.path.join(DATA_DIR, 'ea', 'dbp_wiki_processed')
os.makedirs(out_dir, exist_ok=True)
for variant in ['D_W_15K_V1', 'D_W_15K_V2']:
vdir = os.path.join(src_dir, variant)
if not os.path.exists(vdir):
continue
# Read all entity links (URI-based)
ent_links = []
with open(os.path.join(vdir, 'ent_links'), 'r') as f:
for line in f:
parts = line.strip().split('\t')
if len(parts) == 2:
ent_links.append((parts[0], parts[1]))
print(f" {variant}: {len(ent_links)} entity links")
# Read 5-fold splits
for fold in range(1, 6):
fold_dir = os.path.join(vdir, '721_5fold', str(fold))
test_set = set()
valid_set = set()
for fname, linkset in [('test_links', test_set), ('valid_links', valid_set)]:
fpath = os.path.join(fold_dir, fname)
if os.path.exists(fpath):
with open(fpath, 'r') as f:
for line in f:
parts = line.strip().split('\t')
if len(parts) == 2:
linkset.add((parts[0], parts[1]))
# Create test pairs from test+valid links
test_pairs = []
for e1, e2 in ent_links:
if (e1, e2) in test_set:
test_pairs.append({
'entity_a': e1.split('/')[-1],
'entity_b': e2.split('/')[-1],
'label': 1,
})
elif (e1, e2) in valid_set:
# Also include valid pairs as positive
test_pairs.append({
'entity_a': e1.split('/')[-1],
'entity_b': e2.split('/')[-1],
'label': 1,
})
# Add negatives from test/valid sets (sample non-matching pairs)
# Use ent_links pairs that are not in test/valid as negatives
neg_count = 0
max_neg = min(len(test_pairs) * 2, 500)
all_ent_links_set = set(ent_links)
for e1, e2 in ent_links:
if neg_count >= max_neg:
break
if (e1, e2) not in test_set and (e1, e2) not in valid_set:
test_pairs.append({
'entity_a': e1.split('/')[-1],
'entity_b': e2.split('/')[-1],
'label': 0,
})
neg_count += 1
random.shuffle(test_pairs)
out_path = os.path.join(out_dir, f'{variant}_fold{fold}.json')
with open(out_path, 'w') as f:
json.dump(test_pairs, f)
pos = sum(1 for p in test_pairs if p['label'] == 1)
print(f" fold{fold}: {len(test_pairs)} pairs ({pos} pos)")
print("DBP-WIKI: Done")
# ==============================================================================
# 2. ZESHEL Entity Linking
# ==============================================================================
def prepare_zeshel():
"""Convert ZESHEL to EL format for LELA baseline."""
data_dir = os.path.join(DATA_DIR, 'el', 'zeshel')
out_dir = os.path.join(DATA_DIR, 'el', 'zeshel_processed')
os.makedirs(out_dir, exist_ok=True)
# Load dictionary
print(" Loading ZESHEL dictionary...")
entities = defaultdict(dict)
with open(os.path.join(data_dir, 'dictionary', 'dictionary-00001-of-00001.jsonl')) as f:
for line in f:
d = json.loads(line)
entities[d['subset']][d['id']] = {
'name': d['name'],
'description': d['description'][:300] if d['description'] else '',
}
print(f" Loaded {sum(len(v) for v in entities.values())} entities across {len(entities)} domains")
# Load test data
test_data = []
with open(os.path.join(data_dir, 'data', 'test-00001-of-00001.jsonl')) as f:
for line in f:
test_data.append(json.loads(line))
# Group by domain
by_domain = defaultdict(list)
for item in test_data:
by_domain[item['subset']].append(item)
test_domains = ['forgotten_realms', 'lego', 'star_trek', 'yugioh']
for domain in test_domains:
domain_items = by_domain.get(domain, [])
if not domain_items:
print(f" WARNING: {domain} has no test items")
continue
el_items = []
for item in domain_items[:500]: # Limit to 500 per domain to control API costs
# Get candidate entities
candidates = []
for ent_info in item.get('entities', []):
ent_id = ent_info.get('id', '')
if ent_id and ent_id in entities[domain]:
candidates.append(entities[domain][ent_id])
if not candidates:
continue
# Limit to 10 candidates
gold_is_first = len(item.get('entities', [])) > 0
if len(candidates) > 10:
candidates = candidates[:10]
el_items.append({
'mention': item.get('text', '')[:200],
'context': item.get('text', ''),
'candidates': candidates,
'gold': 'C1', # First entity is gold
'domain': domain,
})
out_path = os.path.join(out_dir, f'zeshel_{domain}.json')
with open(out_path, 'w') as f:
json.dump(el_items, f)
print(f" ZESHEL {domain}: {len(el_items)} EL items")
print("ZESHEL: Done")
# ==============================================================================
# 3. MIMIC & Synthea Schema Matching
# ==============================================================================
def prepare_smat():
"""Convert SMAT OMAP data to SM format."""
import openpyxl
data_dir = os.path.join(DATA_DIR, 'sm', 'synthea', 'datasets', 'omap')
out_dir = os.path.join(DATA_DIR, 'sm', 'smat_processed')
os.makedirs(out_dir, exist_ok=True)
for name, fname in [('synthea', 'omop_synthea_data.xlsx'),
('mimic', 'omop_mimic_data.xlsx')]:
wb = openpyxl.load_workbook(os.path.join(data_dir, fname))
ws = wb.active
items = []
for row in ws.iter_rows(min_row=2, values_only=True):
omop, table, des1, des2, label, d1, d2, d3, d4 = row
label = int(label) if label is not None else 0
items.append({
'source': {
'table': str(omop or '').split('-')[0] if omop else '',
'column': str(omop or '').split('-')[1] if omop and '-' in str(omop) else str(omop or ''),
'name': str(omop or ''),
'description': str(des1 or ''),
},
'target': {
'table': str(table or '').split('-')[0] if table else '',
'column': str(table or '').split('-')[1] if table and '-' in str(table) else str(table or ''),
'name': str(table or ''),
'description': str(des2 or ''),
},
'label': label,
})
pos_items = [it for it in items if it['label'] == 1]
neg_items = [it for it in items if it['label'] == 0]
# Keep all positives + sample negatives (5x for balance)
max_neg = min(len(neg_items), len(pos_items) * 5)
if max_neg < len(neg_items):
neg_items = random.sample(neg_items, max_neg)
balanced = pos_items + neg_items
random.shuffle(balanced)
out_path = os.path.join(out_dir, f'{name}.json')
with open(out_path, 'w') as f:
json.dump(balanced, f)
print(f" SMAT {name}: {len(pos_items)} pos, {len(neg_items)} neg → {len(balanced)} total")
print("SMAT: Done")
# ==============================================================================
# 4. WDC Entity Resolution
# ==============================================================================
def prepare_wdc():
"""Convert WDC multi-class to pair-wise ER format."""
import pandas as pd
data_dir = os.path.join(DATA_DIR, 'er', 'wdc')
out_dir = os.path.join(DATA_DIR, 'er', 'wdc_processed')
os.makedirs(out_dir, exist_ok=True)
parquet_path = os.path.join(data_dir, 'data', 'train-00000-of-00001.parquet')
if not os.path.exists(parquet_path):
print("WDC: Parquet file not found, skipping")
return
df = pd.read_parquet(parquet_path)
print(f" WDC: {len(df)} products, {df['cluster_id'].nunique()} clusters")
# Group by cluster
clusters = defaultdict(list)
for _, row in df.iterrows():
clusters[row['cluster_id']].append(row.to_dict())
# Create pairs
pairs = []
cluster_ids = list(clusters.keys())
# Positive pairs
for cid, items in clusters.items():
if len(items) >= 2:
for i in range(min(len(items), 5)):
for j in range(i+1, min(len(items), 5)):
pairs.append({
'record_a': {k: str(v) for k, v in items[i].items() if k != 'cluster_id'},
'record_b': {k: str(v) for k, v in items[j].items() if k != 'cluster_id'},
'label': 1,
})
# Negative pairs
neg_count = 0
max_neg = min(len(pairs), 500)
while neg_count < max_neg:
c1, c2 = random.sample(cluster_ids, 2)
if clusters[c1] and clusters[c2]:
p1 = random.choice(clusters[c1])
p2 = random.choice(clusters[c2])
pairs.append({
'record_a': {k: str(v) for k, v in p1.items() if k != 'cluster_id'},
'record_b': {k: str(v) for k, v in p2.items() if k != 'cluster_id'},
'label': 0,
})
neg_count += 1
random.shuffle(pairs)
out_path = os.path.join(out_dir, 'wdc_er.json')
with open(out_path, 'w') as f:
json.dump(pairs, f)
pos = sum(1 for p in pairs if p['label']==1)
print(f" WDC: {len(pairs)} pairs ({pos} pos, {len(pairs)-pos} neg)")
print("WDC: Done")
if __name__ == '__main__':
print("=" * 60)
print("Preparing New Datasets for EvoRM Experiments")
print("=" * 60)
print("\n1. DBP-WIKI (Entity Alignment)")
prepare_dbp_wiki()
print("\n2. ZESHEL (Entity Linking)")
prepare_zeshel()
print("\n3. SMAT (Schema Matching)")
prepare_smat()
print("\n4. WDC (Entity Resolution)")
prepare_wdc()
print("\n" + "=" * 60)
print("All datasets prepared!")
|