여러 Excel 시트를 출처 시트 열과 함께 하나의 표로 합치기
같은 열 구조를 사용하는 여러 Excel 시트를 하나로 합치면서 각 행이 어느 시트에서 왔는지 기록합니다. 작은 합성 통합문서를 이용해 행 순서, 개수, 합계를 손으로 확인할 수 있습니다.
주문 단위 CSV를 월별 주문 수, 수량, 금액으로 요약한 뒤 그룹화 결과를 원본 합계와 대조합니다. 작은 합성 데이터로 모든 예상 결과를 손으로 확인할 수 있습니다.
이런 분께 맞아요CSV에서 반복 가능한 월별 요약을 만들면서 집계 과정에서 레코드가 빠지지 않았는지 명시적으로 확인하려는 사람을 위한 안내입니다.
이 예제에서는 달력 월별로 주문 수, 전체 units, 전체 amount의 세 가지 지표를 계산합니다. 합성 원본은 주문 하나당 한 행이므로 이 데이터에서는 행 개수와 주문 수가 같습니다.
핵심은 원본과의 대조입니다. groupby 이후 월별 값을 다시 모두 더해 원본 합계와 비교합니다. 중간에 행이 잘못 필터링되거나 중복되거나 누락되어도 최종 요약값은 그럴듯해 보일 수 있습니다.
아래 데이터는 이 글을 위해 작성한 합성 데이터입니다. monthly_orders.csv로 저장하세요. 2026년 1월, 2월, 3월에 걸친 주문 7개가 있습니다.
order_id,order_date,team,units,amount
O001,2026-01-05,North,2,40
O002,2026-01-20,South,3,60
O003,2026-02-02,North,1,25
O004,2026-02-18,South,4,100
O005,2026-02-28,North,2,50
O006,2026-03-03,South,5,125
O007,2026-03-15,North,1,30
원본 합계는 손으로 확인할 수 있습니다. 주문은 7개입니다. units 합계는 18이며 계산식은 2 + 3 + 1 + 4 + 2 + 5 + 1입니다. amount 합계는 430이며 계산식은 40 + 60 + 25 + 100 + 50 + 125 + 30입니다.
| 지표 | 원본 합계 |
|---|---|
| 주문 | 7 |
| Units | 18 |
| Amount | 430 |
1월에는 O001과 O002, 2월에는 O003, O004, O005, 3월에는 O006과 O007이 있습니다. 각 월을 더하면 아래 결과가 나옵니다.
| month | orders | units | amount |
|---|---|---|---|
| 2026-01 | 2 | 5 | 100 |
| 2026-02 | 3 | 7 | 175 |
| 2026-03 | 2 | 6 | 155 |
월별 값을 다시 더하면 원본과 일치합니다. 주문은 2 + 3 + 2 = 7, units는 5 + 7 + 6 = 18, amount는 100 + 175 + 155 = 430입니다.
다음 스크립트를 monthly_summary_pandas.py로 저장하세요. 필수 열을 확인하고 누락되거나 중복된 주문 ID를 거부하며, 날짜를 하나의 명시적 형식으로 파싱하고 숫자 열을 변환합니다. 월별 집계와 원본 합계 대조가 통과한 뒤에만 결과를 저장합니다.
from pathlib import Path
import pandas as pd
SOURCE = Path("monthly_orders.csv")
OUTPUT_DIR = Path("outputs") / "monthly_summary_result"
OUTPUT = OUTPUT_DIR / "monthly_summary.csv"
REQUIRED = ["order_id", "order_date", "units", "amount"]
def main() -> None:
if not SOURCE.is_file():
raise FileNotFoundError(f"Source CSV not found: {SOURCE}")
if OUTPUT_DIR.exists():
raise FileExistsError(f"Output folder already exists: {OUTPUT_DIR}")
data = pd.read_csv(SOURCE, dtype={"order_id": "string"})
missing = [name for name in REQUIRED if name not in data.columns]
if missing:
raise ValueError(f"Missing required columns: {missing}")
if data.empty:
raise ValueError("Source CSV contains no data rows.")
if data[REQUIRED].isna().any().any():
raise ValueError("A required field contains a missing value.")
if data["order_id"].str.strip().eq("").any():
raise ValueError("order_id contains a blank value.")
if data["order_id"].duplicated().any():
raise ValueError("Duplicate order_id found.")
data["order_date"] = pd.to_datetime(
data["order_date"], format="%Y-%m-%d", errors="raise"
)
data["units"] = pd.to_numeric(data["units"], errors="raise")
data["amount"] = pd.to_numeric(data["amount"], errors="raise")
data["month"] = data["order_date"].dt.to_period("M").astype(str)
summary = (
data.groupby("month", as_index=False, sort=True)
.agg(
orders=("order_id", "count"),
units=("units", "sum"),
amount=("amount", "sum"),
)
)
source_totals = (len(data), data["units"].sum(), data["amount"].sum())
summary_totals = (
summary["orders"].sum(),
summary["units"].sum(),
summary["amount"].sum(),
)
if summary_totals != source_totals:
raise RuntimeError("Monthly totals do not reconcile with the source.")
OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
OUTPUT_DIR.mkdir()
summary.to_csv(OUTPUT, index=False)
check = pd.read_csv(OUTPUT)
if check.to_dict("records") != summary.to_dict("records"):
raise RuntimeError("Saved CSV does not match the calculated summary.")
print(f"Source rows: {len(data)}.")
print(f"Months: {len(summary)}.")
print(f"Verified totals: units={source_totals[1]}, amount={source_totals[2]}.")
print(f"Output: {OUTPUT.as_posix()}")
if __name__ == "__main__":
main()
python monthly_summary_pandas.py출력에는 월 순서로 정렬된 데이터 행 3개가 있어야 합니다. 예상 CSV 내용은 아래와 같습니다.
month,orders,units,amount
2026-01,2,5,100
2026-02,3,7,175
2026-03,2,6,155
아래 예상 콘솔 출력은 합성 데이터와 코드를 기준으로 손으로 도출한 결과이며 실제 실행 로그가 아닙니다.
Source rows: 7.
Months: 3.
Verified totals: units=18, amount=430.
Output: outputs/monthly_summary_result/monthly_summary.csv| 증상 | 확인할 사항 |
|---|---|
| pandas ModuleNotFoundError | 같은 Python 환경에 python -m pip install pandas 명령으로 pandas를 설치하세요. |
| 필수 열 누락 | 예상 스키마를 바꾸기 전에 CSV 헤더를 확인하세요. |
| 날짜 파싱 오류 | 모든 order_date가 YYYY-MM-DD 형식인지 확인하세요. |
| 숫자 변환 오류 | units와 amount에 텍스트, 통화 기호, 구분자 등이 있는지 확인하세요. |
| Duplicate order_id found | 반복된 ID가 단순 중복인지 아니면 다른 데이터 구조를 의미하는지 확인하세요. |
| FileExistsError | 기존 결과를 검토하고 덮어쓰지 말고 새 출력 폴더를 사용하세요. |
전체 합계가 맞는다고 모든 월이 정확하다는 뜻은 아닙니다. 서로 다른 월의 오류가 서로 상쇄될 수 있으므로 중요한 보고서는 일부 원본 행과 월별 소계도 추가로 확인하세요.
이 예제는 한 행이 주문 한 건이고 amount가 정수라고 가정합니다. 하나의 주문이 여러 품목 행으로 나뉘어 있다면 행 개수 대신 order_id의 고유 개수를 사용해야 할 수 있습니다. 실제 금액이 소수라면 일반 부동소수점 대신 최소 화폐 단위 정수나 Decimal 기반 처리가 필요할 수 있습니다.
월은 시간대 처리 없이 날짜에서 직접 계산합니다. 여러 시간대의 타임스탬프가 있다면 어느 업무 시간대를 기준으로 월을 정할지 먼저 결정해야 합니다. 환불, 취소, 필터, 누락 레코드도 명시적인 업무 규칙이 필요하며, 단순한 합계 일치만으로 그 규칙이 적절한지 판단할 수는 없습니다.
2026-09-20 · 예제 수동 검토 · 대상: Python 3.12 · pandas 필요 · 실행하지 않음
설명과 예제는 직접 작성했습니다. 관련 동작과 개념은 아래 공식 자료에서 확인할 수 있습니다.