File size: 18,091 Bytes
981be56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
#!/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!")