본문 바로가기
홈 AI 학습 AI 실무 안내서 출동 통계, CSV 한 장으로 월간 집계
L2 중급 · 14분 읽기

출동 통계, CSV 한 장으로 월간 집계

2026.10.26 갱신 출동 데이터를 직접 다뤄보려는 실무자
#pandas#통계#groupby#검산
검토 상태: 전문 검토 전 위험도 미분류

업무에 AI를 쓸 때 확인하면 좋은 방법과 점검 항목을 정리한 참고 자료입니다. 실제 업무에 적용할 때는 소속 기관의 기준과 담당자 검토를 함께 확인합니다.

매달 출동 현황을 집계하는 일은 반복되고, 옮겨 적다 틀리기 쉽습니다. 요즘은 엑셀의 AI 기능이나 대화형 AI에 CSV를 주면 월별 표가 바로 나옵니다. 그래도 '출동 건수'를 행 수로 셀지 고유 사건 수로 셀지, 어느 시각을 기준으로 월을 나눌지는 사람이 정해야 합니다. 받은 표가 원자료와 맞는지도 사람이 확인해야 합니다.

이 안내서는 월별·유형별·요일×시간대·지역별 집계에서 먼저 정할 정의와, 코드나 AI가 낸 집계표를 검산하는 기준을 다룹니다.

🎯 이 장의 목표
  • 출동 건수의 기준(행 수 또는 고유 사건 수)과 시각 기준을 집계 전에 정할 수 있습니다.
  • AI나 엑셀 기능에 집계를 맡길 때 정의와 검산 조건을 프롬프트로 줄 수 있습니다.
  • 집계 합과 전체 행 수, 고유 ID 수, 결측 수를 대조해 집계표를 검산할 수 있습니다.
  • 집계표가 보여 주는 사실과 별도 근거가 필요한 해석을 구분할 수 있습니다.

AI에게 맡기는 프롬프트

출동 CSV로 월간 집계표를 만들 때 코드를 실행할 수 있는 AI(파일 분석 기능이나 코딩 에이전트)에 주는 프롬프트입니다. 원자료를 외부 AI에 넣어도 되는지는 생성형 AI 입력 확인으로 먼저 점검합니다. 확인되지 않았으면 열 이름과 합성 몇 줄만 주고 코드를 받아 기관 PC에서 실제 자료로 실행합니다. AI에게는 집계표처럼 값이 없는 결과만 다시 보여 줍니다.

TEXT
[목적] 출동 기록 CSV로 보고용 월간 출동 집계표를 만듭니다.
[자료] 붙임: dispatch.csv. 열은 reported_at(접수 일시), incident_type(출동 유형), district(행정동), incident_id(사건 번호)입니다. 실제 신고자·환자 정보가 없는 합성 자료입니다.
[정의]
- 출동 건수는 행 수로 세고, 고유 incident_id 수도 함께 보여 주세요.
- 월은 reported_at 기준의 연·월(예: 2025-01)로 나눠 주세요. 해가 다른 같은 달을 합치지 마세요.
- 날짜로 바꾸지 못한 행과 빈 칸은 지우거나 채우지 말고 건수와 행 목록을 따로 보여 주세요.
[완성 기준] 월별, 유형별, 요일×시간대, 지역별 표. 표마다 합계 행을 붙이고, 지역 표에는 상위 10개 밖 합계와 지역이 빈 행 수를 따로 적어 주세요.
각 표의 합계가 전체 행 수와 같은지 출력해 주세요. 다르면 표를 저장하지 말고 차이를 보여 주세요.
[하지 말 것] 표에 없는 원인·평가·대책은 쓰지 말고, 인력 배치 같은 운영 판단은 확정하지 마세요.
자료에 없는 값은 [확인 필요]로 남겨 주세요.
확인 기준은 https://119-ai-studio.kr/dispatch-stats.html 안내서를 따릅니다.
작업 전에 빠진 정보가 있으면 먼저 질문해 주세요.

결과를 받은 뒤 확인할 것은 다음과 같습니다.

  • 각 표의 합계가 전체 행 수와 같다는 검산 결과가 출력에 실제로 찍혔는지 봅니다.
  • 월 표시에 연도가 붙어 있는지(2025-01) 봅니다. 월 숫자만 있으면 해가 다른 같은 달이 합쳐졌을 수 있습니다.
  • 행 수와 고유 incident_id 수가 다르면 중복 사건인지 세부 기록인지 원자료에서 확인합니다.

표준 열 4개와 시각 기준

출동 기록에서 네 개의 열만 있으면 됩니다.

  • reported_at: 접수 일시
  • incident_type: 출동 유형
  • district: 지역 단위(행정동 등)
  • incident_id: 중복 확인·검산용 사건 번호

