""" excel_to_jsonl.py ──────────────────────────────────────────── 엑셀 파일(질문-답변 쌍) → JSONL 형식 변환 사용법: python excel_to_jsonl.py input.xlsx 출력: data/qa_raw.jsonl """ import json import pathlib import sys import pandas as pd def excel_to_jsonl(excel_path: str, output_path: str = None, q_col_index: int = None, a_col_index: int = None): """ 엑셀 파일을 JSONL 형식으로 변환 Args: excel_path: 입력 엑셀 파일 경로 output_path: 출력 JSONL 파일 경로 (기본: data/qa_raw.jsonl) q_col_index: 질문 열 인덱스 (0부터 시작, 기본: 자동 감지) a_col_index: 답변 열 인덱스 (0부터 시작, 기본: 자동 감지) 엑셀 형식: - 열 이름: "질문", "답변" 또는 "question", "answer" (자동 감지) - 또는 컬럼 인덱스 지정 (예: 3열=2, 4열=3) """ # 경로 설정 excel_path = pathlib.Path(excel_path) if not excel_path.exists(): print(f"❌ 파일을 찾을 수 없습니다: {excel_path}") sys.exit(1) if output_path is None: BASE_DIR = pathlib.Path(__file__).resolve().parent.parent output_path = BASE_DIR / "data" / "qa_raw.jsonl" else: output_path = pathlib.Path(output_path) output_path.parent.mkdir(parents=True, exist_ok=True) print(f"📖 엑셀 파일 읽기: {excel_path}") # 엑셀 파일 읽기 (.xlsx, .xls 지원) try: df = pd.read_excel(excel_path) except Exception as e: print(f"❌ 엑셀 파일 읽기 실패: {e}") print("pandas와 openpyxl이 설치되어 있는지 확인하세요:") print(" pip install pandas openpyxl") sys.exit(1) print(f"📊 총 {len(df)}개 행 발견") print(f"📋 컬럼: {list(df.columns)}") print(f"📋 컬럼 수: {len(df.columns)}개") # 컬럼 이름 찾기 q_col = None a_col = None # 1. 명시적 인덱스가 제공된 경우 if q_col_index is not None and a_col_index is not None: if q_col_index < len(df.columns) and a_col_index < len(df.columns): q_col = df.columns[q_col_index] a_col = df.columns[a_col_index] print(f"✅ 인덱스로 컬럼 선택:") print(f" 질문(q): 열 {q_col_index+1} ({q_col})") print(f" 답변(a): 열 {a_col_index+1} ({a_col})") else: print(f"❌ 잘못된 컬럼 인덱스입니다. 컬럼 수: {len(df.columns)}") sys.exit(1) # 2. 한글 컬럼명 찾기 elif q_col is None or a_col is None: for col in df.columns: col_lower = str(col).lower().strip() if '질문' in col_lower or 'q' == col_lower or 'question' in col_lower: q_col = col if '답변' in col_lower or '답' in col_lower or 'a' == col_lower or 'answer' in col_lower: a_col = col # 3. 컬럼명을 못 찾은 경우, 첫 2개 컬럼 사용 if q_col is None or a_col is None: if len(df.columns) >= 2: q_col = df.columns[0] a_col = df.columns[1] print(f"⚠️ 컬럼명을 자동 인식하지 못했습니다. 첫 2개 컬럼을 사용합니다:") print(f" 질문(q): {q_col}") print(f" 답변(a): {a_col}") else: print(f"❌ 엑셀 파일에 최소 2개의 컬럼이 필요합니다.") sys.exit(1) else: print(f"✅ 컬럼 매핑:") print(f" 질문(q): {q_col}") print(f" 답변(a): {a_col}") # JSONL 변환 print(f"🔄 JSONL 변환 중...") count = 0 skipped = 0 with output_path.open("w", encoding="utf-8") as fout: for idx, row in df.iterrows(): q = str(row[q_col]).strip() a = str(row[a_col]).strip() # 빈 값 건너뛰기 if not q or not a or q == 'nan' or a == 'nan': skipped += 1 continue # JSONL 형식으로 저장 obj = {"q": q, "a": a} fout.write(json.dumps(obj, ensure_ascii=False) + "\n") count += 1 print(f"✅ 변환 완료!") print(f" 출력: {output_path}") print(f" 변환된 QA 쌍: {count}개") if skipped > 0: print(f" 건너뛴 행: {skipped}개 (빈 값)") # 샘플 출력 if count > 0: print(f"\n📝 샘플 (첫 3개):") with output_path.open("r", encoding="utf-8") as fin: for i, line in enumerate(fin): if i >= 3: break obj = json.loads(line) print(f"\n[{i+1}]") print(f"Q: {obj['q'][:80]}{'...' if len(obj['q']) > 80 else ''}") print(f"A: {obj['a'][:80]}{'...' if len(obj['a']) > 80 else ''}") if __name__ == "__main__": if len(sys.argv) < 2: print("사용법: python excel_to_jsonl.py <엑셀파일경로> [질문열] [답변열]") print("\n예제:") print(" python excel_to_jsonl.py qa_data.xlsx") print(" python excel_to_jsonl.py qa_data.xlsx 2 3 # 3열, 4열 사용 (0부터 시작)") print(" python excel_to_jsonl.py /path/to/questions.xlsx") print("\n출력: data/qa_raw.jsonl") print("\n컬럼 인덱스는 0부터 시작합니다 (1열=0, 2열=1, 3열=2, 4열=3)") sys.exit(1) excel_path = sys.argv[1] q_col_index = None a_col_index = None # 컬럼 인덱스가 제공된 경우 if len(sys.argv) >= 4: try: q_col_index = int(sys.argv[2]) a_col_index = int(sys.argv[3]) print(f"📌 지정된 컬럼: 질문={q_col_index+1}열, 답변={a_col_index+1}열") except ValueError: print("❌ 컬럼 인덱스는 숫자여야 합니다.") sys.exit(1) excel_to_jsonl(excel_path, q_col_index=q_col_index, a_col_index=a_col_index)