본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 16. · 7 Views
10년간 서울 기온 변화 분석 프로젝트
기상청 공공데이터를 활용하여 2014년부터 2023년까지 서울의 기온 변화를 분석합니다. 데이터 수집부터 시각화까지 실무 데이터 분석 프로세스를 단계별로 따라가며 배웁니다.
목차
1. 프로젝트 계획 수립
데이터 분석가로 취업한 지 한 달 된 김분석 씨에게 첫 실전 프로젝트가 주어졌습니다. "서울의 최근 10년간 기온 변화를 분석해서 보고서를 작성해주세요." 막막했습니다.
어디서부터 시작해야 할까요?
프로젝트 계획 수립은 데이터 분석의 첫걸음입니다. 무작정 코드를 작성하기 전에 분석 목표를 명확히 정의하고, 필요한 데이터와 도구를 파악하며, 작업 단계를 구조화하는 과정입니다.
마치 여행을 떠나기 전에 지도를 펼쳐 경로를 확인하는 것과 같습니다.
다음 코드를 살펴봅시다.
# 프로젝트 구조 설계
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime
# 분석 기간 설정
START_YEAR = 2014
END_YEAR = 2023
# 분석 목표 정의
ANALYSIS_GOALS = [
"연도별 평균 기온 추세 파악",
"월별 기온 패턴 분석",
"계절별 기온 변화 비교",
"이상 기온 현상 탐지"
]
# 프로젝트 디렉토리 구조
# data/ - 원본 데이터
# output/ - 분석 결과 및 시각화
# reports/ - 최종 보고서
김분석 씨는 선배인 박데이터 씨에게 조언을 구했습니다. "처음 프로젝트라 어떻게 시작해야 할지 모르겠어요." 박데이터 씨가 말했습니다.
"데이터 분석은 요리와 비슷해요. 요리를 시작하기 전에 레시피를 확인하고 재료를 준비하듯이, 분석도 계획부터 세워야 합니다." 프로젝트 계획 수립이란 정확히 무엇일까요?
쉽게 비유하자면, 프로젝트 계획은 마치 건축 설계도와 같습니다. 건물을 짓기 전에 설계도를 그리듯이, 데이터 분석도 시작하기 전에 전체 그림을 그려야 합니다.
어떤 질문에 답할 것인지, 어떤 데이터가 필요한지, 어떤 도구를 사용할 것인지 미리 정리하는 과정입니다. 계획 없이 분석을 시작하면 어떻게 될까요?
많은 초보 분석가들이 흥미로워 보이는 데이터를 발견하면 바로 코드를 작성하기 시작합니다. 하지만 중간에 방향을 잃거나, 필요한 데이터가 없다는 것을 뒤늦게 발견하곤 합니다.
시간을 낭비하고, 분석 결과도 명확하지 않게 됩니다. 바로 이런 문제를 해결하기 위해 체계적인 계획 수립이 필요합니다.
먼저 분석 목표를 명확히 정의해야 합니다. "서울의 기온이 어떻게 변했는지 알고 싶다"는 막연한 목표가 아니라, "연도별 평균 기온의 상승 또는 하락 추세를 파악하고, 계절별 변화 패턴을 비교한다"처럼 구체적으로 설정합니다.
다음으로 필요한 데이터를 파악합니다. 서울의 기온 데이터는 기상청에서 제공하는 공공데이터를 활용할 수 있습니다.
10년치 데이터이므로 2014년부터 2023년까지의 일별 기온 데이터가 필요합니다. 사용할 도구도 미리 정해둡니다.
Python의 pandas 라이브러리로 데이터를 처리하고, matplotlib로 시각화하며, numpy로 통계 계산을 수행할 것입니다. 작업 단계를 나눠봅니다.
데이터 수집, 전처리, 탐색적 분석, 시각화, 보고서 작성 순으로 진행할 계획입니다. 각 단계마다 체크포인트를 두어 진행 상황을 확인합니다.
프로젝트 폴더 구조도 미리 설계합니다. data 폴더에는 원본 데이터를, output 폴더에는 생성된 그래프를, reports 폴더에는 최종 보고서를 저장할 것입니다.
이렇게 정리하면 나중에 파일을 찾기도 쉽고, 다른 사람과 협업할 때도 편리합니다. 박데이터 씨가 덧붙였습니다.
"계획을 세울 때는 예상 소요 시간도 고려하세요. 데이터 수집에 하루, 전처리에 반나루, 분석과 시각화에 이틀 정도면 충분할 거예요." 김분석 씨는 노트북을 펼쳐 계획을 작성하기 시작했습니다.
분석 목표 네 가지를 명확히 적고, 각 단계별로 해야 할 작업을 정리했습니다. 막연했던 프로젝트가 갑자기 실체를 갖추기 시작했습니다.
좋은 계획은 프로젝트의 성공률을 크게 높입니다. 시간을 절약하고, 명확한 결과물을 만들 수 있습니다.
여러분도 데이터 분석을 시작하기 전에 반드시 계획을 수립하는 습관을 들이세요.
실전 팁
💡 - 분석 목표는 구체적이고 측정 가능하게 설정하세요
- 프로젝트 시작 전에 폴더 구조를 만들어두면 작업이 훨씬 수월합니다
- 예상 일정을 세우되, 여유를 20% 정도 더 두는 것이 좋습니다
2. 기상청 데이터 수집 및 로드
계획을 세운 김분석 씨는 이제 실제 데이터를 구해야 했습니다. 기상청 홈페이지를 방문했지만, 어디서 어떻게 데이터를 다운로드해야 하는지 막막했습니다.
CSV 파일을 받았는데, 이걸 어떻게 Python으로 불러올까요?
데이터 수집 및 로드는 분석의 첫 실질적 단계입니다. 기상청 공공데이터 포털에서 서울 지역의 일별 기온 데이터를 CSV 형식으로 다운로드하고, pandas의 read_csv 함수로 Python 환경에 불러옵니다.
이때 날짜 형식 변환과 결측치 확인이 중요합니다.
다음 코드를 살펴봅시다.
import pandas as pd
import os
# CSV 파일 경로 설정
DATA_PATH = "data/seoul_temperature_2014_2023.csv"
# 데이터 로드 (날짜를 datetime 형식으로 변환)
df = pd.read_csv(
DATA_PATH,
encoding='cp949', # 한글 인코딩 처리
parse_dates=['날짜'], # 날짜 컬럼을 datetime으로 변환
index_col='날짜' # 날짜를 인덱스로 설정
)
# 데이터 기본 정보 확인
print(f"데이터 형태: {df.shape}")
print(f"결측치 개수:\n{df.isnull().sum()}")
print(f"기간: {df.index.min()} ~ {df.index.max()}")
김분석 씨는 기상청 기상자료개방포털에 접속했습니다. 회원가입을 하고 "관측 자료 다운로드" 메뉴를 클릭했습니다.
지역은 서울, 관측 요소는 기온, 기간은 2014년 1월 1일부터 2023년 12월 31일까지 설정했습니다. 다운로드 버튼을 누르자 seoul_temperature_2014_2023.csv 파일이 저장되었습니다.
파일을 엑셀로 열어보니 날짜, 평균기온, 최고기온, 최저기온 컬럼이 보였습니다. 총 3,652행의 데이터였습니다.
이제 이 데이터를 Python으로 불러와야 합니다. 여기서 pandas라는 강력한 도구가 등장합니다.
pandas는 마치 엑셀을 Python에서 사용할 수 있게 해주는 마법 같은 라이브러리입니다. 표 형태의 데이터를 읽고, 수정하고, 분석하는 모든 작업을 쉽게 처리할 수 있습니다.
데이터 분석가에게 pandas는 요리사에게 칼과 같은 필수 도구입니다. 데이터를 불러올 때 주의할 점이 있습니다.
첫째, 인코딩 문제입니다. 한국어가 포함된 CSV 파일은 대부분 cp949 인코딩을 사용합니다.
이를 명시하지 않으면 한글이 깨져서 나타납니다. encoding='cp949' 옵션으로 해결할 수 있습니다.
둘째, 날짜 형식 변환입니다. CSV 파일에서 날짜는 그냥 문자열로 저장되어 있습니다.
"2014-01-01" 같은 형식이죠. 이를 Python의 datetime 객체로 변환해야 날짜 계산이나 필터링을 쉽게 할 수 있습니다.
parse_dates 옵션이 이 작업을 자동으로 수행합니다. 셋째, 인덱스 설정입니다.
날짜를 인덱스로 설정하면 시계열 데이터 분석이 훨씬 편리해집니다. "2020년 데이터만 추출" 같은 작업을 간단한 코드로 처리할 수 있습니다.
코드를 한 줄씩 살펴보겠습니다. pd.read_csv() 함수는 CSV 파일을 읽어서 DataFrame이라는 표 형태의 객체로 만듭니다.
DataFrame은 pandas의 핵심 자료구조입니다. 첫 번째 인자로 파일 경로를 전달합니다.
encoding='cp949'는 한글 인코딩을 처리합니다. 만약 이 옵션 없이 실행했다가 에러가 발생하면 encoding='utf-8'이나 encoding='euc-kr'도 시도해볼 수 있습니다.
parse_dates=['날짜']는 '날짜' 컬럼의 값들을 자동으로 datetime 형식으로 변환합니다. 이제 날짜로 정렬하거나, 연도와 월을 추출하는 작업이 가능해집니다.
index_col='날짜'는 날짜 컬럼을 DataFrame의 인덱스로 설정합니다. 이렇게 하면 특정 날짜의 데이터를 빠르게 찾을 수 있습니다.
데이터를 불러온 후에는 반드시 기본 정보를 확인해야 합니다. df.shape는 데이터의 행과 열 개수를 알려줍니다.
(3652, 3)이라면 3,652개의 날짜에 대해 3개의 기온 정보가 있다는 뜻입니다. df.isnull().sum()은 각 컬럼별로 결측치가 몇 개인지 계산합니다.
만약 결측치가 많다면 전처리 단계에서 처리해야 합니다. df.index.min()과 df.index.max()는 데이터의 시작일과 종료일을 보여줍니다.
우리가 원하는 기간의 데이터가 맞는지 확인할 수 있습니다. 김분석 씨가 코드를 실행하자 화면에 정보가 출력되었습니다.
"데이터 형태: (3652, 3)", "결측치 개수: 평균기온 5, 최고기온 3, 최저기온 7". 몇 개의 결측치가 있네요.
다음 단계에서 처리해야겠습니다. 데이터를 성공적으로 불러왔습니다.
이제 본격적인 분석을 시작할 준비가 되었습니다.
실전 팁
💡 - CSV 파일을 처음 열 때는 엑셀로 먼저 확인해보면 구조를 이해하기 쉽습니다
- 인코딩 에러가 발생하면
encoding='utf-8-sig'나encoding='latin1'도 시도해보세요 - 데이터 로드 후
df.head()로 처음 5행을 확인하는 습관을 들이세요
3. 데이터 정제 및 전처리
데이터를 불러온 김분석 씨는 결측치가 몇 개 있다는 것을 발견했습니다. 또한 데이터에 이상한 값들도 보였습니다.
섭씨 60도? 말이 안 됩니다.
박데이터 씨가 말했습니다. "실제 데이터는 항상 완벽하지 않아요.
정제 작업이 필수입니다."
데이터 정제 및 전처리는 원본 데이터의 오류를 수정하고 분석 가능한 형태로 만드는 과정입니다. 결측치 처리, 이상치 탐지 및 제거, 데이터 타입 변환 등이 포함됩니다.
마치 요리하기 전에 재료를 다듬는 것과 같습니다.
다음 코드를 살펴봅시다.
import pandas as pd
import numpy as np
# 결측치 확인 및 처리
print("결측치 처리 전:", df.isnull().sum().sum())
# 선형 보간법으로 결측치 채우기
df['평균기온'] = df['평균기온'].interpolate(method='linear')
df['최고기온'] = df['최고기온'].interpolate(method='linear')
df['최저기온'] = df['최저기온'].interpolate(method='linear')
# 이상치 탐지 (사분위수 기반)
def remove_outliers(series, multiplier=3):
Q1 = series.quantile(0.25)
Q3 = series.quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - multiplier * IQR
upper = Q3 + multiplier * IQR
return series.clip(lower, upper)
# 각 컬럼에 이상치 처리 적용
df['평균기온'] = remove_outliers(df['평균기온'])
df['최고기온'] = remove_outliers(df['최고기온'])
df['최저기온'] = remove_outliers(df['최저기온'])
print("결측치 처리 후:", df.isnull().sum().sum())
박데이터 씨가 설명을 시작했습니다. "실제 현장 데이터는 교과서처럼 깔끔하지 않아요.
측정 장비 오류, 통신 두절, 입력 실수 등으로 문제가 생기죠." 김분석 씨가 물었습니다. "그럼 이런 데이터는 그냥 버려야 하나요?" "아니요.
대부분은 복구하거나 적절히 처리할 수 있습니다." 데이터 정제란 무엇일까요? 쉽게 비유하면, 데이터 정제는 마치 채소를 다듬는 것과 같습니다.
시장에서 사온 채소에는 흙이 묻어있고, 시든 잎도 있고, 벌레 먹은 부분도 있습니다. 이것들을 제거하고 씻어서 요리에 사용할 수 있게 만드는 과정이 바로 데이터 정제입니다.
먼저 결측치 문제를 해결해야 합니다. 결측치란 값이 없는 데이터를 말합니다.
기상 관측 장비가 잠시 고장났거나, 데이터 전송 중 손실이 발생했을 수 있습니다. 우리 데이터에는 평균기온 5개, 최고기온 3개, 최저기온 7개의 결측치가 있었습니다.
결측치를 처리하는 방법은 여러 가지가 있습니다. 그냥 삭제할 수도 있지만, 10년치 데이터 중 15개 정도는 복구하는 것이 더 좋습니다.
보간법을 사용하면 전후 데이터를 바탕으로 합리적인 값을 추정할 수 있습니다. 선형 보간법은 간단하지만 효과적입니다.
예를 들어 1월 5일과 1월 7일의 기온이 각각 -2도, 2도인데 1월 6일 데이터가 없다면, 중간값인 0도로 채우는 것입니다. interpolate(method='linear') 함수가 이 작업을 자동으로 수행합니다.
다음은 이상치 문제입니다. 이상치란 다른 데이터와 크게 동떨어진 값을 말합니다.
서울의 여름 기온이 60도라면? 명백한 측정 오류입니다.
이런 값들은 분석 결과를 왜곡시킬 수 있습니다. 이상치를 찾는 전통적인 방법으로 사분위수(IQR) 방법이 있습니다.
데이터를 크기 순으로 정렬했을 때, 25% 지점을 Q1, 75% 지점을 Q3라고 합니다. Q3에서 Q1을 뺀 값이 IQR입니다.
통계적으로 Q1 - 3×IQR보다 작거나 Q3 + 3×IQR보다 큰 값은 이상치로 간주합니다. 코드를 자세히 살펴보겠습니다.
remove_outliers 함수는 이상치를 탐지하고 처리하는 역할을 합니다. quantile(0.25)는 하위 25% 지점의 값을, quantile(0.75)는 상위 25% 지점의 값을 계산합니다.
IQR = Q3 - Q1은 사분위수 범위를 계산합니다. 이 값이 크면 데이터의 분산이 크다는 의미입니다.
series.clip(lower, upper)는 이상치를 경계값으로 대체합니다. 예를 들어 하한이 -20도, 상한이 40도인데 60도 값이 있다면, 이를 40도로 조정하는 것입니다.
완전히 삭제하는 것보다 보수적인 접근입니다. 실제 프로젝트에서는 어떻게 활용할까요?
전자상거래 회사의 주문 데이터를 분석한다고 가정해봅시다. 가격 컬럼에 -1000원이나 100억 원 같은 값이 있다면 명백한 오류입니다.
이런 값들을 정제하지 않으면 평균 주문 금액이 왜곡됩니다. 하지만 주의할 점이 있습니다.
이상치가 항상 오류는 아닙니다. 때로는 중요한 정보를 담고 있을 수도 있습니다.
예를 들어 특정 날짜에 기온이 급격히 떨어졌다면, 그것은 한파라는 실제 기상 현상일 수 있습니다. 따라서 이상치를 제거하기 전에 항상 도메인 지식을 활용해 판단해야 합니다.
김분석 씨가 코드를 실행했습니다. 결측치가 모두 채워졌고, 극단적인 값들도 합리적인 범위로 조정되었습니다.
"이제 데이터가 깔끔해졌네요!" 박데이터 씨가 웃으며 말했습니다. "전처리에 시간의 80%를 쓴다는 말이 괜히 있는 게 아니에요.
하지만 이 과정을 제대로 하면 분석 결과의 신뢰도가 크게 높아집니다."
실전 팁
💡 - 결측치 처리 전에 결측 비율을 확인하세요. 50% 이상이면 해당 컬럼 자체를 제외하는 것이 좋습니다
- 이상치를 제거하기 전에 시각화해서 분포를 확인하는 것이 안전합니다
df.describe()로 통계 요약을 확인하면 이상한 값을 빠르게 발견할 수 있습니다
4. 연도별 월별 평균 기온 계산
깨끗해진 데이터를 보며 김분석 씨는 생각했습니다. "10년치 일별 데이터가 3,652개나 되는데, 이걸 어떻게 의미 있는 정보로 만들지?" 박데이터 씨가 힌트를 주었습니다.
"먼저 연도별, 월별로 평균을 내보세요. 패턴이 보일 겁니다."
집계 연산은 상세한 데이터를 의미 있는 단위로 요약하는 과정입니다. 일별 데이터에서 연도와 월을 추출하고, groupby 함수로 그룹화한 후 평균을 계산합니다.
이를 통해 전체적인 트렌드를 파악할 수 있습니다.
다음 코드를 살펴봅시다.
import pandas as pd
# 연도와 월 컬럼 추가
df['연도'] = df.index.year
df['월'] = df.index.month
# 연도별 평균 기온 계산
yearly_avg = df.groupby('연도')['평균기온'].mean().round(2)
print("연도별 평균 기온:")
print(yearly_avg)
# 월별 평균 기온 계산 (전체 기간)
monthly_avg = df.groupby('월')['평균기온'].mean().round(2)
print("\n월별 평균 기온:")
print(monthly_avg)
# 연도-월 조합으로 집계
df['연월'] = df.index.to_period('M')
year_month_avg = df.groupby('연월')['평균기온'].mean().round(2)
print(f"\n총 {len(year_month_avg)}개월 데이터")
김분석 씨는 엑셀에서 피벗 테이블을 사용해본 경험이 있었습니다. "pandas에도 비슷한 기능이 있나요?" "있죠.
더 강력합니다." 박데이터 씨가 답했습니다. 집계 연산이란 무엇일까요?
쉽게 비유하면, 집계 연산은 마치 학생들의 성적표를 반별, 과목별로 평균 내는 것과 같습니다. 100명의 학생 개별 점수보다 "1반 수학 평균 85점"이라는 요약 정보가 더 의미 있을 때가 많습니다.
마찬가지로 3,652개의 일별 기온보다 "2020년 평균 기온 12.5도"라는 정보가 트렌드를 파악하기 쉽습니다. pandas의 groupby는 이런 집계 작업의 핵심 도구입니다.
groupby는 "분할-적용-결합" 패턴으로 동작합니다. 먼저 데이터를 특정 기준으로 그룹으로 나눕니다.
각 그룹에 함수를 적용합니다(예: 평균 계산). 마지막으로 결과를 하나로 합칩니다.
이 세 단계가 한 줄의 코드로 이루어집니다. 연도별 집계를 어떻게 할까요?
먼저 날짜 인덱스에서 연도 정보를 추출해야 합니다. df.index.year는 각 날짜의 연도를 반환합니다.
예를 들어 2020-03-15라는 날짜에서 2020을 추출하는 것입니다. 이를 새로운 컬럼으로 추가합니다.
df.groupby('연도')는 데이터를 연도별로 그룹화합니다. 2014년 데이터끼리, 2015년 데이터끼리 묶이는 것입니다.
이제 각 그룹에 대해 평균기온의 평균을 계산합니다. ['평균기온'].mean()은 각 연도 그룹의 평균기온 컬럼에 대해 평균을 계산합니다.
2014년 365개 값의 평균, 2015년 365개 값의 평균... 이렇게 10개의 값이 나옵니다.
round(2)는 소수점 둘째 자리까지 반올림합니다. 12.3456789도를 12.35도로 만드는 것이죠.
보고서에 쓰기 좋은 형태입니다. 월별 집계는 조금 다릅니다.
전체 10년 데이터를 월별로 묶으면 "모든 1월의 평균", "모든 2월의 평균" 같은 정보를 얻을 수 있습니다. 이는 계절 패턴을 파악하는 데 유용합니다.
예를 들어 1월 평균이 -2도, 7월 평균이 25도라면 겨울과 여름의 기온 차이를 명확히 알 수 있습니다. 더 세밀한 분석을 위해서는 연월 조합 집계가 필요합니다.
to_period('M')은 날짜를 월 단위로 변환합니다. 2020-03-01부터 2020-03-31까지를 모두 "2020-03"으로 표현하는 것입니다.
이렇게 하면 120개월(10년×12개월)의 평균 기온을 얻을 수 있습니다. 실제 업무에서는 이런 집계가 필수입니다.
온라인 쇼핑몰의 판매 데이터를 분석한다면? 일별 매출보다 월별 매출 추이가 더 중요할 때가 많습니다.
"12월 매출이 높다"는 정보로 크리스마스 시즌 마케팅을 계획할 수 있습니다. 김분석 씨가 코드를 실행하자 결과가 출력되었습니다.
2014년 평균 12.8도에서 시작해 2023년 13.5도까지 점진적으로 상승하는 경향이 보였습니다. "기온이 정말 오르고 있네요!" 월별 평균을 보니 1월이 가장 낮고(-1.2도), 8월이 가장 높았습니다(26.3도).
한국의 뚜렷한 사계절 기후가 숫자로 확인되었습니다. 박데이터 씨가 만족스러운 표정으로 말했습니다.
"이제 의미 있는 패턴이 보이죠? 다음 단계는 이런 추세를 더 깊이 분석하는 겁니다."
실전 팁
💡 - groupby 후에는 mean() 외에도 sum(), count(), std() 등 다양한 함수를 사용할 수 있습니다
- 여러 컬럼을 동시에 그룹화하려면
groupby(['연도', '월'])처럼 리스트로 전달하세요 agg()함수를 사용하면 한 번에 여러 통계량을 계산할 수 있습니다
5. 기온 변화 트렌드 분석
연도별 평균 기온을 보니 확실히 상승하는 것 같았지만, 김분석 씨는 확신이 서지 않았습니다. "이게 진짜 추세일까요, 아니면 우연일까요?" 박데이터 씨가 답했습니다.
"선형 회귀로 통계적 추세를 확인해봅시다."
트렌드 분석은 시간에 따른 데이터의 변화 패턴을 통계적으로 분석하는 과정입니다. numpy의 polyfit 함수로 선형 회귀선을 그어 기온의 상승 또는 하락 추세를 정량화하고, 그 의미를 해석합니다.
다음 코드를 살펴봅시다.
import numpy as np
import pandas as pd
# 연도별 평균 기온 (숫자로 변환)
years = yearly_avg.index.values
temps = yearly_avg.values
# 선형 회귀 (1차 다항식 fitting)
coefficients = np.polyfit(years, temps, 1)
slope = coefficients[0] # 기울기
intercept = coefficients[1] # 절편
# 추세선 값 계산
trend_line = slope * years + intercept
# 결과 출력
print(f"기울기: {slope:.4f}도/년")
print(f"10년간 총 변화: {slope * 10:.2f}도")
# 상승/하락 판정
if slope > 0.01:
print("통계적으로 유의미한 상승 추세입니다.")
elif slope < -0.01:
print("통계적으로 유의미한 하락 추세입니다.")
else:
print("뚜렷한 추세가 보이지 않습니다.")
김분석 씨가 물었습니다. "눈으로 보면 올라가는 것 같은데, 숫자로 증명할 수 있나요?" "물론입니다.
그게 바로 통계의 힘이죠." 박데이터 씨가 답했습니다. 트렌드 분석이란 정확히 무엇일까요?
쉽게 비유하면, 트렌드 분석은 마치 산길을 올라가는지 내려가는지 확인하는 것과 같습니다. 걸으면서는 오르막인지 내리막인지 헷갈릴 수 있지만, 전체 경로를 그래프로 그리고 평균 경사도를 계산하면 명확해집니다.
데이터도 마찬가지입니다. 선형 회귀는 데이터에 가장 잘 맞는 직선을 찾는 방법입니다.
중학교 수학에서 배운 일차함수를 기억하시나요? y = ax + b 형태의 식입니다.
여기서 a는 기울기, b는 절편입니다. 선형 회귀는 실제 데이터 점들에 가장 가까운 직선의 a와 b 값을 찾는 과정입니다.
numpy의 polyfit 함수가 이 복잡한 계산을 대신해줍니다. np.polyfit(years, temps, 1)에서 첫 번째 인자는 x값(연도), 두 번째 인자는 y값(기온), 세 번째 인자 1은 1차 다항식(직선)을 의미합니다.
만약 2를 넣으면 포물선(2차 함수)을 그리는데, 우리는 단순한 추세만 보려고 하므로 1을 사용합니다. 함수는 두 개의 값을 반환합니다.
첫 번째가 기울기(slope), 두 번째가 절편(intercept)입니다. 기울기가 양수면 시간이 지날수록 기온이 상승한다는 의미이고, 음수면 하락한다는 의미입니다.
기울기를 어떻게 해석할까요? 우리 데이터에서 기울기가 0.0723이 나왔다고 가정해봅시다.
이는 "매년 평균 기온이 0.0723도씩 상승한다"는 뜻입니다. 작은 숫자 같지만, 10년이면 0.723도 상승입니다.
100년이면 7.23도나 됩니다. 기후 변화 관점에서는 심각한 수치입니다.
slope * years + intercept는 추세선의 y값을 계산합니다. 예를 들어 2020년의 추세선 값은 0.0723 * 2020 + intercept가 됩니다.
이 값들을 나중에 실제 데이터와 함께 그래프로 그리면 추세를 시각적으로 확인할 수 있습니다. 실제 데이터 분석에서는 이런 추세 분석이 중요한 결정을 만듭니다.
주식 가격 분석을 생각해봅시다. 단순히 "요즘 주가가 오르는 것 같아"가 아니라 "최근 6개월간 주가는 하루 평균 0.5% 상승 추세"라고 말할 수 있습니다.
이는 투자 결정의 근거가 됩니다. 날씨 데이터에서도 마찬가지입니다.
"여름이 더워진 것 같아"가 아니라 "최근 10년간 서울의 평균 기온은 연간 0.07도 상승했으며, 이는 통계적으로 유의미한 추세"라고 말할 수 있습니다. 주의할 점도 있습니다.
선형 추세가 항상 미래를 정확히 예측하는 것은 아닙니다. 기후는 복잡한 시스템이라 단순한 직선으로 설명하기 어렵습니다.
하지만 전체적인 방향성을 파악하는 데는 충분히 유용합니다. 김분석 씨가 결과를 보며 놀랐습니다.
"10년간 0.7도나 올랐네요. 생각보다 크네요." "맞아요.
이게 바로 데이터가 주는 통찰입니다. 체감으로는 알기 어려운 변화를 숫자로 명확히 보여주죠." 박데이터 씨가 말했습니다.
실전 팁
💡 - 기울기의 절댓값이 너무 작다면(예: 0.001) 실질적으로 변화가 없다고 해석할 수 있습니다
np.polyfit외에도 scipy의linregress함수를 쓰면 R² 같은 추가 통계량도 얻을 수 있습니다- 데이터 기간이 짧으면 추세가 우연일 수 있으니 최소 5년 이상의 데이터로 분석하세요
6. 계절별 기온 비교 시각화
숫자로 된 분석 결과를 보고서에 넣으려니 딱딱해 보였습니다. 김분석 씨는 고민했습니다.
"이걸 좀 더 직관적으로 보여줄 방법이 없을까?" 박데이터 씨가 말했습니다. "시각화가 답입니다.
그래프 하나가 표 백 개보다 강력할 때가 많아요."
데이터 시각화는 숫자를 그래프로 표현하여 패턴을 직관적으로 이해할 수 있게 하는 과정입니다. matplotlib를 사용해 계절별 기온 분포를 박스플롯으로, 연도별 추세를 선 그래프로 그리면 분석 결과가 한눈에 들어옵니다.
다음 코드를 살펴봅시다.
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
# 한글 폰트 설정
plt.rcParams['font.family'] = 'NanumGothic'
plt.rcParams['axes.unicode_minus'] = False
# 계절 구분 함수
def get_season(month):
if month in [3, 4, 5]:
return '봄'
elif month in [6, 7, 8]:
return '여름'
elif month in [9, 10, 11]:
return '가을'
else:
return '겨울'
df['계절'] = df['월'].apply(get_season)
# 계절별 기온 박스플롯
plt.figure(figsize=(10, 6))
df.boxplot(column='평균기온', by='계절', grid=False)
plt.title('계절별 기온 분포 (2014-2023)', fontsize=14)
plt.xlabel('계절', fontsize=12)
plt.ylabel('평균 기온 (℃)', fontsize=12)
plt.suptitle('') # 기본 제목 제거
plt.savefig('output/seasonal_temp_distribution.png', dpi=300, bbox_inches='tight')
plt.show()
김분석 씨는 엑셀에서 그래프를 그려본 경험이 있었습니다. "Python에서도 비슷하게 할 수 있나요?" "훨씬 더 강력하고 유연합니다." 박데이터 씨가 노트북을 열었습니다.
시각화란 무엇일까요? 쉽게 비유하면, 시각화는 마치 복잡한 기계의 작동을 도식으로 설명하는 것과 같습니다.
수백 페이지의 설명서보다 한 장의 그림이 더 이해하기 쉬울 때가 많습니다. 데이터도 마찬가지로, 3,652개의 숫자보다 하나의 그래프가 훨씬 명확한 메시지를 전달합니다.
Python의 matplotlib는 과학 기술 분야에서 가장 널리 쓰이는 시각화 라이브러리입니다. matplotlib는 MATLAB이라는 프로그램의 플로팅 기능을 모델로 만들어졌습니다.
매우 세밀한 제어가 가능해서, 논문이나 보고서에 들어갈 출판 품질의 그래프를 만들 수 있습니다. 한글 폰트 설정이 왜 필요할까요?
matplotlib는 기본적으로 영문 폰트를 사용합니다. 한글을 입력하면 네모 박스로 깨져서 나타납니다.
이를 해결하려면 한글을 지원하는 폰트로 설정해야 합니다. plt.rcParams['font.family'] = 'NanumGothic'가 이 작업을 수행합니다.
plt.rcParams['axes.unicode_minus'] = False는 마이너스 기호가 깨지는 문제를 해결합니다. 한글 폰트를 사용하면 음수 표시가 이상하게 나올 수 있는데, 이 설정으로 방지합니다.
계절을 어떻게 구분할까요? 우리는 get_season 함수를 만들어 월 번호를 계절 이름으로 변환합니다.
3, 4, 5월은 봄, 6, 7, 8월은 여름, 9, 10, 11월은 가을, 12, 1, 2월은 겨울로 분류합니다. 이는 한국의 기상학적 계절 구분과 일치합니다.
df['월'].apply(get_season)은 모든 행의 월 값에 이 함수를 적용합니다. 3월 데이터는 '봄'으로, 12월 데이터는 '겨울'로 변환됩니다.
이제 계절별로 그룹화할 수 있습니다. 박스플롯은 데이터 분포를 보여주는 강력한 도구입니다.
박스플롯은 다섯 가지 통계량을 한 번에 보여줍니다. 최솟값, 25% 지점(Q1), 중앙값, 75% 지점(Q3), 최댓값입니다.
박스의 높이는 데이터의 분산을 나타내고, 박스 안의 선은 중앙값을 표시합니다. 이상치는 점으로 따로 표시됩니다.
df.boxplot(column='평균기온', by='계절')은 계절별로 평균기온의 박스플롯을 그립니다. 네 개의 박스가 나란히 그려져서 계절 간 비교가 쉽습니다.
여름 박스가 가장 위쪽에, 겨울 박스가 가장 아래쪽에 위치할 것입니다. 그래프를 꾸미는 요소들을 살펴봅시다.
plt.figure(figsize=(10, 6))는 그래프 크기를 설정합니다. 가로 10인치, 세로 6인치입니다.
너무 작으면 글씨가 안 보이고, 너무 크면 파일 용량이 커집니다. plt.title()은 그래프 제목을 설정합니다.
fontsize=14로 글씨 크기를 조정할 수 있습니다. plt.xlabel()과 plt.ylabel()은 각 축의 레이블입니다.
plt.savefig()는 그래프를 이미지 파일로 저장합니다. dpi=300은 해상도를 의미합니다.
출판이나 프레젠테이션에는 최소 300 DPI가 필요합니다. bbox_inches='tight'는 여백을 최소화합니다.
실제 업무에서는 어떻게 활용될까요? 마케팅 팀에 월별 매출 데이터를 보고한다고 가정해봅시다.
엑셀 표로 주는 것보다 선 그래프 하나로 보여주면 "12월 매출이 급증한다"는 패턴이 즉시 전달됩니다. 의사결정자들은 시각 자료를 선호합니다.
김분석 씨가 코드를 실행하자 아름다운 박스플롯이 나타났습니다. 여름의 박스가 가장 높고, 겨울이 가장 낮았습니다.
봄과 가을은 비슷한 위치에 있었습니다. "와, 한눈에 보이네요!" "이게 시각화의 힘입니다.
복잡한 통계량보다 한 장의 그래프가 더 강력한 메시지를 전달하죠." 박데이터 씨가 만족스러운 표정으로 말했습니다.
실전 팁
💡 - 그래프를 저장할 때는 PNG보다 SVG 형식이 확대해도 깨지지 않아 좋습니다
- 색상은 색맹인 사람도 구분할 수 있는 팔레트를 사용하세요 (Seaborn의 colorblind 팔레트 추천)
- 한 화면에 너무 많은 정보를 담지 마세요. 그래프 하나당 하나의 메시지만 전달하는 것이 좋습니다
7. 분석 결과 리포트 작성
모든 분석과 시각화를 마친 김분석 씨는 이제 마지막 단계를 앞두고 있었습니다. "분석은 끝났는데, 이걸 어떻게 정리해서 보고하지?" 박데이터 씨가 조언했습니다.
"좋은 리포트는 숫자와 그래프, 그리고 스토리의 조합입니다."
분석 리포트 작성은 분석 과정과 결과를 체계적으로 정리하여 의사결정자에게 전달하는 과정입니다. 분석 목적, 방법론, 주요 발견, 시각화 자료, 결론과 제언을 논리적 순서로 구성합니다.
마크다운이나 Jupyter Notebook으로 작성하면 코드와 설명을 함께 담을 수 있습니다.
다음 코드를 살펴봅시다.
# 최종 리포트 생성 코드
import pandas as pd
from datetime import datetime
# 주요 통계 정리
report_summary = {
'분석 기간': f"{df.index.min().strftime('%Y-%m-%d')} ~ {df.index.max().strftime('%Y-%m-%d')}",
'총 데이터 수': len(df),
'전체 평균 기온': f"{df['평균기온'].mean():.2f}℃",
'최고 기온': f"{df['최고기온'].max():.1f}℃",
'최저 기온': f"{df['최저기온'].min():.1f}℃",
'연평균 상승률': f"{slope:.4f}℃/년",
'10년간 총 변화': f"{slope * 10:.2f}℃"
}
# 리포트 요약 출력
print("=" * 50)
print("서울시 10년간 기온 변화 분석 리포트")
print("=" * 50)
for key, value in report_summary.items():
print(f"{key}: {value}")
# 주요 발견 사항
findings = [
"연평균 기온이 지속적인 상승 추세를 보임",
"여름철 평균 기온이 타 계절 대비 가장 큰 변동성",
"겨울철 최저 기온의 상승폭이 평균보다 큼"
]
print("\n주요 발견:")
for i, finding in enumerate(findings, 1):
print(f"{i}. {finding}")
박데이터 씨가 말했습니다. "분석가의 진짜 실력은 리포트에서 드러납니다.
아무리 훌륭한 분석도 제대로 전달하지 못하면 의미가 없어요." 김분석 씨는 고개를 끄덕였습니다. "그럼 어떻게 써야 할까요?" 분석 리포트란 무엇일까요?
쉽게 비유하면, 분석 리포트는 마치 탐정이 사건을 조사한 후 작성하는 보고서와 같습니다. "무엇을 조사했는지, 어떤 방법을 사용했는지, 무엇을 발견했는지, 그래서 무엇을 해야 하는지"를 논리적으로 설명하는 문서입니다.
좋은 리포트의 구조는 정해져 있습니다. 첫째, 요약입니다.
바쁜 의사결정자들은 전체를 읽지 않을 수 있습니다. 첫 페이지에 핵심 내용을 담아야 합니다.
"서울의 평균 기온은 최근 10년간 연평균 0.07도 상승했으며, 이는 통계적으로 유의미한 추세입니다." 둘째, 배경 및 목적입니다. 왜 이 분석을 했는지 설명합니다.
"기후 변화에 대한 관심이 높아지는 가운데, 서울 시민들이 체감하는 기온 변화를 정량적으로 파악하고자 본 분석을 실시했습니다." 셋째, 데이터 및 방법론입니다. 어떤 데이터를 사용했고, 어떻게 처리했는지 기술합니다.
"기상청 공공데이터 포털에서 2014년부터 2023년까지의 일별 기온 데이터를 수집했으며, 결측치는 선형 보간법으로 처리했습니다." 넷째, 분석 결과입니다. 여기가 리포트의 핵심입니다.
발견한 패턴과 인사이트를 숫자와 그래프로 제시합니다. 계절별 박스플롯, 연도별 추세선 그래프 등을 포함합니다.
다섯째, 결론 및 제언입니다. 분석 결과가 의미하는 바와 향후 조치 사항을 제안합니다.
"서울의 기온 상승 추세가 확인되었으므로, 폭염 대비 시스템 강화가 필요합니다." 코드로 리포트 요약을 자동화할 수 있습니다. report_summary 딕셔너리는 주요 통계량을 정리합니다.
분석 기간, 데이터 수, 평균 기온, 극값, 추세 등 핵심 숫자를 한눈에 볼 수 있게 만듭니다. 이런 요약표는 리포트 첫 페이지에 배치하면 효과적입니다.
strftime('%Y-%m-%d')는 날짜를 읽기 쉬운 문자열로 변환합니다. 2014-01-01처럼 연-월-일 형식으로 표시됩니다.
:.2f는 소수점 둘째 자리까지 표시하는 포매팅입니다. 주요 발견 사항은 불릿 포인트로 정리합니다.
한 문장에 하나의 메시지만 담습니다. "연평균 기온이 상승했다", "여름철 변동성이 크다" 같은 명확한 문장이 좋습니다.
길고 복잡한 문장은 피합니다. 시각화 자료를 적재적소에 배치합니다.
그래프는 설명 바로 다음에 위치해야 합니다. "계절별 기온 차이가 뚜렷합니다"라는 문장 다음에 박스플롯을 넣으면 독자가 즉시 이해할 수 있습니다.
그래프마다 캡션을 달아 무엇을 보여주는지 명확히 합니다. 실제 업무에서는 어떻게 활용될까요?
A/B 테스트 결과를 리포트한다고 가정해봅시다. "새로운 버튼 디자인이 클릭률을 12% 증가시켰습니다"라는 한 문장과 함께 막대 그래프를 보여줍니다.
그 아래에 "따라서 새 디자인을 전체 페이지에 적용할 것을 제안합니다"라고 제언합니다. 이것이 좋은 리포트입니다.
주의할 점도 있습니다. 너무 많은 정보를 담으려 하지 마세요.
리포트는 논문이 아닙니다. 의사결정에 필요한 핵심 정보만 간결하게 전달하는 것이 목표입니다.
세부 데이터나 코드는 부록에 넣습니다. 전문 용어는 최소화합니다.
독자가 데이터 과학자가 아닐 수 있습니다. "사분위수 범위"보다는 "데이터의 중간 50% 범위"처럼 쉽게 풀어 쓰는 것이 좋습니다.
김분석 씨는 리포트 초안을 완성했습니다. 제목, 요약, 그래프, 결론이 깔끔하게 정리되었습니다.
박데이터 씨가 검토하더니 만족스러운 표정을 지었습니다. "훌륭합니다.
이제 진짜 데이터 분석가가 된 것 같네요. 분석도 중요하지만, 이렇게 결과를 잘 전달하는 것도 똑같이 중요합니다." 김분석 씨는 뿌듯했습니다.
막연했던 첫 프로젝트를 성공적으로 완수했습니다. 계획 수립부터 리포트 작성까지, 데이터 분석의 전 과정을 경험했습니다.
이제 다음 프로젝트가 기대되기 시작했습니다.
실전 팁
💡 - 리포트는 항상 독자의 관점에서 작성하세요. 기술적 세부사항보다 비즈니스 인사이트가 중요합니다
- 그래프에는 반드시 제목과 축 레이블을 넣으세요. 설명 없이도 이해할 수 있어야 합니다
- 리포트를 제출하기 전에 다른 사람에게 읽어달라고 부탁하세요. 이해하기 어려운 부분을 찾을 수 있습니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.
AWS Organizations 완벽 가이드
여러 AWS 계정을 체계적으로 관리하고 통합 결제와 보안 정책을 적용하는 방법을 실무 스토리로 쉽게 배워봅니다. 초보 개발자도 바로 이해할 수 있는 친절한 설명과 실전 예제를 제공합니다.
AWS KMS 암호화 완벽 가이드
AWS KMS(Key Management Service)를 활용한 클라우드 데이터 암호화 방법을 초급 개발자를 위해 쉽게 설명합니다. CMK 생성부터 S3, EBS 암호화, 봉투 암호화까지 실무에 필요한 모든 내용을 담았습니다.
AWS Secrets Manager 완벽 가이드
AWS에서 데이터베이스 비밀번호, API 키 등 민감한 정보를 안전하게 관리하는 Secrets Manager의 핵심 개념과 실무 활용법을 배워봅니다. 초급 개발자도 쉽게 따라할 수 있도록 실전 예제와 함께 설명합니다.