reported_at은 119종합상황실이 신고를 받은 접수 시각으로 둡니다. 화재조사 및 보고규정은 출동, 도착, 초진, 완진을 각각 다른 시점으로 정의합니다. 한 열에 이 시점들을 섞지 않고, 원자료의 시각 열이 어느 시점인지 담당 부서에 확인한 뒤 집계합니다. 다른 시점으로 집계했다면 표 제목에 그 기준을 적습니다.

부서마다 열 이름이 다르면 매핑으로 맞춥니다.

PYTHON
COLUMN_MAP = {
    "신고일시": "reported_at",
    "사고유형": "incident_type",
    "행정동": "district",
}

동작 원리

월간 집계는 네 단계로 이뤄집니다.

  1. 일시 문자열을 날짜형으로 바꾸고 연월·요일·시간대 같은 기준 열을 만듭니다.
  2. 기준값이 같은 행끼리 묶습니다(groupby).
  3. 묶음마다 행 수를 셉니다.
  4. 묶음별 수를 다시 더해 입력 전체와 대조합니다.

size()는 묶음 안의 행 수를 세고, 특정 열의 count()는 그 열에서 비어 있지 않은 값만 셉니다. 같은 표에서도 둘의 합이 다를 수 있으므로 "출동 건수"가 행 수인지, 고유 사건 수인지 먼저 정해야 합니다.

묶을 때는 글자가 정확히 같은 값끼리만 한 묶음이 됩니다. 표기를 먼저 맞추는 방법은 출동 데이터의 결측·중복·표기 불일치 정제하기에 있습니다.

마지막 검산은 잘게 나눈 묶음의 수를 다시 더해 입력 전체와 비교하는 일입니다. 다만 중복 사건이 원본에 두 줄이면 양쪽 모두 두 줄로 세기 때문에 합은 맞습니다. 따라서 행 수 검산은 집계 누락을 찾는 검사이고, 고유값·결측·분류 정확성 검사는 별도로 남습니다. 함수 설명은 pandas 코스의 그룹화 장과 날짜와 시간 장에 있습니다.

선택지와 트레이드오프

도구 이름보다 질문의 모양을 먼저 봅니다. 도구에 따라 결과 표의 형태와 결측 처리 방식이 달라집니다.

선택지맞는 질문장점주의할 점
value_counts()한 열의 값별 빈도를 빠르게 볼 때짧고 빈도순 결과를 바로 얻음기본으로 빈 값을 빼므로 결측 처리를 확인해야 함
groupby(...).size()한 개 이상의 기준으로 행 수를 셀 때여러 기준을 조합하고 후속 집계를 붙이기 쉬움원본 표기가 흔들리면 묶음도 갈라짐
pd.crosstab(), pivot_table()요일×시간대처럼 두 축의 격자가 필요할 때보고용 교차표를 바로 만듦pivot_table에서 특정 열을 세면 그 열이 빈 행은 빠짐
고유 incident_id 집계한 사건이 여러 행일 수 있을 때중복 행과 사건 건수를 구분할 수 있음ID 누락·재사용 여부를 먼저 검사해야 함
AI·엑셀 기능에 맡기기코드 없이 표와 차트가 필요할 때요청 몇 번으로 결과를 얻음건수 정의·연월 키·결측 보고를 프롬프트에 적고 합계를 직접 검산해야 함

집계 4종

코드는 사람이 짜든 AI가 짜든 아래와 같은 모양이 됩니다. AI가 낸 코드를 받으면 이 코드와 견주어 월 키와 결측 처리를 확인합니다.

PYTHON
df["일시"] = pd.to_datetime(df["reported_at"])
df["연월"] = df["일시"].dt.to_period("M")          # 2025-01처럼 연도가 붙은 월
df["요일"] = df["일시"].dt.dayofweek.map(dict(enumerate("월화수목금토일")))
df["시간대"] = df["일시"].dt.hour
monthly = df.groupby("연월").size().rename("건수")
by_type = df["incident_type"].value_counts().rename("건수")
heat = (pd.crosstab(df["요일"], df["시간대"])      # 행 수를 세는 교차표
          .reindex(index=list("월화수목금토일"), columns=range(24), fill_value=0))
top_dong = df["district"].value_counts().head(10).rename("건수")

월 숫자(dt.month)만 뽑아 묶으면 자료가 해를 넘길 때 2025년 1월과 2026년 1월이 한 칸에 합쳐지므로 to_period("M")으로 연도가 붙은 월을 씁니다. 교차표는 crosstab으로 행 수를 세고, 건수가 0인 칸도 보이도록 reindex로 채웁니다.

