4b86b2a660
- server-dev start/stop/deploy 및 Gitea push 자동 배포 - local-dev 로컬 개발 환경 Co-authored-by: Cursor <cursoragent@cursor.com>
169 lines
6.0 KiB
Python
169 lines
6.0 KiB
Python
"""
|
|
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)
|
|
|