diff --git a/.gitignore b/.gitignore index e0b82c1..ddbb497 100644 --- a/.gitignore +++ b/.gitignore @@ -19,12 +19,12 @@ wheels/ *.egg-info/ .installed.cfg *.egg -data/* + # Virtual Environment venv/ env/ ENV/ -.venv +.venv/ # IDE .vscode/ @@ -34,51 +34,69 @@ ENV/ *~ .DS_Store +# Claude Code +.claude/ + # API Keys - 중요! .env -.env.local -.env.development -.env.test -.env.production - +.env.* apis/gemini_keys.yaml -!apis/gemini_keys.yaml.template +apis/*.yaml +!apis/*-example.yaml +!apis/*.template.yaml # Logs *.log +logs/ # Jupyter Notebook -.ipynb_checkpoints +.ipynb_checkpoints/ # pytest .pytest_cache/ .coverage +htmlcov/ # MyPy .mypy_cache/ .dmypy.json dmypy.json -# database +# Data - 원본 데이터 +data/ + +# Output - 생성된 결과물 +output/ +output_*/ +I_origin_*/ + +# Temp - 임시 파일 +temp/ + +# Archives +*.zip +*.tar.gz +*.rar + +# Generated JSON (except input templates) +pipeline_output*.json +qa_difficulty_analysis_*.json +qa_for_review_*.json +eval_results_*.json + +# Keep input templates +!test_*_input.json + +# Database/Token info/ token.json -*.json -test_input.json -# env +# Docs (if generated) .bemad/ -docs/ -pipeline_ui/backend/checkpoints/* -pipeline_ui/backend/output/* -pipeline_ui/backend/uploads/* -# Frontend (Node.js) -pipeline_ui/frontend/node_modules/* +# Pipeline UI +pipeline_ui/backend/checkpoints/ +pipeline_ui/backend/output/ +pipeline_ui/backend/uploads/ +pipeline_ui/frontend/node_modules/ pipeline_ui/frontend/package-lock.json - - -I_origin_0/* -I_origin_1/* -I_origin_2/* - -output/* \ No newline at end of file diff --git a/capture_html_images.py b/capture_html_images.py new file mode 100644 index 0000000..4491bcb --- /dev/null +++ b/capture_html_images.py @@ -0,0 +1,127 @@ +""" +Capture HTML files from output_* directories as images using Playwright. +""" +import argparse +import asyncio +from pathlib import Path +from typing import List + +from playwright.async_api import async_playwright + + +async def capture_html_file_async( + html_path: Path, + output_path: Path, + width: int = 800, +) -> None: + """Capture a single HTML file as an image.""" + html_content = html_path.read_text(encoding="utf-8") + + async with async_playwright() as p: + browser = await p.chromium.launch(headless=True) + try: + page = await browser.new_page(viewport={"width": width, "height": 600}) + await page.set_content(html_content) + await page.screenshot(path=output_path, full_page=True) + finally: + await browser.close() + + +async def capture_batch_async( + html_files: List[Path], + output_dir: Path, + width: int = 800, +) -> None: + """Capture multiple HTML files, reusing a single browser instance.""" + async with async_playwright() as p: + browser = await p.chromium.launch(headless=True) + try: + for html_path in html_files: + output_path = output_dir / f"{html_path.stem}.png" + if output_path.exists(): + print(f" [SKIP] {output_path.name} already exists") + continue + + try: + html_content = html_path.read_text(encoding="utf-8") + page = await browser.new_page(viewport={"width": width, "height": 600}) + await page.set_content(html_content) + await page.screenshot(path=output_path, full_page=True) + await page.close() + print(f" [OK] {html_path.name} -> {output_path.name}") + except Exception as e: + print(f" [ERROR] {html_path.name}: {e}") + finally: + await browser.close() + + +def main(): + parser = argparse.ArgumentParser(description="Capture HTML files as images") + parser.add_argument( + "--output-dirs", + nargs="+", + default=None, + help="Specific output directories to process (e.g., output_academic output_finance)", + ) + parser.add_argument( + "--width", + type=int, + default=800, + help="Viewport width for rendering (default: 800)", + ) + parser.add_argument( + "--force", + action="store_true", + help="Overwrite existing images", + ) + args = parser.parse_args() + + base_dir = Path(__file__).parent + + # Find output_* directories + if args.output_dirs: + output_dirs = [base_dir / d for d in args.output_dirs] + else: + output_dirs = sorted(base_dir.glob("output_*")) + output_dirs = [d for d in output_dirs if d.is_dir()] + + if not output_dirs: + print("No output_* directories found.") + return + + print(f"Found {len(output_dirs)} output directories to process") + + for output_dir in output_dirs: + html_dir = output_dir / "html" + if not html_dir.exists(): + print(f"\n[SKIP] {output_dir.name}: no html/ subdirectory") + continue + + # Create images directory + images_dir = output_dir / "images" + images_dir.mkdir(exist_ok=True) + + html_files = sorted(html_dir.glob("*.html")) + if not html_files: + print(f"\n[SKIP] {output_dir.name}: no HTML files found") + continue + + # Filter out already processed files unless --force + if not args.force: + html_files = [ + f for f in html_files + if not (images_dir / f"{f.stem}.png").exists() + ] + + if not html_files: + print(f"\n[SKIP] {output_dir.name}: all files already processed") + continue + + print(f"\n[Processing] {output_dir.name}: {len(html_files)} HTML files") + asyncio.run(capture_batch_async(html_files, images_dir, args.width)) + + print("\nDone!") + + +if __name__ == "__main__": + main() diff --git a/eval/__init__.py b/eval/__init__.py index 65cc49f..4bdd2ee 100644 --- a/eval/__init__.py +++ b/eval/__init__.py @@ -28,6 +28,13 @@ evaluate_predictions, run_evaluation, ) +from .evaluate_vllm import ( + EvalConfig, + load_qa_from_pipeline_output, + evaluate_domain, + evaluate_all_domains, + DOMAIN_DIRS, +) __all__ = [ # Dataset @@ -50,4 +57,10 @@ # Evaluate "evaluate_predictions", "run_evaluation", + # vLLM Evaluate + "EvalConfig", + "load_qa_from_pipeline_output", + "evaluate_domain", + "evaluate_all_domains", + "DOMAIN_DIRS", ] diff --git a/eval/evaluate_vllm.py b/eval/evaluate_vllm.py new file mode 100644 index 0000000..aa9740d --- /dev/null +++ b/eval/evaluate_vllm.py @@ -0,0 +1,648 @@ +#!/usr/bin/env python3 +""" +vLLM 서버를 사용한 Table QA 평가 스크립트. + +output_* 디렉토리의 HTML 테이블 이미지와 QA 데이터를 사용하여 +멀티모달 모델의 Table QA 성능을 평가합니다. + +Usage: + # 단일 도메인 평가 + python -m eval.evaluate_vllm --domain public --vllm-url http://localhost:8000/v1 + + # 모든 도메인 평가 + python -m eval.evaluate_vllm --all-domains --vllm-url http://localhost:8000/v1 + + # 특정 모델 사용 + python -m eval.evaluate_vllm --domain business --model Qwen/Qwen2-VL-7B-Instruct + + # LLM-as-Judge 포함 + python -m eval.evaluate_vllm --domain finance --use-judge --judge-model gpt-4o +""" + +from __future__ import annotations + +import argparse +import asyncio +import json +import logging +import os +import sys +from dataclasses import dataclass, field +from datetime import datetime +from pathlib import Path +from typing import List, Dict, Any, Optional + +# 프로젝트 루트를 path에 추가 +project_root = Path(__file__).parent.parent +if str(project_root) not in sys.path: + sys.path.insert(0, str(project_root)) + +from eval.dataset import QAItem, EvalDataset +from eval.inference import VLLMClient, InferenceRequest, InferenceResponse, run_inference +from eval.metrics import compute_metrics, aggregate_metrics, EvalResult, AggregatedMetrics +from eval.evaluate import evaluate_predictions, generate_report, print_report +from eval.llm_judge import create_judge_client + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" +) +logger = logging.getLogger(__name__) + + +# 도메인별 output 디렉토리 매핑 +DOMAIN_DIRS = { + "academic": "output_academic", + "business": "output_business", + "finance": "output_finance", + "medical": "output_medical", + "public": "output_public", +} + +# 기본 프롬프트 템플릿 +DEFAULT_PROMPT_TEMPLATE = """당신은 테이블 이미지를 분석하여 질문에 답하는 AI 어시스턴트입니다. +주어진 테이블 이미지를 주의 깊게 분석한 후, 질문에 대해 정확하고 간결하게 답변해주세요. + +질문: {question} + +답변:""" + +# Context가 있는 경우의 프롬프트 템플릿 +CONTEXT_PROMPT_TEMPLATE = """당신은 테이블 이미지를 분석하여 질문에 답하는 AI 어시스턴트입니다. +주어진 테이블 이미지와 아래 문맥을 함께 고려하여 질문에 정확하고 간결하게 답변해주세요. + +[문맥] +{context} + +질문: {question} + +답변:""" + + +@dataclass +class EvalConfig: + """평가 설정""" + domain: str + vllm_url: str = "http://localhost:8000/v1" + model: str = "default" + max_tokens: int = 512 + temperature: float = 0.0 + max_concurrent: int = 10 + timeout: float = 120.0 + use_judge: bool = False + judge_provider: str = "openai" + judge_model: Optional[str] = None + judge_api_key: Optional[str] = None + output_dir: Optional[Path] = None + limit: Optional[int] = None # 평가할 최대 샘플 수 (디버깅용) + qa_types: Optional[List[str]] = None # 특정 QA 타입만 평가 + + +def find_table_images( + output_dir: Path, + pair_id: str, + image_paths: List[str], +) -> List[Path]: + """ + QA 항목에 해당하는 테이블 이미지를 찾습니다. + + HTML 파일이 이미지로 캡처되었다고 가정하고, + output_dir/images/ 디렉토리에서 이미지를 찾습니다. + + 파일명 패턴: + - pair_id: "B_origin_3_3_0" + - HTML: "B_origin_3_3_0_table_0.html", "B_origin_3_3_0_table_1.html" + - Images: "B_origin_3_3_0_table_0.png", "B_origin_3_3_0_table_1.png" + + Args: + output_dir: output_* 디렉토리 경로 + pair_id: QA pair ID + image_paths: 원본 이미지 경로 리스트 (data/Public/Table/P_origin_0/...) + + Returns: + 찾은 이미지 경로 리스트 + """ + found_images = [] + + images_dir = output_dir / "images" + html_dir = output_dir / "html" + + # 1. images/ 디렉토리에서 캡처된 이미지 찾기 (pair_id_table_*.png 패턴) + if images_dir.exists(): + # pair_id_table_N.png 패턴으로 찾기 + for img_file in sorted(images_dir.glob(f"{pair_id}_table_*.png")): + found_images.append(img_file) + + # 못 찾았으면 pair_id*.png 패턴으로 시도 + if not found_images: + for img_file in sorted(images_dir.glob(f"{pair_id}*.png")): + found_images.append(img_file) + + # 2. html/ 디렉토리의 HTML 파일에 대응하는 이미지 찾기 + if not found_images and html_dir.exists() and images_dir.exists(): + for html_file in sorted(html_dir.glob(f"{pair_id}*.html")): + img_path = images_dir / f"{html_file.stem}.png" + if img_path.exists(): + found_images.append(img_path) + + # 3. 원본 이미지 경로가 존재하면 사용 (fallback) + if not found_images: + for orig_path in image_paths: + p = Path(orig_path) + if p.exists(): + found_images.append(p) + else: + # 프로젝트 루트 기준 상대 경로 시도 + full_path = project_root / orig_path + if full_path.exists(): + found_images.append(full_path) + + return sorted(set(found_images)) # 중복 제거 및 정렬 + + +def load_qa_from_pipeline_output( + output_dir: Path, + limit: Optional[int] = None, + qa_types: Optional[List[str]] = None, +) -> EvalDataset: + """ + pipeline_output.json에서 QA 데이터를 로드합니다. + + Args: + output_dir: output_* 디렉토리 + limit: 최대 로드할 샘플 수 + qa_types: 특정 QA 타입만 로드 + + Returns: + EvalDataset + """ + pipeline_output = output_dir / "pipeline_output.json" + + if not pipeline_output.exists(): + logger.warning(f"pipeline_output.json not found in {output_dir}") + return EvalDataset() + + with open(pipeline_output, "r", encoding="utf-8") as f: + data = json.load(f) + + items = [] + skipped_no_images = 0 + skipped_qa_type = 0 + + for entry in data: + pair_id = entry.get("pair_id", entry.get("name", "unknown")) + image_paths = entry.get("image_paths", []) + domain = entry.get("domain", "unknown") + qa_results = entry.get("qa_results", []) + + # 테이블 이미지 찾기 + table_images = find_table_images(output_dir, pair_id, image_paths) + + for idx, qa in enumerate(qa_results): + qa_type = qa.get("type", "unknown") + + # QA 타입 필터링 + if qa_types and qa_type not in qa_types: + skipped_qa_type += 1 + continue + + item_id = f"{pair_id}_{idx}" + + # 이미지 경로 결정 + if table_images: + item_image_paths = [str(p) for p in table_images] + elif image_paths: + # 원본 경로 사용 (fallback) + item_image_paths = image_paths + else: + skipped_no_images += 1 + continue + + item = QAItem( + id=item_id, + question=qa.get("question", ""), + answer=qa.get("answer", ""), + qa_type=qa_type, + image_paths=item_image_paths, + reasoning_annotation=qa.get("reasoning_annotation"), + context=qa.get("context"), + source_file=str(pipeline_output), + ) + items.append(item) + + if limit and len(items) >= limit: + break + + if limit and len(items) >= limit: + break + + if skipped_no_images: + logger.warning(f"Skipped {skipped_no_images} QA items without images") + if skipped_qa_type: + logger.info(f"Skipped {skipped_qa_type} QA items due to type filter") + + dataset = EvalDataset( + items=items, + metadata={ + "source": str(pipeline_output), + "domain": output_dir.name, + "total_entries": len(data), + "loaded_qa_count": len(items), + } + ) + + return dataset + + +def create_inference_requests( + dataset: EvalDataset, + prompt_template: Optional[str] = None, +) -> List[InferenceRequest]: + """ + 추론 요청을 생성합니다. + + Args: + dataset: 평가 데이터셋 + prompt_template: 프롬프트 템플릿 + + Returns: + InferenceRequest 리스트 + """ + requests = [] + + for item in dataset: + # context가 있으면 context 템플릿 사용 + if item.context: + template = prompt_template or CONTEXT_PROMPT_TEMPLATE + prompt = template.format( + question=item.question, + context=item.context, + ) + else: + template = prompt_template or DEFAULT_PROMPT_TEMPLATE + prompt = template.format(question=item.question) + + request = InferenceRequest( + id=item.id, + prompt=prompt, + ground_truth=item.answer, + qa_type=item.qa_type, + image_paths=item.image_paths, + ) + requests.append(request) + + return requests + + +async def evaluate_domain( + config: EvalConfig, +) -> tuple[List[EvalResult], AggregatedMetrics, Dict[str, Any]]: + """ + 단일 도메인에 대해 평가를 실행합니다. + + Args: + config: 평가 설정 + + Returns: + (개별 결과, 집계 메트릭, 메타데이터) + """ + domain_dir_name = DOMAIN_DIRS.get(config.domain) + if not domain_dir_name: + raise ValueError(f"Unknown domain: {config.domain}. Available: {list(DOMAIN_DIRS.keys())}") + + output_dir = project_root / domain_dir_name + if not output_dir.exists(): + raise FileNotFoundError(f"Output directory not found: {output_dir}") + + logger.info(f"Evaluating domain: {config.domain}") + logger.info(f"Output directory: {output_dir}") + + # 1. 데이터셋 로드 + dataset = load_qa_from_pipeline_output( + output_dir, + limit=config.limit, + qa_types=config.qa_types, + ) + + if len(dataset) == 0: + logger.error("No QA items loaded. Check if pipeline_output.json exists and contains valid data.") + return [], AggregatedMetrics(), {} + + logger.info(f"Loaded {len(dataset)} QA items") + logger.info(f"Type distribution: {dataset.get_type_distribution()}") + + # 2. 추론 요청 생성 + requests = create_inference_requests(dataset) + + # 3. vLLM 클라이언트 생성 + client = VLLMClient( + base_url=config.vllm_url, + model=config.model, + max_tokens=config.max_tokens, + temperature=config.temperature, + timeout=config.timeout, + max_concurrent=config.max_concurrent, + ) + + # 4. 추론 실행 + inference_output = None + if config.output_dir: + config.output_dir.mkdir(parents=True, exist_ok=True) + inference_output = config.output_dir / f"{config.domain}_inference.json" + + logger.info(f"Running inference on {len(requests)} requests...") + responses = await run_inference(client, requests, output_path=inference_output) + + # 5. 평가 + predictions = [ + { + "id": r.id, + "prediction": r.prediction, + "ground_truth": r.ground_truth, + "qa_type": r.qa_type, + "question": dataset.items[i].question if i < len(dataset.items) else "", + } + for i, r in enumerate(responses) + ] + + # Judge 클라이언트 설정 + judge_client = None + if config.use_judge: + judge_client = create_judge_client( + provider=config.judge_provider, + model=config.judge_model, + api_key=config.judge_api_key, + ) + + questions = [item.question for item in dataset.items] + results, aggregated = await evaluate_predictions( + predictions, + use_judge=config.use_judge, + judge_client=judge_client, + questions=questions, + ) + + # 메타데이터 + metadata = { + "domain": config.domain, + "model": config.model, + "vllm_url": config.vllm_url, + "total_items": len(dataset), + "type_distribution": dataset.get_type_distribution(), + "timestamp": datetime.now().isoformat(), + } + + return results, aggregated, metadata + + +async def evaluate_all_domains( + config: EvalConfig, +) -> Dict[str, tuple[List[EvalResult], AggregatedMetrics]]: + """ + 모든 도메인에 대해 평가를 실행합니다. + + Args: + config: 기본 평가 설정 (domain 필드는 무시됨) + + Returns: + 도메인별 결과 딕셔너리 + """ + all_results = {} + + for domain in DOMAIN_DIRS.keys(): + domain_config = EvalConfig( + domain=domain, + vllm_url=config.vllm_url, + model=config.model, + max_tokens=config.max_tokens, + temperature=config.temperature, + max_concurrent=config.max_concurrent, + timeout=config.timeout, + use_judge=config.use_judge, + judge_provider=config.judge_provider, + judge_model=config.judge_model, + judge_api_key=config.judge_api_key, + output_dir=config.output_dir, + limit=config.limit, + qa_types=config.qa_types, + ) + + try: + results, aggregated, metadata = await evaluate_domain(domain_config) + all_results[domain] = (results, aggregated, metadata) + print_report(aggregated) + except Exception as e: + logger.error(f"Failed to evaluate domain {domain}: {e}") + all_results[domain] = ([], AggregatedMetrics(), {"error": str(e)}) + + return all_results + + +def save_results( + results: List[EvalResult], + aggregated: AggregatedMetrics, + metadata: Dict[str, Any], + output_dir: Path, + domain: str, +) -> None: + """결과를 파일로 저장합니다.""" + output_dir.mkdir(parents=True, exist_ok=True) + + # 전체 리포트 + report = generate_report(results, aggregated, metadata) + report_path = output_dir / f"{domain}_evaluation_report.json" + with open(report_path, "w", encoding="utf-8") as f: + json.dump(report, ensure_ascii=False, indent=2, fp=f) + logger.info(f"Saved report to {report_path}") + + # 요약 결과 (CSV 친화적) + summary_path = output_dir / f"{domain}_summary.json" + summary = { + "domain": domain, + "total_count": aggregated.total_count, + "exact_match": aggregated.exact_match_avg, + "f1_score": aggregated.f1_score_avg, + "contains_match": aggregated.contains_match_avg, + "bleu_score": aggregated.bleu_score_avg, + "by_type": aggregated.by_type, + } + if aggregated.judge_overall_avg is not None: + summary["judge_overall"] = aggregated.judge_overall_avg + summary["judge_accuracy"] = aggregated.judge_accuracy + + with open(summary_path, "w", encoding="utf-8") as f: + json.dump(summary, ensure_ascii=False, indent=2, fp=f) + logger.info(f"Saved summary to {summary_path}") + + +def main(): + parser = argparse.ArgumentParser( + description="vLLM 서버를 사용한 Table QA 평가", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + # 단일 도메인 평가 + python -m eval.evaluate_vllm --domain public --vllm-url http://localhost:8000/v1 + + # 모든 도메인 평가 + python -m eval.evaluate_vllm --all-domains --vllm-url http://localhost:8000/v1 + + # 특정 모델 사용 + python -m eval.evaluate_vllm --domain business --model Qwen/Qwen2-VL-7B-Instruct + + # LLM-as-Judge 포함 + python -m eval.evaluate_vllm --domain finance --use-judge --judge-model gpt-4o + + # 특정 QA 타입만 평가 + python -m eval.evaluate_vllm --domain public --qa-types lookup compare + + # 제한된 샘플로 테스트 + python -m eval.evaluate_vllm --domain public --limit 10 + """, + ) + + # 필수 인자 + parser.add_argument( + "--domain", + choices=list(DOMAIN_DIRS.keys()), + help="평가할 도메인", + ) + parser.add_argument( + "--all-domains", + action="store_true", + help="모든 도메인 평가", + ) + + # vLLM 설정 + parser.add_argument( + "--vllm-url", + default="http://localhost:8000/v1", + help="vLLM 서버 URL (default: http://localhost:8000/v1)", + ) + parser.add_argument( + "--model", + default="default", + help="사용할 모델 이름 (default: vLLM에서 로드된 모델 사용)", + ) + parser.add_argument( + "--max-tokens", + type=int, + default=512, + help="최대 생성 토큰 수 (default: 512)", + ) + parser.add_argument( + "--temperature", + type=float, + default=0.0, + help="생성 온도 (default: 0.0)", + ) + parser.add_argument( + "--max-concurrent", + type=int, + default=10, + help="최대 동시 요청 수 (default: 10)", + ) + parser.add_argument( + "--timeout", + type=float, + default=120.0, + help="요청 타임아웃(초) (default: 120.0)", + ) + + # Judge 설정 + parser.add_argument( + "--use-judge", + action="store_true", + help="LLM-as-Judge 평가 사용", + ) + parser.add_argument( + "--judge-provider", + default="openai", + choices=["openai", "anthropic"], + help="Judge 제공자 (default: openai)", + ) + parser.add_argument( + "--judge-model", + help="Judge 모델 (default: gpt-4o-mini)", + ) + parser.add_argument( + "--judge-api-key", + help="Judge API 키 (환경변수에서 가져오지 않을 경우)", + ) + + # 출력 설정 + parser.add_argument( + "--output-dir", + type=Path, + default=Path("eval_results"), + help="결과 저장 디렉토리 (default: eval_results)", + ) + + # 필터링 옵션 + parser.add_argument( + "--limit", + type=int, + help="평가할 최대 샘플 수 (디버깅용)", + ) + parser.add_argument( + "--qa-types", + nargs="+", + help="특정 QA 타입만 평가 (예: lookup compare arithmetic)", + ) + + args = parser.parse_args() + + # 인자 검증 + if not args.domain and not args.all_domains: + parser.error("--domain 또는 --all-domains 중 하나를 지정해야 합니다.") + + # 설정 생성 + config = EvalConfig( + domain=args.domain or "public", # all-domains일 때 기본값 + vllm_url=args.vllm_url, + model=args.model, + max_tokens=args.max_tokens, + temperature=args.temperature, + max_concurrent=args.max_concurrent, + timeout=args.timeout, + use_judge=args.use_judge, + judge_provider=args.judge_provider, + judge_model=args.judge_model, + judge_api_key=args.judge_api_key, + output_dir=args.output_dir, + limit=args.limit, + qa_types=args.qa_types, + ) + + # 평가 실행 + if args.all_domains: + all_results = asyncio.run(evaluate_all_domains(config)) + + # 전체 요약 저장 + if config.output_dir: + config.output_dir.mkdir(parents=True, exist_ok=True) + + all_summary = {} + for domain, (results, aggregated, metadata) in all_results.items(): + if results: + save_results(results, aggregated, metadata, config.output_dir, domain) + all_summary[domain] = { + "total_count": aggregated.total_count, + "exact_match": aggregated.exact_match_avg, + "f1_score": aggregated.f1_score_avg, + } + + summary_path = config.output_dir / "all_domains_summary.json" + with open(summary_path, "w", encoding="utf-8") as f: + json.dump(all_summary, ensure_ascii=False, indent=2, fp=f) + logger.info(f"Saved all-domains summary to {summary_path}") + else: + results, aggregated, metadata = asyncio.run(evaluate_domain(config)) + print_report(aggregated) + + if config.output_dir and results: + save_results(results, aggregated, metadata, config.output_dir, config.domain) + + +if __name__ == "__main__": + main() diff --git a/filter_qa_by_difficulty.py b/filter_qa_by_difficulty.py new file mode 100755 index 0000000..1469baa --- /dev/null +++ b/filter_qa_by_difficulty.py @@ -0,0 +1,595 @@ +#!/usr/bin/env python3 +""" +vLLM 서버를 사용하여 QA 난이도를 측정하고 필터링하는 스크립트. + +모델이 너무 쉽게 맞추는 문제(10/10)는 제외하고, +적당한 난이도(3-6/10 정확도)의 QA만 검수 대상으로 추출합니다. + +Usage: + # 기본 사용 (business 도메인) + python filter_qa_by_difficulty.py --domain business + + # 여러 도메인 + python filter_qa_by_difficulty.py --all + + # 커스텀 설정 + python filter_qa_by_difficulty.py --domain business --trials 10 --min-acc 0.3 --max-acc 0.6 + + # vLLM 서버 URL 지정 + python filter_qa_by_difficulty.py --domain business --vllm-url http://localhost:8000/v1 +""" + +import argparse +import base64 +import json +import logging +import os +import sys +from dataclasses import dataclass, field +from datetime import datetime +from pathlib import Path +from typing import Any, Dict, List, Optional, Tuple +from concurrent.futures import ThreadPoolExecutor, as_completed + +from dotenv import load_dotenv + +load_dotenv() + +project_root = Path(__file__).parent +sys.path.insert(0, str(project_root)) + +from eval.metrics import normalize_answer, exact_match, f1_score + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s" +) +logger = logging.getLogger(__name__) + +DOMAIN_DIRS = { + "academic": "output_academic", + "business": "output_business", + "finance": "output_finance", + "medical": "output_medical", + "public": "output_public", +} + + +@dataclass +class FilterConfig: + """필터링 설정""" + vllm_url: str = "http://localhost:8000/v1" + model_name: str = "" # vLLM 서버에서 자동 감지 + trials: int = 10 # 각 QA당 시도 횟수 + min_accuracy: float = 0.3 # 최소 정확도 (이상) + max_accuracy: float = 0.6 # 최대 정확도 (이하) + temperature: float = 0.7 # 다양한 응답을 위해 + max_tokens: int = 512 + timeout: int = 60 + max_workers: int = 4 # 병렬 처리 + + +@dataclass +class QADifficultyResult: + """QA 난이도 측정 결과""" + pair_id: str + table_index: int + qa_index: int + question: str + answer: str + qa_type: str + correct_count: int + total_trials: int + accuracy: float + responses: List[str] = field(default_factory=list) + difficulty_category: str = "" # easy, medium, hard, very_hard + + def to_dict(self) -> Dict[str, Any]: + return { + "pair_id": self.pair_id, + "table_index": self.table_index, + "qa_index": self.qa_index, + "question": self.question, + "answer": self.answer, + "qa_type": self.qa_type, + "correct_count": self.correct_count, + "total_trials": self.total_trials, + "accuracy": self.accuracy, + "difficulty_category": self.difficulty_category, + "sample_responses": self.responses[:3], # 샘플만 저장 + } + + +def get_vllm_model_name(vllm_url: str) -> str: + """vLLM 서버에서 모델 이름 가져오기""" + import requests + try: + response = requests.get(f"{vllm_url}/models", timeout=10) + response.raise_for_status() + models = response.json().get("data", []) + if models: + return models[0]["id"] + except Exception as e: + logger.warning(f"Failed to get model name from vLLM: {e}") + return "default" + + +def find_table_images(output_dir: Path, pair_id: str) -> List[Path]: + """pair_id에 해당하는 테이블 이미지 찾기""" + images_dir = output_dir / "images" + if not images_dir.exists(): + return [] + + found_images = [] + for img_file in sorted(images_dir.glob(f"{pair_id}_table_*.png")): + found_images.append(img_file) + + return found_images + + +def encode_image_base64(image_path: Path) -> str: + """이미지를 base64로 인코딩""" + with open(image_path, "rb") as f: + return base64.b64encode(f.read()).decode("utf-8") + + +def run_single_inference( + vllm_url: str, + model_name: str, + image_base64: str, + question: str, + config: FilterConfig, +) -> Optional[str]: + """단일 추론 실행""" + import requests + + messages = [ + { + "role": "user", + "content": [ + { + "type": "image_url", + "image_url": { + "url": f"data:image/png;base64,{image_base64}" + } + }, + { + "type": "text", + "text": f"Based on the table image, answer the following question concisely.\n\nQuestion: {question}\n\nAnswer:" + } + ] + } + ] + + try: + response = requests.post( + f"{vllm_url}/chat/completions", + json={ + "model": model_name, + "messages": messages, + "max_tokens": config.max_tokens, + "temperature": config.temperature, + }, + timeout=config.timeout, + ) + response.raise_for_status() + result = response.json() + return result["choices"][0]["message"]["content"].strip() + except Exception as e: + logger.debug(f"Inference failed: {e}") + return None + + +def check_answer_correct(prediction: str, ground_truth: str) -> bool: + """답변이 맞는지 확인 (EM 또는 F1 > 0.8)""" + if not prediction: + return False + + # Exact match + if exact_match(prediction, ground_truth): + return True + + # F1 score > 0.8 + if f1_score(prediction, ground_truth) > 0.8: + return True + + # 정규화된 답변이 포함되는지 확인 + norm_pred = normalize_answer(prediction) + norm_gt = normalize_answer(ground_truth) + if norm_gt in norm_pred or norm_pred in norm_gt: + return True + + return False + + +def measure_qa_difficulty( + vllm_url: str, + model_name: str, + image_base64: str, + question: str, + answer: str, + config: FilterConfig, +) -> Tuple[int, List[str]]: + """QA 난이도 측정 (여러 번 시도)""" + correct_count = 0 + responses = [] + + for trial in range(config.trials): + response = run_single_inference( + vllm_url, model_name, image_base64, question, config + ) + if response: + responses.append(response) + if check_answer_correct(response, answer): + correct_count += 1 + + return correct_count, responses + + +def categorize_difficulty(accuracy: float) -> str: + """정확도에 따라 난이도 분류""" + if accuracy >= 0.9: + return "too_easy" + elif accuracy >= 0.7: + return "easy" + elif accuracy >= 0.3: + return "medium" # 목표 범위 + elif accuracy > 0: + return "hard" + else: + return "very_hard" + + +def filter_qa_for_domain( + domain: str, + config: FilterConfig, + limit: Optional[int] = None, + dry_run: bool = False, +) -> Dict[str, Any]: + """도메인의 QA를 필터링""" + domain_dir = DOMAIN_DIRS.get(domain) + if not domain_dir: + raise ValueError(f"Unknown domain: {domain}") + + output_dir = project_root / domain_dir + pipeline_output_path = output_dir / "pipeline_output.json" + + if not pipeline_output_path.exists(): + raise FileNotFoundError(f"pipeline_output.json not found: {pipeline_output_path}") + + # 이미지 디렉토리 + images_dir = output_dir / "images" + + # 데이터 로드 + with open(pipeline_output_path, "r", encoding="utf-8") as f: + data = json.load(f) + + logger.info(f"Loaded {len(data)} entries from {pipeline_output_path}") + + if limit: + data = data[:limit] + logger.info(f"Limited to {limit} entries") + + if dry_run: + # QA 수 확인만 (이미지 없어도 OK) + total_qa = sum(len(entry.get("qa_results", [])) for entry in data) + images_exist = images_dir.exists() + image_count = len(list(images_dir.glob("*.png"))) if images_exist else 0 + logger.info(f"Dry run: {len(data)} entries, {total_qa} QA pairs") + logger.info(f"Images directory: {'exists' if images_exist else 'NOT FOUND'} ({image_count} images)") + if not images_exist: + logger.warning("이미지 디렉토리가 없습니다. 먼저 capture_html_to_images.py를 실행하세요.") + return { + "domain": domain, + "entries": len(data), + "total_qa": total_qa, + "images_exist": images_exist, + "image_count": image_count, + "dry_run": True, + } + + # 이미지 디렉토리 확인 (실제 실행 시) + if not images_dir.exists(): + raise FileNotFoundError( + f"Images directory not found: {images_dir}\n" + "먼저 capture_html_to_images.py를 실행하여 HTML을 이미지로 변환하세요." + ) + + # vLLM 모델 이름 가져오기 + model_name = config.model_name or get_vllm_model_name(config.vllm_url) + logger.info(f"Using model: {model_name}") + + # 결과 수집 + all_results: List[QADifficultyResult] = [] + stats = { + "total_qa": 0, + "too_easy": 0, + "easy": 0, + "medium": 0, + "hard": 0, + "very_hard": 0, + "skipped": 0, + } + + for entry_idx, entry in enumerate(data): + pair_id = entry.get("pair_id", entry.get("name", f"entry_{entry_idx}")) + qa_results = entry.get("qa_results", []) + + if not qa_results: + continue + + # 이미지 찾기 + image_files = find_table_images(output_dir, pair_id) + if not image_files: + logger.warning(f"No images found for {pair_id}, skipping") + stats["skipped"] += len(qa_results) + continue + + # 첫 번째 이미지 사용 (TODO: 멀티 이미지 지원) + image_base64 = encode_image_base64(image_files[0]) + + logger.info(f"[{entry_idx + 1}/{len(data)}] Processing {pair_id} ({len(qa_results)} QAs)") + + for qa_idx, qa in enumerate(qa_results): + question = qa.get("question", "") + answer = qa.get("answer", "") + qa_type = qa.get("type", "unknown") + + if not question or not answer: + stats["skipped"] += 1 + continue + + stats["total_qa"] += 1 + + # 난이도 측정 + correct_count, responses = measure_qa_difficulty( + config.vllm_url, + model_name, + image_base64, + question, + answer, + config, + ) + + accuracy = correct_count / config.trials if config.trials > 0 else 0 + difficulty = categorize_difficulty(accuracy) + stats[difficulty] += 1 + + result = QADifficultyResult( + pair_id=pair_id, + table_index=0, + qa_index=qa_idx, + question=question, + answer=answer, + qa_type=qa_type, + correct_count=correct_count, + total_trials=config.trials, + accuracy=accuracy, + responses=responses, + difficulty_category=difficulty, + ) + all_results.append(result) + + # 진행 상황 로그 + status = "✓" if config.min_accuracy <= accuracy <= config.max_accuracy else "✗" + logger.info(f" [{qa_idx + 1}/{len(qa_results)}] {qa_type}: {correct_count}/{config.trials} ({accuracy:.0%}) [{difficulty}] {status}") + + # 필터링 (목표 난이도 범위) + filtered_results = [ + r for r in all_results + if config.min_accuracy <= r.accuracy <= config.max_accuracy + ] + + # 결과 저장 + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + output_file = output_dir / f"qa_difficulty_analysis_{timestamp}.json" + + output_data = { + "domain": domain, + "config": { + "vllm_url": config.vllm_url, + "model_name": model_name, + "trials": config.trials, + "min_accuracy": config.min_accuracy, + "max_accuracy": config.max_accuracy, + "temperature": config.temperature, + }, + "stats": stats, + "filtered_count": len(filtered_results), + "all_results": [r.to_dict() for r in all_results], + "filtered_for_review": [r.to_dict() for r in filtered_results], + "timestamp": timestamp, + } + + with open(output_file, "w", encoding="utf-8") as f: + json.dump(output_data, f, ensure_ascii=False, indent=2) + + logger.info(f"Results saved to {output_file}") + + # 검수용 간단 리스트 저장 + review_file = output_dir / f"qa_for_review_{timestamp}.json" + review_data = { + "domain": domain, + "description": f"QA pairs with accuracy between {config.min_accuracy:.0%} and {config.max_accuracy:.0%}", + "count": len(filtered_results), + "items": [ + { + "pair_id": r.pair_id, + "qa_type": r.qa_type, + "question": r.question, + "answer": r.answer, + "accuracy": f"{r.accuracy:.0%} ({r.correct_count}/{r.total_trials})", + "sample_model_responses": r.responses[:3], + } + for r in filtered_results + ], + } + + with open(review_file, "w", encoding="utf-8") as f: + json.dump(review_data, f, ensure_ascii=False, indent=2) + + logger.info(f"Review list saved to {review_file}") + + return { + "domain": domain, + "total_qa": stats["total_qa"], + "stats": stats, + "filtered_for_review": len(filtered_results), + "output_file": str(output_file), + "review_file": str(review_file), + } + + +def main(): + parser = argparse.ArgumentParser( + description="vLLM을 사용하여 QA 난이도를 측정하고 검수 대상을 필터링합니다.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + # business 도메인 필터링 + python filter_qa_by_difficulty.py --domain business + + # 모든 도메인 + python filter_qa_by_difficulty.py --all + + # 커스텀 설정 (5회 시도, 20-50% 정확도) + python filter_qa_by_difficulty.py --domain business --trials 5 --min-acc 0.2 --max-acc 0.5 + + # vLLM 서버 지정 + python filter_qa_by_difficulty.py --domain business --vllm-url http://localhost:8000/v1 + + # 테스트 (3개 entry만) + python filter_qa_by_difficulty.py --domain business --limit 3 + +Difficulty Categories: + - too_easy: 90-100% accuracy (제외) + - easy: 70-89% accuracy + - medium: 30-69% accuracy (검수 대상) + - hard: 1-29% accuracy + - very_hard: 0% accuracy + """ + ) + + parser.add_argument( + "--domain", + nargs="+", + choices=list(DOMAIN_DIRS.keys()), + help="필터링할 도메인(들)", + ) + parser.add_argument( + "--all", + action="store_true", + help="모든 도메인 필터링", + ) + parser.add_argument( + "--vllm-url", + default="http://localhost:8000/v1", + help="vLLM 서버 URL (default: http://localhost:8000/v1)", + ) + parser.add_argument( + "--model", + default="", + help="모델 이름 (미지정시 vLLM에서 자동 감지)", + ) + parser.add_argument( + "--trials", + type=int, + default=10, + help="각 QA당 시도 횟수 (default: 10)", + ) + parser.add_argument( + "--min-acc", + type=float, + default=0.3, + help="최소 정확도 (default: 0.3)", + ) + parser.add_argument( + "--max-acc", + type=float, + default=0.6, + help="최대 정확도 (default: 0.6)", + ) + parser.add_argument( + "--temperature", + type=float, + default=0.7, + help="샘플링 temperature (default: 0.7)", + ) + parser.add_argument( + "--limit", + type=int, + help="처리할 최대 entry 수 (테스트용)", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="실제 추론 없이 확인만", + ) + + args = parser.parse_args() + + # 도메인 결정 + if args.all: + domains = list(DOMAIN_DIRS.keys()) + elif args.domain: + domains = args.domain + else: + parser.error("--domain 또는 --all을 지정해야 합니다.") + + # 설정 + config = FilterConfig( + vllm_url=args.vllm_url, + model_name=args.model, + trials=args.trials, + min_accuracy=args.min_acc, + max_accuracy=args.max_acc, + temperature=args.temperature, + ) + + logger.info(f"Domains: {domains}") + logger.info(f"Config: trials={config.trials}, accuracy range={config.min_accuracy:.0%}-{config.max_accuracy:.0%}") + + # 각 도메인 처리 + results = [] + for domain in domains: + logger.info(f"\n{'='*60}") + logger.info(f"Processing domain: {domain}") + logger.info(f"{'='*60}") + + try: + result = filter_qa_for_domain( + domain=domain, + config=config, + limit=args.limit, + dry_run=args.dry_run, + ) + results.append(result) + except Exception as e: + logger.error(f"Failed to process {domain}: {e}") + results.append({"domain": domain, "error": str(e)}) + + # 요약 + print("\n" + "=" * 60) + print(" QA Difficulty Filtering Summary") + print("=" * 60) + for result in results: + domain = result.get("domain", "unknown") + if "error" in result: + print(f" {domain}: ERROR - {result['error']}") + elif result.get("dry_run"): + img_status = "✓" if result.get("images_exist") else "✗ (run capture_html_to_images.py first)" + print(f" {domain}: {result.get('total_qa', 0)} QA pairs, {result.get('image_count', 0)} images {img_status} (dry run)") + else: + stats = result.get("stats", {}) + filtered = result.get("filtered_for_review", 0) + total = result.get("total_qa", 0) + print(f" {domain}:") + print(f" Total QA: {total}") + print(f" too_easy: {stats.get('too_easy', 0)}, easy: {stats.get('easy', 0)}") + print(f" medium: {stats.get('medium', 0)}, hard: {stats.get('hard', 0)}, very_hard: {stats.get('very_hard', 0)}") + print(f" → For review: {filtered} ({filtered/total*100:.1f}% of total)" if total > 0 else "") + print("=" * 60) + + +if __name__ == "__main__": + main() diff --git a/generate_synthetic_table/flow.py b/generate_synthetic_table/flow.py index 8be68dd..ce2d976 100644 --- a/generate_synthetic_table/flow.py +++ b/generate_synthetic_table/flow.py @@ -49,6 +49,7 @@ class TableState(TypedDict, total=False): synthetic_json: dict # 파싱된 합성 데이터 JSON qa_results: List[Dict] # 생성된 QA 쌍 token_usage: int # QA 생성에 사용된 총 토큰 수 + is_multi_image: bool # 다중 이미지 입력 여부 (cross-image QA 생성됨) def _encode_image(image_path: Path) -> str: @@ -684,12 +685,65 @@ def _node(state: TableState) -> TableState: return _node +def generate_long_sequence_node(llm: ChatOpenAI) -> Callable[[TableState], TableState]: + """Generate long_sequence QA pair separately (context-dependent questions).""" + + def _node(state: TableState) -> TableState: + logger.info("Entering node: generate_long_sequence") + + # Try to load long_sequence prompt, skip if not available + try: + prompt_template = _load_prompt("generate_long_sequence", state.get("domain")) + except ValueError: + logger.info("No generate_long_sequence prompt found, skipping long_sequence generation") + return state + + if state.get("errors"): + return state + + synthetic_html = state.get("synthetic_table") + if not synthetic_html: + logger.warning("No synthetic table for long_sequence generation, skipping") + return state + + try: + prompt = prompt_template.format(synthetic_html=synthetic_html) + except KeyError as e: + logger.warning(f"long_sequence prompt missing placeholder: {e}, skipping") + return state + + response_text, token_usage = _call_llm(llm, prompt, return_token_usage=True) + + logger.info(f"Long sequence generation token usage: {token_usage}") + + response_json = robust_json_parse(response_text) + + if response_json and "qa_pairs" in response_json: + long_seq_qa = response_json["qa_pairs"] + # Append to existing qa_results + existing_qa = list(state.get("qa_results", [])) + existing_qa.extend(long_seq_qa) + # Update token usage + existing_token_usage = state.get("token_usage", 0) + total_token_usage = existing_token_usage + token_usage + logger.info(f"Added {len(long_seq_qa)} long_sequence QA pairs. Total QA: {len(existing_qa)}") + return {**state, "qa_results": existing_qa, "token_usage": total_token_usage} + else: + logger.warning("long_sequence generation did not return valid JSON or 'qa_pairs' key.") + return state + + return _node + + def generate_qa_from_image_node(llm: ChatOpenAI) -> Callable[[TableState], TableState]: - """Generate QA pairs directly from image (QA-only mode).""" + """Generate QA pairs directly from image (QA-only mode). + + If multiple images are provided, uses 'generate_qa_from_multi_image' prompt + to generate cross-image QA pairs that require understanding multiple tables. + """ def _node(state: TableState) -> TableState: logger.info("Entering node: generate_qa_from_image") - prompt_template = _load_prompt("generate_qa_from_image", state.get("domain")) if state.get("errors"): return state @@ -711,6 +765,18 @@ def _node(state: TableState) -> TableState: else: logger.warning(f"Skipping missing image in batch: {img_p}") + # Use multi-image prompt if there are multiple images + is_multi_image = len(image_data_urls) > 1 + if is_multi_image: + logger.info(f"Multi-image mode detected: {len(image_data_urls)} images. Using cross-image QA prompt.") + try: + prompt_template = _load_prompt("generate_qa_from_multi_image", state.get("domain")) + except ValueError: + logger.warning("Multi-image prompt not found, falling back to single-image prompt") + prompt_template = _load_prompt("generate_qa_from_image", state.get("domain")) + else: + prompt_template = _load_prompt("generate_qa_from_image", state.get("domain")) + prompt = prompt_template response_text, token_usage = _call_llm(llm, prompt, image_urls=image_data_urls, return_token_usage=True) @@ -727,7 +793,10 @@ def _node(state: TableState) -> TableState: logger.warning("QA generation from image did not return valid JSON or 'qa_pairs' key.") logger.info(f"Returning token_usage: {token_usage}") - return {**state, "qa_results": qa_results, "token_usage": token_usage} + result_state = {**state, "qa_results": qa_results, "token_usage": token_usage} + if is_multi_image: + result_state["is_multi_image"] = True + return result_state return _node @@ -800,6 +869,7 @@ def build_synthetic_table_graph( if not skip_qa: graph.add_node("generate_qa", generate_qa_node(llm)) + graph.add_node("generate_long_sequence", generate_long_sequence_node(llm)) # Routing based on provider and input type def route_start(state: TableState) -> str: @@ -852,7 +922,8 @@ def route_start(state: TableState) -> str: graph.add_edge("parse_synthetic_table", END) else: graph.add_edge("parse_synthetic_table", "generate_qa") - graph.add_edge("generate_qa", END) + graph.add_edge("generate_qa", "generate_long_sequence") + graph.add_edge("generate_long_sequence", END) return graph diff --git a/generate_synthetic_table/prompts/academic.yaml b/generate_synthetic_table/prompts/academic.yaml index 87543eb..ea2caed 100644 --- a/generate_synthetic_table/prompts/academic.yaml +++ b/generate_synthetic_table/prompts/academic.yaml @@ -6,7 +6,7 @@ generate_qa: | {synthetic_html} ### [Instructions] - 1. **Candidate Generation & Filtering**: Internally generate 10 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 10 pairs) + 1. **Candidate Generation & Filtering**: Internally generate 9 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 9 pairs) 2. **Domain Suitability**: Questions must maintain an academic tone and accurately handle experimental results, performance metrics (Accuracy, F1-score, etc.), model names, and statistical significance. 3. **Strict Constraints**: - Answers must be derived ONLY from the table (and provided context). No external knowledge. @@ -14,6 +14,7 @@ generate_qa: | - Output format must strictly follow JSON. - Questions and Answers MUST be written in Korean. - reasoning_annotation MUST be written in English and MUST be a single string (not a list). + - **DO NOT use real model/dataset names** (e.g., BERT, GPT, ResNet, ImageNet). Use fictional names like "Model-A", "Dataset-X", "Method-알파". ### [Validation Criteria] - Is the answer uniquely determined within the table? @@ -21,16 +22,15 @@ generate_qa: | - Is the question clear and unambiguous? (e.g., "Best model" -> "Model with highest Accuracy") ### [Reasoning Type Definitions (Academic Domain)] - (1) lookup: Retrieve specific model performance or value without condition/calculation. (e.g., "What is the ImageNet Top-1 Accuracy of ResNet-50?") + (1) lookup: Retrieve specific model performance or value without condition/calculation. (e.g., "What is the Top-1 Accuracy of Model-A?") (2) filter: Select rows/columns meeting specific conditions (performance, params, etc.). (e.g., "List all models with parameters under 10M.") - (3) aggregate: Statistical aggregation of experimental results (Sum, Avg, Max, Min, Count). (e.g., "What is the average F1-score of all BERT variants?") - (4) compare: Compare performance against baseline or between models. (e.g., "Does the Proposed Method have a higher BLEU score than SOTA?") + (3) aggregate: Statistical aggregation of experimental results (Sum, Avg, Max, Min, Count). (e.g., "What is the average F1-score of all model variants?") + (4) compare: Compare performance against baseline or between models. (e.g., "Does the Proposed Method have a higher BLEU score than the baseline?") (5) arithmetic: Specific calculation beyond simple comparison (difference, growth rate). (e.g., "What is the percentage improvement of Large model over Base model?") (6) temporal: Deduce trends over years or epochs. (e.g., "Which model published after 2020 has the best performance?") (7) multi_hop: Multi-step inference finding a value first, then using it as a key. (e.g., "What is the Precision of the model with the highest Recall?") (8) implicit_reference: Referring to specific metrics contextually without explicit column name. (e.g., "Which is the best performing model?" -> implies Bolded value or Accuracy column) (9) ellipsis: Recovering omitted comparisons or criteria from table structure. (e.g., "How much did performance drop in 'w/o attention'?" -> implies comparison to Full Model) - (10) long_sequence (Context-Dependent): Requires interpreting 'Experimental Setup' or 'Hypothesis' text (Context) to filter table data. **Requirement**: Must generate a hypothetical [Context] paragraph needed to solve the question. ### [Output Format (JSON)] {{ @@ -42,7 +42,7 @@ generate_qa: | "reasoning_annotation": "Step-by-step logic to derive answer (MUST be a string, not a list)", "context": null }}, - ... (One per Reasoning Type => Total 10) + ... (One per Reasoning Type => Total 9) ] }} @@ -51,7 +51,7 @@ generate_qa_from_image: | Your mission is to analyze the provided academic table image and generate Question-Answer (QA) pairs that fit the specified Reasoning Type definitions. ### [Instructions] - 1. **Candidate Generation & Filtering**: Internally generate 10 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 10 pairs) + 1. **Candidate Generation & Filtering**: Internally generate 9 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 9 pairs) 2. **Domain Suitability**: Questions must maintain an academic tone and accurately handle experimental results, performance metrics (Accuracy, F1-score, etc.), model names, and statistical significance. 3. **Strict Constraints**: - Answers must be derived ONLY from the table (and provided context). No external knowledge. @@ -59,6 +59,7 @@ generate_qa_from_image: | - Output format must strictly follow JSON. - Questions and Answers MUST be written in Korean. - reasoning_annotation MUST be written in English and MUST be a single string (not a list). + - **DO NOT use real model/dataset names** (e.g., BERT, GPT, ResNet, ImageNet). Use fictional names like "Model-A", "Dataset-X", "Method-알파". ### [Validation Criteria] - Is the answer uniquely determined within the table? @@ -66,16 +67,15 @@ generate_qa_from_image: | - Is the question clear and unambiguous? (e.g., "Best model" -> "Model with highest Accuracy") ### [Reasoning Type Definitions (Academic Domain)] - (1) lookup: Retrieve specific model performance or value without condition/calculation. (e.g., "What is the ImageNet Top-1 Accuracy of ResNet-50?") + (1) lookup: Retrieve specific model performance or value without condition/calculation. (e.g., "What is the Top-1 Accuracy of Model-A?") (2) filter: Select rows/columns meeting specific conditions (performance, params, etc.). (e.g., "List all models with parameters under 10M.") - (3) aggregate: Statistical aggregation of experimental results (Sum, Avg, Max, Min, Count). (e.g., "What is the average F1-score of all BERT variants?") - (4) compare: Compare performance against baseline or between models. (e.g., "Does the Proposed Method have a higher BLEU score than SOTA?") + (3) aggregate: Statistical aggregation of experimental results (Sum, Avg, Max, Min, Count). (e.g., "What is the average F1-score of all model variants?") + (4) compare: Compare performance against baseline or between models. (e.g., "Does the Proposed Method have a higher BLEU score than the baseline?") (5) arithmetic: Specific calculation beyond simple comparison (difference, growth rate). (e.g., "What is the percentage improvement of Large model over Base model?") (6) temporal: Deduce trends over years or epochs. (e.g., "Which model published after 2020 has the best performance?") (7) multi_hop: Multi-step inference finding a value first, then using it as a key. (e.g., "What is the Precision of the model with the highest Recall?") (8) implicit_reference: Referring to specific metrics contextually without explicit column name. (e.g., "Which is the best performing model?" -> implies Bolded value or Accuracy column) (9) ellipsis: Recovering omitted comparisons or criteria from table structure. (e.g., "How much did performance drop in 'w/o attention'?" -> implies comparison to Full Model) - (10) long_sequence (Context-Dependent): Requires interpreting 'Experimental Setup' or 'Hypothesis' text (Context) to filter table data. **Requirement**: Must generate a hypothetical [Context] paragraph needed to solve the question. ### [Output Format (JSON)] {{ @@ -87,7 +87,59 @@ generate_qa_from_image: | "reasoning_annotation": "Step-by-step logic to derive answer (MUST be a string, not a list)", "context": null }}, - ... (One per Reasoning Type => Total 10) + ... (One per Reasoning Type => Total 9) + ] + }} + Return ONLY the JSON object. + +generate_qa_from_multi_image: | + You are an 'AI Data Researcher' specialized in building high-quality QA datasets that require understanding MULTIPLE academic/scientific table images together. + Your mission is to analyze ALL provided academic table images and generate Question-Answer (QA) pairs that REQUIRE information from MULTIPLE images to answer. + + **⚠️ CRITICAL REQUIREMENT: CROSS-IMAGE REASONING ⚠️** + - Every QA pair MUST require information from AT LEAST TWO images to answer correctly. + - Questions answerable from a single image are INVALID. + - Focus on comparisons, aggregations, or inferences that span multiple experimental results, model comparisons, or benchmark tables. + + ### [Instructions] + 1. **Analyze All Images**: First, understand what data each image contains and how they relate (e.g., different datasets, different model ablations, training vs test results). + 2. **Generate Cross-Image QA**: Create 9 diverse QA pairs where each question requires synthesizing information from multiple images. + 3. **Strict Constraints**: + - Answers must be derived from combining data across images. No external knowledge. + - Each QA pair must correspond to exactly one Reasoning Type. + - Output format must strictly follow JSON. + - Questions and Answers MUST be written in Korean. + - reasoning_annotation MUST be written in English, MUST be a single string, and MUST specify which images were used. + - **DO NOT use real model/dataset names** (e.g., BERT, GPT, ImageNet). Use fictional names like "Model-A", "Dataset-X", "Method-알파". + + ### [Validation Criteria] + - Does the answer REQUIRE data from multiple images? (Single-image answers are INVALID) + - Is the reasoning process logically flawless? + - Is the question clear about what experimental data is being compared or combined? + + ### [Cross-Image Reasoning Type Definitions (Academic Domain)] + (1) cross_lookup: Retrieve and combine performance values from different result tables. (e.g., "What is Model-A's accuracy on both Dataset-X and Dataset-Y from the two tables?") + (2) cross_filter: Filter models across benchmark tables based on conditions. (e.g., "Which models achieve >90% accuracy on both datasets shown in the two images?") + (3) cross_aggregate: Aggregate experimental results spanning multiple benchmarks. (e.g., "What is the average F1-score of Method-가 across all evaluation tables?") + (4) cross_compare: Compare model performance between different experimental settings. (e.g., "Does the proposed method outperform the baseline on both in-domain and out-of-domain tests?") + (5) cross_arithmetic: Calculate performance differences using data from multiple tables. (e.g., "What is the accuracy improvement of Model-A from the ablation table to the full model table?") + (6) cross_temporal: Identify experimental trends by combining multiple result tables. (e.g., "Based on both training curves, which model converges faster?") + (7) cross_multi_hop: Multi-step academic inference across tables. (e.g., "Find the best model on Dataset-X in Image 1, then find its parameters in Image 2.") + (8) cross_implicit: Answer questions requiring implicit understanding of relationships between results. (e.g., "Which approach is most efficient?" requires combining accuracy and parameter count from multiple tables) + (9) cross_synthesis: Synthesize research insights only possible by viewing all tables together. (e.g., "Based on both the main results and ablation study, which component contributes most to performance?") + + ### [Output Format (JSON)] + {{ + "qa_pairs": [ + {{ + "question": "Question requiring multiple academic images to answer", + "answer": "Answer derived from multiple images", + "type": "cross_lookup", + "reasoning_annotation": "Step 1: From Image 1, extract X. Step 2: From Image 2, extract Y. Step 3: Combine to get answer.", + "context": null, + "images_used": ["image_1", "image_2"] + }}, + ... (One per Reasoning Type => Total 9) ] }} Return ONLY the JSON object. @@ -111,8 +163,9 @@ generate_synthetic_table: | 3. **⚠️ Data Transformation - ABSOLUTELY MANDATORY ⚠️:** - **ALL data cell values MUST be replaced with completely new synthetic values.** - **NEVER copy any original data values** - generate fresh, realistic alternatives. - - For student/model names: Generate DIFFERENT names - - For university names: Generate DIFFERENT names + - **NEVER use real model/dataset/university names** (BERT, GPT, ResNet, ImageNet, MIT, Stanford, etc.). Use fictional names like "Model-A", "Dataset-X", "University-가". + - For student/model names: Generate DIFFERENT fictional names + - For university names: Generate DIFFERENT fictional names - For grades/scores: Generate DIFFERENT realistic values - For course/research topics: Generate DIFFERENT titles - For dates: Generate DIFFERENT plausible dates @@ -151,7 +204,8 @@ generate_synthetic_table_from_image: | 3. **⚠️ Data Generation - ABSOLUTELY CRITICAL ⚠️:** - **NEVER copy the data values from the image** - this is NOT an OCR task - **ALL cell content must be completely NEW and DIFFERENT** - - For student/model names: Generate DIFFERENT names + - **NEVER use real model/dataset/university names** (BERT, GPT, ResNet, ImageNet, MIT, Stanford, etc.). Use fictional names like "Model-A", "Dataset-X", "University-가". + - For student/model names: Generate DIFFERENT fictional names - For grades/scores: Generate DIFFERENT values - For course/research topics: Generate DIFFERENT titles 4. **Styling:** Use **Tailwind CSS** classes exclusively (NO inline styles). @@ -170,3 +224,40 @@ generate_synthetic_table_from_image: | - Score in image: "점수A" → Generate: "점수B" ⚠️ If the generated content is identical or very similar to the image, the output is INVALID. + +generate_long_sequence: | + You are an 'AI Data Researcher' specialized in creating context-dependent QA pairs for academic/scientific tables. + Your mission is to generate a single high-quality "long_sequence" type QA pair that requires interpreting external context to answer questions about the table. + + **Input Table:** + {synthetic_html} + + ### [Instructions] + 1. **Generate ONE long_sequence QA pair** that requires reading and understanding a context paragraph to filter or interpret the table data. + 2. **Create a realistic academic context** (e.g., "Experimental Setup", "Research Hypothesis", "Ablation Study Goals") that provides information needed to answer the question. + 3. **The question must be unanswerable without the context** - the context should contain key criteria or conditions. + 4. **Strict Constraints**: + - Answer must be derived from BOTH the table AND the context. Neither alone is sufficient. + - Questions and Answers MUST be written in Korean. + - reasoning_annotation MUST be written in English and MUST be a single string. + - Context must be written in Korean and be 2-4 sentences long. + - **DO NOT use real model/dataset names** (e.g., BERT, GPT, ResNet). Use fictional names. + + ### [Example Scenarios (Academic)] + - Context describes experimental conditions (dataset size, hardware) → Question asks which models meet the criteria + - Context outlines baseline comparison requirements → Question asks which methods show improvement + - Context specifies evaluation metrics of interest → Question asks for rankings based on those metrics + + ### [Output Format (JSON)] + {{ + "qa_pairs": [ + {{ + "question": "Question requiring context to answer", + "answer": "Answer derived from table + context", + "type": "long_sequence", + "reasoning_annotation": "Step 1: Extract key criteria from context. Step 2: Apply criteria to table. Step 3: Derive answer.", + "context": "실험 설정에 따르면... (2-4 sentences of academic context in Korean)" + }} + ] + }} + Return ONLY the JSON object. diff --git a/generate_synthetic_table/prompts/business.yaml b/generate_synthetic_table/prompts/business.yaml index 18ebc27..40594c5 100644 --- a/generate_synthetic_table/prompts/business.yaml +++ b/generate_synthetic_table/prompts/business.yaml @@ -6,7 +6,7 @@ generate_qa: | {synthetic_html} ### [Instructions] - 1. **Candidate Generation & Filtering**: Internally generate 10 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 10 pairs) + 1. **Candidate Generation & Filtering**: Internally generate 9 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 9 pairs) 2. **Domain Suitability**: Questions must maintain a business tone and accurately handle revenue, profit margins, growth rates, market share, and employee performance metrics. 3. **Strict Constraints**: - Answers must be derived ONLY from the table (and provided context). No external knowledge. @@ -14,6 +14,7 @@ generate_qa: | - Output format must strictly follow JSON. - Questions and Answers MUST be written in Korean. - reasoning_annotation MUST be written in English and MUST be a single string (not a list). + - **DO NOT use real company names** (e.g., Samsung, Apple, Google). Use fictional names like "A사", "B기업", "가나다 주식회사". ### [Validation Criteria] - Is the answer uniquely determined within the table? @@ -30,7 +31,6 @@ generate_qa: | (7) multi_hop: Multi-step inference finding a value first, then using it as a key. (e.g., "What is the name of the Branch Manager of the branch with the #1 Revenue?") (8) implicit_reference: Referring to specific metrics contextually without explicit column name. (e.g., "Which is the most profitable project?" -> implies Profit Margin column) (9) ellipsis: Recovering omitted comparisons or criteria from table structure. (e.g., "What is Q4 performance?" -> implies continuation from Q1-Q3 context) - (10) long_sequence (Context-Dependent): Requires interpreting 'Management Goals' or 'Market Conditions' text (Context) to filter table data. **Requirement**: Must generate a hypothetical [Context] paragraph needed to solve the question. ### [Output Format (JSON)] {{ @@ -42,7 +42,7 @@ generate_qa: | "reasoning_annotation": "Step-by-step logic to derive answer (MUST be a string, not a list)", "context": null }}, - ... (One per Reasoning Type => Total 10) + ... (One per Reasoning Type => Total 9) ] }} @@ -51,7 +51,7 @@ generate_qa_from_image: | Your mission is to analyze the provided business table image and generate Question-Answer (QA) pairs that fit the specified Reasoning Type definitions. ### [Instructions] - 1. **Candidate Generation & Filtering**: Internally generate 10 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 10 pairs) + 1. **Candidate Generation & Filtering**: Internally generate 9 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 9 pairs) 2. **Domain Suitability**: Questions must maintain a business tone and accurately handle revenue, profit margins, growth rates, market share, and employee performance metrics. 3. **Strict Constraints**: - Answers must be derived ONLY from the table (and provided context). No external knowledge. @@ -59,6 +59,7 @@ generate_qa_from_image: | - Output format must strictly follow JSON. - Questions and Answers MUST be written in Korean. - reasoning_annotation MUST be written in English and MUST be a single string (not a list). + - **DO NOT use real company names** (e.g., Samsung, Apple, Google). Use fictional names like "A사", "B기업", "가나다 주식회사". ### [Validation Criteria] - Is the answer uniquely determined within the table? @@ -75,7 +76,6 @@ generate_qa_from_image: | (7) multi_hop: Multi-step inference finding a value first, then using it as a key. (e.g., "What is the name of the Branch Manager of the branch with the #1 Revenue?") (8) implicit_reference: Referring to specific metrics contextually without explicit column name. (e.g., "Which is the most profitable project?" -> implies Profit Margin column) (9) ellipsis: Recovering omitted comparisons or criteria from table structure. (e.g., "What is Q4 performance?" -> implies continuation from Q1-Q3 context) - (10) long_sequence (Context-Dependent): Requires interpreting 'Management Goals' or 'Market Conditions' text (Context) to filter table data. **Requirement**: Must generate a hypothetical [Context] paragraph needed to solve the question. ### [Output Format (JSON)] {{ @@ -87,7 +87,59 @@ generate_qa_from_image: | "reasoning_annotation": "Step-by-step logic to derive answer (MUST be a string, not a list)", "context": null }}, - ... (One per Reasoning Type => Total 10) + ... (One per Reasoning Type => Total 9) + ] + }} + Return ONLY the JSON object. + +generate_qa_from_multi_image: | + You are an 'AI Data Researcher' specialized in building high-quality QA datasets that require understanding MULTIPLE table images together. + Your mission is to analyze ALL provided business table images and generate Question-Answer (QA) pairs that REQUIRE information from MULTIPLE images to answer. + + **⚠️ CRITICAL REQUIREMENT: CROSS-IMAGE REASONING ⚠️** + - Every QA pair MUST require information from AT LEAST TWO images to answer correctly. + - Questions answerable from a single image are INVALID. + - Focus on comparisons, aggregations, or inferences that span multiple tables. + + ### [Instructions] + 1. **Analyze All Images**: First, understand what data each image contains and how they relate to each other (e.g., same company different periods, different departments, related metrics). + 2. **Generate Cross-Image QA**: Create 9 diverse QA pairs where each question requires synthesizing information from multiple images. + 3. **Strict Constraints**: + - Answers must be derived from combining data across images. No external knowledge. + - Each QA pair must correspond to exactly one Reasoning Type. + - Output format must strictly follow JSON. + - Questions and Answers MUST be written in Korean. + - reasoning_annotation MUST be written in English, MUST be a single string, and MUST specify which images were used. + - **DO NOT use real company names** (e.g., Samsung, Apple, Google). Use fictional names like "A사", "B기업", "가나다 주식회사". + + ### [Validation Criteria] + - Does the answer REQUIRE data from multiple images? (Single-image answers are INVALID) + - Is the reasoning process logically flawless? + - Is the question clear about what is being compared or combined? + + ### [Cross-Image Reasoning Type Definitions (Business Domain)] + (1) cross_lookup: Retrieve and combine specific values from different images. (e.g., "What is the total Q1 revenue of Branch A from both Table 1 and Table 2?") + (2) cross_filter: Filter rows across tables based on conditions spanning multiple images. (e.g., "Which departments appear in both tables and have positive profit margins in both?") + (3) cross_aggregate: Aggregate data spanning multiple images. (e.g., "What is the combined total revenue across all branches shown in both images?") + (4) cross_compare: Compare values or trends between different images. (e.g., "Which table shows higher average profit margin - Table 1 or Table 2?") + (5) cross_arithmetic: Calculate differences, ratios, or changes using data from multiple images. (e.g., "What is the revenue growth rate from the Q1 table to the Q2 table for Branch A?") + (6) cross_temporal: Identify trends or changes by combining time-series data from multiple images. (e.g., "Combining both annual reports, which department showed continuous growth?") + (7) cross_multi_hop: Multi-step inference requiring lookups across images. (e.g., "Find the top performer in Image 1, then find their metrics in Image 2.") + (8) cross_implicit: Answer questions requiring implicit understanding of relationships between images. (e.g., "Which region improved the most?" when improvement requires comparing two period tables) + (9) cross_synthesis: Synthesize insights that are only possible by viewing all images together. (e.g., "Based on both the budget table and the results table, which projects exceeded their targets?") + + ### [Output Format (JSON)] + {{ + "qa_pairs": [ + {{ + "question": "Question requiring multiple images to answer", + "answer": "Answer derived from multiple images", + "type": "cross_lookup", + "reasoning_annotation": "Step 1: From Image 1, extract X. Step 2: From Image 2, extract Y. Step 3: Combine to get answer.", + "context": null, + "images_used": ["image_1", "image_2"] + }}, + ... (One per Reasoning Type => Total 9) ] }} Return ONLY the JSON object. @@ -112,7 +164,8 @@ generate_synthetic_table: | 3. **⚠️ Data Transformation - ABSOLUTELY MANDATORY ⚠️:** - **ALL data cell values MUST be replaced with completely new synthetic values.** - **NEVER copy any original data values** - generate fresh, realistic alternatives. - - For company/team names: Generate DIFFERENT names (e.g., "A팀" → "B팀") + - **NEVER use real company/brand names** (Samsung, Apple, Google, 현대, LG, etc.). Use fictional names like "A사", "가나다 기업", "XYZ Corp". + - For company/team names: Generate DIFFERENT fictional names (e.g., "A팀" → "B팀") - For employee names: Generate DIFFERENT Korean names (e.g., "김OO" → "박OO") - For business metrics: Generate DIFFERENT numbers (e.g., "100억" → "150억") - For strategy/description text: Write DIFFERENT content with similar structure @@ -156,8 +209,9 @@ generate_synthetic_table_from_image: | 3. **⚠️ Data Generation - ABSOLUTELY CRITICAL ⚠️:** - **NEVER copy the data values from the image** - this is NOT an OCR task - **ALL cell content must be completely NEW and DIFFERENT from the original** + - **NEVER use real company/brand names** (Samsung, Apple, Google, 현대, LG, etc.). Use fictional names like "A사", "가나다 기업", "XYZ Corp". - Generate COMPLETELY NEW synthetic business values for all data cells: - * For company/team names: Generate DIFFERENT names (e.g., "A팀" → "B팀") + * For company/team names: Generate DIFFERENT fictional names (e.g., "A팀" → "B팀") * For business metrics: Generate DIFFERENT numbers (e.g., "100억" → "150억") * For strategy/description text: Write DIFFERENT content with similar structure * For bullet point items: Create DIFFERENT but domain-appropriate items @@ -181,3 +235,40 @@ generate_synthetic_table_from_image: | ⚠️ If the generated content is identical or very similar to the image, the output is INVALID. Remember: The output should be a new synthetic business dataset, not a transcription of the original. + +generate_long_sequence: | + You are an 'AI Data Researcher' specialized in creating context-dependent QA pairs for business tables. + Your mission is to generate a single high-quality "long_sequence" type QA pair that requires interpreting external context to answer questions about the table. + + **Input Table:** + {synthetic_html} + + ### [Instructions] + 1. **Generate ONE long_sequence QA pair** that requires reading and understanding a context paragraph to filter or interpret the table data. + 2. **Create a realistic business context** (e.g., "Management Goals", "Market Conditions", "Strategic Guidelines") that provides information needed to answer the question. + 3. **The question must be unanswerable without the context** - the context should contain key criteria or conditions. + 4. **Strict Constraints**: + - Answer must be derived from BOTH the table AND the context. Neither alone is sufficient. + - Questions and Answers MUST be written in Korean. + - reasoning_annotation MUST be written in English and MUST be a single string. + - Context must be written in Korean and be 2-4 sentences long. + - **DO NOT use real company names** (e.g., Samsung, Apple, Google). Use fictional names. + + ### [Example Scenarios (Business)] + - Context describes a target market condition → Question asks which products/departments meet the criteria + - Context outlines budget constraints → Question asks which projects are feasible + - Context specifies performance thresholds → Question asks which teams qualify + + ### [Output Format (JSON)] + {{ + "qa_pairs": [ + {{ + "question": "Question requiring context to answer", + "answer": "Answer derived from table + context", + "type": "long_sequence", + "reasoning_annotation": "Step 1: Extract key criteria from context. Step 2: Apply criteria to table. Step 3: Derive answer.", + "context": "경영 목표에 따르면... (2-4 sentences of business context in Korean)" + }} + ] + }} + Return ONLY the JSON object. diff --git a/generate_synthetic_table/prompts/default.yaml b/generate_synthetic_table/prompts/default.yaml index 0ca2645..4940e0a 100644 --- a/generate_synthetic_table/prompts/default.yaml +++ b/generate_synthetic_table/prompts/default.yaml @@ -77,6 +77,52 @@ generate_qa_from_image: | Return ONLY the JSON object, no additional text. +generate_qa_from_multi_image: | + You are an expert in creating educational and reasoning questions from tabular data. + Your task is to analyze ALL provided table images and generate Question-Answer (QA) pairs that REQUIRE information from MULTIPLE images to answer. + + **⚠️ CRITICAL REQUIREMENT: CROSS-IMAGE REASONING ⚠️** + - Every QA pair MUST require information from AT LEAST TWO images to answer correctly. + - Questions answerable from a single image are INVALID. + - Focus on comparisons, aggregations, or inferences that span multiple tables. + + ### [Instructions] + 1. **Analyze All Images**: First, understand what data each image contains and how they relate to each other. + 2. **Generate Cross-Image QA**: Create 5 diverse QA pairs where each question requires synthesizing information from multiple images. + 3. **Strict Constraints**: + - Answers must be derived from combining data across images. No external knowledge. + - Output format must strictly follow JSON. + - Questions and Answers MUST be written in Korean. + - reasoning_annotation MUST be written in English, MUST be a single string, and MUST specify which images were used. + - **DO NOT use real company/institution names**. Use fictional names. + + ### [Validation Criteria] + - Does the answer REQUIRE data from multiple images? (Single-image answers are INVALID) + - Is the reasoning process logically flawless? + - Is the question clear about what is being compared or combined? + + ### [Cross-Image Reasoning Types] + - **cross_lookup**: Retrieve and combine specific values from different images. + - **cross_compare**: Compare values or trends between different images. + - **cross_aggregate**: Aggregate data spanning multiple images. + - **cross_arithmetic**: Calculate using data from multiple images. + - **cross_synthesis**: Synthesize insights only possible by viewing all images together. + + ### [Output Format (JSON)] + {{ + "qa_pairs": [ + {{ + "question": "Question requiring multiple images to answer", + "answer": "Answer derived from multiple images", + "type": "cross_lookup", + "reasoning_annotation": "Step 1: From Image 1, extract X. Step 2: From Image 2, extract Y. Step 3: Combine to get answer.", + "images_used": ["image_1", "image_2"] + }}, + ... (Total 5 cross-image QA pairs) + ] + }} + Return ONLY the JSON object. + generate_synthetic_table: | You are a Synthetic Data Generator specialized in creating completely NEW data while preserving table structure. @@ -329,3 +375,40 @@ validate_parsed_table: | Return a JSON object with the following keys: - "valid": boolean (true if valid, false otherwise) - "reason": string (brief explanation of the decision) + +generate_long_sequence: | + You are an 'AI Data Researcher' specialized in creating context-dependent QA pairs for tables. + Your mission is to generate a single high-quality "long_sequence" type QA pair that requires interpreting external context to answer questions about the table. + + **Input Table:** + {synthetic_html} + + ### [Instructions] + 1. **Generate ONE long_sequence QA pair** that requires reading and understanding a context paragraph to filter or interpret the table data. + 2. **Create a realistic context paragraph** (e.g., guidelines, criteria, conditions) that provides information needed to answer the question. + 3. **The question must be unanswerable without the context** - the context should contain key criteria or conditions. + 4. **Strict Constraints**: + - Answer must be derived from BOTH the table AND the context. Neither alone is sufficient. + - Questions and Answers MUST be written in Korean. + - reasoning_annotation MUST be written in English and MUST be a single string. + - Context must be written in Korean and be 2-4 sentences long. + - **DO NOT use real company/institution names**. Use fictional names. + + ### [Example Scenarios] + - Context describes selection criteria → Question asks which items meet the criteria + - Context outlines rules/thresholds → Question asks which entries qualify + - Context specifies conditions → Question asks for items matching those conditions + + ### [Output Format (JSON)] + {{ + "qa_pairs": [ + {{ + "question": "Question requiring context to answer", + "answer": "Answer derived from table + context", + "type": "long_sequence", + "reasoning_annotation": "Step 1: Extract key criteria from context. Step 2: Apply criteria to table. Step 3: Derive answer.", + "context": "조건에 따르면... (2-4 sentences of context in Korean)" + }} + ] + }} + Return ONLY the JSON object. diff --git a/generate_synthetic_table/prompts/finance.yaml b/generate_synthetic_table/prompts/finance.yaml index 77d9927..6e700e0 100644 --- a/generate_synthetic_table/prompts/finance.yaml +++ b/generate_synthetic_table/prompts/finance.yaml @@ -6,7 +6,7 @@ generate_qa: | {synthetic_html} ### [Instructions] - 1. **Candidate Generation & Filtering**: Internally generate 10 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 10 pairs) + 1. **Candidate Generation & Filtering**: Internally generate 9 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 9 pairs) 2. **Domain Suitability**: Questions must maintain a financial professional tone and accurately handle stock prices, financial statements (Assets, Liabilities, Equity), investment metrics (PER, PBR, ROE), interest rates, and exchange rates. 3. **Strict Constraints**: - Answers must be derived ONLY from the table (and provided context). No external knowledge. @@ -14,6 +14,7 @@ generate_qa: | - Output format must strictly follow JSON. - Questions and Answers MUST be written in Korean. - reasoning_annotation MUST be written in English and MUST be a single string (not a list). + - **DO NOT use real company names** (e.g., Samsung, Apple, Google, 현대, SK). Use fictional names like "A사", "B기업", "가나다 주식회사". ### [Validation Criteria] - Is the answer uniquely determined within the table? @@ -21,7 +22,7 @@ generate_qa: | - Is the question clear and unambiguous? (e.g., "Most undervalued stock" -> "Stock with lowest PER") ### [Reasoning Type Definitions (Finance Domain)] - (1) lookup: Retrieve specific stock price or financial figures without condition/calculation. (e.g., "What is Samsung Electronics' 2023 dividend?") + (1) lookup: Retrieve specific stock price or financial figures without condition/calculation. (e.g., "What is Company A's 2023 dividend?") (2) filter: Select rows/columns meeting specific conditions (Market Cap cap, specific sector). (e.g., "List all companies with Debt Ratio under 100%.") (3) aggregate: Statistical aggregation of portfolio or time-series data using Sum/Avg etc. (e.g., "What is the total valuation of held stocks?") (4) compare: Compare financial health between companies or investment metrics. (e.g., "Which company has a higher ROE, Company A or B?") @@ -30,7 +31,6 @@ generate_qa: | (7) multi_hop: Multi-step inference finding a value first, then using it as a key. (e.g., "Who is the largest shareholder of the company with #1 Market Cap?") (8) implicit_reference: Referring to specific metrics contextually without explicit column name. (e.g., "Which stock has strong dividend tendency?" -> implies Dividend Yield column) (9) ellipsis: Recovering omitted comparisons or criteria from table structure. (e.g., "What is Net Income?" -> implies continuation from Operating Profit column context) - (10) long_sequence (Context-Dependent): Requires interpreting 'Market Outlook' or 'Investment Strategy' text (Context) to filter table data. **Requirement**: Must generate a hypothetical [Context] paragraph needed to solve the question. ### [Output Format (JSON)] {{ @@ -42,7 +42,7 @@ generate_qa: | "reasoning_annotation": "Step-by-step logic to derive answer (MUST be a string, not a list)", "context": null }}, - ... (One per Reasoning Type => Total 10) + ... (One per Reasoning Type => Total 9) ] }} @@ -51,7 +51,7 @@ generate_qa_from_image: | Your mission is to analyze the provided financial table image and generate Question-Answer (QA) pairs that fit the specified Reasoning Type definitions. ### [Instructions] - 1. **Candidate Generation & Filtering**: Internally generate 10 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 10 pairs) + 1. **Candidate Generation & Filtering**: Internally generate 9 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 9 pairs) 2. **Domain Suitability**: Questions must maintain a financial professional tone and accurately handle stock prices, financial statements (Assets, Liabilities, Equity), investment metrics (PER, PBR, ROE), interest rates, and exchange rates. 3. **Strict Constraints**: - Answers must be derived ONLY from the table (and provided context). No external knowledge. @@ -59,6 +59,7 @@ generate_qa_from_image: | - Output format must strictly follow JSON. - Questions and Answers MUST be written in Korean. - reasoning_annotation MUST be written in English and MUST be a single string (not a list). + - **DO NOT use real company names** (e.g., Samsung, Apple, Google, 현대, SK). Use fictional names like "A사", "B기업", "가나다 주식회사". ### [Validation Criteria] - Is the answer uniquely determined within the table? @@ -66,7 +67,7 @@ generate_qa_from_image: | - Is the question clear and unambiguous? (e.g., "Most undervalued stock" -> "Stock with lowest PER") ### [Reasoning Type Definitions (Finance Domain)] - (1) lookup: Retrieve specific stock price or financial figures without condition/calculation. (e.g., "What is Samsung Electronics' 2023 dividend?") + (1) lookup: Retrieve specific stock price or financial figures without condition/calculation. (e.g., "What is Company A's 2023 dividend?") (2) filter: Select rows/columns meeting specific conditions (Market Cap cap, specific sector). (e.g., "List all companies with Debt Ratio under 100%.") (3) aggregate: Statistical aggregation of portfolio or time-series data using Sum/Avg etc. (e.g., "What is the total valuation of held stocks?") (4) compare: Compare financial health between companies or investment metrics. (e.g., "Which company has a higher ROE, Company A or B?") @@ -75,7 +76,6 @@ generate_qa_from_image: | (7) multi_hop: Multi-step inference finding a value first, then using it as a key. (e.g., "Who is the largest shareholder of the company with #1 Market Cap?") (8) implicit_reference: Referring to specific metrics contextually without explicit column name. (e.g., "Which stock has strong dividend tendency?" -> implies Dividend Yield column) (9) ellipsis: Recovering omitted comparisons or criteria from table structure. (e.g., "What is Net Income?" -> implies continuation from Operating Profit column context) - (10) long_sequence (Context-Dependent): Requires interpreting 'Market Outlook' or 'Investment Strategy' text (Context) to filter table data. **Requirement**: Must generate a hypothetical [Context] paragraph needed to solve the question. ### [Output Format (JSON)] {{ @@ -87,7 +87,59 @@ generate_qa_from_image: | "reasoning_annotation": "Step-by-step logic to derive answer (MUST be a string, not a list)", "context": null }}, - ... (One per Reasoning Type => Total 10) + ... (One per Reasoning Type => Total 9) + ] + }} + Return ONLY the JSON object. + +generate_qa_from_multi_image: | + You are an 'AI Data Researcher' specialized in building high-quality QA datasets that require understanding MULTIPLE financial table images together. + Your mission is to analyze ALL provided financial table images and generate Question-Answer (QA) pairs that REQUIRE information from MULTIPLE images to answer. + + **⚠️ CRITICAL REQUIREMENT: CROSS-IMAGE REASONING ⚠️** + - Every QA pair MUST require information from AT LEAST TWO images to answer correctly. + - Questions answerable from a single image are INVALID. + - Focus on comparisons, aggregations, or inferences that span multiple financial statements or reports. + + ### [Instructions] + 1. **Analyze All Images**: First, understand what data each image contains and how they relate (e.g., different fiscal periods, income statement vs balance sheet, different securities). + 2. **Generate Cross-Image QA**: Create 9 diverse QA pairs where each question requires synthesizing information from multiple images. + 3. **Strict Constraints**: + - Answers must be derived from combining data across images. No external knowledge. + - Each QA pair must correspond to exactly one Reasoning Type. + - Output format must strictly follow JSON. + - Questions and Answers MUST be written in Korean. + - reasoning_annotation MUST be written in English, MUST be a single string, and MUST specify which images were used. + - **DO NOT use real company/fund names** (e.g., Samsung, Apple, Vanguard). Use fictional names like "A사", "B펀드", "가나다증권". + + ### [Validation Criteria] + - Does the answer REQUIRE data from multiple images? (Single-image answers are INVALID) + - Is the reasoning process logically flawless? + - Is the question clear about what is being compared or combined? + + ### [Cross-Image Reasoning Type Definitions (Finance Domain)] + (1) cross_lookup: Retrieve and combine specific financial values from different statements. (e.g., "What is the total assets combining both Q1 and Q2 balance sheets?") + (2) cross_filter: Filter entries across financial statements based on conditions. (e.g., "Which accounts show positive growth in both the income statement and cash flow statement?") + (3) cross_aggregate: Aggregate financial data spanning multiple periods or statements. (e.g., "What is the total revenue across all quarterly reports shown?") + (4) cross_compare: Compare financial ratios or metrics between different periods or portfolios. (e.g., "Did the debt-to-equity ratio improve from Table 1 to Table 2?") + (5) cross_arithmetic: Calculate financial metrics using data from multiple statements. (e.g., "Calculate the year-over-year revenue growth using data from both annual reports.") + (6) cross_temporal: Identify financial trends by combining multiple period data. (e.g., "Based on all quarterly statements, what is the profit margin trend?") + (7) cross_multi_hop: Multi-step financial inference across statements. (e.g., "Find the highest dividend stock in Image 1, then find its P/E ratio in Image 2.") + (8) cross_implicit: Answer questions requiring understanding relationships between financial statements. (e.g., "Which company is more leveraged?" requires comparing debt from multiple sources) + (9) cross_synthesis: Synthesize financial insights only possible by viewing all statements together. (e.g., "Based on both income and cash flow statements, which segments are cash-generative?") + + ### [Output Format (JSON)] + {{ + "qa_pairs": [ + {{ + "question": "Question requiring multiple financial images to answer", + "answer": "Answer derived from multiple images", + "type": "cross_lookup", + "reasoning_annotation": "Step 1: From Image 1, extract X. Step 2: From Image 2, extract Y. Step 3: Combine to get answer.", + "context": null, + "images_used": ["image_1", "image_2"] + }}, + ... (One per Reasoning Type => Total 9) ] }} Return ONLY the JSON object. @@ -111,7 +163,8 @@ generate_synthetic_table: | 3. **⚠️ Data Transformation - ABSOLUTELY MANDATORY ⚠️:** - **ALL data cell values MUST be replaced with completely new synthetic values.** - **NEVER copy any original data values** - generate fresh, realistic alternatives. - - For company names: Generate DIFFERENT names (e.g., "A회사" → "B회사") + - **NEVER use real company/brand names** (Samsung, Apple, Google, 현대, SK, LG, etc.). Use fictional names like "A사", "가나다 기업", "XYZ Corp". + - For company names: Generate DIFFERENT fictional names (e.g., "A회사" → "B회사") - For financial figures: Generate DIFFERENT amounts (similar magnitude, different values) - For percentages/ratios: Generate DIFFERENT metrics - For dates: Generate DIFFERENT plausible dates @@ -150,7 +203,8 @@ generate_synthetic_table_from_image: | 3. **⚠️ Data Generation - ABSOLUTELY CRITICAL ⚠️:** - **NEVER copy the data values from the image** - this is NOT an OCR task - **ALL cell content must be completely NEW and DIFFERENT** - - For company names: Generate DIFFERENT names + - **NEVER use real company/brand names** (Samsung, Apple, Google, 현대, SK, LG, etc.). Use fictional names like "A사", "가나다 기업", "XYZ Corp". + - For company names: Generate DIFFERENT fictional names - For financial figures: Generate DIFFERENT amounts - For percentages/ratios: Generate DIFFERENT metrics 4. **Styling:** Use **Tailwind CSS** classes exclusively (NO inline styles). @@ -169,3 +223,40 @@ generate_synthetic_table_from_image: | - Amount in image: "50억" → Generate: "80억" ⚠️ If the generated content is identical or very similar to the image, the output is INVALID. + +generate_long_sequence: | + You are an 'AI Data Researcher' specialized in creating context-dependent QA pairs for financial tables. + Your mission is to generate a single high-quality "long_sequence" type QA pair that requires interpreting external context to answer questions about the table. + + **Input Table:** + {synthetic_html} + + ### [Instructions] + 1. **Generate ONE long_sequence QA pair** that requires reading and understanding a context paragraph to filter or interpret the table data. + 2. **Create a realistic financial context** (e.g., "Market Outlook", "Investment Strategy", "Risk Guidelines") that provides information needed to answer the question. + 3. **The question must be unanswerable without the context** - the context should contain key criteria or conditions. + 4. **Strict Constraints**: + - Answer must be derived from BOTH the table AND the context. Neither alone is sufficient. + - Questions and Answers MUST be written in Korean. + - reasoning_annotation MUST be written in English and MUST be a single string. + - Context must be written in Korean and be 2-4 sentences long. + - **DO NOT use real company names** (e.g., Samsung, Apple, Google). Use fictional names. + + ### [Example Scenarios (Finance)] + - Context describes investment criteria (PER < 15, ROE > 10%) → Question asks which stocks qualify + - Context outlines risk tolerance levels → Question asks which portfolio allocation is appropriate + - Context specifies sector preferences → Question asks which companies match the strategy + + ### [Output Format (JSON)] + {{ + "qa_pairs": [ + {{ + "question": "Question requiring context to answer", + "answer": "Answer derived from table + context", + "type": "long_sequence", + "reasoning_annotation": "Step 1: Extract key criteria from context. Step 2: Apply criteria to table. Step 3: Derive answer.", + "context": "투자 전략에 따르면... (2-4 sentences of financial context in Korean)" + }} + ] + }} + Return ONLY the JSON object. diff --git a/generate_synthetic_table/prompts/medical.yaml b/generate_synthetic_table/prompts/medical.yaml index 7bf995c..eb16745 100644 --- a/generate_synthetic_table/prompts/medical.yaml +++ b/generate_synthetic_table/prompts/medical.yaml @@ -6,7 +6,7 @@ generate_qa: | {synthetic_html} ### [Instructions] - 1. **Candidate Generation & Filtering**: Internally generate 10 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 10 pairs) + 1. **Candidate Generation & Filtering**: Internally generate 9 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 9 pairs) 2. **Domain Suitability**: Questions must maintain medical professionalism and accurately handle patient vital signs, diagnosis names, medication dosages, lab values, and prognosis. 3. **Strict Constraints**: - Answers must be derived ONLY from the table (and provided context). No external knowledge. @@ -15,6 +15,7 @@ generate_qa: | - **Privacy**: Assume patient names/IDs are pseudonymized synthetic data. - Questions and Answers MUST be written in Korean. - reasoning_annotation MUST be written in English and MUST be a single string (not a list). + - **DO NOT use real hospital/drug/institution names**. Use fictional names like "A병원", "약물-X", "환자ID-001". ### [Validation Criteria] - Is the answer uniquely determined within the table? @@ -22,7 +23,7 @@ generate_qa: | - Is the question clear and unambiguous? (e.g., "Patient in bad condition" -> "Patient with systolic BP under 90mmHg") ### [Reasoning Type Definitions (Medical Domain)] - (1) lookup: Retrieve specific patient lab results or medication info without condition/calculation. (e.g., "What is the blood glucose level of Patient ID-101?") + (1) lookup: Retrieve specific patient lab results or medication info without condition/calculation. (e.g., "What is the blood glucose level of Patient ID-001?") (2) filter: Select rows/columns meeting specific conditions (abnormal range, specific disease). (e.g., "List all patients with temperature above 38°C.") (3) aggregate: Statistical aggregation of patient group data (Mean LOS, Prevalence). (e.g., "What is the average age of patients in Ward A?") (4) compare: Compare efficacy between treatment groups or patient status pre/post. (e.g., "Is cholesterol level lower post-medication than pre-medication?") @@ -31,7 +32,6 @@ generate_qa: | (7) multi_hop: Multi-step inference finding a value first, then using it as a key. (e.g., "Who is the attending physician of the patient prescribed the highest dosage?") (8) implicit_reference: Referring to specific metrics contextually without explicit column name. (e.g., "Which group is at risk of hypertension?" -> implies Systolic/Diastolic BP columns) (9) ellipsis: Recovering omitted comparisons or criteria from table structure. (e.g., "What is the 2nd test result?" -> implies column next to 1st test) - (10) long_sequence (Context-Dependent): Requires interpreting 'Clinical Protocol' or 'Exclusion Criteria' text (Context) to filter table data. **Requirement**: Must generate a hypothetical [Context] paragraph needed to solve the question. ### [Output Format (JSON)] {{ @@ -43,7 +43,7 @@ generate_qa: | "reasoning_annotation": "Step-by-step logic to derive answer (MUST be a string, not a list)", "context": null }}, - ... (One per Reasoning Type => Total 10) + ... (One per Reasoning Type => Total 9) ] }} @@ -52,7 +52,7 @@ generate_qa_from_image: | Your mission is to analyze the provided medical table image and generate Question-Answer (QA) pairs that fit the specified Reasoning Type definitions. ### [Instructions] - 1. **Candidate Generation & Filtering**: Internally generate 10 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 10 pairs) + 1. **Candidate Generation & Filtering**: Internally generate 9 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 9 pairs) 2. **Domain Suitability**: Questions must maintain medical professionalism and accurately handle patient vital signs, diagnosis names, medication dosages, lab values, and prognosis. 3. **Strict Constraints**: - Answers must be derived ONLY from the table (and provided context). No external knowledge. @@ -60,6 +60,7 @@ generate_qa_from_image: | - Output format must strictly follow JSON. - Questions and Answers MUST be written in Korean. - reasoning_annotation MUST be written in English. + - **DO NOT use real hospital/drug/institution names**. Use fictional names like "A병원", "약물-X", "환자ID-001". ### [Validation Criteria] - Is the answer uniquely determined within the table? @@ -67,7 +68,7 @@ generate_qa_from_image: | - Is the question clear and unambiguous? (e.g., "Patient in bad condition" -> "Patient with systolic BP under 90mmHg") ### [Reasoning Type Definitions (Medical Domain)] - (1) lookup: Retrieve specific patient lab results or medication info without condition/calculation. (e.g., "What is the blood glucose level of Patient ID-101?") + (1) lookup: Retrieve specific patient lab results or medication info without condition/calculation. (e.g., "What is the blood glucose level of Patient ID-001?") (2) filter: Select rows/columns meeting specific conditions (abnormal range, specific disease). (e.g., "List all patients with temperature above 38°C.") (3) aggregate: Statistical aggregation of patient group data (Mean LOS, Prevalence). (e.g., "What is the average age of patients in Ward A?") (4) compare: Compare efficacy between treatment groups or patient status pre/post. (e.g., "Is cholesterol level lower post-medication than pre-medication?") @@ -76,7 +77,6 @@ generate_qa_from_image: | (7) multi_hop: Multi-step inference finding a value first, then using it as a key. (e.g., "Who is the attending physician of the patient prescribed the highest dosage?") (8) implicit_reference: Referring to specific metrics contextually without explicit column name. (e.g., "Which group is at risk of hypertension?" -> implies Systolic/Diastolic BP columns) (9) ellipsis: Recovering omitted comparisons or criteria from table structure. (e.g., "What is the 2nd test result?" -> implies column next to 1st test) - (10) long_sequence (Context-Dependent): Requires interpreting 'Clinical Protocol' or 'Exclusion Criteria' text (Context) to filter table data. **Requirement**: Must generate a hypothetical [Context] paragraph needed to solve the question. ### [Output Format (JSON)] {{ @@ -88,7 +88,59 @@ generate_qa_from_image: | "reasoning_annotation": "Step-by-step logic to derive answer", "context": null }}, - ... (One per Reasoning Type => Total 10) + ... (One per Reasoning Type => Total 9) + ] + }} + Return ONLY the JSON object. + +generate_qa_from_multi_image: | + You are an 'AI Data Researcher' specialized in building high-quality QA datasets that require understanding MULTIPLE medical/clinical table images together. + Your mission is to analyze ALL provided medical table images and generate Question-Answer (QA) pairs that REQUIRE information from MULTIPLE images to answer. + + **⚠️ CRITICAL REQUIREMENT: CROSS-IMAGE REASONING ⚠️** + - Every QA pair MUST require information from AT LEAST TWO images to answer correctly. + - Questions answerable from a single image are INVALID. + - Focus on comparisons, aggregations, or inferences that span multiple clinical records, lab results, or patient cohorts. + + ### [Instructions] + 1. **Analyze All Images**: First, understand what data each image contains and how they relate (e.g., different time points, different patient groups, lab results vs vital signs). + 2. **Generate Cross-Image QA**: Create 9 diverse QA pairs where each question requires synthesizing information from multiple images. + 3. **Strict Constraints**: + - Answers must be derived from combining data across images. No external knowledge. + - Each QA pair must correspond to exactly one Reasoning Type. + - Output format must strictly follow JSON. + - Questions and Answers MUST be written in Korean. + - reasoning_annotation MUST be written in English, MUST be a single string, and MUST specify which images were used. + - **DO NOT use real hospital/drug names**. Use fictional names like "A병원", "약물-X", "환자ID-001". + + ### [Validation Criteria] + - Does the answer REQUIRE data from multiple images? (Single-image answers are INVALID) + - Is the reasoning process logically flawless? + - Is the question clear about what clinical data is being compared or combined? + + ### [Cross-Image Reasoning Type Definitions (Medical Domain)] + (1) cross_lookup: Retrieve and combine patient data from different clinical records. (e.g., "What is the patient's blood glucose level before and after treatment from both tables?") + (2) cross_filter: Filter patients across clinical datasets based on conditions. (e.g., "Which patients appear in both the treatment and follow-up tables with improved vitals?") + (3) cross_aggregate: Aggregate clinical data spanning multiple patient cohorts. (e.g., "What is the average age of patients across both study groups?") + (4) cross_compare: Compare clinical outcomes between different time points or treatment groups. (e.g., "Did the treatment group in Table 2 show better outcomes than the control in Table 1?") + (5) cross_arithmetic: Calculate clinical metrics using data from multiple records. (e.g., "What is the change in BMI from the baseline table to the 6-month follow-up table?") + (6) cross_temporal: Identify patient progression by combining multiple visit records. (e.g., "Based on both admission and discharge tables, which patients showed improvement?") + (7) cross_multi_hop: Multi-step clinical inference across records. (e.g., "Find the patient with highest creatinine in Image 1, then find their blood pressure in Image 2.") + (8) cross_implicit: Answer questions requiring implicit understanding of clinical relationships. (e.g., "Which patients are at higher risk?" requires combining data from multiple clinical assessments) + (9) cross_synthesis: Synthesize clinical insights only possible by viewing all records together. (e.g., "Based on both lab results and medication tables, which patients may need dose adjustment?") + + ### [Output Format (JSON)] + {{ + "qa_pairs": [ + {{ + "question": "Question requiring multiple clinical images to answer", + "answer": "Answer derived from multiple images", + "type": "cross_lookup", + "reasoning_annotation": "Step 1: From Image 1, extract X. Step 2: From Image 2, extract Y. Step 3: Combine to get answer.", + "context": null, + "images_used": ["image_1", "image_2"] + }}, + ... (One per Reasoning Type => Total 9) ] }} Return ONLY the JSON object. @@ -112,9 +164,10 @@ generate_synthetic_table: | 3. **⚠️ Data Transformation - ABSOLUTELY MANDATORY ⚠️:** - **ALL data cell values MUST be replaced with completely new synthetic values.** - **NEVER copy any original data values** - generate fresh, realistic alternatives. + - **NEVER use real hospital/drug/institution names**. Use fictional names like "A병원", "약물-X", "제약사-가". - For patient names/IDs: Generate DIFFERENT pseudonymized identifiers - For lab values: Generate DIFFERENT realistic values - - For diagnoses/medications: Generate DIFFERENT names and codes + - For diagnoses/medications: Generate DIFFERENT fictional names and codes - For dates: Generate DIFFERENT plausible dates 4. **Styling:** Use **Tailwind CSS** classes (NO inline styles). **Observe and mimic the original image's visual style:** - Look at the original image's color scheme and design @@ -151,9 +204,10 @@ generate_synthetic_table_from_image: | 3. **⚠️ Data Generation - ABSOLUTELY CRITICAL ⚠️:** - **NEVER copy the data values from the image** - this is NOT an OCR task - **ALL cell content must be completely NEW and DIFFERENT** + - **NEVER use real hospital/drug/institution names**. Use fictional names like "A병원", "약물-X", "제약사-가". - For patient names/IDs: Generate DIFFERENT pseudonymized identifiers - For lab values: Generate DIFFERENT realistic values - - For diagnoses/medications: Generate DIFFERENT names + - For diagnoses/medications: Generate DIFFERENT fictional names 4. **Styling:** Use **Tailwind CSS** classes exclusively (NO inline styles). - ``: `class="w-full border-collapse text-sm"` - ``: `class="bg-gradient-to-r from-teal-700 to-teal-800 text-white"` @@ -170,3 +224,40 @@ generate_synthetic_table_from_image: | - Value in image: "수치A" → Generate: "수치B" ⚠️ If the generated content is identical or very similar to the image, the output is INVALID. + +generate_long_sequence: | + You are an 'AI Data Researcher' specialized in creating context-dependent QA pairs for medical/clinical tables. + Your mission is to generate a single high-quality "long_sequence" type QA pair that requires interpreting external context to answer questions about the table. + + **Input Table:** + {synthetic_html} + + ### [Instructions] + 1. **Generate ONE long_sequence QA pair** that requires reading and understanding a context paragraph to filter or interpret the table data. + 2. **Create a realistic clinical context** (e.g., "Clinical Protocol", "Exclusion Criteria", "Treatment Guidelines") that provides information needed to answer the question. + 3. **The question must be unanswerable without the context** - the context should contain key criteria or conditions. + 4. **Strict Constraints**: + - Answer must be derived from BOTH the table AND the context. Neither alone is sufficient. + - Questions and Answers MUST be written in Korean. + - reasoning_annotation MUST be written in English and MUST be a single string. + - Context must be written in Korean and be 2-4 sentences long. + - **DO NOT use real hospital/drug names**. Use fictional names like "A병원", "약물-X". + + ### [Example Scenarios (Medical)] + - Context describes patient exclusion criteria (age, comorbidities) → Question asks which patients are eligible + - Context outlines dosage adjustment rules → Question asks which patients need dose modification + - Context specifies lab value thresholds for intervention → Question asks which patients require treatment + + ### [Output Format (JSON)] + {{ + "qa_pairs": [ + {{ + "question": "Question requiring context to answer", + "answer": "Answer derived from table + context", + "type": "long_sequence", + "reasoning_annotation": "Step 1: Extract key criteria from context. Step 2: Apply criteria to table. Step 3: Derive answer.", + "context": "임상 프로토콜에 따르면... (2-4 sentences of clinical context in Korean)" + }} + ] + }} + Return ONLY the JSON object. diff --git a/generate_synthetic_table/prompts/public.yaml b/generate_synthetic_table/prompts/public.yaml index b0c4099..4fc21e2 100644 --- a/generate_synthetic_table/prompts/public.yaml +++ b/generate_synthetic_table/prompts/public.yaml @@ -6,7 +6,7 @@ generate_qa: | {synthetic_html} ### [Instructions] - 1. **Candidate Generation & Filtering**: Internally generate 10 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 10 pairs) + 1. **Candidate Generation & Filtering**: Internally generate 9 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 9 pairs) 2. **Domain Suitability**: Questions must maintain a public/objective tone and accurately handle demographics, budgets, administrative region names, policy beneficiaries, and annual indicators. 3. **Strict Constraints**: - Answers must be derived ONLY from the table (and provided context). No external knowledge. @@ -14,6 +14,7 @@ generate_qa: | - Output format must strictly follow JSON. - Questions and Answers MUST be written in Korean. - reasoning_annotation MUST be written in English and MUST be a single string (not a list). + - **DO NOT use real place names** (e.g., Seoul, Busan, Gyeonggi). Use fictional names like "A시", "나구", "다군". ### [Validation Criteria] - Is the answer uniquely determined within the table? @@ -21,16 +22,15 @@ generate_qa: | - Is the question clear and unambiguous? (e.g., "Place with most people" -> "District with highest Population") ### [Reasoning Type Definitions (Public Domain)] - (1) lookup: Retrieve specific regional or annual statistics without condition/calculation. (e.g., "What is the total population of Seoul in 2023?") + (1) lookup: Retrieve specific regional or annual statistics without condition/calculation. (e.g., "What is the total population of Region A in 2023?") (2) filter: Select rows/columns meeting specific conditions (above/below value, specific region). (e.g., "List all departments with budget execution rate over 90%.") - (3) aggregate: Statistical aggregation of regional/annual data (Sum, Avg). (e.g., "What is the average number of births in the 17 provinces?") - (4) compare: Compare regional gaps or annual trends. (e.g., "Which has higher incoming migration, Gyeonggi or Seoul?") + (3) aggregate: Statistical aggregation of regional/annual data (Sum, Avg). (e.g., "What is the average number of births in all provinces?") + (4) compare: Compare regional gaps or annual trends. (e.g., "Which has higher incoming migration, Region A or Region B?") (5) arithmetic: Specific calculation beyond simple comparison (population density, YoY growth). (e.g., "What is the population density of District A?") (6) temporal: Deduce policy effects or long-term statistical trends. (e.g., "Which region shows a decreasing crime rate trend over the last 5 years?") (7) multi_hop: Multi-step inference finding a value first, then using it as a key. (e.g., "Who is the Mayor of the city with the highest financial independence?") (8) implicit_reference: Referring to specific metrics contextually without explicit column name. (e.g., "Which is the most aged city?" -> implies 65+ Population Ratio column) (9) ellipsis: Recovering omitted comparisons or criteria from table structure. (e.g., "What is next year's target?" -> implies column next to this year's value) - (10) long_sequence (Context-Dependent): Requires interpreting 'Policy Guidelines' or 'Legal Requirements' text (Context) to filter table data. **Requirement**: Must generate a hypothetical [Context] paragraph needed to solve the question. ### [Output Format (JSON)] {{ @@ -42,7 +42,7 @@ generate_qa: | "reasoning_annotation": "Step-by-step logic to derive answer (MUST be a string, not a list)", "context": null }}, - ... (One per Reasoning Type => Total 10) + ... (One per Reasoning Type => Total 9) ] }} @@ -51,7 +51,7 @@ generate_qa_from_image: | Your mission is to analyze the provided public/administrative table image and generate Question-Answer (QA) pairs that fit the specified Reasoning Type definitions. ### [Instructions] - 1. **Candidate Generation & Filtering**: Internally generate 10 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 10 pairs) + 1. **Candidate Generation & Filtering**: Internally generate 9 diverse QA pairs first. Then, for each Reasoning Type, select and output only the single most perfect QA pair that passes the [Validation Criteria]. (Total 9 pairs) 2. **Domain Suitability**: Questions must maintain a public/objective tone and accurately handle demographics, budgets, administrative region names, policy beneficiaries, and annual indicators. 3. **Strict Constraints**: - Answers must be derived ONLY from the table (and provided context). No external knowledge. @@ -59,6 +59,7 @@ generate_qa_from_image: | - Output format must strictly follow JSON. - Questions and Answers MUST be written in Korean. - reasoning_annotation MUST be written in English. + - **DO NOT use real place names** (e.g., Seoul, Busan, Gyeonggi). Use fictional names like "A시", "나구", "다군". ### [Validation Criteria] - Is the answer uniquely determined within the table? @@ -66,16 +67,15 @@ generate_qa_from_image: | - Is the question clear and unambiguous? (e.g., "Place with most people" -> "District with highest Population") ### [Reasoning Type Definitions (Public Domain)] - (1) lookup: Retrieve specific regional or annual statistics without condition/calculation. (e.g., "What is the total population of Seoul in 2023?") + (1) lookup: Retrieve specific regional or annual statistics without condition/calculation. (e.g., "What is the total population of Region A in 2023?") (2) filter: Select rows/columns meeting specific conditions (above/below value, specific region). (e.g., "List all departments with budget execution rate over 90%.") - (3) aggregate: Statistical aggregation of regional/annual data (Sum, Avg). (e.g., "What is the average number of births in the 17 provinces?") - (4) compare: Compare regional gaps or annual trends. (e.g., "Which has higher incoming migration, Gyeonggi or Seoul?") + (3) aggregate: Statistical aggregation of regional/annual data (Sum, Avg). (e.g., "What is the average number of births in all provinces?") + (4) compare: Compare regional gaps or annual trends. (e.g., "Which has higher incoming migration, Region A or Region B?") (5) arithmetic: Specific calculation beyond simple comparison (population density, YoY growth). (e.g., "What is the population density of District A?") (6) temporal: Deduce policy effects or long-term statistical trends. (e.g., "Which region shows a decreasing crime rate trend over the last 5 years?") (7) multi_hop: Multi-step inference finding a value first, then using it as a key. (e.g., "Who is the Mayor of the city with the highest financial independence?") (8) implicit_reference: Referring to specific metrics contextually without explicit column name. (e.g., "Which is the most aged city?" -> implies 65+ Population Ratio column) (9) ellipsis: Recovering omitted comparisons or criteria from table structure. (e.g., "What is next year's target?" -> implies column next to this year's value) - (10) long_sequence (Context-Dependent): Requires interpreting 'Policy Guidelines' or 'Legal Requirements' text (Context) to filter table data. **Requirement**: Must generate a hypothetical [Context] paragraph needed to solve the question. ### [Output Format (JSON)] {{ @@ -87,7 +87,59 @@ generate_qa_from_image: | "reasoning_annotation": "Step-by-step logic to derive answer", "context": null }}, - ... (One per Reasoning Type => Total 10) + ... (One per Reasoning Type => Total 9) + ] + }} + Return ONLY the JSON object. + +generate_qa_from_multi_image: | + You are an 'AI Data Researcher' specialized in building high-quality QA datasets that require understanding MULTIPLE public/government table images together. + Your mission is to analyze ALL provided public data table images and generate Question-Answer (QA) pairs that REQUIRE information from MULTIPLE images to answer. + + **⚠️ CRITICAL REQUIREMENT: CROSS-IMAGE REASONING ⚠️** + - Every QA pair MUST require information from AT LEAST TWO images to answer correctly. + - Questions answerable from a single image are INVALID. + - Focus on comparisons, aggregations, or inferences that span multiple regional statistics, budget tables, or policy data. + + ### [Instructions] + 1. **Analyze All Images**: First, understand what data each image contains and how they relate (e.g., different fiscal years, different regions, budget vs expenditure). + 2. **Generate Cross-Image QA**: Create 9 diverse QA pairs where each question requires synthesizing information from multiple images. + 3. **Strict Constraints**: + - Answers must be derived from combining data across images. No external knowledge. + - Each QA pair must correspond to exactly one Reasoning Type. + - Output format must strictly follow JSON. + - Questions and Answers MUST be written in Korean. + - reasoning_annotation MUST be written in English, MUST be a single string, and MUST specify which images were used. + - **DO NOT use real place names** (e.g., Seoul, Busan, Gyeonggi). Use fictional names like "A시", "나구", "다군". + + ### [Validation Criteria] + - Does the answer REQUIRE data from multiple images? (Single-image answers are INVALID) + - Is the reasoning process logically flawless? + - Is the question clear about what public data is being compared or combined? + + ### [Cross-Image Reasoning Type Definitions (Public Domain)] + (1) cross_lookup: Retrieve and combine regional statistics from different data tables. (e.g., "What is the total population of A시 combining both census tables?") + (2) cross_filter: Filter regions across datasets based on conditions. (e.g., "Which districts appear in both tables with budget execution rate >80%?") + (3) cross_aggregate: Aggregate public data spanning multiple regions or years. (e.g., "What is the total government expenditure across all departments in both fiscal year reports?") + (4) cross_compare: Compare regional performance between different periods or metrics. (e.g., "Did the unemployment rate improve from the 2022 table to the 2023 table for Region A?") + (5) cross_arithmetic: Calculate public metrics using data from multiple sources. (e.g., "What is the year-over-year population growth rate using data from both census tables?") + (6) cross_temporal: Identify policy trends by combining multiple year data. (e.g., "Based on both annual reports, which region shows consistent improvement in education metrics?") + (7) cross_multi_hop: Multi-step inference across public data tables. (e.g., "Find the region with highest tax revenue in Image 1, then find its population density in Image 2.") + (8) cross_implicit: Answer questions requiring implicit understanding of relationships between datasets. (e.g., "Which region is most fiscally efficient?" requires combining budget and outcome data from multiple sources) + (9) cross_synthesis: Synthesize policy insights only possible by viewing all tables together. (e.g., "Based on both the budget allocation and service satisfaction tables, which programs are underperforming?") + + ### [Output Format (JSON)] + {{ + "qa_pairs": [ + {{ + "question": "Question requiring multiple public data images to answer", + "answer": "Answer derived from multiple images", + "type": "cross_lookup", + "reasoning_annotation": "Step 1: From Image 1, extract X. Step 2: From Image 2, extract Y. Step 3: Combine to get answer.", + "context": null, + "images_used": ["image_1", "image_2"] + }}, + ... (One per Reasoning Type => Total 9) ] }} Return ONLY the JSON object. @@ -111,7 +163,8 @@ generate_synthetic_table: | 3. **⚠️ Data Transformation - ABSOLUTELY MANDATORY ⚠️:** - **ALL data cell values MUST be replaced with completely new synthetic values.** - **NEVER copy any original data values** - generate fresh, realistic alternatives. - - For regions: Generate DIFFERENT administrative region names + - **NEVER use real place names** (Seoul, Busan, Gyeonggi, etc.). Use fictional names like "A시", "나구", "다군", "라도". + - For regions: Generate DIFFERENT fictional administrative region names - For departments: Generate DIFFERENT department names - For statistics: Generate DIFFERENT numbers (similar magnitude) - For dates: Generate DIFFERENT plausible dates @@ -150,7 +203,8 @@ generate_synthetic_table_from_image: | 3. **⚠️ Data Generation - ABSOLUTELY CRITICAL ⚠️:** - **NEVER copy the data values from the image** - this is NOT an OCR task - **ALL cell content must be completely NEW and DIFFERENT** - - For regions: Generate DIFFERENT administrative region names + - **NEVER use real place names** (Seoul, Busan, Gyeonggi, etc.). Use fictional names like "A시", "나구", "다군", "라도". + - For regions: Generate DIFFERENT fictional administrative region names - For statistics: Generate DIFFERENT numbers - For departments: Generate DIFFERENT names 4. **Styling:** Use **Tailwind CSS** classes exclusively (NO inline styles). @@ -169,3 +223,40 @@ generate_synthetic_table_from_image: | - Statistic in image: "수치A" → Generate: "수치B" ⚠️ If the generated content is identical or very similar to the image, the output is INVALID. + +generate_long_sequence: | + You are an 'AI Data Researcher' specialized in creating context-dependent QA pairs for public/government data tables. + Your mission is to generate a single high-quality "long_sequence" type QA pair that requires interpreting external context to answer questions about the table. + + **Input Table:** + {synthetic_html} + + ### [Instructions] + 1. **Generate ONE long_sequence QA pair** that requires reading and understanding a context paragraph to filter or interpret the table data. + 2. **Create a realistic public policy context** (e.g., "Policy Guidelines", "Legal Requirements", "Budget Allocation Rules") that provides information needed to answer the question. + 3. **The question must be unanswerable without the context** - the context should contain key criteria or conditions. + 4. **Strict Constraints**: + - Answer must be derived from BOTH the table AND the context. Neither alone is sufficient. + - Questions and Answers MUST be written in Korean. + - reasoning_annotation MUST be written in English and MUST be a single string. + - Context must be written in Korean and be 2-4 sentences long. + - **DO NOT use real place names** (e.g., Seoul, Busan). Use fictional names. + + ### [Example Scenarios (Public)] + - Context describes eligibility criteria for a subsidy → Question asks which regions qualify + - Context outlines budget allocation rules → Question asks which departments receive funding + - Context specifies demographic thresholds → Question asks which areas need intervention + + ### [Output Format (JSON)] + {{ + "qa_pairs": [ + {{ + "question": "Question requiring context to answer", + "answer": "Answer derived from table + context", + "type": "long_sequence", + "reasoning_annotation": "Step 1: Extract key criteria from context. Step 2: Apply criteria to table. Step 3: Derive answer.", + "context": "정책 지침에 따르면... (2-4 sentences of policy context in Korean)" + }} + ] + }} + Return ONLY the JSON object. diff --git a/multi_image_json_list/test_academic_input.json b/multi_image_json_list/test_academic_input.json new file mode 100755 index 0000000..478bf45 --- /dev/null +++ b/multi_image_json_list/test_academic_input.json @@ -0,0 +1,192 @@ +[ + { + "index": 0, + "pair_id": "A_origin_0_0", + "image_paths": [ + "data/Academic/Table/A_origin_0/A_table_0.png", + "data/Academic/Table/A_origin_0/A_table_1.png" + ], + "domain": "academic" + }, + { + "index": 1, + "pair_id": "A_origin_0_1", + "image_paths": [ + "data/Academic/Table/A_origin_0/A_table_2.png", + "data/Academic/Table/A_origin_0/A_table_3.png", + "data/Academic/Table/A_origin_0/A_table_4.png" + ], + "domain": "academic" + }, + { + "index": 2, + "pair_id": "A_origin_1_0", + "image_paths": [ + "data/Academic/Table/A_origin_1/A_table_5.png", + "data/Academic/Table/A_origin_1/A_table_6.png" + ], + "domain": "academic" + }, + { + "index": 3, + "pair_id": "A_origin_2_0", + "image_paths": [ + "data/Academic/Table/A_origin_2/A_table_8.png", + "data/Academic/Table/A_origin_2/A_table_9.png" + ], + "domain": "academic" + }, + { + "index": 4, + "pair_id": "A_origin_3_0", + "image_paths": [ + "data/Academic/Table/A_origin_3/A_table_10.png", + "data/Academic/Table/A_origin_3/A_table_11.png" + ], + "domain": "academic" + }, + { + "index": 5, + "pair_id": "A_origin_4_0", + "image_paths": [ + "data/Academic/Table/A_origin_4/A_table_12.png", + "data/Academic/Table/A_origin_4/A_table_13.png" + ], + "domain": "academic" + }, + { + "index": 6, + "pair_id": "A_origin_5_0", + "image_paths": [ + "data/Academic/Table/A_origin_5/A_table_14.png", + "data/Academic/Table/A_origin_5/A_table_15.png", + "data/Academic/Table/A_origin_5/A_table_16.png" + ], + "domain": "academic" + }, + { + "index": 7, + "pair_id": "A_origin_6_0", + "image_paths": [ + "data/Academic/Table/A_origin_6/A_table_17.png", + "data/Academic/Table/A_origin_6/A_table_18.png", + "data/Academic/Table/A_origin_6/A_table_19.png" + ], + "domain": "academic" + }, + { + "index": 8, + "pair_id": "A_origin_7_0", + "image_paths": [ + "data/Academic/Table/A_origin_7/A_table_20.png", + "data/Academic/Table/A_origin_7/A_table_21.png" + ], + "domain": "academic" + }, + { + "index": 9, + "pair_id": "A_origin_8_0", + "image_paths": [ + "data/Academic/Table/A_origin_8/A_table_22.png", + "data/Academic/Table/A_origin_8/A_table_23.png", + "data/Academic/Table/A_origin_8/A_table_24.png" + ], + "domain": "academic" + }, + { + "index": 10, + "pair_id": "A_origin_9_0", + "image_paths": [ + "data/Academic/Table/A_origin_9/A_table_25.png", + "data/Academic/Table/A_origin_9/A_table_26.png", + "data/Academic/Table/A_origin_9/A_table_27.png" + ], + "domain": "academic" + }, + { + "index": 11, + "pair_id": "A_origin_10_0", + "image_paths": [ + "data/Academic/Table/A_origin_10/A_table_28.png", + "data/Academic/Table/A_origin_10/A_table_29.png" + ], + "domain": "academic" + }, + { + "index": 12, + "pair_id": "A_origin_11_0", + "image_paths": [ + "data/Academic/Table/A_origin_11/A_table_30.png", + "data/Academic/Table/A_origin_11/A_table_31.png" + ], + "domain": "academic" + }, + { + "index": 13, + "pair_id": "A_origin_12_0", + "image_paths": [ + "data/Academic/Table/A_origin_12/A_table_32.png", + "data/Academic/Table/A_origin_12/A_table_33.png", + "data/Academic/Table/A_origin_12/A_table_34.png" + ], + "domain": "academic" + }, + { + "index": 14, + "pair_id": "A_origin_14_0", + "image_paths": [ + "data/Academic/Table/A_origin_14/A_table_39.png", + "data/Academic/Table/A_origin_14/A_table_40.png" + ], + "domain": "academic" + }, + { + "index": 15, + "pair_id": "A_origin_18_0", + "image_paths": [ + "data/Academic/Table/A_origin_18/A_table_47.png", + "data/Academic/Table/A_origin_18/A_table_48.png", + "data/Academic/Table/A_origin_18/A_table_49.png" + ], + "domain": "academic" + }, + { + "index": 16, + "pair_id": "A_origin_26_0", + "image_paths": [ + "data/Academic/Table/A_origin_26/A_table_59_01.png", + "data/Academic/Table/A_origin_26/A_table_59_02.png", + "data/Academic/Table/A_origin_26/A_table_60.png", + "data/Academic/Table/A_origin_26/A_table_61.png" + ], + "domain": "academic" + }, + { + "index": 17, + "pair_id": "A_origin_28_0", + "image_paths": [ + "data/Academic/Table/A_origin_28/A_table_63_01.png", + "data/Academic/Table/A_origin_28/A_table_63_02.png", + "data/Academic/Table/A_origin_28/A_table_64.png" + ], + "domain": "academic" + }, + { + "index": 18, + "pair_id": "A_origin_36_0", + "image_paths": [ + "data/Academic/Table/A_origin_36/A_table_73.png", + "data/Academic/Table/A_origin_36/A_table_75.png" + ], + "domain": "academic" + }, + { + "index": 19, + "pair_id": "A_origin_43_0", + "image_paths": [ + "data/Academic/Table/A_origin_43/A_table_89.png", + "data/Academic/Table/A_origin_43/A_table_90.png" + ], + "domain": "academic" + } +] \ No newline at end of file diff --git a/multi_image_json_list/test_business_input.json b/multi_image_json_list/test_business_input.json new file mode 100755 index 0000000..ace8a1f --- /dev/null +++ b/multi_image_json_list/test_business_input.json @@ -0,0 +1,173 @@ +[ + { + "index": 0, + "pair_id": "B_origin_0_0_0", + "image_paths": [ + "data/Business/Table/B_origin_0/B_table_0_0.png", + "data/Business/Table/B_origin_0/B_table_1_0.png" + ], + "domain": "Business" + }, + { + "index": 1, + "pair_id": "B_origin_3_3_0", + "image_paths": [ + "data/Business/Table/B_origin_3/B_table_10_0.png", + "data/Business/Table/B_origin_3/B_table_11_0.png" + ], + "domain": "Business" + }, + { + "index": 2, + "pair_id": "B_origin_4_4_0", + "image_paths": [ + "data/Business/Table/B_origin_4/B_table_16_0.png", + "data/Business/Table/B_origin_4/B_table_16_1.png" + ], + "domain": "Business" + }, + { + "index": 3, + "pair_id": "B_origin_4_4_1", + "image_paths": [ + "data/Business/Table/B_origin_4/B_table_14_0.png", + "data/Business/Table/B_origin_4/B_table_15_0.png" + ], + "domain": "Business" + }, + { + "index": 4, + "pair_id": "B_origin_6_6_0", + "image_paths": [ + "data/Business/Table/B_origin_6/B_table_20_0.png", + "data/Business/Table/B_origin_6/B_table_21_0.png" + ], + "domain": "Business" + }, + { + "index": 5, + "pair_id": "B_origin_2_10_0", + "image_paths": [ + "data/Business/Table/B_origin_2/B_table_6_0.png", + "data/Business/Table/B_origin_6/B_table_23_0.png" + ], + "domain": "Business" + }, + { + "index": 7, + "pair_id": "B_origin_14_14_0", + "image_paths": [ + "data/Business/Table/B_origin_14/B_table_45_0.png", + "data/Business/Table/B_origin_14/B_table_45_1.png" + ], + "domain": "Business" + }, + { + "index": 8, + "pair_id": "B_origin_15_15_0", + "image_paths": [ + "data/Business/Table/B_origin_15/B_table_51_0.png", + "data/Business/Table/B_origin_15/B_table_51_1.png" + ], + "domain": "Business" + }, + { + "index": 9, + "pair_id": "B_origin_10_15_0", + "image_paths": [ + "data/Business/Table/B_origin_10/B_table_33_0.png", + "data/Business/Table/B_origin_15/B_table_52_0.png" + ], + "domain": "Business" + }, + { + "index": 10, + "pair_id": "B_origin_18_18_0", + "image_paths": [ + "data/Business/Table/B_origin_18/B_table_63_0.png", + "data/Business/Table/B_origin_18/B_table_63_1.png" + ], + "domain": "Business" + }, + { + "index": 11, + "pair_id": "B_origin_18_18_1", + "image_paths": [ + "data/Business/Table/B_origin_18/B_table_61_0.png", + "data/Business/Table/B_origin_18/B_table_64_0.png" + ], + "domain": "Business" + }, + { + "index": 12, + "pair_id": "B_origin_20_20_0", + "image_paths": [ + "data/Business/Table/B_origin_20/B_table_68_0.png", + "data/Business/Table/B_origin_20/B_table_69_0.png" + ], + "domain": "Business" + }, + { + "index": 13, + "pair_id": "B_origin_21_21_0", + "image_paths": [ + "data/Business/Table/B_origin_21/B_table_70_0.png", + "data/Business/Table/B_origin_21/B_table_71_0.png" + ], + "domain": "Business" + }, + { + "index": 14, + "pair_id": "B_origin_21_21_1", + "image_paths": [ + "data/Business/Table/B_origin_21/B_table_72_0.png", + "data/Business/Table/B_origin_21/B_table_72_1.png" + ], + "domain": "Business" + }, + { + "index": 15, + "pair_id": "B_origin_23_23_0", + "image_paths": [ + "data/Business/Table/B_origin_23/B_table_80_0.png", + "data/Business/Table/B_origin_23/B_table_81_0.png" + ], + "domain": "Business" + }, + { + "index": 16, + "pair_id": "B_origin_23_23_0", + "image_paths": [ + "data/Business/Table/B_origin_23/B_table_80_0.png", + "data/Business/Table/B_origin_23/B_table_81_0.png" + ], + "domain": "Business" + }, + { + "index": 17, + "pair_id": "B_origin_24_24_0", + "image_paths": [ + "data/Business/Table/B_origin_24/B_table_83_0.png", + "data/Business/Table/B_origin_24/B_table_84_0.png" + ], + "domain": "Business" + }, + { + "index": 18, + "pair_id": "B_origin_32_32_0", + "image_paths": [ + "data/Business/Table/B_origin_32/B_table_110_0.png", + "data/Business/Table/B_origin_32/B_table_112_0.png" + ], + "domain": "Business" + }, + { + "index": 19, + "pair_id": "B_origin_37_37_0", + "image_paths": [ + "data/Business/Table/B_origin_37/B_table_132_0.png", + "data/Business/Table/B_origin_37/B_table_132_1.png" + ], + "domain": "Business" + } +] \ No newline at end of file diff --git a/multi_image_json_list/test_finance_input.json b/multi_image_json_list/test_finance_input.json new file mode 100755 index 0000000..98b7491 --- /dev/null +++ b/multi_image_json_list/test_finance_input.json @@ -0,0 +1,182 @@ +[ + { + "index": 0, + "pair_id": "F_table_0", + "image_paths": [ + "data/Finance/Table/F_origin_0/F_table_0_0.png", + "data/Finance/Table/F_origin_3/F_table_5_0.png" + ], + "domain": "finance" + }, + { + "index": 1, + "pair_id": "F_table_1", + "image_paths": [ + "data/Finance/Table/F_origin_4/F_table_6_0.png", + "data/Finance/Table/F_origin_6/F_table_8_0.png" + ], + "domain": "finance" + }, + { + "index": 2, + "pair_id": "F_table_2", + "image_paths": [ + "data/Finance/Table/F_origin_10/F_table_14_0.png", + "data/Finance/Table/F_origin_16/F_table_16_0.png" + ], + "domain": "finance" + }, + { + "index": 3, + "pair_id": "F_table_3", + "image_paths": [ + "data/Finance/Table/F_origin_21/F_table_71_0.png", + "data/Finance/Table/F_origin_23/F_table_74_0.png" + ], + "domain": "finance" + }, + { + "index": 4, + "pair_id": "F_table_4", + "image_paths": [ + "data/Finance/Table/F_origin_38/F_table_122_0.png", + "data/Finance/Table/F_origin_39/F_table_123_0.png" + ], + "domain": "finance" + }, + { + "index": 5, + "pair_id": "F_table_5", + "image_paths": [ + "data/Finance/Table/F_origin_41/F_table_129_0.png", + "data/Finance/Table/F_origin_42/F_table_130_0.png" + ], + "domain": "finance" + }, + { + "index": 6, + "pair_id": "F_table_6", + "image_paths": [ + "data/Finance/Table/F_origin_43/F_table_131_0.png", + "data/Finance/Table/F_origin_44/F_table_132_0.png" + ], + "domain": "finance" + }, + { + "index": 7, + "pair_id": "F_table_7", + "image_paths": [ + "data/Finance/Table/F_origin_45/F_table_136_0.png", + "data/Finance/Table/F_origin_49/F_table_146_0.png" + ], + "domain": "finance" + }, + { + "index": 8, + "pair_id": "F_table_8", + "image_paths": [ + "data/Finance/Table/F_origin_0/F_table_0_0.png", + "data/Finance/Table/F_origin_0/F_table_1_0.png" + ], + "domain": "finance" + }, + { + "index": 9, + "pair_id": "F_table_9", + "image_paths": [ + "data/Finance/Table/F_origin_2/F_table_3_1.png", + "data/Finance/Table/F_origin_2/F_table_4_0.png" + ], + "domain": "finance" + }, + { + "index": 10, + "pair_id": "F_table_10", + "image_paths": [ + "data/Finance/Table/F_origin_6/F_table_8_1.png", + "data/Finance/Table/F_origin_6/F_table_9_0.png" + ], + "domain": "finance" + }, + { + "index": 11, + "pair_id": "F_table_11", + "image_paths": [ + "data/Finance/Table/F_origin_16/F_table_39_0.png", + "data/Finance/Table/F_origin_16/F_table_45_0.png" + ], + "domain": "finance" + }, + { + "index": 12, + "pair_id": "F_table_12", + "image_paths": [ + "data/Finance/Table/F_origin_1/F_table_2_0.png", + "data/Finance/Table/F_origin_11/F_table_15_0.png" + ], + "domain": "finance" + }, + { + "index": 13, + "pair_id": "F_table_13", + "image_paths": [ + "data/Finance/Table/F_origin_3/F_table_5_0.png", + "data/Finance/Table/F_origin_13/F_table_21_0.png" + ], + "domain": "finance" + }, + { + "index": 14, + "pair_id": "F_table_14", + "image_paths": [ + "data/Finance/Table/F_origin_4/F_table_6_0.png", + "data/Finance/Table/F_origin_23/F_table_74_0.png" + ], + "domain": "finance" + }, + { + "index": 15, + "pair_id": "F_table_15", + "image_paths": [ + "data/Finance/Table/F_origin_10/F_table_14_0.png", + "data/Finance/Table/F_origin_21/F_table_71_0.png" + ], + "domain": "finance" + }, + { + "index": 16, + "pair_id": "F_table_16", + "image_paths": [ + "data/Finance/Table/F_origin_12/F_table_16_0.png", + "data/Finance/Table/F_origin_45/F_table_136_0.png" + ], + "domain": "finance" + }, + { + "index": 17, + "pair_id": "F_table_17", + "image_paths": [ + "data/Finance/Table/F_origin_17/F_table_49_0.png", + "data/Finance/Table/F_origin_48/F_table_143_0.png" + ], + "domain": "finance" + }, + { + "index": 18, + "pair_id": "F_table_18", + "image_paths": [ + "data/Finance/Table/F_origin_38/F_table_122_0.png", + "data/Finance/Table/F_origin_45/F_table_136_0.png" + ], + "domain": "finance" + }, + { + "index": 19, + "pair_id": "F_table_19", + "image_paths": [ + "data/Finance/Table/F_origin_43/F_table_131_0.png", + "data/Finance/Table/F_origin_49/F_table_146_0.png" + ], + "domain": "finance" + } +] \ No newline at end of file diff --git a/multi_image_json_list/test_medical_input.json b/multi_image_json_list/test_medical_input.json new file mode 100755 index 0000000..279ef70 --- /dev/null +++ b/multi_image_json_list/test_medical_input.json @@ -0,0 +1,164 @@ +[ + { + "index": 0, + "pair_id": "M_origin_0", + "image_paths": [ + "data/Medical/Table/M_table_0_0_0.png", + "data/Medical/Table/M_table_0_1_0.png" + ], + "domain": "medical" + }, + { + "index": 1, + "pair_id": "M_origin_2", + "image_paths": [ + "data/Medical/Table/M_table_2_0_0.png", + "data/Medical/Table/M_table_2_1_0.png" + ], + "domain": "medical" + }, + { + "index": 2, + "pair_id": "M_origin_3", + "image_paths": [ + "data/Medical/Table/M_table_3_0_1.png", + "data/Medical/Table/M_table_3_0_2.png" + ], + "domain": "medical" + }, + { + "index": 3, + "pair_id": "M_origin_4", + "image_paths": [ + "data/Medical/Table/M_table_4_0_0.png", + "data/Medical/Table/M_table_4_0_1.png" + ], + "domain": "medical" + }, + { + "index": 4, + "pair_id": "M_revised_2_2", + "image_paths": [ + "data/Medical/Table/M_table_2_2_0.png", + "data/Medical/Table/M_table_2_2_1.png" + ], + "domain": "medical" + }, + { + "index": 5, + "pair_id": "M_revised_3_0", + "image_paths": [ + "data/Medical/Table/M_table_3_0_0.png", + "data/Medical/Table/M_table_3_0_1.png" + ], + "domain": "medical" + }, + { + "index": 6, + "pair_id": "M_revised_4_0", + "image_paths": [ + "data/Medical/Table/M_table_4_0_0.png", + "data/Medical/Table/M_table_4_0_1.png" + ], + "domain": "medical" + }, + { + "index": 7, + "pair_id": "M_revised_6_3", + "image_paths": [ + "data/Medical/Table/M_table_6_3_0.png", + "data/Medical/Table/M_table_6_3_1.png" + ], + "domain": "medical" + }, + { + "index": 8, + "pair_id": "M_revised_8_0", + "image_paths": [ + "data/Medical/Table/M_table_8_0_0.png", + "data/Medical/Table/M_table_8_0_1.png" + ], + "domain": "medical" + }, + { + "index": 9, + "pair_id": "M_revised_9_0", + "image_paths": [ + "data/Medical/Table/M_table_9_0_0.png", + "data/Medical/Table/M_table_9_0_1.png" + ], + "domain": "medical" + }, + { + "index": 10, + "pair_id": "M_revised_10_0", + "image_paths": [ + "data/Medical/Table/M_table_10_0_0.png", + "data/Medical/Table/M_table_10_0_1.png" + ], + "domain": "medical" + }, + { + "index": 11, + "pair_id": "M_revised_11_0", + "image_paths": [ + "data/Medical/Table/M_table_11_0_0.png", + "data/Medical/Table/M_table_11_0_1.png" + ], + "domain": "medical" + }, + { + "index": 12, + "pair_id": "M_revised_13_0", + "image_paths": [ + "data/Medical/Table/M_table_13_0_0.png", + "data/Medical/Table/M_table_13_0_1.png" + ], + "domain": "medical" + }, + { + "index": 13, + "pair_id": "M_revised_14_0", + "image_paths": [ + "data/Medical/Table/M_table_14_0_0.png", + "data/Medical/Table/M_table_14_0_1.png" + ], + "domain": "medical" + }, + { + "index": 14, + "pair_id": "M_revised_15_0", + "image_paths": [ + "data/Medical/Table/M_table_15_0_0.png", + "data/Medical/Table/M_table_15_0_1.png" + ], + "domain": "medical" + }, + { + "index": 15, + "pair_id": "M_revised_16_0", + "image_paths": [ + "data/Medical/Table/M_table_16_0_0.png", + "data/Medical/Table/M_table_16_0_1.png" + ], + "domain": "medical" + }, + { + "index": 16, + "pair_id": "M_revised_2_3", + "image_paths": [ + "data/Medical/Table/M_table_2_3_0.png", + "data/Medical/Table/M_table_2_3_1.png" + ], + "domain": "medical" + }, + { + "index": 17, + "pair_id": "M_revised_10_1", + "image_paths": [ + "data/Medical/Table/M_table_10_1_0.png", + "data/Medical/Table/M_table_10_1_1.png" + ], + "domain": "medical" + } +] \ No newline at end of file diff --git a/multi_image_json_list/test_public_input.json b/multi_image_json_list/test_public_input.json new file mode 100755 index 0000000..832ef44 --- /dev/null +++ b/multi_image_json_list/test_public_input.json @@ -0,0 +1,182 @@ +[ + { + "index": 0, + "pair_id": "P_origin_0_1", + "image_paths": [ + "data/Public/Table/P_origin_0/P_origin_0_1_0.png", + "data/Public/Table/P_origin_0/P_origin_0_1_1.png" + ], + "domain": "public" + }, + { + "index": 1, + "pair_id": "P_origin_0_2", + "image_paths": [ + "data/Public/Table/P_origin_0/P_origin_0_2_1.png", + "data/Public/Table/P_origin_0/P_origin_0_2_2.png" + ], + "domain": "public" + }, + { + "index": 2, + "pair_id": "P_origin_1_9", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_9_0.png", + "data/Public/Table/P_origin_1/P_origin_1_9_1.png" + ], + "domain": "public" + }, + { + "index": 3, + "pair_id": "P_origin_1_10", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_10_0.png", + "data/Public/Table/P_origin_1/P_origin_1_10_1.png" + ], + "domain": "public" + }, + { + "index": 4, + "pair_id": "P_origin_1_12", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_12_0.png", + "data/Public/Table/P_origin_1/P_origin_1_12_1.png" + ], + "domain": "public" + }, + { + "index": 5, + "pair_id": "P_origin_1_16", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_16_0.png", + "data/Public/Table/P_origin_1/P_origin_1_16_1.png" + ], + "domain": "public" + }, + { + "index": 6, + "pair_id": "P_origin_1_17", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_17_0.png", + "data/Public/Table/P_origin_1/P_origin_1_17_1.png" + ], + "domain": "public" + }, + { + "index": 7, + "pair_id": "P_origin_1_18", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_18_0.png", + "data/Public/Table/P_origin_1/P_origin_1_18_1.png" + ], + "domain": "public" + }, + { + "index": 8, + "pair_id": "P_origin_1_23", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_23_0.png", + "data/Public/Table/P_origin_1/P_origin_1_23_1.png" + ], + "domain": "public" + }, + { + "index": 9, + "pair_id": "P_origin_2_0", + "image_paths": [ + "data/Public/Table/P_origin_2/P_origin_2_0_0.png", + "data/Public/Table/P_origin_2/P_origin_2_0_1.png" + ], + "domain": "public" + }, + { + "index": 10, + "pair_id": "P_origin_3_2", + "image_paths": [ + "data/Public/Table/P_origin_3/P_origin_3_2_0.png", + "data/Public/Table/P_origin_3/P_origin_3_2_1.png" + ], + "domain": "public" + }, + { + "index": 11, + "pair_id": "P_origin_4_9", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_9_0.png", + "data/Public/Table/P_origin_4/P_origin_4_9_1.png" + ], + "domain": "public" + }, + { + "index": 12, + "pair_id": "P_origin_4_11", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_11_0.png", + "data/Public/Table/P_origin_4/P_origin_4_11_1.png" + ], + "domain": "public" + }, + { + "index": 13, + "pair_id": "P_origin_5_1", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_1_0.png", + "data/Public/Table/P_origin_5/P_origin_5_1_1.png" + ], + "domain": "public" + }, + { + "index": 14, + "pair_id": "P_origin_5_17", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_17_0.png", + "data/Public/Table/P_origin_5/P_origin_5_17_1.png" + ], + "domain": "public" + }, + { + "index": 15, + "pair_id": "P_origin_6_12", + "image_paths": [ + "data/Public/Table/P_origin_6/P_origin_6_12_0.png", + "data/Public/Table/P_origin_6/P_origin_6_12_1.png" + ], + "domain": "public" + }, + { + "index": 16, + "pair_id": "P_origin_7_4", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_4_0.png", + "data/Public/Table/P_origin_7/P_origin_7_4_1.png" + ], + "domain": "public" + }, + { + "index": 17, + "pair_id": "P_origin_7_8", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_8_0.png", + "data/Public/Table/P_origin_7/P_origin_7_8_1.png" + ], + "domain": "public" + }, + { + "index": 18, + "pair_id": "P_origin_8_14", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_14_0.png", + "data/Public/Table/P_origin_8/P_origin_8_14_1.png" + ], + "domain": "public" + }, + { + "index": 19, + "pair_id": "P_origin_9_0", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_0_1.png", + "data/Public/Table/P_origin_9/P_origin_9_0_2.png" + ], + "domain": "public" + } +] \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 513cc81..8d5c460 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,4 +32,5 @@ dependencies = [ "pymongo>=4.6.1", "langgraph-checkpoint-sqlite>=3.0.1", "notion-client>=2.0.0", + "playwright>=1.57.0", ] diff --git a/regenerate_qa.py b/regenerate_qa.py new file mode 100755 index 0000000..008dac4 --- /dev/null +++ b/regenerate_qa.py @@ -0,0 +1,455 @@ +#!/usr/bin/env python3 +""" +기존 synthetic 테이블에서 QA를 재생성하는 스크립트. + +output_*/html/ 디렉토리의 HTML 파일을 직접 읽어서 새로운 QA pairs를 생성합니다. +pipeline_output.json은 entry 목록과 결과 저장에만 사용됩니다. + +Usage: + # 특정 도메인 재생성 + python regenerate_qa.py --domain business + + # 여러 도메인 재생성 + python regenerate_qa.py --domain business finance academic medical + + # 모든 도메인 재생성 (output_public 제외) + python regenerate_qa.py --all + + # 특정 provider/model 사용 + python regenerate_qa.py --domain business --provider openai --model gpt-4o +""" + +import argparse +import json +import logging +import os +import re +import sys +from pathlib import Path +from typing import List, Dict, Any, Optional +from concurrent.futures import ThreadPoolExecutor, as_completed +from datetime import datetime + +from dotenv import load_dotenv + +load_dotenv() + +# Add project root to path +project_root = Path(__file__).parent +sys.path.insert(0, str(project_root)) + +from generate_synthetic_table.flow import ( + _load_prompt, + _call_llm, + robust_json_parse, +) + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s" +) +logger = logging.getLogger(__name__) + +# 도메인별 output 디렉토리 +DOMAIN_DIRS = { + "academic": "output_academic", + "business": "output_business", + "finance": "output_finance", + "medical": "output_medical", + # "public": "output_public", # 제외 +} + + +def get_llm_client(provider: str, model: str): + """LLM 클라이언트 생성""" + from langchain_openai import ChatOpenAI + from langchain_anthropic import ChatAnthropic + from langchain_google_genai import ChatGoogleGenerativeAI + + if provider == "openai": + return ChatOpenAI( + model=model, + temperature=0.7, + api_key=os.getenv("OPENAI_API_KEY"), + ) + elif provider in ["claude", "anthropic"]: + return ChatAnthropic( + model=model, + temperature=0.7, + api_key=os.getenv("ANTHROPIC_API_KEY"), + ) + elif provider in ["gemini", "google"]: + return ChatGoogleGenerativeAI( + model=model, + temperature=0.7, + google_api_key=os.getenv("GOOGLE_API_KEY"), + ) + else: + raise ValueError(f"Unknown provider: {provider}") + + +def find_html_files(output_dir: Path, pair_id: str) -> List[Path]: + """ + output_dir/html/ 디렉토리에서 pair_id에 해당하는 HTML 파일들을 찾습니다. + + 파일 패턴: {pair_id}_table_*.html + 예: B_origin_0_0_0_table_0.html, B_origin_0_0_0_table_1.html + """ + html_dir = output_dir / "html" + if not html_dir.exists(): + return [] + + # pair_id로 시작하는 HTML 파일 찾기 + pattern = f"{pair_id}_table_*.html" + html_files = sorted(html_dir.glob(pattern)) + + return html_files + + +def read_html_files(html_files: List[Path]) -> List[str]: + """ + HTML 파일들을 읽어서 내용을 반환합니다. + """ + html_contents = [] + for html_file in html_files: + try: + with open(html_file, "r", encoding="utf-8") as f: + content = f.read().strip() + if content: + html_contents.append(content) + except Exception as e: + logger.warning(f"Failed to read {html_file}: {e}") + + return html_contents + + +def generate_qa_for_table( + llm, + synthetic_html: str, + domain: str, +) -> List[Dict[str, Any]]: + """단일 synthetic table에 대해 QA를 생성합니다.""" + try: + prompt_template = _load_prompt("generate_qa", domain) + prompt = prompt_template.format(synthetic_html=synthetic_html) + + response_text, _ = _call_llm(llm, prompt, return_token_usage=True) + response_json = robust_json_parse(response_text) + + if response_json and "qa_pairs" in response_json: + return response_json["qa_pairs"] + else: + logger.warning("QA generation did not return valid qa_pairs") + return [] + except Exception as e: + logger.error(f"Failed to generate QA: {e}") + return [] + + +def generate_long_sequence_for_table( + llm, + synthetic_html: str, + domain: str, +) -> List[Dict[str, Any]]: + """단일 synthetic table에 대해 long_sequence QA를 생성합니다.""" + try: + prompt_template = _load_prompt("generate_long_sequence", domain) + prompt = prompt_template.format(synthetic_html=synthetic_html) + + response_text, _ = _call_llm(llm, prompt, return_token_usage=True) + response_json = robust_json_parse(response_text) + + if response_json and "qa_pairs" in response_json: + return response_json["qa_pairs"] + else: + return [] + except ValueError: + # generate_long_sequence prompt not found + return [] + except Exception as e: + logger.warning(f"Failed to generate long_sequence QA: {e}") + return [] + + +def regenerate_qa_for_entry( + llm, + entry: Dict[str, Any], + output_dir: Path, + domain: str, + include_long_sequence: bool = True, +) -> Dict[str, Any]: + """ + 단일 entry에 대해 QA를 재생성합니다. + + html/ 디렉토리에서 HTML 파일을 직접 읽어서 QA를 생성합니다. + 여러 테이블이 있는 경우, 각 테이블에 대해 QA를 생성하고 합칩니다. + """ + pair_id = entry.get("pair_id", entry.get("name", "unknown")) + + # HTML 파일 찾기 및 읽기 + html_files = find_html_files(output_dir, pair_id) + if not html_files: + logger.warning(f"No HTML files found for {pair_id} in {output_dir}/html/") + return entry + + synthetic_tables = read_html_files(html_files) + if not synthetic_tables: + logger.warning(f"Failed to read HTML files for {pair_id}") + return entry + + logger.info(f" Found {len(html_files)} HTML files: {[f.name for f in html_files]}") + + all_qa_results = [] + + # 각 테이블에 대해 QA 생성 + for idx, synthetic_html in enumerate(synthetic_tables): + logger.info(f" Generating QA for table {idx + 1}/{len(synthetic_tables)}") + + # 기본 QA 생성 (9개 타입) + qa_results = generate_qa_for_table(llm, synthetic_html, domain) + all_qa_results.extend(qa_results) + + # long_sequence QA 생성 (선택적) + if include_long_sequence: + long_seq_results = generate_long_sequence_for_table(llm, synthetic_html, domain) + all_qa_results.extend(long_seq_results) + + # 결과 업데이트 + updated_entry = entry.copy() + updated_entry["qa_results"] = all_qa_results + updated_entry["qa_regenerated_at"] = datetime.now().isoformat() + updated_entry["html_files_used"] = [f.name for f in html_files] + + return updated_entry + + +def regenerate_qa_for_domain( + domain: str, + provider: str = "claude", + model: str = "claude-sonnet-4-5", + include_long_sequence: bool = True, + limit: Optional[int] = None, + dry_run: bool = False, +) -> Dict[str, Any]: + """ + 특정 도메인의 모든 entry에 대해 QA를 재생성합니다. + """ + domain_dir = DOMAIN_DIRS.get(domain) + if not domain_dir: + raise ValueError(f"Unknown domain: {domain}") + + output_dir = project_root / domain_dir + pipeline_output_path = output_dir / "pipeline_output.json" + + if not pipeline_output_path.exists(): + raise FileNotFoundError(f"pipeline_output.json not found: {pipeline_output_path}") + + # Load existing data + with open(pipeline_output_path, "r", encoding="utf-8") as f: + data = json.load(f) + + logger.info(f"Loaded {len(data)} entries from {pipeline_output_path}") + + if limit: + data = data[:limit] + logger.info(f"Limited to {limit} entries") + + if dry_run: + logger.info("Dry run mode - not regenerating QA") + # HTML 파일 존재 여부 확인 + html_dir = output_dir / "html" + if not html_dir.exists(): + logger.warning(f"HTML directory not found: {html_dir}") + return {"domain": domain, "entries": len(data), "dry_run": True, "html_dir_exists": False} + + # 각 entry에 대해 HTML 파일 수 확인 + entries_with_html = 0 + total_html_files = 0 + for entry in data: + pair_id = entry.get("pair_id", entry.get("name", "")) + html_files = find_html_files(output_dir, pair_id) + if html_files: + entries_with_html += 1 + total_html_files += len(html_files) + logger.info(f" {pair_id}: {len(html_files)} HTML files") + + logger.info(f"Summary: {entries_with_html}/{len(data)} entries have HTML files ({total_html_files} total)") + return { + "domain": domain, + "entries": len(data), + "entries_with_html": entries_with_html, + "total_html_files": total_html_files, + "dry_run": True, + } + + # Create LLM client + llm = get_llm_client(provider, model) + + # Regenerate QA for each entry + updated_data = [] + success_count = 0 + error_count = 0 + + for i, entry in enumerate(data): + pair_id = entry.get("pair_id", entry.get("name", f"entry_{i}")) + logger.info(f"[{i + 1}/{len(data)}] Processing: {pair_id}") + + try: + updated_entry = regenerate_qa_for_entry( + llm, + entry, + output_dir, + domain, + include_long_sequence=include_long_sequence, + ) + updated_data.append(updated_entry) + + qa_count = len(updated_entry.get("qa_results", [])) + logger.info(f" Generated {qa_count} QA pairs") + success_count += 1 + except Exception as e: + logger.error(f" Failed: {e}") + updated_data.append(entry) # Keep original + error_count += 1 + + # Backup original file + backup_path = output_dir / f"pipeline_output_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json" + with open(backup_path, "w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=False, indent=2) + logger.info(f"Backed up original to {backup_path}") + + # Save updated data + with open(pipeline_output_path, "w", encoding="utf-8") as f: + json.dump(updated_data, f, ensure_ascii=False, indent=2) + logger.info(f"Saved updated data to {pipeline_output_path}") + + return { + "domain": domain, + "total_entries": len(data), + "success": success_count, + "errors": error_count, + "backup": str(backup_path), + } + + +def main(): + parser = argparse.ArgumentParser( + description="기존 synthetic 테이블에서 QA를 재생성합니다.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + # 특정 도메인 재생성 + python regenerate_qa.py --domain business + + # 여러 도메인 재생성 + python regenerate_qa.py --domain business finance + + # 모든 도메인 재생성 (output_public 제외) + python regenerate_qa.py --all + + # OpenAI 사용 + python regenerate_qa.py --domain business --provider openai --model gpt-4o + + # 테스트 (5개만) + python regenerate_qa.py --domain business --limit 5 + + # Dry run (실제 재생성 없이 확인만) + python regenerate_qa.py --domain business --dry-run + """ + ) + + parser.add_argument( + "--domain", + nargs="+", + choices=list(DOMAIN_DIRS.keys()), + help="재생성할 도메인(들)", + ) + parser.add_argument( + "--all", + action="store_true", + help="모든 도메인 재생성 (output_public 제외)", + ) + parser.add_argument( + "--provider", + default="claude", + choices=["claude", "anthropic", "openai", "gemini", "google"], + help="LLM 제공자 (default: claude)", + ) + parser.add_argument( + "--model", + default="claude-sonnet-4-5", + help="모델 이름 (default: claude-sonnet-4-5)", + ) + parser.add_argument( + "--no-long-sequence", + action="store_true", + help="long_sequence QA 생성 스킵", + ) + parser.add_argument( + "--limit", + type=int, + help="처리할 최대 entry 수 (테스트용)", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="실제 재생성 없이 확인만", + ) + + args = parser.parse_args() + + # Determine domains to process + if args.all: + domains = list(DOMAIN_DIRS.keys()) + elif args.domain: + domains = args.domain + else: + parser.error("--domain 또는 --all을 지정해야 합니다.") + + logger.info(f"Domains to process: {domains}") + logger.info(f"Provider: {args.provider}, Model: {args.model}") + + # Process each domain + results = [] + for domain in domains: + logger.info(f"\n{'='*60}") + logger.info(f"Processing domain: {domain}") + logger.info(f"{'='*60}") + + try: + result = regenerate_qa_for_domain( + domain=domain, + provider=args.provider, + model=args.model, + include_long_sequence=not args.no_long_sequence, + limit=args.limit, + dry_run=args.dry_run, + ) + results.append(result) + logger.info(f"Completed: {result}") + except Exception as e: + logger.error(f"Failed to process {domain}: {e}") + results.append({"domain": domain, "error": str(e)}) + + # Summary + print("\n" + "=" * 60) + print(" QA Regeneration Summary") + print("=" * 60) + for result in results: + domain = result.get("domain", "unknown") + if "error" in result: + print(f" {domain}: ERROR - {result['error']}") + elif result.get("dry_run"): + html_info = "" + if "entries_with_html" in result: + html_info = f", {result['entries_with_html']}/{result['entries']} with HTML ({result['total_html_files']} files)" + elif result.get("html_dir_exists") is False: + html_info = ", NO html/ directory!" + print(f" {domain}: {result.get('entries', 0)} entries (dry run){html_info}") + else: + print(f" {domain}: {result.get('success', 0)}/{result.get('total_entries', 0)} success, {result.get('errors', 0)} errors") + print("=" * 60) + + +if __name__ == "__main__": + main() diff --git a/run_all.sh b/run_all.sh new file mode 100755 index 0000000..87ed09f --- /dev/null +++ b/run_all.sh @@ -0,0 +1,536 @@ +#!/bin/bash + +# ============================================================================== +# TableMagnifier - Master Pipeline Script +# ============================================================================== +# +# 전체 파이프라인을 통합 실행합니다: +# 1. Synthetic Table 생성 (from JSON input) +# 2. HTML → Image 변환 +# 3. QA 재생성 (선택) +# 4. QA 난이도 필터링 (vLLM 필요) +# 5. 평가 (vLLM 필요) +# +# Usage: +# ./run_all.sh --input data.json --domain business [OPTIONS] +# +# Examples: +# # 기본 파이프라인 (테이블 생성 + 이미지 변환) +# ./run_all.sh --input test.json --domain business +# +# # 전체 파이프라인 (vLLM 평가 포함) +# ./run_all.sh --input test.json --domain business --with-eval --vllm-url http://localhost:8000/v1 +# +# # QA 재생성만 +# ./run_all.sh --domain business --regenerate-qa-only +# +# # 필터링 + 평가만 (이미 테이블/이미지가 있는 경우) +# ./run_all.sh --domain business --filter-only --with-eval +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +# ============================================================================== +# Configuration +# ============================================================================== + +# Default values +INPUT_JSON="" +DOMAIN="" +OUTPUT_DIR="" +PROVIDER="claude" +MODEL="claude-sonnet-4-5" +VLLM_URL="http://localhost:8000/v1" + +# Pipeline steps (default: generate + capture) +DO_GENERATE=true +DO_CAPTURE=true +DO_REGENERATE_QA=false +DO_FILTER=false +DO_EVAL=false + +# Options +LIMIT="" +DRY_RUN=false +SKIP_QA=false +FILTER_TRIALS=10 +FILTER_MIN_ACC=0.3 +FILTER_MAX_ACC=0.6 + +# Colors +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' + +# ============================================================================== +# Helper Functions +# ============================================================================== + +echo_header() { + echo "" + echo -e "${BLUE}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${BLUE}${BOLD} $1${NC}" + echo -e "${BLUE}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +} + +echo_step() { + echo "" + echo -e "${CYAN}▶ STEP $1: $2${NC}" + echo -e "${CYAN}─────────────────────────────────────────────────────────────${NC}" +} + +echo_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +echo_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +echo_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +echo_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +show_help() { + cat << 'EOF' +Usage: ./run_all.sh [OPTIONS] + +TableMagnifier 전체 파이프라인을 통합 실행합니다. + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Required Options +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + --domain DOMAIN 도메인 (business, finance, academic, medical, public) + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Pipeline Steps +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + --input FILE 입력 JSON 파일 (테이블 생성 시 필수) + --regenerate-qa QA 재생성 포함 + --regenerate-qa-only QA 재생성만 실행 (테이블 생성 스킵) + --with-filter vLLM으로 QA 난이도 필터링 포함 + --filter-only 필터링만 실행 (테이블/이미지 생성 스킵) + --with-eval vLLM으로 평가 포함 + --eval-only 평가만 실행 + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Generation Options +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + --output-dir DIR 출력 디렉토리 (default: output_{domain}) + --provider PROVIDER LLM 제공자: claude, openai, gemini (default: claude) + --model MODEL 모델 이름 (default: claude-sonnet-4-5) + --skip-qa 테이블 생성 시 QA 생성 스킵 + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + vLLM Options (for filter/eval) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + --vllm-url URL vLLM 서버 URL (default: http://localhost:8000/v1) + --filter-trials N 필터링 시 QA당 시도 횟수 (default: 10) + --filter-min-acc FLOAT 필터링 최소 정확도 (default: 0.3) + --filter-max-acc FLOAT 필터링 최대 정확도 (default: 0.6) + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Other Options +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + --limit N 처리할 최대 entry 수 (테스트용) + --dry-run 실제 실행 없이 확인만 + -h, --help 도움말 표시 + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Examples +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + # 1. 기본 파이프라인 (테이블 생성 → 이미지 변환) + ./run_all.sh --input test.json --domain business + + # 2. 전체 파이프라인 (생성 → 이미지 → 필터링 → 평가) + ./run_all.sh --input test.json --domain business --with-filter --with-eval + + # 3. QA만 재생성 (기존 테이블 유지) + ./run_all.sh --domain business --regenerate-qa-only + + # 4. 필터링만 (이미 이미지가 있는 경우) + ./run_all.sh --domain business --filter-only + + # 5. 평가만 + ./run_all.sh --domain business --eval-only + + # 6. OpenAI 사용 + ./run_all.sh --input test.json --domain business --provider openai --model gpt-4o + + # 7. 테스트 (3개만) + ./run_all.sh --input test.json --domain business --limit 3 --dry-run + +EOF +} + +# ============================================================================== +# Argument Parsing +# ============================================================================== + +# Parse first argument as JSON file if it ends with .json +if [[ "$1" == *.json ]]; then + INPUT_JSON="$1" + shift +fi + +while [[ $# -gt 0 ]]; do + case $1 in + --input) + INPUT_JSON="$2" + shift 2 + ;; + --domain) + DOMAIN="$2" + shift 2 + ;; + --output-dir) + OUTPUT_DIR="$2" + shift 2 + ;; + --provider) + PROVIDER="$2" + shift 2 + ;; + --model) + MODEL="$2" + shift 2 + ;; + --vllm-url) + VLLM_URL="$2" + shift 2 + ;; + --regenerate-qa) + DO_REGENERATE_QA=true + shift + ;; + --regenerate-qa-only) + DO_GENERATE=false + DO_CAPTURE=false + DO_REGENERATE_QA=true + shift + ;; + --with-filter) + DO_FILTER=true + shift + ;; + --filter-only) + DO_GENERATE=false + DO_CAPTURE=false + DO_FILTER=true + shift + ;; + --with-eval) + DO_EVAL=true + shift + ;; + --eval-only) + DO_GENERATE=false + DO_CAPTURE=false + DO_EVAL=true + shift + ;; + --filter-trials) + FILTER_TRIALS="$2" + shift 2 + ;; + --filter-min-acc) + FILTER_MIN_ACC="$2" + shift 2 + ;; + --filter-max-acc) + FILTER_MAX_ACC="$2" + shift 2 + ;; + --skip-qa) + SKIP_QA=true + shift + ;; + --limit) + LIMIT="$2" + shift 2 + ;; + --dry-run) + DRY_RUN=true + shift + ;; + -h|--help) + show_help + exit 0 + ;; + *) + echo_error "Unknown option: $1" + echo "Use -h or --help for usage information." + exit 1 + ;; + esac +done + +# ============================================================================== +# Validation +# ============================================================================== + +# Domain is always required +if [[ -z "$DOMAIN" ]]; then + echo_error "--domain is required" + echo "Use -h or --help for usage information." + exit 1 +fi + +# Input JSON required for generation +if [[ "$DO_GENERATE" == true ]] && [[ -z "$INPUT_JSON" ]]; then + echo_error "--input is required for table generation" + echo "Use --regenerate-qa-only, --filter-only, or --eval-only to skip generation." + exit 1 +fi + +# Check input file exists +if [[ -n "$INPUT_JSON" ]] && [[ ! -f "$INPUT_JSON" ]]; then + echo_error "Input file not found: $INPUT_JSON" + exit 1 +fi + +# Set default output directory +if [[ -z "$OUTPUT_DIR" ]]; then + OUTPUT_DIR="output_${DOMAIN}" +fi + +# ============================================================================== +# Check Dependencies +# ============================================================================== + +check_vllm_connection() { + if curl -s --connect-timeout 5 "${VLLM_URL}/models" > /dev/null 2>&1; then + VLLM_MODEL=$(curl -s "${VLLM_URL}/models" | python3 -c "import sys, json; data = json.load(sys.stdin); print(data['data'][0]['id'] if data.get('data') else 'unknown')" 2>/dev/null || echo "unknown") + echo_info "vLLM connected: ${VLLM_MODEL}" + return 0 + else + return 1 + fi +} + +check_api_key() { + case $PROVIDER in + claude|anthropic) + if [[ -z "$ANTHROPIC_API_KEY" ]]; then + echo_warn "ANTHROPIC_API_KEY is not set" + fi + ;; + openai) + if [[ -z "$OPENAI_API_KEY" ]]; then + echo_warn "OPENAI_API_KEY is not set" + fi + ;; + gemini|google) + if [[ -z "$GOOGLE_API_KEY" ]]; then + echo_warn "GOOGLE_API_KEY is not set" + fi + ;; + esac +} + +# ============================================================================== +# Main Pipeline +# ============================================================================== + +echo_header "TableMagnifier - Master Pipeline" + +echo "" +echo "Configuration:" +echo " Domain: $DOMAIN" +echo " Output Dir: $OUTPUT_DIR" +echo " Provider: $PROVIDER" +echo " Model: $MODEL" +if [[ -n "$INPUT_JSON" ]]; then + echo " Input JSON: $INPUT_JSON" +fi +if [[ -n "$LIMIT" ]]; then + echo " Limit: $LIMIT entries" +fi +if [[ "$DRY_RUN" == true ]]; then + echo " Mode: DRY RUN" +fi +echo "" +echo "Pipeline Steps:" +echo " 1. Generate Tables: $([ "$DO_GENERATE" == true ] && echo "✓" || echo "✗")" +echo " 2. Capture Images: $([ "$DO_CAPTURE" == true ] && echo "✓" || echo "✗")" +echo " 3. Regenerate QA: $([ "$DO_REGENERATE_QA" == true ] && echo "✓" || echo "✗")" +echo " 4. Filter QA: $([ "$DO_FILTER" == true ] && echo "✓" || echo "✗")" +echo " 5. Evaluate: $([ "$DO_EVAL" == true ] && echo "✓" || echo "✗")" +echo "" + +# Check API key for generation steps +if [[ "$DO_GENERATE" == true ]] || [[ "$DO_REGENERATE_QA" == true ]]; then + check_api_key +fi + +# Check vLLM for filter/eval steps +if [[ "$DO_FILTER" == true ]] || [[ "$DO_EVAL" == true ]]; then + echo_info "Checking vLLM connection..." + if ! check_vllm_connection; then + echo_error "Cannot connect to vLLM server at ${VLLM_URL}" + echo_error "Please ensure vLLM server is running for filter/eval steps." + exit 1 + fi +fi + +STEP_NUM=0 + +# ------------------------------------------------------------------------------ +# Step 1: Generate Synthetic Tables +# ------------------------------------------------------------------------------ +if [[ "$DO_GENERATE" == true ]]; then + STEP_NUM=$((STEP_NUM + 1)) + echo_step $STEP_NUM "Generate Synthetic Tables" + + GENERATE_ARGS="--input \"$INPUT_JSON\" --output-dir \"$OUTPUT_DIR\" --provider \"$PROVIDER\" --model \"$MODEL\" --domain \"$DOMAIN\"" + + if [[ "$SKIP_QA" == true ]]; then + GENERATE_ARGS="$GENERATE_ARGS --skip-qa" + fi + + if [[ -n "$LIMIT" ]]; then + GENERATE_ARGS="$GENERATE_ARGS --limit $LIMIT" + fi + + if [[ "$DRY_RUN" == true ]]; then + echo_info "[DRY RUN] Would run: uv run python run_pipeline_json.py $GENERATE_ARGS" + else + eval "uv run python run_pipeline_json.py $GENERATE_ARGS" + echo_success "Table generation completed" + fi +fi + +# ------------------------------------------------------------------------------ +# Step 2: Capture HTML to Images +# ------------------------------------------------------------------------------ +if [[ "$DO_CAPTURE" == true ]]; then + STEP_NUM=$((STEP_NUM + 1)) + echo_step $STEP_NUM "Capture HTML to Images" + + CAPTURE_ARGS="--output-dirs $OUTPUT_DIR" + + if [[ "$DRY_RUN" == true ]]; then + echo_info "[DRY RUN] Would run: uv run python capture_html_images.py $CAPTURE_ARGS" + else + uv run python capture_html_images.py $CAPTURE_ARGS + echo_success "Image capture completed" + fi +fi + +# ------------------------------------------------------------------------------ +# Step 3: Regenerate QA (Optional) +# ------------------------------------------------------------------------------ +if [[ "$DO_REGENERATE_QA" == true ]]; then + STEP_NUM=$((STEP_NUM + 1)) + echo_step $STEP_NUM "Regenerate QA" + + REGEN_ARGS="--domain $DOMAIN --provider $PROVIDER --model $MODEL" + + if [[ -n "$LIMIT" ]]; then + REGEN_ARGS="$REGEN_ARGS --limit $LIMIT" + fi + + if [[ "$DRY_RUN" == true ]]; then + REGEN_ARGS="$REGEN_ARGS --dry-run" + fi + + uv run python regenerate_qa.py $REGEN_ARGS + echo_success "QA regeneration completed" +fi + +# ------------------------------------------------------------------------------ +# Step 4: Filter QA by Difficulty (Optional) +# ------------------------------------------------------------------------------ +if [[ "$DO_FILTER" == true ]]; then + STEP_NUM=$((STEP_NUM + 1)) + echo_step $STEP_NUM "Filter QA by Difficulty" + + FILTER_ARGS="--domain $DOMAIN --vllm-url $VLLM_URL --trials $FILTER_TRIALS --min-acc $FILTER_MIN_ACC --max-acc $FILTER_MAX_ACC" + + if [[ -n "$LIMIT" ]]; then + FILTER_ARGS="$FILTER_ARGS --limit $LIMIT" + fi + + if [[ "$DRY_RUN" == true ]]; then + FILTER_ARGS="$FILTER_ARGS --dry-run" + fi + + uv run python filter_qa_by_difficulty.py $FILTER_ARGS + echo_success "QA filtering completed" +fi + +# ------------------------------------------------------------------------------ +# Step 5: Evaluate (Optional) +# ------------------------------------------------------------------------------ +if [[ "$DO_EVAL" == true ]]; then + STEP_NUM=$((STEP_NUM + 1)) + echo_step $STEP_NUM "Evaluate with vLLM" + + EVAL_ARGS="--domain $DOMAIN --vllm-url $VLLM_URL" + + if [[ -n "$LIMIT" ]]; then + EVAL_ARGS="$EVAL_ARGS --limit $LIMIT" + fi + + if [[ "$DRY_RUN" == true ]]; then + EVAL_ARGS="$EVAL_ARGS --dry-run" + fi + + uv run python -m eval.evaluate_vllm $EVAL_ARGS + echo_success "Evaluation completed" +fi + +# ============================================================================== +# Summary +# ============================================================================== + +echo_header "Pipeline Completed" + +echo "" +echo "Output Directory: $OUTPUT_DIR/" +echo "" +echo "Generated Files:" + +if [[ -d "$OUTPUT_DIR" ]]; then + # Count files + JSON_COUNT=$(find "$OUTPUT_DIR" -maxdepth 1 -name "*.json" 2>/dev/null | wc -l) + HTML_COUNT=$(find "$OUTPUT_DIR/html" -name "*.html" 2>/dev/null | wc -l) + IMAGE_COUNT=$(find "$OUTPUT_DIR/images" -name "*.png" 2>/dev/null | wc -l) + + echo " - JSON files: $JSON_COUNT" + echo " - HTML files: $HTML_COUNT (in html/)" + echo " - Images: $IMAGE_COUNT (in images/)" + + if [[ "$DO_FILTER" == true ]] && [[ "$DRY_RUN" != true ]]; then + REVIEW_FILE=$(ls -t "$OUTPUT_DIR"/qa_for_review_*.json 2>/dev/null | head -1) + if [[ -n "$REVIEW_FILE" ]]; then + REVIEW_COUNT=$(python3 -c "import json; print(json.load(open('$REVIEW_FILE'))['count'])" 2>/dev/null || echo "?") + echo "" + echo " Review File: $(basename $REVIEW_FILE)" + echo " QA for Review: $REVIEW_COUNT items" + fi + fi + + if [[ "$DO_EVAL" == true ]] && [[ "$DRY_RUN" != true ]]; then + EVAL_FILE=$(ls -t "$OUTPUT_DIR"/eval_results_*.json 2>/dev/null | head -1) + if [[ -n "$EVAL_FILE" ]]; then + echo "" + echo " Eval Results: $(basename $EVAL_FILE)" + fi + fi +fi + +echo "" +echo -e "${GREEN}Done!${NC}" diff --git a/run_capture_html.sh b/run_capture_html.sh new file mode 100755 index 0000000..fb3a9df --- /dev/null +++ b/run_capture_html.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# ============================================================================== +# HTML to Image Capture Script +# Captures HTML files from output_*/html/ directories as PNG images +# ============================================================================== + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +echo "===================================" +echo " HTML to Image Capture" +echo "===================================" + +# Check if playwright is installed +if ! uv run python -c "import playwright" 2>/dev/null; then + echo "[INFO] Installing playwright..." + uv add playwright + uv run playwright install chromium +fi + +# Run the capture script +uv run python "$SCRIPT_DIR/capture_html_images.py" "$@" diff --git a/run_evaluate_vllm.sh b/run_evaluate_vllm.sh new file mode 100755 index 0000000..762a672 --- /dev/null +++ b/run_evaluate_vllm.sh @@ -0,0 +1,255 @@ +#!/bin/bash +# +# vLLM 서버를 사용한 Table QA 평가 스크립트 +# +# 사전 요구사항: +# 1. vLLM 서버가 실행 중이어야 합니다 +# 2. HTML 파일들이 이미지로 캡처되어 있어야 합니다 (./run_capture_html.sh 실행) +# +# Usage: +# ./run_evaluate_vllm.sh [OPTIONS] +# +# Examples: +# # 모든 도메인 평가 +# ./run_evaluate_vllm.sh --all-domains +# +# # 단일 도메인 평가 +# ./run_evaluate_vllm.sh --domain public +# +# # 커스텀 vLLM URL +# ./run_evaluate_vllm.sh --domain public --vllm-url http://gpu-server:8000/v1 +# +# # 특정 모델 사용 +# ./run_evaluate_vllm.sh --domain business --model Qwen/Qwen2.5-VL-7B-Instruct +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +# 기본 설정 +VLLM_URL="${VLLM_URL:-http://localhost:8000/v1}" +MODEL="${MODEL:-default}" +OUTPUT_DIR="${OUTPUT_DIR:-eval_results}" + +# 색상 정의 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +echo_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +echo_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +echo_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# 헬프 메시지 +show_help() { + cat << EOF +Usage: $0 [OPTIONS] + +vLLM 서버를 사용한 Table QA 평가 + +Options: + --domain DOMAIN 평가할 도메인 (academic, business, finance, medical, public) + --all-domains 모든 도메인 평가 + --vllm-url URL vLLM 서버 URL (default: $VLLM_URL) + --model MODEL 사용할 모델 이름 (default: $MODEL) + --output-dir DIR 결과 저장 디렉토리 (default: $OUTPUT_DIR) + --use-judge LLM-as-Judge 평가 사용 + --judge-model MODEL Judge 모델 (default: gpt-4o-mini) + --limit N 평가할 최대 샘플 수 (디버깅용) + --qa-types TYPES 특정 QA 타입만 평가 (예: "lookup compare") + --capture-html 평가 전 HTML을 이미지로 캡처 + -h, --help 이 도움말 표시 + +Environment Variables: + VLLM_URL vLLM 서버 URL + MODEL 사용할 모델 이름 + OUTPUT_DIR 결과 저장 디렉토리 + OPENAI_API_KEY LLM-as-Judge 사용 시 OpenAI API 키 + +Examples: + # 모든 도메인 평가 + $0 --all-domains + + # public 도메인만 평가 + $0 --domain public + + # LLM-as-Judge 포함 평가 + $0 --domain finance --use-judge --judge-model gpt-4o + + # 10개 샘플로 빠른 테스트 + $0 --domain public --limit 10 +EOF +} + +# vLLM 서버 연결 확인 +check_vllm_connection() { + local url=$1 + echo_info "vLLM 서버 연결 확인: $url" + + # /v1/models 엔드포인트로 연결 테스트 + if curl -s --connect-timeout 5 "$url/models" > /dev/null 2>&1; then + echo_info "vLLM 서버 연결 성공" + return 0 + else + echo_error "vLLM 서버에 연결할 수 없습니다: $url" + echo_error "vLLM 서버가 실행 중인지 확인하세요." + return 1 + fi +} + +# 이미지 디렉토리 확인 +check_images() { + local domains=("academic" "business" "finance" "medical" "public") + local missing=0 + + for domain in "${domains[@]}"; do + local output_dir="output_${domain}" + local images_dir="${output_dir}/images" + + if [[ -d "$output_dir" ]]; then + if [[ ! -d "$images_dir" ]] || [[ -z "$(ls -A "$images_dir" 2>/dev/null)" ]]; then + echo_warn "$domain: 이미지 디렉토리가 비어있거나 없습니다 ($images_dir)" + ((missing++)) + fi + fi + done + + if [[ $missing -gt 0 ]]; then + echo_warn "일부 도메인에 이미지가 없습니다. HTML 캡처가 필요할 수 있습니다." + echo_warn "실행: ./run_capture_html.sh" + fi +} + +# 인자 파싱 +DOMAIN="" +ALL_DOMAINS=false +CAPTURE_HTML=false +USE_JUDGE=false +JUDGE_MODEL="" +LIMIT="" +QA_TYPES="" + +while [[ $# -gt 0 ]]; do + case $1 in + --domain) + DOMAIN="$2" + shift 2 + ;; + --all-domains) + ALL_DOMAINS=true + shift + ;; + --vllm-url) + VLLM_URL="$2" + shift 2 + ;; + --model) + MODEL="$2" + shift 2 + ;; + --output-dir) + OUTPUT_DIR="$2" + shift 2 + ;; + --use-judge) + USE_JUDGE=true + shift + ;; + --judge-model) + JUDGE_MODEL="$2" + shift 2 + ;; + --limit) + LIMIT="$2" + shift 2 + ;; + --qa-types) + QA_TYPES="$2" + shift 2 + ;; + --capture-html) + CAPTURE_HTML=true + shift + ;; + -h|--help) + show_help + exit 0 + ;; + *) + echo_error "알 수 없는 옵션: $1" + show_help + exit 1 + ;; + esac +done + +# 인자 검증 +if [[ -z "$DOMAIN" ]] && [[ "$ALL_DOMAINS" != true ]]; then + echo_error "--domain 또는 --all-domains를 지정해야 합니다." + show_help + exit 1 +fi + +# HTML 캡처 (옵션) +if [[ "$CAPTURE_HTML" == true ]]; then + echo_info "HTML 파일을 이미지로 캡처합니다..." + if [[ -f "./run_capture_html.sh" ]]; then + ./run_capture_html.sh + else + echo_warn "run_capture_html.sh를 찾을 수 없습니다. 스킵합니다." + fi +fi + +# vLLM 연결 확인 +check_vllm_connection "$VLLM_URL" || exit 1 + +# 이미지 확인 +check_images + +# 평가 명령어 구성 +CMD="uv run python -m eval.evaluate_vllm" +CMD="$CMD --vllm-url $VLLM_URL" +CMD="$CMD --model $MODEL" +CMD="$CMD --output-dir $OUTPUT_DIR" + +if [[ "$ALL_DOMAINS" == true ]]; then + CMD="$CMD --all-domains" +elif [[ -n "$DOMAIN" ]]; then + CMD="$CMD --domain $DOMAIN" +fi + +if [[ "$USE_JUDGE" == true ]]; then + CMD="$CMD --use-judge" + if [[ -n "$JUDGE_MODEL" ]]; then + CMD="$CMD --judge-model $JUDGE_MODEL" + fi +fi + +if [[ -n "$LIMIT" ]]; then + CMD="$CMD --limit $LIMIT" +fi + +if [[ -n "$QA_TYPES" ]]; then + CMD="$CMD --qa-types $QA_TYPES" +fi + +# 평가 실행 +echo_info "평가 시작..." +echo_info "Command: $CMD" +echo "" + +eval $CMD + +echo "" +echo_info "평가 완료. 결과: $OUTPUT_DIR/" diff --git a/run_filter_qa.sh b/run_filter_qa.sh new file mode 100755 index 0000000..8ed106b --- /dev/null +++ b/run_filter_qa.sh @@ -0,0 +1,137 @@ +#!/bin/bash + +# ============================================================================== +# TableMagnifier - QA Difficulty Filtering +# ============================================================================== +# +# vLLM 서버를 사용하여 QA 난이도를 측정하고 검수 대상을 필터링합니다. +# 모델이 너무 쉽게 맞추는 문제(90%+)는 제외하고, +# 적당한 난이도(30-60%)의 QA만 검수 리스트로 추출합니다. +# +# Usage: +# ./run_filter_qa.sh [OPTIONS] +# +# Examples: +# ./run_filter_qa.sh --domain business +# ./run_filter_qa.sh --all --trials 5 +# ./run_filter_qa.sh --domain business --vllm-url http://gpu-server:8000/v1 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +# Colors +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' + +echo_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +echo_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +echo_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +show_help() { + cat << EOF +Usage: $0 [OPTIONS] + +vLLM 서버를 사용하여 QA 난이도를 측정하고 검수 대상을 필터링합니다. + +Options: + --domain DOMAIN [...] 필터링할 도메인(들) (business, finance, academic, medical, public) + --all 모든 도메인 필터링 + --vllm-url URL vLLM 서버 URL (default: http://localhost:8000/v1) + --model MODEL 모델 이름 (미지정시 자동 감지) + --trials N 각 QA당 시도 횟수 (default: 10) + --min-acc FLOAT 최소 정확도 (default: 0.3) + --max-acc FLOAT 최대 정확도 (default: 0.6) + --limit N 처리할 최대 entry 수 (테스트용) + --dry-run 실제 추론 없이 확인만 + -h, --help 도움말 표시 + +Difficulty Categories: + - too_easy: 90-100% (제외 - 모델이 다 맞춤) + - easy: 70-89% + - medium: 30-69% (검수 대상 ✓) + - hard: 1-29% + - very_hard: 0% + +Examples: + # business 도메인 필터링 + $0 --domain business + + # 빠른 테스트 (5회 시도, 2개 entry만) + $0 --domain business --trials 5 --limit 2 + + # 외부 vLLM 서버 사용 + $0 --domain business --vllm-url http://gpu-server:8000/v1 + +Output: + - qa_difficulty_analysis_*.json: 전체 분석 결과 + - qa_for_review_*.json: 검수용 필터링된 QA 리스트 +EOF +} + +# Check for help +for arg in "$@"; do + if [[ "$arg" == "-h" ]] || [[ "$arg" == "--help" ]]; then + show_help + exit 0 + fi +done + +# Check for required arguments +if [[ $# -eq 0 ]]; then + show_help + exit 1 +fi + +# Parse vllm-url for connection check +VLLM_URL="http://localhost:8000/v1" +for i in $(seq 1 $#); do + arg="${!i}" + if [[ "$arg" == "--vllm-url" ]]; then + next=$((i + 1)) + VLLM_URL="${!next}" + break + fi +done + +echo "==============================================" +echo " TableMagnifier - QA Difficulty Filtering" +echo "==============================================" +echo "" + +# Check vLLM connection +echo_info "Checking vLLM server connection..." +if curl -s --connect-timeout 5 "${VLLM_URL}/models" > /dev/null 2>&1; then + MODEL_INFO=$(curl -s "${VLLM_URL}/models" | python3 -c "import sys, json; data = json.load(sys.stdin); print(data['data'][0]['id'] if data.get('data') else 'unknown')" 2>/dev/null || echo "unknown") + echo_info "vLLM server connected. Model: ${MODEL_INFO}" +else + echo_error "Cannot connect to vLLM server at ${VLLM_URL}" + echo_error "Please ensure vLLM server is running." + exit 1 +fi + +echo "" +echo_info "Starting QA difficulty filtering..." +echo "" + +# Run the filter script +uv run python filter_qa_by_difficulty.py "$@" + +echo "" +echo_info "Filtering completed!" +echo "" +echo "Generated files:" +echo " - qa_difficulty_analysis_*.json: Full analysis results" +echo " - qa_for_review_*.json: Filtered QA for human review" diff --git a/run_openai_public.sh b/run_openai_public.sh deleted file mode 100644 index d90e1f6..0000000 --- a/run_openai_public.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -# ============================================================================== -# TableMagnifier - JSON Pipeline (Public Domain) -# ============================================================================== - -# Default Configuration -INPUT_JSON="test_business.json" -OUTPUT_DIR="output_business" -DEFAULT_ARGS="--provider claude --model claude-sonnet-4-5 --domain business" - -# Check if the first argument is a JSON file path -if [[ "$1" == *.json ]]; then - INPUT_JSON="$1" - shift -fi - -echo "==============================================" -echo " TableMagnifier - JSON Pipeline (Public)" -echo "==============================================" -echo "Input JSON: $INPUT_JSON" -echo "Output Dir: $OUTPUT_DIR" -echo "Provider: claude" -echo "Model: claude-sonnet-4-5" -echo "Domain: business" -echo "" -echo "💡 Tip: To upload to Notion during pipeline execution:" -echo " Add --upload-to-notion flag to the command" -echo "" -echo "💡 To upload existing results later:" -echo " python upload_to_notion_from_json.py $OUTPUT_DIR" -echo "" - -# Check for ANTHROPIC_API_KEY -if [[ -z "$ANTHROPIC_API_KEY" ]]; then - echo "⚠️ Warning: ANTHROPIC_API_KEY is not set." - echo " Please set it in your environment or .env file." - echo "" -fi - -# Run the pipeline -# Note: "$@" appends any remaining arguments, allowing overrides of defaults -uv run python run_pipeline_json.py --input "$INPUT_JSON" --output-dir "$OUTPUT_DIR" $DEFAULT_ARGS "$@" \ No newline at end of file diff --git a/run_pipeline.sh b/run_pipeline.sh new file mode 100644 index 0000000..460929c --- /dev/null +++ b/run_pipeline.sh @@ -0,0 +1,171 @@ +#!/bin/bash + +# ============================================================================== +# TableMagnifier - JSON Pipeline +# ============================================================================== +# +# Usage: +# ./run_pipeline.sh [INPUT_JSON] [OPTIONS] +# +# Examples: +# ./run_pipeline.sh test_public.json --domain public +# ./run_pipeline.sh test_business.json --domain business --provider openai +# ./run_pipeline.sh --input data.json --output-dir output_custom +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +# Default Configuration +INPUT_JSON="" +OUTPUT_DIR="" +PROVIDER="claude" +MODEL="claude-sonnet-4-5" +DOMAIN="public" + +# Colors +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +show_help() { + cat << EOF +Usage: $0 [INPUT_JSON] [OPTIONS] + +TableMagnifier JSON Pipeline Runner + +Arguments: + INPUT_JSON Input JSON file path (optional, can use --input instead) + +Options: + --input FILE Input JSON file + --output-dir DIR Output directory (default: output_{domain}) + --provider PROVIDER LLM provider: claude, openai, gemini (default: claude) + --model MODEL Model name (default: claude-sonnet-4-5) + --domain DOMAIN Domain: public, business, finance, medical, academic (default: public) + --qa-only Generate QA only (skip table generation) + --skip-qa Skip QA generation (table only) + --upload-to-notion Upload results to Notion + -h, --help Show this help + +Examples: + # Public domain with Claude + $0 test_public.json --domain public + + # Business domain with OpenAI + $0 test_business.json --domain business --provider openai --model gpt-4o + + # Finance domain, QA only mode + $0 test_finance.json --domain finance --qa-only + + # Custom output directory + $0 data.json --output-dir my_output --domain medical +EOF +} + +# Parse first argument as JSON file if it ends with .json +if [[ "$1" == *.json ]]; then + INPUT_JSON="$1" + shift +fi + +# Parse remaining arguments +EXTRA_ARGS="" +while [[ $# -gt 0 ]]; do + case $1 in + --input) + INPUT_JSON="$2" + shift 2 + ;; + --output-dir) + OUTPUT_DIR="$2" + shift 2 + ;; + --provider) + PROVIDER="$2" + shift 2 + ;; + --model) + MODEL="$2" + shift 2 + ;; + --domain) + DOMAIN="$2" + shift 2 + ;; + -h|--help) + show_help + exit 0 + ;; + *) + EXTRA_ARGS="$EXTRA_ARGS $1" + shift + ;; + esac +done + +# Set default output directory based on domain +if [[ -z "$OUTPUT_DIR" ]]; then + OUTPUT_DIR="output_${DOMAIN}" +fi + +# Validate input +if [[ -z "$INPUT_JSON" ]]; then + echo -e "${YELLOW}[WARN]${NC} No input JSON specified." + show_help + exit 1 +fi + +if [[ ! -f "$INPUT_JSON" ]]; then + echo -e "${YELLOW}[ERROR]${NC} Input file not found: $INPUT_JSON" + exit 1 +fi + +echo "==============================================" +echo " TableMagnifier - JSON Pipeline" +echo "==============================================" +echo "Input JSON: $INPUT_JSON" +echo "Output Dir: $OUTPUT_DIR" +echo "Provider: $PROVIDER" +echo "Model: $MODEL" +echo "Domain: $DOMAIN" +echo "" + +# Check API keys based on provider +case $PROVIDER in + claude|anthropic) + if [[ -z "$ANTHROPIC_API_KEY" ]]; then + echo -e "${YELLOW}[WARN]${NC} ANTHROPIC_API_KEY is not set." + fi + ;; + openai) + if [[ -z "$OPENAI_API_KEY" ]]; then + echo -e "${YELLOW}[WARN]${NC} OPENAI_API_KEY is not set." + fi + ;; + gemini|google) + if [[ -z "$GOOGLE_API_KEY" ]]; then + echo -e "${YELLOW}[WARN]${NC} GOOGLE_API_KEY is not set." + fi + ;; +esac + +echo -e "${GREEN}[INFO]${NC} Starting pipeline..." +echo "" + +# Run the pipeline +uv run python run_pipeline_json.py \ + --input "$INPUT_JSON" \ + --output-dir "$OUTPUT_DIR" \ + --provider "$PROVIDER" \ + --model "$MODEL" \ + --domain "$DOMAIN" \ + $EXTRA_ARGS + +echo "" +echo -e "${GREEN}[INFO]${NC} Pipeline completed. Results saved to: $OUTPUT_DIR/" +echo "" +echo "To upload results to Notion:" +echo " python upload_to_notion_from_json.py $OUTPUT_DIR" diff --git a/run_pipeline_json.py b/run_pipeline_json.py index d8fef9c..b282e46 100644 --- a/run_pipeline_json.py +++ b/run_pipeline_json.py @@ -576,6 +576,7 @@ def main(): parser.add_argument("--max-workers", type=int, default=3, help="Maximum number of parallel workers (default: 3)") parser.add_argument("--randomize-style", action="store_true", default=True, help="Randomize HTML table styles (fonts, colors) for diversity (default: True)") parser.add_argument("--no-randomize-style", dest="randomize_style", action="store_false", help="Disable style randomization") + parser.add_argument("--limit", type=int, help="Limit number of entries to process (for testing)") args = parser.parse_args() @@ -596,6 +597,11 @@ def main(): print("Error: Input JSON must be a list of pairs.") return + # Apply limit if specified + if args.limit: + input_data = input_data[:args.limit] + print(f"Limited to {len(input_data)} entries") + data_root = Path(args.data_root) output_dir = Path(args.output_dir) diff --git a/run_regenerate_qa.sh b/run_regenerate_qa.sh new file mode 100755 index 0000000..58aa98d --- /dev/null +++ b/run_regenerate_qa.sh @@ -0,0 +1,130 @@ +#!/bin/bash + +# ============================================================================== +# TableMagnifier - QA Regeneration Script +# ============================================================================== +# +# 기존 synthetic 테이블에서 QA를 재생성합니다. +# output_public은 제외됩니다. +# +# Usage: +# ./run_regenerate_qa.sh [OPTIONS] +# +# Examples: +# ./run_regenerate_qa.sh --all # 모든 도메인 +# ./run_regenerate_qa.sh --domain business # 특정 도메인 +# ./run_regenerate_qa.sh --domain business finance # 여러 도메인 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +# Colors +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' + +echo_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +echo_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +echo_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +show_help() { + cat << EOF +Usage: $0 [OPTIONS] + +기존 synthetic 테이블에서 QA를 재생성합니다. + +Options: + --domain DOMAIN [DOMAIN ...] 재생성할 도메인(들) (business, finance, academic, medical) + --all 모든 도메인 재생성 (output_public 제외) + --provider PROVIDER LLM 제공자 (claude, openai, gemini) (default: claude) + --model MODEL 모델 이름 (default: claude-sonnet-4-5) + --no-long-sequence long_sequence QA 생성 스킵 + --limit N 처리할 최대 entry 수 (테스트용) + --dry-run 실제 재생성 없이 확인만 + -h, --help 도움말 표시 + +Examples: + # 모든 도메인 재생성 + $0 --all + + # business 도메인만 + $0 --domain business + + # OpenAI 사용 + $0 --domain business --provider openai --model gpt-4o + + # 테스트 (5개만) + $0 --domain business --limit 5 + + # Dry run + $0 --all --dry-run +EOF +} + +# Check for required arguments +if [[ $# -eq 0 ]]; then + show_help + exit 1 +fi + +# Check for help flag +for arg in "$@"; do + if [[ "$arg" == "-h" ]] || [[ "$arg" == "--help" ]]; then + show_help + exit 0 + fi +done + +# Check API keys +check_api_keys() { + local provider="$1" + case $provider in + claude|anthropic) + if [[ -z "$ANTHROPIC_API_KEY" ]]; then + echo_warn "ANTHROPIC_API_KEY is not set" + fi + ;; + openai) + if [[ -z "$OPENAI_API_KEY" ]]; then + echo_warn "OPENAI_API_KEY is not set" + fi + ;; + gemini|google) + if [[ -z "$GOOGLE_API_KEY" ]]; then + echo_warn "GOOGLE_API_KEY is not set" + fi + ;; + esac +} + +# Parse provider from arguments +PROVIDER="claude" +for i in "${!@}"; do + if [[ "${!i}" == "--provider" ]]; then + next=$((i + 1)) + PROVIDER="${!next}" + break + fi +done + +check_api_keys "$PROVIDER" + +echo "==============================================" +echo " TableMagnifier - QA Regeneration" +echo "==============================================" +echo "" + +# Run the regeneration script +uv run python regenerate_qa.py "$@" diff --git a/single_image_json_list/single_table_academic_input.json b/single_image_json_list/single_table_academic_input.json new file mode 100644 index 0000000..483318f --- /dev/null +++ b/single_image_json_list/single_table_academic_input.json @@ -0,0 +1,1602 @@ +[ + { + "index": 0, + "pair_id": "A_origin_0_A_table_0", + "image_paths": [ + "data/Academic/Table/A_origin_0/A_table_0.png" + ], + "domain": "Academic", + "origin": "A_origin_0", + "table_id": "A_table_0" + }, + { + "index": 1, + "pair_id": "A_origin_0_A_table_1", + "image_paths": [ + "data/Academic/Table/A_origin_0/A_table_1.png" + ], + "domain": "Academic", + "origin": "A_origin_0", + "table_id": "A_table_1" + }, + { + "index": 2, + "pair_id": "A_origin_0_A_table_2", + "image_paths": [ + "data/Academic/Table/A_origin_0/A_table_2.png" + ], + "domain": "Academic", + "origin": "A_origin_0", + "table_id": "A_table_2" + }, + { + "index": 3, + "pair_id": "A_origin_0_A_table_3", + "image_paths": [ + "data/Academic/Table/A_origin_0/A_table_3.png" + ], + "domain": "Academic", + "origin": "A_origin_0", + "table_id": "A_table_3" + }, + { + "index": 4, + "pair_id": "A_origin_0_A_table_4", + "image_paths": [ + "data/Academic/Table/A_origin_0/A_table_4.png" + ], + "domain": "Academic", + "origin": "A_origin_0", + "table_id": "A_table_4" + }, + { + "index": 5, + "pair_id": "A_origin_1_A_table_5_0", + "image_paths": [ + "data/Academic/Table/A_origin_1/A_table_5_0.png" + ], + "domain": "Academic", + "origin": "A_origin_1", + "table_id": "A_table_5_0" + }, + { + "index": 6, + "pair_id": "A_origin_1_A_table_5_1", + "image_paths": [ + "data/Academic/Table/A_origin_1/A_table_5_1.png" + ], + "domain": "Academic", + "origin": "A_origin_1", + "table_id": "A_table_5_1" + }, + { + "index": 7, + "pair_id": "A_origin_1_A_table_6_0", + "image_paths": [ + "data/Academic/Table/A_origin_1/A_table_6_0.png" + ], + "domain": "Academic", + "origin": "A_origin_1", + "table_id": "A_table_6_0" + }, + { + "index": 8, + "pair_id": "A_origin_1_A_table_6_1", + "image_paths": [ + "data/Academic/Table/A_origin_1/A_table_6_1.png" + ], + "domain": "Academic", + "origin": "A_origin_1", + "table_id": "A_table_6_1" + }, + { + "index": 9, + "pair_id": "A_origin_1_A_table_7_0", + "image_paths": [ + "data/Academic/Table/A_origin_1/A_table_7_0.png" + ], + "domain": "Academic", + "origin": "A_origin_1", + "table_id": "A_table_7_0" + }, + { + "index": 10, + "pair_id": "A_origin_1_A_table_7_1", + "image_paths": [ + "data/Academic/Table/A_origin_1/A_table_7_1.png" + ], + "domain": "Academic", + "origin": "A_origin_1", + "table_id": "A_table_7_1" + }, + { + "index": 11, + "pair_id": "A_origin_10_A_table_28", + "image_paths": [ + "data/Academic/Table/A_origin_10/A_table_28.png" + ], + "domain": "Academic", + "origin": "A_origin_10", + "table_id": "A_table_28" + }, + { + "index": 12, + "pair_id": "A_origin_10_A_table_29", + "image_paths": [ + "data/Academic/Table/A_origin_10/A_table_29.png" + ], + "domain": "Academic", + "origin": "A_origin_10", + "table_id": "A_table_29" + }, + { + "index": 13, + "pair_id": "A_origin_11_A_table_30", + "image_paths": [ + "data/Academic/Table/A_origin_11/A_table_30.png" + ], + "domain": "Academic", + "origin": "A_origin_11", + "table_id": "A_table_30" + }, + { + "index": 14, + "pair_id": "A_origin_11_A_table_31", + "image_paths": [ + "data/Academic/Table/A_origin_11/A_table_31.png" + ], + "domain": "Academic", + "origin": "A_origin_11", + "table_id": "A_table_31" + }, + { + "index": 15, + "pair_id": "A_origin_12_A_table_32", + "image_paths": [ + "data/Academic/Table/A_origin_12/A_table_32.png" + ], + "domain": "Academic", + "origin": "A_origin_12", + "table_id": "A_table_32" + }, + { + "index": 16, + "pair_id": "A_origin_12_A_table_33", + "image_paths": [ + "data/Academic/Table/A_origin_12/A_table_33.png" + ], + "domain": "Academic", + "origin": "A_origin_12", + "table_id": "A_table_33" + }, + { + "index": 17, + "pair_id": "A_origin_12_A_table_34", + "image_paths": [ + "data/Academic/Table/A_origin_12/A_table_34.png" + ], + "domain": "Academic", + "origin": "A_origin_12", + "table_id": "A_table_34" + }, + { + "index": 18, + "pair_id": "A_origin_13_A_table_35", + "image_paths": [ + "data/Academic/Table/A_origin_13/A_table_35.png" + ], + "domain": "Academic", + "origin": "A_origin_13", + "table_id": "A_table_35" + }, + { + "index": 19, + "pair_id": "A_origin_13_A_table_36", + "image_paths": [ + "data/Academic/Table/A_origin_13/A_table_36.png" + ], + "domain": "Academic", + "origin": "A_origin_13", + "table_id": "A_table_36" + }, + { + "index": 20, + "pair_id": "A_origin_13_A_table_37", + "image_paths": [ + "data/Academic/Table/A_origin_13/A_table_37.png" + ], + "domain": "Academic", + "origin": "A_origin_13", + "table_id": "A_table_37" + }, + { + "index": 21, + "pair_id": "A_origin_13_A_table_38", + "image_paths": [ + "data/Academic/Table/A_origin_13/A_table_38.png" + ], + "domain": "Academic", + "origin": "A_origin_13", + "table_id": "A_table_38" + }, + { + "index": 22, + "pair_id": "A_origin_14_A_table_39", + "image_paths": [ + "data/Academic/Table/A_origin_14/A_table_39.png" + ], + "domain": "Academic", + "origin": "A_origin_14", + "table_id": "A_table_39" + }, + { + "index": 23, + "pair_id": "A_origin_14_A_table_40", + "image_paths": [ + "data/Academic/Table/A_origin_14/A_table_40.png" + ], + "domain": "Academic", + "origin": "A_origin_14", + "table_id": "A_table_40" + }, + { + "index": 24, + "pair_id": "A_origin_15_A_table_41", + "image_paths": [ + "data/Academic/Table/A_origin_15/A_table_41.png" + ], + "domain": "Academic", + "origin": "A_origin_15", + "table_id": "A_table_41" + }, + { + "index": 25, + "pair_id": "A_origin_16_A_table_42", + "image_paths": [ + "data/Academic/Table/A_origin_16/A_table_42.png" + ], + "domain": "Academic", + "origin": "A_origin_16", + "table_id": "A_table_42" + }, + { + "index": 26, + "pair_id": "A_origin_16_A_table_43", + "image_paths": [ + "data/Academic/Table/A_origin_16/A_table_43.png" + ], + "domain": "Academic", + "origin": "A_origin_16", + "table_id": "A_table_43" + }, + { + "index": 27, + "pair_id": "A_origin_17_A_table_44", + "image_paths": [ + "data/Academic/Table/A_origin_17/A_table_44.png" + ], + "domain": "Academic", + "origin": "A_origin_17", + "table_id": "A_table_44" + }, + { + "index": 28, + "pair_id": "A_origin_17_A_table_45", + "image_paths": [ + "data/Academic/Table/A_origin_17/A_table_45.png" + ], + "domain": "Academic", + "origin": "A_origin_17", + "table_id": "A_table_45" + }, + { + "index": 29, + "pair_id": "A_origin_17_A_table_46", + "image_paths": [ + "data/Academic/Table/A_origin_17/A_table_46.png" + ], + "domain": "Academic", + "origin": "A_origin_17", + "table_id": "A_table_46" + }, + { + "index": 30, + "pair_id": "A_origin_18_A_table_47", + "image_paths": [ + "data/Academic/Table/A_origin_18/A_table_47.png" + ], + "domain": "Academic", + "origin": "A_origin_18", + "table_id": "A_table_47" + }, + { + "index": 31, + "pair_id": "A_origin_18_A_table_48", + "image_paths": [ + "data/Academic/Table/A_origin_18/A_table_48.png" + ], + "domain": "Academic", + "origin": "A_origin_18", + "table_id": "A_table_48" + }, + { + "index": 32, + "pair_id": "A_origin_18_A_table_49", + "image_paths": [ + "data/Academic/Table/A_origin_18/A_table_49.png" + ], + "domain": "Academic", + "origin": "A_origin_18", + "table_id": "A_table_49" + }, + { + "index": 33, + "pair_id": "A_origin_19_A_table_50", + "image_paths": [ + "data/Academic/Table/A_origin_19/A_table_50.png" + ], + "domain": "Academic", + "origin": "A_origin_19", + "table_id": "A_table_50" + }, + { + "index": 34, + "pair_id": "A_origin_2_A_table_8", + "image_paths": [ + "data/Academic/Table/A_origin_2/A_table_8.png" + ], + "domain": "Academic", + "origin": "A_origin_2", + "table_id": "A_table_8" + }, + { + "index": 35, + "pair_id": "A_origin_2_A_table_9", + "image_paths": [ + "data/Academic/Table/A_origin_2/A_table_9.png" + ], + "domain": "Academic", + "origin": "A_origin_2", + "table_id": "A_table_9" + }, + { + "index": 36, + "pair_id": "A_origin_20_A_table_51", + "image_paths": [ + "data/Academic/Table/A_origin_20/A_table_51.png" + ], + "domain": "Academic", + "origin": "A_origin_20", + "table_id": "A_table_51" + }, + { + "index": 37, + "pair_id": "A_origin_20_A_table_52", + "image_paths": [ + "data/Academic/Table/A_origin_20/A_table_52.png" + ], + "domain": "Academic", + "origin": "A_origin_20", + "table_id": "A_table_52" + }, + { + "index": 38, + "pair_id": "A_origin_21_A_table_53", + "image_paths": [ + "data/Academic/Table/A_origin_21/A_table_53.png" + ], + "domain": "Academic", + "origin": "A_origin_21", + "table_id": "A_table_53" + }, + { + "index": 39, + "pair_id": "A_origin_21_A_table_54", + "image_paths": [ + "data/Academic/Table/A_origin_21/A_table_54.png" + ], + "domain": "Academic", + "origin": "A_origin_21", + "table_id": "A_table_54" + }, + { + "index": 40, + "pair_id": "A_origin_22_A_table_55", + "image_paths": [ + "data/Academic/Table/A_origin_22/A_table_55.png" + ], + "domain": "Academic", + "origin": "A_origin_22", + "table_id": "A_table_55" + }, + { + "index": 41, + "pair_id": "A_origin_23_A_table_56", + "image_paths": [ + "data/Academic/Table/A_origin_23/A_table_56.png" + ], + "domain": "Academic", + "origin": "A_origin_23", + "table_id": "A_table_56" + }, + { + "index": 42, + "pair_id": "A_origin_24_A_table_57", + "image_paths": [ + "data/Academic/Table/A_origin_24/A_table_57.png" + ], + "domain": "Academic", + "origin": "A_origin_24", + "table_id": "A_table_57" + }, + { + "index": 43, + "pair_id": "A_origin_25_A_table_58", + "image_paths": [ + "data/Academic/Table/A_origin_25/A_table_58.png" + ], + "domain": "Academic", + "origin": "A_origin_25", + "table_id": "A_table_58" + }, + { + "index": 44, + "pair_id": "A_origin_26_A_table_59_01", + "image_paths": [ + "data/Academic/Table/A_origin_26/A_table_59_01.png" + ], + "domain": "Academic", + "origin": "A_origin_26", + "table_id": "A_table_59_01" + }, + { + "index": 45, + "pair_id": "A_origin_26_A_table_59_02", + "image_paths": [ + "data/Academic/Table/A_origin_26/A_table_59_02.png" + ], + "domain": "Academic", + "origin": "A_origin_26", + "table_id": "A_table_59_02" + }, + { + "index": 46, + "pair_id": "A_origin_26_A_table_60", + "image_paths": [ + "data/Academic/Table/A_origin_26/A_table_60.png" + ], + "domain": "Academic", + "origin": "A_origin_26", + "table_id": "A_table_60" + }, + { + "index": 47, + "pair_id": "A_origin_26_A_table_61", + "image_paths": [ + "data/Academic/Table/A_origin_26/A_table_61.png" + ], + "domain": "Academic", + "origin": "A_origin_26", + "table_id": "A_table_61" + }, + { + "index": 48, + "pair_id": "A_origin_27_A_table_62_01", + "image_paths": [ + "data/Academic/Table/A_origin_27/A_table_62_01.png" + ], + "domain": "Academic", + "origin": "A_origin_27", + "table_id": "A_table_62_01" + }, + { + "index": 49, + "pair_id": "A_origin_27_A_table_62_02", + "image_paths": [ + "data/Academic/Table/A_origin_27/A_table_62_02.png" + ], + "domain": "Academic", + "origin": "A_origin_27", + "table_id": "A_table_62_02" + }, + { + "index": 50, + "pair_id": "A_origin_28_A_table_63_01", + "image_paths": [ + "data/Academic/Table/A_origin_28/A_table_63_01.png" + ], + "domain": "Academic", + "origin": "A_origin_28", + "table_id": "A_table_63_01" + }, + { + "index": 51, + "pair_id": "A_origin_28_A_table_63_02", + "image_paths": [ + "data/Academic/Table/A_origin_28/A_table_63_02.png" + ], + "domain": "Academic", + "origin": "A_origin_28", + "table_id": "A_table_63_02" + }, + { + "index": 52, + "pair_id": "A_origin_28_A_table_64", + "image_paths": [ + "data/Academic/Table/A_origin_28/A_table_64.png" + ], + "domain": "Academic", + "origin": "A_origin_28", + "table_id": "A_table_64" + }, + { + "index": 53, + "pair_id": "A_origin_29_A_table_65", + "image_paths": [ + "data/Academic/Table/A_origin_29/A_table_65.png" + ], + "domain": "Academic", + "origin": "A_origin_29", + "table_id": "A_table_65" + }, + { + "index": 54, + "pair_id": "A_origin_3_A_table_10", + "image_paths": [ + "data/Academic/Table/A_origin_3/A_table_10.png" + ], + "domain": "Academic", + "origin": "A_origin_3", + "table_id": "A_table_10" + }, + { + "index": 55, + "pair_id": "A_origin_3_A_table_11", + "image_paths": [ + "data/Academic/Table/A_origin_3/A_table_11.png" + ], + "domain": "Academic", + "origin": "A_origin_3", + "table_id": "A_table_11" + }, + { + "index": 56, + "pair_id": "A_origin_30_A_table_66_01", + "image_paths": [ + "data/Academic/Table/A_origin_30/A_table_66_01.png" + ], + "domain": "Academic", + "origin": "A_origin_30", + "table_id": "A_table_66_01" + }, + { + "index": 57, + "pair_id": "A_origin_30_A_table_66_02", + "image_paths": [ + "data/Academic/Table/A_origin_30/A_table_66_02.png" + ], + "domain": "Academic", + "origin": "A_origin_30", + "table_id": "A_table_66_02" + }, + { + "index": 58, + "pair_id": "A_origin_31_A_table_67_01", + "image_paths": [ + "data/Academic/Table/A_origin_31/A_table_67_01.png" + ], + "domain": "Academic", + "origin": "A_origin_31", + "table_id": "A_table_67_01" + }, + { + "index": 59, + "pair_id": "A_origin_31_A_table_67_02", + "image_paths": [ + "data/Academic/Table/A_origin_31/A_table_67_02.png" + ], + "domain": "Academic", + "origin": "A_origin_31", + "table_id": "A_table_67_02" + }, + { + "index": 60, + "pair_id": "A_origin_32_A_table_68_01", + "image_paths": [ + "data/Academic/Table/A_origin_32/A_table_68_01.png" + ], + "domain": "Academic", + "origin": "A_origin_32", + "table_id": "A_table_68_01" + }, + { + "index": 61, + "pair_id": "A_origin_32_A_table_68_02", + "image_paths": [ + "data/Academic/Table/A_origin_32/A_table_68_02.png" + ], + "domain": "Academic", + "origin": "A_origin_32", + "table_id": "A_table_68_02" + }, + { + "index": 62, + "pair_id": "A_origin_32_A_table_69_01", + "image_paths": [ + "data/Academic/Table/A_origin_32/A_table_69_01.png" + ], + "domain": "Academic", + "origin": "A_origin_32", + "table_id": "A_table_69_01" + }, + { + "index": 63, + "pair_id": "A_origin_32_A_table_69_02", + "image_paths": [ + "data/Academic/Table/A_origin_32/A_table_69_02.png" + ], + "domain": "Academic", + "origin": "A_origin_32", + "table_id": "A_table_69_02" + }, + { + "index": 64, + "pair_id": "A_origin_33_A_table_70_01", + "image_paths": [ + "data/Academic/Table/A_origin_33/A_table_70_01.png" + ], + "domain": "Academic", + "origin": "A_origin_33", + "table_id": "A_table_70_01" + }, + { + "index": 65, + "pair_id": "A_origin_33_A_table_70_02", + "image_paths": [ + "data/Academic/Table/A_origin_33/A_table_70_02.png" + ], + "domain": "Academic", + "origin": "A_origin_33", + "table_id": "A_table_70_02" + }, + { + "index": 66, + "pair_id": "A_origin_34_A_table_71_01", + "image_paths": [ + "data/Academic/Table/A_origin_34/A_table_71_01.png" + ], + "domain": "Academic", + "origin": "A_origin_34", + "table_id": "A_table_71_01" + }, + { + "index": 67, + "pair_id": "A_origin_34_A_table_71_02", + "image_paths": [ + "data/Academic/Table/A_origin_34/A_table_71_02.png" + ], + "domain": "Academic", + "origin": "A_origin_34", + "table_id": "A_table_71_02" + }, + { + "index": 68, + "pair_id": "A_origin_35_A_table_72", + "image_paths": [ + "data/Academic/Table/A_origin_35/A_table_72.png" + ], + "domain": "Academic", + "origin": "A_origin_35", + "table_id": "A_table_72" + }, + { + "index": 69, + "pair_id": "A_origin_36_A_table_73", + "image_paths": [ + "data/Academic/Table/A_origin_36/A_table_73.png" + ], + "domain": "Academic", + "origin": "A_origin_36", + "table_id": "A_table_73" + }, + { + "index": 70, + "pair_id": "A_origin_36_A_table_74", + "image_paths": [ + "data/Academic/Table/A_origin_36/A_table_74.png" + ], + "domain": "Academic", + "origin": "A_origin_36", + "table_id": "A_table_74" + }, + { + "index": 71, + "pair_id": "A_origin_36_A_table_75", + "image_paths": [ + "data/Academic/Table/A_origin_36/A_table_75.png" + ], + "domain": "Academic", + "origin": "A_origin_36", + "table_id": "A_table_75" + }, + { + "index": 72, + "pair_id": "A_origin_37_A_table_76_01", + "image_paths": [ + "data/Academic/Table/A_origin_37/A_table_76_01.png" + ], + "domain": "Academic", + "origin": "A_origin_37", + "table_id": "A_table_76_01" + }, + { + "index": 73, + "pair_id": "A_origin_37_A_table_76_02", + "image_paths": [ + "data/Academic/Table/A_origin_37/A_table_76_02.png" + ], + "domain": "Academic", + "origin": "A_origin_37", + "table_id": "A_table_76_02" + }, + { + "index": 74, + "pair_id": "A_origin_38_A_table_77", + "image_paths": [ + "data/Academic/Table/A_origin_38/A_table_77.png" + ], + "domain": "Academic", + "origin": "A_origin_38", + "table_id": "A_table_77" + }, + { + "index": 75, + "pair_id": "A_origin_38_A_table_78", + "image_paths": [ + "data/Academic/Table/A_origin_38/A_table_78.png" + ], + "domain": "Academic", + "origin": "A_origin_38", + "table_id": "A_table_78" + }, + { + "index": 76, + "pair_id": "A_origin_39_A_table_79", + "image_paths": [ + "data/Academic/Table/A_origin_39/A_table_79.png" + ], + "domain": "Academic", + "origin": "A_origin_39", + "table_id": "A_table_79" + }, + { + "index": 77, + "pair_id": "A_origin_39_A_table_80_01", + "image_paths": [ + "data/Academic/Table/A_origin_39/A_table_80_01.png" + ], + "domain": "Academic", + "origin": "A_origin_39", + "table_id": "A_table_80_01" + }, + { + "index": 78, + "pair_id": "A_origin_39_A_table_80_02", + "image_paths": [ + "data/Academic/Table/A_origin_39/A_table_80_02.png" + ], + "domain": "Academic", + "origin": "A_origin_39", + "table_id": "A_table_80_02" + }, + { + "index": 79, + "pair_id": "A_origin_4_A_table_12", + "image_paths": [ + "data/Academic/Table/A_origin_4/A_table_12.png" + ], + "domain": "Academic", + "origin": "A_origin_4", + "table_id": "A_table_12" + }, + { + "index": 80, + "pair_id": "A_origin_4_A_table_13", + "image_paths": [ + "data/Academic/Table/A_origin_4/A_table_13.png" + ], + "domain": "Academic", + "origin": "A_origin_4", + "table_id": "A_table_13" + }, + { + "index": 81, + "pair_id": "A_origin_40_A_table_81", + "image_paths": [ + "data/Academic/Table/A_origin_40/A_table_81.png" + ], + "domain": "Academic", + "origin": "A_origin_40", + "table_id": "A_table_81" + }, + { + "index": 82, + "pair_id": "A_origin_40_A_table_82", + "image_paths": [ + "data/Academic/Table/A_origin_40/A_table_82.png" + ], + "domain": "Academic", + "origin": "A_origin_40", + "table_id": "A_table_82" + }, + { + "index": 83, + "pair_id": "A_origin_40_A_table_83", + "image_paths": [ + "data/Academic/Table/A_origin_40/A_table_83.png" + ], + "domain": "Academic", + "origin": "A_origin_40", + "table_id": "A_table_83" + }, + { + "index": 84, + "pair_id": "A_origin_41_A_table_84", + "image_paths": [ + "data/Academic/Table/A_origin_41/A_table_84.png" + ], + "domain": "Academic", + "origin": "A_origin_41", + "table_id": "A_table_84" + }, + { + "index": 85, + "pair_id": "A_origin_41_A_table_85", + "image_paths": [ + "data/Academic/Table/A_origin_41/A_table_85.png" + ], + "domain": "Academic", + "origin": "A_origin_41", + "table_id": "A_table_85" + }, + { + "index": 86, + "pair_id": "A_origin_42_A_table_86", + "image_paths": [ + "data/Academic/Table/A_origin_42/A_table_86.png" + ], + "domain": "Academic", + "origin": "A_origin_42", + "table_id": "A_table_86" + }, + { + "index": 87, + "pair_id": "A_origin_42_A_table_87", + "image_paths": [ + "data/Academic/Table/A_origin_42/A_table_87.png" + ], + "domain": "Academic", + "origin": "A_origin_42", + "table_id": "A_table_87" + }, + { + "index": 88, + "pair_id": "A_origin_43_A_table_88", + "image_paths": [ + "data/Academic/Table/A_origin_43/A_table_88.png" + ], + "domain": "Academic", + "origin": "A_origin_43", + "table_id": "A_table_88" + }, + { + "index": 89, + "pair_id": "A_origin_43_A_table_89", + "image_paths": [ + "data/Academic/Table/A_origin_43/A_table_89.png" + ], + "domain": "Academic", + "origin": "A_origin_43", + "table_id": "A_table_89" + }, + { + "index": 90, + "pair_id": "A_origin_43_A_table_90", + "image_paths": [ + "data/Academic/Table/A_origin_43/A_table_90.png" + ], + "domain": "Academic", + "origin": "A_origin_43", + "table_id": "A_table_90" + }, + { + "index": 91, + "pair_id": "A_origin_44_A_table_91", + "image_paths": [ + "data/Academic/Table/A_origin_44/A_table_91.png" + ], + "domain": "Academic", + "origin": "A_origin_44", + "table_id": "A_table_91" + }, + { + "index": 92, + "pair_id": "A_origin_45_A_table_92", + "image_paths": [ + "data/Academic/Table/A_origin_45/A_table_92.png" + ], + "domain": "Academic", + "origin": "A_origin_45", + "table_id": "A_table_92" + }, + { + "index": 93, + "pair_id": "A_origin_45_A_table_93", + "image_paths": [ + "data/Academic/Table/A_origin_45/A_table_93.png" + ], + "domain": "Academic", + "origin": "A_origin_45", + "table_id": "A_table_93" + }, + { + "index": 94, + "pair_id": "A_origin_45_A_table_94", + "image_paths": [ + "data/Academic/Table/A_origin_45/A_table_94.png" + ], + "domain": "Academic", + "origin": "A_origin_45", + "table_id": "A_table_94" + }, + { + "index": 95, + "pair_id": "A_origin_45_A_table_95", + "image_paths": [ + "data/Academic/Table/A_origin_45/A_table_95.png" + ], + "domain": "Academic", + "origin": "A_origin_45", + "table_id": "A_table_95" + }, + { + "index": 96, + "pair_id": "A_origin_46_A_table_96", + "image_paths": [ + "data/Academic/Table/A_origin_46/A_table_96.png" + ], + "domain": "Academic", + "origin": "A_origin_46", + "table_id": "A_table_96" + }, + { + "index": 97, + "pair_id": "A_origin_47_A_table_97", + "image_paths": [ + "data/Academic/Table/A_origin_47/A_table_97.png" + ], + "domain": "Academic", + "origin": "A_origin_47", + "table_id": "A_table_97" + }, + { + "index": 98, + "pair_id": "A_origin_47_A_table_98", + "image_paths": [ + "data/Academic/Table/A_origin_47/A_table_98.png" + ], + "domain": "Academic", + "origin": "A_origin_47", + "table_id": "A_table_98" + }, + { + "index": 99, + "pair_id": "A_origin_48_A_table_99", + "image_paths": [ + "data/Academic/Table/A_origin_48/A_table_99.png" + ], + "domain": "Academic", + "origin": "A_origin_48", + "table_id": "A_table_99" + }, + { + "index": 100, + "pair_id": "A_origin_49_A_table_100", + "image_paths": [ + "data/Academic/Table/A_origin_49/A_table_100.png" + ], + "domain": "Academic", + "origin": "A_origin_49", + "table_id": "A_table_100" + }, + { + "index": 101, + "pair_id": "A_origin_49_A_table_101", + "image_paths": [ + "data/Academic/Table/A_origin_49/A_table_101.png" + ], + "domain": "Academic", + "origin": "A_origin_49", + "table_id": "A_table_101" + }, + { + "index": 102, + "pair_id": "A_origin_5_A_table_14", + "image_paths": [ + "data/Academic/Table/A_origin_5/A_table_14.png" + ], + "domain": "Academic", + "origin": "A_origin_5", + "table_id": "A_table_14" + }, + { + "index": 103, + "pair_id": "A_origin_5_A_table_15", + "image_paths": [ + "data/Academic/Table/A_origin_5/A_table_15.png" + ], + "domain": "Academic", + "origin": "A_origin_5", + "table_id": "A_table_15" + }, + { + "index": 104, + "pair_id": "A_origin_5_A_table_16", + "image_paths": [ + "data/Academic/Table/A_origin_5/A_table_16.png" + ], + "domain": "Academic", + "origin": "A_origin_5", + "table_id": "A_table_16" + }, + { + "index": 105, + "pair_id": "A_origin_50_A_table_102", + "image_paths": [ + "data/Academic/Table/A_origin_50/A_table_102.png" + ], + "domain": "Academic", + "origin": "A_origin_50", + "table_id": "A_table_102" + }, + { + "index": 106, + "pair_id": "A_origin_51_A_table_103", + "image_paths": [ + "data/Academic/Table/A_origin_51/A_table_103.png" + ], + "domain": "Academic", + "origin": "A_origin_51", + "table_id": "A_table_103" + }, + { + "index": 107, + "pair_id": "A_origin_51_A_table_104", + "image_paths": [ + "data/Academic/Table/A_origin_51/A_table_104.png" + ], + "domain": "Academic", + "origin": "A_origin_51", + "table_id": "A_table_104" + }, + { + "index": 108, + "pair_id": "A_origin_52_A_table_105", + "image_paths": [ + "data/Academic/Table/A_origin_52/A_table_105.png" + ], + "domain": "Academic", + "origin": "A_origin_52", + "table_id": "A_table_105" + }, + { + "index": 109, + "pair_id": "A_origin_53_A_table_106_01", + "image_paths": [ + "data/Academic/Table/A_origin_53/A_table_106_01.png" + ], + "domain": "Academic", + "origin": "A_origin_53", + "table_id": "A_table_106_01" + }, + { + "index": 110, + "pair_id": "A_origin_53_A_table_106_02", + "image_paths": [ + "data/Academic/Table/A_origin_53/A_table_106_02.png" + ], + "domain": "Academic", + "origin": "A_origin_53", + "table_id": "A_table_106_02" + }, + { + "index": 111, + "pair_id": "A_origin_53_A_table_107_01", + "image_paths": [ + "data/Academic/Table/A_origin_53/A_table_107_01.png" + ], + "domain": "Academic", + "origin": "A_origin_53", + "table_id": "A_table_107_01" + }, + { + "index": 112, + "pair_id": "A_origin_53_A_table_107_02", + "image_paths": [ + "data/Academic/Table/A_origin_53/A_table_107_02.png" + ], + "domain": "Academic", + "origin": "A_origin_53", + "table_id": "A_table_107_02" + }, + { + "index": 113, + "pair_id": "A_origin_53_A_table_108", + "image_paths": [ + "data/Academic/Table/A_origin_53/A_table_108.png" + ], + "domain": "Academic", + "origin": "A_origin_53", + "table_id": "A_table_108" + }, + { + "index": 114, + "pair_id": "A_origin_54_A_table_109", + "image_paths": [ + "data/Academic/Table/A_origin_54/A_table_109.png" + ], + "domain": "Academic", + "origin": "A_origin_54", + "table_id": "A_table_109" + }, + { + "index": 115, + "pair_id": "A_origin_54_A_table_110", + "image_paths": [ + "data/Academic/Table/A_origin_54/A_table_110.png" + ], + "domain": "Academic", + "origin": "A_origin_54", + "table_id": "A_table_110" + }, + { + "index": 116, + "pair_id": "A_origin_55_A_table_111", + "image_paths": [ + "data/Academic/Table/A_origin_55/A_table_111.png" + ], + "domain": "Academic", + "origin": "A_origin_55", + "table_id": "A_table_111" + }, + { + "index": 117, + "pair_id": "A_origin_55_A_table_112", + "image_paths": [ + "data/Academic/Table/A_origin_55/A_table_112.png" + ], + "domain": "Academic", + "origin": "A_origin_55", + "table_id": "A_table_112" + }, + { + "index": 118, + "pair_id": "A_origin_56_A_table_113", + "image_paths": [ + "data/Academic/Table/A_origin_56/A_table_113.png" + ], + "domain": "Academic", + "origin": "A_origin_56", + "table_id": "A_table_113" + }, + { + "index": 119, + "pair_id": "A_origin_56_A_table_114", + "image_paths": [ + "data/Academic/Table/A_origin_56/A_table_114.png" + ], + "domain": "Academic", + "origin": "A_origin_56", + "table_id": "A_table_114" + }, + { + "index": 120, + "pair_id": "A_origin_56_A_table_115", + "image_paths": [ + "data/Academic/Table/A_origin_56/A_table_115.png" + ], + "domain": "Academic", + "origin": "A_origin_56", + "table_id": "A_table_115" + }, + { + "index": 121, + "pair_id": "A_origin_57_A_table_116", + "image_paths": [ + "data/Academic/Table/A_origin_57/A_table_116.png" + ], + "domain": "Academic", + "origin": "A_origin_57", + "table_id": "A_table_116" + }, + { + "index": 122, + "pair_id": "A_origin_57_A_table_117", + "image_paths": [ + "data/Academic/Table/A_origin_57/A_table_117.png" + ], + "domain": "Academic", + "origin": "A_origin_57", + "table_id": "A_table_117" + }, + { + "index": 123, + "pair_id": "A_origin_57_A_table_118", + "image_paths": [ + "data/Academic/Table/A_origin_57/A_table_118.png" + ], + "domain": "Academic", + "origin": "A_origin_57", + "table_id": "A_table_118" + }, + { + "index": 124, + "pair_id": "A_origin_57_A_table_119", + "image_paths": [ + "data/Academic/Table/A_origin_57/A_table_119.png" + ], + "domain": "Academic", + "origin": "A_origin_57", + "table_id": "A_table_119" + }, + { + "index": 125, + "pair_id": "A_origin_57_A_table_120", + "image_paths": [ + "data/Academic/Table/A_origin_57/A_table_120.png" + ], + "domain": "Academic", + "origin": "A_origin_57", + "table_id": "A_table_120" + }, + { + "index": 126, + "pair_id": "A_origin_58_A_table_121", + "image_paths": [ + "data/Academic/Table/A_origin_58/A_table_121.png" + ], + "domain": "Academic", + "origin": "A_origin_58", + "table_id": "A_table_121" + }, + { + "index": 127, + "pair_id": "A_origin_58_A_table_122", + "image_paths": [ + "data/Academic/Table/A_origin_58/A_table_122.png" + ], + "domain": "Academic", + "origin": "A_origin_58", + "table_id": "A_table_122" + }, + { + "index": 128, + "pair_id": "A_origin_58_A_table_123_01", + "image_paths": [ + "data/Academic/Table/A_origin_58/A_table_123_01.png" + ], + "domain": "Academic", + "origin": "A_origin_58", + "table_id": "A_table_123_01" + }, + { + "index": 129, + "pair_id": "A_origin_58_A_table_123_02", + "image_paths": [ + "data/Academic/Table/A_origin_58/A_table_123_02.png" + ], + "domain": "Academic", + "origin": "A_origin_58", + "table_id": "A_table_123_02" + }, + { + "index": 130, + "pair_id": "A_origin_59_A_table_124_01", + "image_paths": [ + "data/Academic/Table/A_origin_59/A_table_124_01.png" + ], + "domain": "Academic", + "origin": "A_origin_59", + "table_id": "A_table_124_01" + }, + { + "index": 131, + "pair_id": "A_origin_59_A_table_124_02", + "image_paths": [ + "data/Academic/Table/A_origin_59/A_table_124_02.png" + ], + "domain": "Academic", + "origin": "A_origin_59", + "table_id": "A_table_124_02" + }, + { + "index": 132, + "pair_id": "A_origin_6_A_table_18", + "image_paths": [ + "data/Academic/Table/A_origin_6/A_table_18.png" + ], + "domain": "Academic", + "origin": "A_origin_6", + "table_id": "A_table_18" + }, + { + "index": 133, + "pair_id": "A_origin_6_A_table_19", + "image_paths": [ + "data/Academic/Table/A_origin_6/A_table_19.png" + ], + "domain": "Academic", + "origin": "A_origin_6", + "table_id": "A_table_19" + }, + { + "index": 134, + "pair_id": "A_origin_6_A_table_8", + "image_paths": [ + "data/Academic/Table/A_origin_6/A_table_8.png" + ], + "domain": "Academic", + "origin": "A_origin_6", + "table_id": "A_table_8" + }, + { + "index": 135, + "pair_id": "A_origin_60_A_table_125_01", + "image_paths": [ + "data/Academic/Table/A_origin_60/A_table_125_01.png" + ], + "domain": "Academic", + "origin": "A_origin_60", + "table_id": "A_table_125_01" + }, + { + "index": 136, + "pair_id": "A_origin_60_A_table_125_02", + "image_paths": [ + "data/Academic/Table/A_origin_60/A_table_125_02.png" + ], + "domain": "Academic", + "origin": "A_origin_60", + "table_id": "A_table_125_02" + }, + { + "index": 137, + "pair_id": "A_origin_61_A_table_126", + "image_paths": [ + "data/Academic/Table/A_origin_61/A_table_126.png" + ], + "domain": "Academic", + "origin": "A_origin_61", + "table_id": "A_table_126" + }, + { + "index": 138, + "pair_id": "A_origin_62_A_table_127_01", + "image_paths": [ + "data/Academic/Table/A_origin_62/A_table_127_01.png" + ], + "domain": "Academic", + "origin": "A_origin_62", + "table_id": "A_table_127_01" + }, + { + "index": 139, + "pair_id": "A_origin_62_A_table_127_02", + "image_paths": [ + "data/Academic/Table/A_origin_62/A_table_127_02.png" + ], + "domain": "Academic", + "origin": "A_origin_62", + "table_id": "A_table_127_02" + }, + { + "index": 140, + "pair_id": "A_origin_63_A_table_128", + "image_paths": [ + "data/Academic/Table/A_origin_63/A_table_128.png" + ], + "domain": "Academic", + "origin": "A_origin_63", + "table_id": "A_table_128" + }, + { + "index": 141, + "pair_id": "A_origin_63_A_table_129", + "image_paths": [ + "data/Academic/Table/A_origin_63/A_table_129.png" + ], + "domain": "Academic", + "origin": "A_origin_63", + "table_id": "A_table_129" + }, + { + "index": 142, + "pair_id": "A_origin_64_A_table_130", + "image_paths": [ + "data/Academic/Table/A_origin_64/A_table_130.png" + ], + "domain": "Academic", + "origin": "A_origin_64", + "table_id": "A_table_130" + }, + { + "index": 143, + "pair_id": "A_origin_64_A_table_131", + "image_paths": [ + "data/Academic/Table/A_origin_64/A_table_131.png" + ], + "domain": "Academic", + "origin": "A_origin_64", + "table_id": "A_table_131" + }, + { + "index": 144, + "pair_id": "A_origin_64_A_table_132", + "image_paths": [ + "data/Academic/Table/A_origin_64/A_table_132.png" + ], + "domain": "Academic", + "origin": "A_origin_64", + "table_id": "A_table_132" + }, + { + "index": 145, + "pair_id": "A_origin_65_A_table_133", + "image_paths": [ + "data/Academic/Table/A_origin_65/A_table_133.png" + ], + "domain": "Academic", + "origin": "A_origin_65", + "table_id": "A_table_133" + }, + { + "index": 146, + "pair_id": "A_origin_65_A_table_134", + "image_paths": [ + "data/Academic/Table/A_origin_65/A_table_134.png" + ], + "domain": "Academic", + "origin": "A_origin_65", + "table_id": "A_table_134" + }, + { + "index": 147, + "pair_id": "A_origin_66_A_table_135", + "image_paths": [ + "data/Academic/Table/A_origin_66/A_table_135.png" + ], + "domain": "Academic", + "origin": "A_origin_66", + "table_id": "A_table_135" + }, + { + "index": 148, + "pair_id": "A_origin_66_A_table_136", + "image_paths": [ + "data/Academic/Table/A_origin_66/A_table_136.png" + ], + "domain": "Academic", + "origin": "A_origin_66", + "table_id": "A_table_136" + }, + { + "index": 149, + "pair_id": "A_origin_66_A_table_137", + "image_paths": [ + "data/Academic/Table/A_origin_66/A_table_137.png" + ], + "domain": "Academic", + "origin": "A_origin_66", + "table_id": "A_table_137" + }, + { + "index": 150, + "pair_id": "A_origin_66_A_table_138", + "image_paths": [ + "data/Academic/Table/A_origin_66/A_table_138.png" + ], + "domain": "Academic", + "origin": "A_origin_66", + "table_id": "A_table_138" + }, + { + "index": 151, + "pair_id": "A_origin_67_A_table_139", + "image_paths": [ + "data/Academic/Table/A_origin_67/A_table_139.png" + ], + "domain": "Academic", + "origin": "A_origin_67", + "table_id": "A_table_139" + }, + { + "index": 152, + "pair_id": "A_origin_7_A_table_20", + "image_paths": [ + "data/Academic/Table/A_origin_7/A_table_20.png" + ], + "domain": "Academic", + "origin": "A_origin_7", + "table_id": "A_table_20" + }, + { + "index": 153, + "pair_id": "A_origin_7_A_table_21", + "image_paths": [ + "data/Academic/Table/A_origin_7/A_table_21.png" + ], + "domain": "Academic", + "origin": "A_origin_7", + "table_id": "A_table_21" + }, + { + "index": 154, + "pair_id": "A_origin_8_A_table_22", + "image_paths": [ + "data/Academic/Table/A_origin_8/A_table_22.png" + ], + "domain": "Academic", + "origin": "A_origin_8", + "table_id": "A_table_22" + }, + { + "index": 155, + "pair_id": "A_origin_8_A_table_23", + "image_paths": [ + "data/Academic/Table/A_origin_8/A_table_23.png" + ], + "domain": "Academic", + "origin": "A_origin_8", + "table_id": "A_table_23" + }, + { + "index": 156, + "pair_id": "A_origin_8_A_table_24", + "image_paths": [ + "data/Academic/Table/A_origin_8/A_table_24.png" + ], + "domain": "Academic", + "origin": "A_origin_8", + "table_id": "A_table_24" + }, + { + "index": 157, + "pair_id": "A_origin_9_A_table_25", + "image_paths": [ + "data/Academic/Table/A_origin_9/A_table_25.png" + ], + "domain": "Academic", + "origin": "A_origin_9", + "table_id": "A_table_25" + }, + { + "index": 158, + "pair_id": "A_origin_9_A_table_26", + "image_paths": [ + "data/Academic/Table/A_origin_9/A_table_26.png" + ], + "domain": "Academic", + "origin": "A_origin_9", + "table_id": "A_table_26" + }, + { + "index": 159, + "pair_id": "A_origin_9_A_table_27", + "image_paths": [ + "data/Academic/Table/A_origin_9/A_table_27.png" + ], + "domain": "Academic", + "origin": "A_origin_9", + "table_id": "A_table_27" + } +] diff --git a/single_image_json_list/single_table_business_input.json b/single_image_json_list/single_table_business_input.json new file mode 100644 index 0000000..7309657 --- /dev/null +++ b/single_image_json_list/single_table_business_input.json @@ -0,0 +1,1492 @@ +[ + { + "index": 0, + "pair_id": "B_origin_0_B_table_0_0", + "image_paths": [ + "data/Business/Table/B_origin_0/B_table_0_0.png" + ], + "domain": "Business", + "origin": "B_origin_0", + "table_id": "B_table_0_0" + }, + { + "index": 1, + "pair_id": "B_origin_0_B_table_1_0", + "image_paths": [ + "data/Business/Table/B_origin_0/B_table_1_0.png" + ], + "domain": "Business", + "origin": "B_origin_0", + "table_id": "B_table_1_0" + }, + { + "index": 2, + "pair_id": "B_origin_0_B_table_2_0", + "image_paths": [ + "data/Business/Table/B_origin_0/B_table_2_0.png" + ], + "domain": "Business", + "origin": "B_origin_0", + "table_id": "B_table_2_0" + }, + { + "index": 3, + "pair_id": "B_origin_1_B_table_3_0", + "image_paths": [ + "data/Business/Table/B_origin_1/B_table_3_0.png" + ], + "domain": "Business", + "origin": "B_origin_1", + "table_id": "B_table_3_0" + }, + { + "index": 4, + "pair_id": "B_origin_1_B_table_4_0", + "image_paths": [ + "data/Business/Table/B_origin_1/B_table_4_0.png" + ], + "domain": "Business", + "origin": "B_origin_1", + "table_id": "B_table_4_0" + }, + { + "index": 5, + "pair_id": "B_origin_10_B_table_31_0", + "image_paths": [ + "data/Business/Table/B_origin_10/B_table_31_0.png" + ], + "domain": "Business", + "origin": "B_origin_10", + "table_id": "B_table_31_0" + }, + { + "index": 6, + "pair_id": "B_origin_10_B_table_32_0", + "image_paths": [ + "data/Business/Table/B_origin_10/B_table_32_0.png" + ], + "domain": "Business", + "origin": "B_origin_10", + "table_id": "B_table_32_0" + }, + { + "index": 7, + "pair_id": "B_origin_10_B_table_33_0", + "image_paths": [ + "data/Business/Table/B_origin_10/B_table_33_0.png" + ], + "domain": "Business", + "origin": "B_origin_10", + "table_id": "B_table_33_0" + }, + { + "index": 8, + "pair_id": "B_origin_10_B_table_34_0", + "image_paths": [ + "data/Business/Table/B_origin_10/B_table_34_0.png" + ], + "domain": "Business", + "origin": "B_origin_10", + "table_id": "B_table_34_0" + }, + { + "index": 9, + "pair_id": "B_origin_11_B_table_35_0", + "image_paths": [ + "data/Business/Table/B_origin_11/B_table_35_0.png" + ], + "domain": "Business", + "origin": "B_origin_11", + "table_id": "B_table_35_0" + }, + { + "index": 10, + "pair_id": "B_origin_11_B_table_36_0", + "image_paths": [ + "data/Business/Table/B_origin_11/B_table_36_0.png" + ], + "domain": "Business", + "origin": "B_origin_11", + "table_id": "B_table_36_0" + }, + { + "index": 11, + "pair_id": "B_origin_11_B_table_37_0", + "image_paths": [ + "data/Business/Table/B_origin_11/B_table_37_0.png" + ], + "domain": "Business", + "origin": "B_origin_11", + "table_id": "B_table_37_0" + }, + { + "index": 12, + "pair_id": "B_origin_11_B_table_38_0", + "image_paths": [ + "data/Business/Table/B_origin_11/B_table_38_0.png" + ], + "domain": "Business", + "origin": "B_origin_11", + "table_id": "B_table_38_0" + }, + { + "index": 13, + "pair_id": "B_origin_12_B_table_39_0", + "image_paths": [ + "data/Business/Table/B_origin_12/B_table_39_0.png" + ], + "domain": "Business", + "origin": "B_origin_12", + "table_id": "B_table_39_0" + }, + { + "index": 14, + "pair_id": "B_origin_12_B_table_40_0", + "image_paths": [ + "data/Business/Table/B_origin_12/B_table_40_0.png" + ], + "domain": "Business", + "origin": "B_origin_12", + "table_id": "B_table_40_0" + }, + { + "index": 15, + "pair_id": "B_origin_13_B_table_41_0", + "image_paths": [ + "data/Business/Table/B_origin_13/B_table_41_0.png" + ], + "domain": "Business", + "origin": "B_origin_13", + "table_id": "B_table_41_0" + }, + { + "index": 16, + "pair_id": "B_origin_13_B_table_42_0", + "image_paths": [ + "data/Business/Table/B_origin_13/B_table_42_0.png" + ], + "domain": "Business", + "origin": "B_origin_13", + "table_id": "B_table_42_0" + }, + { + "index": 17, + "pair_id": "B_origin_13_B_table_43_0", + "image_paths": [ + "data/Business/Table/B_origin_13/B_table_43_0.png" + ], + "domain": "Business", + "origin": "B_origin_13", + "table_id": "B_table_43_0" + }, + { + "index": 18, + "pair_id": "B_origin_13_B_table_44_0", + "image_paths": [ + "data/Business/Table/B_origin_13/B_table_44_0.png" + ], + "domain": "Business", + "origin": "B_origin_13", + "table_id": "B_table_44_0" + }, + { + "index": 19, + "pair_id": "B_origin_14_B_table_45_0", + "image_paths": [ + "data/Business/Table/B_origin_14/B_table_45_0.png" + ], + "domain": "Business", + "origin": "B_origin_14", + "table_id": "B_table_45_0" + }, + { + "index": 20, + "pair_id": "B_origin_14_B_table_45_1", + "image_paths": [ + "data/Business/Table/B_origin_14/B_table_45_1.png" + ], + "domain": "Business", + "origin": "B_origin_14", + "table_id": "B_table_45_1" + }, + { + "index": 21, + "pair_id": "B_origin_14_B_table_45_2", + "image_paths": [ + "data/Business/Table/B_origin_14/B_table_45_2.png" + ], + "domain": "Business", + "origin": "B_origin_14", + "table_id": "B_table_45_2" + }, + { + "index": 22, + "pair_id": "B_origin_14_B_table_46_0", + "image_paths": [ + "data/Business/Table/B_origin_14/B_table_46_0.png" + ], + "domain": "Business", + "origin": "B_origin_14", + "table_id": "B_table_46_0" + }, + { + "index": 23, + "pair_id": "B_origin_14_B_table_47_0", + "image_paths": [ + "data/Business/Table/B_origin_14/B_table_47_0.png" + ], + "domain": "Business", + "origin": "B_origin_14", + "table_id": "B_table_47_0" + }, + { + "index": 24, + "pair_id": "B_origin_14_B_table_48_0", + "image_paths": [ + "data/Business/Table/B_origin_14/B_table_48_0.png" + ], + "domain": "Business", + "origin": "B_origin_14", + "table_id": "B_table_48_0" + }, + { + "index": 25, + "pair_id": "B_origin_15_B_table_49_0", + "image_paths": [ + "data/Business/Table/B_origin_15/B_table_49_0.png" + ], + "domain": "Business", + "origin": "B_origin_15", + "table_id": "B_table_49_0" + }, + { + "index": 26, + "pair_id": "B_origin_15_B_table_50_0", + "image_paths": [ + "data/Business/Table/B_origin_15/B_table_50_0.png" + ], + "domain": "Business", + "origin": "B_origin_15", + "table_id": "B_table_50_0" + }, + { + "index": 27, + "pair_id": "B_origin_15_B_table_51_0", + "image_paths": [ + "data/Business/Table/B_origin_15/B_table_51_0.png" + ], + "domain": "Business", + "origin": "B_origin_15", + "table_id": "B_table_51_0" + }, + { + "index": 28, + "pair_id": "B_origin_15_B_table_51_1", + "image_paths": [ + "data/Business/Table/B_origin_15/B_table_51_1.png" + ], + "domain": "Business", + "origin": "B_origin_15", + "table_id": "B_table_51_1" + }, + { + "index": 29, + "pair_id": "B_origin_15_B_table_52_0", + "image_paths": [ + "data/Business/Table/B_origin_15/B_table_52_0.png" + ], + "domain": "Business", + "origin": "B_origin_15", + "table_id": "B_table_52_0" + }, + { + "index": 30, + "pair_id": "B_origin_15_B_table_53_0", + "image_paths": [ + "data/Business/Table/B_origin_15/B_table_53_0.png" + ], + "domain": "Business", + "origin": "B_origin_15", + "table_id": "B_table_53_0" + }, + { + "index": 31, + "pair_id": "B_origin_16_B_table_54_0", + "image_paths": [ + "data/Business/Table/B_origin_16/B_table_54_0.png" + ], + "domain": "Business", + "origin": "B_origin_16", + "table_id": "B_table_54_0" + }, + { + "index": 32, + "pair_id": "B_origin_17_B_table_55_0", + "image_paths": [ + "data/Business/Table/B_origin_17/B_table_55_0.png" + ], + "domain": "Business", + "origin": "B_origin_17", + "table_id": "B_table_55_0" + }, + { + "index": 33, + "pair_id": "B_origin_17_B_table_56_0", + "image_paths": [ + "data/Business/Table/B_origin_17/B_table_56_0.png" + ], + "domain": "Business", + "origin": "B_origin_17", + "table_id": "B_table_56_0" + }, + { + "index": 34, + "pair_id": "B_origin_17_B_table_57_0", + "image_paths": [ + "data/Business/Table/B_origin_17/B_table_57_0.png" + ], + "domain": "Business", + "origin": "B_origin_17", + "table_id": "B_table_57_0" + }, + { + "index": 35, + "pair_id": "B_origin_17_B_table_58_0", + "image_paths": [ + "data/Business/Table/B_origin_17/B_table_58_0.png" + ], + "domain": "Business", + "origin": "B_origin_17", + "table_id": "B_table_58_0" + }, + { + "index": 36, + "pair_id": "B_origin_17_B_table_59_0", + "image_paths": [ + "data/Business/Table/B_origin_17/B_table_59_0.png" + ], + "domain": "Business", + "origin": "B_origin_17", + "table_id": "B_table_59_0" + }, + { + "index": 37, + "pair_id": "B_origin_17_B_table_60_0", + "image_paths": [ + "data/Business/Table/B_origin_17/B_table_60_0.png" + ], + "domain": "Business", + "origin": "B_origin_17", + "table_id": "B_table_60_0" + }, + { + "index": 38, + "pair_id": "B_origin_18_B_table_61_0", + "image_paths": [ + "data/Business/Table/B_origin_18/B_table_61_0.png" + ], + "domain": "Business", + "origin": "B_origin_18", + "table_id": "B_table_61_0" + }, + { + "index": 39, + "pair_id": "B_origin_18_B_table_62_0", + "image_paths": [ + "data/Business/Table/B_origin_18/B_table_62_0.png" + ], + "domain": "Business", + "origin": "B_origin_18", + "table_id": "B_table_62_0" + }, + { + "index": 40, + "pair_id": "B_origin_18_B_table_63_0", + "image_paths": [ + "data/Business/Table/B_origin_18/B_table_63_0.png" + ], + "domain": "Business", + "origin": "B_origin_18", + "table_id": "B_table_63_0" + }, + { + "index": 41, + "pair_id": "B_origin_18_B_table_63_1", + "image_paths": [ + "data/Business/Table/B_origin_18/B_table_63_1.png" + ], + "domain": "Business", + "origin": "B_origin_18", + "table_id": "B_table_63_1" + }, + { + "index": 42, + "pair_id": "B_origin_18_B_table_64_0", + "image_paths": [ + "data/Business/Table/B_origin_18/B_table_64_0.png" + ], + "domain": "Business", + "origin": "B_origin_18", + "table_id": "B_table_64_0" + }, + { + "index": 43, + "pair_id": "B_origin_18_B_table_65_0", + "image_paths": [ + "data/Business/Table/B_origin_18/B_table_65_0.png" + ], + "domain": "Business", + "origin": "B_origin_18", + "table_id": "B_table_65_0" + }, + { + "index": 44, + "pair_id": "B_origin_19_B_table_66_0", + "image_paths": [ + "data/Business/Table/B_origin_19/B_table_66_0.png" + ], + "domain": "Business", + "origin": "B_origin_19", + "table_id": "B_table_66_0" + }, + { + "index": 45, + "pair_id": "B_origin_2_B_table_5_0", + "image_paths": [ + "data/Business/Table/B_origin_2/B_table_5_0.png" + ], + "domain": "Business", + "origin": "B_origin_2", + "table_id": "B_table_5_0" + }, + { + "index": 46, + "pair_id": "B_origin_2_B_table_6_0", + "image_paths": [ + "data/Business/Table/B_origin_2/B_table_6_0.png" + ], + "domain": "Business", + "origin": "B_origin_2", + "table_id": "B_table_6_0" + }, + { + "index": 47, + "pair_id": "B_origin_2_B_table_7_0", + "image_paths": [ + "data/Business/Table/B_origin_2/B_table_7_0.png" + ], + "domain": "Business", + "origin": "B_origin_2", + "table_id": "B_table_7_0" + }, + { + "index": 48, + "pair_id": "B_origin_2_B_table_8_0", + "image_paths": [ + "data/Business/Table/B_origin_2/B_table_8_0.png" + ], + "domain": "Business", + "origin": "B_origin_2", + "table_id": "B_table_8_0" + }, + { + "index": 49, + "pair_id": "B_origin_2_B_table_9_0", + "image_paths": [ + "data/Business/Table/B_origin_2/B_table_9_0.png" + ], + "domain": "Business", + "origin": "B_origin_2", + "table_id": "B_table_9_0" + }, + { + "index": 50, + "pair_id": "B_origin_20_B_table_67_0", + "image_paths": [ + "data/Business/Table/B_origin_20/B_table_67_0.png" + ], + "domain": "Business", + "origin": "B_origin_20", + "table_id": "B_table_67_0" + }, + { + "index": 51, + "pair_id": "B_origin_20_B_table_68_0", + "image_paths": [ + "data/Business/Table/B_origin_20/B_table_68_0.png" + ], + "domain": "Business", + "origin": "B_origin_20", + "table_id": "B_table_68_0" + }, + { + "index": 52, + "pair_id": "B_origin_20_B_table_69_0", + "image_paths": [ + "data/Business/Table/B_origin_20/B_table_69_0.png" + ], + "domain": "Business", + "origin": "B_origin_20", + "table_id": "B_table_69_0" + }, + { + "index": 53, + "pair_id": "B_origin_21_B_table_70_0", + "image_paths": [ + "data/Business/Table/B_origin_21/B_table_70_0.png" + ], + "domain": "Business", + "origin": "B_origin_21", + "table_id": "B_table_70_0" + }, + { + "index": 54, + "pair_id": "B_origin_21_B_table_71_0", + "image_paths": [ + "data/Business/Table/B_origin_21/B_table_71_0.png" + ], + "domain": "Business", + "origin": "B_origin_21", + "table_id": "B_table_71_0" + }, + { + "index": 55, + "pair_id": "B_origin_21_B_table_72_0", + "image_paths": [ + "data/Business/Table/B_origin_21/B_table_72_0.png" + ], + "domain": "Business", + "origin": "B_origin_21", + "table_id": "B_table_72_0" + }, + { + "index": 56, + "pair_id": "B_origin_21_B_table_72_1", + "image_paths": [ + "data/Business/Table/B_origin_21/B_table_72_1.png" + ], + "domain": "Business", + "origin": "B_origin_21", + "table_id": "B_table_72_1" + }, + { + "index": 57, + "pair_id": "B_origin_21_B_table_73_0", + "image_paths": [ + "data/Business/Table/B_origin_21/B_table_73_0.png" + ], + "domain": "Business", + "origin": "B_origin_21", + "table_id": "B_table_73_0" + }, + { + "index": 58, + "pair_id": "B_origin_21_B_table_74_0", + "image_paths": [ + "data/Business/Table/B_origin_21/B_table_74_0.png" + ], + "domain": "Business", + "origin": "B_origin_21", + "table_id": "B_table_74_0" + }, + { + "index": 59, + "pair_id": "B_origin_22_B_table_75_0", + "image_paths": [ + "data/Business/Table/B_origin_22/B_table_75_0.png" + ], + "domain": "Business", + "origin": "B_origin_22", + "table_id": "B_table_75_0" + }, + { + "index": 60, + "pair_id": "B_origin_22_B_table_76_0", + "image_paths": [ + "data/Business/Table/B_origin_22/B_table_76_0.png" + ], + "domain": "Business", + "origin": "B_origin_22", + "table_id": "B_table_76_0" + }, + { + "index": 61, + "pair_id": "B_origin_22_B_table_77_0", + "image_paths": [ + "data/Business/Table/B_origin_22/B_table_77_0.png" + ], + "domain": "Business", + "origin": "B_origin_22", + "table_id": "B_table_77_0" + }, + { + "index": 62, + "pair_id": "B_origin_22_B_table_78_0", + "image_paths": [ + "data/Business/Table/B_origin_22/B_table_78_0.png" + ], + "domain": "Business", + "origin": "B_origin_22", + "table_id": "B_table_78_0" + }, + { + "index": 63, + "pair_id": "B_origin_22_B_table_79_0", + "image_paths": [ + "data/Business/Table/B_origin_22/B_table_79_0.png" + ], + "domain": "Business", + "origin": "B_origin_22", + "table_id": "B_table_79_0" + }, + { + "index": 64, + "pair_id": "B_origin_23_B_table_79_0", + "image_paths": [ + "data/Business/Table/B_origin_23/B_table_79_0.png" + ], + "domain": "Business", + "origin": "B_origin_23", + "table_id": "B_table_79_0" + }, + { + "index": 65, + "pair_id": "B_origin_23_B_table_80_0", + "image_paths": [ + "data/Business/Table/B_origin_23/B_table_80_0.png" + ], + "domain": "Business", + "origin": "B_origin_23", + "table_id": "B_table_80_0" + }, + { + "index": 66, + "pair_id": "B_origin_23_B_table_81_0", + "image_paths": [ + "data/Business/Table/B_origin_23/B_table_81_0.png" + ], + "domain": "Business", + "origin": "B_origin_23", + "table_id": "B_table_81_0" + }, + { + "index": 67, + "pair_id": "B_origin_24_B_table_82_0", + "image_paths": [ + "data/Business/Table/B_origin_24/B_table_82_0.png" + ], + "domain": "Business", + "origin": "B_origin_24", + "table_id": "B_table_82_0" + }, + { + "index": 68, + "pair_id": "B_origin_24_B_table_83_0", + "image_paths": [ + "data/Business/Table/B_origin_24/B_table_83_0.png" + ], + "domain": "Business", + "origin": "B_origin_24", + "table_id": "B_table_83_0" + }, + { + "index": 69, + "pair_id": "B_origin_24_B_table_84_0", + "image_paths": [ + "data/Business/Table/B_origin_24/B_table_84_0.png" + ], + "domain": "Business", + "origin": "B_origin_24", + "table_id": "B_table_84_0" + }, + { + "index": 70, + "pair_id": "B_origin_25_B_table_85_0", + "image_paths": [ + "data/Business/Table/B_origin_25/B_table_85_0.png" + ], + "domain": "Business", + "origin": "B_origin_25", + "table_id": "B_table_85_0" + }, + { + "index": 71, + "pair_id": "B_origin_25_B_table_86_0", + "image_paths": [ + "data/Business/Table/B_origin_25/B_table_86_0.png" + ], + "domain": "Business", + "origin": "B_origin_25", + "table_id": "B_table_86_0" + }, + { + "index": 72, + "pair_id": "B_origin_26_B_table_87_0", + "image_paths": [ + "data/Business/Table/B_origin_26/B_table_87_0.png" + ], + "domain": "Business", + "origin": "B_origin_26", + "table_id": "B_table_87_0" + }, + { + "index": 73, + "pair_id": "B_origin_26_B_table_88_0", + "image_paths": [ + "data/Business/Table/B_origin_26/B_table_88_0.png" + ], + "domain": "Business", + "origin": "B_origin_26", + "table_id": "B_table_88_0" + }, + { + "index": 74, + "pair_id": "B_origin_27_B_table_89_0", + "image_paths": [ + "data/Business/Table/B_origin_27/B_table_89_0.png" + ], + "domain": "Business", + "origin": "B_origin_27", + "table_id": "B_table_89_0" + }, + { + "index": 75, + "pair_id": "B_origin_27_B_table_90_0", + "image_paths": [ + "data/Business/Table/B_origin_27/B_table_90_0.png" + ], + "domain": "Business", + "origin": "B_origin_27", + "table_id": "B_table_90_0" + }, + { + "index": 76, + "pair_id": "B_origin_27_B_table_91_0", + "image_paths": [ + "data/Business/Table/B_origin_27/B_table_91_0.png" + ], + "domain": "Business", + "origin": "B_origin_27", + "table_id": "B_table_91_0" + }, + { + "index": 77, + "pair_id": "B_origin_28_B_table_92_0", + "image_paths": [ + "data/Business/Table/B_origin_28/B_table_92_0.png" + ], + "domain": "Business", + "origin": "B_origin_28", + "table_id": "B_table_92_0" + }, + { + "index": 78, + "pair_id": "B_origin_28_B_table_93_0", + "image_paths": [ + "data/Business/Table/B_origin_28/B_table_93_0.png" + ], + "domain": "Business", + "origin": "B_origin_28", + "table_id": "B_table_93_0" + }, + { + "index": 79, + "pair_id": "B_origin_28_B_table_94_0", + "image_paths": [ + "data/Business/Table/B_origin_28/B_table_94_0.png" + ], + "domain": "Business", + "origin": "B_origin_28", + "table_id": "B_table_94_0" + }, + { + "index": 80, + "pair_id": "B_origin_28_B_table_95_0", + "image_paths": [ + "data/Business/Table/B_origin_28/B_table_95_0.png" + ], + "domain": "Business", + "origin": "B_origin_28", + "table_id": "B_table_95_0" + }, + { + "index": 81, + "pair_id": "B_origin_28_B_table_96_0", + "image_paths": [ + "data/Business/Table/B_origin_28/B_table_96_0.png" + ], + "domain": "Business", + "origin": "B_origin_28", + "table_id": "B_table_96_0" + }, + { + "index": 82, + "pair_id": "B_origin_28_B_table_97_0", + "image_paths": [ + "data/Business/Table/B_origin_28/B_table_97_0.png" + ], + "domain": "Business", + "origin": "B_origin_28", + "table_id": "B_table_97_0" + }, + { + "index": 83, + "pair_id": "B_origin_29_B_table_100_0", + "image_paths": [ + "data/Business/Table/B_origin_29/B_table_100_0.png" + ], + "domain": "Business", + "origin": "B_origin_29", + "table_id": "B_table_100_0" + }, + { + "index": 84, + "pair_id": "B_origin_29_B_table_98_0", + "image_paths": [ + "data/Business/Table/B_origin_29/B_table_98_0.png" + ], + "domain": "Business", + "origin": "B_origin_29", + "table_id": "B_table_98_0" + }, + { + "index": 85, + "pair_id": "B_origin_29_B_table_99_0", + "image_paths": [ + "data/Business/Table/B_origin_29/B_table_99_0.png" + ], + "domain": "Business", + "origin": "B_origin_29", + "table_id": "B_table_99_0" + }, + { + "index": 86, + "pair_id": "B_origin_3_B_table_10_0", + "image_paths": [ + "data/Business/Table/B_origin_3/B_table_10_0.png" + ], + "domain": "Business", + "origin": "B_origin_3", + "table_id": "B_table_10_0" + }, + { + "index": 87, + "pair_id": "B_origin_3_B_table_11_0", + "image_paths": [ + "data/Business/Table/B_origin_3/B_table_11_0.png" + ], + "domain": "Business", + "origin": "B_origin_3", + "table_id": "B_table_11_0" + }, + { + "index": 88, + "pair_id": "B_origin_3_B_table_12_0", + "image_paths": [ + "data/Business/Table/B_origin_3/B_table_12_0.png" + ], + "domain": "Business", + "origin": "B_origin_3", + "table_id": "B_table_12_0" + }, + { + "index": 89, + "pair_id": "B_origin_3_B_table_13_0", + "image_paths": [ + "data/Business/Table/B_origin_3/B_table_13_0.png" + ], + "domain": "Business", + "origin": "B_origin_3", + "table_id": "B_table_13_0" + }, + { + "index": 90, + "pair_id": "B_origin_30_B_table_101_0", + "image_paths": [ + "data/Business/Table/B_origin_30/B_table_101_0.png" + ], + "domain": "Business", + "origin": "B_origin_30", + "table_id": "B_table_101_0" + }, + { + "index": 91, + "pair_id": "B_origin_30_B_table_102_0", + "image_paths": [ + "data/Business/Table/B_origin_30/B_table_102_0.png" + ], + "domain": "Business", + "origin": "B_origin_30", + "table_id": "B_table_102_0" + }, + { + "index": 92, + "pair_id": "B_origin_30_B_table_103_0", + "image_paths": [ + "data/Business/Table/B_origin_30/B_table_103_0.png" + ], + "domain": "Business", + "origin": "B_origin_30", + "table_id": "B_table_103_0" + }, + { + "index": 93, + "pair_id": "B_origin_31_B_table_104_0", + "image_paths": [ + "data/Business/Table/B_origin_31/B_table_104_0.png" + ], + "domain": "Business", + "origin": "B_origin_31", + "table_id": "B_table_104_0" + }, + { + "index": 94, + "pair_id": "B_origin_31_B_table_105_0", + "image_paths": [ + "data/Business/Table/B_origin_31/B_table_105_0.png" + ], + "domain": "Business", + "origin": "B_origin_31", + "table_id": "B_table_105_0" + }, + { + "index": 95, + "pair_id": "B_origin_31_B_table_106_0", + "image_paths": [ + "data/Business/Table/B_origin_31/B_table_106_0.png" + ], + "domain": "Business", + "origin": "B_origin_31", + "table_id": "B_table_106_0" + }, + { + "index": 96, + "pair_id": "B_origin_31_B_table_107_0", + "image_paths": [ + "data/Business/Table/B_origin_31/B_table_107_0.png" + ], + "domain": "Business", + "origin": "B_origin_31", + "table_id": "B_table_107_0" + }, + { + "index": 97, + "pair_id": "B_origin_31_B_table_108_0", + "image_paths": [ + "data/Business/Table/B_origin_31/B_table_108_0.png" + ], + "domain": "Business", + "origin": "B_origin_31", + "table_id": "B_table_108_0" + }, + { + "index": 98, + "pair_id": "B_origin_32_B_table_109_0", + "image_paths": [ + "data/Business/Table/B_origin_32/B_table_109_0.png" + ], + "domain": "Business", + "origin": "B_origin_32", + "table_id": "B_table_109_0" + }, + { + "index": 99, + "pair_id": "B_origin_32_B_table_110_0", + "image_paths": [ + "data/Business/Table/B_origin_32/B_table_110_0.png" + ], + "domain": "Business", + "origin": "B_origin_32", + "table_id": "B_table_110_0" + }, + { + "index": 100, + "pair_id": "B_origin_32_B_table_111_0", + "image_paths": [ + "data/Business/Table/B_origin_32/B_table_111_0.png" + ], + "domain": "Business", + "origin": "B_origin_32", + "table_id": "B_table_111_0" + }, + { + "index": 101, + "pair_id": "B_origin_32_B_table_112_0", + "image_paths": [ + "data/Business/Table/B_origin_32/B_table_112_0.png" + ], + "domain": "Business", + "origin": "B_origin_32", + "table_id": "B_table_112_0" + }, + { + "index": 102, + "pair_id": "B_origin_33_B_table_113_0", + "image_paths": [ + "data/Business/Table/B_origin_33/B_table_113_0.png" + ], + "domain": "Business", + "origin": "B_origin_33", + "table_id": "B_table_113_0" + }, + { + "index": 103, + "pair_id": "B_origin_33_B_table_114_0", + "image_paths": [ + "data/Business/Table/B_origin_33/B_table_114_0.png" + ], + "domain": "Business", + "origin": "B_origin_33", + "table_id": "B_table_114_0" + }, + { + "index": 104, + "pair_id": "B_origin_33_B_table_115_9", + "image_paths": [ + "data/Business/Table/B_origin_33/B_table_115_9.png" + ], + "domain": "Business", + "origin": "B_origin_33", + "table_id": "B_table_115_9" + }, + { + "index": 105, + "pair_id": "B_origin_33_B_table_116_0", + "image_paths": [ + "data/Business/Table/B_origin_33/B_table_116_0.png" + ], + "domain": "Business", + "origin": "B_origin_33", + "table_id": "B_table_116_0" + }, + { + "index": 106, + "pair_id": "B_origin_33_B_table_117_0", + "image_paths": [ + "data/Business/Table/B_origin_33/B_table_117_0.png" + ], + "domain": "Business", + "origin": "B_origin_33", + "table_id": "B_table_117_0" + }, + { + "index": 107, + "pair_id": "B_origin_34_B_table_118_0", + "image_paths": [ + "data/Business/Table/B_origin_34/B_table_118_0.png" + ], + "domain": "Business", + "origin": "B_origin_34", + "table_id": "B_table_118_0" + }, + { + "index": 108, + "pair_id": "B_origin_34_B_table_119_0", + "image_paths": [ + "data/Business/Table/B_origin_34/B_table_119_0.png" + ], + "domain": "Business", + "origin": "B_origin_34", + "table_id": "B_table_119_0" + }, + { + "index": 109, + "pair_id": "B_origin_34_B_table_120_0", + "image_paths": [ + "data/Business/Table/B_origin_34/B_table_120_0.png" + ], + "domain": "Business", + "origin": "B_origin_34", + "table_id": "B_table_120_0" + }, + { + "index": 110, + "pair_id": "B_origin_34_B_table_121_0", + "image_paths": [ + "data/Business/Table/B_origin_34/B_table_121_0.png" + ], + "domain": "Business", + "origin": "B_origin_34", + "table_id": "B_table_121_0" + }, + { + "index": 111, + "pair_id": "B_origin_35_B_table_122_0", + "image_paths": [ + "data/Business/Table/B_origin_35/B_table_122_0.png" + ], + "domain": "Business", + "origin": "B_origin_35", + "table_id": "B_table_122_0" + }, + { + "index": 112, + "pair_id": "B_origin_35_B_table_123_0", + "image_paths": [ + "data/Business/Table/B_origin_35/B_table_123_0.png" + ], + "domain": "Business", + "origin": "B_origin_35", + "table_id": "B_table_123_0" + }, + { + "index": 113, + "pair_id": "B_origin_35_B_table_124_0", + "image_paths": [ + "data/Business/Table/B_origin_35/B_table_124_0.png" + ], + "domain": "Business", + "origin": "B_origin_35", + "table_id": "B_table_124_0" + }, + { + "index": 114, + "pair_id": "B_origin_35_B_table_125_0", + "image_paths": [ + "data/Business/Table/B_origin_35/B_table_125_0.png" + ], + "domain": "Business", + "origin": "B_origin_35", + "table_id": "B_table_125_0" + }, + { + "index": 115, + "pair_id": "B_origin_35_B_table_126_0", + "image_paths": [ + "data/Business/Table/B_origin_35/B_table_126_0.png" + ], + "domain": "Business", + "origin": "B_origin_35", + "table_id": "B_table_126_0" + }, + { + "index": 116, + "pair_id": "B_origin_36_B_table_127_0", + "image_paths": [ + "data/Business/Table/B_origin_36/B_table_127_0.png" + ], + "domain": "Business", + "origin": "B_origin_36", + "table_id": "B_table_127_0" + }, + { + "index": 117, + "pair_id": "B_origin_36_B_table_128_0", + "image_paths": [ + "data/Business/Table/B_origin_36/B_table_128_0.png" + ], + "domain": "Business", + "origin": "B_origin_36", + "table_id": "B_table_128_0" + }, + { + "index": 118, + "pair_id": "B_origin_36_B_table_129_0", + "image_paths": [ + "data/Business/Table/B_origin_36/B_table_129_0.png" + ], + "domain": "Business", + "origin": "B_origin_36", + "table_id": "B_table_129_0" + }, + { + "index": 119, + "pair_id": "B_origin_36_B_table_130_0", + "image_paths": [ + "data/Business/Table/B_origin_36/B_table_130_0.png" + ], + "domain": "Business", + "origin": "B_origin_36", + "table_id": "B_table_130_0" + }, + { + "index": 120, + "pair_id": "B_origin_37_B_table_131_0", + "image_paths": [ + "data/Business/Table/B_origin_37/B_table_131_0.png" + ], + "domain": "Business", + "origin": "B_origin_37", + "table_id": "B_table_131_0" + }, + { + "index": 121, + "pair_id": "B_origin_37_B_table_132_0", + "image_paths": [ + "data/Business/Table/B_origin_37/B_table_132_0.png" + ], + "domain": "Business", + "origin": "B_origin_37", + "table_id": "B_table_132_0" + }, + { + "index": 122, + "pair_id": "B_origin_37_B_table_132_1", + "image_paths": [ + "data/Business/Table/B_origin_37/B_table_132_1.png" + ], + "domain": "Business", + "origin": "B_origin_37", + "table_id": "B_table_132_1" + }, + { + "index": 123, + "pair_id": "B_origin_37_B_table_133_0", + "image_paths": [ + "data/Business/Table/B_origin_37/B_table_133_0.png" + ], + "domain": "Business", + "origin": "B_origin_37", + "table_id": "B_table_133_0" + }, + { + "index": 124, + "pair_id": "B_origin_37_B_table_134_0", + "image_paths": [ + "data/Business/Table/B_origin_37/B_table_134_0.png" + ], + "domain": "Business", + "origin": "B_origin_37", + "table_id": "B_table_134_0" + }, + { + "index": 125, + "pair_id": "B_origin_38_B_table_135_0", + "image_paths": [ + "data/Business/Table/B_origin_38/B_table_135_0.png" + ], + "domain": "Business", + "origin": "B_origin_38", + "table_id": "B_table_135_0" + }, + { + "index": 126, + "pair_id": "B_origin_38_B_table_136_0", + "image_paths": [ + "data/Business/Table/B_origin_38/B_table_136_0.png" + ], + "domain": "Business", + "origin": "B_origin_38", + "table_id": "B_table_136_0" + }, + { + "index": 127, + "pair_id": "B_origin_38_B_table_137_0", + "image_paths": [ + "data/Business/Table/B_origin_38/B_table_137_0.png" + ], + "domain": "Business", + "origin": "B_origin_38", + "table_id": "B_table_137_0" + }, + { + "index": 128, + "pair_id": "B_origin_38_B_table_138_0", + "image_paths": [ + "data/Business/Table/B_origin_38/B_table_138_0.png" + ], + "domain": "Business", + "origin": "B_origin_38", + "table_id": "B_table_138_0" + }, + { + "index": 129, + "pair_id": "B_origin_39_B_table_139_0", + "image_paths": [ + "data/Business/Table/B_origin_39/B_table_139_0.png" + ], + "domain": "Business", + "origin": "B_origin_39", + "table_id": "B_table_139_0" + }, + { + "index": 130, + "pair_id": "B_origin_39_B_table_140_0", + "image_paths": [ + "data/Business/Table/B_origin_39/B_table_140_0.png" + ], + "domain": "Business", + "origin": "B_origin_39", + "table_id": "B_table_140_0" + }, + { + "index": 131, + "pair_id": "B_origin_4_B_table_14_0", + "image_paths": [ + "data/Business/Table/B_origin_4/B_table_14_0.png" + ], + "domain": "Business", + "origin": "B_origin_4", + "table_id": "B_table_14_0" + }, + { + "index": 132, + "pair_id": "B_origin_4_B_table_15_0", + "image_paths": [ + "data/Business/Table/B_origin_4/B_table_15_0.png" + ], + "domain": "Business", + "origin": "B_origin_4", + "table_id": "B_table_15_0" + }, + { + "index": 133, + "pair_id": "B_origin_4_B_table_16_0", + "image_paths": [ + "data/Business/Table/B_origin_4/B_table_16_0.png" + ], + "domain": "Business", + "origin": "B_origin_4", + "table_id": "B_table_16_0" + }, + { + "index": 134, + "pair_id": "B_origin_4_B_table_16_1", + "image_paths": [ + "data/Business/Table/B_origin_4/B_table_16_1.png" + ], + "domain": "Business", + "origin": "B_origin_4", + "table_id": "B_table_16_1" + }, + { + "index": 135, + "pair_id": "B_origin_5_B_table_17_0", + "image_paths": [ + "data/Business/Table/B_origin_5/B_table_17_0.png" + ], + "domain": "Business", + "origin": "B_origin_5", + "table_id": "B_table_17_0" + }, + { + "index": 136, + "pair_id": "B_origin_5_B_table_18_0", + "image_paths": [ + "data/Business/Table/B_origin_5/B_table_18_0.png" + ], + "domain": "Business", + "origin": "B_origin_5", + "table_id": "B_table_18_0" + }, + { + "index": 137, + "pair_id": "B_origin_6_B_table_19_0", + "image_paths": [ + "data/Business/Table/B_origin_6/B_table_19_0.png" + ], + "domain": "Business", + "origin": "B_origin_6", + "table_id": "B_table_19_0" + }, + { + "index": 138, + "pair_id": "B_origin_6_B_table_20_0", + "image_paths": [ + "data/Business/Table/B_origin_6/B_table_20_0.png" + ], + "domain": "Business", + "origin": "B_origin_6", + "table_id": "B_table_20_0" + }, + { + "index": 139, + "pair_id": "B_origin_6_B_table_21_0", + "image_paths": [ + "data/Business/Table/B_origin_6/B_table_21_0.png" + ], + "domain": "Business", + "origin": "B_origin_6", + "table_id": "B_table_21_0" + }, + { + "index": 140, + "pair_id": "B_origin_6_B_table_22_0", + "image_paths": [ + "data/Business/Table/B_origin_6/B_table_22_0.png" + ], + "domain": "Business", + "origin": "B_origin_6", + "table_id": "B_table_22_0" + }, + { + "index": 141, + "pair_id": "B_origin_7_B_table_23_0", + "image_paths": [ + "data/Business/Table/B_origin_7/B_table_23_0.png" + ], + "domain": "Business", + "origin": "B_origin_7", + "table_id": "B_table_23_0" + }, + { + "index": 142, + "pair_id": "B_origin_7_B_table_24_0", + "image_paths": [ + "data/Business/Table/B_origin_7/B_table_24_0.png" + ], + "domain": "Business", + "origin": "B_origin_7", + "table_id": "B_table_24_0" + }, + { + "index": 143, + "pair_id": "B_origin_7_B_table_25_0", + "image_paths": [ + "data/Business/Table/B_origin_7/B_table_25_0.png" + ], + "domain": "Business", + "origin": "B_origin_7", + "table_id": "B_table_25_0" + }, + { + "index": 144, + "pair_id": "B_origin_8_B_table_26_0", + "image_paths": [ + "data/Business/Table/B_origin_8/B_table_26_0.png" + ], + "domain": "Business", + "origin": "B_origin_8", + "table_id": "B_table_26_0" + }, + { + "index": 145, + "pair_id": "B_origin_8_B_table_27_0", + "image_paths": [ + "data/Business/Table/B_origin_8/B_table_27_0.png" + ], + "domain": "Business", + "origin": "B_origin_8", + "table_id": "B_table_27_0" + }, + { + "index": 146, + "pair_id": "B_origin_9_B_table_28_0", + "image_paths": [ + "data/Business/Table/B_origin_9/B_table_28_0.png" + ], + "domain": "Business", + "origin": "B_origin_9", + "table_id": "B_table_28_0" + }, + { + "index": 147, + "pair_id": "B_origin_9_B_table_29_0", + "image_paths": [ + "data/Business/Table/B_origin_9/B_table_29_0.png" + ], + "domain": "Business", + "origin": "B_origin_9", + "table_id": "B_table_29_0" + }, + { + "index": 148, + "pair_id": "B_origin_9_B_table_30_0", + "image_paths": [ + "data/Business/Table/B_origin_9/B_table_30_0.png" + ], + "domain": "Business", + "origin": "B_origin_9", + "table_id": "B_table_30_0" + } +] \ No newline at end of file diff --git a/single_image_json_list/single_table_finance_input.json b/single_image_json_list/single_table_finance_input.json new file mode 100644 index 0000000..c40cdac --- /dev/null +++ b/single_image_json_list/single_table_finance_input.json @@ -0,0 +1,3152 @@ +[ + { + "index": 0, + "pair_id": "F_origin_0_F_table_0_0", + "image_paths": [ + "data/Finance/Table/F_origin_0/F_table_0_0.png" + ], + "domain": "Finance", + "origin": "F_origin_0", + "table_id": "F_table_0_0" + }, + { + "index": 1, + "pair_id": "F_origin_0_F_table_0_1", + "image_paths": [ + "data/Finance/Table/F_origin_0/F_table_0_1.png" + ], + "domain": "Finance", + "origin": "F_origin_0", + "table_id": "F_table_0_1" + }, + { + "index": 2, + "pair_id": "F_origin_0_F_table_0_2", + "image_paths": [ + "data/Finance/Table/F_origin_0/F_table_0_2.png" + ], + "domain": "Finance", + "origin": "F_origin_0", + "table_id": "F_table_0_2" + }, + { + "index": 3, + "pair_id": "F_origin_0_F_table_0_3", + "image_paths": [ + "data/Finance/Table/F_origin_0/F_table_0_3.png" + ], + "domain": "Finance", + "origin": "F_origin_0", + "table_id": "F_table_0_3" + }, + { + "index": 4, + "pair_id": "F_origin_0_F_table_0_4", + "image_paths": [ + "data/Finance/Table/F_origin_0/F_table_0_4.png" + ], + "domain": "Finance", + "origin": "F_origin_0", + "table_id": "F_table_0_4" + }, + { + "index": 5, + "pair_id": "F_origin_0_F_table_1_0", + "image_paths": [ + "data/Finance/Table/F_origin_0/F_table_1_0.png" + ], + "domain": "Finance", + "origin": "F_origin_0", + "table_id": "F_table_1_0" + }, + { + "index": 6, + "pair_id": "F_origin_0_F_table_1_1", + "image_paths": [ + "data/Finance/Table/F_origin_0/F_table_1_1.png" + ], + "domain": "Finance", + "origin": "F_origin_0", + "table_id": "F_table_1_1" + }, + { + "index": 7, + "pair_id": "F_origin_1_F_table_2_0", + "image_paths": [ + "data/Finance/Table/F_origin_1/F_table_2_0.png" + ], + "domain": "Finance", + "origin": "F_origin_1", + "table_id": "F_table_2_0" + }, + { + "index": 8, + "pair_id": "F_origin_1_F_table_2_1", + "image_paths": [ + "data/Finance/Table/F_origin_1/F_table_2_1.png" + ], + "domain": "Finance", + "origin": "F_origin_1", + "table_id": "F_table_2_1" + }, + { + "index": 9, + "pair_id": "F_origin_1_F_table_2_2", + "image_paths": [ + "data/Finance/Table/F_origin_1/F_table_2_2.png" + ], + "domain": "Finance", + "origin": "F_origin_1", + "table_id": "F_table_2_2" + }, + { + "index": 10, + "pair_id": "F_origin_1_F_table_2_3", + "image_paths": [ + "data/Finance/Table/F_origin_1/F_table_2_3.png" + ], + "domain": "Finance", + "origin": "F_origin_1", + "table_id": "F_table_2_3" + }, + { + "index": 11, + "pair_id": "F_origin_1_F_table_2_4", + "image_paths": [ + "data/Finance/Table/F_origin_1/F_table_2_4.png" + ], + "domain": "Finance", + "origin": "F_origin_1", + "table_id": "F_table_2_4" + }, + { + "index": 12, + "pair_id": "F_origin_10_F_table_14_0", + "image_paths": [ + "data/Finance/Table/F_origin_10/F_table_14_0.png" + ], + "domain": "Finance", + "origin": "F_origin_10", + "table_id": "F_table_14_0" + }, + { + "index": 13, + "pair_id": "F_origin_10_F_table_14_1", + "image_paths": [ + "data/Finance/Table/F_origin_10/F_table_14_1.png" + ], + "domain": "Finance", + "origin": "F_origin_10", + "table_id": "F_table_14_1" + }, + { + "index": 14, + "pair_id": "F_origin_10_F_table_14_2", + "image_paths": [ + "data/Finance/Table/F_origin_10/F_table_14_2.png" + ], + "domain": "Finance", + "origin": "F_origin_10", + "table_id": "F_table_14_2" + }, + { + "index": 15, + "pair_id": "F_origin_10_F_table_14_3", + "image_paths": [ + "data/Finance/Table/F_origin_10/F_table_14_3.png" + ], + "domain": "Finance", + "origin": "F_origin_10", + "table_id": "F_table_14_3" + }, + { + "index": 16, + "pair_id": "F_origin_10_F_table_14_4", + "image_paths": [ + "data/Finance/Table/F_origin_10/F_table_14_4.png" + ], + "domain": "Finance", + "origin": "F_origin_10", + "table_id": "F_table_14_4" + }, + { + "index": 17, + "pair_id": "F_origin_11_F_table_15_0", + "image_paths": [ + "data/Finance/Table/F_origin_11/F_table_15_0.png" + ], + "domain": "Finance", + "origin": "F_origin_11", + "table_id": "F_table_15_0" + }, + { + "index": 18, + "pair_id": "F_origin_11_F_table_15_1", + "image_paths": [ + "data/Finance/Table/F_origin_11/F_table_15_1.png" + ], + "domain": "Finance", + "origin": "F_origin_11", + "table_id": "F_table_15_1" + }, + { + "index": 19, + "pair_id": "F_origin_11_F_table_15_2", + "image_paths": [ + "data/Finance/Table/F_origin_11/F_table_15_2.png" + ], + "domain": "Finance", + "origin": "F_origin_11", + "table_id": "F_table_15_2" + }, + { + "index": 20, + "pair_id": "F_origin_11_F_table_15_3", + "image_paths": [ + "data/Finance/Table/F_origin_11/F_table_15_3.png" + ], + "domain": "Finance", + "origin": "F_origin_11", + "table_id": "F_table_15_3" + }, + { + "index": 21, + "pair_id": "F_origin_11_F_table_15_4", + "image_paths": [ + "data/Finance/Table/F_origin_11/F_table_15_4.png" + ], + "domain": "Finance", + "origin": "F_origin_11", + "table_id": "F_table_15_4" + }, + { + "index": 22, + "pair_id": "F_origin_11_F_table_15_5", + "image_paths": [ + "data/Finance/Table/F_origin_11/F_table_15_5.png" + ], + "domain": "Finance", + "origin": "F_origin_11", + "table_id": "F_table_15_5" + }, + { + "index": 23, + "pair_id": "F_origin_12_F_table_16_0", + "image_paths": [ + "data/Finance/Table/F_origin_12/F_table_16_0.png" + ], + "domain": "Finance", + "origin": "F_origin_12", + "table_id": "F_table_16_0" + }, + { + "index": 24, + "pair_id": "F_origin_12_F_table_17_0", + "image_paths": [ + "data/Finance/Table/F_origin_12/F_table_17_0.png" + ], + "domain": "Finance", + "origin": "F_origin_12", + "table_id": "F_table_17_0" + }, + { + "index": 25, + "pair_id": "F_origin_12_F_table_18_0", + "image_paths": [ + "data/Finance/Table/F_origin_12/F_table_18_0.png" + ], + "domain": "Finance", + "origin": "F_origin_12", + "table_id": "F_table_18_0" + }, + { + "index": 26, + "pair_id": "F_origin_12_F_table_19_0", + "image_paths": [ + "data/Finance/Table/F_origin_12/F_table_19_0.png" + ], + "domain": "Finance", + "origin": "F_origin_12", + "table_id": "F_table_19_0" + }, + { + "index": 27, + "pair_id": "F_origin_12_F_table_20_0", + "image_paths": [ + "data/Finance/Table/F_origin_12/F_table_20_0.png" + ], + "domain": "Finance", + "origin": "F_origin_12", + "table_id": "F_table_20_0" + }, + { + "index": 28, + "pair_id": "F_origin_12_F_table_21_0", + "image_paths": [ + "data/Finance/Table/F_origin_12/F_table_21_0.png" + ], + "domain": "Finance", + "origin": "F_origin_12", + "table_id": "F_table_21_0" + }, + { + "index": 29, + "pair_id": "F_origin_13_F_table_21_0", + "image_paths": [ + "data/Finance/Table/F_origin_13/F_table_21_0.png" + ], + "domain": "Finance", + "origin": "F_origin_13", + "table_id": "F_table_21_0" + }, + { + "index": 30, + "pair_id": "F_origin_13_F_table_22_0", + "image_paths": [ + "data/Finance/Table/F_origin_13/F_table_22_0.png" + ], + "domain": "Finance", + "origin": "F_origin_13", + "table_id": "F_table_22_0" + }, + { + "index": 31, + "pair_id": "F_origin_13_F_table_23_0", + "image_paths": [ + "data/Finance/Table/F_origin_13/F_table_23_0.png" + ], + "domain": "Finance", + "origin": "F_origin_13", + "table_id": "F_table_23_0" + }, + { + "index": 32, + "pair_id": "F_origin_13_F_table_24_0", + "image_paths": [ + "data/Finance/Table/F_origin_13/F_table_24_0.png" + ], + "domain": "Finance", + "origin": "F_origin_13", + "table_id": "F_table_24_0" + }, + { + "index": 33, + "pair_id": "F_origin_13_F_table_25_0", + "image_paths": [ + "data/Finance/Table/F_origin_13/F_table_25_0.png" + ], + "domain": "Finance", + "origin": "F_origin_13", + "table_id": "F_table_25_0" + }, + { + "index": 34, + "pair_id": "F_origin_14_F_table_26_0", + "image_paths": [ + "data/Finance/Table/F_origin_14/F_table_26_0.png" + ], + "domain": "Finance", + "origin": "F_origin_14", + "table_id": "F_table_26_0" + }, + { + "index": 35, + "pair_id": "F_origin_14_F_table_27_0", + "image_paths": [ + "data/Finance/Table/F_origin_14/F_table_27_0.png" + ], + "domain": "Finance", + "origin": "F_origin_14", + "table_id": "F_table_27_0" + }, + { + "index": 36, + "pair_id": "F_origin_14_F_table_28_0", + "image_paths": [ + "data/Finance/Table/F_origin_14/F_table_28_0.png" + ], + "domain": "Finance", + "origin": "F_origin_14", + "table_id": "F_table_28_0" + }, + { + "index": 37, + "pair_id": "F_origin_14_F_table_29_0", + "image_paths": [ + "data/Finance/Table/F_origin_14/F_table_29_0.png" + ], + "domain": "Finance", + "origin": "F_origin_14", + "table_id": "F_table_29_0" + }, + { + "index": 38, + "pair_id": "F_origin_14_F_table_30_0", + "image_paths": [ + "data/Finance/Table/F_origin_14/F_table_30_0.png" + ], + "domain": "Finance", + "origin": "F_origin_14", + "table_id": "F_table_30_0" + }, + { + "index": 39, + "pair_id": "F_origin_14_F_table_31_0", + "image_paths": [ + "data/Finance/Table/F_origin_14/F_table_31_0.png" + ], + "domain": "Finance", + "origin": "F_origin_14", + "table_id": "F_table_31_0" + }, + { + "index": 40, + "pair_id": "F_origin_15_F_table_32_0", + "image_paths": [ + "data/Finance/Table/F_origin_15/F_table_32_0.png" + ], + "domain": "Finance", + "origin": "F_origin_15", + "table_id": "F_table_32_0" + }, + { + "index": 41, + "pair_id": "F_origin_15_F_table_33_0", + "image_paths": [ + "data/Finance/Table/F_origin_15/F_table_33_0.png" + ], + "domain": "Finance", + "origin": "F_origin_15", + "table_id": "F_table_33_0" + }, + { + "index": 42, + "pair_id": "F_origin_15_F_table_34_0", + "image_paths": [ + "data/Finance/Table/F_origin_15/F_table_34_0.png" + ], + "domain": "Finance", + "origin": "F_origin_15", + "table_id": "F_table_34_0" + }, + { + "index": 43, + "pair_id": "F_origin_15_F_table_35_0", + "image_paths": [ + "data/Finance/Table/F_origin_15/F_table_35_0.png" + ], + "domain": "Finance", + "origin": "F_origin_15", + "table_id": "F_table_35_0" + }, + { + "index": 44, + "pair_id": "F_origin_15_F_table_36_0", + "image_paths": [ + "data/Finance/Table/F_origin_15/F_table_36_0.png" + ], + "domain": "Finance", + "origin": "F_origin_15", + "table_id": "F_table_36_0" + }, + { + "index": 45, + "pair_id": "F_origin_16_F_table_37_0", + "image_paths": [ + "data/Finance/Table/F_origin_16/F_table_37_0.png" + ], + "domain": "Finance", + "origin": "F_origin_16", + "table_id": "F_table_37_0" + }, + { + "index": 46, + "pair_id": "F_origin_16_F_table_38_0", + "image_paths": [ + "data/Finance/Table/F_origin_16/F_table_38_0.png" + ], + "domain": "Finance", + "origin": "F_origin_16", + "table_id": "F_table_38_0" + }, + { + "index": 47, + "pair_id": "F_origin_16_F_table_39_0", + "image_paths": [ + "data/Finance/Table/F_origin_16/F_table_39_0.png" + ], + "domain": "Finance", + "origin": "F_origin_16", + "table_id": "F_table_39_0" + }, + { + "index": 48, + "pair_id": "F_origin_16_F_table_40_0", + "image_paths": [ + "data/Finance/Table/F_origin_16/F_table_40_0.png" + ], + "domain": "Finance", + "origin": "F_origin_16", + "table_id": "F_table_40_0" + }, + { + "index": 49, + "pair_id": "F_origin_16_F_table_41_0", + "image_paths": [ + "data/Finance/Table/F_origin_16/F_table_41_0.png" + ], + "domain": "Finance", + "origin": "F_origin_16", + "table_id": "F_table_41_0" + }, + { + "index": 50, + "pair_id": "F_origin_16_F_table_42_0", + "image_paths": [ + "data/Finance/Table/F_origin_16/F_table_42_0.png" + ], + "domain": "Finance", + "origin": "F_origin_16", + "table_id": "F_table_42_0" + }, + { + "index": 51, + "pair_id": "F_origin_16_F_table_43_0", + "image_paths": [ + "data/Finance/Table/F_origin_16/F_table_43_0.png" + ], + "domain": "Finance", + "origin": "F_origin_16", + "table_id": "F_table_43_0" + }, + { + "index": 52, + "pair_id": "F_origin_16_F_table_44_0", + "image_paths": [ + "data/Finance/Table/F_origin_16/F_table_44_0.png" + ], + "domain": "Finance", + "origin": "F_origin_16", + "table_id": "F_table_44_0" + }, + { + "index": 53, + "pair_id": "F_origin_16_F_table_45_0", + "image_paths": [ + "data/Finance/Table/F_origin_16/F_table_45_0.png" + ], + "domain": "Finance", + "origin": "F_origin_16", + "table_id": "F_table_45_0" + }, + { + "index": 54, + "pair_id": "F_origin_17_F_table_46_0", + "image_paths": [ + "data/Finance/Table/F_origin_17/F_table_46_0.png" + ], + "domain": "Finance", + "origin": "F_origin_17", + "table_id": "F_table_46_0" + }, + { + "index": 55, + "pair_id": "F_origin_17_F_table_47_0", + "image_paths": [ + "data/Finance/Table/F_origin_17/F_table_47_0.png" + ], + "domain": "Finance", + "origin": "F_origin_17", + "table_id": "F_table_47_0" + }, + { + "index": 56, + "pair_id": "F_origin_17_F_table_48_0", + "image_paths": [ + "data/Finance/Table/F_origin_17/F_table_48_0.png" + ], + "domain": "Finance", + "origin": "F_origin_17", + "table_id": "F_table_48_0" + }, + { + "index": 57, + "pair_id": "F_origin_17_F_table_49_0", + "image_paths": [ + "data/Finance/Table/F_origin_17/F_table_49_0.png" + ], + "domain": "Finance", + "origin": "F_origin_17", + "table_id": "F_table_49_0" + }, + { + "index": 58, + "pair_id": "F_origin_17_F_table_50_0", + "image_paths": [ + "data/Finance/Table/F_origin_17/F_table_50_0.png" + ], + "domain": "Finance", + "origin": "F_origin_17", + "table_id": "F_table_50_0" + }, + { + "index": 59, + "pair_id": "F_origin_17_F_table_51_0", + "image_paths": [ + "data/Finance/Table/F_origin_17/F_table_51_0.png" + ], + "domain": "Finance", + "origin": "F_origin_17", + "table_id": "F_table_51_0" + }, + { + "index": 60, + "pair_id": "F_origin_17_F_table_52_0", + "image_paths": [ + "data/Finance/Table/F_origin_17/F_table_52_0.png" + ], + "domain": "Finance", + "origin": "F_origin_17", + "table_id": "F_table_52_0" + }, + { + "index": 61, + "pair_id": "F_origin_17_F_table_53_0", + "image_paths": [ + "data/Finance/Table/F_origin_17/F_table_53_0.png" + ], + "domain": "Finance", + "origin": "F_origin_17", + "table_id": "F_table_53_0" + }, + { + "index": 62, + "pair_id": "F_origin_17_F_table_54_0", + "image_paths": [ + "data/Finance/Table/F_origin_17/F_table_54_0.png" + ], + "domain": "Finance", + "origin": "F_origin_17", + "table_id": "F_table_54_0" + }, + { + "index": 63, + "pair_id": "F_origin_18_F_table_55_0", + "image_paths": [ + "data/Finance/Table/F_origin_18/F_table_55_0.png" + ], + "domain": "Finance", + "origin": "F_origin_18", + "table_id": "F_table_55_0" + }, + { + "index": 64, + "pair_id": "F_origin_18_F_table_56_0", + "image_paths": [ + "data/Finance/Table/F_origin_18/F_table_56_0.png" + ], + "domain": "Finance", + "origin": "F_origin_18", + "table_id": "F_table_56_0" + }, + { + "index": 65, + "pair_id": "F_origin_18_F_table_57_0", + "image_paths": [ + "data/Finance/Table/F_origin_18/F_table_57_0.png" + ], + "domain": "Finance", + "origin": "F_origin_18", + "table_id": "F_table_57_0" + }, + { + "index": 66, + "pair_id": "F_origin_18_F_table_58_0", + "image_paths": [ + "data/Finance/Table/F_origin_18/F_table_58_0.png" + ], + "domain": "Finance", + "origin": "F_origin_18", + "table_id": "F_table_58_0" + }, + { + "index": 67, + "pair_id": "F_origin_19_F_table_59_0", + "image_paths": [ + "data/Finance/Table/F_origin_19/F_table_59_0.png" + ], + "domain": "Finance", + "origin": "F_origin_19", + "table_id": "F_table_59_0" + }, + { + "index": 68, + "pair_id": "F_origin_19_F_table_59_1", + "image_paths": [ + "data/Finance/Table/F_origin_19/F_table_59_1.png" + ], + "domain": "Finance", + "origin": "F_origin_19", + "table_id": "F_table_59_1" + }, + { + "index": 69, + "pair_id": "F_origin_19_F_table_60_0", + "image_paths": [ + "data/Finance/Table/F_origin_19/F_table_60_0.png" + ], + "domain": "Finance", + "origin": "F_origin_19", + "table_id": "F_table_60_0" + }, + { + "index": 70, + "pair_id": "F_origin_19_F_table_61_0", + "image_paths": [ + "data/Finance/Table/F_origin_19/F_table_61_0.png" + ], + "domain": "Finance", + "origin": "F_origin_19", + "table_id": "F_table_61_0" + }, + { + "index": 71, + "pair_id": "F_origin_19_F_table_62_0", + "image_paths": [ + "data/Finance/Table/F_origin_19/F_table_62_0.png" + ], + "domain": "Finance", + "origin": "F_origin_19", + "table_id": "F_table_62_0" + }, + { + "index": 72, + "pair_id": "F_origin_19_F_table_63_0", + "image_paths": [ + "data/Finance/Table/F_origin_19/F_table_63_0.png" + ], + "domain": "Finance", + "origin": "F_origin_19", + "table_id": "F_table_63_0" + }, + { + "index": 73, + "pair_id": "F_origin_19_F_table_64_0", + "image_paths": [ + "data/Finance/Table/F_origin_19/F_table_64_0.png" + ], + "domain": "Finance", + "origin": "F_origin_19", + "table_id": "F_table_64_0" + }, + { + "index": 74, + "pair_id": "F_origin_19_F_table_65_0", + "image_paths": [ + "data/Finance/Table/F_origin_19/F_table_65_0.png" + ], + "domain": "Finance", + "origin": "F_origin_19", + "table_id": "F_table_65_0" + }, + { + "index": 75, + "pair_id": "F_origin_19_F_table_66_0", + "image_paths": [ + "data/Finance/Table/F_origin_19/F_table_66_0.png" + ], + "domain": "Finance", + "origin": "F_origin_19", + "table_id": "F_table_66_0" + }, + { + "index": 76, + "pair_id": "F_origin_2_F_table_3_0", + "image_paths": [ + "data/Finance/Table/F_origin_2/F_table_3_0.png" + ], + "domain": "Finance", + "origin": "F_origin_2", + "table_id": "F_table_3_0" + }, + { + "index": 77, + "pair_id": "F_origin_2_F_table_3_1", + "image_paths": [ + "data/Finance/Table/F_origin_2/F_table_3_1.png" + ], + "domain": "Finance", + "origin": "F_origin_2", + "table_id": "F_table_3_1" + }, + { + "index": 78, + "pair_id": "F_origin_2_F_table_3_2", + "image_paths": [ + "data/Finance/Table/F_origin_2/F_table_3_2.png" + ], + "domain": "Finance", + "origin": "F_origin_2", + "table_id": "F_table_3_2" + }, + { + "index": 79, + "pair_id": "F_origin_2_F_table_3_3", + "image_paths": [ + "data/Finance/Table/F_origin_2/F_table_3_3.png" + ], + "domain": "Finance", + "origin": "F_origin_2", + "table_id": "F_table_3_3" + }, + { + "index": 80, + "pair_id": "F_origin_2_F_table_3_4", + "image_paths": [ + "data/Finance/Table/F_origin_2/F_table_3_4.png" + ], + "domain": "Finance", + "origin": "F_origin_2", + "table_id": "F_table_3_4" + }, + { + "index": 81, + "pair_id": "F_origin_2_F_table_3_5", + "image_paths": [ + "data/Finance/Table/F_origin_2/F_table_3_5.png" + ], + "domain": "Finance", + "origin": "F_origin_2", + "table_id": "F_table_3_5" + }, + { + "index": 82, + "pair_id": "F_origin_2_F_table_4_0", + "image_paths": [ + "data/Finance/Table/F_origin_2/F_table_4_0.png" + ], + "domain": "Finance", + "origin": "F_origin_2", + "table_id": "F_table_4_0" + }, + { + "index": 83, + "pair_id": "F_origin_2_F_table_4_1", + "image_paths": [ + "data/Finance/Table/F_origin_2/F_table_4_1.png" + ], + "domain": "Finance", + "origin": "F_origin_2", + "table_id": "F_table_4_1" + }, + { + "index": 84, + "pair_id": "F_origin_20_F_table_67_0", + "image_paths": [ + "data/Finance/Table/F_origin_20/F_table_67_0.png" + ], + "domain": "Finance", + "origin": "F_origin_20", + "table_id": "F_table_67_0" + }, + { + "index": 85, + "pair_id": "F_origin_20_F_table_67_1", + "image_paths": [ + "data/Finance/Table/F_origin_20/F_table_67_1.png" + ], + "domain": "Finance", + "origin": "F_origin_20", + "table_id": "F_table_67_1" + }, + { + "index": 86, + "pair_id": "F_origin_20_F_table_68_0", + "image_paths": [ + "data/Finance/Table/F_origin_20/F_table_68_0.png" + ], + "domain": "Finance", + "origin": "F_origin_20", + "table_id": "F_table_68_0" + }, + { + "index": 87, + "pair_id": "F_origin_20_F_table_68_1", + "image_paths": [ + "data/Finance/Table/F_origin_20/F_table_68_1.png" + ], + "domain": "Finance", + "origin": "F_origin_20", + "table_id": "F_table_68_1" + }, + { + "index": 88, + "pair_id": "F_origin_20_F_table_68_2", + "image_paths": [ + "data/Finance/Table/F_origin_20/F_table_68_2.png" + ], + "domain": "Finance", + "origin": "F_origin_20", + "table_id": "F_table_68_2" + }, + { + "index": 89, + "pair_id": "F_origin_20_F_table_68_3", + "image_paths": [ + "data/Finance/Table/F_origin_20/F_table_68_3.png" + ], + "domain": "Finance", + "origin": "F_origin_20", + "table_id": "F_table_68_3" + }, + { + "index": 90, + "pair_id": "F_origin_20_F_table_68_4", + "image_paths": [ + "data/Finance/Table/F_origin_20/F_table_68_4.png" + ], + "domain": "Finance", + "origin": "F_origin_20", + "table_id": "F_table_68_4" + }, + { + "index": 91, + "pair_id": "F_origin_20_F_table_68_5", + "image_paths": [ + "data/Finance/Table/F_origin_20/F_table_68_5.png" + ], + "domain": "Finance", + "origin": "F_origin_20", + "table_id": "F_table_68_5" + }, + { + "index": 92, + "pair_id": "F_origin_20_F_table_68_6", + "image_paths": [ + "data/Finance/Table/F_origin_20/F_table_68_6.png" + ], + "domain": "Finance", + "origin": "F_origin_20", + "table_id": "F_table_68_6" + }, + { + "index": 93, + "pair_id": "F_origin_20_F_table_69_0", + "image_paths": [ + "data/Finance/Table/F_origin_20/F_table_69_0.png" + ], + "domain": "Finance", + "origin": "F_origin_20", + "table_id": "F_table_69_0" + }, + { + "index": 94, + "pair_id": "F_origin_21_F_table_70_0", + "image_paths": [ + "data/Finance/Table/F_origin_21/F_table_70_0.png" + ], + "domain": "Finance", + "origin": "F_origin_21", + "table_id": "F_table_70_0" + }, + { + "index": 95, + "pair_id": "F_origin_21_F_table_70_1", + "image_paths": [ + "data/Finance/Table/F_origin_21/F_table_70_1.png" + ], + "domain": "Finance", + "origin": "F_origin_21", + "table_id": "F_table_70_1" + }, + { + "index": 96, + "pair_id": "F_origin_21_F_table_70_2", + "image_paths": [ + "data/Finance/Table/F_origin_21/F_table_70_2.png" + ], + "domain": "Finance", + "origin": "F_origin_21", + "table_id": "F_table_70_2" + }, + { + "index": 97, + "pair_id": "F_origin_21_F_table_70_3", + "image_paths": [ + "data/Finance/Table/F_origin_21/F_table_70_3.png" + ], + "domain": "Finance", + "origin": "F_origin_21", + "table_id": "F_table_70_3" + }, + { + "index": 98, + "pair_id": "F_origin_21_F_table_71_0", + "image_paths": [ + "data/Finance/Table/F_origin_21/F_table_71_0.png" + ], + "domain": "Finance", + "origin": "F_origin_21", + "table_id": "F_table_71_0" + }, + { + "index": 99, + "pair_id": "F_origin_22_F_table_72_0", + "image_paths": [ + "data/Finance/Table/F_origin_22/F_table_72_0.png" + ], + "domain": "Finance", + "origin": "F_origin_22", + "table_id": "F_table_72_0" + }, + { + "index": 100, + "pair_id": "F_origin_22_F_table_72_1", + "image_paths": [ + "data/Finance/Table/F_origin_22/F_table_72_1.png" + ], + "domain": "Finance", + "origin": "F_origin_22", + "table_id": "F_table_72_1" + }, + { + "index": 101, + "pair_id": "F_origin_22_F_table_72_2", + "image_paths": [ + "data/Finance/Table/F_origin_22/F_table_72_2.png" + ], + "domain": "Finance", + "origin": "F_origin_22", + "table_id": "F_table_72_2" + }, + { + "index": 102, + "pair_id": "F_origin_22_F_table_72_3", + "image_paths": [ + "data/Finance/Table/F_origin_22/F_table_72_3.png" + ], + "domain": "Finance", + "origin": "F_origin_22", + "table_id": "F_table_72_3" + }, + { + "index": 103, + "pair_id": "F_origin_22_F_table_72_4", + "image_paths": [ + "data/Finance/Table/F_origin_22/F_table_72_4.png" + ], + "domain": "Finance", + "origin": "F_origin_22", + "table_id": "F_table_72_4" + }, + { + "index": 104, + "pair_id": "F_origin_22_F_table_72_5", + "image_paths": [ + "data/Finance/Table/F_origin_22/F_table_72_5.png" + ], + "domain": "Finance", + "origin": "F_origin_22", + "table_id": "F_table_72_5" + }, + { + "index": 105, + "pair_id": "F_origin_22_F_table_73_0", + "image_paths": [ + "data/Finance/Table/F_origin_22/F_table_73_0.png" + ], + "domain": "Finance", + "origin": "F_origin_22", + "table_id": "F_table_73_0" + }, + { + "index": 106, + "pair_id": "F_origin_23_F_table_74_0", + "image_paths": [ + "data/Finance/Table/F_origin_23/F_table_74_0.png" + ], + "domain": "Finance", + "origin": "F_origin_23", + "table_id": "F_table_74_0" + }, + { + "index": 107, + "pair_id": "F_origin_23_F_table_74_1", + "image_paths": [ + "data/Finance/Table/F_origin_23/F_table_74_1.png" + ], + "domain": "Finance", + "origin": "F_origin_23", + "table_id": "F_table_74_1" + }, + { + "index": 108, + "pair_id": "F_origin_23_F_table_74_2", + "image_paths": [ + "data/Finance/Table/F_origin_23/F_table_74_2.png" + ], + "domain": "Finance", + "origin": "F_origin_23", + "table_id": "F_table_74_2" + }, + { + "index": 109, + "pair_id": "F_origin_23_F_table_74_3", + "image_paths": [ + "data/Finance/Table/F_origin_23/F_table_74_3.png" + ], + "domain": "Finance", + "origin": "F_origin_23", + "table_id": "F_table_74_3" + }, + { + "index": 110, + "pair_id": "F_origin_23_F_table_74_4", + "image_paths": [ + "data/Finance/Table/F_origin_23/F_table_74_4.png" + ], + "domain": "Finance", + "origin": "F_origin_23", + "table_id": "F_table_74_4" + }, + { + "index": 111, + "pair_id": "F_origin_23_F_table_74_5", + "image_paths": [ + "data/Finance/Table/F_origin_23/F_table_74_5.png" + ], + "domain": "Finance", + "origin": "F_origin_23", + "table_id": "F_table_74_5" + }, + { + "index": 112, + "pair_id": "F_origin_23_F_table_75_0", + "image_paths": [ + "data/Finance/Table/F_origin_23/F_table_75_0.png" + ], + "domain": "Finance", + "origin": "F_origin_23", + "table_id": "F_table_75_0" + }, + { + "index": 113, + "pair_id": "F_origin_24_F_table_75_0", + "image_paths": [ + "data/Finance/Table/F_origin_24/F_table_75_0.png" + ], + "domain": "Finance", + "origin": "F_origin_24", + "table_id": "F_table_75_0" + }, + { + "index": 114, + "pair_id": "F_origin_24_F_table_75_1", + "image_paths": [ + "data/Finance/Table/F_origin_24/F_table_75_1.png" + ], + "domain": "Finance", + "origin": "F_origin_24", + "table_id": "F_table_75_1" + }, + { + "index": 115, + "pair_id": "F_origin_24_F_table_75_2", + "image_paths": [ + "data/Finance/Table/F_origin_24/F_table_75_2.png" + ], + "domain": "Finance", + "origin": "F_origin_24", + "table_id": "F_table_75_2" + }, + { + "index": 116, + "pair_id": "F_origin_24_F_table_75_3", + "image_paths": [ + "data/Finance/Table/F_origin_24/F_table_75_3.png" + ], + "domain": "Finance", + "origin": "F_origin_24", + "table_id": "F_table_75_3" + }, + { + "index": 117, + "pair_id": "F_origin_24_F_table_75_4", + "image_paths": [ + "data/Finance/Table/F_origin_24/F_table_75_4.png" + ], + "domain": "Finance", + "origin": "F_origin_24", + "table_id": "F_table_75_4" + }, + { + "index": 118, + "pair_id": "F_origin_24_F_table_75_5", + "image_paths": [ + "data/Finance/Table/F_origin_24/F_table_75_5.png" + ], + "domain": "Finance", + "origin": "F_origin_24", + "table_id": "F_table_75_5" + }, + { + "index": 119, + "pair_id": "F_origin_24_F_table_75_6", + "image_paths": [ + "data/Finance/Table/F_origin_24/F_table_75_6.png" + ], + "domain": "Finance", + "origin": "F_origin_24", + "table_id": "F_table_75_6" + }, + { + "index": 120, + "pair_id": "F_origin_24_F_table_75_7", + "image_paths": [ + "data/Finance/Table/F_origin_24/F_table_75_7.png" + ], + "domain": "Finance", + "origin": "F_origin_24", + "table_id": "F_table_75_7" + }, + { + "index": 121, + "pair_id": "F_origin_24_F_table_76_0", + "image_paths": [ + "data/Finance/Table/F_origin_24/F_table_76_0.png" + ], + "domain": "Finance", + "origin": "F_origin_24", + "table_id": "F_table_76_0" + }, + { + "index": 122, + "pair_id": "F_origin_24_F_table_76_1", + "image_paths": [ + "data/Finance/Table/F_origin_24/F_table_76_1.png" + ], + "domain": "Finance", + "origin": "F_origin_24", + "table_id": "F_table_76_1" + }, + { + "index": 123, + "pair_id": "F_origin_24_F_table_76_2", + "image_paths": [ + "data/Finance/Table/F_origin_24/F_table_76_2.png" + ], + "domain": "Finance", + "origin": "F_origin_24", + "table_id": "F_table_76_2" + }, + { + "index": 124, + "pair_id": "F_origin_24_F_table_76_3", + "image_paths": [ + "data/Finance/Table/F_origin_24/F_table_76_3.png" + ], + "domain": "Finance", + "origin": "F_origin_24", + "table_id": "F_table_76_3" + }, + { + "index": 125, + "pair_id": "F_origin_24_F_table_76_4", + "image_paths": [ + "data/Finance/Table/F_origin_24/F_table_76_4.png" + ], + "domain": "Finance", + "origin": "F_origin_24", + "table_id": "F_table_76_4" + }, + { + "index": 126, + "pair_id": "F_origin_24_F_table_77_0", + "image_paths": [ + "data/Finance/Table/F_origin_24/F_table_77_0.png" + ], + "domain": "Finance", + "origin": "F_origin_24", + "table_id": "F_table_77_0" + }, + { + "index": 127, + "pair_id": "F_origin_25_F_table_79_0", + "image_paths": [ + "data/Finance/Table/F_origin_25/F_table_79_0.png" + ], + "domain": "Finance", + "origin": "F_origin_25", + "table_id": "F_table_79_0" + }, + { + "index": 128, + "pair_id": "F_origin_25_F_table_80_0", + "image_paths": [ + "data/Finance/Table/F_origin_25/F_table_80_0.png" + ], + "domain": "Finance", + "origin": "F_origin_25", + "table_id": "F_table_80_0" + }, + { + "index": 129, + "pair_id": "F_origin_25_F_table_81_0", + "image_paths": [ + "data/Finance/Table/F_origin_25/F_table_81_0.png" + ], + "domain": "Finance", + "origin": "F_origin_25", + "table_id": "F_table_81_0" + }, + { + "index": 130, + "pair_id": "F_origin_25_F_table_82_0", + "image_paths": [ + "data/Finance/Table/F_origin_25/F_table_82_0.png" + ], + "domain": "Finance", + "origin": "F_origin_25", + "table_id": "F_table_82_0" + }, + { + "index": 131, + "pair_id": "F_origin_25_F_table_82_1", + "image_paths": [ + "data/Finance/Table/F_origin_25/F_table_82_1.png" + ], + "domain": "Finance", + "origin": "F_origin_25", + "table_id": "F_table_82_1" + }, + { + "index": 132, + "pair_id": "F_origin_25_F_table_82_2", + "image_paths": [ + "data/Finance/Table/F_origin_25/F_table_82_2.png" + ], + "domain": "Finance", + "origin": "F_origin_25", + "table_id": "F_table_82_2" + }, + { + "index": 133, + "pair_id": "F_origin_25_F_table_83_0", + "image_paths": [ + "data/Finance/Table/F_origin_25/F_table_83_0.png" + ], + "domain": "Finance", + "origin": "F_origin_25", + "table_id": "F_table_83_0" + }, + { + "index": 134, + "pair_id": "F_origin_26_F_table_84_0", + "image_paths": [ + "data/Finance/Table/F_origin_26/F_table_84_0.png" + ], + "domain": "Finance", + "origin": "F_origin_26", + "table_id": "F_table_84_0" + }, + { + "index": 135, + "pair_id": "F_origin_26_F_table_84_1", + "image_paths": [ + "data/Finance/Table/F_origin_26/F_table_84_1.png" + ], + "domain": "Finance", + "origin": "F_origin_26", + "table_id": "F_table_84_1" + }, + { + "index": 136, + "pair_id": "F_origin_26_F_table_85_0", + "image_paths": [ + "data/Finance/Table/F_origin_26/F_table_85_0.png" + ], + "domain": "Finance", + "origin": "F_origin_26", + "table_id": "F_table_85_0" + }, + { + "index": 137, + "pair_id": "F_origin_26_F_table_86_0", + "image_paths": [ + "data/Finance/Table/F_origin_26/F_table_86_0.png" + ], + "domain": "Finance", + "origin": "F_origin_26", + "table_id": "F_table_86_0" + }, + { + "index": 138, + "pair_id": "F_origin_26_F_table_87_0", + "image_paths": [ + "data/Finance/Table/F_origin_26/F_table_87_0.png" + ], + "domain": "Finance", + "origin": "F_origin_26", + "table_id": "F_table_87_0" + }, + { + "index": 139, + "pair_id": "F_origin_27_F_table_88_0", + "image_paths": [ + "data/Finance/Table/F_origin_27/F_table_88_0.png" + ], + "domain": "Finance", + "origin": "F_origin_27", + "table_id": "F_table_88_0" + }, + { + "index": 140, + "pair_id": "F_origin_27_F_table_88_1", + "image_paths": [ + "data/Finance/Table/F_origin_27/F_table_88_1.png" + ], + "domain": "Finance", + "origin": "F_origin_27", + "table_id": "F_table_88_1" + }, + { + "index": 141, + "pair_id": "F_origin_27_F_table_89_0", + "image_paths": [ + "data/Finance/Table/F_origin_27/F_table_89_0.png" + ], + "domain": "Finance", + "origin": "F_origin_27", + "table_id": "F_table_89_0" + }, + { + "index": 142, + "pair_id": "F_origin_27_F_table_89_1", + "image_paths": [ + "data/Finance/Table/F_origin_27/F_table_89_1.png" + ], + "domain": "Finance", + "origin": "F_origin_27", + "table_id": "F_table_89_1" + }, + { + "index": 143, + "pair_id": "F_origin_27_F_table_89_2", + "image_paths": [ + "data/Finance/Table/F_origin_27/F_table_89_2.png" + ], + "domain": "Finance", + "origin": "F_origin_27", + "table_id": "F_table_89_2" + }, + { + "index": 144, + "pair_id": "F_origin_27_F_table_90_0", + "image_paths": [ + "data/Finance/Table/F_origin_27/F_table_90_0.png" + ], + "domain": "Finance", + "origin": "F_origin_27", + "table_id": "F_table_90_0" + }, + { + "index": 145, + "pair_id": "F_origin_28_F_table_91_0", + "image_paths": [ + "data/Finance/Table/F_origin_28/F_table_91_0.png" + ], + "domain": "Finance", + "origin": "F_origin_28", + "table_id": "F_table_91_0" + }, + { + "index": 146, + "pair_id": "F_origin_28_F_table_91_1", + "image_paths": [ + "data/Finance/Table/F_origin_28/F_table_91_1.png" + ], + "domain": "Finance", + "origin": "F_origin_28", + "table_id": "F_table_91_1" + }, + { + "index": 147, + "pair_id": "F_origin_28_F_table_91_2", + "image_paths": [ + "data/Finance/Table/F_origin_28/F_table_91_2.png" + ], + "domain": "Finance", + "origin": "F_origin_28", + "table_id": "F_table_91_2" + }, + { + "index": 148, + "pair_id": "F_origin_28_F_table_91_3", + "image_paths": [ + "data/Finance/Table/F_origin_28/F_table_91_3.png" + ], + "domain": "Finance", + "origin": "F_origin_28", + "table_id": "F_table_91_3" + }, + { + "index": 149, + "pair_id": "F_origin_28_F_table_92_0", + "image_paths": [ + "data/Finance/Table/F_origin_28/F_table_92_0.png" + ], + "domain": "Finance", + "origin": "F_origin_28", + "table_id": "F_table_92_0" + }, + { + "index": 150, + "pair_id": "F_origin_29_F_table_93_0", + "image_paths": [ + "data/Finance/Table/F_origin_29/F_table_93_0.png" + ], + "domain": "Finance", + "origin": "F_origin_29", + "table_id": "F_table_93_0" + }, + { + "index": 151, + "pair_id": "F_origin_29_F_table_93_1", + "image_paths": [ + "data/Finance/Table/F_origin_29/F_table_93_1.png" + ], + "domain": "Finance", + "origin": "F_origin_29", + "table_id": "F_table_93_1" + }, + { + "index": 152, + "pair_id": "F_origin_29_F_table_93_2", + "image_paths": [ + "data/Finance/Table/F_origin_29/F_table_93_2.png" + ], + "domain": "Finance", + "origin": "F_origin_29", + "table_id": "F_table_93_2" + }, + { + "index": 153, + "pair_id": "F_origin_29_F_table_93_3", + "image_paths": [ + "data/Finance/Table/F_origin_29/F_table_93_3.png" + ], + "domain": "Finance", + "origin": "F_origin_29", + "table_id": "F_table_93_3" + }, + { + "index": 154, + "pair_id": "F_origin_29_F_table_93_4", + "image_paths": [ + "data/Finance/Table/F_origin_29/F_table_93_4.png" + ], + "domain": "Finance", + "origin": "F_origin_29", + "table_id": "F_table_93_4" + }, + { + "index": 155, + "pair_id": "F_origin_29_F_table_94_0", + "image_paths": [ + "data/Finance/Table/F_origin_29/F_table_94_0.png" + ], + "domain": "Finance", + "origin": "F_origin_29", + "table_id": "F_table_94_0" + }, + { + "index": 156, + "pair_id": "F_origin_3_F_table_5_0", + "image_paths": [ + "data/Finance/Table/F_origin_3/F_table_5_0.png" + ], + "domain": "Finance", + "origin": "F_origin_3", + "table_id": "F_table_5_0" + }, + { + "index": 157, + "pair_id": "F_origin_3_F_table_5_1", + "image_paths": [ + "data/Finance/Table/F_origin_3/F_table_5_1.png" + ], + "domain": "Finance", + "origin": "F_origin_3", + "table_id": "F_table_5_1" + }, + { + "index": 158, + "pair_id": "F_origin_3_F_table_5_10", + "image_paths": [ + "data/Finance/Table/F_origin_3/F_table_5_10.png" + ], + "domain": "Finance", + "origin": "F_origin_3", + "table_id": "F_table_5_10" + }, + { + "index": 159, + "pair_id": "F_origin_3_F_table_5_11", + "image_paths": [ + "data/Finance/Table/F_origin_3/F_table_5_11.png" + ], + "domain": "Finance", + "origin": "F_origin_3", + "table_id": "F_table_5_11" + }, + { + "index": 160, + "pair_id": "F_origin_3_F_table_5_2", + "image_paths": [ + "data/Finance/Table/F_origin_3/F_table_5_2.png" + ], + "domain": "Finance", + "origin": "F_origin_3", + "table_id": "F_table_5_2" + }, + { + "index": 161, + "pair_id": "F_origin_3_F_table_5_3", + "image_paths": [ + "data/Finance/Table/F_origin_3/F_table_5_3.png" + ], + "domain": "Finance", + "origin": "F_origin_3", + "table_id": "F_table_5_3" + }, + { + "index": 162, + "pair_id": "F_origin_3_F_table_5_4", + "image_paths": [ + "data/Finance/Table/F_origin_3/F_table_5_4.png" + ], + "domain": "Finance", + "origin": "F_origin_3", + "table_id": "F_table_5_4" + }, + { + "index": 163, + "pair_id": "F_origin_3_F_table_5_5", + "image_paths": [ + "data/Finance/Table/F_origin_3/F_table_5_5.png" + ], + "domain": "Finance", + "origin": "F_origin_3", + "table_id": "F_table_5_5" + }, + { + "index": 164, + "pair_id": "F_origin_3_F_table_5_6", + "image_paths": [ + "data/Finance/Table/F_origin_3/F_table_5_6.png" + ], + "domain": "Finance", + "origin": "F_origin_3", + "table_id": "F_table_5_6" + }, + { + "index": 165, + "pair_id": "F_origin_3_F_table_5_7", + "image_paths": [ + "data/Finance/Table/F_origin_3/F_table_5_7.png" + ], + "domain": "Finance", + "origin": "F_origin_3", + "table_id": "F_table_5_7" + }, + { + "index": 166, + "pair_id": "F_origin_3_F_table_5_8", + "image_paths": [ + "data/Finance/Table/F_origin_3/F_table_5_8.png" + ], + "domain": "Finance", + "origin": "F_origin_3", + "table_id": "F_table_5_8" + }, + { + "index": 167, + "pair_id": "F_origin_3_F_table_5_9", + "image_paths": [ + "data/Finance/Table/F_origin_3/F_table_5_9.png" + ], + "domain": "Finance", + "origin": "F_origin_3", + "table_id": "F_table_5_9" + }, + { + "index": 168, + "pair_id": "F_origin_30_F_table_95_0", + "image_paths": [ + "data/Finance/Table/F_origin_30/F_table_95_0.png" + ], + "domain": "Finance", + "origin": "F_origin_30", + "table_id": "F_table_95_0" + }, + { + "index": 169, + "pair_id": "F_origin_30_F_table_96_0", + "image_paths": [ + "data/Finance/Table/F_origin_30/F_table_96_0.png" + ], + "domain": "Finance", + "origin": "F_origin_30", + "table_id": "F_table_96_0" + }, + { + "index": 170, + "pair_id": "F_origin_30_F_table_97_0", + "image_paths": [ + "data/Finance/Table/F_origin_30/F_table_97_0.png" + ], + "domain": "Finance", + "origin": "F_origin_30", + "table_id": "F_table_97_0" + }, + { + "index": 171, + "pair_id": "F_origin_30_F_table_97_1", + "image_paths": [ + "data/Finance/Table/F_origin_30/F_table_97_1.png" + ], + "domain": "Finance", + "origin": "F_origin_30", + "table_id": "F_table_97_1" + }, + { + "index": 172, + "pair_id": "F_origin_30_F_table_97_2", + "image_paths": [ + "data/Finance/Table/F_origin_30/F_table_97_2.png" + ], + "domain": "Finance", + "origin": "F_origin_30", + "table_id": "F_table_97_2" + }, + { + "index": 173, + "pair_id": "F_origin_31_F_table_100_0", + "image_paths": [ + "data/Finance/Table/F_origin_31/F_table_100_0.png" + ], + "domain": "Finance", + "origin": "F_origin_31", + "table_id": "F_table_100_0" + }, + { + "index": 174, + "pair_id": "F_origin_31_F_table_100_1", + "image_paths": [ + "data/Finance/Table/F_origin_31/F_table_100_1.png" + ], + "domain": "Finance", + "origin": "F_origin_31", + "table_id": "F_table_100_1" + }, + { + "index": 175, + "pair_id": "F_origin_31_F_table_100_2", + "image_paths": [ + "data/Finance/Table/F_origin_31/F_table_100_2.png" + ], + "domain": "Finance", + "origin": "F_origin_31", + "table_id": "F_table_100_2" + }, + { + "index": 176, + "pair_id": "F_origin_31_F_table_98_0", + "image_paths": [ + "data/Finance/Table/F_origin_31/F_table_98_0.png" + ], + "domain": "Finance", + "origin": "F_origin_31", + "table_id": "F_table_98_0" + }, + { + "index": 177, + "pair_id": "F_origin_31_F_table_99_0", + "image_paths": [ + "data/Finance/Table/F_origin_31/F_table_99_0.png" + ], + "domain": "Finance", + "origin": "F_origin_31", + "table_id": "F_table_99_0" + }, + { + "index": 178, + "pair_id": "F_origin_32_F_table_101_0", + "image_paths": [ + "data/Finance/Table/F_origin_32/F_table_101_0.png" + ], + "domain": "Finance", + "origin": "F_origin_32", + "table_id": "F_table_101_0" + }, + { + "index": 179, + "pair_id": "F_origin_32_F_table_102_0", + "image_paths": [ + "data/Finance/Table/F_origin_32/F_table_102_0.png" + ], + "domain": "Finance", + "origin": "F_origin_32", + "table_id": "F_table_102_0" + }, + { + "index": 180, + "pair_id": "F_origin_32_F_table_103_0", + "image_paths": [ + "data/Finance/Table/F_origin_32/F_table_103_0.png" + ], + "domain": "Finance", + "origin": "F_origin_32", + "table_id": "F_table_103_0" + }, + { + "index": 181, + "pair_id": "F_origin_32_F_table_103_1", + "image_paths": [ + "data/Finance/Table/F_origin_32/F_table_103_1.png" + ], + "domain": "Finance", + "origin": "F_origin_32", + "table_id": "F_table_103_1" + }, + { + "index": 182, + "pair_id": "F_origin_32_F_table_103_2", + "image_paths": [ + "data/Finance/Table/F_origin_32/F_table_103_2.png" + ], + "domain": "Finance", + "origin": "F_origin_32", + "table_id": "F_table_103_2" + }, + { + "index": 183, + "pair_id": "F_origin_33_F_table_104_0", + "image_paths": [ + "data/Finance/Table/F_origin_33/F_table_104_0.png" + ], + "domain": "Finance", + "origin": "F_origin_33", + "table_id": "F_table_104_0" + }, + { + "index": 184, + "pair_id": "F_origin_33_F_table_105_0", + "image_paths": [ + "data/Finance/Table/F_origin_33/F_table_105_0.png" + ], + "domain": "Finance", + "origin": "F_origin_33", + "table_id": "F_table_105_0" + }, + { + "index": 185, + "pair_id": "F_origin_33_F_table_106_0", + "image_paths": [ + "data/Finance/Table/F_origin_33/F_table_106_0.png" + ], + "domain": "Finance", + "origin": "F_origin_33", + "table_id": "F_table_106_0" + }, + { + "index": 186, + "pair_id": "F_origin_33_F_table_106_1", + "image_paths": [ + "data/Finance/Table/F_origin_33/F_table_106_1.png" + ], + "domain": "Finance", + "origin": "F_origin_33", + "table_id": "F_table_106_1" + }, + { + "index": 187, + "pair_id": "F_origin_33_F_table_106_2", + "image_paths": [ + "data/Finance/Table/F_origin_33/F_table_106_2.png" + ], + "domain": "Finance", + "origin": "F_origin_33", + "table_id": "F_table_106_2" + }, + { + "index": 188, + "pair_id": "F_origin_34_F_table_107_0", + "image_paths": [ + "data/Finance/Table/F_origin_34/F_table_107_0.png" + ], + "domain": "Finance", + "origin": "F_origin_34", + "table_id": "F_table_107_0" + }, + { + "index": 189, + "pair_id": "F_origin_34_F_table_108_0", + "image_paths": [ + "data/Finance/Table/F_origin_34/F_table_108_0.png" + ], + "domain": "Finance", + "origin": "F_origin_34", + "table_id": "F_table_108_0" + }, + { + "index": 190, + "pair_id": "F_origin_34_F_table_109_0", + "image_paths": [ + "data/Finance/Table/F_origin_34/F_table_109_0.png" + ], + "domain": "Finance", + "origin": "F_origin_34", + "table_id": "F_table_109_0" + }, + { + "index": 191, + "pair_id": "F_origin_34_F_table_109_1", + "image_paths": [ + "data/Finance/Table/F_origin_34/F_table_109_1.png" + ], + "domain": "Finance", + "origin": "F_origin_34", + "table_id": "F_table_109_1" + }, + { + "index": 192, + "pair_id": "F_origin_34_F_table_109_2", + "image_paths": [ + "data/Finance/Table/F_origin_34/F_table_109_2.png" + ], + "domain": "Finance", + "origin": "F_origin_34", + "table_id": "F_table_109_2" + }, + { + "index": 193, + "pair_id": "F_origin_35_F_table_110_0", + "image_paths": [ + "data/Finance/Table/F_origin_35/F_table_110_0.png" + ], + "domain": "Finance", + "origin": "F_origin_35", + "table_id": "F_table_110_0" + }, + { + "index": 194, + "pair_id": "F_origin_35_F_table_111_0", + "image_paths": [ + "data/Finance/Table/F_origin_35/F_table_111_0.png" + ], + "domain": "Finance", + "origin": "F_origin_35", + "table_id": "F_table_111_0" + }, + { + "index": 195, + "pair_id": "F_origin_35_F_table_112_0", + "image_paths": [ + "data/Finance/Table/F_origin_35/F_table_112_0.png" + ], + "domain": "Finance", + "origin": "F_origin_35", + "table_id": "F_table_112_0" + }, + { + "index": 196, + "pair_id": "F_origin_35_F_table_112_1", + "image_paths": [ + "data/Finance/Table/F_origin_35/F_table_112_1.png" + ], + "domain": "Finance", + "origin": "F_origin_35", + "table_id": "F_table_112_1" + }, + { + "index": 197, + "pair_id": "F_origin_35_F_table_112_2", + "image_paths": [ + "data/Finance/Table/F_origin_35/F_table_112_2.png" + ], + "domain": "Finance", + "origin": "F_origin_35", + "table_id": "F_table_112_2" + }, + { + "index": 198, + "pair_id": "F_origin_36_F_table_113_0", + "image_paths": [ + "data/Finance/Table/F_origin_36/F_table_113_0.png" + ], + "domain": "Finance", + "origin": "F_origin_36", + "table_id": "F_table_113_0" + }, + { + "index": 199, + "pair_id": "F_origin_36_F_table_114_0", + "image_paths": [ + "data/Finance/Table/F_origin_36/F_table_114_0.png" + ], + "domain": "Finance", + "origin": "F_origin_36", + "table_id": "F_table_114_0" + }, + { + "index": 200, + "pair_id": "F_origin_36_F_table_115_0", + "image_paths": [ + "data/Finance/Table/F_origin_36/F_table_115_0.png" + ], + "domain": "Finance", + "origin": "F_origin_36", + "table_id": "F_table_115_0" + }, + { + "index": 201, + "pair_id": "F_origin_36_F_table_115_1", + "image_paths": [ + "data/Finance/Table/F_origin_36/F_table_115_1.png" + ], + "domain": "Finance", + "origin": "F_origin_36", + "table_id": "F_table_115_1" + }, + { + "index": 202, + "pair_id": "F_origin_36_F_table_115_2", + "image_paths": [ + "data/Finance/Table/F_origin_36/F_table_115_2.png" + ], + "domain": "Finance", + "origin": "F_origin_36", + "table_id": "F_table_115_2" + }, + { + "index": 203, + "pair_id": "F_origin_36_F_table_116_0", + "image_paths": [ + "data/Finance/Table/F_origin_36/F_table_116_0.png" + ], + "domain": "Finance", + "origin": "F_origin_36", + "table_id": "F_table_116_0" + }, + { + "index": 204, + "pair_id": "F_origin_37_F_table_117_0", + "image_paths": [ + "data/Finance/Table/F_origin_37/F_table_117_0.png" + ], + "domain": "Finance", + "origin": "F_origin_37", + "table_id": "F_table_117_0" + }, + { + "index": 205, + "pair_id": "F_origin_37_F_table_118_0", + "image_paths": [ + "data/Finance/Table/F_origin_37/F_table_118_0.png" + ], + "domain": "Finance", + "origin": "F_origin_37", + "table_id": "F_table_118_0" + }, + { + "index": 206, + "pair_id": "F_origin_37_F_table_119_0", + "image_paths": [ + "data/Finance/Table/F_origin_37/F_table_119_0.png" + ], + "domain": "Finance", + "origin": "F_origin_37", + "table_id": "F_table_119_0" + }, + { + "index": 207, + "pair_id": "F_origin_37_F_table_119_1", + "image_paths": [ + "data/Finance/Table/F_origin_37/F_table_119_1.png" + ], + "domain": "Finance", + "origin": "F_origin_37", + "table_id": "F_table_119_1" + }, + { + "index": 208, + "pair_id": "F_origin_37_F_table_119_2", + "image_paths": [ + "data/Finance/Table/F_origin_37/F_table_119_2.png" + ], + "domain": "Finance", + "origin": "F_origin_37", + "table_id": "F_table_119_2" + }, + { + "index": 209, + "pair_id": "F_origin_37_F_table_119_3", + "image_paths": [ + "data/Finance/Table/F_origin_37/F_table_119_3.png" + ], + "domain": "Finance", + "origin": "F_origin_37", + "table_id": "F_table_119_3" + }, + { + "index": 210, + "pair_id": "F_origin_37_F_table_119_4", + "image_paths": [ + "data/Finance/Table/F_origin_37/F_table_119_4.png" + ], + "domain": "Finance", + "origin": "F_origin_37", + "table_id": "F_table_119_4" + }, + { + "index": 211, + "pair_id": "F_origin_38_F_table_120_0", + "image_paths": [ + "data/Finance/Table/F_origin_38/F_table_120_0.png" + ], + "domain": "Finance", + "origin": "F_origin_38", + "table_id": "F_table_120_0" + }, + { + "index": 212, + "pair_id": "F_origin_38_F_table_121_0", + "image_paths": [ + "data/Finance/Table/F_origin_38/F_table_121_0.png" + ], + "domain": "Finance", + "origin": "F_origin_38", + "table_id": "F_table_121_0" + }, + { + "index": 213, + "pair_id": "F_origin_38_F_table_122_0", + "image_paths": [ + "data/Finance/Table/F_origin_38/F_table_122_0.png" + ], + "domain": "Finance", + "origin": "F_origin_38", + "table_id": "F_table_122_0" + }, + { + "index": 214, + "pair_id": "F_origin_38_F_table_122_1", + "image_paths": [ + "data/Finance/Table/F_origin_38/F_table_122_1.png" + ], + "domain": "Finance", + "origin": "F_origin_38", + "table_id": "F_table_122_1" + }, + { + "index": 215, + "pair_id": "F_origin_38_F_table_122_2", + "image_paths": [ + "data/Finance/Table/F_origin_38/F_table_122_2.png" + ], + "domain": "Finance", + "origin": "F_origin_38", + "table_id": "F_table_122_2" + }, + { + "index": 216, + "pair_id": "F_origin_39_F_table_123_0", + "image_paths": [ + "data/Finance/Table/F_origin_39/F_table_123_0.png" + ], + "domain": "Finance", + "origin": "F_origin_39", + "table_id": "F_table_123_0" + }, + { + "index": 217, + "pair_id": "F_origin_39_F_table_124_0", + "image_paths": [ + "data/Finance/Table/F_origin_39/F_table_124_0.png" + ], + "domain": "Finance", + "origin": "F_origin_39", + "table_id": "F_table_124_0" + }, + { + "index": 218, + "pair_id": "F_origin_39_F_table_125_0", + "image_paths": [ + "data/Finance/Table/F_origin_39/F_table_125_0.png" + ], + "domain": "Finance", + "origin": "F_origin_39", + "table_id": "F_table_125_0" + }, + { + "index": 219, + "pair_id": "F_origin_39_F_table_125_1", + "image_paths": [ + "data/Finance/Table/F_origin_39/F_table_125_1.png" + ], + "domain": "Finance", + "origin": "F_origin_39", + "table_id": "F_table_125_1" + }, + { + "index": 220, + "pair_id": "F_origin_39_F_table_125_2", + "image_paths": [ + "data/Finance/Table/F_origin_39/F_table_125_2.png" + ], + "domain": "Finance", + "origin": "F_origin_39", + "table_id": "F_table_125_2" + }, + { + "index": 221, + "pair_id": "F_origin_4_F_table_6_0", + "image_paths": [ + "data/Finance/Table/F_origin_4/F_table_6_0.png" + ], + "domain": "Finance", + "origin": "F_origin_4", + "table_id": "F_table_6_0" + }, + { + "index": 222, + "pair_id": "F_origin_4_F_table_6_1", + "image_paths": [ + "data/Finance/Table/F_origin_4/F_table_6_1.png" + ], + "domain": "Finance", + "origin": "F_origin_4", + "table_id": "F_table_6_1" + }, + { + "index": 223, + "pair_id": "F_origin_4_F_table_6_2", + "image_paths": [ + "data/Finance/Table/F_origin_4/F_table_6_2.png" + ], + "domain": "Finance", + "origin": "F_origin_4", + "table_id": "F_table_6_2" + }, + { + "index": 224, + "pair_id": "F_origin_4_F_table_6_3", + "image_paths": [ + "data/Finance/Table/F_origin_4/F_table_6_3.png" + ], + "domain": "Finance", + "origin": "F_origin_4", + "table_id": "F_table_6_3" + }, + { + "index": 225, + "pair_id": "F_origin_40_F_table_126_0", + "image_paths": [ + "data/Finance/Table/F_origin_40/F_table_126_0.png" + ], + "domain": "Finance", + "origin": "F_origin_40", + "table_id": "F_table_126_0" + }, + { + "index": 226, + "pair_id": "F_origin_40_F_table_127_0", + "image_paths": [ + "data/Finance/Table/F_origin_40/F_table_127_0.png" + ], + "domain": "Finance", + "origin": "F_origin_40", + "table_id": "F_table_127_0" + }, + { + "index": 227, + "pair_id": "F_origin_40_F_table_128_0", + "image_paths": [ + "data/Finance/Table/F_origin_40/F_table_128_0.png" + ], + "domain": "Finance", + "origin": "F_origin_40", + "table_id": "F_table_128_0" + }, + { + "index": 228, + "pair_id": "F_origin_40_F_table_128_1", + "image_paths": [ + "data/Finance/Table/F_origin_40/F_table_128_1.png" + ], + "domain": "Finance", + "origin": "F_origin_40", + "table_id": "F_table_128_1" + }, + { + "index": 229, + "pair_id": "F_origin_40_F_table_128_2", + "image_paths": [ + "data/Finance/Table/F_origin_40/F_table_128_2.png" + ], + "domain": "Finance", + "origin": "F_origin_40", + "table_id": "F_table_128_2" + }, + { + "index": 230, + "pair_id": "F_origin_41_F_table_129_0", + "image_paths": [ + "data/Finance/Table/F_origin_41/F_table_129_0.png" + ], + "domain": "Finance", + "origin": "F_origin_41", + "table_id": "F_table_129_0" + }, + { + "index": 231, + "pair_id": "F_origin_41_F_table_129_1", + "image_paths": [ + "data/Finance/Table/F_origin_41/F_table_129_1.png" + ], + "domain": "Finance", + "origin": "F_origin_41", + "table_id": "F_table_129_1" + }, + { + "index": 232, + "pair_id": "F_origin_41_F_table_129_2", + "image_paths": [ + "data/Finance/Table/F_origin_41/F_table_129_2.png" + ], + "domain": "Finance", + "origin": "F_origin_41", + "table_id": "F_table_129_2" + }, + { + "index": 233, + "pair_id": "F_origin_41_F_table_129_3", + "image_paths": [ + "data/Finance/Table/F_origin_41/F_table_129_3.png" + ], + "domain": "Finance", + "origin": "F_origin_41", + "table_id": "F_table_129_3" + }, + { + "index": 234, + "pair_id": "F_origin_41_F_table_129_4", + "image_paths": [ + "data/Finance/Table/F_origin_41/F_table_129_4.png" + ], + "domain": "Finance", + "origin": "F_origin_41", + "table_id": "F_table_129_4" + }, + { + "index": 235, + "pair_id": "F_origin_41_F_table_129_5", + "image_paths": [ + "data/Finance/Table/F_origin_41/F_table_129_5.png" + ], + "domain": "Finance", + "origin": "F_origin_41", + "table_id": "F_table_129_5" + }, + { + "index": 236, + "pair_id": "F_origin_42_F_table_130_0", + "image_paths": [ + "data/Finance/Table/F_origin_42/F_table_130_0.png" + ], + "domain": "Finance", + "origin": "F_origin_42", + "table_id": "F_table_130_0" + }, + { + "index": 237, + "pair_id": "F_origin_42_F_table_130_1", + "image_paths": [ + "data/Finance/Table/F_origin_42/F_table_130_1.png" + ], + "domain": "Finance", + "origin": "F_origin_42", + "table_id": "F_table_130_1" + }, + { + "index": 238, + "pair_id": "F_origin_42_F_table_130_2", + "image_paths": [ + "data/Finance/Table/F_origin_42/F_table_130_2.png" + ], + "domain": "Finance", + "origin": "F_origin_42", + "table_id": "F_table_130_2" + }, + { + "index": 239, + "pair_id": "F_origin_42_F_table_130_3", + "image_paths": [ + "data/Finance/Table/F_origin_42/F_table_130_3.png" + ], + "domain": "Finance", + "origin": "F_origin_42", + "table_id": "F_table_130_3" + }, + { + "index": 240, + "pair_id": "F_origin_42_F_table_130_4", + "image_paths": [ + "data/Finance/Table/F_origin_42/F_table_130_4.png" + ], + "domain": "Finance", + "origin": "F_origin_42", + "table_id": "F_table_130_4" + }, + { + "index": 241, + "pair_id": "F_origin_42_F_table_130_5", + "image_paths": [ + "data/Finance/Table/F_origin_42/F_table_130_5.png" + ], + "domain": "Finance", + "origin": "F_origin_42", + "table_id": "F_table_130_5" + }, + { + "index": 242, + "pair_id": "F_origin_43_F_table_131_0", + "image_paths": [ + "data/Finance/Table/F_origin_43/F_table_131_0.png" + ], + "domain": "Finance", + "origin": "F_origin_43", + "table_id": "F_table_131_0" + }, + { + "index": 243, + "pair_id": "F_origin_43_F_table_131_1", + "image_paths": [ + "data/Finance/Table/F_origin_43/F_table_131_1.png" + ], + "domain": "Finance", + "origin": "F_origin_43", + "table_id": "F_table_131_1" + }, + { + "index": 244, + "pair_id": "F_origin_43_F_table_131_2", + "image_paths": [ + "data/Finance/Table/F_origin_43/F_table_131_2.png" + ], + "domain": "Finance", + "origin": "F_origin_43", + "table_id": "F_table_131_2" + }, + { + "index": 245, + "pair_id": "F_origin_43_F_table_131_3", + "image_paths": [ + "data/Finance/Table/F_origin_43/F_table_131_3.png" + ], + "domain": "Finance", + "origin": "F_origin_43", + "table_id": "F_table_131_3" + }, + { + "index": 246, + "pair_id": "F_origin_43_F_table_131_4", + "image_paths": [ + "data/Finance/Table/F_origin_43/F_table_131_4.png" + ], + "domain": "Finance", + "origin": "F_origin_43", + "table_id": "F_table_131_4" + }, + { + "index": 247, + "pair_id": "F_origin_44_F_table_132_0", + "image_paths": [ + "data/Finance/Table/F_origin_44/F_table_132_0.png" + ], + "domain": "Finance", + "origin": "F_origin_44", + "table_id": "F_table_132_0" + }, + { + "index": 248, + "pair_id": "F_origin_44_F_table_132_1", + "image_paths": [ + "data/Finance/Table/F_origin_44/F_table_132_1.png" + ], + "domain": "Finance", + "origin": "F_origin_44", + "table_id": "F_table_132_1" + }, + { + "index": 249, + "pair_id": "F_origin_44_F_table_133_0", + "image_paths": [ + "data/Finance/Table/F_origin_44/F_table_133_0.png" + ], + "domain": "Finance", + "origin": "F_origin_44", + "table_id": "F_table_133_0" + }, + { + "index": 250, + "pair_id": "F_origin_44_F_table_133_1", + "image_paths": [ + "data/Finance/Table/F_origin_44/F_table_133_1.png" + ], + "domain": "Finance", + "origin": "F_origin_44", + "table_id": "F_table_133_1" + }, + { + "index": 251, + "pair_id": "F_origin_44_F_table_133_2", + "image_paths": [ + "data/Finance/Table/F_origin_44/F_table_133_2.png" + ], + "domain": "Finance", + "origin": "F_origin_44", + "table_id": "F_table_133_2" + }, + { + "index": 252, + "pair_id": "F_origin_44_F_table_134_0", + "image_paths": [ + "data/Finance/Table/F_origin_44/F_table_134_0.png" + ], + "domain": "Finance", + "origin": "F_origin_44", + "table_id": "F_table_134_0" + }, + { + "index": 253, + "pair_id": "F_origin_44_F_table_135_0", + "image_paths": [ + "data/Finance/Table/F_origin_44/F_table_135_0.png" + ], + "domain": "Finance", + "origin": "F_origin_44", + "table_id": "F_table_135_0" + }, + { + "index": 254, + "pair_id": "F_origin_44_F_table_135_1", + "image_paths": [ + "data/Finance/Table/F_origin_44/F_table_135_1.png" + ], + "domain": "Finance", + "origin": "F_origin_44", + "table_id": "F_table_135_1" + }, + { + "index": 255, + "pair_id": "F_origin_44_F_table_135_2", + "image_paths": [ + "data/Finance/Table/F_origin_44/F_table_135_2.png" + ], + "domain": "Finance", + "origin": "F_origin_44", + "table_id": "F_table_135_2" + }, + { + "index": 256, + "pair_id": "F_origin_45_F_table_136_0", + "image_paths": [ + "data/Finance/Table/F_origin_45/F_table_136_0.png" + ], + "domain": "Finance", + "origin": "F_origin_45", + "table_id": "F_table_136_0" + }, + { + "index": 257, + "pair_id": "F_origin_45_F_table_136_1", + "image_paths": [ + "data/Finance/Table/F_origin_45/F_table_136_1.png" + ], + "domain": "Finance", + "origin": "F_origin_45", + "table_id": "F_table_136_1" + }, + { + "index": 258, + "pair_id": "F_origin_45_F_table_136_2", + "image_paths": [ + "data/Finance/Table/F_origin_45/F_table_136_2.png" + ], + "domain": "Finance", + "origin": "F_origin_45", + "table_id": "F_table_136_2" + }, + { + "index": 259, + "pair_id": "F_origin_45_F_table_136_3", + "image_paths": [ + "data/Finance/Table/F_origin_45/F_table_136_3.png" + ], + "domain": "Finance", + "origin": "F_origin_45", + "table_id": "F_table_136_3" + }, + { + "index": 260, + "pair_id": "F_origin_45_F_table_136_4", + "image_paths": [ + "data/Finance/Table/F_origin_45/F_table_136_4.png" + ], + "domain": "Finance", + "origin": "F_origin_45", + "table_id": "F_table_136_4" + }, + { + "index": 261, + "pair_id": "F_origin_45_F_table_136_5", + "image_paths": [ + "data/Finance/Table/F_origin_45/F_table_136_5.png" + ], + "domain": "Finance", + "origin": "F_origin_45", + "table_id": "F_table_136_5" + }, + { + "index": 262, + "pair_id": "F_origin_46_F_table_137_0", + "image_paths": [ + "data/Finance/Table/F_origin_46/F_table_137_0.png" + ], + "domain": "Finance", + "origin": "F_origin_46", + "table_id": "F_table_137_0" + }, + { + "index": 263, + "pair_id": "F_origin_46_F_table_137_1", + "image_paths": [ + "data/Finance/Table/F_origin_46/F_table_137_1.png" + ], + "domain": "Finance", + "origin": "F_origin_46", + "table_id": "F_table_137_1" + }, + { + "index": 264, + "pair_id": "F_origin_46_F_table_138_0", + "image_paths": [ + "data/Finance/Table/F_origin_46/F_table_138_0.png" + ], + "domain": "Finance", + "origin": "F_origin_46", + "table_id": "F_table_138_0" + }, + { + "index": 265, + "pair_id": "F_origin_46_F_table_138_1", + "image_paths": [ + "data/Finance/Table/F_origin_46/F_table_138_1.png" + ], + "domain": "Finance", + "origin": "F_origin_46", + "table_id": "F_table_138_1" + }, + { + "index": 266, + "pair_id": "F_origin_46_F_table_139_0", + "image_paths": [ + "data/Finance/Table/F_origin_46/F_table_139_0.png" + ], + "domain": "Finance", + "origin": "F_origin_46", + "table_id": "F_table_139_0" + }, + { + "index": 267, + "pair_id": "F_origin_46_F_table_139_1", + "image_paths": [ + "data/Finance/Table/F_origin_46/F_table_139_1.png" + ], + "domain": "Finance", + "origin": "F_origin_46", + "table_id": "F_table_139_1" + }, + { + "index": 268, + "pair_id": "F_origin_47_F_table_140_0", + "image_paths": [ + "data/Finance/Table/F_origin_47/F_table_140_0.png" + ], + "domain": "Finance", + "origin": "F_origin_47", + "table_id": "F_table_140_0" + }, + { + "index": 269, + "pair_id": "F_origin_47_F_table_141_0", + "image_paths": [ + "data/Finance/Table/F_origin_47/F_table_141_0.png" + ], + "domain": "Finance", + "origin": "F_origin_47", + "table_id": "F_table_141_0" + }, + { + "index": 270, + "pair_id": "F_origin_47_F_table_141_1", + "image_paths": [ + "data/Finance/Table/F_origin_47/F_table_141_1.png" + ], + "domain": "Finance", + "origin": "F_origin_47", + "table_id": "F_table_141_1" + }, + { + "index": 271, + "pair_id": "F_origin_47_F_table_142_0", + "image_paths": [ + "data/Finance/Table/F_origin_47/F_table_142_0.png" + ], + "domain": "Finance", + "origin": "F_origin_47", + "table_id": "F_table_142_0" + }, + { + "index": 272, + "pair_id": "F_origin_47_F_table_142_1", + "image_paths": [ + "data/Finance/Table/F_origin_47/F_table_142_1.png" + ], + "domain": "Finance", + "origin": "F_origin_47", + "table_id": "F_table_142_1" + }, + { + "index": 273, + "pair_id": "F_origin_47_F_table_142_2", + "image_paths": [ + "data/Finance/Table/F_origin_47/F_table_142_2.png" + ], + "domain": "Finance", + "origin": "F_origin_47", + "table_id": "F_table_142_2" + }, + { + "index": 274, + "pair_id": "F_origin_47_F_table_142_3", + "image_paths": [ + "data/Finance/Table/F_origin_47/F_table_142_3.png" + ], + "domain": "Finance", + "origin": "F_origin_47", + "table_id": "F_table_142_3" + }, + { + "index": 275, + "pair_id": "F_origin_48_F_table_143_0", + "image_paths": [ + "data/Finance/Table/F_origin_48/F_table_143_0.png" + ], + "domain": "Finance", + "origin": "F_origin_48", + "table_id": "F_table_143_0" + }, + { + "index": 276, + "pair_id": "F_origin_48_F_table_144_0", + "image_paths": [ + "data/Finance/Table/F_origin_48/F_table_144_0.png" + ], + "domain": "Finance", + "origin": "F_origin_48", + "table_id": "F_table_144_0" + }, + { + "index": 277, + "pair_id": "F_origin_48_F_table_144_1", + "image_paths": [ + "data/Finance/Table/F_origin_48/F_table_144_1.png" + ], + "domain": "Finance", + "origin": "F_origin_48", + "table_id": "F_table_144_1" + }, + { + "index": 278, + "pair_id": "F_origin_48_F_table_145_0", + "image_paths": [ + "data/Finance/Table/F_origin_48/F_table_145_0.png" + ], + "domain": "Finance", + "origin": "F_origin_48", + "table_id": "F_table_145_0" + }, + { + "index": 279, + "pair_id": "F_origin_48_F_table_145_1", + "image_paths": [ + "data/Finance/Table/F_origin_48/F_table_145_1.png" + ], + "domain": "Finance", + "origin": "F_origin_48", + "table_id": "F_table_145_1" + }, + { + "index": 280, + "pair_id": "F_origin_49_F_table_146_0", + "image_paths": [ + "data/Finance/Table/F_origin_49/F_table_146_0.png" + ], + "domain": "Finance", + "origin": "F_origin_49", + "table_id": "F_table_146_0" + }, + { + "index": 281, + "pair_id": "F_origin_49_F_table_147_0", + "image_paths": [ + "data/Finance/Table/F_origin_49/F_table_147_0.png" + ], + "domain": "Finance", + "origin": "F_origin_49", + "table_id": "F_table_147_0" + }, + { + "index": 282, + "pair_id": "F_origin_49_F_table_147_1", + "image_paths": [ + "data/Finance/Table/F_origin_49/F_table_147_1.png" + ], + "domain": "Finance", + "origin": "F_origin_49", + "table_id": "F_table_147_1" + }, + { + "index": 283, + "pair_id": "F_origin_49_F_table_147_2", + "image_paths": [ + "data/Finance/Table/F_origin_49/F_table_147_2.png" + ], + "domain": "Finance", + "origin": "F_origin_49", + "table_id": "F_table_147_2" + }, + { + "index": 284, + "pair_id": "F_origin_49_F_table_148_0", + "image_paths": [ + "data/Finance/Table/F_origin_49/F_table_148_0.png" + ], + "domain": "Finance", + "origin": "F_origin_49", + "table_id": "F_table_148_0" + }, + { + "index": 285, + "pair_id": "F_origin_49_F_table_148_1", + "image_paths": [ + "data/Finance/Table/F_origin_49/F_table_148_1.png" + ], + "domain": "Finance", + "origin": "F_origin_49", + "table_id": "F_table_148_1" + }, + { + "index": 286, + "pair_id": "F_origin_5_F_table_7_0", + "image_paths": [ + "data/Finance/Table/F_origin_5/F_table_7_0.png" + ], + "domain": "Finance", + "origin": "F_origin_5", + "table_id": "F_table_7_0" + }, + { + "index": 287, + "pair_id": "F_origin_5_F_table_7_1", + "image_paths": [ + "data/Finance/Table/F_origin_5/F_table_7_1.png" + ], + "domain": "Finance", + "origin": "F_origin_5", + "table_id": "F_table_7_1" + }, + { + "index": 288, + "pair_id": "F_origin_5_F_table_7_2", + "image_paths": [ + "data/Finance/Table/F_origin_5/F_table_7_2.png" + ], + "domain": "Finance", + "origin": "F_origin_5", + "table_id": "F_table_7_2" + }, + { + "index": 289, + "pair_id": "F_origin_5_F_table_7_3", + "image_paths": [ + "data/Finance/Table/F_origin_5/F_table_7_3.png" + ], + "domain": "Finance", + "origin": "F_origin_5", + "table_id": "F_table_7_3" + }, + { + "index": 290, + "pair_id": "F_origin_5_F_table_7_4", + "image_paths": [ + "data/Finance/Table/F_origin_5/F_table_7_4.png" + ], + "domain": "Finance", + "origin": "F_origin_5", + "table_id": "F_table_7_4" + }, + { + "index": 291, + "pair_id": "F_origin_6_F_table_8_0", + "image_paths": [ + "data/Finance/Table/F_origin_6/F_table_8_0.png" + ], + "domain": "Finance", + "origin": "F_origin_6", + "table_id": "F_table_8_0" + }, + { + "index": 292, + "pair_id": "F_origin_6_F_table_8_1", + "image_paths": [ + "data/Finance/Table/F_origin_6/F_table_8_1.png" + ], + "domain": "Finance", + "origin": "F_origin_6", + "table_id": "F_table_8_1" + }, + { + "index": 293, + "pair_id": "F_origin_6_F_table_8_2", + "image_paths": [ + "data/Finance/Table/F_origin_6/F_table_8_2.png" + ], + "domain": "Finance", + "origin": "F_origin_6", + "table_id": "F_table_8_2" + }, + { + "index": 294, + "pair_id": "F_origin_6_F_table_8_3", + "image_paths": [ + "data/Finance/Table/F_origin_6/F_table_8_3.png" + ], + "domain": "Finance", + "origin": "F_origin_6", + "table_id": "F_table_8_3" + }, + { + "index": 295, + "pair_id": "F_origin_6_F_table_8_4", + "image_paths": [ + "data/Finance/Table/F_origin_6/F_table_8_4.png" + ], + "domain": "Finance", + "origin": "F_origin_6", + "table_id": "F_table_8_4" + }, + { + "index": 296, + "pair_id": "F_origin_6_F_table_9_0", + "image_paths": [ + "data/Finance/Table/F_origin_6/F_table_9_0.png" + ], + "domain": "Finance", + "origin": "F_origin_6", + "table_id": "F_table_9_0" + }, + { + "index": 297, + "pair_id": "F_origin_6_F_table_9_1", + "image_paths": [ + "data/Finance/Table/F_origin_6/F_table_9_1.png" + ], + "domain": "Finance", + "origin": "F_origin_6", + "table_id": "F_table_9_1" + }, + { + "index": 298, + "pair_id": "F_origin_7_F_table_10_0", + "image_paths": [ + "data/Finance/Table/F_origin_7/F_table_10_0.png" + ], + "domain": "Finance", + "origin": "F_origin_7", + "table_id": "F_table_10_0" + }, + { + "index": 299, + "pair_id": "F_origin_7_F_table_10_1", + "image_paths": [ + "data/Finance/Table/F_origin_7/F_table_10_1.png" + ], + "domain": "Finance", + "origin": "F_origin_7", + "table_id": "F_table_10_1" + }, + { + "index": 300, + "pair_id": "F_origin_7_F_table_10_2", + "image_paths": [ + "data/Finance/Table/F_origin_7/F_table_10_2.png" + ], + "domain": "Finance", + "origin": "F_origin_7", + "table_id": "F_table_10_2" + }, + { + "index": 301, + "pair_id": "F_origin_7_F_table_10_3", + "image_paths": [ + "data/Finance/Table/F_origin_7/F_table_10_3.png" + ], + "domain": "Finance", + "origin": "F_origin_7", + "table_id": "F_table_10_3" + }, + { + "index": 302, + "pair_id": "F_origin_7_F_table_10_4", + "image_paths": [ + "data/Finance/Table/F_origin_7/F_table_10_4.png" + ], + "domain": "Finance", + "origin": "F_origin_7", + "table_id": "F_table_10_4" + }, + { + "index": 303, + "pair_id": "F_origin_7_F_table_11_0", + "image_paths": [ + "data/Finance/Table/F_origin_7/F_table_11_0.png" + ], + "domain": "Finance", + "origin": "F_origin_7", + "table_id": "F_table_11_0" + }, + { + "index": 304, + "pair_id": "F_origin_7_F_table_11_1", + "image_paths": [ + "data/Finance/Table/F_origin_7/F_table_11_1.png" + ], + "domain": "Finance", + "origin": "F_origin_7", + "table_id": "F_table_11_1" + }, + { + "index": 305, + "pair_id": "F_origin_8_F_table_12_0", + "image_paths": [ + "data/Finance/Table/F_origin_8/F_table_12_0.png" + ], + "domain": "Finance", + "origin": "F_origin_8", + "table_id": "F_table_12_0" + }, + { + "index": 306, + "pair_id": "F_origin_8_F_table_12_1", + "image_paths": [ + "data/Finance/Table/F_origin_8/F_table_12_1.png" + ], + "domain": "Finance", + "origin": "F_origin_8", + "table_id": "F_table_12_1" + }, + { + "index": 307, + "pair_id": "F_origin_8_F_table_12_2", + "image_paths": [ + "data/Finance/Table/F_origin_8/F_table_12_2.png" + ], + "domain": "Finance", + "origin": "F_origin_8", + "table_id": "F_table_12_2" + }, + { + "index": 308, + "pair_id": "F_origin_8_F_table_12_3", + "image_paths": [ + "data/Finance/Table/F_origin_8/F_table_12_3.png" + ], + "domain": "Finance", + "origin": "F_origin_8", + "table_id": "F_table_12_3" + }, + { + "index": 309, + "pair_id": "F_origin_8_F_table_12_4", + "image_paths": [ + "data/Finance/Table/F_origin_8/F_table_12_4.png" + ], + "domain": "Finance", + "origin": "F_origin_8", + "table_id": "F_table_12_4" + }, + { + "index": 310, + "pair_id": "F_origin_9_F_table_13_0", + "image_paths": [ + "data/Finance/Table/F_origin_9/F_table_13_0.png" + ], + "domain": "Finance", + "origin": "F_origin_9", + "table_id": "F_table_13_0" + }, + { + "index": 311, + "pair_id": "F_origin_9_F_table_13_1", + "image_paths": [ + "data/Finance/Table/F_origin_9/F_table_13_1.png" + ], + "domain": "Finance", + "origin": "F_origin_9", + "table_id": "F_table_13_1" + }, + { + "index": 312, + "pair_id": "F_origin_9_F_table_13_2", + "image_paths": [ + "data/Finance/Table/F_origin_9/F_table_13_2.png" + ], + "domain": "Finance", + "origin": "F_origin_9", + "table_id": "F_table_13_2" + }, + { + "index": 313, + "pair_id": "F_origin_9_F_table_13_3", + "image_paths": [ + "data/Finance/Table/F_origin_9/F_table_13_3.png" + ], + "domain": "Finance", + "origin": "F_origin_9", + "table_id": "F_table_13_3" + }, + { + "index": 314, + "pair_id": "F_origin_9_F_table_13_4", + "image_paths": [ + "data/Finance/Table/F_origin_9/F_table_13_4.png" + ], + "domain": "Finance", + "origin": "F_origin_9", + "table_id": "F_table_13_4" + } +] \ No newline at end of file diff --git a/single_image_json_list/single_table_insurance_input.json b/single_image_json_list/single_table_insurance_input.json new file mode 100644 index 0000000..73dc561 --- /dev/null +++ b/single_image_json_list/single_table_insurance_input.json @@ -0,0 +1,1572 @@ +[ + { + "index": 0, + "pair_id": "I_origin_0_I_table_0", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_0.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_0" + }, + { + "index": 1, + "pair_id": "I_origin_0_I_table_1", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_1.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_1" + }, + { + "index": 2, + "pair_id": "I_origin_0_I_table_10", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_10.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_10" + }, + { + "index": 3, + "pair_id": "I_origin_0_I_table_11", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_11.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_11" + }, + { + "index": 4, + "pair_id": "I_origin_0_I_table_12_0", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_12_0.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_12_0" + }, + { + "index": 5, + "pair_id": "I_origin_0_I_table_12_1", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_12_1.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_12_1" + }, + { + "index": 6, + "pair_id": "I_origin_0_I_table_13", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_13.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_13" + }, + { + "index": 7, + "pair_id": "I_origin_0_I_table_14", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_14.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_14" + }, + { + "index": 8, + "pair_id": "I_origin_0_I_table_15", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_15.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_15" + }, + { + "index": 9, + "pair_id": "I_origin_0_I_table_16", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_16.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_16" + }, + { + "index": 10, + "pair_id": "I_origin_0_I_table_17", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_17.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_17" + }, + { + "index": 11, + "pair_id": "I_origin_0_I_table_18", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_18.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_18" + }, + { + "index": 12, + "pair_id": "I_origin_0_I_table_19_0", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_0.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_0" + }, + { + "index": 13, + "pair_id": "I_origin_0_I_table_19_1", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_1.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_1" + }, + { + "index": 14, + "pair_id": "I_origin_0_I_table_19_10", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_10.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_10" + }, + { + "index": 15, + "pair_id": "I_origin_0_I_table_19_11", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_11.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_11" + }, + { + "index": 16, + "pair_id": "I_origin_0_I_table_19_12", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_12.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_12" + }, + { + "index": 17, + "pair_id": "I_origin_0_I_table_19_13", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_13.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_13" + }, + { + "index": 18, + "pair_id": "I_origin_0_I_table_19_14", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_14.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_14" + }, + { + "index": 19, + "pair_id": "I_origin_0_I_table_19_15", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_15.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_15" + }, + { + "index": 20, + "pair_id": "I_origin_0_I_table_19_16", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_16.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_16" + }, + { + "index": 21, + "pair_id": "I_origin_0_I_table_19_17", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_17.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_17" + }, + { + "index": 22, + "pair_id": "I_origin_0_I_table_19_18", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_18.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_18" + }, + { + "index": 23, + "pair_id": "I_origin_0_I_table_19_19", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_19.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_19" + }, + { + "index": 24, + "pair_id": "I_origin_0_I_table_19_2", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_2.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_2" + }, + { + "index": 25, + "pair_id": "I_origin_0_I_table_19_20", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_20.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_20" + }, + { + "index": 26, + "pair_id": "I_origin_0_I_table_19_21", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_21.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_21" + }, + { + "index": 27, + "pair_id": "I_origin_0_I_table_19_3", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_3.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_3" + }, + { + "index": 28, + "pair_id": "I_origin_0_I_table_19_4", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_4.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_4" + }, + { + "index": 29, + "pair_id": "I_origin_0_I_table_19_5", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_5.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_5" + }, + { + "index": 30, + "pair_id": "I_origin_0_I_table_19_6", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_6.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_6" + }, + { + "index": 31, + "pair_id": "I_origin_0_I_table_19_7", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_7.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_7" + }, + { + "index": 32, + "pair_id": "I_origin_0_I_table_19_8", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_8.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_8" + }, + { + "index": 33, + "pair_id": "I_origin_0_I_table_19_9", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_19_9.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_19_9" + }, + { + "index": 34, + "pair_id": "I_origin_0_I_table_2", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_2.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_2" + }, + { + "index": 35, + "pair_id": "I_origin_0_I_table_20_0", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_20_0.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_20_0" + }, + { + "index": 36, + "pair_id": "I_origin_0_I_table_20_1", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_20_1.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_20_1" + }, + { + "index": 37, + "pair_id": "I_origin_0_I_table_20_2", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_20_2.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_20_2" + }, + { + "index": 38, + "pair_id": "I_origin_0_I_table_20_3", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_20_3.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_20_3" + }, + { + "index": 39, + "pair_id": "I_origin_0_I_table_20_4", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_20_4.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_20_4" + }, + { + "index": 40, + "pair_id": "I_origin_0_I_table_20_5", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_20_5.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_20_5" + }, + { + "index": 41, + "pair_id": "I_origin_0_I_table_20_6", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_20_6.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_20_6" + }, + { + "index": 42, + "pair_id": "I_origin_0_I_table_20_7", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_20_7.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_20_7" + }, + { + "index": 43, + "pair_id": "I_origin_0_I_table_20_8", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_20_8.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_20_8" + }, + { + "index": 44, + "pair_id": "I_origin_0_I_table_20_9", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_20_9.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_20_9" + }, + { + "index": 45, + "pair_id": "I_origin_0_I_table_21", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_21.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_21" + }, + { + "index": 46, + "pair_id": "I_origin_0_I_table_22", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_22.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_22" + }, + { + "index": 47, + "pair_id": "I_origin_0_I_table_23", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_23.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_23" + }, + { + "index": 48, + "pair_id": "I_origin_0_I_table_24", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_24.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_24" + }, + { + "index": 49, + "pair_id": "I_origin_0_I_table_25", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_25.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_25" + }, + { + "index": 50, + "pair_id": "I_origin_0_I_table_26", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_26.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_26" + }, + { + "index": 51, + "pair_id": "I_origin_0_I_table_27", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_27.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_27" + }, + { + "index": 52, + "pair_id": "I_origin_0_I_table_28", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_28.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_28" + }, + { + "index": 53, + "pair_id": "I_origin_0_I_table_29", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_29.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_29" + }, + { + "index": 54, + "pair_id": "I_origin_0_I_table_3", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_3.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_3" + }, + { + "index": 55, + "pair_id": "I_origin_0_I_table_30", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_30.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_30" + }, + { + "index": 56, + "pair_id": "I_origin_0_I_table_31", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_31.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_31" + }, + { + "index": 57, + "pair_id": "I_origin_0_I_table_32", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_32.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_32" + }, + { + "index": 58, + "pair_id": "I_origin_0_I_table_33", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_33.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_33" + }, + { + "index": 59, + "pair_id": "I_origin_0_I_table_34", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_34.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_34" + }, + { + "index": 60, + "pair_id": "I_origin_0_I_table_35", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_35.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_35" + }, + { + "index": 61, + "pair_id": "I_origin_0_I_table_36", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_36.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_36" + }, + { + "index": 62, + "pair_id": "I_origin_0_I_table_37", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_37.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_37" + }, + { + "index": 63, + "pair_id": "I_origin_0_I_table_38", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_38.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_38" + }, + { + "index": 64, + "pair_id": "I_origin_0_I_table_39", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_39.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_39" + }, + { + "index": 65, + "pair_id": "I_origin_0_I_table_4", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_4.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_4" + }, + { + "index": 66, + "pair_id": "I_origin_0_I_table_40_0", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_40_0.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_40_0" + }, + { + "index": 67, + "pair_id": "I_origin_0_I_table_40_1", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_40_1.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_40_1" + }, + { + "index": 68, + "pair_id": "I_origin_0_I_table_41", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_41.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_41" + }, + { + "index": 69, + "pair_id": "I_origin_0_I_table_42", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_42.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_42" + }, + { + "index": 70, + "pair_id": "I_origin_0_I_table_43", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_43.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_43" + }, + { + "index": 71, + "pair_id": "I_origin_0_I_table_44", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_44.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_44" + }, + { + "index": 72, + "pair_id": "I_origin_0_I_table_45", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_45.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_45" + }, + { + "index": 73, + "pair_id": "I_origin_0_I_table_46", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_46.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_46" + }, + { + "index": 74, + "pair_id": "I_origin_0_I_table_47", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_47.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_47" + }, + { + "index": 75, + "pair_id": "I_origin_0_I_table_48", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_48.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_48" + }, + { + "index": 76, + "pair_id": "I_origin_0_I_table_49", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_49.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_49" + }, + { + "index": 77, + "pair_id": "I_origin_0_I_table_5", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_5.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_5" + }, + { + "index": 78, + "pair_id": "I_origin_0_I_table_50", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_50.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_50" + }, + { + "index": 79, + "pair_id": "I_origin_0_I_table_51", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_51.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_51" + }, + { + "index": 80, + "pair_id": "I_origin_0_I_table_52", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_52.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_52" + }, + { + "index": 81, + "pair_id": "I_origin_0_I_table_53", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_53.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_53" + }, + { + "index": 82, + "pair_id": "I_origin_0_I_table_54", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_54.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_54" + }, + { + "index": 83, + "pair_id": "I_origin_0_I_table_55", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_55.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_55" + }, + { + "index": 84, + "pair_id": "I_origin_0_I_table_56", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_56.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_56" + }, + { + "index": 85, + "pair_id": "I_origin_0_I_table_57", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_57.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_57" + }, + { + "index": 86, + "pair_id": "I_origin_0_I_table_58", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_58.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_58" + }, + { + "index": 87, + "pair_id": "I_origin_0_I_table_59", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_59.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_59" + }, + { + "index": 88, + "pair_id": "I_origin_0_I_table_6", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_6.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_6" + }, + { + "index": 89, + "pair_id": "I_origin_0_I_table_60", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_60.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_60" + }, + { + "index": 90, + "pair_id": "I_origin_0_I_table_61", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_61.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_61" + }, + { + "index": 91, + "pair_id": "I_origin_0_I_table_62", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_62.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_62" + }, + { + "index": 92, + "pair_id": "I_origin_0_I_table_63", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_63.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_63" + }, + { + "index": 93, + "pair_id": "I_origin_0_I_table_64_0", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_0.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_0" + }, + { + "index": 94, + "pair_id": "I_origin_0_I_table_64_1", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_1.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_1" + }, + { + "index": 95, + "pair_id": "I_origin_0_I_table_64_10", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_10.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_10" + }, + { + "index": 96, + "pair_id": "I_origin_0_I_table_64_11", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_11.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_11" + }, + { + "index": 97, + "pair_id": "I_origin_0_I_table_64_12", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_12.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_12" + }, + { + "index": 98, + "pair_id": "I_origin_0_I_table_64_13", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_13.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_13" + }, + { + "index": 99, + "pair_id": "I_origin_0_I_table_64_14", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_14.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_14" + }, + { + "index": 100, + "pair_id": "I_origin_0_I_table_64_15", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_15.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_15" + }, + { + "index": 101, + "pair_id": "I_origin_0_I_table_64_16", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_16.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_16" + }, + { + "index": 102, + "pair_id": "I_origin_0_I_table_64_17", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_17.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_17" + }, + { + "index": 103, + "pair_id": "I_origin_0_I_table_64_18", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_18.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_18" + }, + { + "index": 104, + "pair_id": "I_origin_0_I_table_64_19", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_19.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_19" + }, + { + "index": 105, + "pair_id": "I_origin_0_I_table_64_2", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_2.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_2" + }, + { + "index": 106, + "pair_id": "I_origin_0_I_table_64_20", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_20.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_20" + }, + { + "index": 107, + "pair_id": "I_origin_0_I_table_64_21", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_21.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_21" + }, + { + "index": 108, + "pair_id": "I_origin_0_I_table_64_22", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_22.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_22" + }, + { + "index": 109, + "pair_id": "I_origin_0_I_table_64_23", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_23.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_23" + }, + { + "index": 110, + "pair_id": "I_origin_0_I_table_64_24", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_24.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_24" + }, + { + "index": 111, + "pair_id": "I_origin_0_I_table_64_25", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_25.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_25" + }, + { + "index": 112, + "pair_id": "I_origin_0_I_table_64_26", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_26.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_26" + }, + { + "index": 113, + "pair_id": "I_origin_0_I_table_64_27", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_27.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_27" + }, + { + "index": 114, + "pair_id": "I_origin_0_I_table_64_3", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_3.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_3" + }, + { + "index": 115, + "pair_id": "I_origin_0_I_table_64_4", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_4.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_4" + }, + { + "index": 116, + "pair_id": "I_origin_0_I_table_64_5", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_5.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_5" + }, + { + "index": 117, + "pair_id": "I_origin_0_I_table_64_6", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_6.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_6" + }, + { + "index": 118, + "pair_id": "I_origin_0_I_table_64_7", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_7.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_7" + }, + { + "index": 119, + "pair_id": "I_origin_0_I_table_64_8", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_8.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_8" + }, + { + "index": 120, + "pair_id": "I_origin_0_I_table_64_9", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_64_9.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_64_9" + }, + { + "index": 121, + "pair_id": "I_origin_0_I_table_65_0", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_65_0.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_65_0" + }, + { + "index": 122, + "pair_id": "I_origin_0_I_table_65_1", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_65_1.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_65_1" + }, + { + "index": 123, + "pair_id": "I_origin_0_I_table_65_2", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_65_2.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_65_2" + }, + { + "index": 124, + "pair_id": "I_origin_0_I_table_66", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_66.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_66" + }, + { + "index": 125, + "pair_id": "I_origin_0_I_table_67", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_67.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_67" + }, + { + "index": 126, + "pair_id": "I_origin_0_I_table_68", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_68.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_68" + }, + { + "index": 127, + "pair_id": "I_origin_0_I_table_69", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_69.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_69" + }, + { + "index": 128, + "pair_id": "I_origin_0_I_table_7", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_7.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_7" + }, + { + "index": 129, + "pair_id": "I_origin_0_I_table_70", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_70.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_70" + }, + { + "index": 130, + "pair_id": "I_origin_0_I_table_71", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_71.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_71" + }, + { + "index": 131, + "pair_id": "I_origin_0_I_table_72", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_72.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_72" + }, + { + "index": 132, + "pair_id": "I_origin_0_I_table_73", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_73.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_73" + }, + { + "index": 133, + "pair_id": "I_origin_0_I_table_74", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_74.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_74" + }, + { + "index": 134, + "pair_id": "I_origin_0_I_table_75", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_75.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_75" + }, + { + "index": 135, + "pair_id": "I_origin_0_I_table_76", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_76.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_76" + }, + { + "index": 136, + "pair_id": "I_origin_0_I_table_77", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_77.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_77" + }, + { + "index": 137, + "pair_id": "I_origin_0_I_table_8", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_8.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_8" + }, + { + "index": 138, + "pair_id": "I_origin_0_I_table_9", + "image_paths": [ + "data/Insurance/Table/I_origin_0/I_table_9.png" + ], + "domain": "Insurance", + "origin": "I_origin_0", + "table_id": "I_table_9" + }, + { + "index": 139, + "pair_id": "I_origin_1_I_table_78", + "image_paths": [ + "data/Insurance/Table/I_origin_1/I_table_78.png" + ], + "domain": "Insurance", + "origin": "I_origin_1", + "table_id": "I_table_78" + }, + { + "index": 140, + "pair_id": "I_origin_2_I_table_79", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_79.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_79" + }, + { + "index": 141, + "pair_id": "I_origin_2_I_table_80", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_80.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_80" + }, + { + "index": 142, + "pair_id": "I_origin_2_I_table_81", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_81.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_81" + }, + { + "index": 143, + "pair_id": "I_origin_2_I_table_82", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_82.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_82" + }, + { + "index": 144, + "pair_id": "I_origin_2_I_table_83", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_83.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_83" + }, + { + "index": 145, + "pair_id": "I_origin_2_I_table_84", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_84.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_84" + }, + { + "index": 146, + "pair_id": "I_origin_2_I_table_85", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_85.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_85" + }, + { + "index": 147, + "pair_id": "I_origin_2_I_table_86", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_86.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_86" + }, + { + "index": 148, + "pair_id": "I_origin_2_I_table_87", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_87.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_87" + }, + { + "index": 149, + "pair_id": "I_origin_2_I_table_88", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_88.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_88" + }, + { + "index": 150, + "pair_id": "I_origin_2_I_table_89", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_89.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_89" + }, + { + "index": 151, + "pair_id": "I_origin_2_I_table_90", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_90.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_90" + }, + { + "index": 152, + "pair_id": "I_origin_2_I_table_91", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_91.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_91" + }, + { + "index": 153, + "pair_id": "I_origin_2_I_table_92", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_92.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_92" + }, + { + "index": 154, + "pair_id": "I_origin_2_I_table_93", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_93.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_93" + }, + { + "index": 155, + "pair_id": "I_origin_2_I_table_94", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_94.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_94" + }, + { + "index": 156, + "pair_id": "I_origin_2_I_table_95", + "image_paths": [ + "data/Insurance/Table/I_origin_2/I_table_95.png" + ], + "domain": "Insurance", + "origin": "I_origin_2", + "table_id": "I_table_95" + } +] \ No newline at end of file diff --git a/single_image_json_list/single_table_medical_input.json b/single_image_json_list/single_table_medical_input.json new file mode 100644 index 0000000..3978487 --- /dev/null +++ b/single_image_json_list/single_table_medical_input.json @@ -0,0 +1,1292 @@ +[ + { + "index": 0, + "pair_id": "Medical_M_table_0_0_0", + "image_paths": [ + "data/Medical/Table/M_table_0_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_0_0_0" + }, + { + "index": 1, + "pair_id": "Medical_M_table_0_1_0", + "image_paths": [ + "data/Medical/Table/M_table_0_1_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_0_1_0" + }, + { + "index": 2, + "pair_id": "Medical_M_table_10_0_0", + "image_paths": [ + "data/Medical/Table/M_table_10_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_0_0" + }, + { + "index": 3, + "pair_id": "Medical_M_table_10_0_1", + "image_paths": [ + "data/Medical/Table/M_table_10_0_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_0_1" + }, + { + "index": 4, + "pair_id": "Medical_M_table_10_0_2", + "image_paths": [ + "data/Medical/Table/M_table_10_0_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_0_2" + }, + { + "index": 5, + "pair_id": "Medical_M_table_10_1_0", + "image_paths": [ + "data/Medical/Table/M_table_10_1_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_1_0" + }, + { + "index": 6, + "pair_id": "Medical_M_table_10_1_1", + "image_paths": [ + "data/Medical/Table/M_table_10_1_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_1_1" + }, + { + "index": 7, + "pair_id": "Medical_M_table_10_1_2", + "image_paths": [ + "data/Medical/Table/M_table_10_1_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_1_2" + }, + { + "index": 8, + "pair_id": "Medical_M_table_10_1_3", + "image_paths": [ + "data/Medical/Table/M_table_10_1_3.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_1_3" + }, + { + "index": 9, + "pair_id": "Medical_M_table_10_2_0", + "image_paths": [ + "data/Medical/Table/M_table_10_2_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_2_0" + }, + { + "index": 10, + "pair_id": "Medical_M_table_10_2_1", + "image_paths": [ + "data/Medical/Table/M_table_10_2_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_2_1" + }, + { + "index": 11, + "pair_id": "Medical_M_table_10_2_2", + "image_paths": [ + "data/Medical/Table/M_table_10_2_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_2_2" + }, + { + "index": 12, + "pair_id": "Medical_M_table_10_2_3", + "image_paths": [ + "data/Medical/Table/M_table_10_2_3.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_2_3" + }, + { + "index": 13, + "pair_id": "Medical_M_table_10_2_4", + "image_paths": [ + "data/Medical/Table/M_table_10_2_4.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_2_4" + }, + { + "index": 14, + "pair_id": "Medical_M_table_10_2_5", + "image_paths": [ + "data/Medical/Table/M_table_10_2_5.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_2_5" + }, + { + "index": 15, + "pair_id": "Medical_M_table_10_3_0", + "image_paths": [ + "data/Medical/Table/M_table_10_3_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_3_0" + }, + { + "index": 16, + "pair_id": "Medical_M_table_10_3_1", + "image_paths": [ + "data/Medical/Table/M_table_10_3_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_3_1" + }, + { + "index": 17, + "pair_id": "Medical_M_table_10_3_2", + "image_paths": [ + "data/Medical/Table/M_table_10_3_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_3_2" + }, + { + "index": 18, + "pair_id": "Medical_M_table_10_3_3", + "image_paths": [ + "data/Medical/Table/M_table_10_3_3.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_3_3" + }, + { + "index": 19, + "pair_id": "Medical_M_table_10_3_4", + "image_paths": [ + "data/Medical/Table/M_table_10_3_4.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_3_4" + }, + { + "index": 20, + "pair_id": "Medical_M_table_10_4_0", + "image_paths": [ + "data/Medical/Table/M_table_10_4_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_4_0" + }, + { + "index": 21, + "pair_id": "Medical_M_table_10_4_1", + "image_paths": [ + "data/Medical/Table/M_table_10_4_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_4_1" + }, + { + "index": 22, + "pair_id": "Medical_M_table_10_4_2", + "image_paths": [ + "data/Medical/Table/M_table_10_4_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_4_2" + }, + { + "index": 23, + "pair_id": "Medical_M_table_10_4_3", + "image_paths": [ + "data/Medical/Table/M_table_10_4_3.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_4_3" + }, + { + "index": 24, + "pair_id": "Medical_M_table_10_4_4", + "image_paths": [ + "data/Medical/Table/M_table_10_4_4.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_4_4" + }, + { + "index": 25, + "pair_id": "Medical_M_table_10_5_0", + "image_paths": [ + "data/Medical/Table/M_table_10_5_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_10_5_0" + }, + { + "index": 26, + "pair_id": "Medical_M_table_11_0_0", + "image_paths": [ + "data/Medical/Table/M_table_11_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_11_0_0" + }, + { + "index": 27, + "pair_id": "Medical_M_table_11_0_1", + "image_paths": [ + "data/Medical/Table/M_table_11_0_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_11_0_1" + }, + { + "index": 28, + "pair_id": "Medical_M_table_11_0_2", + "image_paths": [ + "data/Medical/Table/M_table_11_0_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_11_0_2" + }, + { + "index": 29, + "pair_id": "Medical_M_table_11_0_3", + "image_paths": [ + "data/Medical/Table/M_table_11_0_3.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_11_0_3" + }, + { + "index": 30, + "pair_id": "Medical_M_table_11_0_4", + "image_paths": [ + "data/Medical/Table/M_table_11_0_4.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_11_0_4" + }, + { + "index": 31, + "pair_id": "Medical_M_table_12_0_0", + "image_paths": [ + "data/Medical/Table/M_table_12_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_12_0_0" + }, + { + "index": 32, + "pair_id": "Medical_M_table_13_0_0", + "image_paths": [ + "data/Medical/Table/M_table_13_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_13_0_0" + }, + { + "index": 33, + "pair_id": "Medical_M_table_13_0_1", + "image_paths": [ + "data/Medical/Table/M_table_13_0_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_13_0_1" + }, + { + "index": 34, + "pair_id": "Medical_M_table_13_0_2", + "image_paths": [ + "data/Medical/Table/M_table_13_0_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_13_0_2" + }, + { + "index": 35, + "pair_id": "Medical_M_table_13_0_3", + "image_paths": [ + "data/Medical/Table/M_table_13_0_3.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_13_0_3" + }, + { + "index": 36, + "pair_id": "Medical_M_table_13_1_0", + "image_paths": [ + "data/Medical/Table/M_table_13_1_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_13_1_0" + }, + { + "index": 37, + "pair_id": "Medical_M_table_13_1_1", + "image_paths": [ + "data/Medical/Table/M_table_13_1_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_13_1_1" + }, + { + "index": 38, + "pair_id": "Medical_M_table_13_1_10", + "image_paths": [ + "data/Medical/Table/M_table_13_1_10.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_13_1_10" + }, + { + "index": 39, + "pair_id": "Medical_M_table_13_1_11", + "image_paths": [ + "data/Medical/Table/M_table_13_1_11.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_13_1_11" + }, + { + "index": 40, + "pair_id": "Medical_M_table_13_1_2", + "image_paths": [ + "data/Medical/Table/M_table_13_1_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_13_1_2" + }, + { + "index": 41, + "pair_id": "Medical_M_table_13_1_6", + "image_paths": [ + "data/Medical/Table/M_table_13_1_6.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_13_1_6" + }, + { + "index": 42, + "pair_id": "Medical_M_table_13_1_8", + "image_paths": [ + "data/Medical/Table/M_table_13_1_8.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_13_1_8" + }, + { + "index": 43, + "pair_id": "Medical_M_table_13_2_0", + "image_paths": [ + "data/Medical/Table/M_table_13_2_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_13_2_0" + }, + { + "index": 44, + "pair_id": "Medical_M_table_13_2_1", + "image_paths": [ + "data/Medical/Table/M_table_13_2_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_13_2_1" + }, + { + "index": 45, + "pair_id": "Medical_M_table_14_0_0", + "image_paths": [ + "data/Medical/Table/M_table_14_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_14_0_0" + }, + { + "index": 46, + "pair_id": "Medical_M_table_14_0_1", + "image_paths": [ + "data/Medical/Table/M_table_14_0_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_14_0_1" + }, + { + "index": 47, + "pair_id": "Medical_M_table_14_1_0", + "image_paths": [ + "data/Medical/Table/M_table_14_1_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_14_1_0" + }, + { + "index": 48, + "pair_id": "Medical_M_table_14_2_0", + "image_paths": [ + "data/Medical/Table/M_table_14_2_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_14_2_0" + }, + { + "index": 49, + "pair_id": "Medical_M_table_14_3_0", + "image_paths": [ + "data/Medical/Table/M_table_14_3_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_14_3_0" + }, + { + "index": 50, + "pair_id": "Medical_M_table_14_3_1", + "image_paths": [ + "data/Medical/Table/M_table_14_3_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_14_3_1" + }, + { + "index": 51, + "pair_id": "Medical_M_table_14_3_2", + "image_paths": [ + "data/Medical/Table/M_table_14_3_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_14_3_2" + }, + { + "index": 52, + "pair_id": "Medical_M_table_14_3_3", + "image_paths": [ + "data/Medical/Table/M_table_14_3_3.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_14_3_3" + }, + { + "index": 53, + "pair_id": "Medical_M_table_14_3_4", + "image_paths": [ + "data/Medical/Table/M_table_14_3_4.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_14_3_4" + }, + { + "index": 54, + "pair_id": "Medical_M_table_15_0_0", + "image_paths": [ + "data/Medical/Table/M_table_15_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_0_0" + }, + { + "index": 55, + "pair_id": "Medical_M_table_15_0_1", + "image_paths": [ + "data/Medical/Table/M_table_15_0_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_0_1" + }, + { + "index": 56, + "pair_id": "Medical_M_table_15_0_2", + "image_paths": [ + "data/Medical/Table/M_table_15_0_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_0_2" + }, + { + "index": 57, + "pair_id": "Medical_M_table_15_10_0", + "image_paths": [ + "data/Medical/Table/M_table_15_10_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_10_0" + }, + { + "index": 58, + "pair_id": "Medical_M_table_15_10_1", + "image_paths": [ + "data/Medical/Table/M_table_15_10_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_10_1" + }, + { + "index": 59, + "pair_id": "Medical_M_table_15_11_0", + "image_paths": [ + "data/Medical/Table/M_table_15_11_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_11_0" + }, + { + "index": 60, + "pair_id": "Medical_M_table_15_12_0", + "image_paths": [ + "data/Medical/Table/M_table_15_12_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_12_0" + }, + { + "index": 61, + "pair_id": "Medical_M_table_15_13_0", + "image_paths": [ + "data/Medical/Table/M_table_15_13_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_13_0" + }, + { + "index": 62, + "pair_id": "Medical_M_table_15_1_0", + "image_paths": [ + "data/Medical/Table/M_table_15_1_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_1_0" + }, + { + "index": 63, + "pair_id": "Medical_M_table_15_2_0", + "image_paths": [ + "data/Medical/Table/M_table_15_2_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_2_0" + }, + { + "index": 64, + "pair_id": "Medical_M_table_15_3_0", + "image_paths": [ + "data/Medical/Table/M_table_15_3_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_3_0" + }, + { + "index": 65, + "pair_id": "Medical_M_table_15_4_0", + "image_paths": [ + "data/Medical/Table/M_table_15_4_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_4_0" + }, + { + "index": 66, + "pair_id": "Medical_M_table_15_5_0", + "image_paths": [ + "data/Medical/Table/M_table_15_5_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_5_0" + }, + { + "index": 67, + "pair_id": "Medical_M_table_15_5_1", + "image_paths": [ + "data/Medical/Table/M_table_15_5_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_5_1" + }, + { + "index": 68, + "pair_id": "Medical_M_table_15_5_2", + "image_paths": [ + "data/Medical/Table/M_table_15_5_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_5_2" + }, + { + "index": 69, + "pair_id": "Medical_M_table_15_6_0", + "image_paths": [ + "data/Medical/Table/M_table_15_6_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_6_0" + }, + { + "index": 70, + "pair_id": "Medical_M_table_15_7_0", + "image_paths": [ + "data/Medical/Table/M_table_15_7_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_7_0" + }, + { + "index": 71, + "pair_id": "Medical_M_table_15_8_0", + "image_paths": [ + "data/Medical/Table/M_table_15_8_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_8_0" + }, + { + "index": 72, + "pair_id": "Medical_M_table_15_9_0", + "image_paths": [ + "data/Medical/Table/M_table_15_9_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_15_9_0" + }, + { + "index": 73, + "pair_id": "Medical_M_table_16_0_0", + "image_paths": [ + "data/Medical/Table/M_table_16_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_16_0_0" + }, + { + "index": 74, + "pair_id": "Medical_M_table_16_0_1", + "image_paths": [ + "data/Medical/Table/M_table_16_0_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_16_0_1" + }, + { + "index": 75, + "pair_id": "Medical_M_table_16_1_0", + "image_paths": [ + "data/Medical/Table/M_table_16_1_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_16_1_0" + }, + { + "index": 76, + "pair_id": "Medical_M_table_16_1_1", + "image_paths": [ + "data/Medical/Table/M_table_16_1_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_16_1_1" + }, + { + "index": 77, + "pair_id": "Medical_M_table_1_0_0", + "image_paths": [ + "data/Medical/Table/M_table_1_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_1_0_0" + }, + { + "index": 78, + "pair_id": "Medical_M_table_2_0_0", + "image_paths": [ + "data/Medical/Table/M_table_2_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_0_0" + }, + { + "index": 79, + "pair_id": "Medical_M_table_2_1_0", + "image_paths": [ + "data/Medical/Table/M_table_2_1_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_1_0" + }, + { + "index": 80, + "pair_id": "Medical_M_table_2_2_0", + "image_paths": [ + "data/Medical/Table/M_table_2_2_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_2_0" + }, + { + "index": 81, + "pair_id": "Medical_M_table_2_2_1", + "image_paths": [ + "data/Medical/Table/M_table_2_2_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_2_1" + }, + { + "index": 82, + "pair_id": "Medical_M_table_2_3_0", + "image_paths": [ + "data/Medical/Table/M_table_2_3_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_3_0" + }, + { + "index": 83, + "pair_id": "Medical_M_table_2_3_1", + "image_paths": [ + "data/Medical/Table/M_table_2_3_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_3_1" + }, + { + "index": 84, + "pair_id": "Medical_M_table_2_4_0", + "image_paths": [ + "data/Medical/Table/M_table_2_4_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_4_0" + }, + { + "index": 85, + "pair_id": "Medical_M_table_2_4_1", + "image_paths": [ + "data/Medical/Table/M_table_2_4_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_4_1" + }, + { + "index": 86, + "pair_id": "Medical_M_table_2_5_0", + "image_paths": [ + "data/Medical/Table/M_table_2_5_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_5_0" + }, + { + "index": 87, + "pair_id": "Medical_M_table_2_6_0", + "image_paths": [ + "data/Medical/Table/M_table_2_6_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_6_0" + }, + { + "index": 88, + "pair_id": "Medical_M_table_2_6_1", + "image_paths": [ + "data/Medical/Table/M_table_2_6_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_6_1" + }, + { + "index": 89, + "pair_id": "Medical_M_table_2_6_2", + "image_paths": [ + "data/Medical/Table/M_table_2_6_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_6_2" + }, + { + "index": 90, + "pair_id": "Medical_M_table_2_6_3", + "image_paths": [ + "data/Medical/Table/M_table_2_6_3.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_6_3" + }, + { + "index": 91, + "pair_id": "Medical_M_table_2_6_4", + "image_paths": [ + "data/Medical/Table/M_table_2_6_4.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_6_4" + }, + { + "index": 92, + "pair_id": "Medical_M_table_2_6_5", + "image_paths": [ + "data/Medical/Table/M_table_2_6_5.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_6_5" + }, + { + "index": 93, + "pair_id": "Medical_M_table_2_6_6", + "image_paths": [ + "data/Medical/Table/M_table_2_6_6.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_6_6" + }, + { + "index": 94, + "pair_id": "Medical_M_table_2_6_7", + "image_paths": [ + "data/Medical/Table/M_table_2_6_7.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_6_7" + }, + { + "index": 95, + "pair_id": "Medical_M_table_2_7_0", + "image_paths": [ + "data/Medical/Table/M_table_2_7_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_2_7_0" + }, + { + "index": 96, + "pair_id": "Medical_M_table_3_0_0", + "image_paths": [ + "data/Medical/Table/M_table_3_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_3_0_0" + }, + { + "index": 97, + "pair_id": "Medical_M_table_3_0_1", + "image_paths": [ + "data/Medical/Table/M_table_3_0_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_3_0_1" + }, + { + "index": 98, + "pair_id": "Medical_M_table_3_0_2", + "image_paths": [ + "data/Medical/Table/M_table_3_0_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_3_0_2" + }, + { + "index": 99, + "pair_id": "Medical_M_table_3_1_0", + "image_paths": [ + "data/Medical/Table/M_table_3_1_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_3_1_0" + }, + { + "index": 100, + "pair_id": "Medical_M_table_3_1_1", + "image_paths": [ + "data/Medical/Table/M_table_3_1_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_3_1_1" + }, + { + "index": 101, + "pair_id": "Medical_M_table_3_2_0", + "image_paths": [ + "data/Medical/Table/M_table_3_2_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_3_2_0" + }, + { + "index": 102, + "pair_id": "Medical_M_table_3_2_1", + "image_paths": [ + "data/Medical/Table/M_table_3_2_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_3_2_1" + }, + { + "index": 103, + "pair_id": "Medical_M_table_3_3_0", + "image_paths": [ + "data/Medical/Table/M_table_3_3_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_3_3_0" + }, + { + "index": 104, + "pair_id": "Medical_M_table_3_3_1", + "image_paths": [ + "data/Medical/Table/M_table_3_3_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_3_3_1" + }, + { + "index": 105, + "pair_id": "Medical_M_table_4_0_0", + "image_paths": [ + "data/Medical/Table/M_table_4_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_4_0_0" + }, + { + "index": 106, + "pair_id": "Medical_M_table_4_0_1", + "image_paths": [ + "data/Medical/Table/M_table_4_0_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_4_0_1" + }, + { + "index": 107, + "pair_id": "Medical_M_table_4_1_0", + "image_paths": [ + "data/Medical/Table/M_table_4_1_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_4_1_0" + }, + { + "index": 108, + "pair_id": "Medical_M_table_5_0_0", + "image_paths": [ + "data/Medical/Table/M_table_5_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_5_0_0" + }, + { + "index": 109, + "pair_id": "Medical_M_table_6_0_0", + "image_paths": [ + "data/Medical/Table/M_table_6_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_6_0_0" + }, + { + "index": 110, + "pair_id": "Medical_M_table_6_1_0", + "image_paths": [ + "data/Medical/Table/M_table_6_1_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_6_1_0" + }, + { + "index": 111, + "pair_id": "Medical_M_table_6_2_0", + "image_paths": [ + "data/Medical/Table/M_table_6_2_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_6_2_0" + }, + { + "index": 112, + "pair_id": "Medical_M_table_6_3_0", + "image_paths": [ + "data/Medical/Table/M_table_6_3_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_6_3_0" + }, + { + "index": 113, + "pair_id": "Medical_M_table_6_3_1", + "image_paths": [ + "data/Medical/Table/M_table_6_3_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_6_3_1" + }, + { + "index": 114, + "pair_id": "Medical_M_table_8_0_0", + "image_paths": [ + "data/Medical/Table/M_table_8_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_8_0_0" + }, + { + "index": 115, + "pair_id": "Medical_M_table_8_0_1", + "image_paths": [ + "data/Medical/Table/M_table_8_0_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_8_0_1" + }, + { + "index": 116, + "pair_id": "Medical_M_table_8_0_2", + "image_paths": [ + "data/Medical/Table/M_table_8_0_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_8_0_2" + }, + { + "index": 117, + "pair_id": "Medical_M_table_8_1_0", + "image_paths": [ + "data/Medical/Table/M_table_8_1_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_8_1_0" + }, + { + "index": 118, + "pair_id": "Medical_M_table_8_2_0", + "image_paths": [ + "data/Medical/Table/M_table_8_2_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_8_2_0" + }, + { + "index": 119, + "pair_id": "Medical_M_table_8_3_0", + "image_paths": [ + "data/Medical/Table/M_table_8_3_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_8_3_0" + }, + { + "index": 120, + "pair_id": "Medical_M_table_9_0_0", + "image_paths": [ + "data/Medical/Table/M_table_9_0_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_9_0_0" + }, + { + "index": 121, + "pair_id": "Medical_M_table_9_0_1", + "image_paths": [ + "data/Medical/Table/M_table_9_0_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_9_0_1" + }, + { + "index": 122, + "pair_id": "Medical_M_table_9_0_2", + "image_paths": [ + "data/Medical/Table/M_table_9_0_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_9_0_2" + }, + { + "index": 123, + "pair_id": "Medical_M_table_9_1_0", + "image_paths": [ + "data/Medical/Table/M_table_9_1_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_9_1_0" + }, + { + "index": 124, + "pair_id": "Medical_M_table_9_1_1", + "image_paths": [ + "data/Medical/Table/M_table_9_1_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_9_1_1" + }, + { + "index": 125, + "pair_id": "Medical_M_table_9_1_2", + "image_paths": [ + "data/Medical/Table/M_table_9_1_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_9_1_2" + }, + { + "index": 126, + "pair_id": "Medical_M_table_9_2_0", + "image_paths": [ + "data/Medical/Table/M_table_9_2_0.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_9_2_0" + }, + { + "index": 127, + "pair_id": "Medical_M_table_9_2_1", + "image_paths": [ + "data/Medical/Table/M_table_9_2_1.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_9_2_1" + }, + { + "index": 128, + "pair_id": "Medical_M_table_9_2_2", + "image_paths": [ + "data/Medical/Table/M_table_9_2_2.png" + ], + "domain": "Medical", + "origin": "Medical", + "table_id": "M_table_9_2_2" + } +] \ No newline at end of file diff --git a/single_image_json_list/single_table_public_input.json b/single_image_json_list/single_table_public_input.json new file mode 100644 index 0000000..b7d33d5 --- /dev/null +++ b/single_image_json_list/single_table_public_input.json @@ -0,0 +1,2492 @@ +[ + { + "index": 0, + "pair_id": "P_origin_0_P_origin_0_0", + "image_paths": [ + "data/Public/Table/P_origin_0/P_origin_0_0.png" + ], + "domain": "Public", + "origin": "P_origin_0", + "table_id": "P_origin_0_0" + }, + { + "index": 1, + "pair_id": "P_origin_0_P_origin_0_1_0", + "image_paths": [ + "data/Public/Table/P_origin_0/P_origin_0_1_0.png" + ], + "domain": "Public", + "origin": "P_origin_0", + "table_id": "P_origin_0_1_0" + }, + { + "index": 2, + "pair_id": "P_origin_0_P_origin_0_1_1", + "image_paths": [ + "data/Public/Table/P_origin_0/P_origin_0_1_1.png" + ], + "domain": "Public", + "origin": "P_origin_0", + "table_id": "P_origin_0_1_1" + }, + { + "index": 3, + "pair_id": "P_origin_0_P_origin_0_1_2", + "image_paths": [ + "data/Public/Table/P_origin_0/P_origin_0_1_2.png" + ], + "domain": "Public", + "origin": "P_origin_0", + "table_id": "P_origin_0_1_2" + }, + { + "index": 4, + "pair_id": "P_origin_0_P_origin_0_1_3", + "image_paths": [ + "data/Public/Table/P_origin_0/P_origin_0_1_3.png" + ], + "domain": "Public", + "origin": "P_origin_0", + "table_id": "P_origin_0_1_3" + }, + { + "index": 5, + "pair_id": "P_origin_0_P_origin_0_1_4", + "image_paths": [ + "data/Public/Table/P_origin_0/P_origin_0_1_4.png" + ], + "domain": "Public", + "origin": "P_origin_0", + "table_id": "P_origin_0_1_4" + }, + { + "index": 6, + "pair_id": "P_origin_0_P_origin_0_1_5", + "image_paths": [ + "data/Public/Table/P_origin_0/P_origin_0_1_5.png" + ], + "domain": "Public", + "origin": "P_origin_0", + "table_id": "P_origin_0_1_5" + }, + { + "index": 7, + "pair_id": "P_origin_0_P_origin_0_1_6", + "image_paths": [ + "data/Public/Table/P_origin_0/P_origin_0_1_6.png" + ], + "domain": "Public", + "origin": "P_origin_0", + "table_id": "P_origin_0_1_6" + }, + { + "index": 8, + "pair_id": "P_origin_0_P_origin_0_2_0", + "image_paths": [ + "data/Public/Table/P_origin_0/P_origin_0_2_0.png" + ], + "domain": "Public", + "origin": "P_origin_0", + "table_id": "P_origin_0_2_0" + }, + { + "index": 9, + "pair_id": "P_origin_0_P_origin_0_2_1", + "image_paths": [ + "data/Public/Table/P_origin_0/P_origin_0_2_1.png" + ], + "domain": "Public", + "origin": "P_origin_0", + "table_id": "P_origin_0_2_1" + }, + { + "index": 10, + "pair_id": "P_origin_0_P_origin_0_2_2", + "image_paths": [ + "data/Public/Table/P_origin_0/P_origin_0_2_2.png" + ], + "domain": "Public", + "origin": "P_origin_0", + "table_id": "P_origin_0_2_2" + }, + { + "index": 11, + "pair_id": "P_origin_1_P_origin_1_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_0" + }, + { + "index": 12, + "pair_id": "P_origin_1_P_origin_1_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_1" + }, + { + "index": 13, + "pair_id": "P_origin_1_P_origin_1_10", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_10.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_10" + }, + { + "index": 14, + "pair_id": "P_origin_1_P_origin_1_11_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_11_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_11_0" + }, + { + "index": 15, + "pair_id": "P_origin_1_P_origin_1_11_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_11_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_11_1" + }, + { + "index": 16, + "pair_id": "P_origin_1_P_origin_1_11_2", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_11_2.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_11_2" + }, + { + "index": 17, + "pair_id": "P_origin_1_P_origin_1_12_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_12_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_12_0" + }, + { + "index": 18, + "pair_id": "P_origin_1_P_origin_1_12_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_12_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_12_1" + }, + { + "index": 19, + "pair_id": "P_origin_1_P_origin_1_12_2", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_12_2.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_12_2" + }, + { + "index": 20, + "pair_id": "P_origin_1_P_origin_1_13_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_13_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_13_0" + }, + { + "index": 21, + "pair_id": "P_origin_1_P_origin_1_13_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_13_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_13_1" + }, + { + "index": 22, + "pair_id": "P_origin_1_P_origin_1_13_2", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_13_2.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_13_2" + }, + { + "index": 23, + "pair_id": "P_origin_1_P_origin_1_14_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_14_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_14_0" + }, + { + "index": 24, + "pair_id": "P_origin_1_P_origin_1_14_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_14_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_14_1" + }, + { + "index": 25, + "pair_id": "P_origin_1_P_origin_1_14_2", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_14_2.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_14_2" + }, + { + "index": 26, + "pair_id": "P_origin_1_P_origin_1_15_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_15_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_15_0" + }, + { + "index": 27, + "pair_id": "P_origin_1_P_origin_1_15_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_15_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_15_1" + }, + { + "index": 28, + "pair_id": "P_origin_1_P_origin_1_15_2", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_15_2.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_15_2" + }, + { + "index": 29, + "pair_id": "P_origin_1_P_origin_1_16_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_16_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_16_0" + }, + { + "index": 30, + "pair_id": "P_origin_1_P_origin_1_16_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_16_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_16_1" + }, + { + "index": 31, + "pair_id": "P_origin_1_P_origin_1_16_2", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_16_2.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_16_2" + }, + { + "index": 32, + "pair_id": "P_origin_1_P_origin_1_17_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_17_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_17_0" + }, + { + "index": 33, + "pair_id": "P_origin_1_P_origin_1_17_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_17_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_17_1" + }, + { + "index": 34, + "pair_id": "P_origin_1_P_origin_1_17_2", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_17_2.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_17_2" + }, + { + "index": 35, + "pair_id": "P_origin_1_P_origin_1_18_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_18_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_18_0" + }, + { + "index": 36, + "pair_id": "P_origin_1_P_origin_1_18_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_18_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_18_1" + }, + { + "index": 37, + "pair_id": "P_origin_1_P_origin_1_19_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_19_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_19_0" + }, + { + "index": 38, + "pair_id": "P_origin_1_P_origin_1_19_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_19_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_19_1" + }, + { + "index": 39, + "pair_id": "P_origin_1_P_origin_1_2", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_2.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_2" + }, + { + "index": 40, + "pair_id": "P_origin_1_P_origin_1_20_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_20_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_20_0" + }, + { + "index": 41, + "pair_id": "P_origin_1_P_origin_1_20_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_20_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_20_1" + }, + { + "index": 42, + "pair_id": "P_origin_1_P_origin_1_20_2", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_20_2.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_20_2" + }, + { + "index": 43, + "pair_id": "P_origin_1_P_origin_1_21_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_21_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_21_0" + }, + { + "index": 44, + "pair_id": "P_origin_1_P_origin_1_21_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_21_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_21_1" + }, + { + "index": 45, + "pair_id": "P_origin_1_P_origin_1_21_2", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_21_2.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_21_2" + }, + { + "index": 46, + "pair_id": "P_origin_1_P_origin_1_22_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_22_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_22_0" + }, + { + "index": 47, + "pair_id": "P_origin_1_P_origin_1_22_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_22_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_22_1" + }, + { + "index": 48, + "pair_id": "P_origin_1_P_origin_1_22_2", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_22_2.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_22_2" + }, + { + "index": 49, + "pair_id": "P_origin_1_P_origin_1_23_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_23_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_23_0" + }, + { + "index": 50, + "pair_id": "P_origin_1_P_origin_1_23_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_23_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_23_1" + }, + { + "index": 51, + "pair_id": "P_origin_1_P_origin_1_23_2", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_23_2.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_23_2" + }, + { + "index": 52, + "pair_id": "P_origin_1_P_origin_1_24", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_24.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_24" + }, + { + "index": 53, + "pair_id": "P_origin_1_P_origin_1_25", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_25.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_25" + }, + { + "index": 54, + "pair_id": "P_origin_1_P_origin_1_26", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_26.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_26" + }, + { + "index": 55, + "pair_id": "P_origin_1_P_origin_1_3", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_3.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_3" + }, + { + "index": 56, + "pair_id": "P_origin_1_P_origin_1_4", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_4.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_4" + }, + { + "index": 57, + "pair_id": "P_origin_1_P_origin_1_5", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_5.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_5" + }, + { + "index": 58, + "pair_id": "P_origin_1_P_origin_1_6", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_6.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_6" + }, + { + "index": 59, + "pair_id": "P_origin_1_P_origin_1_7", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_7.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_7" + }, + { + "index": 60, + "pair_id": "P_origin_1_P_origin_1_8_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_8_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_8_0" + }, + { + "index": 61, + "pair_id": "P_origin_1_P_origin_1_8_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_8_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_8_1" + }, + { + "index": 62, + "pair_id": "P_origin_1_P_origin_1_8_2", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_8_2.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_8_2" + }, + { + "index": 63, + "pair_id": "P_origin_1_P_origin_1_9_0", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_9_0.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_9_0" + }, + { + "index": 64, + "pair_id": "P_origin_1_P_origin_1_9_1", + "image_paths": [ + "data/Public/Table/P_origin_1/P_origin_1_9_1.png" + ], + "domain": "Public", + "origin": "P_origin_1", + "table_id": "P_origin_1_9_1" + }, + { + "index": 65, + "pair_id": "P_origin_10_P_origin_10_0_0", + "image_paths": [ + "data/Public/Table/P_origin_10/P_origin_10_0_0.png" + ], + "domain": "Public", + "origin": "P_origin_10", + "table_id": "P_origin_10_0_0" + }, + { + "index": 66, + "pair_id": "P_origin_10_P_origin_10_0_1", + "image_paths": [ + "data/Public/Table/P_origin_10/P_origin_10_0_1.png" + ], + "domain": "Public", + "origin": "P_origin_10", + "table_id": "P_origin_10_0_1" + }, + { + "index": 67, + "pair_id": "P_origin_10_P_origin_10_0_2", + "image_paths": [ + "data/Public/Table/P_origin_10/P_origin_10_0_2.png" + ], + "domain": "Public", + "origin": "P_origin_10", + "table_id": "P_origin_10_0_2" + }, + { + "index": 68, + "pair_id": "P_origin_10_P_origin_10_0_3", + "image_paths": [ + "data/Public/Table/P_origin_10/P_origin_10_0_3.png" + ], + "domain": "Public", + "origin": "P_origin_10", + "table_id": "P_origin_10_0_3" + }, + { + "index": 69, + "pair_id": "P_origin_10_P_origin_10_0_4", + "image_paths": [ + "data/Public/Table/P_origin_10/P_origin_10_0_4.png" + ], + "domain": "Public", + "origin": "P_origin_10", + "table_id": "P_origin_10_0_4" + }, + { + "index": 70, + "pair_id": "P_origin_11_P_origin_11_0", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_0.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_0" + }, + { + "index": 71, + "pair_id": "P_origin_11_P_origin_11_10", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_10.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_10" + }, + { + "index": 72, + "pair_id": "P_origin_11_P_origin_11_11", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_11.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_11" + }, + { + "index": 73, + "pair_id": "P_origin_11_P_origin_11_12", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_12.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_12" + }, + { + "index": 74, + "pair_id": "P_origin_11_P_origin_11_13", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_13.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_13" + }, + { + "index": 75, + "pair_id": "P_origin_11_P_origin_11_14", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_14.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_14" + }, + { + "index": 76, + "pair_id": "P_origin_11_P_origin_11_15", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_15.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_15" + }, + { + "index": 77, + "pair_id": "P_origin_11_P_origin_11_16", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_16.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_16" + }, + { + "index": 78, + "pair_id": "P_origin_11_P_origin_11_17", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_17.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_17" + }, + { + "index": 79, + "pair_id": "P_origin_11_P_origin_11_18", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_18.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_18" + }, + { + "index": 80, + "pair_id": "P_origin_11_P_origin_11_19", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_19.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_19" + }, + { + "index": 81, + "pair_id": "P_origin_11_P_origin_11_1_0", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_1_0.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_1_0" + }, + { + "index": 82, + "pair_id": "P_origin_11_P_origin_11_1_1", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_1_1.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_1_1" + }, + { + "index": 83, + "pair_id": "P_origin_11_P_origin_11_1_2", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_1_2.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_1_2" + }, + { + "index": 84, + "pair_id": "P_origin_11_P_origin_11_20", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_20.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_20" + }, + { + "index": 85, + "pair_id": "P_origin_11_P_origin_11_2_0", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_2_0.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_2_0" + }, + { + "index": 86, + "pair_id": "P_origin_11_P_origin_11_2_1", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_2_1.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_2_1" + }, + { + "index": 87, + "pair_id": "P_origin_11_P_origin_11_3", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_3.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_3" + }, + { + "index": 88, + "pair_id": "P_origin_11_P_origin_11_4", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_4.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_4" + }, + { + "index": 89, + "pair_id": "P_origin_11_P_origin_11_5", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_5.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_5" + }, + { + "index": 90, + "pair_id": "P_origin_11_P_origin_11_6", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_6.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_6" + }, + { + "index": 91, + "pair_id": "P_origin_11_P_origin_11_7", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_7.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_7" + }, + { + "index": 92, + "pair_id": "P_origin_11_P_origin_11_8", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_8.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_8" + }, + { + "index": 93, + "pair_id": "P_origin_11_P_origin_11_9", + "image_paths": [ + "data/Public/Table/P_origin_11/P_origin_11_9.png" + ], + "domain": "Public", + "origin": "P_origin_11", + "table_id": "P_origin_11_9" + }, + { + "index": 94, + "pair_id": "P_origin_2_P_origin_2_0_0", + "image_paths": [ + "data/Public/Table/P_origin_2/P_origin_2_0_0.png" + ], + "domain": "Public", + "origin": "P_origin_2", + "table_id": "P_origin_2_0_0" + }, + { + "index": 95, + "pair_id": "P_origin_2_P_origin_2_0_1", + "image_paths": [ + "data/Public/Table/P_origin_2/P_origin_2_0_1.png" + ], + "domain": "Public", + "origin": "P_origin_2", + "table_id": "P_origin_2_0_1" + }, + { + "index": 96, + "pair_id": "P_origin_2_P_origin_2_0_2", + "image_paths": [ + "data/Public/Table/P_origin_2/P_origin_2_0_2.png" + ], + "domain": "Public", + "origin": "P_origin_2", + "table_id": "P_origin_2_0_2" + }, + { + "index": 97, + "pair_id": "P_origin_2_P_origin_2_0_3", + "image_paths": [ + "data/Public/Table/P_origin_2/P_origin_2_0_3.png" + ], + "domain": "Public", + "origin": "P_origin_2", + "table_id": "P_origin_2_0_3" + }, + { + "index": 98, + "pair_id": "P_origin_2_P_origin_2_1_0", + "image_paths": [ + "data/Public/Table/P_origin_2/P_origin_2_1_0.png" + ], + "domain": "Public", + "origin": "P_origin_2", + "table_id": "P_origin_2_1_0" + }, + { + "index": 99, + "pair_id": "P_origin_2_P_origin_2_1_1", + "image_paths": [ + "data/Public/Table/P_origin_2/P_origin_2_1_1.png" + ], + "domain": "Public", + "origin": "P_origin_2", + "table_id": "P_origin_2_1_1" + }, + { + "index": 100, + "pair_id": "P_origin_2_P_origin_2_1_2", + "image_paths": [ + "data/Public/Table/P_origin_2/P_origin_2_1_2.png" + ], + "domain": "Public", + "origin": "P_origin_2", + "table_id": "P_origin_2_1_2" + }, + { + "index": 101, + "pair_id": "P_origin_2_P_origin_2_2_0", + "image_paths": [ + "data/Public/Table/P_origin_2/P_origin_2_2_0.png" + ], + "domain": "Public", + "origin": "P_origin_2", + "table_id": "P_origin_2_2_0" + }, + { + "index": 102, + "pair_id": "P_origin_2_P_origin_2_2_1", + "image_paths": [ + "data/Public/Table/P_origin_2/P_origin_2_2_1.png" + ], + "domain": "Public", + "origin": "P_origin_2", + "table_id": "P_origin_2_2_1" + }, + { + "index": 103, + "pair_id": "P_origin_2_P_origin_2_2_2", + "image_paths": [ + "data/Public/Table/P_origin_2/P_origin_2_2_2.png" + ], + "domain": "Public", + "origin": "P_origin_2", + "table_id": "P_origin_2_2_2" + }, + { + "index": 104, + "pair_id": "P_origin_3_P_origin_3_0", + "image_paths": [ + "data/Public/Table/P_origin_3/P_origin_3_0.png" + ], + "domain": "Public", + "origin": "P_origin_3", + "table_id": "P_origin_3_0" + }, + { + "index": 105, + "pair_id": "P_origin_3_P_origin_3_1", + "image_paths": [ + "data/Public/Table/P_origin_3/P_origin_3_1.png" + ], + "domain": "Public", + "origin": "P_origin_3", + "table_id": "P_origin_3_1" + }, + { + "index": 106, + "pair_id": "P_origin_3_P_origin_3_2_0", + "image_paths": [ + "data/Public/Table/P_origin_3/P_origin_3_2_0.png" + ], + "domain": "Public", + "origin": "P_origin_3", + "table_id": "P_origin_3_2_0" + }, + { + "index": 107, + "pair_id": "P_origin_3_P_origin_3_2_1", + "image_paths": [ + "data/Public/Table/P_origin_3/P_origin_3_2_1.png" + ], + "domain": "Public", + "origin": "P_origin_3", + "table_id": "P_origin_3_2_1" + }, + { + "index": 108, + "pair_id": "P_origin_4_P_origin_4_0", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_0.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_0" + }, + { + "index": 109, + "pair_id": "P_origin_4_P_origin_4_1", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_1.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_1" + }, + { + "index": 110, + "pair_id": "P_origin_4_P_origin_4_10", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_10.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_10" + }, + { + "index": 111, + "pair_id": "P_origin_4_P_origin_4_11_0", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_11_0.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_11_0" + }, + { + "index": 112, + "pair_id": "P_origin_4_P_origin_4_11_1", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_11_1.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_11_1" + }, + { + "index": 113, + "pair_id": "P_origin_4_P_origin_4_12", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_12.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_12" + }, + { + "index": 114, + "pair_id": "P_origin_4_P_origin_4_13", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_13.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_13" + }, + { + "index": 115, + "pair_id": "P_origin_4_P_origin_4_14", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_14.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_14" + }, + { + "index": 116, + "pair_id": "P_origin_4_P_origin_4_15", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_15.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_15" + }, + { + "index": 117, + "pair_id": "P_origin_4_P_origin_4_16", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_16.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_16" + }, + { + "index": 118, + "pair_id": "P_origin_4_P_origin_4_17", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_17.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_17" + }, + { + "index": 119, + "pair_id": "P_origin_4_P_origin_4_18", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_18.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_18" + }, + { + "index": 120, + "pair_id": "P_origin_4_P_origin_4_19", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_19.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_19" + }, + { + "index": 121, + "pair_id": "P_origin_4_P_origin_4_2", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_2.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_2" + }, + { + "index": 122, + "pair_id": "P_origin_4_P_origin_4_3", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_3.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_3" + }, + { + "index": 123, + "pair_id": "P_origin_4_P_origin_4_4", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_4.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_4" + }, + { + "index": 124, + "pair_id": "P_origin_4_P_origin_4_5", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_5.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_5" + }, + { + "index": 125, + "pair_id": "P_origin_4_P_origin_4_6", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_6.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_6" + }, + { + "index": 126, + "pair_id": "P_origin_4_P_origin_4_7", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_7.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_7" + }, + { + "index": 127, + "pair_id": "P_origin_4_P_origin_4_8", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_8.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_8" + }, + { + "index": 128, + "pair_id": "P_origin_4_P_origin_4_9_0", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_9_0.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_9_0" + }, + { + "index": 129, + "pair_id": "P_origin_4_P_origin_4_9_1", + "image_paths": [ + "data/Public/Table/P_origin_4/P_origin_4_9_1.png" + ], + "domain": "Public", + "origin": "P_origin_4", + "table_id": "P_origin_4_9_1" + }, + { + "index": 130, + "pair_id": "P_origin_5_P_origin_5_0_0", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_0_0.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_0_0" + }, + { + "index": 131, + "pair_id": "P_origin_5_P_origin_5_0_1", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_0_1.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_0_1" + }, + { + "index": 132, + "pair_id": "P_origin_5_P_origin_5_10", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_10.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_10" + }, + { + "index": 133, + "pair_id": "P_origin_5_P_origin_5_11", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_11.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_11" + }, + { + "index": 134, + "pair_id": "P_origin_5_P_origin_5_12", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_12.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_12" + }, + { + "index": 135, + "pair_id": "P_origin_5_P_origin_5_13", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_13.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_13" + }, + { + "index": 136, + "pair_id": "P_origin_5_P_origin_5_14", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_14.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_14" + }, + { + "index": 137, + "pair_id": "P_origin_5_P_origin_5_16", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_16.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_16" + }, + { + "index": 138, + "pair_id": "P_origin_5_P_origin_5_17_0", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_17_0.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_17_0" + }, + { + "index": 139, + "pair_id": "P_origin_5_P_origin_5_17_1", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_17_1.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_17_1" + }, + { + "index": 140, + "pair_id": "P_origin_5_P_origin_5_18", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_18.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_18" + }, + { + "index": 141, + "pair_id": "P_origin_5_P_origin_5_19", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_19.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_19" + }, + { + "index": 142, + "pair_id": "P_origin_5_P_origin_5_1_0", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_1_0.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_1_0" + }, + { + "index": 143, + "pair_id": "P_origin_5_P_origin_5_1_1", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_1_1.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_1_1" + }, + { + "index": 144, + "pair_id": "P_origin_5_P_origin_5_1_2", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_1_2.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_1_2" + }, + { + "index": 145, + "pair_id": "P_origin_5_P_origin_5_2", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_2.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_2" + }, + { + "index": 146, + "pair_id": "P_origin_5_P_origin_5_20", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_20.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_20" + }, + { + "index": 147, + "pair_id": "P_origin_5_P_origin_5_21", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_21.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_21" + }, + { + "index": 148, + "pair_id": "P_origin_5_P_origin_5_22", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_22.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_22" + }, + { + "index": 149, + "pair_id": "P_origin_5_P_origin_5_23", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_23.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_23" + }, + { + "index": 150, + "pair_id": "P_origin_5_P_origin_5_24", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_24.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_24" + }, + { + "index": 151, + "pair_id": "P_origin_5_P_origin_5_25", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_25.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_25" + }, + { + "index": 152, + "pair_id": "P_origin_5_P_origin_5_26_0", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_26_0.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_26_0" + }, + { + "index": 153, + "pair_id": "P_origin_5_P_origin_5_26_1", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_26_1.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_26_1" + }, + { + "index": 154, + "pair_id": "P_origin_5_P_origin_5_3", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_3.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_3" + }, + { + "index": 155, + "pair_id": "P_origin_5_P_origin_5_4", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_4.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_4" + }, + { + "index": 156, + "pair_id": "P_origin_5_P_origin_5_5", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_5.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_5" + }, + { + "index": 157, + "pair_id": "P_origin_5_P_origin_5_6", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_6.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_6" + }, + { + "index": 158, + "pair_id": "P_origin_5_P_origin_5_7", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_7.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_7" + }, + { + "index": 159, + "pair_id": "P_origin_5_P_origin_5_8", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_8.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_8" + }, + { + "index": 160, + "pair_id": "P_origin_5_P_origin_5_9", + "image_paths": [ + "data/Public/Table/P_origin_5/P_origin_5_9.png" + ], + "domain": "Public", + "origin": "P_origin_5", + "table_id": "P_origin_5_9" + }, + { + "index": 161, + "pair_id": "P_origin_6_P_origin_6_0", + "image_paths": [ + "data/Public/Table/P_origin_6/P_origin_6_0.png" + ], + "domain": "Public", + "origin": "P_origin_6", + "table_id": "P_origin_6_0" + }, + { + "index": 162, + "pair_id": "P_origin_6_P_origin_6_1", + "image_paths": [ + "data/Public/Table/P_origin_6/P_origin_6_1.png" + ], + "domain": "Public", + "origin": "P_origin_6", + "table_id": "P_origin_6_1" + }, + { + "index": 163, + "pair_id": "P_origin_6_P_origin_6_10", + "image_paths": [ + "data/Public/Table/P_origin_6/P_origin_6_10.png" + ], + "domain": "Public", + "origin": "P_origin_6", + "table_id": "P_origin_6_10" + }, + { + "index": 164, + "pair_id": "P_origin_6_P_origin_6_11", + "image_paths": [ + "data/Public/Table/P_origin_6/P_origin_6_11.png" + ], + "domain": "Public", + "origin": "P_origin_6", + "table_id": "P_origin_6_11" + }, + { + "index": 165, + "pair_id": "P_origin_6_P_origin_6_12_0", + "image_paths": [ + "data/Public/Table/P_origin_6/P_origin_6_12_0.png" + ], + "domain": "Public", + "origin": "P_origin_6", + "table_id": "P_origin_6_12_0" + }, + { + "index": 166, + "pair_id": "P_origin_6_P_origin_6_12_1", + "image_paths": [ + "data/Public/Table/P_origin_6/P_origin_6_12_1.png" + ], + "domain": "Public", + "origin": "P_origin_6", + "table_id": "P_origin_6_12_1" + }, + { + "index": 167, + "pair_id": "P_origin_6_P_origin_6_2", + "image_paths": [ + "data/Public/Table/P_origin_6/P_origin_6_2.png" + ], + "domain": "Public", + "origin": "P_origin_6", + "table_id": "P_origin_6_2" + }, + { + "index": 168, + "pair_id": "P_origin_6_P_origin_6_3", + "image_paths": [ + "data/Public/Table/P_origin_6/P_origin_6_3.png" + ], + "domain": "Public", + "origin": "P_origin_6", + "table_id": "P_origin_6_3" + }, + { + "index": 169, + "pair_id": "P_origin_6_P_origin_6_4", + "image_paths": [ + "data/Public/Table/P_origin_6/P_origin_6_4.png" + ], + "domain": "Public", + "origin": "P_origin_6", + "table_id": "P_origin_6_4" + }, + { + "index": 170, + "pair_id": "P_origin_6_P_origin_6_5", + "image_paths": [ + "data/Public/Table/P_origin_6/P_origin_6_5.png" + ], + "domain": "Public", + "origin": "P_origin_6", + "table_id": "P_origin_6_5" + }, + { + "index": 171, + "pair_id": "P_origin_6_P_origin_6_6", + "image_paths": [ + "data/Public/Table/P_origin_6/P_origin_6_6.png" + ], + "domain": "Public", + "origin": "P_origin_6", + "table_id": "P_origin_6_6" + }, + { + "index": 172, + "pair_id": "P_origin_6_P_origin_6_7", + "image_paths": [ + "data/Public/Table/P_origin_6/P_origin_6_7.png" + ], + "domain": "Public", + "origin": "P_origin_6", + "table_id": "P_origin_6_7" + }, + { + "index": 173, + "pair_id": "P_origin_6_P_origin_6_8", + "image_paths": [ + "data/Public/Table/P_origin_6/P_origin_6_8.png" + ], + "domain": "Public", + "origin": "P_origin_6", + "table_id": "P_origin_6_8" + }, + { + "index": 174, + "pair_id": "P_origin_6_P_origin_6_9", + "image_paths": [ + "data/Public/Table/P_origin_6/P_origin_6_9.png" + ], + "domain": "Public", + "origin": "P_origin_6", + "table_id": "P_origin_6_9" + }, + { + "index": 175, + "pair_id": "P_origin_7_P_origin_7_0_0", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_0_0.png" + ], + "domain": "Public", + "origin": "P_origin_7", + "table_id": "P_origin_7_0_0" + }, + { + "index": 176, + "pair_id": "P_origin_7_P_origin_7_0_1", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_0_1.png" + ], + "domain": "Public", + "origin": "P_origin_7", + "table_id": "P_origin_7_0_1" + }, + { + "index": 177, + "pair_id": "P_origin_7_P_origin_7_1_0", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_1_0.png" + ], + "domain": "Public", + "origin": "P_origin_7", + "table_id": "P_origin_7_1_0" + }, + { + "index": 178, + "pair_id": "P_origin_7_P_origin_7_1_1", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_1_1.png" + ], + "domain": "Public", + "origin": "P_origin_7", + "table_id": "P_origin_7_1_1" + }, + { + "index": 179, + "pair_id": "P_origin_7_P_origin_7_2_0", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_2_0.png" + ], + "domain": "Public", + "origin": "P_origin_7", + "table_id": "P_origin_7_2_0" + }, + { + "index": 180, + "pair_id": "P_origin_7_P_origin_7_2_1", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_2_1.png" + ], + "domain": "Public", + "origin": "P_origin_7", + "table_id": "P_origin_7_2_1" + }, + { + "index": 181, + "pair_id": "P_origin_7_P_origin_7_3", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_3.png" + ], + "domain": "Public", + "origin": "P_origin_7", + "table_id": "P_origin_7_3" + }, + { + "index": 182, + "pair_id": "P_origin_7_P_origin_7_4_0", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_4_0.png" + ], + "domain": "Public", + "origin": "P_origin_7", + "table_id": "P_origin_7_4_0" + }, + { + "index": 183, + "pair_id": "P_origin_7_P_origin_7_4_1", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_4_1.png" + ], + "domain": "Public", + "origin": "P_origin_7", + "table_id": "P_origin_7_4_1" + }, + { + "index": 184, + "pair_id": "P_origin_7_P_origin_7_5", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_5.png" + ], + "domain": "Public", + "origin": "P_origin_7", + "table_id": "P_origin_7_5" + }, + { + "index": 185, + "pair_id": "P_origin_7_P_origin_7_6", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_6.png" + ], + "domain": "Public", + "origin": "P_origin_7", + "table_id": "P_origin_7_6" + }, + { + "index": 186, + "pair_id": "P_origin_7_P_origin_7_7", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_7.png" + ], + "domain": "Public", + "origin": "P_origin_7", + "table_id": "P_origin_7_7" + }, + { + "index": 187, + "pair_id": "P_origin_7_P_origin_7_8_0", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_8_0.png" + ], + "domain": "Public", + "origin": "P_origin_7", + "table_id": "P_origin_7_8_0" + }, + { + "index": 188, + "pair_id": "P_origin_7_P_origin_7_8_1", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_8_1.png" + ], + "domain": "Public", + "origin": "P_origin_7", + "table_id": "P_origin_7_8_1" + }, + { + "index": 189, + "pair_id": "P_origin_7_P_origin_7_8_2", + "image_paths": [ + "data/Public/Table/P_origin_7/P_origin_7_8_2.png" + ], + "domain": "Public", + "origin": "P_origin_7", + "table_id": "P_origin_7_8_2" + }, + { + "index": 190, + "pair_id": "P_origin_8_P_origin_8_0", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_0.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_0" + }, + { + "index": 191, + "pair_id": "P_origin_8_P_origin_8_1", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_1.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_1" + }, + { + "index": 192, + "pair_id": "P_origin_8_P_origin_8_10_0", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_10_0.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_10_0" + }, + { + "index": 193, + "pair_id": "P_origin_8_P_origin_8_10_1", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_10_1.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_10_1" + }, + { + "index": 194, + "pair_id": "P_origin_8_P_origin_8_11", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_11.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_11" + }, + { + "index": 195, + "pair_id": "P_origin_8_P_origin_8_12_0", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_12_0.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_12_0" + }, + { + "index": 196, + "pair_id": "P_origin_8_P_origin_8_12_1", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_12_1.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_12_1" + }, + { + "index": 197, + "pair_id": "P_origin_8_P_origin_8_13", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_13.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_13" + }, + { + "index": 198, + "pair_id": "P_origin_8_P_origin_8_14_0", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_14_0.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_14_0" + }, + { + "index": 199, + "pair_id": "P_origin_8_P_origin_8_14_1", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_14_1.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_14_1" + }, + { + "index": 200, + "pair_id": "P_origin_8_P_origin_8_14_2", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_14_2.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_14_2" + }, + { + "index": 201, + "pair_id": "P_origin_8_P_origin_8_14_3", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_14_3.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_14_3" + }, + { + "index": 202, + "pair_id": "P_origin_8_P_origin_8_14_4", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_14_4.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_14_4" + }, + { + "index": 203, + "pair_id": "P_origin_8_P_origin_8_14_5", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_14_5.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_14_5" + }, + { + "index": 204, + "pair_id": "P_origin_8_P_origin_8_14_6", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_14_6.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_14_6" + }, + { + "index": 205, + "pair_id": "P_origin_8_P_origin_8_14_7", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_14_7.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_14_7" + }, + { + "index": 206, + "pair_id": "P_origin_8_P_origin_8_15", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_15.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_15" + }, + { + "index": 207, + "pair_id": "P_origin_8_P_origin_8_16", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_16.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_16" + }, + { + "index": 208, + "pair_id": "P_origin_8_P_origin_8_17_0", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_17_0.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_17_0" + }, + { + "index": 209, + "pair_id": "P_origin_8_P_origin_8_17_1", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_17_1.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_17_1" + }, + { + "index": 210, + "pair_id": "P_origin_8_P_origin_8_17_2", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_17_2.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_17_2" + }, + { + "index": 211, + "pair_id": "P_origin_8_P_origin_8_17_3", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_17_3.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_17_3" + }, + { + "index": 212, + "pair_id": "P_origin_8_P_origin_8_17_4", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_17_4.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_17_4" + }, + { + "index": 213, + "pair_id": "P_origin_8_P_origin_8_17_5", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_17_5.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_17_5" + }, + { + "index": 214, + "pair_id": "P_origin_8_P_origin_8_17_6", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_17_6.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_17_6" + }, + { + "index": 215, + "pair_id": "P_origin_8_P_origin_8_17_7", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_17_7.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_17_7" + }, + { + "index": 216, + "pair_id": "P_origin_8_P_origin_8_18", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_18.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_18" + }, + { + "index": 217, + "pair_id": "P_origin_8_P_origin_8_19", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_19.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_19" + }, + { + "index": 218, + "pair_id": "P_origin_8_P_origin_8_2", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_2.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_2" + }, + { + "index": 219, + "pair_id": "P_origin_8_P_origin_8_3", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_3.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_3" + }, + { + "index": 220, + "pair_id": "P_origin_8_P_origin_8_4", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_4.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_4" + }, + { + "index": 221, + "pair_id": "P_origin_8_P_origin_8_5", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_5.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_5" + }, + { + "index": 222, + "pair_id": "P_origin_8_P_origin_8_6", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_6.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_6" + }, + { + "index": 223, + "pair_id": "P_origin_8_P_origin_8_7", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_7.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_7" + }, + { + "index": 224, + "pair_id": "P_origin_8_P_origin_8_8", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_8.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_8" + }, + { + "index": 225, + "pair_id": "P_origin_8_P_origin_8_9_0", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_9_0.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_9_0" + }, + { + "index": 226, + "pair_id": "P_origin_8_P_origin_8_9_1", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_9_1.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_9_1" + }, + { + "index": 227, + "pair_id": "P_origin_8_P_origin_8_9_2", + "image_paths": [ + "data/Public/Table/P_origin_8/P_origin_8_9_2.png" + ], + "domain": "Public", + "origin": "P_origin_8", + "table_id": "P_origin_8_9_2" + }, + { + "index": 228, + "pair_id": "P_origin_9_P_origin_9_0_0", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_0_0.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_0_0" + }, + { + "index": 229, + "pair_id": "P_origin_9_P_origin_9_0_1", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_0_1.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_0_1" + }, + { + "index": 230, + "pair_id": "P_origin_9_P_origin_9_0_2", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_0_2.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_0_2" + }, + { + "index": 231, + "pair_id": "P_origin_9_P_origin_9_0_3", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_0_3.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_0_3" + }, + { + "index": 232, + "pair_id": "P_origin_9_P_origin_9_10", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_10.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_10" + }, + { + "index": 233, + "pair_id": "P_origin_9_P_origin_9_11", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_11.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_11" + }, + { + "index": 234, + "pair_id": "P_origin_9_P_origin_9_1_0", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_1_0.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_1_0" + }, + { + "index": 235, + "pair_id": "P_origin_9_P_origin_9_1_1", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_1_1.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_1_1" + }, + { + "index": 236, + "pair_id": "P_origin_9_P_origin_9_1_2", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_1_2.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_1_2" + }, + { + "index": 237, + "pair_id": "P_origin_9_P_origin_9_2_0", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_2_0.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_2_0" + }, + { + "index": 238, + "pair_id": "P_origin_9_P_origin_9_2_1", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_2_1.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_2_1" + }, + { + "index": 239, + "pair_id": "P_origin_9_P_origin_9_2_2", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_2_2.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_2_2" + }, + { + "index": 240, + "pair_id": "P_origin_9_P_origin_9_3", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_3.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_3" + }, + { + "index": 241, + "pair_id": "P_origin_9_P_origin_9_4", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_4.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_4" + }, + { + "index": 242, + "pair_id": "P_origin_9_P_origin_9_5", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_5.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_5" + }, + { + "index": 243, + "pair_id": "P_origin_9_P_origin_9_6_0", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_6_0.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_6_0" + }, + { + "index": 244, + "pair_id": "P_origin_9_P_origin_9_6_1", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_6_1.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_6_1" + }, + { + "index": 245, + "pair_id": "P_origin_9_P_origin_9_6_2", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_6_2.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_6_2" + }, + { + "index": 246, + "pair_id": "P_origin_9_P_origin_9_7", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_7.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_7" + }, + { + "index": 247, + "pair_id": "P_origin_9_P_origin_9_8", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_8.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_8" + }, + { + "index": 248, + "pair_id": "P_origin_9_P_origin_9_9", + "image_paths": [ + "data/Public/Table/P_origin_9/P_origin_9_9.png" + ], + "domain": "Public", + "origin": "P_origin_9", + "table_id": "P_origin_9_9" + } +] \ No newline at end of file diff --git a/test_input.json b/test_input.json deleted file mode 100644 index f51ecd0..0000000 --- a/test_input.json +++ /dev/null @@ -1,119 +0,0 @@ -[ - { - "index": 0, - "pair_id": "P_origin_0_1", - "image_paths": [ - "data/Public/Table/P_origin_0/P_origin_0_1_0.png", - "data/Public/Table/P_origin_0/P_origin_0_1_1.png" - ], - "domain": "public" - }, - { - "index": 1, - "pair_id": "P_origin_0_2", - "image_paths": [ - "data/Public/Table/P_origin_0/P_origin_0_2_1.png", - "data/Public/Table/P_origin_0/P_origin_0_2_2.png" - ], - "domain": "public" - }, - { - "index": 2, - "pair_id": "P_origin_1_0", - "image_paths": [ - "data/Public/Table/P_origin_1/P_origin_1_0.png" - ], - "domain": "public" - }, - { - "index": 3, - "pair_id": "P_origin_1_2", - "image_paths": [ - "data/Public/Table/P_origin_1/P_origin_1_2.png" - ], - "domain": "public" - }, - { - "index": 4, - "pair_id": "P_origin_1_4", - "image_paths": [ - "data/Public/Table/P_origin_1/P_origin_1_4.png" - ], - "domain": "public" - }, - { - "index": 5, - "pair_id": "P_origin_1_5", - "image_paths": [ - "data/Public/Table/P_origin_1/P_origin_1_5.png" - ], - "domain": "public" - }, - { - "index": 6, - "pair_id": "P_origin_1_7", - "image_paths": [ - "data/Public/Table/P_origin_1/P_origin_1_7.png" - ], - "domain": "public" - }, - { - "index": 7, - "pair_id": "P_origin_1_9", - "image_paths": [ - "data/Public/Table/P_origin_1/P_origin_1_9_0.png", - "data/Public/Table/P_origin_1/P_origin_1_9_1.png" - ], - "domain": "public" - }, - { - "index": 8, - "pair_id": "P_origin_1_10", - "image_paths": [ - "data/Public/Table/P_origin_1/P_origin_1_10_0.png", - "data/Public/Table/P_origin_1/P_origin_1_10_1.png" - ], - "domain": "public" - }, - { - "index": 9, - "pair_id": "P_origin_1_12", - "image_paths": [ - "data/Public/Table/P_origin_1/P_origin_1_12_0.png", - "data/Public/Table/P_origin_1/P_origin_1_12_1.png" - ], - "domain": "public" - }, - { - "index": 10, - "pair_id": "P_origin_1_13", - "image_paths": [ - "data/Public/Table/P_origin_1/P_origin_1_13_0.png" - ], - "domain": "public" - }, - { - "index": 11, - "pair_id": "P_origin_1_14", - "image_paths": [ - "data/Public/Table/P_origin_1/P_origin_1_14_0.png" - ], - "domain": "public" - }, - { - "index": 12, - "pair_id": "P_origin_1_23", - "image_paths": [ - "data/Public/Table/P_origin_1/P_origin_1_23_0.png" - ], - "domain": "public" - }, - { - "index": 13, - "pair_id": "P_origin_4_6", - "image_paths": [ - "data/Public/Table/P_origin_4/P_origin_4_6.png" - ], - "domain": "public" - } -] \ No newline at end of file diff --git a/tests/choi/QA_example/README.md b/tests/choi/QA_example/README.md deleted file mode 100644 index 30f07f3..0000000 --- a/tests/choi/QA_example/README.md +++ /dev/null @@ -1,129 +0,0 @@ -# QA Dataset Generation Module - -보험 테이블 마크다운 데이터를 기반으로 고품질 QA(Question-Answer) 데이터셋을 생성하는 모듈입니다. - -## 주요 기능 - -### 1. 난이도별 QA 생성 -- **IR (Information Retrieval)**: 단순 정보 검색 (Level 1) -- **Analysis**: 분석적 질문 (Level 2) -- **Compare (Multi-hop)**: 비교 및 다중 추론 (Level 3) -- **Aggregation**: 집계 연산 (Level 4) -- **Reasoning**: 복합 추론 (Level 5) -- **Insight**: 통찰 도출 (Level 6) - -### 2. 다양한 답변 유형 -- **Exact Match**: 단답형 (숫자, 예/아니오) - 정확한 매칭 평가 -- **Descriptive**: 서술형 - LLM-as-Judge 평가 -- **Calculation**: 수치 계산 결과 - Python 코드로 검증 - -### 3. 고급 기능 -- **Multi-Table QA**: 복수 테이블 참조 필요 질문 -- **Follow-up QA**: 꼬리 질문 체인 생성 -- **Evol-Instruct**: 질문 난이도 진화 -- **LLM-as-Judge**: 품질 평가 - -## 사용법 - -### 기본 사용 - -```python -from QA_example import InsuranceTableQAGenerator, QADifficulty - -# 테이블 데이터 준비 -tables = { - "table_1": "| 구분 | 값 |\n|---|---|\n| A | 100 |", - "table_2": "| 항목 | 금액 |\n|---|---|\n| B | 200 |" -} - -# Generator 초기화 -generator = InsuranceTableQAGenerator() - -# 특정 난이도 QA 생성 -ir_qa = generator.generate_qa_by_difficulty(tables, QADifficulty.IR, num_questions=3) - -# 종합 데이터셋 생성 -dataset = generator.generate_comprehensive_qa_dataset( - tables, - questions_per_difficulty=2, - include_followup=True, - include_evolution=True -) -``` - -### 간편 함수 사용 - -```python -from QA_example import generate_qa_from_tables - -# 모든 난이도 QA 생성 -all_qa = generate_qa_from_tables(tables, num_questions=2) - -# 특정 난이도만 생성 -ir_only = generate_qa_from_tables(tables, difficulty=QADifficulty.IR) -``` - -## 커버되는 QA 양상 - -| # | 양상 | 설명 | 구현 방식 | -|---|------|------|----------| -| 1 | Multi-table QA | 복수 테이블 참조 | `generate_multi_table_qa()` | -| 2 | 난이도별 QA | 6단계 난이도 체계 | `QADifficulty` Enum | -| 3 | 다양한 답변 유형 | Exact Match, Descriptive | `QAType` Enum | -| 4 | 수치 계산 QA | 집계, 비율 계산 | Aggregation 난이도 | -| 5 | 꼬리 질문 | Q-A 체인 | `generate_followup_qa()` | -| 6 | 셀 기반 측정 | 여러 셀 기반 | Compare, Aggregation | -| 7 | 특정 셀 Q-A | 단일 셀 검색 | IR 난이도 | -| 8 | 이미지 연관 QA | 테이블 구조 기반 | 테이블 마크다운 입력 | - -## 파일 구조 - -``` -QA_example/ -├── __init__.py # 모듈 초기화 -├── prompts.py # 프롬프트 템플릿 -├── qa_generator.py # QA 생성 핵심 로직 -├── qa_generation.ipynb # 사용 예제 노트북 -├── README.md # 이 문서 -└── output/ # 생성된 데이터셋 -``` - -## 프롬프트 전략 - -### Chain-of-Table -- 단계별 표 해석 과정 명시 -- 동적 계획법 기반 질문 생성 - -### Program-of-Thought (PoT) -- 수치 계산을 Python 코드로 생성 -- 계산 결과의 무결성 보장 - -### Tabular Chain-of-Thought -- 추론 과정을 표 형태로 구조화 -- Step → Sub-question → Evidence → Reasoning - -### Evol-Instruct -- 제약 조건 추가 (Adding Constraints) -- 심층 추론 (Deepening Reasoning) -- 구체화 (Concretizing) -- 입력 복잡도 증가 (Complicating Input) - -## 품질 평가 (LLM-as-Judge) - -생성된 QA의 품질을 5가지 차원으로 평가: - -1. **정확성 (Correctness)**: 답변의 사실적 정확성 -2. **충실성 (Faithfulness)**: 테이블 데이터에 대한 충실도 -3. **관련성 (Relevance)**: 보험 도메인 실용성 -4. **난이도 적절성**: 표기 난이도와 실제 난이도 일치 -5. **명확성 (Clarity)**: 질문과 답변의 명확성 - -## 의존성 - -- `polling_gemini`: Gemini API 풀링 시스템 -- `google-generativeai`: Google Gemini API -- `pyyaml`: YAML 설정 파일 처리 - -## 라이센스 - -MIT License diff --git a/tests/choi/QA_example/__init__.py b/tests/choi/QA_example/__init__.py deleted file mode 100644 index 53e6823..0000000 --- a/tests/choi/QA_example/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -QA Dataset Generation Module for Insurance Tables -보험 테이블 기반 QA 데이터셋 생성 모듈 -""" - -from .qa_generator import ( - InsuranceTableQAGenerator, - QADifficulty, - QAType, - generate_qa_from_tables, -) - -__all__ = [ - 'InsuranceTableQAGenerator', - 'QADifficulty', - 'QAType', - 'generate_qa_from_tables', -] - -__version__ = '0.1.0' diff --git a/tests/choi/QA_example/prompts.py b/tests/choi/QA_example/prompts.py deleted file mode 100644 index 9adfeb5..0000000 --- a/tests/choi/QA_example/prompts.py +++ /dev/null @@ -1,479 +0,0 @@ -""" -QA Generation Prompts for Insurance Table Data -보험 테이블 기반 QA 생성을 위한 프롬프트 템플릿 - -난이도별 QA 유형: -1. IR (Information Retrieval): 단순 정보 검색 -2. Analysis: 분석적 질문 -3. Compare (Multi-hop): 비교 및 다중 추론 -4. Aggregation: 집계 연산 -5. Reasoning: 복합 추론 -6. Insight: 통찰 도출 - -답변 유형: -- Exact Match: 단답형 (숫자, 예/아니오) -- Descriptive: 서술형 (LLM Judge 평가) -""" - -# ============================================================================= -# System Prompts -# ============================================================================= - -QA_GENERATOR_SYSTEM_PROMPT = """# Role Definition -당신은 보험 도메인 전문가이자 고품질 QA 데이터셋 구축 전문가입니다. -주어진 보험 테이블 데이터를 기반으로 다양한 난이도와 유형의 질문-답변 쌍을 생성해야 합니다. - -# Core Principles -1. **정확성(Accuracy):** 모든 답변은 주어진 테이블 데이터에 근거해야 합니다. 테이블에 없는 정보를 추측하지 마십시오. -2. **다양성(Diversity):** 단순 검색부터 복잡한 추론까지 다양한 난이도의 질문을 생성해야 합니다. -3. **실용성(Practicality):** 실제 보험 고객이 물어볼 수 있는 현실적인 질문을 생성해야 합니다. -4. **명확성(Clarity):** 질문과 답변 모두 명확하고 모호하지 않아야 합니다. - -# Difficulty Levels (난이도) -- **IR (Level 1):** 특정 셀의 값을 직접 찾는 단순 검색 -- **Analysis (Level 2):** 단일 테이블 내에서의 분석적 질문 -- **Compare (Level 3):** 여러 행/열 또는 복수 테이블 간 비교 -- **Aggregation (Level 4):** 합계, 평균, 최대/최소 등의 집계 연산 -- **Reasoning (Level 5):** 여러 정보를 종합한 복합 추론 -- **Insight (Level 6):** 데이터로부터 통찰이나 시사점 도출 - -# Answer Types (답변 유형) -- **exact_match:** 숫자, 예/아니오, 특정 텍스트 등 정확히 일치해야 하는 답변 -- **descriptive:** 설명이 필요한 서술형 답변 (LLM-as-Judge로 평가) -- **calculation:** 수치 계산 결과 (계산 과정 포함) -- **comparison:** 비교 결과 및 근거""" - -# ============================================================================= -# QA Generation Prompts by Difficulty -# ============================================================================= - -IR_QA_PROMPT = """## Task: Information Retrieval (IR) Level QA 생성 -단일 테이블에서 특정 셀의 값을 직접 검색하는 간단한 QA를 생성하세요. - -### Input Tables -{tables} - -### Requirements -1. 특정 행과 열이 교차하는 지점의 값을 묻는 질문 -2. 단답형으로 대답 가능한 질문 -3. 테이블에서 바로 찾을 수 있는 정보만 질문 - -### Output Format (JSON) -```json -{{ - "questions": [ - {{ - "id": "IR_001", - "difficulty": "IR", - "answer_type": "exact_match", - "question": "질문 내용", - "answer": "정확한 답변", - "evidence": {{ - "table_id": "table_1", - "row": "행 정보", - "column": "열 정보" - }}, - "tags": ["single_cell", "numeric"] - }} - ] -}} -``` - -### Generate {num_questions} IR-level QA pairs.""" - -ANALYSIS_QA_PROMPT = """## Task: Analysis Level QA 생성 -단일 테이블 내에서 데이터를 분석하는 질문을 생성하세요. - -### Input Tables -{tables} - -### Requirements -1. 특정 조건을 만족하는 행/열 찾기 -2. 최대값/최소값을 가진 항목 식별 -3. 특정 범위 내의 데이터 확인 -4. 단답형 또는 짧은 설명형 답변 - -### Output Format (JSON) -```json -{{ - "questions": [ - {{ - "id": "ANALYSIS_001", - "difficulty": "Analysis", - "answer_type": "exact_match", - "question": "질문 내용", - "answer": "정확한 답변", - "reasoning": "답을 도출하는 과정 설명", - "evidence": {{ - "table_id": "table_1", - "relevant_cells": ["셀 위치1", "셀 위치2"] - }}, - "tags": ["conditional_search", "extrema"] - }} - ] -}} -``` - -### Generate {num_questions} Analysis-level QA pairs.""" - -COMPARE_QA_PROMPT = """## Task: Compare (Multi-hop) Level QA 생성 -여러 행, 열, 또는 복수 테이블 간 비교가 필요한 질문을 생성하세요. - -### Input Tables -{tables} - -### Requirements -1. 두 개 이상의 셀 값을 비교하는 질문 -2. 시계열 변화를 비교하는 질문 -3. 복수 테이블의 정보를 연결하는 질문 -4. 차이, 비율, 증감률 등을 묻는 질문 - -### Output Format (JSON) -```json -{{ - "questions": [ - {{ - "id": "COMPARE_001", - "difficulty": "Compare", - "answer_type": "calculation", - "question": "질문 내용", - "answer": "정확한 답변", - "calculation": "계산 과정", - "reasoning": "비교 논리 설명", - "evidence": {{ - "table_ids": ["table_1", "table_2"], - "compared_cells": [ - {{"table": "table_1", "row": "행1", "column": "열1", "value": "값1"}}, - {{"table": "table_1", "row": "행2", "column": "열2", "value": "값2"}} - ] - }}, - "tags": ["multi_hop", "comparison", "calculation"] - }} - ] -}} -``` - -### Generate {num_questions} Compare-level QA pairs.""" - -AGGREGATION_QA_PROMPT = """## Task: Aggregation Level QA 생성 -합계, 평균, 누적값 등 집계 연산이 필요한 질문을 생성하세요. - -### Input Tables -{tables} - -### Requirements -1. 특정 열/행의 합계를 구하는 질문 -2. 평균값을 계산하는 질문 -3. 누적 증가율을 구하는 질문 -4. 조건부 집계 (특정 조건을 만족하는 항목들의 합계 등) - -### Python Code for Verification -답변의 정확성 검증을 위해 Python 코드도 함께 생성하세요. - -### Output Format (JSON) -```json -{{ - "questions": [ - {{ - "id": "AGG_001", - "difficulty": "Aggregation", - "answer_type": "calculation", - "question": "질문 내용", - "answer": "정확한 수치 답변", - "calculation": "단계별 계산 과정", - "python_verification": "import pandas as pd\\n# 검증 코드", - "evidence": {{ - "table_id": "table_1", - "aggregated_cells": ["셀1", "셀2", "셀3"] - }}, - "tags": ["aggregation", "sum", "average"] - }} - ] -}} -``` - -### Generate {num_questions} Aggregation-level QA pairs.""" - -REASONING_QA_PROMPT = """## Task: Reasoning Level QA 생성 -여러 정보를 종합하여 복합적인 추론이 필요한 질문을 생성하세요. - -### Input Tables -{tables} - -### Requirements -1. 조건부 로직을 적용한 추론 질문 -2. 가정(Assumption)을 포함한 시나리오 기반 질문 -3. 인과관계를 파악하는 질문 -4. 여러 단계의 논리적 추론이 필요한 질문 - -### Chain-of-Thought Reasoning -답변 도출 과정을 단계별로 명시하세요. - -### Output Format (JSON) -```json -{{ - "questions": [ - {{ - "id": "REASON_001", - "difficulty": "Reasoning", - "answer_type": "descriptive", - "question": "질문 내용", - "answer": "답변", - "chain_of_thought": [ - "Step 1: ...", - "Step 2: ...", - "Step 3: ..." - ], - "assumptions": ["가정1", "가정2"], - "evidence": {{ - "table_ids": ["table_1"], - "relevant_data": ["관련 데이터 포인트"] - }}, - "tags": ["multi_step_reasoning", "conditional_logic"] - }} - ] -}} -``` - -### Generate {num_questions} Reasoning-level QA pairs.""" - -INSIGHT_QA_PROMPT = """## Task: Insight Level QA 생성 -데이터로부터 통찰이나 시사점을 도출하는 고난도 질문을 생성하세요. - -### Input Tables -{tables} - -### Requirements -1. 데이터 추세(Trend)를 파악하는 질문 -2. 이상치(Anomaly)나 특이 패턴을 발견하는 질문 -3. 데이터 기반 예측이나 권고를 요청하는 질문 -4. 비즈니스적 함의를 도출하는 질문 - -### Output Format (JSON) -```json -{{ - "questions": [ - {{ - "id": "INSIGHT_001", - "difficulty": "Insight", - "answer_type": "descriptive", - "question": "질문 내용", - "answer": "통찰 및 답변", - "supporting_analysis": "분석 과정", - "key_findings": ["발견1", "발견2"], - "evidence": {{ - "table_ids": ["table_1", "table_2"], - "data_points": ["근거 데이터"] - }}, - "tags": ["trend_analysis", "insight", "recommendation"] - }} - ] -}} -``` - -### Generate {num_questions} Insight-level QA pairs.""" - -# ============================================================================= -# Follow-up Question Prompts (꼬리 질문) -# ============================================================================= - -FOLLOWUP_QA_PROMPT = """## Task: Follow-up Question (꼬리 질문) 생성 -주어진 초기 QA에 대해 연속적인 후속 질문을 생성하세요. - -### Original QA -{original_qa} - -### Input Tables -{tables} - -### Requirements -1. 원래 질문의 맥락을 유지하면서 심화된 질문 -2. 원래 답변에서 파생되는 추가 질문 -3. 관련된 다른 데이터 포인트를 탐색하는 질문 -4. 2-3개의 연속적인 후속 질문 체인 생성 - -### Output Format (JSON) -```json -{{ - "original_qa": {{ - "question": "원래 질문", - "answer": "원래 답변" - }}, - "followup_chain": [ - {{ - "id": "FOLLOWUP_001_1", - "question": "후속 질문 1", - "answer": "답변 1", - "reasoning": "이전 답변과의 연결고리" - }}, - {{ - "id": "FOLLOWUP_001_2", - "question": "후속 질문 2 (질문1 기반)", - "answer": "답변 2", - "reasoning": "이전 답변과의 연결고리" - }} - ] -}} -``` - -### Generate follow-up questions chain.""" - -# ============================================================================= -# Multi-Table QA Prompts -# ============================================================================= - -MULTI_TABLE_QA_PROMPT = """## Task: Multi-Table QA 생성 -복수의 테이블을 참조해야 답변 가능한 질문을 생성하세요. - -### Input Tables -{tables} - -### Requirements -1. 반드시 2개 이상의 테이블 정보를 조합해야 답변 가능한 질문 -2. 테이블 간 연결 키(Key)를 활용한 질문 -3. 서로 다른 테이블의 수치를 비교/연산하는 질문 -4. 종합적인 분석이 필요한 질문 - -### Output Format (JSON) -```json -{{ - "questions": [ - {{ - "id": "MULTI_001", - "difficulty": "Compare", - "answer_type": "calculation", - "question": "질문 내용", - "answer": "답변", - "required_tables": ["table_1", "table_2"], - "join_logic": "테이블 연결 방법 설명", - "reasoning": "답변 도출 과정", - "tags": ["multi_table", "join", "cross_reference"] - }} - ] -}} -``` - -### Generate {num_questions} Multi-Table QA pairs.""" - -# ============================================================================= -# Evol-Instruct Prompts (난이도 진화) -# ============================================================================= - -EVOL_INSTRUCT_PROMPT = """## Task: Evol-Instruct - 질문 난이도 진화 -주어진 기본 질문을 더 복잡하고 도전적인 질문으로 진화시키세요. - -### Original Question -{original_question} - -### Evolution Strategies -다음 전략 중 하나 이상을 적용하여 질문을 진화시키세요: - -1. **제약 조건 추가 (Adding Constraints):** - - 특정 조건(나이, 기간, 금액 범위 등)을 추가 - -2. **심층 추론 (Deepening Reasoning):** - - 다단계 논리적 사고를 요구하도록 변환 - -3. **구체화 (Concretizing):** - - 추상적 질문을 구체적 시나리오로 대체 - -4. **입력 복잡도 증가 (Complicating Input):** - - 복수 테이블이나 추가 조건을 참조하도록 변환 - -### Input Tables -{tables} - -### Output Format (JSON) -```json -{{ - "original": {{ - "question": "원래 질문", - "difficulty": "원래 난이도" - }}, - "evolved": {{ - "question": "진화된 질문", - "difficulty": "새로운 난이도", - "evolution_strategy": "적용된 전략", - "answer": "새로운 답변", - "reasoning": "답변 도출 과정" - }} -}} -``` - -### Evolve the question.""" - -# ============================================================================= -# Quality Evaluation Prompts (LLM-as-Judge) -# ============================================================================= - -QA_EVALUATION_PROMPT = """## Task: QA 품질 평가 (LLM-as-Judge) -생성된 QA 쌍의 품질을 다면적으로 평가하세요. - -### QA to Evaluate -{qa_pair} - -### Reference Tables -{tables} - -### Evaluation Criteria (1-5점 척도) - -1. **정확성 (Correctness):** - - 답변이 테이블 데이터에 정확히 근거하는가? - - 수치 계산이 정확한가? - -2. **충실성 (Faithfulness):** - - 테이블에 없는 정보를 날조(Hallucination)하지 않았는가? - - 근거 데이터가 명확한가? - -3. **관련성 (Relevance):** - - 질문이 보험 도메인에서 실용적인가? - - 실제 고객이 물어볼 법한 질문인가? - -4. **난이도 적절성 (Difficulty Appropriateness):** - - 표기된 난이도와 실제 난이도가 일치하는가? - -5. **명확성 (Clarity):** - - 질문과 답변이 명확하고 모호하지 않은가? - -### Output Format (JSON) -```json -{{ - "evaluation": {{ - "correctness": {{"score": 5, "comment": "평가 코멘트"}}, - "faithfulness": {{"score": 5, "comment": "평가 코멘트"}}, - "relevance": {{"score": 5, "comment": "평가 코멘트"}}, - "difficulty_appropriateness": {{"score": 5, "comment": "평가 코멘트"}}, - "clarity": {{"score": 5, "comment": "평가 코멘트"}} - }}, - "overall_score": 5.0, - "pass": true, - "improvement_suggestions": ["개선 제안1", "개선 제안2"] -}} -``` - -### Evaluate the QA pair.""" - - -# ============================================================================= -# Helper Functions -# ============================================================================= - -def get_qa_prompt_by_difficulty(difficulty: str) -> str: - """난이도에 따른 프롬프트 반환""" - prompts = { - "IR": IR_QA_PROMPT, - "Analysis": ANALYSIS_QA_PROMPT, - "Compare": COMPARE_QA_PROMPT, - "Aggregation": AGGREGATION_QA_PROMPT, - "Reasoning": REASONING_QA_PROMPT, - "Insight": INSIGHT_QA_PROMPT, - } - return prompts.get(difficulty, IR_QA_PROMPT) - - -def format_tables_for_prompt(tables: dict) -> str: - """테이블 딕셔너리를 프롬프트용 문자열로 변환""" - formatted = [] - for table_id, table_content in tables.items(): - formatted.append(f"### {table_id}\n```markdown\n{table_content}\n```\n") - return "\n".join(formatted) diff --git a/tests/choi/QA_example/qa_generation.ipynb b/tests/choi/QA_example/qa_generation.ipynb deleted file mode 100644 index 34481fa..0000000 --- a/tests/choi/QA_example/qa_generation.ipynb +++ /dev/null @@ -1,1432 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "8386b6ee", - "metadata": {}, - "source": [ - "## 1. 환경 설정" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "8972c3e2", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "프로젝트 루트: /Users/jaehyeokchoi/Desktop/chois_toy/private/TableMagnifier\n", - "현재 작업 디렉토리: /Users/jaehyeokchoi/Desktop/chois_toy/private/TableMagnifier/QA_example\n" - ] - } - ], - "source": [ - "import sys\n", - "import json\n", - "import asyncio\n", - "from pathlib import Path\n", - "from datetime import datetime\n", - "\n", - "# 프로젝트 루트 경로 설정\n", - "project_root = Path.cwd().parent\n", - "if str(project_root) not in sys.path:\n", - " sys.path.insert(0, str(project_root))\n", - "\n", - "print(f\"프로젝트 루트: {project_root}\")\n", - "print(f\"현재 작업 디렉토리: {Path.cwd()}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "ca7782da", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✅ 모듈 임포트 완료 (리로드됨)\n" - ] - } - ], - "source": [ - "# QA Generator 모듈 임포트 (변경 시 리로드)\n", - "import importlib\n", - "import QA_example.qa_generator\n", - "importlib.reload(QA_example.qa_generator)\n", - "\n", - "from QA_example.qa_generator import (\n", - " InsuranceTableQAGenerator,\n", - " QADifficulty,\n", - " QAType,\n", - " generate_qa_from_tables,\n", - ")\n", - "\n", - "# Gemini API Pool 임포트\n", - "from polling_gemini import get_gemini_pool\n", - "\n", - "print(\"✅ 모듈 임포트 완료 (리로드됨)\")" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "68b1f92e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "📊 API Pool 상태:\n", - " 현재 키: key1\n", - " 총 키 수: 3\n" - ] - } - ], - "source": [ - "# API Pool 상태 확인\n", - "pool = get_gemini_pool()\n", - "print(\"📊 API Pool 상태:\")\n", - "print(f\" 현재 키: {pool.get_current_key_info()['name']}\")\n", - "print(f\" 총 키 수: {pool.get_current_key_info()['total_keys']}\")" - ] - }, - { - "cell_type": "markdown", - "id": "c38801d5", - "metadata": {}, - "source": [ - "## 2. 샘플 테이블 데이터 준비\n", - "\n", - "보험 도메인의 다양한 테이블 예시를 준비합니다." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "504b0780", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✅ 샘플 테이블 데이터 준비 완료\n", - " - TABLE_1: 보험료 산출 기초율\n", - " - TABLE_2: 해지환급금 예시\n", - " - TABLE_3: 보장 내역\n", - " - TABLE_4: 연령별 보험료\n" - ] - } - ], - "source": [ - "# 샘플 테이블 1: 보험료 산출 기초율 테이블 (실제 데이터 기반)\n", - "TABLE_1_PREMIUM_CALCULATION = \"\"\"\n", - "|구분|XX세|XX+1세|XX+2세|XX+3세|XX+4세|XX+5세|\n", - "|---|---|---|---|---|---|---|\n", - "|나이증가분(A)||1059|1357|1739|2229|2855|\n", - "|보험료 산출 기초율(위험률 등) 증가분(B=전년도 기준보험료의 최대 25% 가정)||10846|13897|17806|22815|29232|\n", - "|기준보험료(C=전년도 기준보험료+A+B)|42325|54321|69485|89030|114074|146161|\n", - "\"\"\".strip()\n", - "\n", - "# 샘플 테이블 2: 해지환급금 예시표\n", - "TABLE_2_SURRENDER_VALUE = \"\"\"\n", - "|경과기간|납입보험료 누계|해지환급금|환급률|\n", - "|---|---|---|---|\n", - "|1년|600000|0|0%|\n", - "|3년|1800000|540000|30%|\n", - "|5년|3000000|1650000|55%|\n", - "|10년|6000000|4800000|80%|\n", - "|15년|9000000|8550000|95%|\n", - "|20년(만기)|12000000|12000000|100%|\n", - "\"\"\".strip()\n", - "\n", - "# 샘플 테이블 3: 보장 내역표\n", - "TABLE_3_COVERAGE = \"\"\"\n", - "|보장항목|보장내용|지급금액|지급조건|\n", - "|---|---|---|---|\n", - "|사망보험금|일반사망|50000000|피보험자 사망시|\n", - "|사망보험금|재해사망|100000000|재해로 인한 사망시|\n", - "|암진단금|일반암|30000000|암 최초 진단시|\n", - "|암진단금|소액암|6000000|소액암 진단시|\n", - "|암진단금|유사암|3000000|유사암 진단시|\n", - "|입원비|일반입원|50000|1일당 (최대 180일)|\n", - "|입원비|암입원|100000|1일당 (최대 180일)|\n", - "|수술비|일반수술|500000|1회당|\n", - "|수술비|암수술|2000000|1회당|\n", - "\"\"\".strip()\n", - "\n", - "# 샘플 테이블 4: 연령별 보험료 예시\n", - "TABLE_4_AGE_PREMIUM = \"\"\"\n", - "|가입연령|성별|20년납_월보험료|전기납_월보험료|일시납_총보험료|\n", - "|---|---|---|---|---|\n", - "|30세|남|45000|38000|8500000|\n", - "|30세|여|42000|35000|7800000|\n", - "|40세|남|65000|52000|11500000|\n", - "|40세|여|58000|47000|10200000|\n", - "|50세|남|95000|75000|16000000|\n", - "|50세|여|82000|65000|14000000|\n", - "\"\"\".strip()\n", - "\n", - "print(\"✅ 샘플 테이블 데이터 준비 완료\")\n", - "print(f\" - TABLE_1: 보험료 산출 기초율\")\n", - "print(f\" - TABLE_2: 해지환급금 예시\")\n", - "print(f\" - TABLE_3: 보장 내역\")\n", - "print(f\" - TABLE_4: 연령별 보험료\")" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "c22033b5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "============================================================\n", - "📋 table_1_premium_calculation\n", - "============================================================\n" - ] - }, - { - "data": { - "text/markdown": [ - "|구분|XX세|XX+1세|XX+2세|XX+3세|XX+4세|XX+5세|\n", - "|---|---|---|---|---|---|---|\n", - "|나이증가분(A)||1059|1357|1739|2229|2855|\n", - "|보험료 산출 기초율(위험률 등) 증가분(B=전년도 기준보험료의 최대 25% 가정)||10846|13897|17806|22815|29232|\n", - "|기준보험료(C=전년도 기준보험료+A+B)|42325|54321|69485|89030|114074|146161|" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "============================================================\n", - "📋 table_2_surrender_value\n", - "============================================================\n" - ] - }, - { - "data": { - "text/markdown": [ - "|경과기간|납입보험료 누계|해지환급금|환급률|\n", - "|---|---|---|---|\n", - "|1년|600000|0|0%|\n", - "|3년|1800000|540000|30%|\n", - "|5년|3000000|1650000|55%|\n", - "|10년|6000000|4800000|80%|\n", - "|15년|9000000|8550000|95%|\n", - "|20년(만기)|12000000|12000000|100%|" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "============================================================\n", - "📋 table_3_coverage\n", - "============================================================\n" - ] - }, - { - "data": { - "text/markdown": [ - "|보장항목|보장내용|지급금액|지급조건|\n", - "|---|---|---|---|\n", - "|사망보험금|일반사망|50000000|피보험자 사망시|\n", - "|사망보험금|재해사망|100000000|재해로 인한 사망시|\n", - "|암진단금|일반암|30000000|암 최초 진단시|\n", - "|암진단금|소액암|6000000|소액암 진단시|\n", - "|암진단금|유사암|3000000|유사암 진단시|\n", - "|입원비|일반입원|50000|1일당 (최대 180일)|\n", - "|입원비|암입원|100000|1일당 (최대 180일)|\n", - "|수술비|일반수술|500000|1회당|\n", - "|수술비|암수술|2000000|1회당|" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "============================================================\n", - "📋 table_4_age_premium\n", - "============================================================\n" - ] - }, - { - "data": { - "text/markdown": [ - "|가입연령|성별|20년납_월보험료|전기납_월보험료|일시납_총보험료|\n", - "|---|---|---|---|---|\n", - "|30세|남|45000|38000|8500000|\n", - "|30세|여|42000|35000|7800000|\n", - "|40세|남|65000|52000|11500000|\n", - "|40세|여|58000|47000|10200000|\n", - "|50세|남|95000|75000|16000000|\n", - "|50세|여|82000|65000|14000000|" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# 테이블 딕셔너리 구성\n", - "tables = {\n", - " \"table_1_premium_calculation\": TABLE_1_PREMIUM_CALCULATION,\n", - " \"table_2_surrender_value\": TABLE_2_SURRENDER_VALUE,\n", - " \"table_3_coverage\": TABLE_3_COVERAGE,\n", - " \"table_4_age_premium\": TABLE_4_AGE_PREMIUM,\n", - "}\n", - "\n", - "# 테이블 미리보기\n", - "from IPython.display import display, Markdown\n", - "\n", - "for table_id, content in tables.items():\n", - " print(f\"\\n{'='*60}\")\n", - " print(f\"📋 {table_id}\")\n", - " print('='*60)\n", - " display(Markdown(content))" - ] - }, - { - "cell_type": "markdown", - "id": "0480f78c", - "metadata": {}, - "source": [ - "## 3. QA Generator 초기화" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "d4d5e0c6", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✅ QA Generator 초기화 완료\n", - " 사용 모델: gemini-2.0-flash\n" - ] - } - ], - "source": [ - "# QA Generator 인스턴스 생성\n", - "qa_generator = InsuranceTableQAGenerator(\n", - " model_name=\"gemini-2.0-flash\" # 또는 \"gemini-1.5-flash\"\n", - ")\n", - "\n", - "print(\"✅ QA Generator 초기화 완료\")\n", - "print(f\" 사용 모델: gemini-2.0-flash\")" - ] - }, - { - "cell_type": "markdown", - "id": "f21cb567", - "metadata": {}, - "source": [ - "## 4. 난이도별 QA 생성\n", - "\n", - "### 4.1 IR (Information Retrieval) - 단순 정보 검색" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "11267214", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "🔍 IR (Information Retrieval) QA 생성 중...\n", - "\n", - "✅ 3개의 IR QA 생성 완료\n", - "\n", - "📌 [IR_001] IR\n", - " Q: XX세의 기준보험료는 얼마인가요?\n", - " A: 42325\n", - " Evidence: {'table_id': 'table_1_premium_calculation', 'row': '기준보험료(C=전년도 기준보험료+A+B)', 'column': 'XX세'}\n", - "\n", - "📌 [IR_002] IR\n", - " Q: 경과기간이 1년일 때 해지환급금은 얼마인가요?\n", - " A: 0\n", - " Evidence: {'table_id': 'table_2_surrender_value', 'row': '1년', 'column': '해지환급금'}\n", - "\n", - "📌 [IR_003] IR\n", - " Q: 30세 남성의 20년납 월보험료는 얼마인가요?\n", - " A: 45000\n", - " Evidence: {'table_id': 'table_4_age_premium', 'row': '30세 (남)', 'column': '20년납_월보험료'}\n", - "\n", - "✅ 3개의 IR QA 생성 완료\n", - "\n", - "📌 [IR_001] IR\n", - " Q: XX세의 기준보험료는 얼마인가요?\n", - " A: 42325\n", - " Evidence: {'table_id': 'table_1_premium_calculation', 'row': '기준보험료(C=전년도 기준보험료+A+B)', 'column': 'XX세'}\n", - "\n", - "📌 [IR_002] IR\n", - " Q: 경과기간이 1년일 때 해지환급금은 얼마인가요?\n", - " A: 0\n", - " Evidence: {'table_id': 'table_2_surrender_value', 'row': '1년', 'column': '해지환급금'}\n", - "\n", - "📌 [IR_003] IR\n", - " Q: 30세 남성의 20년납 월보험료는 얼마인가요?\n", - " A: 45000\n", - " Evidence: {'table_id': 'table_4_age_premium', 'row': '30세 (남)', 'column': '20년납_월보험료'}\n" - ] - } - ], - "source": [ - "# IR 난이도 QA 생성 (단답형, 특정 셀 검색)\n", - "print(\"🔍 IR (Information Retrieval) QA 생성 중...\")\n", - "\n", - "ir_qa_pairs = qa_generator.generate_qa_by_difficulty(\n", - " tables=tables,\n", - " difficulty=QADifficulty.IR,\n", - " num_questions=3\n", - ")\n", - "\n", - "print(f\"\\n✅ {len(ir_qa_pairs)}개의 IR QA 생성 완료\")\n", - "for qa in ir_qa_pairs:\n", - " print(f\"\\n📌 [{qa.id}] {qa.difficulty}\")\n", - " print(f\" Q: {qa.question}\")\n", - " print(f\" A: {qa.answer}\")\n", - " if qa.evidence:\n", - " print(f\" Evidence: {qa.evidence}\")" - ] - }, - { - "cell_type": "markdown", - "id": "6c4f477c", - "metadata": {}, - "source": [ - "### 4.2 Analysis - 분석적 질문" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "647c157f", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "📊 Analysis QA 생성 중...\n", - "\n", - "✅ 3개의 Analysis QA 생성 완료\n", - "\n", - "📌 [ANALYSIS_001] Analysis\n", - " Q: 암진단금 보장항목 중에서 가장 높은 지급금액을 제공하는 보장내용은 무엇인가요?\n", - " A: 일반암\n", - " Reasoning: table_3_coverage에서 '보장항목'이 '암진단금'인 행들을 확인하고, 그 중 '지급금액'이 가장 높은 '보장내용'을 찾습니다. 일반암은 30,000,000원, 소액암은 6,000,000원, 유사암은 3,000,000원이므로 일반암이 가장 높습니다.\n", - "\n", - "📌 [ANALYSIS_002] Analysis\n", - " Q: 해지환급률이 50% 이상이 되려면 최소 몇 년의 경과기간이 필요합니까?\n", - " A: 5년\n", - " Reasoning: table_2_surrender_value에서 '환급률'이 50% 이상인 행들을 찾고, 그 중 가장 작은 '경과기간'을 확인합니다. 3년 경과 시 환급률은 30%이고, 5년 경과 시 환급률은 55%이므로 최소 5년이 필요합니다.\n", - "\n", - "📌 [ANALYSIS_003] Analysis\n", - " Q: 40세 가입자의 경우, 남성과 여성 중 누가 20년납_월보험료가 더 높은가요?\n", - " A: 남성\n", - " Reasoning: table_4_age_premium에서 '가입연령'이 40세인 행들을 찾고, 해당 행들의 '20년납_월보험료'를 비교합니다. 40세 남성의 20년납_월보험료는 65,000원이고, 40세 여성의 20년납_월보험료는 58,000원이므로 남성이 더 높습니다.\n", - "\n", - "✅ 3개의 Analysis QA 생성 완료\n", - "\n", - "📌 [ANALYSIS_001] Analysis\n", - " Q: 암진단금 보장항목 중에서 가장 높은 지급금액을 제공하는 보장내용은 무엇인가요?\n", - " A: 일반암\n", - " Reasoning: table_3_coverage에서 '보장항목'이 '암진단금'인 행들을 확인하고, 그 중 '지급금액'이 가장 높은 '보장내용'을 찾습니다. 일반암은 30,000,000원, 소액암은 6,000,000원, 유사암은 3,000,000원이므로 일반암이 가장 높습니다.\n", - "\n", - "📌 [ANALYSIS_002] Analysis\n", - " Q: 해지환급률이 50% 이상이 되려면 최소 몇 년의 경과기간이 필요합니까?\n", - " A: 5년\n", - " Reasoning: table_2_surrender_value에서 '환급률'이 50% 이상인 행들을 찾고, 그 중 가장 작은 '경과기간'을 확인합니다. 3년 경과 시 환급률은 30%이고, 5년 경과 시 환급률은 55%이므로 최소 5년이 필요합니다.\n", - "\n", - "📌 [ANALYSIS_003] Analysis\n", - " Q: 40세 가입자의 경우, 남성과 여성 중 누가 20년납_월보험료가 더 높은가요?\n", - " A: 남성\n", - " Reasoning: table_4_age_premium에서 '가입연령'이 40세인 행들을 찾고, 해당 행들의 '20년납_월보험료'를 비교합니다. 40세 남성의 20년납_월보험료는 65,000원이고, 40세 여성의 20년납_월보험료는 58,000원이므로 남성이 더 높습니다.\n" - ] - } - ], - "source": [ - "# Analysis 난이도 QA 생성\n", - "print(\"📊 Analysis QA 생성 중...\")\n", - "\n", - "analysis_qa_pairs = qa_generator.generate_qa_by_difficulty(\n", - " tables=tables,\n", - " difficulty=QADifficulty.ANALYSIS,\n", - " num_questions=3\n", - ")\n", - "\n", - "print(f\"\\n✅ {len(analysis_qa_pairs)}개의 Analysis QA 생성 완료\")\n", - "for qa in analysis_qa_pairs:\n", - " print(f\"\\n📌 [{qa.id}] {qa.difficulty}\")\n", - " print(f\" Q: {qa.question}\")\n", - " print(f\" A: {qa.answer}\")\n", - " if qa.reasoning:\n", - " print(f\" Reasoning: {qa.reasoning}\")" - ] - }, - { - "cell_type": "markdown", - "id": "7c744604", - "metadata": {}, - "source": [ - "### 4.3 Compare (Multi-hop) - 비교 및 다중 추론" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "597806d6", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "⚖️ Compare (Multi-hop) QA 생성 중...\n", - "\n", - "✅ 3개의 Compare QA 생성 완료\n", - "\n", - "📌 [COMPARE_001] Compare\n", - " Q: table_1_premium_calculation에서 XX+1세의 경우, 보험료 산출 기초율 증가분(B)은 나이증가분(A)보다 얼마나 더 큰가요?\n", - " A: 9,787원 더 큽니다.\n", - " Reasoning: XX+1세의 보험료 산출 기초율 증가분(B) 값에서 나이증가분(A) 값을 차감하여 그 차이를 계산합니다.\n", - "\n", - "📌 [COMPARE_002] Compare\n", - " Q: table_2_surrender_value에서 보험 가입 후 5년 경과 시점과 10년 경과 시점의 해지환급금은 얼마나 차이가 나나요?\n", - " A: 3,150,000원 차이가 납니다.\n", - " Reasoning: 10년 경과 시점의 해지환급금에서 5년 경과 시점의 해지환급금을 차감하여 차이를 계산합니다.\n", - "\n", - "📌 [COMPARE_003] Compare\n", - " Q: table_4_age_premium에서 30세 남성이 20년납 월보험료로 가입했을 경우, 20년 만기 시점의 총 납입보험료와 table_2_surrender_value의 20년 경과 시점 해지환급금은 얼마나 차이가 나나요?\n", - " A: 1,200,000원 차이가 납니다.\n", - " Reasoning: table_4에서 30세 남성의 20년납 월보험료를 찾아 20년간의 총 납입보험료를 계산하고, table_2에서 20년 경과 시점의 해지환급금을 찾아 두 값의 차이를 계산합니다.\n", - "\n", - "✅ 3개의 Compare QA 생성 완료\n", - "\n", - "📌 [COMPARE_001] Compare\n", - " Q: table_1_premium_calculation에서 XX+1세의 경우, 보험료 산출 기초율 증가분(B)은 나이증가분(A)보다 얼마나 더 큰가요?\n", - " A: 9,787원 더 큽니다.\n", - " Reasoning: XX+1세의 보험료 산출 기초율 증가분(B) 값에서 나이증가분(A) 값을 차감하여 그 차이를 계산합니다.\n", - "\n", - "📌 [COMPARE_002] Compare\n", - " Q: table_2_surrender_value에서 보험 가입 후 5년 경과 시점과 10년 경과 시점의 해지환급금은 얼마나 차이가 나나요?\n", - " A: 3,150,000원 차이가 납니다.\n", - " Reasoning: 10년 경과 시점의 해지환급금에서 5년 경과 시점의 해지환급금을 차감하여 차이를 계산합니다.\n", - "\n", - "📌 [COMPARE_003] Compare\n", - " Q: table_4_age_premium에서 30세 남성이 20년납 월보험료로 가입했을 경우, 20년 만기 시점의 총 납입보험료와 table_2_surrender_value의 20년 경과 시점 해지환급금은 얼마나 차이가 나나요?\n", - " A: 1,200,000원 차이가 납니다.\n", - " Reasoning: table_4에서 30세 남성의 20년납 월보험료를 찾아 20년간의 총 납입보험료를 계산하고, table_2에서 20년 경과 시점의 해지환급금을 찾아 두 값의 차이를 계산합니다.\n" - ] - } - ], - "source": [ - "# Compare 난이도 QA 생성 (Multi-hop 추론)\n", - "print(\"⚖️ Compare (Multi-hop) QA 생성 중...\")\n", - "\n", - "compare_qa_pairs = qa_generator.generate_qa_by_difficulty(\n", - " tables=tables,\n", - " difficulty=QADifficulty.COMPARE,\n", - " num_questions=3\n", - ")\n", - "\n", - "print(f\"\\n✅ {len(compare_qa_pairs)}개의 Compare QA 생성 완료\")\n", - "for qa in compare_qa_pairs:\n", - " print(f\"\\n📌 [{qa.id}] {qa.difficulty}\")\n", - " print(f\" Q: {qa.question}\")\n", - " print(f\" A: {qa.answer}\")\n", - " if qa.reasoning:\n", - " print(f\" Reasoning: {qa.reasoning}\")" - ] - }, - { - "cell_type": "markdown", - "id": "c91c5b2e", - "metadata": {}, - "source": [ - "### 4.4 Aggregation - 집계 연산" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "c8a555ce", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "➕ Aggregation QA 생성 중...\n", - "\n", - "✅ 3개의 Aggregation QA 생성 완료\n", - "\n", - "📌 [AGG_001] Aggregation\n", - " Q: table_1_premium_calculation에서 XX+1세부터 XX+5세까지 '나이증가분(A)'의 총합은 얼마인가요?\n", - " A: 9239\n", - " Python 검증 코드 포함: ✅\n", - "\n", - "📌 [AGG_002] Aggregation\n", - " Q: table_2_surrender_value에서 경과기간 1년, 3년, 5년, 10년, 15년, 20년 시점의 '해지환급금'의 평균은 얼마인가요?\n", - " A: 4590000\n", - " Python 검증 코드 포함: ✅\n", - "\n", - "📌 [AGG_003] Aggregation\n", - " Q: table_4_age_premium에서 30세 남성이 20년납으로 가입했을 경우, 20년 동안 총 납입해야 할 보험료는 얼마인가요?\n", - " A: 10800000\n", - " Python 검증 코드 포함: ✅\n", - "\n", - "✅ 3개의 Aggregation QA 생성 완료\n", - "\n", - "📌 [AGG_001] Aggregation\n", - " Q: table_1_premium_calculation에서 XX+1세부터 XX+5세까지 '나이증가분(A)'의 총합은 얼마인가요?\n", - " A: 9239\n", - " Python 검증 코드 포함: ✅\n", - "\n", - "📌 [AGG_002] Aggregation\n", - " Q: table_2_surrender_value에서 경과기간 1년, 3년, 5년, 10년, 15년, 20년 시점의 '해지환급금'의 평균은 얼마인가요?\n", - " A: 4590000\n", - " Python 검증 코드 포함: ✅\n", - "\n", - "📌 [AGG_003] Aggregation\n", - " Q: table_4_age_premium에서 30세 남성이 20년납으로 가입했을 경우, 20년 동안 총 납입해야 할 보험료는 얼마인가요?\n", - " A: 10800000\n", - " Python 검증 코드 포함: ✅\n" - ] - } - ], - "source": [ - "# Aggregation 난이도 QA 생성 (수치 계산)\n", - "print(\"➕ Aggregation QA 생성 중...\")\n", - "\n", - "agg_qa_pairs = qa_generator.generate_qa_by_difficulty(\n", - " tables=tables,\n", - " difficulty=QADifficulty.AGGREGATION,\n", - " num_questions=3\n", - ")\n", - "\n", - "print(f\"\\n✅ {len(agg_qa_pairs)}개의 Aggregation QA 생성 완료\")\n", - "for qa in agg_qa_pairs:\n", - " print(f\"\\n📌 [{qa.id}] {qa.difficulty}\")\n", - " print(f\" Q: {qa.question}\")\n", - " print(f\" A: {qa.answer}\")\n", - " if qa.python_verification:\n", - " print(f\" Python 검증 코드 포함: ✅\")" - ] - }, - { - "cell_type": "markdown", - "id": "05f6c85e", - "metadata": {}, - "source": [ - "### 4.5 Reasoning - 복합 추론" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "0a3ce609", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "🧠 Reasoning QA 생성 중...\n", - "\n", - "✅ 3개의 Reasoning QA 생성 완료\n", - "\n", - "📌 [REASON_001] Reasoning\n", - " Q: 30세 남성이 20년납 월보험료로 가입했을 때, `table_1`의 '기준보험료' 증가율이 '월보험료'에 동일하게 적용된다고 가정하면, 가입 후 3년이 지난 시점(즉, 33세)의 월보험료는 얼마가 될까요?\n", - " A: 약 94,658원\n", - " Chain of Thought:\n", - " Step 1: Step 1: `table_4`에서 30세 남성의 20년납 월보험료를 확인합니다: 45,000원.\n", - " Step 2: Step 2: `table_1`에서 'XX세'를 30세로 가정하고, 'XX세' (30세)의 기준보험료(C)는 42,325원, 'XX+3세' (33세)의 기준보험료(C)는 89,030원임을 확인합니다.\n", - " Step 3: Step 3: 30세 대비 33세의 기준보험료 증가율을 계산합니다: (89,030 / 42,325).\n", - " Step 4: Step 4: 이 증가율을 30세 남성의 초기 월보험료에 적용하여 33세의 월보험료를 계산합니다: 45,000원 * (89,030 / 42,325) = 94,657.76...\n", - " Step 5: Step 5: 계산된 값을 반올림하여 최종 월보험료를 도출합니다.\n", - "\n", - "📌 [REASON_002] Reasoning\n", - " Q: 3년 동안 보험료를 납입한 계약자가 일반암 진단을 받고, 진단금을 수령한 직후 보험을 해지한다면, 이 계약자가 총 얼마의 금액을 받게 될까요? (단, 납입보험료는 해지환급금 계산에만 영향을 미치며, 진단금 수령 시점까지의 납입보험료는 해지환급금 누계에 포함된다고 가정합니다.)\n", - " A: 30,540,000원\n", - " Chain of Thought:\n", - " Step 1: Step 1: `table_3`에서 '일반암' 진단금을 확인합니다: 30,000,000원.\n", - " Step 2: Step 2: `table_2`에서 '경과기간' 3년 시점의 '해지환급금'을 확인합니다: 540,000원.\n", - " Step 3: Step 3: 계약자가 받게 될 총 금액은 '일반암 진단금'과 '해지환급금'의 합계입니다.\n", - " Step 4: Step 4: 총 수령액 = 30,000,000원 + 540,000원 = 30,540,000원.\n", - "\n", - "📌 [REASON_003] Reasoning\n", - " Q: 40세 남성과 40세 여성이 각각 20년납으로 보험에 가입했다고 가정합니다. 두 사람 모두 가입 후 5년이 지난 시점에 일반암 진단을 받고, 10일간 암으로 입원했으며, 1회의 암수술을 받았을 경우, 다음 두 가지 질문에 답하세요.\n", - "1. 두 사람이 5년간 납입한 총 보험료의 차이는 얼마입니까?\n", - "2. 두 사람이 받게 되는 총 보장금액의 차이는 얼마입니까?\n", - " A: 1. 5년간 납입 총 보험료 차이: 420,000원\n", - "2. 총 보장금액 차이: 0원\n", - " Chain of Thought:\n", - " Step 1: Step 1: `table_4`에서 40세 남성과 40세 여성의 20년납 월보험료를 확인합니다.\n", - " Step 2: - 40세 남성: 65,000원\n", - " Step 3: - 40세 여성: 58,000원\n", - " Step 4: Step 2: 각 성별로 5년간 납입할 총 보험료를 계산합니다 (월보험료 * 12개월 * 5년).\n", - " Step 5: - 남성 총 납입 보험료: 65,000원 * 12 * 5 = 3,900,000원\n", - " Step 6: - 여성 총 납입 보험료: 58,000원 * 12 * 5 = 3,480,000원\n", - " Step 7: Step 3: 두 사람의 5년간 납입 총 보험료 차이를 계산합니다: 3,900,000원 - 3,480,000원 = 420,000원.\n", - " Step 8: Step 4: `table_3`에서 일반암 진단금, 암입원비 (10일), 암수술비 (1회)를 확인합니다.\n", - " Step 9: - 일반암 진단금: 30,000,000원\n", - " Step 10: - 암입원비: 100,000원 (1일당) * 10일 = 1,000,000원\n", - " Step 11: - 암수술비: 2,000,000원 (1회당)\n", - " Step 12: Step 5: 각 사람이 받게 되는 총 보장금액을 계산합니다 (성별에 따른 보장금액 차이는 `table_3`에 명시되어 있지 않으므로 동일하다고 가정).\n", - " Step 13: - 총 보장금액 = 30,000,000원 + 1,000,000원 + 2,000,000원 = 33,000,000원.\n", - " Step 14: Step 6: 두 사람의 총 보장금액 차이를 계산합니다: 33,000,000원 - 33,000,000원 = 0원.\n", - "\n", - "✅ 3개의 Reasoning QA 생성 완료\n", - "\n", - "📌 [REASON_001] Reasoning\n", - " Q: 30세 남성이 20년납 월보험료로 가입했을 때, `table_1`의 '기준보험료' 증가율이 '월보험료'에 동일하게 적용된다고 가정하면, 가입 후 3년이 지난 시점(즉, 33세)의 월보험료는 얼마가 될까요?\n", - " A: 약 94,658원\n", - " Chain of Thought:\n", - " Step 1: Step 1: `table_4`에서 30세 남성의 20년납 월보험료를 확인합니다: 45,000원.\n", - " Step 2: Step 2: `table_1`에서 'XX세'를 30세로 가정하고, 'XX세' (30세)의 기준보험료(C)는 42,325원, 'XX+3세' (33세)의 기준보험료(C)는 89,030원임을 확인합니다.\n", - " Step 3: Step 3: 30세 대비 33세의 기준보험료 증가율을 계산합니다: (89,030 / 42,325).\n", - " Step 4: Step 4: 이 증가율을 30세 남성의 초기 월보험료에 적용하여 33세의 월보험료를 계산합니다: 45,000원 * (89,030 / 42,325) = 94,657.76...\n", - " Step 5: Step 5: 계산된 값을 반올림하여 최종 월보험료를 도출합니다.\n", - "\n", - "📌 [REASON_002] Reasoning\n", - " Q: 3년 동안 보험료를 납입한 계약자가 일반암 진단을 받고, 진단금을 수령한 직후 보험을 해지한다면, 이 계약자가 총 얼마의 금액을 받게 될까요? (단, 납입보험료는 해지환급금 계산에만 영향을 미치며, 진단금 수령 시점까지의 납입보험료는 해지환급금 누계에 포함된다고 가정합니다.)\n", - " A: 30,540,000원\n", - " Chain of Thought:\n", - " Step 1: Step 1: `table_3`에서 '일반암' 진단금을 확인합니다: 30,000,000원.\n", - " Step 2: Step 2: `table_2`에서 '경과기간' 3년 시점의 '해지환급금'을 확인합니다: 540,000원.\n", - " Step 3: Step 3: 계약자가 받게 될 총 금액은 '일반암 진단금'과 '해지환급금'의 합계입니다.\n", - " Step 4: Step 4: 총 수령액 = 30,000,000원 + 540,000원 = 30,540,000원.\n", - "\n", - "📌 [REASON_003] Reasoning\n", - " Q: 40세 남성과 40세 여성이 각각 20년납으로 보험에 가입했다고 가정합니다. 두 사람 모두 가입 후 5년이 지난 시점에 일반암 진단을 받고, 10일간 암으로 입원했으며, 1회의 암수술을 받았을 경우, 다음 두 가지 질문에 답하세요.\n", - "1. 두 사람이 5년간 납입한 총 보험료의 차이는 얼마입니까?\n", - "2. 두 사람이 받게 되는 총 보장금액의 차이는 얼마입니까?\n", - " A: 1. 5년간 납입 총 보험료 차이: 420,000원\n", - "2. 총 보장금액 차이: 0원\n", - " Chain of Thought:\n", - " Step 1: Step 1: `table_4`에서 40세 남성과 40세 여성의 20년납 월보험료를 확인합니다.\n", - " Step 2: - 40세 남성: 65,000원\n", - " Step 3: - 40세 여성: 58,000원\n", - " Step 4: Step 2: 각 성별로 5년간 납입할 총 보험료를 계산합니다 (월보험료 * 12개월 * 5년).\n", - " Step 5: - 남성 총 납입 보험료: 65,000원 * 12 * 5 = 3,900,000원\n", - " Step 6: - 여성 총 납입 보험료: 58,000원 * 12 * 5 = 3,480,000원\n", - " Step 7: Step 3: 두 사람의 5년간 납입 총 보험료 차이를 계산합니다: 3,900,000원 - 3,480,000원 = 420,000원.\n", - " Step 8: Step 4: `table_3`에서 일반암 진단금, 암입원비 (10일), 암수술비 (1회)를 확인합니다.\n", - " Step 9: - 일반암 진단금: 30,000,000원\n", - " Step 10: - 암입원비: 100,000원 (1일당) * 10일 = 1,000,000원\n", - " Step 11: - 암수술비: 2,000,000원 (1회당)\n", - " Step 12: Step 5: 각 사람이 받게 되는 총 보장금액을 계산합니다 (성별에 따른 보장금액 차이는 `table_3`에 명시되어 있지 않으므로 동일하다고 가정).\n", - " Step 13: - 총 보장금액 = 30,000,000원 + 1,000,000원 + 2,000,000원 = 33,000,000원.\n", - " Step 14: Step 6: 두 사람의 총 보장금액 차이를 계산합니다: 33,000,000원 - 33,000,000원 = 0원.\n" - ] - } - ], - "source": [ - "# Reasoning 난이도 QA 생성 (Chain-of-Thought)\n", - "print(\"🧠 Reasoning QA 생성 중...\")\n", - "\n", - "reasoning_qa_pairs = qa_generator.generate_qa_by_difficulty(\n", - " tables=tables,\n", - " difficulty=QADifficulty.REASONING,\n", - " num_questions=3\n", - ")\n", - "\n", - "print(f\"\\n✅ {len(reasoning_qa_pairs)}개의 Reasoning QA 생성 완료\")\n", - "for qa in reasoning_qa_pairs:\n", - " print(f\"\\n📌 [{qa.id}] {qa.difficulty}\")\n", - " print(f\" Q: {qa.question}\")\n", - " print(f\" A: {qa.answer}\")\n", - " if qa.chain_of_thought:\n", - " print(f\" Chain of Thought:\")\n", - " for i, step in enumerate(qa.chain_of_thought, 1):\n", - " print(f\" Step {i}: {step}\")" - ] - }, - { - "cell_type": "markdown", - "id": "8c33297d", - "metadata": {}, - "source": [ - "### 4.6 Insight - 통찰 도출" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "72b940c2", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "💡 Insight QA 생성 중...\n", - "\n", - "✅ 2개의 Insight QA 생성 완료\n", - "\n", - "📌 [INSIGHT_001] Insight\n", - " Q: 이 보험 상품의 해지환급금 데이터를 분석했을 때, 조기 해지가 고객에게 미치는 재정적 영향은 무엇이며, 이는 상품 설계 철학에 대해 어떤 시사점을 제공합니까?\n", - " A: 이 보험 상품을 조기에 해지할 경우, 고객은 상당한 재정적 손실을 입게 됩니다. 예를 들어, 1년 경과 후 해지 시 납입보험료 누계의 0%만 환급되며, 3년 후에도 30%만 돌려받습니다. 납입한 원금(환급률 100%)을 온전히 돌려받으려면 20년 만기까지 유지해야 합니다. 이는 이 상품이 장기적인 보장과 유지를 전제로 설계되었으며, 단기 해지에 대해 높은 ...\n", - "\n", - "📌 [INSIGHT_002] Insight\n", - " Q: 보험료 산출 구성 요소와 연령 및 성별에 따른 월 보험료 데이터를 종합적으로 고려할 때, 이 보험 상품이 위험을 평가하고 보험료를 책정하는 방식에 대해 어떤 통찰을 얻을 수 있으며, 특히 연령이 증가함에 따라 위험 평가가 어떻게 변화하는 것으로 보입니까?\n", - " A: 이 보험 상품은 연령이 증가함에 따라 위험 관련 요소를 보험료 산출에 매우 중요하게 반영하고 있습니다. `table_1`에서 '나이증가분(A)'보다 '보험료 산출 기초율(위험률 등) 증가분(B)'이 훨씬 더 큰 폭으로 보험료를 상승시키는 주요 요인임을 알 수 있습니다. 이는 연령이 높아질수록 질병 발생률이나 사고 위험 등 보험사가 인지하는 위험도가 급격히 ...\n", - "\n", - "✅ 2개의 Insight QA 생성 완료\n", - "\n", - "📌 [INSIGHT_001] Insight\n", - " Q: 이 보험 상품의 해지환급금 데이터를 분석했을 때, 조기 해지가 고객에게 미치는 재정적 영향은 무엇이며, 이는 상품 설계 철학에 대해 어떤 시사점을 제공합니까?\n", - " A: 이 보험 상품을 조기에 해지할 경우, 고객은 상당한 재정적 손실을 입게 됩니다. 예를 들어, 1년 경과 후 해지 시 납입보험료 누계의 0%만 환급되며, 3년 후에도 30%만 돌려받습니다. 납입한 원금(환급률 100%)을 온전히 돌려받으려면 20년 만기까지 유지해야 합니다. 이는 이 상품이 장기적인 보장과 유지를 전제로 설계되었으며, 단기 해지에 대해 높은 ...\n", - "\n", - "📌 [INSIGHT_002] Insight\n", - " Q: 보험료 산출 구성 요소와 연령 및 성별에 따른 월 보험료 데이터를 종합적으로 고려할 때, 이 보험 상품이 위험을 평가하고 보험료를 책정하는 방식에 대해 어떤 통찰을 얻을 수 있으며, 특히 연령이 증가함에 따라 위험 평가가 어떻게 변화하는 것으로 보입니까?\n", - " A: 이 보험 상품은 연령이 증가함에 따라 위험 관련 요소를 보험료 산출에 매우 중요하게 반영하고 있습니다. `table_1`에서 '나이증가분(A)'보다 '보험료 산출 기초율(위험률 등) 증가분(B)'이 훨씬 더 큰 폭으로 보험료를 상승시키는 주요 요인임을 알 수 있습니다. 이는 연령이 높아질수록 질병 발생률이나 사고 위험 등 보험사가 인지하는 위험도가 급격히 ...\n" - ] - } - ], - "source": [ - "# Insight 난이도 QA 생성 (서술형)\n", - "print(\"💡 Insight QA 생성 중...\")\n", - "\n", - "insight_qa_pairs = qa_generator.generate_qa_by_difficulty(\n", - " tables=tables,\n", - " difficulty=QADifficulty.INSIGHT,\n", - " num_questions=2\n", - ")\n", - "\n", - "print(f\"\\n✅ {len(insight_qa_pairs)}개의 Insight QA 생성 완료\")\n", - "for qa in insight_qa_pairs:\n", - " print(f\"\\n📌 [{qa.id}] {qa.difficulty}\")\n", - " print(f\" Q: {qa.question}\")\n", - " print(f\" A: {qa.answer[:200]}...\" if len(qa.answer) > 200 else f\" A: {qa.answer}\")" - ] - }, - { - "cell_type": "markdown", - "id": "15f40c4d", - "metadata": {}, - "source": [ - "## 5. Multi-Table QA 생성\n", - "\n", - "복수의 테이블을 참조해야 답변 가능한 질문을 생성합니다." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "2dbbbad2", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "🔗 Multi-Table QA 생성 중...\n", - "\n", - "✅ 3개의 Multi-Table QA 생성 완료\n", - "\n", - "📌 [MULTI_001] Reasoning\n", - " Q: table_2에서 10년 경과 시점의 납입보험료 누계가 6,000,000원일 때, 만약 이 보험이 20년납 상품이었다면 월 평균 보험료는 얼마였을까요? 그리고 이 월 평균 보험료는 table_4의 30세 남성 20년납 월보험료(45,000원)와 비교했을 때 얼마나 차이가 나나요?\n", - " A: table_2의 10년 경과 시점 납입보험료 누계(6,000,000원)를 기준으로 20년납 상품의 월 평균 보험료를 계산하면 50,000원입니다. 이는 table_4의 30세 남성 20년납 월보험료(45,000원)보다 5,000원 더 높은 금액입니다.\n", - "\n", - "📌 [MULTI_002] Calculation\n", - " Q: table_4에서 40세 여성이 전기납 월보험료 상품에 가입했다고 할 때, 이 보험료가 table_1의 'XX세' 기준보험료(C)에 해당한다고 가정합니다. 이 경우, 'XX+2세' 시점의 기준보험료는 얼마로 예상할 수 있을까요? (단, table_1의 나이증가분(A)와 보험료 산출 기초율 증가분(B)는 table_1의 해당 값을 따릅니다.)\n", - " A: table_4의 40세 여성 전기납 월보험료 47,000원을 'XX세' 기준보험료로 가정하면, 'XX+1세' 시점의 기준보험료는 47,000 + 1,059 + 10,846 = 58,905원입니다. 이어서 'XX+2세' 시점의 기준보험료는 58,905 + 1,357 + 13,897 = 74,159원입니다.\n", - "\n", - "📌 [MULTI_003] Reasoning\n", - " Q: 40세 남성이 20년납 월보험료 상품에 가입하여 5년 동안 보험료를 납입했을 경우, 총 납입한 보험료는 얼마인가요? 만약 이 시점에 일반암 진단을 받았다면, 총 납입 보험료 대비 암진단금은 몇 배에 해당하나요? 또한, 만약 이 보험의 해지환급금이 table_2의 '5년 경과' 시점의 납입보험료 누계(3,000,000원)에 해당하는 해지환급금과 동일하다고 가정할 때, 암진단금은 해지환급금보다 얼마나 더 많은 금액인가요?\n", - " A: 40세 남성이 5년 동안 납입한 총 보험료는 3,900,000원입니다. 이 시점에 일반암 진단을 받았다면, 총 납입 보험료 대비 암진단금(30,000,000원)은 약 7.69배에 해당합니다. 또한, table_2의 5년 경과 시점 해지환급금(1,650,000원)과 비교했을 때, 암진단금은 해지환급금보다 28,350,000원 더 많은 금액입니다.\n", - "\n", - "✅ 3개의 Multi-Table QA 생성 완료\n", - "\n", - "📌 [MULTI_001] Reasoning\n", - " Q: table_2에서 10년 경과 시점의 납입보험료 누계가 6,000,000원일 때, 만약 이 보험이 20년납 상품이었다면 월 평균 보험료는 얼마였을까요? 그리고 이 월 평균 보험료는 table_4의 30세 남성 20년납 월보험료(45,000원)와 비교했을 때 얼마나 차이가 나나요?\n", - " A: table_2의 10년 경과 시점 납입보험료 누계(6,000,000원)를 기준으로 20년납 상품의 월 평균 보험료를 계산하면 50,000원입니다. 이는 table_4의 30세 남성 20년납 월보험료(45,000원)보다 5,000원 더 높은 금액입니다.\n", - "\n", - "📌 [MULTI_002] Calculation\n", - " Q: table_4에서 40세 여성이 전기납 월보험료 상품에 가입했다고 할 때, 이 보험료가 table_1의 'XX세' 기준보험료(C)에 해당한다고 가정합니다. 이 경우, 'XX+2세' 시점의 기준보험료는 얼마로 예상할 수 있을까요? (단, table_1의 나이증가분(A)와 보험료 산출 기초율 증가분(B)는 table_1의 해당 값을 따릅니다.)\n", - " A: table_4의 40세 여성 전기납 월보험료 47,000원을 'XX세' 기준보험료로 가정하면, 'XX+1세' 시점의 기준보험료는 47,000 + 1,059 + 10,846 = 58,905원입니다. 이어서 'XX+2세' 시점의 기준보험료는 58,905 + 1,357 + 13,897 = 74,159원입니다.\n", - "\n", - "📌 [MULTI_003] Reasoning\n", - " Q: 40세 남성이 20년납 월보험료 상품에 가입하여 5년 동안 보험료를 납입했을 경우, 총 납입한 보험료는 얼마인가요? 만약 이 시점에 일반암 진단을 받았다면, 총 납입 보험료 대비 암진단금은 몇 배에 해당하나요? 또한, 만약 이 보험의 해지환급금이 table_2의 '5년 경과' 시점의 납입보험료 누계(3,000,000원)에 해당하는 해지환급금과 동일하다고 가정할 때, 암진단금은 해지환급금보다 얼마나 더 많은 금액인가요?\n", - " A: 40세 남성이 5년 동안 납입한 총 보험료는 3,900,000원입니다. 이 시점에 일반암 진단을 받았다면, 총 납입 보험료 대비 암진단금(30,000,000원)은 약 7.69배에 해당합니다. 또한, table_2의 5년 경과 시점 해지환급금(1,650,000원)과 비교했을 때, 암진단금은 해지환급금보다 28,350,000원 더 많은 금액입니다.\n" - ] - } - ], - "source": [ - "# Multi-Table QA 생성\n", - "print(\"🔗 Multi-Table QA 생성 중...\")\n", - "\n", - "multi_table_qa = qa_generator.generate_multi_table_qa(\n", - " tables=tables,\n", - " num_questions=3\n", - ")\n", - "\n", - "print(f\"\\n✅ {len(multi_table_qa)}개의 Multi-Table QA 생성 완료\")\n", - "for qa in multi_table_qa:\n", - " print(f\"\\n📌 [{qa.id}] {qa.difficulty}\")\n", - " print(f\" Q: {qa.question}\")\n", - " print(f\" A: {qa.answer}\")\n", - " if qa.evidence and 'required_tables' in str(qa.evidence):\n", - " print(f\" 참조 테이블: {qa.evidence}\")" - ] - }, - { - "cell_type": "markdown", - "id": "cc4df731", - "metadata": {}, - "source": [ - "## 6. 꼬리 질문 (Follow-up) 생성\n", - "\n", - "특정 QA에 대해 연속적인 후속 질문 체인을 생성합니다." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "d2c2b8a1", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "🔄 Follow-up QA 생성 중...\n", - "\n", - "원본 QA:\n", - " Q: XX세의 기준보험료는 얼마인가요?\n", - " A: 42325\n", - "\n", - "✅ 3개의 Follow-up QA 생성 완료\n", - "\n", - " 🔹 Follow-up 1:\n", - " Q: XX세에서 XX+1세로 나이가 증가할 때, 기준보험료는 얼마나 증가하나요?\n", - " A: XX세의 기준보험료는 42325원이고, XX+1세의 기준보험료는 54321원이므로, 54321 - 42325 = 11996원 증가합니다.\n", - "\n", - " 🔹 Follow-up 2:\n", - " Q: 이 11996원의 기준보험료 증가분은 주로 '나이증가분(A)'과 '보험료 산출 기초율(위험률 등) 증가분(B)' 중 어떤 요인에 의해 발생한 것인가요?\n", - " A: XX+1세의 '나이증가분(A)'은 1059원이고, '보험료 산출 기초율(위험률 등) 증가분(B)'은 10846원입니다. 두 요인 중 '보험료 산출 기초율(위험률 등) 증가분(B)'이 훨씬 크므로, 주로 이 요인에 의해 기준보험료가 증가했습니다.\n", - "\n", - " 🔹 Follow-up 3:\n", - " Q: '보험료 산출 기초율(위험률 등) 증가분(B)'이 '전년도 기준보험료의 최대 25% 가정'이라는 설명에 따르면, XX+1세의 B값(10846원)은 XX세의 기준보험료(42325원)의 25%를 초과하나요?\n", - " A: XX세의 기준보험료 42325원의 25%는 42325 * 0.25 = 10581.25원입니다. XX+1세의 '보험료 산출 기초율 증가분(B)'은 10846원이므로, 10581.25원을 초과합니다.\n", - "\n", - "✅ 3개의 Follow-up QA 생성 완료\n", - "\n", - " 🔹 Follow-up 1:\n", - " Q: XX세에서 XX+1세로 나이가 증가할 때, 기준보험료는 얼마나 증가하나요?\n", - " A: XX세의 기준보험료는 42325원이고, XX+1세의 기준보험료는 54321원이므로, 54321 - 42325 = 11996원 증가합니다.\n", - "\n", - " 🔹 Follow-up 2:\n", - " Q: 이 11996원의 기준보험료 증가분은 주로 '나이증가분(A)'과 '보험료 산출 기초율(위험률 등) 증가분(B)' 중 어떤 요인에 의해 발생한 것인가요?\n", - " A: XX+1세의 '나이증가분(A)'은 1059원이고, '보험료 산출 기초율(위험률 등) 증가분(B)'은 10846원입니다. 두 요인 중 '보험료 산출 기초율(위험률 등) 증가분(B)'이 훨씬 크므로, 주로 이 요인에 의해 기준보험료가 증가했습니다.\n", - "\n", - " 🔹 Follow-up 3:\n", - " Q: '보험료 산출 기초율(위험률 등) 증가분(B)'이 '전년도 기준보험료의 최대 25% 가정'이라는 설명에 따르면, XX+1세의 B값(10846원)은 XX세의 기준보험료(42325원)의 25%를 초과하나요?\n", - " A: XX세의 기준보험료 42325원의 25%는 42325 * 0.25 = 10581.25원입니다. XX+1세의 '보험료 산출 기초율 증가분(B)'은 10846원이므로, 10581.25원을 초과합니다.\n" - ] - } - ], - "source": [ - "# 꼬리 질문 생성 (IR QA 기반)\n", - "if ir_qa_pairs:\n", - " print(\"🔄 Follow-up QA 생성 중...\")\n", - " \n", - " original_qa = ir_qa_pairs[0]\n", - " print(f\"\\n원본 QA:\")\n", - " print(f\" Q: {original_qa.question}\")\n", - " print(f\" A: {original_qa.answer}\")\n", - " \n", - " followup_result = qa_generator.generate_followup_qa(\n", - " tables=tables,\n", - " original_qa=original_qa\n", - " )\n", - " \n", - " if followup_result and 'followup_chain' in followup_result:\n", - " print(f\"\\n✅ {len(followup_result['followup_chain'])}개의 Follow-up QA 생성 완료\")\n", - " for i, followup in enumerate(followup_result['followup_chain'], 1):\n", - " print(f\"\\n 🔹 Follow-up {i}:\")\n", - " print(f\" Q: {followup.get('question', 'N/A')}\")\n", - " print(f\" A: {followup.get('answer', 'N/A')}\")\n", - " else:\n", - " print(f\"\\n결과: {followup_result}\")" - ] - }, - { - "cell_type": "markdown", - "id": "c1b07346", - "metadata": {}, - "source": [ - "## 7. Evol-Instruct: 질문 난이도 진화\n", - "\n", - "기본 질문을 더 복잡한 질문으로 진화시킵니다." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "1ea8a3b0", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "🧬 Evol-Instruct 적용 중...\n", - "\n", - "원본 질문: 40세 남성의 20년납 월보험료는 얼마인가?\n", - "\n", - "✅ 진화 완료\n", - " 진화 전략: 제약 조건 추가 (Adding Constraints), 심층 추론 (Deepening Reasoning), 입력 복잡도 증가 (Complicating Input)\n", - " 진화된 질문: 40세 남성이 20년납으로 보험에 가입할 경우 월보험료는 65,000원이다. 만약 이 남성이 10년 뒤인 50세에 동일한 조건으로 가입한다면 월보험료는 얼마로 예상되는가? 그리고 table_1_premium_calculation 데이터를 참조하여 나이가 증가함에 따라 보험료가 상승하는 주요 원인 두 가지를 설명하시오.\n", - " 새로운 난이도: Reasoning (Level 5)\n", - " 답변: 50세에 동일한 조건으로 가입한다면 월보험료는 95,000원으로 예상됩니다. 보험료가 나이가 증가함에 따라 상승하는 주요 원인은 '나이증가분(A)'과 '보험료 산출 기초율(위험률 등) 증가분(B)'입니다. 이 두 가지 요소가 전년도 기준보험료에 더해져 새로운 기준보험료(C)를 형성하기 때문입니다.\n", - "\n", - "✅ 진화 완료\n", - " 진화 전략: 제약 조건 추가 (Adding Constraints), 심층 추론 (Deepening Reasoning), 입력 복잡도 증가 (Complicating Input)\n", - " 진화된 질문: 40세 남성이 20년납으로 보험에 가입할 경우 월보험료는 65,000원이다. 만약 이 남성이 10년 뒤인 50세에 동일한 조건으로 가입한다면 월보험료는 얼마로 예상되는가? 그리고 table_1_premium_calculation 데이터를 참조하여 나이가 증가함에 따라 보험료가 상승하는 주요 원인 두 가지를 설명하시오.\n", - " 새로운 난이도: Reasoning (Level 5)\n", - " 답변: 50세에 동일한 조건으로 가입한다면 월보험료는 95,000원으로 예상됩니다. 보험료가 나이가 증가함에 따라 상승하는 주요 원인은 '나이증가분(A)'과 '보험료 산출 기초율(위험률 등) 증가분(B)'입니다. 이 두 가지 요소가 전년도 기준보험료에 더해져 새로운 기준보험료(C)를 형성하기 때문입니다.\n" - ] - } - ], - "source": [ - "# Evol-Instruct 적용\n", - "print(\"🧬 Evol-Instruct 적용 중...\")\n", - "\n", - "# 간단한 질문에서 시작\n", - "simple_question = \"40세 남성의 20년납 월보험료는 얼마인가?\"\n", - "print(f\"\\n원본 질문: {simple_question}\")\n", - "\n", - "evolved_result = qa_generator.evolve_question(\n", - " tables=tables,\n", - " original_question=simple_question\n", - ")\n", - "\n", - "if evolved_result and 'evolved' in evolved_result:\n", - " evolved = evolved_result['evolved']\n", - " print(f\"\\n✅ 진화 완료\")\n", - " print(f\" 진화 전략: {evolved.get('evolution_strategy', 'N/A')}\")\n", - " print(f\" 진화된 질문: {evolved.get('question', 'N/A')}\")\n", - " print(f\" 새로운 난이도: {evolved.get('difficulty', 'N/A')}\")\n", - " print(f\" 답변: {evolved.get('answer', 'N/A')}\")\n", - "else:\n", - " print(f\"\\n결과: {evolved_result}\")" - ] - }, - { - "cell_type": "markdown", - "id": "4ca79e89", - "metadata": {}, - "source": [ - "## 8. LLM-as-Judge: 품질 평가" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "287c2e34", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "⚖️ QA 품질 평가 중...\n", - "\n", - "평가 대상 QA:\n", - " Q: XX세의 기준보험료는 얼마인가요?\n", - " A: 42325\n", - "\n", - "✅ 평가 완료\n", - " 📊 Overall Score: 5.0/5.0\n", - " ✓ Pass: 예\n", - "\n", - " 세부 점수:\n", - " - 정확성: 5\n", - " - 충실성: 5\n", - " - 관련성: 5\n", - " - 난이도 적절성: 5\n", - " - 명확성: 5\n", - "\n", - "✅ 평가 완료\n", - " 📊 Overall Score: 5.0/5.0\n", - " ✓ Pass: 예\n", - "\n", - " 세부 점수:\n", - " - 정확성: 5\n", - " - 충실성: 5\n", - " - 관련성: 5\n", - " - 난이도 적절성: 5\n", - " - 명확성: 5\n" - ] - } - ], - "source": [ - "# QA 품질 평가\n", - "if ir_qa_pairs:\n", - " print(\"⚖️ QA 품질 평가 중...\")\n", - " \n", - " qa_to_evaluate = ir_qa_pairs[0]\n", - " print(f\"\\n평가 대상 QA:\")\n", - " print(f\" Q: {qa_to_evaluate.question}\")\n", - " print(f\" A: {qa_to_evaluate.answer}\")\n", - " \n", - " evaluation = qa_generator.evaluate_qa(\n", - " tables=tables,\n", - " qa_pair=qa_to_evaluate\n", - " )\n", - " \n", - " if evaluation:\n", - " print(f\"\\n✅ 평가 완료\")\n", - " print(f\" 📊 Overall Score: {evaluation.overall_score}/5.0\")\n", - " print(f\" ✓ Pass: {'예' if evaluation.passed else '아니오'}\")\n", - " print(f\"\\n 세부 점수:\")\n", - " print(f\" - 정확성: {evaluation.correctness.get('score', 'N/A')}\")\n", - " print(f\" - 충실성: {evaluation.faithfulness.get('score', 'N/A')}\")\n", - " print(f\" - 관련성: {evaluation.relevance.get('score', 'N/A')}\")\n", - " print(f\" - 난이도 적절성: {evaluation.difficulty_appropriateness.get('score', 'N/A')}\")\n", - " print(f\" - 명확성: {evaluation.clarity.get('score', 'N/A')}\")\n", - " \n", - " if evaluation.improvement_suggestions:\n", - " print(f\"\\n 💡 개선 제안:\")\n", - " for suggestion in evaluation.improvement_suggestions:\n", - " print(f\" - {suggestion}\")" - ] - }, - { - "cell_type": "markdown", - "id": "5dcbcf93", - "metadata": {}, - "source": [ - "## 9. 종합 QA 데이터셋 생성\n", - "\n", - "모든 난이도의 QA를 한 번에 생성하고 데이터셋으로 저장합니다." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "342690ff", - "metadata": {}, - "outputs": [], - "source": [ - "# 종합 QA 데이터셋 생성\n", - "print(\"📦 종합 QA 데이터셋 생성 중...\")\n", - "print(\" (모든 난이도 + Follow-up + Evol-Instruct)\")\n", - "print(\" ⏳ 약 2-3분 소요될 수 있습니다...\\n\")\n", - "\n", - "comprehensive_dataset = qa_generator.generate_comprehensive_qa_dataset(\n", - " tables=tables,\n", - " questions_per_difficulty=2,\n", - " include_followup=True,\n", - " include_evolution=True,\n", - " evaluate_quality=False # 평가는 시간이 오래 걸려서 선택적으로\n", - ")\n", - "\n", - "print(\"\\n\" + \"=\"*60)\n", - "print(\"📊 데이터셋 생성 결과\")\n", - "print(\"=\"*60)\n", - "print(f\" 총 QA 쌍: {comprehensive_dataset['metadata'].get('total_qa_pairs', 0)}개\")\n", - "print(f\" Follow-up 체인: {comprehensive_dataset['metadata'].get('total_followups', 0)}개\")\n", - "print(f\" 진화된 질문: {comprehensive_dataset['metadata'].get('total_evolved', 0)}개\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c24832a3", - "metadata": {}, - "outputs": [], - "source": [ - "# 생성된 QA 미리보기\n", - "print(\"\\n📋 생성된 QA 샘플 (난이도별 1개씩):\")\n", - "print(\"=\"*60)\n", - "\n", - "shown_difficulties = set()\n", - "for qa in comprehensive_dataset['qa_pairs']:\n", - " difficulty = qa.get('difficulty', 'Unknown')\n", - " if difficulty not in shown_difficulties:\n", - " shown_difficulties.add(difficulty)\n", - " print(f\"\\n🔹 [{difficulty}]\")\n", - " print(f\" Q: {qa.get('question', 'N/A')}\")\n", - " answer = qa.get('answer', 'N/A')\n", - " if len(str(answer)) > 150:\n", - " print(f\" A: {str(answer)[:150]}...\")\n", - " else:\n", - " print(f\" A: {answer}\")" - ] - }, - { - "cell_type": "markdown", - "id": "48fda52a", - "metadata": {}, - "source": [ - "## 10. 데이터셋 저장" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7a74e734", - "metadata": {}, - "outputs": [], - "source": [ - "# 출력 디렉토리 생성\n", - "output_dir = Path.cwd() / \"output\"\n", - "output_dir.mkdir(exist_ok=True)\n", - "\n", - "# 타임스탬프 생성\n", - "timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n", - "\n", - "# JSON 파일로 저장\n", - "output_file = output_dir / f\"insurance_qa_dataset_{timestamp}.json\"\n", - "\n", - "with open(output_file, 'w', encoding='utf-8') as f:\n", - " json.dump(comprehensive_dataset, f, ensure_ascii=False, indent=2)\n", - "\n", - "print(f\"✅ 데이터셋 저장 완료: {output_file}\")\n", - "print(f\" 파일 크기: {output_file.stat().st_size / 1024:.1f} KB\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2669b33d", - "metadata": {}, - "outputs": [], - "source": [ - "# 간단한 통계 출력용 함수\n", - "def print_dataset_statistics(dataset: dict):\n", - " \"\"\"데이터셋 통계 출력\"\"\"\n", - " print(\"\\n\" + \"=\"*60)\n", - " print(\"📈 QA 데이터셋 통계\")\n", - " print(\"=\"*60)\n", - " \n", - " # 난이도별 카운트\n", - " difficulty_counts = {}\n", - " answer_type_counts = {}\n", - " \n", - " for qa in dataset.get('qa_pairs', []):\n", - " diff = qa.get('difficulty', 'Unknown')\n", - " atype = qa.get('answer_type', 'Unknown')\n", - " \n", - " difficulty_counts[diff] = difficulty_counts.get(diff, 0) + 1\n", - " answer_type_counts[atype] = answer_type_counts.get(atype, 0) + 1\n", - " \n", - " print(\"\\n📊 난이도별 분포:\")\n", - " for diff, count in sorted(difficulty_counts.items()):\n", - " bar = \"█\" * count\n", - " print(f\" {diff:15} | {bar} ({count})\")\n", - " \n", - " print(\"\\n📊 답변 유형별 분포:\")\n", - " for atype, count in sorted(answer_type_counts.items()):\n", - " bar = \"█\" * count\n", - " print(f\" {atype:15} | {bar} ({count})\")\n", - " \n", - " print(f\"\\n📊 총계:\")\n", - " print(f\" 총 QA 쌍: {len(dataset.get('qa_pairs', []))}개\")\n", - " print(f\" Follow-up 체인: {len(dataset.get('followup_chains', []))}개\")\n", - " print(f\" 진화된 질문: {len(dataset.get('evolved_questions', []))}개\")\n", - "\n", - "# 통계 출력\n", - "print_dataset_statistics(comprehensive_dataset)" - ] - }, - { - "cell_type": "markdown", - "id": "a8e41aa7", - "metadata": {}, - "source": [ - "## 11. 비동기 대량 생성 (선택사항)\n", - "\n", - "더 빠른 생성을 위해 비동기 방식을 사용할 수 있습니다." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "05cf2fa2", - "metadata": {}, - "outputs": [], - "source": [ - "# 비동기 QA 생성 (선택사항)\n", - "async def generate_async_dataset():\n", - " \"\"\"비동기 방식으로 QA 데이터셋 생성\"\"\"\n", - " print(\"🚀 비동기 QA 데이터셋 생성 중...\")\n", - " \n", - " dataset = await qa_generator.agenerate_comprehensive_qa_dataset(\n", - " tables=tables,\n", - " questions_per_difficulty=2\n", - " )\n", - " \n", - " return dataset\n", - "\n", - "# Jupyter에서 비동기 실행\n", - "# async_dataset = await generate_async_dataset()\n", - "# print(f\"비동기 생성 완료: {len(async_dataset['qa_pairs'])}개 QA\")" - ] - }, - { - "cell_type": "markdown", - "id": "3e43bab8", - "metadata": {}, - "source": [ - "## 12. 커스텀 프롬프트로 특화 QA 생성\n", - "\n", - "특정 요구사항에 맞는 커스텀 QA를 생성합니다." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4b0fbe57", - "metadata": {}, - "outputs": [], - "source": [ - "# 커스텀 프롬프트 예시: 수치 계산 중심 QA\n", - "CUSTOM_CALCULATION_PROMPT = \"\"\"\n", - "## Task: 수치 계산 중심 QA 생성\n", - "보험 테이블에서 수치 계산이 필요한 질문을 생성하세요.\n", - "\n", - "### Input Tables\n", - "{tables}\n", - "\n", - "### Requirements\n", - "1. 반드시 수치 계산(사칙연산, 비율, 증감률 등)이 필요한 질문\n", - "2. 답변은 정확한 숫자로 제공\n", - "3. 계산 과정(Python 코드) 포함\n", - "4. 검증 가능한 형태로 출력\n", - "\n", - "### Output Format (JSON)\n", - "```json\n", - "{{\n", - " \"questions\": [\n", - " {{\n", - " \"id\": \"CALC_001\",\n", - " \"difficulty\": \"Aggregation\",\n", - " \"answer_type\": \"calculation\",\n", - " \"question\": \"질문\",\n", - " \"answer\": \"수치 답변\",\n", - " \"calculation_steps\": [\"단계1\", \"단계2\"],\n", - " \"python_code\": \"검증용 Python 코드\"\n", - " }}\n", - " ]\n", - "}}\n", - "```\n", - "\n", - "### Generate 3 calculation-focused QA pairs.\n", - "\"\"\"\n", - "\n", - "# 커스텀 프롬프트로 생성\n", - "from QA_example.prompts import format_tables_for_prompt\n", - "\n", - "formatted_tables = format_tables_for_prompt(tables)\n", - "custom_prompt = CUSTOM_CALCULATION_PROMPT.format(tables=formatted_tables)\n", - "\n", - "print(\"🔢 수치 계산 중심 QA 생성 중...\")\n", - "response = qa_generator.pool.generate_content(custom_prompt)\n", - "\n", - "# 결과 파싱\n", - "result = qa_generator._parse_json_response(response)\n", - "\n", - "if 'questions' in result:\n", - " print(f\"\\n✅ {len(result['questions'])}개의 계산 QA 생성 완료\")\n", - " for qa in result['questions']:\n", - " print(f\"\\n📌 [{qa.get('id', 'N/A')}]\")\n", - " print(f\" Q: {qa.get('question', 'N/A')}\")\n", - " print(f\" A: {qa.get('answer', 'N/A')}\")\n", - " if 'python_code' in qa:\n", - " print(f\" Python Code: 포함됨 ✅\")\n", - "else:\n", - " print(f\"결과: {result}\")" - ] - }, - { - "cell_type": "markdown", - "id": "484134d3", - "metadata": {}, - "source": [ - "## 13. 요약 및 결론\n", - "\n", - "### 생성된 QA 데이터셋의 특징\n", - "\n", - "| 특징 | 설명 | 커버되는 양상 |\n", - "|------|------|-------------|\n", - "| 난이도 다양성 | IR부터 Insight까지 6단계 | #2 |\n", - "| Multi-Table | 복수 테이블 참조 필요 | #1 |\n", - "| 답변 유형 | Exact Match, Descriptive, Calculation | #3 |\n", - "| 수치 계산 | 집계, 비교, 증감률 계산 | #4, #6 |\n", - "| 꼬리 질문 | Follow-up 체인 생성 | #5 |\n", - "| 특정 셀 QA | 단일 셀 기반 Q-A | #7 |\n", - "| Evol-Instruct | 질문 난이도 진화 | #2, #4 |\n", - "| LLM Judge | 품질 평가 | #3 |" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/tests/choi/QA_example/qa_generator.py b/tests/choi/QA_example/qa_generator.py deleted file mode 100644 index b1ab1fe..0000000 --- a/tests/choi/QA_example/qa_generator.py +++ /dev/null @@ -1,556 +0,0 @@ -""" -QA Generator for Insurance Table Data -보험 테이블 기반 QA 데이터셋 생성기 - -이 모듈은 Gemini API를 활용하여 보험 테이블 마크다운 데이터로부터 -다양한 난이도와 유형의 QA 데이터셋을 생성합니다. - -주요 기능: -1. 난이도별 QA 생성 (IR, Analysis, Compare, Aggregation, Reasoning, Insight) -2. Multi-table QA 생성 -3. 꼬리 질문 (Follow-up) 생성 -4. Evol-Instruct 기반 난이도 진화 -5. LLM-as-Judge 품질 평가 -""" - -import json -import asyncio -import logging -from pathlib import Path -from typing import Optional, Dict, Any, List, Union -from dataclasses import dataclass, asdict -from enum import Enum -import sys -from json_repair import repair_json - -# 프로젝트 루트 추가 -project_root = Path(__file__).parent.parent -sys.path.insert(0, str(project_root)) - -from polling_gemini import get_gemini_pool, GeminiAPIPool - -from .prompts import ( - QA_GENERATOR_SYSTEM_PROMPT, - IR_QA_PROMPT, - ANALYSIS_QA_PROMPT, - COMPARE_QA_PROMPT, - AGGREGATION_QA_PROMPT, - REASONING_QA_PROMPT, - INSIGHT_QA_PROMPT, - FOLLOWUP_QA_PROMPT, - MULTI_TABLE_QA_PROMPT, - EVOL_INSTRUCT_PROMPT, - QA_EVALUATION_PROMPT, - get_qa_prompt_by_difficulty, - format_tables_for_prompt, -) - -# 로깅 설정 -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' -) -logger = logging.getLogger(__name__) - - -class QADifficulty(Enum): - """QA 난이도 레벨""" - IR = "IR" # Level 1: 단순 정보 검색 - ANALYSIS = "Analysis" # Level 2: 분석적 질문 - COMPARE = "Compare" # Level 3: 비교/Multi-hop - AGGREGATION = "Aggregation" # Level 4: 집계 연산 - REASONING = "Reasoning" # Level 5: 복합 추론 - INSIGHT = "Insight" # Level 6: 통찰 도출 - - -class QAType(Enum): - """QA 답변 유형""" - EXACT_MATCH = "exact_match" # 단답형 (정확히 일치) - DESCRIPTIVE = "descriptive" # 서술형 (LLM Judge 평가) - CALCULATION = "calculation" # 계산형 (수치 결과) - COMPARISON = "comparison" # 비교형 (비교 결과 및 근거) - - -@dataclass -class QAPair: - """QA 쌍 데이터 클래스""" - id: str - difficulty: str - answer_type: str - question: str - answer: str - reasoning: Optional[str] = None - evidence: Optional[Dict[str, Any]] = None - tags: Optional[List[str]] = None - python_verification: Optional[str] = None - chain_of_thought: Optional[List[str]] = None - # 추가 필드들 (LLM이 생성할 수 있는 다양한 필드) - calculation: Optional[str] = None - calculation_steps: Optional[List[str]] = None - assumptions: Optional[List[str]] = None - supporting_analysis: Optional[str] = None - key_findings: Optional[List[str]] = None - required_tables: Optional[List[str]] = None - join_logic: Optional[str] = None - python_code: Optional[str] = None - extra: Optional[Dict[str, Any]] = None # 기타 알 수 없는 필드용 - - def to_dict(self) -> Dict[str, Any]: - """딕셔너리로 변환""" - return {k: v for k, v in asdict(self).items() if v is not None} - - @classmethod - def from_dict(cls, data: Dict[str, Any]) -> 'QAPair': - """딕셔너리에서 QAPair 생성 (알 수 없는 필드는 extra에 저장)""" - # QAPair의 필드 목록 - known_fields = { - 'id', 'difficulty', 'answer_type', 'question', 'answer', - 'reasoning', 'evidence', 'tags', 'python_verification', - 'chain_of_thought', 'calculation', 'calculation_steps', - 'assumptions', 'supporting_analysis', 'key_findings', - 'required_tables', 'join_logic', 'python_code', 'extra' - } - - # 알려진 필드와 알 수 없는 필드 분리 - known_data = {} - extra_data = {} - - for key, value in data.items(): - if key in known_fields: - known_data[key] = value - else: - extra_data[key] = value - - # 필수 필드 기본값 설정 - known_data.setdefault('id', 'UNKNOWN') - known_data.setdefault('difficulty', 'Unknown') - known_data.setdefault('answer_type', 'unknown') - known_data.setdefault('question', '') - known_data.setdefault('answer', '') - - # extra 필드가 있으면 추가 - if extra_data: - known_data['extra'] = extra_data - - return cls(**known_data) - - -@dataclass -class EvaluationResult: - """QA 평가 결과 데이터 클래스""" - correctness: Dict[str, Any] - faithfulness: Dict[str, Any] - relevance: Dict[str, Any] - difficulty_appropriateness: Dict[str, Any] - clarity: Dict[str, Any] - overall_score: float - passed: bool - improvement_suggestions: List[str] - - -class InsuranceTableQAGenerator: - """ - 보험 테이블 기반 QA 데이터셋 생성기 - - Gemini API Pool을 활용하여 자동 키 로테이션을 지원하며, - 다양한 난이도와 유형의 QA를 생성합니다. - """ - - def __init__( - self, - config_path: Optional[str] = None, - model_name: str = "gemini-2.0-flash", - ): - """ - Args: - config_path: API 키 설정 파일 경로 - model_name: 사용할 Gemini 모델 - """ - self.pool = get_gemini_pool(config_path) - self.model_name = model_name - self.system_prompt = QA_GENERATOR_SYSTEM_PROMPT - - def _parse_json_response(self, response: str) -> Dict[str, Any]: - """LLM 응답에서 JSON 추출 및 파싱 (agentjson 사용)""" - - try: - result = repair_json(response, return_objects=True) - - if isinstance(result, dict): - logger.debug(f"JSON 파싱 성공") - return result - elif isinstance(result, list): - logger.warning(f"JSON 파싱 결과가 리스트입니다. 딕셔너리로 변환을 시도합니다.") - # 질문 리스트라고 가정 - return {"questions": result} - else: - logger.warning(f"JSON 파싱 실패 (예상치 못한 타입): {type(result)}") - return {"error": "parsing_failed", "raw_response": response} - - except Exception as e: - logger.error(f"JSON 파싱 실패: {e}") - logger.debug(f"원본 응답: {response}") - return {"error": str(e), "raw_response": response} - - def _build_prompt( - self, - base_prompt: str, - tables: Dict[str, str], - **kwargs - ) -> str: - """프롬프트 구성""" - formatted_tables = format_tables_for_prompt(tables) - - prompt = f"{self.system_prompt}\n\n{base_prompt}" - prompt = prompt.format(tables=formatted_tables, **kwargs) - - return prompt - - def generate_qa_by_difficulty( - self, - tables: Dict[str, str], - difficulty: QADifficulty, - num_questions: int = 3, - **kwargs - ) -> List[QAPair]: - """ - 특정 난이도의 QA 생성 - - Args: - tables: 테이블 딕셔너리 {table_id: markdown_content} - difficulty: QA 난이도 - num_questions: 생성할 질문 수 - - Returns: - 생성된 QA 쌍 리스트 - """ - base_prompt = get_qa_prompt_by_difficulty(difficulty.value) - prompt = self._build_prompt( - base_prompt, - tables, - num_questions=num_questions - ) - - try: - response = self.pool.generate_content(prompt) - result = self._parse_json_response(response) - - if "questions" in result: - return [QAPair.from_dict(q) for q in result["questions"]] - else: - logger.warning(f"예상치 못한 응답 형식: {result}") - return [] - - except Exception as e: - logger.error(f"QA 생성 실패: {e}") - return [] - - async def agenerate_qa_by_difficulty( - self, - tables: Dict[str, str], - difficulty: QADifficulty, - num_questions: int = 3, - **kwargs - ) -> List[QAPair]: - """난이도별 QA 생성 (비동기)""" - base_prompt = get_qa_prompt_by_difficulty(difficulty.value) - prompt = self._build_prompt( - base_prompt, - tables, - num_questions=num_questions - ) - - try: - response = await self.pool.agenerate_content(prompt) - result = self._parse_json_response(response) - - if "questions" in result: - return [QAPair.from_dict(q) for q in result["questions"]] - else: - return [] - - except Exception as e: - logger.error(f"비동기 QA 생성 실패: {e}") - return [] - - def generate_multi_table_qa( - self, - tables: Dict[str, str], - num_questions: int = 3, - ) -> List[QAPair]: - """ - Multi-table QA 생성 - - 복수의 테이블을 참조해야 답변 가능한 질문 생성 - """ - if len(tables) < 2: - logger.warning("Multi-table QA는 최소 2개의 테이블이 필요합니다.") - # 단일 테이블이라도 시도 - - prompt = self._build_prompt( - MULTI_TABLE_QA_PROMPT, - tables, - num_questions=num_questions - ) - - try: - response = self.pool.generate_content(prompt) - result = self._parse_json_response(response) - - if "questions" in result: - return [QAPair.from_dict(q) for q in result["questions"]] - return [] - - except Exception as e: - logger.error(f"Multi-table QA 생성 실패: {e}") - return [] - - def generate_followup_qa( - self, - tables: Dict[str, str], - original_qa: QAPair, - ) -> Dict[str, Any]: - """ - 꼬리 질문 (Follow-up) 생성 - - 원래 QA를 기반으로 연속적인 후속 질문 체인 생성 - """ - original_qa_str = json.dumps(original_qa.to_dict(), ensure_ascii=False, indent=2) - - prompt = self._build_prompt( - FOLLOWUP_QA_PROMPT, - tables, - original_qa=original_qa_str - ) - - try: - response = self.pool.generate_content(prompt) - return self._parse_json_response(response) - - except Exception as e: - logger.error(f"Follow-up QA 생성 실패: {e}") - return {} - - def evolve_question( - self, - tables: Dict[str, str], - original_question: str, - ) -> Dict[str, Any]: - """ - Evol-Instruct: 질문 난이도 진화 - - 기본 질문을 더 복잡한 질문으로 진화 - """ - prompt = self._build_prompt( - EVOL_INSTRUCT_PROMPT, - tables, - original_question=original_question - ) - - try: - response = self.pool.generate_content(prompt) - return self._parse_json_response(response) - - except Exception as e: - logger.error(f"Evol-Instruct 실패: {e}") - return {} - - def evaluate_qa( - self, - tables: Dict[str, str], - qa_pair: QAPair, - ) -> EvaluationResult: - """ - LLM-as-Judge: QA 품질 평가 - - 생성된 QA 쌍의 품질을 다면적으로 평가 - """ - qa_str = json.dumps(qa_pair.to_dict(), ensure_ascii=False, indent=2) - - prompt = self._build_prompt( - QA_EVALUATION_PROMPT, - tables, - qa_pair=qa_str - ) - - try: - response = self.pool.generate_content(prompt) - result = self._parse_json_response(response) - - if "evaluation" in result: - eval_data = result["evaluation"] - return EvaluationResult( - correctness=eval_data.get("correctness", {}), - faithfulness=eval_data.get("faithfulness", {}), - relevance=eval_data.get("relevance", {}), - difficulty_appropriateness=eval_data.get("difficulty_appropriateness", {}), - clarity=eval_data.get("clarity", {}), - overall_score=result.get("overall_score", 0.0), - passed=result.get("pass", False), - improvement_suggestions=result.get("improvement_suggestions", []) - ) - else: - logger.warning(f"평가 결과 파싱 실패: {result}") - return None - - except Exception as e: - logger.error(f"QA 평가 실패: {e}") - return None - - def generate_comprehensive_qa_dataset( - self, - tables: Dict[str, str], - questions_per_difficulty: int = 2, - include_followup: bool = True, - include_evolution: bool = True, - evaluate_quality: bool = False, - ) -> Dict[str, Any]: - """ - 종합적인 QA 데이터셋 생성 - - 모든 난이도의 QA를 생성하고 선택적으로 꼬리질문, 진화, 평가를 수행 - - Args: - tables: 테이블 딕셔너리 - questions_per_difficulty: 난이도별 질문 수 - include_followup: 꼬리질문 포함 여부 - include_evolution: Evol-Instruct 포함 여부 - evaluate_quality: 품질 평가 수행 여부 - - Returns: - 종합 QA 데이터셋 - """ - dataset = { - "metadata": { - "tables_count": len(tables), - "questions_per_difficulty": questions_per_difficulty, - }, - "qa_pairs": [], - "followup_chains": [], - "evolved_questions": [], - "evaluations": [], - } - - # 1. 각 난이도별 QA 생성 - for difficulty in QADifficulty: - logger.info(f"Generating {difficulty.value} level QA...") - qa_pairs = self.generate_qa_by_difficulty( - tables, difficulty, questions_per_difficulty - ) - - for qa in qa_pairs: - qa_dict = qa.to_dict() - dataset["qa_pairs"].append(qa_dict) - - # 2. 꼬리질문 생성 (선택적) - if include_followup and difficulty in [QADifficulty.IR, QADifficulty.ANALYSIS]: - followup = self.generate_followup_qa(tables, qa) - if followup: - dataset["followup_chains"].append(followup) - - # 3. 질문 진화 (선택적) - if include_evolution and difficulty in [QADifficulty.IR, QADifficulty.ANALYSIS]: - evolved = self.evolve_question(tables, qa.question) - if evolved: - dataset["evolved_questions"].append(evolved) - - # 4. 품질 평가 (선택적) - if evaluate_quality: - evaluation = self.evaluate_qa(tables, qa) - if evaluation: - dataset["evaluations"].append({ - "qa_id": qa.id, - "evaluation": asdict(evaluation) - }) - - # 5. Multi-table QA (테이블이 2개 이상인 경우) - if len(tables) >= 2: - logger.info("Generating Multi-table QA...") - multi_qa = self.generate_multi_table_qa(tables, questions_per_difficulty) - for qa in multi_qa: - dataset["qa_pairs"].append(qa.to_dict()) - - # 메타데이터 업데이트 - dataset["metadata"]["total_qa_pairs"] = len(dataset["qa_pairs"]) - dataset["metadata"]["total_followups"] = len(dataset["followup_chains"]) - dataset["metadata"]["total_evolved"] = len(dataset["evolved_questions"]) - - return dataset - - async def agenerate_comprehensive_qa_dataset( - self, - tables: Dict[str, str], - questions_per_difficulty: int = 2, - ) -> Dict[str, Any]: - """종합 QA 데이터셋 생성 (비동기)""" - dataset = { - "metadata": { - "tables_count": len(tables), - "questions_per_difficulty": questions_per_difficulty, - }, - "qa_pairs": [], - } - - # 모든 난이도에 대해 병렬로 QA 생성 - tasks = [ - self.agenerate_qa_by_difficulty(tables, difficulty, questions_per_difficulty) - for difficulty in QADifficulty - ] - - results = await asyncio.gather(*tasks) - - for qa_list in results: - for qa in qa_list: - dataset["qa_pairs"].append(qa.to_dict()) - - dataset["metadata"]["total_qa_pairs"] = len(dataset["qa_pairs"]) - - return dataset - - -# ============================================================================= -# Convenience Functions -# ============================================================================= - -def generate_qa_from_tables( - tables: Dict[str, str], - difficulty: Optional[QADifficulty] = None, - num_questions: int = 3, - config_path: Optional[str] = None, -) -> List[Dict[str, Any]]: - """ - 테이블에서 QA 생성 (간편 함수) - - Args: - tables: 테이블 딕셔너리 {table_id: markdown_content} - difficulty: 난이도 (None이면 모든 난이도) - num_questions: 난이도별 질문 수 - config_path: API 설정 파일 경로 - - Returns: - 생성된 QA 리스트 - """ - generator = InsuranceTableQAGenerator(config_path=config_path) - - if difficulty: - qa_pairs = generator.generate_qa_by_difficulty(tables, difficulty, num_questions) - return [qa.to_dict() for qa in qa_pairs] - else: - dataset = generator.generate_comprehensive_qa_dataset( - tables, - questions_per_difficulty=num_questions, - include_followup=False, - include_evolution=False, - evaluate_quality=False, - ) - return dataset["qa_pairs"] - - -async def agenerate_qa_from_tables( - tables: Dict[str, str], - num_questions: int = 3, - config_path: Optional[str] = None, -) -> Dict[str, Any]: - """테이블에서 QA 생성 (비동기 간편 함수)""" - generator = InsuranceTableQAGenerator(config_path=config_path) - return await generator.agenerate_comprehensive_qa_dataset(tables, num_questions) diff --git a/tests/choi/Table_example/README.md b/tests/choi/Table_example/README.md deleted file mode 100644 index f0cc165..0000000 --- a/tests/choi/Table_example/README.md +++ /dev/null @@ -1,168 +0,0 @@ -# Table Example - 보험 테이블 추출기 - -`polling_gemini` 패키지를 활용하여 보험 문서 이미지에서 테이블을 Markdown 형식으로 추출하는 예제입니다. - -## 주요 특징 - -- **VLM + OCR 하이브리드 접근**: Gemini의 시각적 추론 능력과 OCR 텍스트를 결합 -- **보험 도메인 특화 프롬프트**: 계층적 헤더, 셀 병합, 복합 데이터 처리에 최적화 -- **자동 API 키 로테이션**: `polling_gemini` 패키지를 통한 API 키 관리 -- **동기/비동기 지원**: 대용량 처리를 위한 비동기 API 지원 - -## 설치 - -프로젝트 루트에서: - -```bash -uv sync -``` - -## 사용법 - -### 1. 기본 사용 - -```python -from Table_example import extract_table_from_image - -# 이미지에서 테이블 추출 -result = extract_table_from_image("insurance_table.png") -print(result) -``` - -### 2. OCR 참조 텍스트와 함께 사용 - -```python -from Table_example import extract_table_from_image - -# OCR로 먼저 추출한 텍스트가 있는 경우 -ocr_text = """| 구분 | 보험기간 | 납입기간 | -| 상해사망 | 80세 | 20년 |""" - -result = extract_table_from_image( - "insurance_table.png", - ocr_markdown=ocr_text # OCR 결과를 참조로 제공 -) -``` - -### 3. 비동기 처리 - -```python -import asyncio -from Table_example import aextract_table_from_image - -async def process_multiple_images(): - images = ["table1.png", "table2.png", "table3.png"] - - # 동시에 여러 이미지 처리 - tasks = [aextract_table_from_image(img) for img in images] - results = await asyncio.gather(*tasks) - - return results - -results = asyncio.run(process_multiple_images()) -``` - -### 4. InsuranceTableExtractor 클래스 직접 사용 - -```python -from Table_example import InsuranceTableExtractor - -# 추출기 인스턴스 생성 -extractor = InsuranceTableExtractor( - config_path="apis/gemini_keys.yaml", # 커스텀 설정 경로 - model_name="gemini-2.5-flash" # 모델 지정 -) - -# 테이블 추출 -result = extractor.extract("insurance_table.png") - -# API Pool 상태 확인 -status = extractor.get_pool_status() -print(f"현재 API 키: {status['current_key']['name']}") -``` - -## 프롬프트 설계 - -### System Prompt 핵심 원칙 - -1. **구조적 완전성**: 병합된 셀을 비정규화하여 모든 행에 값 채우기 -2. **헤더 평탄화**: 다중 행 헤더를 언더스코어(_)로 연결하여 단일 행으로 변환 -3. **데이터 무결성**: 금액 포맷 정리, 퍼센트 유지, 부가 정보 제거 -4. **Hybrid Reference**: OCR 텍스트 참조로 오타 교정 - -### User Prompt Chain-of-Table 단계 - -1. **구조 분석**: 레이아웃, 헤더 행 수, 병합 셀 식별 -2. **헤더 처리**: 다중 행 헤더 → 단일 행 변환 -3. **데이터 추출**: 값 추출 및 병합 셀 반복 입력 -4. **포맷팅 검증**: 금액 정리, 컬럼 개수 확인 - -## 테스트 - -```bash -cd TableMagnifier -python -m Table_example.test_extraction -``` - -### 테스트 이미지 준비 - -`Table_example/sample_images/` 폴더에 테스트할 보험 테이블 이미지를 추가하세요: -- 지원 형식: PNG, JPG, JPEG, GIF, WebP, BMP -- 권장: 해상도가 높은 테이블 이미지 - -## 출력 예시 - -입력 이미지: -``` -┌─────────────┬────────────────────────────┐ -│ │ 해지환급금 │ -│ 구분 ├──────────────┬─────────────┤ -│ │ 금액 │ 환급률 │ -├─────────────┼──────────────┼─────────────┤ -│ 1년 │ 100,000원 │ 10% │ -│ 5년 │ 500,000원 │ 50% │ -│ 10년 │ 1,000,000원 │ 100% │ -└─────────────┴──────────────┴─────────────┘ -``` - -출력 Markdown: -```markdown -| 구분 | 해지환급금_금액 | 해지환급금_환급률 | -| :--- | :--- | :--- | -| 1년 | 100000 | 10% | -| 5년 | 500000 | 50% | -| 10년 | 1000000 | 100% | -``` - -## 파일 구조 - -``` -Table_example/ -├── __init__.py # 패키지 초기화 -├── prompts.py # 시스템/사용자 프롬프트 정의 -├── table_extractor.py # 테이블 추출기 클래스 -├── test_extraction.py # 테스트 코드 -├── sample_images/ # 테스트 이미지 폴더 -└── README.md # 이 문서 -``` - -## API 키 설정 - -`apis/gemini_keys.yaml` 파일에 Gemini API 키를 설정하세요: - -```yaml -api_keys: - - key: "YOUR_GEMINI_API_KEY" - name: "key1" - enabled: true - -settings: - model: "gemini-2.5-flash" - temperature: 0.1 - max_retries: 3 - retry_delay: 2 -``` - -## 라이센스 - -MIT License diff --git a/tests/choi/Table_example/__init__.py b/tests/choi/Table_example/__init__.py deleted file mode 100644 index 30e122d..0000000 --- a/tests/choi/Table_example/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -Table Extraction Example -보험 도메인 특화 테이블 추출 예제 -polling_gemini 패키지를 사용하여 이미지에서 Markdown 테이블 추출 -""" - -from .table_extractor import ( - InsuranceTableExtractor, - extract_table_from_image, - aextract_table_from_image, -) -from .prompts import SYSTEM_PROMPT, USER_PROMPT_TEMPLATE - -__all__ = [ - 'InsuranceTableExtractor', - 'extract_table_from_image', - 'aextract_table_from_image', - 'SYSTEM_PROMPT', - 'USER_PROMPT_TEMPLATE', -] diff --git a/tests/choi/Table_example/example_batch.py b/tests/choi/Table_example/example_batch.py deleted file mode 100644 index 2f348ff..0000000 --- a/tests/choi/Table_example/example_batch.py +++ /dev/null @@ -1,167 +0,0 @@ -""" -비동기 배치 테이블 추출 예제 -여러 이미지를 동시에 처리하여 효율적으로 테이블을 추출합니다. -""" - -import sys -import asyncio -import time -from pathlib import Path -from typing import List, Tuple, Optional - -# 프로젝트 루트를 Python 경로에 추가 -project_root = Path(__file__).parent.parent -sys.path.insert(0, str(project_root)) - -from Table_example import aextract_table_from_image, InsuranceTableExtractor - - -async def process_single_image( - image_path: Path, - ocr_markdown: str = "N/A" -) -> Tuple[Path, Optional[str], Optional[str]]: - """ - 단일 이미지 처리 (비동기) - - Returns: - (이미지 경로, 결과 또는 None, 에러 메시지 또는 None) - """ - try: - result = await aextract_table_from_image( - image_path=image_path, - ocr_markdown=ocr_markdown - ) - return (image_path, result, None) - except Exception as e: - return (image_path, None, str(e)) - - -async def batch_extract_tables( - image_paths: List[Path], - max_concurrent: int = 3 -) -> List[Tuple[Path, Optional[str], Optional[str]]]: - """ - 여러 이미지를 배치로 처리 (동시성 제한) - - Args: - image_paths: 처리할 이미지 경로 리스트 - max_concurrent: 동시 처리 최대 개수 - - Returns: - (이미지 경로, 결과, 에러) 튜플 리스트 - """ - # 세마포어로 동시 실행 제한 - semaphore = asyncio.Semaphore(max_concurrent) - - async def process_with_semaphore(image_path: Path): - async with semaphore: - return await process_single_image(image_path) - - # 모든 이미지 동시 처리 (세마포어로 제한) - tasks = [process_with_semaphore(path) for path in image_paths] - results = await asyncio.gather(*tasks) - - return results - - -def find_all_images(directory: Path) -> List[Path]: - """디렉토리에서 모든 이미지 파일 찾기""" - extensions = ["*.png", "*.jpg", "*.jpeg", "*.gif", "*.webp", "*.bmp"] - images = [] - for ext in extensions: - images.extend(directory.glob(ext)) - return sorted(images) - - -async def main(): - """ - 배치 처리 예제 - sample_images 폴더의 모든 이미지를 처리합니다. - """ - print("🏥 보험 테이블 배치 추출 시작") - print("=" * 70) - - # ============================================ - # 📌 설정 - # ============================================ - sample_dir = Path(__file__).parent / "sample_images" - output_dir = Path(__file__).parent / "output" - max_concurrent = 3 # 동시 처리 최대 개수 - - # 출력 디렉토리 생성 - output_dir.mkdir(exist_ok=True) - - # ============================================ - # 📌 이미지 파일 수집 - # ============================================ - image_paths = find_all_images(sample_dir) - - if not image_paths: - print(f"❌ 이미지 파일이 없습니다: {sample_dir}") - print("\n📝 sample_images 폴더에 테이블 이미지를 추가하세요.") - return - - print(f"📁 처리할 이미지: {len(image_paths)}개") - for i, path in enumerate(image_paths, 1): - print(f" {i}. {path.name}") - print(f"⚡ 동시 처리 수: {max_concurrent}") - print("=" * 70) - - # ============================================ - # 🚀 배치 처리 실행 - # ============================================ - start_time = time.time() - - results = await batch_extract_tables( - image_paths=image_paths, - max_concurrent=max_concurrent - ) - - elapsed = time.time() - start_time - - # ============================================ - # 📊 결과 처리 - # ============================================ - success_count = 0 - failed_count = 0 - - print("\n📊 처리 결과:") - print("-" * 70) - - for image_path, result, error in results: - if result: - success_count += 1 - status = "✅ 성공" - - # 결과 파일 저장 - output_path = output_dir / f"{image_path.stem}_table.md" - with open(output_path, "w", encoding="utf-8") as f: - f.write(f"# {image_path.name}\n\n") - f.write(result) - - print(f"\n{status}: {image_path.name}") - print(f" 💾 저장: {output_path.name}") - print(f" 📝 미리보기: {result[:100]}...") - else: - failed_count += 1 - status = "❌ 실패" - print(f"\n{status}: {image_path.name}") - print(f" ⚠️ 에러: {error}") - - # ============================================ - # 📈 최종 요약 - # ============================================ - print("\n" + "=" * 70) - print("📈 처리 완료!") - print(f" ⏱️ 총 소요 시간: {elapsed:.2f}초") - print(f" ✅ 성공: {success_count}개") - print(f" ❌ 실패: {failed_count}개") - print(f" 📁 출력 폴더: {output_dir}") - - if success_count > 0: - avg_time = elapsed / success_count - print(f" ⚡ 평균 처리 시간: {avg_time:.2f}초/이미지") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/tests/choi/Table_example/example_simple.py b/tests/choi/Table_example/example_simple.py deleted file mode 100644 index c0bdbbc..0000000 --- a/tests/choi/Table_example/example_simple.py +++ /dev/null @@ -1,97 +0,0 @@ -""" -간단한 테이블 추출 예제 -실제 이미지 파일 경로를 지정하여 테이블을 추출합니다. -""" - -import sys -from pathlib import Path - -# 프로젝트 루트를 Python 경로에 추가 -project_root = Path(__file__).parent.parent -sys.path.insert(0, str(project_root)) - -from Table_example import extract_table_from_image, InsuranceTableExtractor - - -def main(): - """ - 사용 예제: - 1. sample_images 폴더에 테이블 이미지를 추가하세요 - 2. 아래 IMAGE_PATH를 해당 이미지 경로로 수정하세요 - 3. 스크립트를 실행하세요 - """ - - # ============================================ - # 📌 이미지 경로 설정 - # ============================================ - # 방법 1: sample_images 폴더의 이미지 사용 - sample_dir = Path(__file__).parent / "sample_images" - - # sample_images 폴더의 첫 번째 이미지 자동 선택 - image_files = list(sample_dir.glob("*.png")) + \ - list(sample_dir.glob("*.jpg")) + \ - list(sample_dir.glob("*.jpeg")) - - if not image_files: - print("❌ sample_images 폴더에 이미지가 없습니다.") - print(f" 경로: {sample_dir}") - print("\n📝 테이블 이미지를 추가한 후 다시 실행하세요.") - return - - IMAGE_PATH = image_files[0] - - # 방법 2: 직접 경로 지정 (주석 해제하여 사용) - # IMAGE_PATH = "/path/to/your/table_image.png" - - # ============================================ - # 📌 OCR 참조 텍스트 (선택적) - # ============================================ - # OCR로 먼저 추출한 텍스트가 있으면 여기에 입력 - # 이미지가 흐릿할 때 숫자 정확도 향상에 도움됩니다 - OCR_MARKDOWN = "N/A" # 없으면 "N/A" - - # 예시: - # OCR_MARKDOWN = """ - # | 구분 | 보험료 | - # | 상해 | 10000 | - # | 질병 | 15000 | - # """ - - # ============================================ - # 🚀 테이블 추출 실행 - # ============================================ - print("🏥 보험 테이블 추출 시작") - print("=" * 60) - print(f"📁 이미지: {IMAGE_PATH}") - print(f"📝 OCR 참조: {'있음' if OCR_MARKDOWN != 'N/A' else '없음'}") - print("=" * 60) - - try: - # 테이블 추출 - result = extract_table_from_image( - image_path=IMAGE_PATH, - ocr_markdown=OCR_MARKDOWN - ) - - print("\n✅ 추출 완료!") - print("\n📊 결과 (Markdown Table):") - print("-" * 60) - print(result) - print("-" * 60) - - # 결과를 파일로 저장 - output_path = Path(IMAGE_PATH).with_suffix(".md") - with open(output_path, "w", encoding="utf-8") as f: - f.write(result) - print(f"\n💾 결과 저장됨: {output_path}") - - except FileNotFoundError as e: - print(f"❌ 파일을 찾을 수 없습니다: {e}") - except Exception as e: - print(f"❌ 추출 실패: {e}") - import traceback - traceback.print_exc() - - -if __name__ == "__main__": - main() diff --git a/tests/choi/Table_example/output/.gitkeep b/tests/choi/Table_example/output/.gitkeep deleted file mode 100644 index bc83f8d..0000000 --- a/tests/choi/Table_example/output/.gitkeep +++ /dev/null @@ -1,3 +0,0 @@ -# Output - -이 폴더에 추출된 테이블 결과가 저장됩니다. diff --git a/tests/choi/Table_example/prompts.py b/tests/choi/Table_example/prompts.py deleted file mode 100644 index e928665..0000000 --- a/tests/choi/Table_example/prompts.py +++ /dev/null @@ -1,69 +0,0 @@ - -# System Prompt - 보험 데이터 엔지니어 역할 정의 -SYSTEM_PROMPT = """# Role Definition -당신은 20년 경력의 '수석 보험 데이터 엔지니어'이자 'OCR 후처리 전문가'입니다. -당신의 임무는 제공된 [보험 문서 이미지]와 선택적으로 제공되는 [기초 OCR 텍스트]를 분석하여, 데이터베이스 적재가 가능한 완벽한 형태의 'Standardized Markdown Table'로 변환하는 것입니다. - -# Core Principles -1. **구조적 완전성(Structural Integrity):** 시각적으로 병합(Merge)된 셀은 반드시 비정규화(Denormalization)하여 모든 행에 값을 채워야 합니다. 빈칸이나 " 상동" 등의 표현은 금지됩니다. -2. **헤더 평탄화(Header Flattening):** 2행 이상의 계층적 헤더(Multi-row Headers)는 상위 헤더와 하위 헤더를 언더스코어(_)로 연결하여 단일 행(Single-row) 헤더로 변환합니다. (예: '보장내용' 하위에 '지급금액'이 있다면 -> '보장내용_지급금액') -3. **데이터 무결성(Data Integrity):** - - 금액의 천 단위 구분자(,)는 제거합니다. (예: 1,000,000 -> 1000000) - - 퍼센트(%)는 기호를 포함한 문자열로 유지합니다. (예: 98.5%) - - 괄호 안의 보조 정보(예: 전년 대비 증감액)는 무시하고 핵심 수치만 추출합니다. -4. **Hybrid Reference:** [기초 OCR 텍스트]가 제공될 경우, 이미지 내 텍스트가 흐릿하거나 불분명할 때 해당 텍스트를 참조하여 오타를 교정하십시오. 단, 표의 구조(행/열 위치) 판단은 반드시 [원본 이미지]를 기준으로 합니다.""" - -# User Prompt Template - 단계별 지시사항 포함 -USER_PROMPT_TEMPLATE = """# Task Description -아래 제공된 입력을 바탕으로 보험 테이블 데이터를 추출하십시오. - -# Input Data -1. **Target Image:** [첨부된 이미지] -2. **Reference OCR Markdown (Optional):** -\"\"\" -{ocr_markdown} -\"\"\" - -# Step-by-Step Instructions (Chain-of-Table) -단계별로 생각하고 실행하십시오: - -**Step 1. 구조 분석 (Structure Analysis)** -- 이미지를 보고 표의 전체적인 레이아웃을 파악하십시오. -- 헤더가 몇 개의 행(Row)으로 구성되어 있는지 확인하십시오. -- 세로로 병합된(Vertically Merged) '구분'이나 '기간' 컬럼이 있는지 식별하십시오. - -**Step 2. 헤더 처리 (Header Processing)** -- 다중 행 헤더를 단일 행 키(Unique Key)로 변환하십시오. -- 예: - | 구분 | 해지환급금 | - | | 금액 | 환급률 | - -> | 구분 | 해지환급금_금액 | 해지환급금_환급률 | - -**Step 3. 데이터 추출 및 채우기 (Extraction & Filling)** -- 각 행(Row)의 데이터를 추출하십시오. -- **중요:** 병합된 셀은 해당 범위에 속하는 모든 행에 동일한 값을 반복 입력(Repeat Value)하십시오. 절대 빈 칸으로 두지 마십시오. -- OCR 참고용 텍스트가 있다면, 숫자의 정확성을 검증하는 데 사용하십시오. - -**Step 4. 포맷팅 및 검증 (Formatting & Verification)** -- 금액에서 '원', ',' 제거 / 정수형 변환. -- 출력 전, 헤더의 컬럼 개수와 데이터 행의 컬럼 개수가 일치하는지 확인하십시오. - -# Output Format -설명이나 사족 없이 오직 **Markdown Table** 만 출력하십시오. - -| 헤더1 | 헤더2_서브1 | 헤더2_서브2 | ... | -| :--- | :--- | :--- | ... | -| 값1 | 값2 | 값3 | ... |""" - - -def get_user_prompt(ocr_markdown: str = "N/A") -> str: - """ - OCR 마크다운 데이터를 포함한 사용자 프롬프트 생성 - - Args: - ocr_markdown: OCR로 추출된 마크다운 텍스트 (없으면 "N/A") - - Returns: - 완성된 사용자 프롬프트 문자열 - """ - return USER_PROMPT_TEMPLATE.format(ocr_markdown=ocr_markdown) diff --git a/tests/choi/Table_example/sample_images/.gitkeep b/tests/choi/Table_example/sample_images/.gitkeep deleted file mode 100644 index eede5be..0000000 --- a/tests/choi/Table_example/sample_images/.gitkeep +++ /dev/null @@ -1,15 +0,0 @@ -# Sample Images - -이 폴더에 테스트할 보험 테이블 이미지를 추가하세요. - -## 지원 형식 -- PNG -- JPG / JPEG -- GIF -- WebP -- BMP - -## 권장 이미지 -- 해상도가 높은 테이블 이미지 -- 보험 약관, 보험료표, 해지환급금표 등 -- 계층적 헤더가 있는 복잡한 테이블 diff --git a/tests/choi/Table_example/sample_images/I_table_78.md b/tests/choi/Table_example/sample_images/I_table_78.md deleted file mode 100644 index 727197b..0000000 --- a/tests/choi/Table_example/sample_images/I_table_78.md +++ /dev/null @@ -1,6 +0,0 @@ -|구분|XX세|XX+1세|XX+2세|XX+3세|XX+4세|XX+5세| -|---|---|---|---|---|---|---| -|나이증가분(A)||1,059|1,357|1,739|2,229|2,855| -|보험료 산출 기초율
(위험률 등) 증가분
(B=전년도
기준보험료의 최대
25% 가정)||10,846|13,897|17,806|22,815|29,232| -|기준보험료
(C=전년도
기준보험료+A+B)|42,325|54,321|69,485|89,030|114,074|146,161| - diff --git a/tests/choi/Table_example/sample_images/I_table_78.png b/tests/choi/Table_example/sample_images/I_table_78.png deleted file mode 100644 index 3237899..0000000 Binary files a/tests/choi/Table_example/sample_images/I_table_78.png and /dev/null differ diff --git a/tests/choi/Table_example/sample_images/qa_output/I_table_78_pair_0_qa.json b/tests/choi/Table_example/sample_images/qa_output/I_table_78_pair_0_qa.json deleted file mode 100644 index f4d70a8..0000000 --- a/tests/choi/Table_example/sample_images/qa_output/I_table_78_pair_0_qa.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "name": "I_table_78_pair_0", - "image_paths": [ - "tests/choi/Table_example/sample_images/I_table_78.png" - ], - "qa_results": [ - { - "question": "XX+2세의 기준보험료(C)는 얼마입니까?", - "answer": "69,485", - "type": "lookup", - "reasoning_annotation": "Directly retrieve the value from the '기준보험료 (C)' row and 'XX+2세' column.", - "context": null - }, - { - "question": "기준보험료(C)가 70,000 미만인 연령대는 어디입니까?", - "answer": "XX세, XX+1세, XX+2세", - "type": "filter", - "reasoning_annotation": "Filter the '기준보험료 (C)' row for values less than 70,000 and list the corresponding age categories.", - "context": null - }, - { - "question": "XX+1세부터 XX+5세까지의 나이증가분(A)의 총합은 얼마입니까?", - "answer": "9,239", - "type": "aggregate", - "reasoning_annotation": "Sum the values in the '나이증가분(A)' row for age categories XX+1세 (1,059), XX+2세 (1,357), XX+3세 (1,739), XX+4세 (2,229), and XX+5세 (2,855). (1,059 + 1,357 + 1,739 + 2,229 + 2,855 = 9,239).", - "context": null - }, - { - "question": "XX+4세와 XX+5세 중 나이증가분(A)이 더 높은 연령대는 어디입니까?", - "answer": "XX+5세", - "type": "compare", - "reasoning_annotation": "Compare the '나이증가분(A)' values for XX+4세 (2,229) and XX+5세 (2,855). XX+5세 has a higher value.", - "context": null - }, - { - "question": "XX+5세의 기준보험료(C)와 XX세의 기준보험료(C)의 차이는 얼마입니까?", - "answer": "103,836", - "type": "arithmetic", - "reasoning_annotation": "Subtract the '기준보험료 (C)' value for XX세 (42,325) from the '기준보험료 (C)' value for XX+5세 (146,161). (146,161 - 42,325 = 103,836).", - "context": null - }, - { - "question": "XX세부터 XX+5세까지 기준보험료(C)의 전반적인 추세는 어떻습니까?", - "answer": "연령이 증가함에 따라 기준보험료(C)가 지속적으로 증가하는 상승 추세를 보입니다.", - "type": "temporal", - "reasoning_annotation": "Observe the values in the '기준보험료 (C)' row across all age categories (42,325, 54,321, 69,485, 89,030, 114,074, 146,161). All values show a consistent increase.", - "context": null - }, - { - "question": "나이증가분(A)이 1,739인 연령대의 기준보험료(C)는 얼마입니까?", - "answer": "89,030", - "type": "multi_hop", - "reasoning_annotation": "First, find the age category where '나이증가분(A)' is 1,739, which is 'XX+3세'. Then, retrieve the '기준보험료 (C)' value for 'XX+3세'.", - "context": null - }, - { - "question": "위험률 등 기초율 변화로 인한 추가 비용이 가장 높은 연령대는 어디입니까?", - "answer": "XX+5세", - "type": "implicit_reference", - "reasoning_annotation": "The question implicitly refers to '보험료 산출 기초율 (위험률 등) 증가분 (B)'. Find the maximum value in this row, which is 29,232 for 'XX+5세'.", - "context": null - }, - { - "question": "XX+4세 바로 다음 연령대의 기준보험료는 얼마입니까?", - "answer": "146,161", - "type": "ellipsis", - "reasoning_annotation": "The question implicitly asks for the '기준보험료 (C)' of the age category immediately following 'XX+4세', which is 'XX+5세'. Retrieve the value from the '기준보험료 (C)' row and 'XX+5세' column.", - "context": null - }, - { - "question": "정부의 보험료 안정화 방안에 따라, 보험료 인상률을 재조정해야 하는 연령대는 어디입니까?", - "answer": "XX+4세, XX+5세", - "type": "long_sequence", - "reasoning_annotation": "According to the provided context, identify age categories where '보험료 산출 기초율 증가분(B)' exceeds 20,000. These are XX+4세 (22,815) and XX+5세 (29,232).", - "context": "정부의 보험료 안정화 방안에 따라, '보험료 산출 기초율 증가분(B)'이 20,000을 초과하는 연령대에 대해서는 보험료 인상률을 재조정해야 한다." - } - ], - "token_usage": 8086, - "errors": [] -} \ No newline at end of file diff --git a/tests/choi/Table_example/sample_images/qa_output/_summary.json b/tests/choi/Table_example/sample_images/qa_output/_summary.json deleted file mode 100644 index 1d6400d..0000000 --- a/tests/choi/Table_example/sample_images/qa_output/_summary.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "folder": "tests/choi/Table_example/sample_images", - "total": 1, - "success": 1, - "failed": 0, - "qa_only": true, - "provider": "gemini_pool", - "model": "gpt-4o-mini", - "checkpointing_enabled": false, - "checkpoint_dir": null, - "results": [ - { - "name": "I_table_78_pair_0", - "status": "success", - "qa_count": 10, - "output_file": "tests/choi/Table_example/sample_images/qa_output/I_table_78_pair_0_qa.json", - "errors": [] - } - ] -} \ No newline at end of file diff --git a/tests/choi/Table_example/table_extractor.py b/tests/choi/Table_example/table_extractor.py deleted file mode 100644 index 4024079..0000000 --- a/tests/choi/Table_example/table_extractor.py +++ /dev/null @@ -1,295 +0,0 @@ -""" -보험 테이블 추출기 -polling_gemini 패키지를 사용하여 이미지에서 테이블을 Markdown으로 변환 -""" - -import os -import asyncio -import base64 -import logging -from pathlib import Path -from typing import Optional, Dict, Any, Union -import google.generativeai as genai -from google.api_core import exceptions as google_exceptions - -from .prompts import SYSTEM_PROMPT, get_user_prompt - -# polling_gemini 패키지에서 API Pool 가져오기 -import sys -project_root = Path(__file__).parent.parent -sys.path.insert(0, str(project_root)) - -from polling_gemini import get_gemini_pool, GeminiAPIPool - -# 로깅 설정 -logger = logging.getLogger(__name__) - - -def load_image_as_base64(image_path: Union[str, Path]) -> str: - """이미지 파일을 Base64로 인코딩""" - image_path = Path(image_path) - if not image_path.exists(): - raise FileNotFoundError(f"이미지 파일을 찾을 수 없습니다: {image_path}") - - with open(image_path, "rb") as f: - return base64.b64encode(f.read()).decode("utf-8") - - -def get_image_mime_type(image_path: Union[str, Path]) -> str: - """이미지 파일의 MIME 타입 반환""" - image_path = Path(image_path) - suffix = image_path.suffix.lower() - - mime_types = { - ".jpg": "image/jpeg", - ".jpeg": "image/jpeg", - ".png": "image/png", - ".gif": "image/gif", - ".webp": "image/webp", - ".bmp": "image/bmp", - } - - return mime_types.get(suffix, "image/jpeg") - - -class InsuranceTableExtractor: - """ - 보험 문서 이미지에서 테이블을 추출하는 클래스 - - polling_gemini의 GeminiAPIPool을 활용하여 자동 API 키 로테이션을 지원합니다. - """ - - def __init__( - self, - config_path: Optional[str] = None, - model_name: str = "gemini-2.5-flash", - ): - """ - Args: - config_path: API 키 설정 파일 경로 (None이면 기본 경로 사용) - model_name: 사용할 Gemini 모델 이름 - """ - self.pool = get_gemini_pool(config_path) - self.model_name = model_name - self.system_prompt = SYSTEM_PROMPT - - def _create_multimodal_content( - self, - image_path: Union[str, Path], - ocr_markdown: str = "N/A" - ) -> list: - """ - 멀티모달 콘텐츠 생성 (이미지 + 텍스트) - - Args: - image_path: 이미지 파일 경로 - ocr_markdown: 참조용 OCR 마크다운 텍스트 - - Returns: - Gemini API에 전달할 콘텐츠 리스트 - """ - # 이미지 로드 - image_data = load_image_as_base64(image_path) - mime_type = get_image_mime_type(image_path) - - # 사용자 프롬프트 생성 - user_prompt = get_user_prompt(ocr_markdown) - - # 멀티모달 콘텐츠 구성 - content = [ - # 시스템 프롬프트를 포함한 전체 프롬프트 - f"{self.system_prompt}\n\n{user_prompt}", - # 이미지 데이터 - { - "mime_type": mime_type, - "data": image_data - } - ] - - return content - - def extract( - self, - image_path: Union[str, Path], - ocr_markdown: str = "N/A", - **kwargs - ) -> str: - """ - 이미지에서 테이블을 추출하여 Markdown으로 반환 (동기) - - Args: - image_path: 테이블이 포함된 이미지 파일 경로 - ocr_markdown: 참조용 OCR 마크다운 텍스트 (선택적) - **kwargs: 추가 생성 파라미터 - - Returns: - 추출된 Markdown 테이블 문자열 - """ - content = self._create_multimodal_content(image_path, ocr_markdown) - - # API Pool의 현재 설정된 모델 사용 - # 멀티모달 요청을 위해 직접 genai 호출 - max_retries = self.pool.settings.get('max_retries', 3) - retry_delay = self.pool.settings.get('retry_delay', 2) - - last_error = None - attempts = 0 - max_attempts = len(self.pool.api_keys) * max_retries - - while attempts < max_attempts: - current_key = self.pool.api_keys[self.pool.current_key_index] - - try: - # 현재 키로 Gemini 설정 - genai.configure(api_key=current_key.key) - model = genai.GenerativeModel(self.model_name) - - # 생성 설정 - generation_config = { - 'temperature': self.pool.settings.get('temperature', 0.1), - } - generation_config.update(kwargs.get('generation_config', {})) - - # API 호출 - response = model.generate_content( - content, - generation_config=generation_config - ) - - # 성공 시 실패 카운트 리셋 - current_key.failed_count = 0 - current_key.last_error = None - - return response.text - - except google_exceptions.ResourceExhausted as e: - logger.warning(f"API 키 '{current_key.name}' 할당량 초과. 다음 키로 전환합니다.") - current_key.failed_count += 1 - current_key.last_error = str(e) - last_error = e - - if not self.pool._rotate_key(): - break - - except Exception as e: - logger.warning(f"API 호출 실패 (키: {current_key.name}): {e}") - current_key.failed_count += 1 - current_key.last_error = str(e) - last_error = e - - if self.pool._is_quota_error(e): - if not self.pool._rotate_key(): - break - else: - import time - time.sleep(retry_delay) - - attempts += 1 - - error_msg = f"모든 API 키로 시도했으나 실패했습니다. 마지막 에러: {last_error}" - logger.error(error_msg) - raise Exception(error_msg) - - async def aextract( - self, - image_path: Union[str, Path], - ocr_markdown: str = "N/A", - **kwargs - ) -> str: - """ - 이미지에서 테이블을 추출하여 Markdown으로 반환 (비동기) - - Args: - image_path: 테이블이 포함된 이미지 파일 경로 - ocr_markdown: 참조용 OCR 마크다운 텍스트 (선택적) - **kwargs: 추가 생성 파라미터 - - Returns: - 추출된 Markdown 테이블 문자열 - """ - # 동기 메서드를 비동기로 래핑 - loop = asyncio.get_event_loop() - return await loop.run_in_executor( - None, - lambda: self.extract(image_path, ocr_markdown, **kwargs) - ) - - def get_pool_status(self) -> Dict[str, Any]: - """현재 API Pool 상태 반환""" - return { - 'current_key': self.pool.get_current_key_info(), - 'all_keys': self.pool.get_all_keys_status() - } - - -# 편의 함수들 - -def extract_table_from_image( - image_path: Union[str, Path], - ocr_markdown: str = "N/A", - config_path: Optional[str] = None, - **kwargs -) -> str: - """ - 이미지에서 보험 테이블을 추출하는 간편 함수 (동기) - - Args: - image_path: 테이블이 포함된 이미지 파일 경로 - ocr_markdown: 참조용 OCR 마크다운 텍스트 (선택적) - config_path: API 키 설정 파일 경로 - **kwargs: 추가 생성 파라미터 - - Returns: - 추출된 Markdown 테이블 문자열 - - Example: - ```python - from Table_example import extract_table_from_image - - # 기본 사용 - result = extract_table_from_image("insurance_table.png") - print(result) - - # OCR 참조 텍스트와 함께 사용 - result = extract_table_from_image( - "insurance_table.png", - ocr_markdown="| 구분 | 금액 |\\n| 보험료 | 10000 |" - ) - ``` - """ - extractor = InsuranceTableExtractor(config_path=config_path) - return extractor.extract(image_path, ocr_markdown, **kwargs) - - -async def aextract_table_from_image( - image_path: Union[str, Path], - ocr_markdown: str = "N/A", - config_path: Optional[str] = None, - **kwargs -) -> str: - """ - 이미지에서 보험 테이블을 추출하는 간편 함수 (비동기) - - Args: - image_path: 테이블이 포함된 이미지 파일 경로 - ocr_markdown: 참조용 OCR 마크다운 텍스트 (선택적) - config_path: API 키 설정 파일 경로 - **kwargs: 추가 생성 파라미터 - - Returns: - 추출된 Markdown 테이블 문자열 - - Example: - ```python - import asyncio - from Table_example import aextract_table_from_image - - async def main(): - result = await aextract_table_from_image("insurance_table.png") - print(result) - - asyncio.run(main()) - ``` - """ - extractor = InsuranceTableExtractor(config_path=config_path) - return await extractor.aextract(image_path, ocr_markdown, **kwargs) diff --git a/tests/choi/Table_example/test_extraction.ipynb b/tests/choi/Table_example/test_extraction.ipynb deleted file mode 100644 index fc7a143..0000000 --- a/tests/choi/Table_example/test_extraction.ipynb +++ /dev/null @@ -1,610 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "03a4228b", - "metadata": {}, - "source": [ - "## 1. 환경 설정" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "ac52abd7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "프로젝트 루트: /Users/jaehyeokchoi/Desktop/chois_toy/private/TableMagnifier\n" - ] - } - ], - "source": [ - "import sys\n", - "from pathlib import Path\n", - "\n", - "# 프로젝트 루트를 Python 경로에 추가\n", - "project_root = Path.cwd().parent\n", - "if str(project_root) not in sys.path:\n", - " sys.path.insert(0, str(project_root))\n", - "\n", - "print(f\"프로젝트 루트: {project_root}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "3f5862d6", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✅ Table_example 패키지 로드 완료!\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/jaehyeokchoi/Desktop/chois_toy/private/TableMagnifier/.venv/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - } - ], - "source": [ - "# Table_example 패키지 임포트\n", - "from Table_example import (\n", - " InsuranceTableExtractor,\n", - " extract_table_from_image,\n", - " aextract_table_from_image,\n", - " SYSTEM_PROMPT,\n", - " USER_PROMPT_TEMPLATE,\n", - ")\n", - "from Table_example.prompts import get_user_prompt\n", - "\n", - "print(\"✅ Table_example 패키지 로드 완료!\")" - ] - }, - { - "cell_type": "markdown", - "id": "7e2f995a", - "metadata": {}, - "source": [ - "## 2. 프롬프트 확인\n", - "\n", - "보험 도메인 특화 프롬프트를 확인합니다." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "95f59ce1", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "📋 System Prompt:\n", - "======================================================================\n", - "# Role Definition\n", - "당신은 20년 경력의 '수석 보험 데이터 엔지니어'이자 'OCR 후처리 전문가'입니다.\n", - "당신의 임무는 제공된 [보험 문서 이미지]와 선택적으로 제공되는 [기초 OCR 텍스트]를 분석하여, 데이터베이스 적재가 가능한 완벽한 형태의 'Standardized Markdown Table'로 변환하는 것입니다.\n", - "\n", - "# Core Principles\n", - "1. **구조적 완전성(Structural Integrity):** 시각적으로 병합(Merge)된 셀은 반드시 비정규화(Denormalization)하여 모든 행에 값을 채워야 합니다. 빈칸이나 \" 상동\" 등의 표현은 금지됩니다.\n", - "2. **헤더 평탄화(Header Flattening):** 2행 이상의 계층적 헤더(Multi-row Headers)는 상위 헤더와 하위 헤더를 언더스코어(_)로 연결하여 단일 행(Single-row) 헤더로 변환합니다. (예: '보장내용' 하위에 '지급금액'이 있다면 -> '보장내용_지급금액')\n", - "3. **데이터 무결성(Data Integrity):**\n", - " - 금액의 천 단위 구분자(,)는 제거합니다. (예: 1,000,000 -> 1000000)\n", - " - 퍼센트(%)는 기호를 포함한 문자열로 유지합니다. (예: 98.5%)\n", - " - 괄호 안의 보조 정보(예: 전년 대비 증감액)는 무시하고 핵심 수치만 추출합니다.\n", - "4. **Hybrid Reference:** [기초 OCR 텍스트]가 제공될 경우, 이미지 내 텍스트가 흐릿하거나 불분명할 때 해당 텍스트를 참조하여 오타를 교정하십시오. 단, 표의 구조(행/열 위치) 판단은 반드시 [원본 이미지]를 기준으로 합니다.\n", - "======================================================================\n" - ] - } - ], - "source": [ - "# System Prompt 확인\n", - "print(\"📋 System Prompt:\")\n", - "print(\"=\" * 70)\n", - "print(SYSTEM_PROMPT)\n", - "print(\"=\" * 70)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "3bd4aaa6", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "📋 User Prompt Template:\n", - "======================================================================\n", - "# Task Description\n", - "아래 제공된 입력을 바탕으로 보험 테이블 데이터를 추출하십시오.\n", - "\n", - "# Input Data\n", - "1. **Target Image:** [첨부된 이미지]\n", - "2. **Reference OCR Markdown (Optional):**\n", - "\"\"\"\n", - "{ocr_markdown}\n", - "\"\"\"\n", - "\n", - "# Step-by-Step Instructions (Chain-of-Table)\n", - "단계별로 생각하고 실행하십시오:\n", - "\n", - "**Step 1. 구조 분석 (Structure Analysis)**\n", - "- 이미지를 보고 표의 전체적인 레이아웃을 파악하십시오.\n", - "- 헤더가 몇 개의 행(Row)으로 구성되어 있는지 확인하십시오.\n", - "- 세로로 병합된(Vertically Merged) '구분'이나 '기간' 컬럼이 있는지 식별하십시오.\n", - "\n", - "**Step 2. 헤더 처리 (Header Processing)**\n", - "- 다중 행 헤더를 단일 행 키(Unique Key)로 변환하십시오.\n", - "- 예:\n", - " | 구분 | 해지환급금 |\n", - " | | 금액 | 환급률 |\n", - " -> | 구분 | 해지환급금_금액 | 해지환급금_환급률 |\n", - "\n", - "**Step 3. 데이터 추출 및 채우기 (Extraction & Filling)**\n", - "- 각 행(Row)의 데이터를 추출하십시오.\n", - "- **중요:** 병합된 셀은 해당 범위에 속하는 모든 행에 동일한 값을 반복 입력(Repeat Value)하십시오. 절대 빈 칸으로 두지 마십시오.\n", - "- OCR 참고용 텍스트가 있다면, 숫자의 정확성을 검증하는 데 사용하십시오.\n", - "\n", - "**Step 4. 포맷팅 및 검증 (Formatting & Verification)**\n", - "- 금액에서 '원', ',' 제거 / 정수형 변환.\n", - "- 출력 전, 헤더의 컬럼 개수와 데이터 행의 컬럼 개수가 일치하는지 확인하십시오.\n", - "\n", - "# Output Format\n", - "설명이나 사족 없이 오직 **Markdown Table** 만 출력하십시오.\n", - "\n", - "| 헤더1 | 헤더2_서브1 | 헤더2_서브2 | ... |\n", - "| :--- | :--- | :--- | ... |\n", - "| 값1 | 값2 | 값3 | ... |\n", - "======================================================================\n" - ] - } - ], - "source": [ - "# User Prompt Template 확인\n", - "print(\"📋 User Prompt Template:\")\n", - "print(\"=\" * 70)\n", - "print(USER_PROMPT_TEMPLATE)\n", - "print(\"=\" * 70)" - ] - }, - { - "cell_type": "markdown", - "id": "efe2d4d4", - "metadata": {}, - "source": [ - "## 3. 추출기 초기화\n", - "\n", - "API 키 설정 확인 및 추출기를 초기화합니다." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "4d6f9bcb", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✅ API 키 설정 파일 확인됨: /Users/jaehyeokchoi/Desktop/chois_toy/private/TableMagnifier/apis/gemini_keys.yaml\n" - ] - } - ], - "source": [ - "# API 키 설정 파일 확인\n", - "config_path = project_root / \"apis\" / \"gemini_keys.yaml\"\n", - "\n", - "if not config_path.exists():\n", - " print(f\"❌ API 키 설정 파일이 없습니다: {config_path}\")\n", - " print(\"\\n다음 단계를 수행하세요:\")\n", - " print(\"1. apis/gemini_keys-example.yaml을 apis/gemini_keys.yaml로 복사\")\n", - " print(\"2. 실제 Gemini API 키를 입력\")\n", - " print(\"3. Google AI Studio에서 무료 API 키 발급:\")\n", - " print(\" https://makersuite.google.com/app/apikey\")\n", - "else:\n", - " print(f\"✅ API 키 설정 파일 확인됨: {config_path}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "08823fe6", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2025-12-02 14:27:15,381 - polling_gemini.api_pool - INFO - 총 3개의 API 키를 로드했습니다.\n", - "2025-12-02 14:27:15,382 - polling_gemini.api_pool - INFO - API 키 'key1' 사용 중 (모델: gemini-2.5-flash)\n", - "2025-12-02 14:27:15,382 - polling_gemini.api_pool - INFO - API 키 'key1' 사용 중 (모델: gemini-2.5-flash)\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✅ InsuranceTableExtractor 초기화 성공!\n", - "\n", - "현재 사용 중인 API 키: key1\n", - "총 API 키 수: 3\n", - "\n", - "전체 API 키 상태:\n", - " - key1: 활성화=True, 실패횟수=0\n", - " - key2: 활성화=True, 실패횟수=0\n", - " - key3: 활성화=True, 실패횟수=0\n" - ] - } - ], - "source": [ - "# InsuranceTableExtractor 초기화\n", - "try:\n", - " extractor = InsuranceTableExtractor()\n", - " \n", - " # API Pool 상태 확인\n", - " status = extractor.get_pool_status()\n", - " \n", - " print(\"✅ InsuranceTableExtractor 초기화 성공!\")\n", - " print(f\"\\n현재 사용 중인 API 키: {status['current_key']['name']}\")\n", - " print(f\"총 API 키 수: {status['current_key']['total_keys']}\")\n", - " \n", - " print(\"\\n전체 API 키 상태:\")\n", - " for key in status['all_keys']:\n", - " print(f\" - {key['name']}: 활성화={key['enabled']}, 실패횟수={key['failed_count']}\")\n", - " \n", - "except FileNotFoundError as e:\n", - " print(f\"❌ API 키 파일 오류: {e}\")\n", - "except Exception as e:\n", - " print(f\"❌ 초기화 실패: {e}\")" - ] - }, - { - "cell_type": "markdown", - "id": "41a30785", - "metadata": {}, - "source": [ - "## 4. 샘플 이미지 확인\n", - "\n", - "테스트할 이미지 파일을 확인합니다." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "dd007a21", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "📁 샘플 이미지 디렉토리: /Users/jaehyeokchoi/Desktop/chois_toy/private/TableMagnifier/Table_example/sample_images\n", - "📷 발견된 이미지: 1개\n", - " 1. I_table_78.png\n" - ] - } - ], - "source": [ - "# 샘플 이미지 디렉토리 확인\n", - "sample_images_dir = Path.cwd() / \"sample_images\"\n", - "\n", - "if not sample_images_dir.exists():\n", - " sample_images_dir.mkdir(parents=True, exist_ok=True)\n", - " print(f\"📁 샘플 이미지 디렉토리 생성됨: {sample_images_dir}\")\n", - "\n", - "# 이미지 파일 검색\n", - "image_extensions = [\"*.png\", \"*.jpg\", \"*.jpeg\", \"*.gif\", \"*.webp\", \"*.bmp\"]\n", - "image_files = []\n", - "for ext in image_extensions:\n", - " image_files.extend(sample_images_dir.glob(ext))\n", - "\n", - "print(f\"\\n📁 샘플 이미지 디렉토리: {sample_images_dir}\")\n", - "print(f\"📷 발견된 이미지: {len(image_files)}개\")\n", - "\n", - "if image_files:\n", - " for i, img in enumerate(image_files, 1):\n", - " print(f\" {i}. {img.name}\")\n", - "else:\n", - " print(\"\\n⚠️ 테스트할 이미지를 sample_images 폴더에 추가하세요!\")" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "7f0f1733", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "📷 첫 번째 이미지 미리보기:\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAx4AAAJJCAIAAACrpHaSAAAACXBIWXMAAC4jAAAuIwF4pT92AAESa0lEQVR4nOydd3zUxpv/1y3Yxj4M2GCKbXo9Wqjm6AS+4QiB0ENNKOE4CC0QYgK86AcBQif0knChEyAGQigBQjE1lAABQjlKCIRiujEG/z63c9FvvyvpWUkr7drL8/6DF1YZzTwrzbwljWZsfn5+NoZhGIZhGMYsypQp8w3DZHLy589frlw5b+fCQ/Ts2RNX7tSpU72dkQxK69atER9v5yLjUq9eveDgYG/nIuOCmgT1ibdzkXEpWrTo61PZ6uWtt976P7X6xz/+kc4wmZx//dd//fd//3dv58JD/Pd//zeu3P/5n//xdkYyKP/1X/+F+Hg7FxmX//iP//iXf/kXb+ci44KaBPWJt3ORcfm3f/u316ey1ct//ud/sloxvgOrFSPBakXDakXDakXDakXAasX4FKxWjASrFQ2rFQ2rFQ2rFQGrFeNTsFoxEqxWNKxWNKxWNKxWBKxWjE/BasVIsFrRsFrRsFrRsFoRsFoxPgWrFSPBakXDakXDakXDakXAasX4FKxWjASrFQ2rFQ2rFQ2rFQGrFeNTsFoxEqxWNKxWNKxWNKxWBKxWjE/BasVIsFrRsFrRsFrRsFoRsFoxPgWrFSPBakXDakXDakXDakXAasX4FKxWjASrFQ2rFQ2rFQ2rFQGrFeNTsFoxEqxWNKxWNKxWNKxWBKxWjE/BasVIsFrRsFrRsFrRsFoRsFoxPgWrFSPBakXDakXDakXDakXAasX4FKxWjASrFQ2rFQ2rFQ2rFQGrFeNTsFoxEqxWNKxWNKxWNKxWBKxWjE/BasVIsFrRsFrRsFrRsFoRsFoxPgWrFSPBakXDakXDakXDakXAasX4FKxWjASrFQ2rFQ2rFQ2rFQGrlRdITk5Go6h3r7Nnz65fv96K/PgSmUWt1qxZ06dPH+nPO3futGnTRq8kvSZqZThWr4NaPX36tF27dgcPHpSWzJkzZ/LkyVr2fR3Uyp34vA5q5U58Xge1MhwfVitNXLlypU6dOlX0ULt2bbQHf/75pzy1KVOmIOBHjhzRlYdOnTr5+/s/f/7cpDL5JtrV6sGDBzNmzFCL5+XLl7H21atXW7du3bdvn1oiixYtOnXqlNraHj16dO/eXXFVs2bNcA6kpaWJPzdu3Ig/9Qo3rVYeKKAWHj58OG3aNKmkalgRK1qtvBgflOXHH39E9oYPH75gwYI//viD3h63VQULFjx9+rR81YkTJ1DGoUOHSksqVKhQpEgRLdmg1UpLwTXGUEtmHIE9L1y4EIUaM2bMhg0bXFZ6FsWHVisvxseJ7777bt26dcQGFsWHVquMEx+XWBEfVitNHD58GFpj009sbOzjx4+dUhs9ejRW/fTTT7ry8O6772KvR48emVYqX0S7WqHWRjyXLFmiuLZ169ZYm5SUFBQUVKBAAcXL+8yZM9jm7bffVjtE0aJFixUrprhK/JovXrwQf6JyxJ/ffPONlpxL0GrlgQJqYeXKlUhEsdpyxIpY0WrlrfggTVTNjrVEYGDgsGHDiCbk22+/xWbLly+Xr/rll1+wasiQIdKSsmXLFipUSEtOCLVCZrQUXEsMHe/4tQDXDAkJcYwPikPfiFoUH0KtvBgfJ3A6IZHcuXMT21gUH0KtvBUfJJVLBRTq4sWLintZER9WK63Aaf7Sw9WrV5s3b47ALl682CkpoVaQ+md6eOedd1itXKJdrXBHhXiidpCvws+HegGtzpMnT9566y1stn37dvlm/fr1w6qxY8eqHSJ//vxqVbMH1MrSAu7du/fKlStaMondkQhqKHozK2JFq5VX4oM79dDQUOxVpkyZPn36fP7556gl3njjDSzp37+/WlYXLlyIDdauXStfZZFaAS0F1xhDp1UPHjxITExUPKg4pUH9+vURHLRPODHwZ/bs2S9duqSWVYviQz+18kp85KDhxiHoF7sWxYd+amVdfIjK57PPPlN7zOHv769WC1kRH1YrCxHnTUJCgtNyoVbGYLWi0dXXqkSJEgjpb7/95rR80qRJWN6rVy/8f8WKFfh/mzZtnLZJSUnJkSNHQEDAzZs31dKPioqqUqWK4ioPqFW6ZQVMS0sLDAxs1KiRlkzGx8drUSsrYuWyr5Xn44N6GalNmTLFceHp06ejo6Ox/MCBA4r5nD17NtZu2bJFvso6tdJYcC0xdGLUqFFYdejQIaflaE2zZcuGJnDlypXSQtxViqcXzZo1U8uqRfGh1crz8ZFz+PBh0S7QamVRfGi1sig+dOXTrl077HX8+PFrMu7evauWVSviw2plIeJXGTBggNNyoVZxcXGl9BAWFsZq5RJdajV58mSE9JNPPnFaXrx4cSw/efIk/v/8+fOcOXO+8cYbTlemeIb83nvvEemjvlN7W+QZtbKogGjtsKpWrVp09lJTU4cNGyaqF5dqZUWsXKqVh+Ozbds2LG/RooV8l8TERKxq27atYj5F70zFNyPWqZXGgmuJoROffvqpTalHxPjx47F80KBBTsufPHkSGRnp5+d3/fp1xaxaFB9arTwfHzlNmjTRolYWxYdWK4viQ1c+NWvWxBH1ds+yIj6sVhZCqxX3tbICXWolnjlHRUVBAqSFu3fvRpyrVasmLRGPr6dNm+a4b506dWz2t7pqib98+RKNQfv27RXXekatLCqgS7Xat28f1qI5lJ620mplUaxcqpWH4yPS2blzp2JmYmNj0UAqtgrDhw/Hjr///rt8lXVqla6t4Bpj6IiaOqCpxnLFx8AooE392wWL4uPyC0EPx8eJ48ePYzMoSIECBejf0aL4uPxC0Ir40JVPXFycxj74jlgRH1YrC7FCreSd4hlH9A6+0KpVK9s/v2VHA2/75x5yotOlYyV74cIFLFHrpCm4ffu24t2YwDNqlW5NAV2q1YoVK1Bd+tvRolYWxUrL4AuejI9Qh6dPnyrmRLz2UuxR1LNnT7Vr31K10lhwLTF0RE0dIiIi1D5lEB9DDBs2THGtRfFxqVYejo8TLVq0wGZLly5Fcejf0aL4uFQrK+JDVD64QwsMDKxXr56WzDtiRXxYrSzk2LFjCCyuE6flsHib+mcRiuCkKVmyZEhIiKkZ9EH0qtWPP/5oc/jI6969e1myZMmWLZtT+yc6DElPjAcNGoQ/x40bR6R8+vRpbPPll18qrvWYWllRQI0vBAUfffSRS7WyKFZa1MqT8UGNTHzJ1b9/f5v9m0T5qpYtW4aHhyvuZalapWsruMYYSqipQ0BAQM2aNRV32bNnD3bp16+f4lqL4qNlXCtPxscRcckULFgQ14VLtbIoPlrGtTI9PkTlc+PGDaz68MMPtWTeESviw2plIXv37kVgR40a5bT80KFDWA5PwvlRTxtFixbFLg0aNPBKQTIRetUKN0+4hfLz87t69Wr639Yr7zu5aNEiLO/WrVu6vQtRrly56A7sYOfOndjFsUOuIx5TKysKaLpaWRQrLWrlyfhERkYSbytEq7Nr1y75KghHiRIlFPeyWq20FFxjDCXU1KFatWpqz6XEYGZjxoxRXGtRfLSolSfj48j777+PbebNm5duV3b6d7QoPlrUyvT4EJXPgQMHsGrkyJFaMu+IFfFhtbIQIeOKI7dC2wMDA216wG9/7tw5z5cic2FgNHbxflZckKVLl7Yp9Z188uQJbmvCwsIeP368Zs0abNO8eXM62fnz59vUB2XxmFqlW1BA09XKolhpHI3dY/GpUaOGWn+y9L8/blIcgDRv3rxqffytViuNBdcSQwnt3bSd0l+6dKniWovio0WtvBKf8+fP+/v758+fXwy26VKtLIqPFrUyPT5E5SPeGqu9ZiWwIj6sVhayZMkSBHbFihWKa3FVXLx48YI2FEd1Z+QYUKvr16+jnoqNjRVPGdX6lgpFWLhwYYMGDWxkB3bBwIEDsdmtW7cU13pSrUwvoOlqZVGsNKqVx+JD9MxD6aKjo0NCQqRiSqBxQmrQIMUdrVardG0F1xhDgV61SktLK1asGNK/ceOGfK118dE40Y3n49O5c2dsMH36dPEnrVbWxUfjRDfmxoeofMR4DTt37kxOTp42bVrr1q1btGgBabt27RqRPYviw2plIeKrlmPHjnk7I68RxuYQxC42+7c2NvU+cGIIGVTxfn5+BQsWdPl9b9OmTQMCAl6+fIn///rrr9/8M2+++abH1Mr0ApquVhbFSvscgl6Mj2DmzJnYRfGZlvgQTPQrSElJWbt2rWNwxIislqqVxoJriaFAl1rhcB9//DG2b9KkieIG1sVHo1p5OD6XLl3CxZIrVy6cZmIJrVbWxUejWpkbH+LiEucJCiVGiZMIDQ0l5sWyKD6sVhYCSQ8KCpIPJstYhzG1Wr9+vbgKiL6loFy5cmIzugO7oHDhwthy2bJl+H/lypVtSnhMrcwtoOlqZVGstKuVF+OTbu+jnSVLljfeeOP8+fPytQiLaJnS0tI2b96sGBxL1SpdW8E1xjBdm1o9fvx4w4YNkydPrlixIjYODw9X/DY+3cr4aJ+e2ZPx6d69O9Z+8cUX0hJarayLj/bpmU2MD3FxiclGcSnB4Tp37rx8+fLZs2fXqFEDC7Fk48aNiglaFB9WK6uAUaGuNPAhKOMOxtRKvI6xkX1L0/9+tOCyA3u6/SMXcVnhLi01NXXHjh0jRowY7gCuZJsH1crNAqLGmeiAUBYUzXEhWkHFER1dqpV1sdKuVl6Mz9atW7NmzUqUqE+fPiI+CxYsgHBMmDDBMTgivFarlZYzXy2Gd+7cmTp1qmMoxMhGPXr0cFy4Zs0ax73E0wIJpKB2XOvio12tPBafa9eu4XY9R44cjgMc0mplXXy0q5U78dF+cYnH29mzZ9+9e7djCvA5LEfiDx48kB/XoviwWlnFnDlzbPYXzI4LZ8yYgZuwN90DGk5Mp/WaY0yt0v/uO+l4LyhHdMOMiIhQm6pdAu0lthTTPqJakW/gyb5WAncKGBMTo3gz54TiB1wu1cq6WGlXq3QvxWfevHlob3BLjWpd7aDVqlXLnz8/7vvz5csnvQOS8EBfq3TNZ75iDMU3Yi4JDAx0fFX0008/NWjQoEKFCsHBwWIDVJ6KB7UuPtrVymPxgXbg/6NHj3ZMgVYr6+KjXa3ciY/2i6t48eL+/v579uyRpywGjXMavFRgUXxYrVxw48aNkiVL5tWPiGrOnDnFn1Dj9L/HRnMTnD379+/3dmAyKMbUSsweb7O/6Sc2EzOh2tQ/TZAQs4CdP38eVyzullJSUpw28LBauVlA3CEMcKBv377YBjWR48KBAweePn1anqZLtbIuVtrVyvPxQTMpRluAOqxbt07tiGiKsEG7du2wjWLb4Bm10nLmq8UQ5ydK6hgK8c4XlaHjQrXnUmjtvvrqqyxZssBB5V+NWRof7WrlmfjcvHkTccBPlpyc7JgCoVaWxke7WrkTH+0XF8q4evVqxcTFUKUVK1Z0Wm5dfFitXHD16tXs2bP7k0jGQ9CxY8d0+8if90l69OiBpE6cOEFswwOyExhTqy5dutjsL+nx7969exW3QQ3o5+cntqlfvz6dYM2aNfPkyYP/iCt29uzZTht4WK3MLaC5fa2si5V2tfJwfFJTU8W4RJGRkYpjhEqIEb/mzZsHFRM31k73/R5QK40F1xJDgYHBFxYvXmxTmmPR0vhoVCuPxUcMKutYFgGhVpbGR6NamRsfA9+ICFB82LnTcynr4sNq5S7il4b2mpKaeO97+fJlU1J7DTGgVg8fPsyaNWt4eDjuCxH8Tp06KW4mZhoeOXKkeEB98eJFtQQfPXoUGBgovvbCFYubsLi4OKeP6j2pVqYX0ES1sjRWGtXKw/FBMaEINnt/EdxM03kbPHgwthSbiZ/b6dWhB9RKS8E1xlBgQK1wU4qjh4aGOrV8lsZHo1p5Jj63b98OCQlBBO7cueO0PaFWlsZHo1qZGx/DavXhhx+KxxaOC62LD6uVu7BaZSgMqNXcuXNt9nFN0MyHhYUFBwc7PW9Pt9fsuKHBTc+NGzdGjBiB7RMSEtQSFG2/NLa4SF98/ibhSbUyvYAmqpWlsdKoVh6Oz+eff461RYsWdfkxRLq9HpfGiUYE0DgVKVLEsU+S1WqlseBaYihhQK1Ahw4d5E2jpfHRolYei4+QAPmMtOmkWlkaHy1qZXp8DKuVCKDTbAfWxYfVyl1YrTIUBtSqUqVKUtsvPmyeNWuW0zabNm3CcrTx6faPdPz9/aOjo+WjOwpat24dFBQkfY2CMyQyMrJq1aqO23hSrUwvoIlqZWmsNKqVJ+Oze/duPz8/7EsPYyg4e/Ys0kGTIC2ZPHkylvzwww/SEqvVSmPBtcRQQlEdbt261bNnz7/++kttr88++wx7bdmyRVpidXy0qJVn4gNBwXVhs382OFxGRETEG2+8If4vjSOabn18tKiV6fExrFYIjlPZLY0Pq5W7GFCrS5cu4QpJTU2Vr2K1chO9aiXGi8OFLf48evQo/ixXrpzTZk2bNsVyVBPiz8aNG+PP9evXyxN8/PhxSEgINnBcKEYbR+LSEo+plekFTDdPrayOlRa18mR80ECiXoZa0V1tJERjcOTIEWnJ3bt3s2TJ8s4770hLrFYrLQXXGEMJRbVauHAhFi5atEhtL/G5g+OhrY6PFrXyTHzQWGifGE2ausPq+GhRK9PjY1itRE81x0vP0viwWrmLAbX64osvsMuBAwfkq4RaXblyxbwMvl7oVStxAcyfP19aUqFCBSw5fPiwtOTmzZsBAQExMTFiuPD0vyeLVTzQvHnzsCoxMdFx4fnz57GwS5cu0hKPqZXpBUw3T62sjpUWtfJkfDZs2GBTn1LDibS0NBxR/k3T+++/DzmTqghL1UpjwbXE0BFFtVq1ahUWTpw4US0zomncuXOn+NMD8XGpVp6Mz9q1a4eoIJ5aif9LD3s8EB+XamVFfIjKh/7A67333rP93a0q3fr4sFq5iwG1ElNRKnY1mDlzZmRkJD1WL0OgS60Q52zZsompQ6WFYkCy7t27S0vEiHOOE6rjssyXL5+/v7/8tU7p0qVjY2OlqkSidu3a4eHh0vcpnlErKwqYbp5aWR0rl2rl4fg0adIEtbbGcchWr15tk3WqBbt27cLysWPHij8tVSstBdcYQ0cU1QG3msQu4K233sIG0pB+HoiPS7XyZHwIFPtaeSA+LtXKivioXVxoVRGE+/fvq2UmLi4uODhYqm2sjg+rlbuYq1bp9pE2zMnZa4kutVq6dCl+CLT9jgvFtyrSpf7q1StcSKgLnIbSFp+9iJmnJL7//nssnDBhgvxYYmBAadgVz6iV6QUUmKJWHoiVS7XyZHxSU1NDQ0Pj4+NdZlscFPfTOXLkkE+ThVVoJEqVKiX+tE6tNBZcSwydUFQHBC0oKKhIkSKKmblz584bb7yB204pbx6ID61WHo4PgVytPBMfWq0sio9a5SN6aKm9UD506BDWvv3221LerI4Pq5W7mK5WjDvoUitUDbZ/7tYj6Nq1q+3vkfS3b99us48V7rTN1atX/fz8HB+64IYMF2R0dLTirJGoLHDP1LRpU/GnZ9TK3AJKuK9WnomVS7XyZHyOHDmChWg1XWYbLF++HBt/+eWXimsTEhKw9vjx4+lWqpXGgmuJoRNq6iAm6N28ebN8l969e9scpkDxTHxotfJ8fNSQq5Vn4kOrlUXxUat8vv32W5t9UFDFj0vEmKVSpeGB+LBauYsBtRozZgx2wZlnXa5eW7Srlfg8pEKFCvJV4hZHfKcmZkhQnNqzUaNGNofPScTlOmfOHLUjtmnTJiQkRPzfA2plegElnj9/juV169bVkklFtfJMrGi18nB8RJEXL178Qh3J0ooVK4ZbZ7UH2CLn4oGfdWqlpeAaY+iE+NZPPiHJjh07sDxPnjxOw31Nnz7dZh+2XuoB45n40Grl+fioIVcrz8SHViuL4qNW+WA5dM1mf43oaFe4psSXMWICZrHQA/FhtXIXA2o1f/58m6vZyhhjaFcr0StWrXUvX768qN2CgoLy5csnXZOOiKnamzdvLv5cu3Yt/q/2QT44c+aMNKaLB9TK9AI6groG5dWSSUW18kysaLXycHzEWIg0gYGBYmbZevXqbd26lSgabs/El00WqdWdO3e0FFxLDOWz0yDPXbt2VXxgKZ5VZM2aFf+BUY0bNy4+Pl4Ex/Fdj2fiQ6iVt+KjiFytPBMfQq0sjY9a5bN3717cj9nsg8ah9pg1a9awYcPEvIRYLp5CCTwQH1YrdzGgVmgLxZe00dHRsXqAa586dcq6svgA2tUKW6IGf/jwoeLa2bNn4wfq3Lkz/sX1qbgNao28efMiEQP59IBaebeAEuLtBm4EDadgkVp5OD4iMzT+/v47duzQkpqERWqFpktLwbXEcNKkSVoyI8CvLFzckezZs3/77bfaE3HEIrXyVnwUQbuQM2dOY/tapFbeik9SUlKRIkWczh8U59ChQ9oTcYTVymukpKQgeh06dNC1FxrFuLg4etpBOTgL1b7XZQTa1WrZsmWLFy9WW/vo0aPBgwdfunQJzc/t27fVNlu3bp00krgumjVrhtNGuqUT3yTjrNCVCK1W3i2gBG46+/bt6zjAsV4Mx4pWqwwSHzc5ceIEyjh06FBpSYUKFdQ6gztBqFVycrKWgmuJoXxiFpccP3585MiR3bt3h5cvX74c6ehNQcKd+BBq5d34OIHbjHnz5hnb1534EGrlxfigokAVMWjQIJw/KNemTZsUn5xpxHB8WK1MoGXLlsSE3ownMTY9s+dZsGBB69atpT9v3bpVtWpVvUPFap+eOVNjOFbap2fOvDx58gQtnHiNKBg3bhwxCY8jGucQzNS4Ex+NcwhmatyJj8Y5BDM1huPDasX4FJlFrUzhNVErw7wOauUOr4NaucProFbu8DqolWFYrRifgtWKkWC1omG1omG1omG1ImC1YnwKVitGgtWKhtWKhtWKhtWKgNWK8SlYrRgJVisaVisaVisaVisCVivGp2C1YiRYrWhYrWhYrWhYrQhYrRifgtWKkWC1omG1omG1omG1ImC1YnwKVitGgtWKhtWKhtWKhtWKgNWK8SlYrRgJVisaVisaVisaVisCVivGp2C1YiRYrWhYrWhYrWhYrQhYrRifgtWKkWC1omG1omG1omG1ImC1YnwKVitGgtWKhtWKhtWKhtWKgNWK8SlYrRgJVisaVisaVisaVisCVivGp2C1YiRYrWhYrWhYrWhYrQhYrRifgtWKkWC1omG1omG1omG1ImC1YnwKVitGgtWKhtWKhtWKhtWKgNWK8SlYrRgJVisaVisaVisaVisCVivGp2C1YiRYrWhYrWhYrWhYrQhYrRifgtWKkWC1omG1omG1omG1ImC1YnwKVitGgtWKhtWKhtWKhtWKgNWK8SlYrRgJVisaVisaVisaViuC/69W5cqVW8UwmZyYmJgKFSp4Oxceok+fPrhyZ8+e7e2MZFDatWuH+Hg7FxmXBg0ahISEeDsXGRfUJKhPvJ2LjEvx4sVfn8pWL//4xz9sDMMwDMMwjMnEx8cfYZhMTuHChf/t3/7N27nwBAMGDIiIiMCVm5iY6O28ZFB69+6N+Hg7FxmXFi1aZM2a1du5yLigJkF94u1cZFzKlSv3mlS2BmjVqtX/qRX3tWJ8gNehr9WrV68SEhJQ0lmzZtm4r5U63NeKhvta0XBfKxrua0XA3dgZn8Ln1SotLa179+7Vq1e/d+8ed2OnYbWiYbWiYbWiYbUiYLVifArfVquUlJTmzZujgE+ePEnnLwRdwWpFw2pFw2pFw2pFwGrF+BQ+rFYPHz6sW7du+/btU1NTxRJWKxpWKxpWKxpWKxpWKwJWK8an8FW1un37dsWKFfv06fPq1StpIasVDasVDasVDasVDasVAasV41P4pFpduXKlWLFio0ePdlrOakXDakXDakXDakXDakXAasX4FL6nVqdPn46NjZ0zZ458FasVDasVDasVDasVDasVAasV41P4mFolJSXlzp171apVimtZrWhYrWhYrWhYrWhYrQhYrRifwpfU6ocffsiVK9f27dvVNmC1omG1omG1omG1omG1ImC1YnwKn1Gr5cuXR0dHHzp0iNiG1YqG1YqG1YqG1YqG1YqA1YrxKXxDrWbOnBkbG3v27Fl6M1YrGlYrGlYrGlYrGlYrAlYrxqfwAbUaMWJE8eLFr1696nJLVisaVisaVisaVisaVisCVivGp8jUavXy5UtckJUqVbpz546W7VmtaFitaFitaFitaFitCFitGJ8i86rV8+fP27RpU79+/UePHmnchdWKhtWKhtWKhtWKhtWKgNWK8SkyqVo9fvy4YcOGLVu2hGBp34vViobViobViobViobVioDVivEpMqNa3b17t0qVKh999FFaWpquHVmtaFitaFitaFitaFitCFitGJ8i06nV9evXS5Ys+fnnnxvYl9WKhtWKhtWKhtWKhtWKgNWK+V9Onz6dkJDw4sULwymsW7du4cKFJmbJGJlLrX777be4uLipU6ca253ViobViobViobViobVioDVivlf6tevj3MAgmU4heLFiwcFBV25csXEXBkgE6nV4cOH8+TJs2zZMsMpsFrRsFrRsFrRsFrRsFoRZD61SkhI+Pbbb81K7fvvv0cIzErNLI4fP96xY8eHDx/KVyUnJ6NB1Zvg2bNn169fr7Z2165dOAGKFSv28uVLvSlLDBkyBIl069bNcAqmkFnUaseOHbly5UpMTHQnEVYrGlYrGlYrGlYrGlYr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QED58uXbt2+PEwPR+/LLL+FbEydOHDFiRM+ePRs1aiR96tikSRPFT0rTfVSt6tSpgyjJl7Na0bBa0bBa0bBa0bBaEXjuC8GnT59+8skn4qmME1CKH374QZ4UoVbNmzfX+8ikUKFCpjxLGzp0KFJDQfz8/DZs2KC2mXa1QtOYkJAQHBxss48jdf36dWQV/y9WrNjSpUufPXvmfp4J9u/fj2PNmjXLabmBvlbiw0m4td483Lt3b8KECRUqVKDH1IiIiGjZsiXdxc331AoSGR4ejstHvorViobViobViobViobVisBDaoWGoXr16liOK7lXr17z5s3bvHnz2rVrp06d2qBBA5GBuXPnYpXjB3H58uVTVKszZ85geZEiRbD7VxpAyhs3btQylrdLXr16Be/Jli3byZMnIyMjIViHDh1S3FJNrV6+fIm28MCBA19//TVcs2LFisInoFaTJ08W29y+ffu9994TYcmaNWuTJk3Gjx8P+zx37pzpprV161YcBZlxWm5ArRBn7JI9e3bDz9vu37+flJS0fPlyqN7EiRPHjRs3ZcoUcbYgPwidyxR8T61WrVqlViJWKxpWKxpWKxpWKxpWKwIPqZV40tOuXTvFgY6OHTsWHR2t9sRCrlYbNmxQfNDiARITE3Hovn37pttHy8ySJUuuXLkuXbok31JU61WqVGlop3379mLQc9EF2xEoGhxL3il+165drVq1cnrOBwOTfynmDmvWrLH984eZAgNqVadOHZFJmJCJOdSF76lVp06dZs+erbiK1YqG1YqG1YqG1YqG1YrAQ2pVsGDB3LlzP3/+XG2vlStX2uzjmzt22xIPtORqtXr1aixfvHixroyZQr169aCA586dE38uWrQIOcHll5KS4rSlqNYlAgICRPeg8ePHZ8uWrWTJkgjUiBEjdu/eTT/jefLkyapVq/r379+4ceOiRYvmzJlTOropiCLI37LpVSvx9Eu4YIUKFbQ8YbICH1MrhDEqKkpNnlitaFitaFitaFitaFitCCi1GjNmDJZv375dV4qINfZyevsWGhpKd8ER7/gGDBjguFCtr5W31AqWieO+//77jgs//PBD6TmWI6JaR+N31o7TFCUZhyFDhiCfcl3TpVY3btyIjY2FPuJ37NOnD3ZEsvQu+BGNjZvqSIcOHf7880/HZH1MrZKSkojKndWKhtWKhtWKhtWK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", - "text/plain": [ - "" - ] - }, - "metadata": { - "image/png": { - "width": 600 - } - }, - "output_type": "display_data" - } - ], - "source": [ - "# 이미지 미리보기 (IPython 사용)\n", - "if image_files:\n", - " from IPython.display import Image, display\n", - " \n", - " print(\"📷 첫 번째 이미지 미리보기:\")\n", - " display(Image(filename=str(image_files[0]), width=600))" - ] - }, - { - "cell_type": "markdown", - "id": "43bd0f11", - "metadata": {}, - "source": [ - "## 5. 기본 테이블 추출\n", - "\n", - "이미지에서 테이블을 추출합니다." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "101765ab", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "🎯 추출 대상 이미지: I_table_78.png\n" - ] - } - ], - "source": [ - "# 추출할 이미지 선택\n", - "if image_files:\n", - " # 첫 번째 이미지 사용\n", - " target_image = image_files[0]\n", - " print(f\"🎯 추출 대상 이미지: {target_image.name}\")\n", - "else:\n", - " print(\"❌ 이미지가 없습니다. sample_images 폴더에 이미지를 추가하세요.\")\n", - " target_image = None" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "8b4d4f8a", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "🚀 테이블 추출 시작: I_table_78.png\n", - "======================================================================\n", - "\n", - "✅ 추출 완료!\n", - "\n", - "📊 결과 (Markdown Table):\n", - "----------------------------------------------------------------------\n", - "| 구분 | XX세 | XX+1세 | XX+2세 | XX+3세 | XX+4세 | XX+5세 |\n", - "| :--- | :--- | :--- | :--- | :--- | :--- | :--- |\n", - "| 나이증가분(A) | | 1059 | 1357 | 1739 | 2229 | 2855 |\n", - "| 보험료 산출 기초율 (위험률 등) 증가분 (B=전년도 기준보험료의 최대 25% 가정) | | 10846 | 13897 | 17806 | 22815 | 29232 |\n", - "| 기준보험료 (C=전년도 기준보험료+A+B) | 42325 | 54321 | 69485 | 89030 | 114074 | 146161 |\n", - "----------------------------------------------------------------------\n", - "CPU times: user 21.5 ms, sys: 14.8 ms, total: 36.3 ms\n", - "Wall time: 14.7 s\n", - "\n", - "✅ 추출 완료!\n", - "\n", - "📊 결과 (Markdown Table):\n", - "----------------------------------------------------------------------\n", - "| 구분 | XX세 | XX+1세 | XX+2세 | XX+3세 | XX+4세 | XX+5세 |\n", - "| :--- | :--- | :--- | :--- | :--- | :--- | :--- |\n", - "| 나이증가분(A) | | 1059 | 1357 | 1739 | 2229 | 2855 |\n", - "| 보험료 산출 기초율 (위험률 등) 증가분 (B=전년도 기준보험료의 최대 25% 가정) | | 10846 | 13897 | 17806 | 22815 | 29232 |\n", - "| 기준보험료 (C=전년도 기준보험료+A+B) | 42325 | 54321 | 69485 | 89030 | 114074 | 146161 |\n", - "----------------------------------------------------------------------\n", - "CPU times: user 21.5 ms, sys: 14.8 ms, total: 36.3 ms\n", - "Wall time: 14.7 s\n" - ] - } - ], - "source": [ - "%%time\n", - "# 테이블 추출 실행\n", - "if target_image:\n", - " print(f\"🚀 테이블 추출 시작: {target_image.name}\")\n", - " print(\"=\" * 70)\n", - " \n", - " try:\n", - " result = extract_table_from_image(target_image)\n", - " \n", - " print(\"\\n✅ 추출 완료!\")\n", - " print(\"\\n📊 결과 (Markdown Table):\")\n", - " print(\"-\" * 70)\n", - " print(result)\n", - " print(\"-\" * 70)\n", - " \n", - " except Exception as e:\n", - " print(f\"❌ 추출 실패: {e}\")\n", - " import traceback\n", - " traceback.print_exc()" - ] - }, - { - "cell_type": "markdown", - "id": "bc284731", - "metadata": {}, - "source": [ - "## 6. OCR 참조 텍스트와 함께 추출\n", - "\n", - "OCR로 먼저 추출한 텍스트를 참조로 제공하여 정확도를 높입니다." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "361949ff", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✅ OCR 참조 파일 로드됨: I_table_78.md\n", - "\n", - "📝 OCR 참조 텍스트:\n", - "----------------------------------------------------------------------\n", - "|구분|XX세|XX+1세|XX+2세|XX+3세|XX+4세|XX+5세|\n", - "|---|---|---|---|---|---|---|\n", - "|나이증가분(A)||1,059|1,357|1,739|2,229|2,855|\n", - "|보험료 산출 기초율
(위험률 등) 증가분
(B=전년도
기준보험료의 최대
25% 가정)||10,846|13,897|17,806|22,815|29,232|\n", - "|기준보험료
(C=전년도
기준보험료+A+B)|42,325|54,321|69,485|89,030|114,074|146,161|\n", - "----------------------------------------------------------------------\n" - ] - } - ], - "source": [ - "# OCR 참조 텍스트 로드 (이미지와 같은 이름의 .md 파일에서)\n", - "# 예: I_table_78.png → I_table_78.md\n", - "\n", - "if target_image:\n", - " # 이미지와 같은 이름의 .md 파일 찾기\n", - " ocr_md_path = target_image.with_suffix(\".md\")\n", - " \n", - " if ocr_md_path.exists():\n", - " with open(ocr_md_path, \"r\", encoding=\"utf-8\") as f:\n", - " sample_ocr_markdown = f.read().strip()\n", - " print(f\"✅ OCR 참조 파일 로드됨: {ocr_md_path.name}\")\n", - " print(\"\\n📝 OCR 참조 텍스트:\")\n", - " print(\"-\" * 70)\n", - " print(sample_ocr_markdown)\n", - " print(\"-\" * 70)\n", - " else:\n", - " sample_ocr_markdown = None\n", - " print(f\"⚠️ OCR 참조 파일이 없습니다: {ocr_md_path.name}\")\n", - " print(\" 이미지와 같은 이름의 .md 파일을 sample_images 폴더에 추가하세요.\")\n", - "else:\n", - " sample_ocr_markdown = None\n", - " print(\"❌ 대상 이미지가 없습니다.\")" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "8495b93f", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "🚀 OCR 참조와 함께 테이블 추출: I_table_78.png\n", - "======================================================================\n", - "\n", - "✅ 추출 완료!\n", - "\n", - "📊 결과 (OCR 참조 사용):\n", - "----------------------------------------------------------------------\n", - "|구분|XX세|XX+1세|XX+2세|XX+3세|XX+4세|XX+5세|\n", - "|:---|:---|:---|:---|:---|:---|:---|:---|\n", - "|나이증가분(A)||1059|1357|1739|2229|2855|\n", - "|보험료 산출 기초율 (위험률 등) 증가분 (B=전년도 기준보험료의 최대 25% 가정)||10846|13897|17806|22815|29232|\n", - "|기준보험료 (C=전년도 기준보험료+A+B)|42325|54321|69485|89030|114074|146161|\n", - "----------------------------------------------------------------------\n", - "CPU times: user 17.5 ms, sys: 17.2 ms, total: 34.7 ms\n", - "Wall time: 20.8 s\n", - "\n", - "✅ 추출 완료!\n", - "\n", - "📊 결과 (OCR 참조 사용):\n", - "----------------------------------------------------------------------\n", - "|구분|XX세|XX+1세|XX+2세|XX+3세|XX+4세|XX+5세|\n", - "|:---|:---|:---|:---|:---|:---|:---|:---|\n", - "|나이증가분(A)||1059|1357|1739|2229|2855|\n", - "|보험료 산출 기초율 (위험률 등) 증가분 (B=전년도 기준보험료의 최대 25% 가정)||10846|13897|17806|22815|29232|\n", - "|기준보험료 (C=전년도 기준보험료+A+B)|42325|54321|69485|89030|114074|146161|\n", - "----------------------------------------------------------------------\n", - "CPU times: user 17.5 ms, sys: 17.2 ms, total: 34.7 ms\n", - "Wall time: 20.8 s\n" - ] - } - ], - "source": [ - "%%time\n", - "# OCR 참조와 함께 테이블 추출\n", - "if target_image and sample_ocr_markdown:\n", - " print(f\"🚀 OCR 참조와 함께 테이블 추출: {target_image.name}\")\n", - " print(\"=\" * 70)\n", - " \n", - " try:\n", - " result_with_ocr = extract_table_from_image(\n", - " target_image,\n", - " ocr_markdown=sample_ocr_markdown\n", - " )\n", - " \n", - " print(\"\\n✅ 추출 완료!\")\n", - " print(\"\\n📊 결과 (OCR 참조 사용):\")\n", - " print(\"-\" * 70)\n", - " print(result_with_ocr)\n", - " print(\"-\" * 70)\n", - " \n", - " except Exception as e:\n", - " print(f\"❌ 추출 실패: {e}\")\n", - "elif target_image and not sample_ocr_markdown:\n", - " print(\"⚠️ OCR 참조 파일이 없어서 이 섹션을 건너뜁니다.\")\n", - " print(f\" {target_image.stem}.md 파일을 sample_images 폴더에 추가하세요.\")\n", - "else:\n", - " print(\"❌ 이미지가 없습니다.\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/tests/choi/Table_example/test_extraction.py b/tests/choi/Table_example/test_extraction.py deleted file mode 100644 index fb4e153..0000000 --- a/tests/choi/Table_example/test_extraction.py +++ /dev/null @@ -1,346 +0,0 @@ -""" -보험 테이블 추출 테스트 코드 -Table_example 패키지를 사용하여 이미지에서 테이블을 추출하는 예제 -""" - -import sys -import asyncio -from pathlib import Path - -# 프로젝트 루트를 Python 경로에 추가 -project_root = Path(__file__).parent.parent -sys.path.insert(0, str(project_root)) - -from Table_example import ( - InsuranceTableExtractor, - extract_table_from_image, - aextract_table_from_image, - SYSTEM_PROMPT, - USER_PROMPT_TEMPLATE, -) - - -def test_prompt_display(): - """1. 프롬프트 내용 확인""" - print("\n" + "="*70) - print("테스트 1: 프롬프트 내용 확인") - print("="*70) - - print("\n📋 System Prompt:") - print("-"*50) - print(SYSTEM_PROMPT[:500] + "...") - - print("\n📋 User Prompt Template (일부):") - print("-"*50) - print(USER_PROMPT_TEMPLATE[:500] + "...") - - print("\n✅ 프롬프트 로드 성공!") - return True - - -def test_extractor_initialization(): - """2. 추출기 초기화 테스트""" - print("\n" + "="*70) - print("테스트 2: InsuranceTableExtractor 초기화") - print("="*70) - - try: - extractor = InsuranceTableExtractor() - - status = extractor.get_pool_status() - print(f"\n현재 사용 중인 키: {status['current_key']['name']}") - print(f"총 API 키 수: {status['current_key']['total_keys']}") - - print("\n✅ 추출기 초기화 성공!") - return True - - except FileNotFoundError as e: - print(f"\n⚠️ API 키 파일이 없습니다: {e}") - print("apis/gemini_keys.yaml 파일을 생성하세요.") - return False - except Exception as e: - print(f"\n❌ 초기화 실패: {e}") - return False - - -def test_extract_from_sample_image(): - """3. 샘플 이미지에서 테이블 추출 테스트""" - print("\n" + "="*70) - print("테스트 3: 샘플 이미지에서 테이블 추출") - print("="*70) - - # 샘플 이미지 경로 확인 - sample_images_dir = project_root / "Table_example" / "sample_images" - - if not sample_images_dir.exists(): - print(f"\n⚠️ 샘플 이미지 디렉토리가 없습니다: {sample_images_dir}") - print("sample_images 폴더에 테스트 이미지를 추가하세요.") - return None - - # 샘플 이미지 찾기 - image_files = list(sample_images_dir.glob("*.png")) + \ - list(sample_images_dir.glob("*.jpg")) + \ - list(sample_images_dir.glob("*.jpeg")) - - if not image_files: - print(f"\n⚠️ 샘플 이미지가 없습니다: {sample_images_dir}") - print("PNG, JPG, JPEG 형식의 이미지를 추가하세요.") - return None - - # 첫 번째 이미지로 테스트 - image_path = image_files[0] - print(f"\n테스트 이미지: {image_path.name}") - - try: - # 테이블 추출 - result = extract_table_from_image(image_path) - - print("\n📊 추출 결과:") - print("-"*50) - print(result) - print("-"*50) - - print("\n✅ 테이블 추출 성공!") - return result - - except Exception as e: - print(f"\n❌ 추출 실패: {e}") - import traceback - traceback.print_exc() - return None - - -def test_extract_with_ocr_reference(): - """4. OCR 참조 텍스트와 함께 추출 테스트""" - print("\n" + "="*70) - print("테스트 4: OCR 참조 텍스트와 함께 추출") - print("="*70) - - sample_images_dir = project_root / "Table_example" / "sample_images" - - if not sample_images_dir.exists(): - print(f"\n⚠️ 샘플 이미지 디렉토리가 없습니다.") - return None - - image_files = list(sample_images_dir.glob("*.png")) + \ - list(sample_images_dir.glob("*.jpg")) - - if not image_files: - print(f"\n⚠️ 샘플 이미지가 없습니다.") - return None - - image_path = image_files[0] - print(f"\n테스트 이미지: {image_path.name}") - - # OCR 참조 텍스트 예시 (실제로는 OCR 결과를 사용) - sample_ocr_markdown = """| 구분 | 보험기간 | 납입기간 | 가입금액 | -| 상해사망 | 80세 | 20년 | 1억원 | -| 질병사망 | 80세 | 20년 | 5천만원 |""" - - try: - result = extract_table_from_image( - image_path, - ocr_markdown=sample_ocr_markdown - ) - - print("\n📊 추출 결과 (OCR 참조 사용):") - print("-"*50) - print(result) - print("-"*50) - - print("\n✅ OCR 참조 추출 성공!") - return result - - except Exception as e: - print(f"\n❌ 추출 실패: {e}") - import traceback - traceback.print_exc() - return None - - -def test_async_extraction(): - """5. 비동기 테이블 추출 테스트""" - print("\n" + "="*70) - print("테스트 5: 비동기 테이블 추출") - print("="*70) - - async def async_test(): - sample_images_dir = project_root / "Table_example" / "sample_images" - - if not sample_images_dir.exists(): - print(f"\n⚠️ 샘플 이미지 디렉토리가 없습니다.") - return None - - image_files = list(sample_images_dir.glob("*.png")) + \ - list(sample_images_dir.glob("*.jpg")) - - if not image_files: - print(f"\n⚠️ 샘플 이미지가 없습니다.") - return None - - image_path = image_files[0] - print(f"\n테스트 이미지: {image_path.name}") - - try: - import time - start_time = time.time() - - result = await aextract_table_from_image(image_path) - - elapsed = time.time() - start_time - - print(f"\n⏱️ 소요 시간: {elapsed:.2f}초") - print("\n📊 추출 결과:") - print("-"*50) - print(result[:500] + "..." if len(result) > 500 else result) - print("-"*50) - - print("\n✅ 비동기 추출 성공!") - return result - - except Exception as e: - print(f"\n❌ 추출 실패: {e}") - import traceback - traceback.print_exc() - return None - - return asyncio.run(async_test()) - - -def test_multiple_images(): - """6. 여러 이미지 동시 처리 테스트""" - print("\n" + "="*70) - print("테스트 6: 여러 이미지 동시 처리 (비동기)") - print("="*70) - - async def async_test(): - sample_images_dir = project_root / "Table_example" / "sample_images" - - if not sample_images_dir.exists(): - print(f"\n⚠️ 샘플 이미지 디렉토리가 없습니다.") - return None - - image_files = list(sample_images_dir.glob("*.png")) + \ - list(sample_images_dir.glob("*.jpg")) + \ - list(sample_images_dir.glob("*.jpeg")) - - if len(image_files) < 2: - print(f"\n⚠️ 2개 이상의 샘플 이미지가 필요합니다. 현재: {len(image_files)}개") - return None - - # 최대 3개 이미지 처리 - images_to_process = image_files[:3] - - print(f"\n처리할 이미지 {len(images_to_process)}개:") - for img in images_to_process: - print(f" - {img.name}") - - try: - import time - start_time = time.time() - - # 동시에 여러 이미지 처리 - tasks = [aextract_table_from_image(img) for img in images_to_process] - results = await asyncio.gather(*tasks, return_exceptions=True) - - elapsed = time.time() - start_time - - print(f"\n⏱️ 총 소요 시간: {elapsed:.2f}초") - - for i, (img, result) in enumerate(zip(images_to_process, results), 1): - print(f"\n📊 이미지 {i} ({img.name}) 결과:") - print("-"*40) - if isinstance(result, Exception): - print(f"❌ 에러: {result}") - else: - print(result[:300] + "..." if len(result) > 300 else result) - - success_count = sum(1 for r in results if not isinstance(r, Exception)) - print(f"\n✅ {len(images_to_process)}개 중 {success_count}개 성공!") - return results - - except Exception as e: - print(f"\n❌ 처리 실패: {e}") - import traceback - traceback.print_exc() - return None - - return asyncio.run(async_test()) - - -def main(): - """모든 테스트 실행""" - print("\n" + "🏥 " * 20) - print("보험 테이블 추출 테스트 시작") - print("🏥 " * 20) - - # API 키 설정 확인 - config_path = project_root / "apis" / "gemini_keys.yaml" - if not config_path.exists(): - print(f"\n❌ 오류: API 키 설정 파일이 없습니다: {config_path}") - print("\n다음 단계를 수행하세요:") - print("1. apis/gemini_keys-example.yaml을 apis/gemini_keys.yaml로 복사") - print("2. 실제 Gemini API 키를 입력") - print("3. Google AI Studio에서 무료 API 키 발급:") - print(" https://makersuite.google.com/app/apikey") - return - - # 샘플 이미지 디렉토리 확인/생성 - sample_images_dir = project_root / "Table_example" / "sample_images" - if not sample_images_dir.exists(): - sample_images_dir.mkdir(parents=True, exist_ok=True) - print(f"\n📁 샘플 이미지 디렉토리를 생성했습니다: {sample_images_dir}") - print("⚠️ 테스트할 보험 테이블 이미지를 이 폴더에 추가하세요.") - - # 테스트 실행 - tests = [ - ("프롬프트 확인", test_prompt_display), - ("추출기 초기화", test_extractor_initialization), - ("이미지 추출", test_extract_from_sample_image), - ("OCR 참조 추출", test_extract_with_ocr_reference), - ("비동기 추출", test_async_extraction), - ("다중 이미지", test_multiple_images), - ] - - results = [] - for name, test_func in tests: - try: - result = test_func() - # None은 이미지가 없어서 스킵된 경우 - if result is None: - results.append((name, "skipped")) - else: - results.append((name, "success")) - except KeyboardInterrupt: - print("\n\n⚠️ 사용자가 테스트를 중단했습니다.") - break - except Exception as e: - print(f"\n❌ 예상치 못한 오류: {e}") - results.append((name, "failed")) - - # 결과 요약 - print("\n" + "=" * 70) - print("테스트 결과 요약") - print("=" * 70) - - for name, status in results: - if status == "success": - icon = "✅" - elif status == "skipped": - icon = "⏭️ " - else: - icon = "❌" - print(f"{icon} {name}: {status}") - - success_count = sum(1 for _, s in results if s == "success") - skipped_count = sum(1 for _, s in results if s == "skipped") - total_count = len(results) - - print(f"\n총 {total_count}개 테스트 중 {success_count}개 성공, {skipped_count}개 스킵") - - if skipped_count > 0: - print("\n💡 팁: sample_images 폴더에 보험 테이블 이미지를 추가하면 더 많은 테스트가 실행됩니다.") - - -if __name__ == "__main__": - main() diff --git a/tests/choi/upload_fin.txt b/tests/choi/upload_fin.txt deleted file mode 100644 index da757cb..0000000 --- a/tests/choi/upload_fin.txt +++ /dev/null @@ -1 +0,0 @@ -I_origin_0/I_table_1.png diff --git a/tests/test_flow.py b/tests/test_flow.py deleted file mode 100644 index 417c125..0000000 --- a/tests/test_flow.py +++ /dev/null @@ -1,19 +0,0 @@ -import pytest -from unittest.mock import MagicMock -from langchain_core.messages import AIMessage -from generate_synthetic_table.flow import build_synthetic_table_graph, TableState - -@pytest.fixture -def mock_llm(): - llm = MagicMock() - llm.invoke.return_value = AIMessage(content="Mock response") - return llm - -def test_graph_compilation(mock_llm): - graph = build_synthetic_table_graph(mock_llm) - assert graph is not None - -def test_image_to_html_node(mock_llm): - # This is a bit harder to test without mocking the file system or _load_prompt - # For now, we just check if the graph builds and runs with a mock - pass diff --git a/tests/test_html_to_image.py b/tests/test_html_to_image.py deleted file mode 100644 index e90c721..0000000 --- a/tests/test_html_to_image.py +++ /dev/null @@ -1,41 +0,0 @@ - -from pathlib import Path -import pytest -from generate_synthetic_table.html_to_image import capture_html_as_image - -def test_capture_html_as_image(tmp_path): - html = """ - - - - - -

Test Table

-
- - - - - - - - - - - - -
NameAge
Alice30
Bob25
- - - """ - - output_path = tmp_path / "test_table.png" - capture_html_as_image(html, output_path) - - assert output_path.exists() - assert output_path.stat().st_size > 0 - print(f"Image saved to {output_path}") diff --git a/tests/test_utils.py b/tests/test_utils.py deleted file mode 100644 index d78f755..0000000 --- a/tests/test_utils.py +++ /dev/null @@ -1,39 +0,0 @@ -import pytest -from generate_synthetic_table.validators import robust_json_parse, validate_html - -def test_robust_json_parse_valid(): - assert robust_json_parse('{"a": 1}') == {"a": 1} - -def test_robust_json_parse_markdown(): - text = """ - Here is the json: - ```json - { - "key": "value" - } - ``` - """ - assert robust_json_parse(text) == {"key": "value"} - -def test_robust_json_parse_markdown_no_lang(): - text = """ - ``` - {"x": [1, 2]} - ``` - """ - assert robust_json_parse(text) == {"x": [1, 2]} - -def test_robust_json_parse_dirty(): - text = "Sure! {\"a\": 1} is the answer." - assert robust_json_parse(text) == {"a": 1} - -def test_validate_html_valid(): - html = "
Cell
" - assert validate_html(html) is True - -def test_validate_html_invalid_structure(): - html = "
Not a table
" - assert validate_html(html) is False - -def test_validate_html_empty(): - assert validate_html("") is False diff --git a/tests/test_validation.py b/tests/test_validation.py deleted file mode 100644 index 3b72a1b..0000000 --- a/tests/test_validation.py +++ /dev/null @@ -1,354 +0,0 @@ -"""3단계 검증 체인 테스트 스크립트 (standalone)""" - -import re -import logging -from typing import Optional, Any, List, Tuple -from bs4 import BeautifulSoup - -# pandas/pandasql import -try: - import pandas as pd - from pandasql import sqldf - PANDAS_SQL_AVAILABLE = True -except ImportError: - PANDAS_SQL_AVAILABLE = False - pd = None - sqldf = None - -# REPL import -try: - from langchain_experimental.tools import PythonREPLTool - REPL_AVAILABLE = True -except ImportError: - REPL_AVAILABLE = False - PythonREPLTool = None - -logger = logging.getLogger(__name__) - - -def parse_html_table_to_json(html: str) -> Optional[dict]: - """HTML 테이블을 JSON으로 파싱""" - if not html: - return None - - try: - soup = BeautifulSoup(html, "html.parser") - table = soup.find("table") - if not table: - return None - - rows = table.find_all("tr") - if not rows: - return None - - max_cols = 0 - for row in rows: - cols = sum(int(cell.get("colspan", 1)) for cell in row.find_all(["td", "th"])) - max_cols = max(max_cols, cols) - - if max_cols == 0: - return None - - grid = [] - for row_idx, row in enumerate(rows): - while len(grid) <= row_idx: - grid.append([None] * max_cols) - - col_idx = 0 - for cell in row.find_all(["td", "th"]): - while col_idx < max_cols and grid[row_idx][col_idx] is not None: - col_idx += 1 - - if col_idx >= max_cols: - break - - cell_text = cell.get_text(strip=True) - colspan = int(cell.get("colspan", 1)) - rowspan = int(cell.get("rowspan", 1)) - - for r in range(rowspan): - for c in range(colspan): - target_row = row_idx + r - target_col = col_idx + c - while len(grid) <= target_row: - grid.append([None] * max_cols) - if target_col < max_cols: - grid[target_row][target_col] = cell_text - - col_idx += colspan - - raw_rows = [[cell if cell is not None else "" for cell in row] for row in grid] - if not raw_rows: - return None - - has_header = bool(rows[0].find_all("th")) - - if has_header and len(raw_rows) > 1: - headers = raw_rows[0] - data = raw_rows[1:] - else: - headers = [f"col_{i+1}" for i in range(len(raw_rows[0]))] - data = raw_rows - - return {"headers": headers, "data": data, "raw_rows": raw_rows} - except Exception as e: - logger.error(f"HTML parsing failed: {e}") - return None - - -def html_table_to_dataframe(html: str) -> Optional[Any]: - """HTML 테이블을 DataFrame으로 변환""" - if not PANDAS_SQL_AVAILABLE: - return None - - parsed = parse_html_table_to_json(html) - if not parsed: - return None - - try: - headers = parsed["headers"] - data = parsed["data"] - - clean_headers = [] - for i, h in enumerate(headers): - clean = re.sub(r'[^\w가-힣]', '_', str(h).strip()) - clean = re.sub(r'_+', '_', clean).strip('_') - if not clean or clean[0].isdigit(): - clean = f"col_{i}" - clean_headers.append(clean) - - seen = {} - final_headers = [] - for h in clean_headers: - if h in seen: - seen[h] += 1 - final_headers.append(f"{h}_{seen[h]}") - else: - seen[h] = 0 - final_headers.append(h) - - return pd.DataFrame(data, columns=final_headers) - except Exception as e: - logger.error(f"DataFrame conversion failed: {e}") - return None - - -def compare_tables_with_sql(original_html: str, synthetic_html: str) -> Tuple[bool, List[str]]: - """pandasql로 두 테이블 비교""" - if not PANDAS_SQL_AVAILABLE: - return True, ["pandas/pandasql not available"] - - issues = [] - - df_original = html_table_to_dataframe(original_html) - df_synthetic = html_table_to_dataframe(synthetic_html) - - if df_original is None: - return False, ["원본 테이블 변환 실패"] - if df_synthetic is None: - return False, ["합성 테이블 변환 실패"] - - orig_shape = df_original.shape - synth_shape = df_synthetic.shape - - if orig_shape[1] != synth_shape[1]: - issues.append(f"열 수 불일치: 원본={orig_shape[1]}, 합성={synth_shape[1]}") - - if orig_shape[0] != synth_shape[0]: - issues.append(f"행 수 불일치: 원본={orig_shape[0]}, 합성={synth_shape[0]}") - - if orig_shape[1] != synth_shape[1]: - return False, issues - - try: - common_cols = [f"c{i}" for i in range(orig_shape[1])] - df_original.columns = common_cols - df_synthetic.columns = common_cols - - env = {"df_original": df_original, "df_synthetic": df_synthetic} - - numeric_cols = df_original.select_dtypes(include=['number']).columns.tolist() - - for col in numeric_cols: - try: - sum_query = f""" - SELECT - (SELECT COALESCE(SUM(CAST({col} AS REAL)), 0) FROM df_original) as orig_sum, - (SELECT COALESCE(SUM(CAST({col} AS REAL)), 0) FROM df_synthetic) as synth_sum - """ - sum_result = sqldf(sum_query, env) - orig_sum = sum_result.iloc[0]['orig_sum'] - synth_sum = sum_result.iloc[0]['synth_sum'] - - if abs(orig_sum - synth_sum) > 0.01: - issues.append(f"열 '{col}' 합계 불일치: 원본={orig_sum:.2f}, 합성={synth_sum:.2f}") - except: - pass - - except Exception as e: - issues.append(f"SQL 비교 오류: {e}") - - passed = len([i for i in issues if not i.startswith(" -")]) == 0 - return passed, issues - - -def compare_tables_with_repl(original_html: str, synthetic_html: str) -> Tuple[bool, List[str]]: - """REPL로 두 테이블 비교""" - if not REPL_AVAILABLE or not PANDAS_SQL_AVAILABLE: - return True, ["REPL 사용 불가"] - - df_original = html_table_to_dataframe(original_html) - df_synthetic = html_table_to_dataframe(synthetic_html) - - if df_original is None or df_synthetic is None: - return False, ["DataFrame 변환 실패"] - - issues = [] - - try: - # Shape 비교 - if df_original.shape != df_synthetic.shape: - issues.append(f"Shape 불일치: 원본={df_original.shape}, 합성={df_synthetic.shape}") - - # 행 수 비교 - if len(df_original) != len(df_synthetic): - issues.append(f"행 수 불일치: 원본={len(df_original)}, 합성={len(df_synthetic)}") - - # 숫자 컬럼 합계 비교 - for col in df_original.columns: - if col in df_synthetic.columns: - try: - orig_sum = pd.to_numeric(df_original[col], errors='coerce').sum() - synth_sum = pd.to_numeric(df_synthetic[col], errors='coerce').sum() - if pd.notna(orig_sum) and pd.notna(synth_sum): - if abs(orig_sum - synth_sum) > 0.01: - issues.append(f"컬럼 '{col}' 합계 불일치: 원본={orig_sum:.2f}, 합성={synth_sum:.2f}") - except: - pass - - except Exception as e: - issues.append(f"REPL 비교 오류: {e}") - - return len(issues) == 0, issues - -print("=" * 60) -print("3단계 검증 체인 테스트") -print("=" * 60) - -# 패키지 상태 확인 -print(f"\n📦 패키지 상태:") -print(f" - pandas/pandasql: {'✅ 사용 가능' if PANDAS_SQL_AVAILABLE else '❌ 없음'}") -print(f" - langchain REPL: {'✅ 사용 가능' if REPL_AVAILABLE else '❌ 없음'}") - -# 테스트용 HTML 테이블 -original_html = """ - - - - - - - - - -
이름나이점수
김철수2585
이영희3092
박민수2878
-""" - -# 테스트 1: 동일한 테이블 비교 (PASS 예상) -print("\n" + "=" * 60) -print("테스트 1: 동일한 테이블 비교 (PASS 예상)") -print("=" * 60) - -identical_synthetic = """ - - - - - -
이름나이점수
김철수2585
이영희3092
박민수2878
-""" - -print("\n[1/3] pandasql 검증...") -passed, issues = compare_tables_with_sql(original_html, identical_synthetic) -print(f" 결과: {'✅ PASS' if passed else '❌ FAIL'}") -if issues: - print(f" 이슈: {issues}") - -print("\n[2/3] REPL 검증...") -passed, issues = compare_tables_with_repl(original_html, identical_synthetic) -print(f" 결과: {'✅ PASS' if passed else '❌ FAIL'}") -if issues: - print(f" 이슈: {issues}") - -# 테스트 2: 행 수가 다른 테이블 (FAIL 예상) -print("\n" + "=" * 60) -print("테스트 2: 행 수가 다른 테이블 (FAIL 예상)") -print("=" * 60) - -different_rows = """ - - - - -
이름나이점수
김철수2585
이영희3092
-""" - -print("\n[1/3] pandasql 검증...") -passed, issues = compare_tables_with_sql(original_html, different_rows) -print(f" 결과: {'✅ PASS' if passed else '❌ FAIL'}") -if issues: - for issue in issues[:5]: - print(f" - {issue}") - -print("\n[2/3] REPL 검증...") -passed, issues = compare_tables_with_repl(original_html, different_rows) -print(f" 결과: {'✅ PASS' if passed else '❌ FAIL'}") -if issues: - for issue in issues[:5]: - print(f" - {issue}") - -# 테스트 3: 숫자 값이 다른 테이블 (FAIL 예상) -print("\n" + "=" * 60) -print("테스트 3: 숫자 값이 다른 테이블 (FAIL 예상)") -print("=" * 60) - -different_values = """ - - - - - -
이름나이점수
김철수2590
이영희3095
박민수2880
-""" - -print("\n[1/3] pandasql 검증...") -passed, issues = compare_tables_with_sql(original_html, different_values) -print(f" 결과: {'✅ PASS' if passed else '❌ FAIL'}") -if issues: - for issue in issues[:5]: - print(f" - {issue}") - -print("\n[2/3] REPL 검증...") -passed, issues = compare_tables_with_repl(original_html, different_values) -print(f" 결과: {'✅ PASS' if passed else '❌ FAIL'}") -if issues: - for issue in issues[:5]: - print(f" - {issue}") - -# DataFrame 변환 테스트 -print("\n" + "=" * 60) -print("DataFrame 변환 테스트") -print("=" * 60) - -df = html_table_to_dataframe(original_html) -if df is not None: - print(f"\n변환된 DataFrame:") - print(df) - print(f"\nShape: {df.shape}") - print(f"Columns: {list(df.columns)}") -else: - print("❌ DataFrame 변환 실패") - -print("\n" + "=" * 60) -print("테스트 완료!") -print("=" * 60) diff --git a/uv.lock b/uv.lock index 9e69bc9..6daa4bd 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.12" resolution-markers = [ "python_full_version >= '3.14'", @@ -2425,6 +2425,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" }, ] +[[package]] +name = "playwright" +version = "1.57.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet" }, + { name = "pyee" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/b6/e17543cea8290ae4dced10be21d5a43c360096aa2cce0aa7039e60c50df3/playwright-1.57.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:9351c1ac3dfd9b3820fe7fc4340d96c0d3736bb68097b9b7a69bd45d25e9370c", size = 41985039, upload-time = "2025-12-09T08:06:18.408Z" }, + { url = "https://files.pythonhosted.org/packages/8b/04/ef95b67e1ff59c080b2effd1a9a96984d6953f667c91dfe9d77c838fc956/playwright-1.57.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a4a9d65027bce48eeba842408bcc1421502dfd7e41e28d207e94260fa93ca67e", size = 40775575, upload-time = "2025-12-09T08:06:22.105Z" }, + { url = "https://files.pythonhosted.org/packages/60/bd/5563850322a663956c927eefcf1457d12917e8f118c214410e815f2147d1/playwright-1.57.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:99104771abc4eafee48f47dac2369e0015516dc1ce8c409807d2dd440828b9a4", size = 41985042, upload-time = "2025-12-09T08:06:25.357Z" }, + { url = "https://files.pythonhosted.org/packages/56/61/3a803cb5ae0321715bfd5247ea871d25b32c8f372aeb70550a90c5f586df/playwright-1.57.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:284ed5a706b7c389a06caa431b2f0ba9ac4130113c3a779767dda758c2497bb1", size = 45975252, upload-time = "2025-12-09T08:06:29.186Z" }, + { url = "https://files.pythonhosted.org/packages/83/d7/b72eb59dfbea0013a7f9731878df8c670f5f35318cedb010c8a30292c118/playwright-1.57.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a1bae6c0a07839cdeaddbc0756b3b2b85e476c07945f64ece08f1f956a86f1", size = 45706917, upload-time = "2025-12-09T08:06:32.549Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/3fc9ebd7c95ee54ba6a68d5c0bc23e449f7235f4603fc60534a364934c16/playwright-1.57.0-py3-none-win32.whl", hash = "sha256:1dd93b265688da46e91ecb0606d36f777f8eadcf7fbef12f6426b20bf0c9137c", size = 36553860, upload-time = "2025-12-09T08:06:35.864Z" }, + { url = "https://files.pythonhosted.org/packages/58/d4/dcdfd2a33096aeda6ca0d15584800443dd2be64becca8f315634044b135b/playwright-1.57.0-py3-none-win_amd64.whl", hash = "sha256:6caefb08ed2c6f29d33b8088d05d09376946e49a73be19271c8cd5384b82b14c", size = 36553864, upload-time = "2025-12-09T08:06:38.915Z" }, + { url = "https://files.pythonhosted.org/packages/6a/60/fe31d7e6b8907789dcb0584f88be741ba388413e4fbce35f1eba4e3073de/playwright-1.57.0-py3-none-win_arm64.whl", hash = "sha256:5f065f5a133dbc15e6e7c71e7bc04f258195755b1c32a432b792e28338c8335e", size = 32837940, upload-time = "2025-12-09T08:06:42.268Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -2721,6 +2740,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, ] +[[package]] +name = "pyee" +version = "13.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250, upload-time = "2025-03-17T18:53:15.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730, upload-time = "2025-03-17T18:53:14.532Z" }, +] + [[package]] name = "pygments" version = "2.19.2" @@ -3374,6 +3405,7 @@ dependencies = [ { name = "notion-client" }, { name = "pandas" }, { name = "pandasql" }, + { name = "playwright" }, { name = "pydantic" }, { name = "pymongo" }, { name = "pymupdf" }, @@ -3405,6 +3437,7 @@ requires-dist = [ { name = "notion-client", specifier = ">=2.0.0" }, { name = "pandas", specifier = ">=2.3.3" }, { name = "pandasql", specifier = ">=0.7.3" }, + { name = "playwright", specifier = ">=1.57.0" }, { name = "pydantic", specifier = ">=2.12.4" }, { name = "pymongo", specifier = ">=4.6.1" }, { name = "pymupdf", specifier = ">=1.26.7" },