다만 top_dong은 보고용 일부 보기입니다. value_counts()는 기본적으로 빈 지역명(NaN)을 제외하고, head(10)은 상위 10개 밖 지역을 숨깁니다. 보고 전에는 df["district"].isna().sum()으로 결측 건수를 따로 세고 df["district"].value_counts(dropna=False)의 전체 합을 입력 행 수와 대조합니다. 이 검사를 통과한 뒤에만 TOP10을 축약 표로 사용합니다.

가상 사례: 상황실 담당자의 교대 시간대 비교

다음은 설명을 위한 가상 사례입니다. 상황실 담당자가 승인된 출동 CSV에서 교대 전후 시간대의 접수 건수 분포를 확인하려 합니다. 입력에는 일시가 비어 있는 행과 형식이 다른 문자열이 섞여 있습니다. 담당자는 인력 배치를 단정하지 않고, 원자료 정비가 필요한 구간을 찾으려 합니다.

예제의 pd.to_datetime(df["reported_at"])는 읽을 수 없는 날짜 문자열을 만나면 기본 설정에서 변환을 중단합니다. 담당자는 오류가 가리킨 원본 행을 확인하고, 수정 또는 제외를 승인받아 입력을 정비한 뒤 날짜 변환부터 다시 실행합니다. 시간대별 건수가 높아도 곧바로 "인력이 부족하다"고 쓰지 않고, 그 시간대의 입력 품질과 사건 중복을 먼저 확인합니다. 오류를 피하려고 날짜를 임의로 채우거나 문제 행을 근거 없이 지운 뒤 재실행하면 실패합니다.

가상 사례: AI가 낸 월별 표가 원자료와 다를 때

다음은 학습용 가상 사례입니다(숫자는 예시입니다). 담당자가 2025년 1월부터 2026년 1월까지의 합성 출동 CSV 3,000행을 대화형 AI에 주고 월별 표와 요일×시간대 표를 받았습니다.

월별 표는 1월부터 12월까지 12줄이고 합계 3,000으로 검산을 통과했습니다. 그런데 1월 건수가 다른 달의 두 배쯤 됩니다. 코드를 보니 월 숫자(dt.month)로 묶어 2025년 1월과 2026년 1월이 합쳐져 있었습니다. 합계 검산은 행이 빠졌는지만 보므로 이 오류를 잡지 못합니다. 담당자는 연월로 다시 묶어 달라고 요청해 13줄짜리 표를 받습니다.

요일×시간대 표는 합이 2,980이었습니다. AI가 pivot_table로 incident_id를 세는 바람에 사건 번호가 빈 20행이 빠졌습니다. 담당자는 행 수를 세는 교차표로 바꾸게 하고, 번호가 빈 20행은 목록으로 받아 원자료 담당에게 확인을 요청합니다. 합계가 맞는다는 이유로 1월 건수를 그대로 보고하거나, 교차표 합이 다른 이유를 확인하지 않고 쓰면 실패입니다.

검산: 집계 합 = 전체 행 수

집계가 끝나면 각 표의 합이 전체 행 수와 같은지 확인하고, 다르면 멈춥니다. 같은 자료를 연월로도, 유형으로도, 요일×시간대로도 나눠 셌으므로 각 합은 서로 같고 전체 행 수와도 같아야 합니다.

PYTHON
# 검산: 집계의 합 = 전체 행 수 (다르면 멈추고 사람이 본다)
assert monthly.sum() == len(df) == by_type.sum() == heat.to_numpy().sum(), "검산 실패: 집계 합이 전체 행 수와 다릅니다"
print(f"[검산] 연월 합 {monthly.sum():,} = 유형 합 {by_type.sum():,} = 전체 {len(df):,}")

이 검산은 집계 과정의 누락을 잡습니다. 날짜가 빈 행은 오류 없이 빈 날짜(NaT)가 되어 연월 묶음에서 빠지는데, 이때도 합이 맞지 않아 여기서 멈춥니다. 반대로 원자료 자체가 틀린 것은 못 잡습니다. 입력에 중복이 남아 있거나 유형이 잘못 분류돼 있으면 합은 맞아도 결론은 틀립니다. 그래서 행 수와 고유 incident_id 수를 함께 보고, 정제 단계의 기록과 맞춰 봅니다.

▸ 소방 활용 포인트
이 assert 한 줄은 코드 안의 집계 누락을 자동으로 잡습니다. 집계표를 보고서로 옮겨 적을 때의 실수는 잡지 못하므로 보고서의 합계도 원표와 한 번 더 대조합니다. 합성 자료로 만든 표에는 합성 표기를 남깁니다.

숫자를 문장으로 옮길 때

집계표에서 3월 건수가 다른 달보다 많아도, 표로 알 수 있는 것은 "3월에 건수가 많았다"까지입니다. 왜 많았는지(계절 요인, 입력 방식 변경, 특정 사건)는 표 밖의 근거가 필요합니다. 집계표로 보고 문장을 받을 때는 다음처럼 요청합니다.

TEXT
아래 표는 합성 자료(또는 승인된 집계표)입니다. 이 표로 보고용 문장 세 개를 써 주세요.
표에 없는 원인·평가·책임 소재는 쓰지 마세요.
문장마다 근거가 된 표의 숫자를 괄호로 붙여 주세요.
공식 판단처럼 보이는 표현은 [확인 필요]로 바꿔 주세요.

아래 문장은 합성 데이터 기준 예시입니다.

표로 알 수 있는 것별도 근거가 필요한 것
합성 데이터 기준 3월 건수가 다른 월보다 많습니다3월에 실제 위험이 증가했습니다
해당 집계 단위에서 건수가 높게 나타납니다이 지역은 위험지역입니다
야간 시간대 건수가 주간보다 많습니다야간 근무 인력이 부족합니다
이 집계는 내부 확인용입니다관할 공식 통계입니다

오른쪽 문장을 쓰려면 표가 아닌 별도 근거가 있어야 합니다. 내부 집계와 공식 통계의 구분은 용어 사전의 내부관리통계와 국가승인통계 정의를 봅니다. 이 경계를 제출 직전에 점검하는 절차는 AI 결과, 제출 전 확인 사항에 있습니다.

내부 집계를 공식 통계와 비교하려면 국가화재정보시스템 화재통계 조회나 소방청 화재정보서비스를 보고, 기간·관서·건수 정의가 같은지 먼저 적습니다. AI 도구에서 공개 통계를 조회하는 소방안전 빅데이터 플랫폼 MCP 서비스의 답도 플랫폼의 원 데이터와 대조해 씁니다.

⚠️ 흔한 실수
이 안내서의 숫자는 모두 합성 예시이며 현실 통계가 아닙니다. 합성 자료를 쓸 때 정할 것은 119 합성 데이터로 안전하게 연습하기에 있습니다.

문제 해결

증상가능한 원인처방확인
날짜 변환에서 실행이 중단됨기본 변환이 읽을 수 없거나 형식이 다른 날짜 문자열을 만남원자료 형식을 format=으로 명시하고, 읽지 못한 원본 행은 수정·제외 승인 후 처음부터 재실행정비 사유를 기록하고 재실행한 전체 행 수와 집계 합을 확인
같은 달 건수가 유난히 큼월 숫자만으로 묶어 해가 다른 같은 달이 합쳐짐to_period("M")으로 연월 키를 만듦연월 표의 줄 수가 자료 기간의 달 수와 같은지 확인
월별 합은 맞는데 유형별 합이 작음유형 열의 결측을 count()로 세었음행 수 질문이면 size()를 쓰고 결측 유형을 별도 표시결측 포함 여부를 맞춘 두 합을 다시 비교
사건 수가 예상보다 많음한 incident_id가 여러 행에 존재중복이 오류인지 세부 기록 구조인지 먼저 확인행 수와 고유 ID 수를 함께 보고 차이 사유 기록
지역 TOP10 합이 전체보다 작음결측이 기본 제외되고 10위 밖 지역이 잘림결측 수와 dropna=False 전체 분포를 먼저 산출전체 분포 합이 입력 행 수와 같은지 확인

자주 묻는 질문

Q. 검산 합이 맞으면 보고서 숫자를 믿어도 되나요?

집계 과정에서 행이 빠지지 않았다는 근거는 됩니다. 원자료의 중복·오분류·잘못된 일시나 해가 섞인 월 키까지 맞다는 뜻은 아니므로 고유 ID, 결측, 연월 표시 검사가 함께 필요합니다.

Q. 지역 TOP10만 보고해도 되나요?

상위 목록은 나머지 지역을 감춥니다. 전체 지역 수와 제외된 결측 건수를 함께 제시하고, TOP10이 전체를 대표한다는 표현은 별도 근거 없이 쓰지 않습니다.

Q. 집계표를 AI에 바로 붙여넣어 문장을 만들어도 되나요?

원자료나 개인정보 열은 넣지 않고 집계표만 줍니다. 집계표라도 표에 없는 원인·책임·대책을 만들지 않도록 제한하고, 받은 문장은 사람이 원표와 대조합니다.

핵심 정리

  • 집계 전에 건수 기준(행 수 또는 고유 사건 수), 시각 기준(접수), 연·월 키를 먼저 정합니다.
  • AI나 엑셀 기능에 맡길 때도 이 정의와 결측·변환 실패 보고를 프롬프트에 적고, 원자료를 넣기 전에 입력 가능 여부를 점검합니다.
  • 각 표의 합과 전체 행 수의 대조는 집계 누락을 잡고, 중복·결측·오분류는 고유 ID와 결측 수로 따로 확인합니다.
  • 집계표가 보여 주는 차이와 원인·운영 판단을 분리해 보고합니다.

다음 단계