데이터 중심 어플리케이션 설계에 이어 컴퓨터 밑바닥의 비밀이라는 책을 스터디 합니다. 1장은 프로그래밍 언어가 작동하는 기본을 설명하는 단원으로 컴파일러와 링커 그리고 추상화에에 대한 내용입니다. 이번에는 조금 색다르게 비유법을 통해서 설명해보겠습니다.


 

목차

1. 용어 정리

신규개념 설명
컴파일러(compiler) 고수준의 언어를 저수준의 언어로 번역하는 프로그램
토큰(Token) 컴파일러가 쪼갠 각 항목에 정보를 결합한 것
해석(parsing) 소스코드에서 토큰을 추출하는 과정
GCC 컴파일러 유닉스/리눅스 계열의 표준 컴파일러
대상 파일(object file) 소스파일을 컴파일한 결과로 기계어로 쓰여진 프로그램
링커(Linker) 컴파일러가 만든 대상파일을 하나의 실행파일로 합쳐주는 프로그램
심벌(Symbol) 전역 변수(Global Variable)과 함수(function)을 포함하는 모든 변수 이름들
라이브러리 컴파일된 패키지와 모든 함수의 선언을 포함한 헤더 파일로 만든 것
헤더 파일(header file) 컴파일러가 외부 심벌 정보를 기록하는 표

2. 프로그램이 음식점이라면

여기 이세계 식당이 있습니다. 요리사와 요리법이 존재하는 것 같지만 재료는 무한이 복제되는 마법 창고에서 생성됩니다. 이 때 요리사는 재료와 요리법의 순서를 조합하여 하나의 완성된 요리를 하게 됩니다. 이를 우린 컴퓨터에서 컴파일러라고 부릅니다.

만약 제육볶음을 만든다면? 파, 고기 앞다리살과 같은 재료가 필요할 것이구요, 파는 썰고 고기는 적당히 볶고 맛나게 먹는 그런 레시피가 만들어질 것 입니다. 이렇듯 컴파일러는 데이터와 코드의 조합으로 실행 가능한 그리고 기계가 이해할 수 있는 대상 파일을 만들게 됩니다. 

그런데 말입니다. 옆 집 중국 음식점의 두반장이 그렇게 인기가 많다는데요? 제육볶음에 한 번 넣어보면 어떨까요? 마침 중국집 위치가 적혀있는 정보도 가지고 있네요! 찾아가서 가져옵니다. 이때 두반장은 외부 변수이고 외부 재료의 위치는 심벌 테이블로 비유될 수 있습니다.

자 이제 제육볶음 레시피를 개발했으니 우리 수 쉐프에게 보고하죠. 쉪! 이게 새로운 제육볶음 레시피입니다. 쉐프가 흡족해 하는군요. 이걸 우린 컴퓨터에서 링커 라고 합니다. 이 링커는 흡족한 대상 파일을 모아 하나의 요리법으로 정리합니다. 이를 우리는 라이브러리라고 하구요. 마치 밀키트를 연상 시키는군요!
 

그런데 말입니다. 컴파일러를 열심히 설명해봤는데, 인터프리터라는 것도 있단 말이죠? 말로는 파이썬, 자바스크립트가 인터프리터라고 하고 C가 컴파일러라는데 잘 이해가 되지 않는 군요. 요리법으로 비교하면 말이죠,  컴파일러는 설계형 요리사입니다. 차근히 대상파일을 보고 변수가 잘 선언되어있는지 에러는 나지 않는지 검토합니다. 레시피를 검토하는 것처럼 말이죠!  반면 인터프리터는 즉석 요리사입니다. 일단 만들고 해석합니다. 그러다보니 아차! 제육이 없군요! 다시 시작 해야겠어요. 
 

3. 동적/정적 라이브러리

그런데 말입니다. 남의 요리법을 가져다 쓸 수 있다는 사실. 우린 라이브러리를 가져다 쓰는 것에 익숙한데 이렇게 남의 노하우, 요리법을 복사해다가 내 음식점을 저장해놀 수 있겠습니다. 마라탕이 그렇게 인기가 많대요!! 우리도 써봐요. 문제는 요리법이 세상에 너~무 많아서 이걸 모두 복사해놓다간 음식 창고보다 문서창고가 더 많아지겠어요. 이거이거 문제군요.

그러지 말고 우리 돕고 삽시다! 저기 동문시장 청년몰은 요리법을 공유하는 시스템이 있대요!! 몰래 복사해 놓을 것이아니라 필요할 때 마다 가져다 쓰면 된답니다 오호!! 역시 사람은 돕고 살아야해요. 운영시간 내내 가져다 써도 되고 주문이 들어올 때 마다 가져다 써도 된다니 이거 참 인심이 좋은 곳입니다.

 
이렇게 동적 라이브러리는 장점이 많아요! 매번 요리법을 복제하지 않아도 되고요! 요리법 ver 2.0가 나와도 수정된 요리법만 가져오면 만사 Ok!  요리법을 복사해다 쓰던(정적 라이브러리)시절보다 훨씬 편하군요. 추가 메뉴 확장도 좋아요. 요즘 또 안성재 쉐프가 유명하니까 도토리 국수 플러그인을 써보겠습니다. 또 다른 요리법과 쓰까 쓸 수 있는 점도 아주 좋군요!

하지만 단점도 있는데요! 아무래도 요리법을 공유 주방에서 가져오다보니 살짝 시간이 소요가 되네요! 아무렴 어떠한가요. 그리고 잘 쓰던 중국집 위치가 이사 갔대요 흑흑 원래는 참조표를 보고 다녔는데 이사갔나 보군요. 이런 경우를 대비해서 멀-티버스를 만들어서 극복~ 했답니다. (급마무리)
 

4. 후기

보통 데이터를 음식으로 표현하니까 적용해봤는데 재밌으면서도 엄밀하지 않은 부분이 공존하는 그런 글이 되었네요. 사실 링커가 내부 변수는 관심을 가지지 않는다 와 같은 사실도 한 번 표현해보고자 했는데 가상화 부분이 살짝 이해가 안되어서 추후 메모리 단원에서 정리할 예정!

5. 출처

https://www.aladin.co.kr/shop/wproduct.aspx?itemid=335644868&srsltid=afmbooqwsqgopudtwswt2orsbm-denky529mzgxv3i6jvikbxrsqrvid

 

컴퓨터 밑바닥의 비밀 : 알라딘

코드를 실행하면 컴퓨터 내부에서는 어떤 일이 벌어질까? 운영 체제부터 CPU, 동기화, 입출력을 구현하는 원리까지, 내 코드를 바꿔줄 컴퓨터 밑바닥의 비밀을 들여다보자.

www.aladin.co.kr

 

 

데이터 중심 어플리케이션 설계(대중애설) 스터디의 후기를 작성하고 좋았던 방법과 이 책을 스터디로 선택하려는 사람들을 위한 기록을 남겨봅니다.


1. 스터디 방법

  • 사용 플랫폼: 슬랙 - 허들
  • 기간: 25년 2월 ~ 4월 24일
  • 참여 방법: 총 12장을 매주 읽고 블로그/노션에 NDA 템플릿을 이용해서 정리하기
    • 새롭게 알게된 점(New)
    • 어려웠거나 이해하지 못한 부분(Difficulty)
    • 추가 내용(Amendment)
  • 스터디 방법: 매주 일요일 오후 10시에 모여 작성한 내용 읽으며 질의응답

2. 후기

  • 책 후기

"데이터"라는 문구만 보고 데이터분석/사이언티스트가 도전할 수도 있겠으나 명확히는 설계에 관점이 맞춰져 있기 때문에 백엔드에 관심 있는 사람들이 보는게 좋다. Part 1에서는 신뢰성, 데이터 모델, 저장소와 검색, 부호화 Part 2에서는 분산 데이터, 복제, 파티셔닝, 트랜젝션, 일관성 Part 3 배치 처리, 스트림 처리에 대해서 다루는 흐름으로 폭 넓게 데이터 시스템 설계에 대한 이해를 도울 수 있을 것으로 판단할 수 있다. 하지만 각 단원마다의 내용이 압축되어 소개하므로 각 시스템에 대해서 설계하고 구현한 사람들에는 복기의 느낌으로 읽어보는 것이 가장 큰 도움이 될 수 있다. 다른 이론서와 비슷하게 책 자체를 필사해서 적는 블로그가 많기 때문에(본인 포함...) 참고 할만한 블로그가 적다. 늘 느끼지만 이런 난이도 높은 책들은 완벽하게 이해하려하기 보다는 책/단원에서 얘기말하고자 하는 바를 인내심있게 쫓되 나의 무지를 너무 탓하지 않고 꾸준히 읽는 것이 스터디 완료의 비결인 듯!  단원이 말하고자 바를 요약하고 나의 언어로 재구성해서 작성하는 셀프 스터디가 필요하다. 본인이 데이터 관련 직무라면 3장 정도는 읽어보는거 매우 추천

https://snowgot.tistory.com/173

 

DDIA Chapter 3: 저장소와 검색

솔직히 말이죠 이런 책을 읽는다는게 쉽지만은 않습니다만, 특히 이번장에서는 데이베이스의 저장구조와 검색에 대한 테크니컬한 내용이 많이 들어가서 중간에 도망갈뻔 했습니다. 그런데 참

snowgot.tistory.com

  • 스터디 방법 후기

자율적으로 글을 쓰고 발표하기로 하였어서 고정적인 2-3명의 발표자가 있고 청중이 5명정도 유지되었다. 특히 청중에는 N사 광고도메인 개발자 분이 계셔서, 추상적으로 읽을 수 밖에 없는 주니어 개발자들의 질문이나 고민을 실무적으로 대답해주는 부분이 이번 스터디의 가장 큰 특징이였다. 느슨한 스터디에도 꾸준히 진행할 수 있었던 동기부여가 된 것 같다. 서로 얻을 수 있는 부분을 자연스럽게 구축하는 것이 스터디에서 중요함을 느꼈다. 꽤나 잘 진행된 스터디어서 다음 책은 컴퓨터밑바닥의 비밀 이라는 책을 진행할 예정

https://www.aladin.co.kr/shop/wproduct.aspx?itemid=335644868&srsltid=afmboopu5fkt_m8yvdlgx5duttjhtqcg5hfodxg-vn11qjc0tcqsyyht

 

컴퓨터 밑바닥의 비밀 : 알라딘

코드를 실행하면 컴퓨터 내부에서는 어떤 일이 벌어질까? 운영 체제부터 CPU, 동기화, 입출력을 구현하는 원리까지, 내 코드를 바꿔줄 컴퓨터 밑바닥의 비밀을 들여다보자.

www.aladin.co.kr

 

3. 스터디원들 후기(KPT)

각 스터디원들의 KPT 후기를 텍스트로 남긴다. 중도에 포기하고 싶은 마음도 많았지만 그래도 끝까지 해본다는 경험으로 나의 무지를 받아들이며 했던 경험이 좋았다. 다음 사진은 꾸준함에 대한 이상과 실제 그림 ㅎㅎ 일단 끝까지 해보자고~

Keep Problem Try
다른분들의 열정을 보고 배움... 책을 안읽음, 정리를 안함, 와서 듣기만함 참여자체에 대한 고민을 더 하자
다른 분들 필기와 열정을 배움 배워서 응용할 생각을 해야 하는데 그냥 필기 열심히 하는 범생이 같아서 좀 별로였음 (나자신) 그냥 책 공부한다고 생각하지 말고 연관관계를 최소한 5개는 더 만들것. 책 내용 이외의 대화거리를 끄집어 낼것
DB에 대해 더 배운 느낌.,. 무엇보다 회사에서 어떤 시스템을 쓰고 있고 왜 이런 방식을 도입했나 조금 이해가 갔습니다. 중반 이후로는 조금 어려웠다 .. 어렵다 보니까 손이 확실히 덜갔다. 여러 번 읽어야 하는 책일 것 같다. 다시 어려웠던 부분부터 읽어나가면 좋을 것 같다.
데이터 중심의 설계를 고민해본 적이 없어서 배운 시도 자체가  좋았다.

특히 3장의 저장소와 검색부분을 읽으면서 깨달았던 OLTP와 OLAP의 진정한 차이 등등이 너무 좋았음

신규 개념을 상단에 적는 형식의 글 템플릿 작성이 좋았다.
난이도가 넘 어려워서 1-2번 읽고도 이해 못할 때가 많았다.

이론적인 부분만 읽어서 모호한 부분이 많앗음
NDA 템플릿 잘 안 씀..
요약부터 읽어버리자. 3번 읽자. 남이 쓴것도 줍줍해서보자

실제 사례를 실습해보자(Data Flow같은 스트림처리 서비스 써보기)

글의 전개방식으로 쓰다보니까 NDA처럼 순서를 고려하지 않는 정리는 조큼 어려운듯
책에 내용이 많아서 소제목 간의 연관성이 바로 정리되지 않았는데 발표 준비 + 들으면서 정리할 수 있었음

책 내용 뿐만 아니라 실무 이야기, 대응책에 대해 배울 수 있어서좋았음

NDA 템플릿 좋았음
책 내용 이해하기에 급급해서 NDA 템플릿을 잘 활용하지 못함
제대로 남에게 설명할 수 있을 정도로 이해하거나 기억하지 못함....(기억력 이슈?)
좀더 실무에 적용해서 어떤 사례에 어떻게 대처할 수 있는지 정리하기

기업 발표 내용처럼 실무에 적용한 사례 찾아보면서 책 이해하기 (내가 나중에 적용을 고민할 수 있는 포인트가 무엇인지 제대로 이해할 수 있을 것 같음)
나와 다른 경험들을 가진 분들의 이야기를 듣는 것, 중도 하차했고 참여한 것도 모두 청강이었지만 딱 한 번 내 경험을 공유한 적이 있었는데, 제 3자의 관점에서 나의 고민과 문제를 봐주셨을 때 느낀 점들이 있었음 감사합니다. 미리 안읽음 / 읽다가 이해 안하면 책 집어 던짐(던지진 않고 고이 책 덮어버림) / 읽으면서 자꾸 '내가 이게 필요한가?'라는 생각이 듬 / 자기검열 읽는 시간을 확보해서 읽어보기. 이해 안가도 그냥 쭉 읽어보기 ㅠ

 

 

집앞의 횡단보도를 자주 지나가는데, 횡단보도를 기다릴지 아닐지 늘 고민하면서 지나갑니다.'4명 기다리고 있는데 금방 바뀌지 않을까? 좀 더 가서 사거리에서 건너는게 나을까?' 저는 데이터를 수집해서 언제쯤 횡단보도가 바뀌는지, 그리고 이를 활용할 수 있는 분포는 뭐가 있는지 확인해보자는 문제의식을 정의하고 해결하는 방법을 작성했습니다. 월간 데이터노트 4기의 스터디로 진행된 분석입니다. 월간 데이터노트 참여에 관심이 있다면 다음 공지를 확인해주세요!

월간데이터 노트 홈페이지

월간데이터 노트 링크드인


1. 주변상황 정보

  • 횡단보도 기준 동편에는 1710세대, 서편에는 1068세대의 아파트 단지
  • 횡단보도 바로 앞에 카페, 빠리바게트 등의 소규모 상가가 있으며, 도서관은 현재 운영 X
  • 북쪽에 역시 대단지, 남쪽에는 공공기관이 있음
  • 횡단보도를 이용하는 사람은 주민, 학생, 출퇴근 직장인들이 주류를 이루며, 10시/ 4시 정도에는 일부 유모차를 동반한 엄마가 등장

2. 데이터 수집

  • 4차선도로이며 횡단보도 기준 빨간불 2분, 파란불 25초
  • 제가 주로 이동하는 시간인 출퇴근/점심시간/퇴근 시간에 수시로 22개 수기 수집

  • 평균 4.1명, 중위 3.5, 표준편차 3.6명, IQR 기준 이상치 2개(13, 14)

  • 시간대 분포

3. 분포 모델 검토

처음에는 포아송 분포라고 생각하고 호기심 발제했음
  • 포아송 분포
    • 정의: 일정 시간 & 공간 내에 독립적으로 발생하는 사건의 횟수를 모델링
    • 분포의 특징과 적합성 체크
      • 사건은 서로 독립적으로 발생 → 맞음
      • 사건은 동시에 발생하지 않음 → 동시에 사람들이 도착하기도 함
      • 평균 발생률은 관측 구간 내 일정 → 맞음
      • 분산/평균의 값이 1임 → 아님 3.01임 (과산포임)
다른 조건이 어느정도 맞으나 분산과 평균이 같지 않아서 Claude가 음이항 분포를 추천함
  • 음이항 분포
    • 정의: r번째 성공을 관측할 때 까지 필요한 시행 횟수나 성공 확률이 p인 베르누이 시행에서 r번의 성공을 관측할때까지 실패한 횟수의 분포
    • 분포의 특징과 적합성 체크
      • 분산이 평균보다 큰 과산포(overdispersion) 데이터에 적합
        • Ex) 특정 지역의 범죄 발생 건수(범죄가 특정 지역에 집중)
        • Ex2) 소비자 구매 행동(일부 고객이 훨씬 더 자주 구매)
    • 데이터에 과산포가 존재(분산> 평균) → 맞음
    • 사건 발생이 군집화 되는 경향이 있음 → 맞음 일부시간대 몰림
    • 개체별로 발생률에 차이가 있음 → 있을 수 있음(주부/학생 등)
    • 발생 확률이 시간에 따라 변할 수 있음 → 그러함

4. 분석

  1. 히스토그램 및 분포 시각화
  2. 포아송 분포와 음이항 분포 적합
  3. 적합도 검정 (카이제곱 검정)
  4. AIC 및 BIC를 사용한 모델 비교
import matplotlib.pyplot as plt
import platform

def set_matplotlib_font():
    system = platform.system()

    if system == "Windows":
        plt.rc('font', family='Malgun Gothic')
    elif system == "Darwin":  # macOS
        plt.rc('font', family='AppleGothic')
    elif system == "Linux":
        plt.rc('font', family='NanumGothic')
    else:
        print("Unknown system. Please set font manually.")

    plt.rcParams['axes.unicode_minus'] = False

# 폰트 설정 함수 호출
set_matplotlib_font()

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import scipy.stats as stats
from statsmodels.discrete.discrete_model import Poisson, NegativeBinomial

df = data.copy()
df.columns = ['timestamp','count']
print("데이터 샘플:")
print(df.head())

# 기본 통계량
print("\n기본 통계량:")
print(df['count'].describe())

mean_count = df['count'].mean()
var_count = df['count'].var()
print(f"평균: {mean_count:.4f}")
print(f"분산: {var_count:.4f}")
print(f"분산/평균 비율: {var_count/mean_count:.4f}")

# 히스토그램 및 분포 시각화
plt.figure(figsize=(12, 6))

# 히스토그램
plt.subplot(1, 2, 1)
# sns.histplot(df['count'], kde=True, stat='density', discrete=True)
df['count'].hist(density = True)
plt.title('횡단보도 건너는 사람 수 히스토그램')
plt.xlabel('사람 수')
plt.ylabel('빈도')

# 경험적 분포와 이론적 분포 비교
x = np.arange(0, df['count'].max() + 1)

# 포아송 분포 확률 질량 함수
poisson_pmf = stats.poisson.pmf(x, mean_count)

# 음이항 분포 매개변수 추정
# 방법 1: 모멘트 방법
r = mean_count**2 / (var_count - mean_count) if var_count > mean_count else 100
p = mean_count / var_count if var_count > mean_count else mean_count / (mean_count + 1)

# 음이항 분포 확률 질량 함수
nb_pmf = stats.nbinom.pmf(x, r, p)

plt.subplot(1, 2, 2)
plt.bar(x, [df['count'].value_counts().get(i, 0) / len(df) for i in x], alpha=0.5, label='실제 데이터')
plt.plot(x, poisson_pmf, 'ro-', label=f'포아송 분포 (λ={mean_count:.2f})')
plt.plot(x, nb_pmf, 'go-', label=f'음이항 분포 (r={r:.2f}, p={p:.2f})')
plt.title('경험적 분포와 이론적 분포 비교')
plt.xlabel('사람 수')
plt.ylabel('확률')
plt.legend()
plt.tight_layout()

# 포아송 분포 적합도 검정 (카이제곱 검정)
observed = np.zeros(df['count'].max() + 1)
for i in range(len(observed)):
    observed[i] = df['count'].value_counts().get(i, 0)

expected = len(df) * stats.poisson.pmf(np.arange(len(observed)), mean_count)

# 카이제곱 값 계산
# 기대빈도가 5 미만인 셀은 통합
min_expected = 5
observed_adj = []
expected_adj = []
last_obs = 0
last_exp = 0
for obs, exp in zip(observed, expected):
    if exp >= min_expected:
        observed_adj.append(obs)
        expected_adj.append(exp)
    else:
        last_obs += obs
        last_exp += exp

if last_exp > 0:
    observed_adj.append(last_obs)
    expected_adj.append(last_exp)

observed_adj = np.array(observed_adj)
expected_adj = np.array(expected_adj)

chi2 = np.sum((observed_adj - expected_adj) ** 2 / expected_adj)
df_chi2 = len(observed_adj) - 1 - 1  # 매개변수 1개 추정 (자유도 = 셀 수 - 1 - 추정된 매개변수 수)
p_value = 1 - stats.chi2.cdf(chi2, df_chi2) if df_chi2 > 0 else None

print("\n포아송 분포 적합도 검정 (카이제곱 검정):")
print(f"카이제곱 값: {chi2:.4f}")
print(f"자유도: {df_chi2}")
print(f"p-value: {p_value if p_value is not None else '자유도가 0 이하'}")
print(f"결론: {'포아송 분포에 적합하지 않음 (p < 0.05)' if p_value is not None and p_value < 0.05 else '포아송 분포에 적합함 (p >= 0.05)' if p_value is not None else '검정 불가 (자유도 부족)'}")

# AIC 및 BIC를 사용한 모델 비교
# 인공 변수를 만들어 모델 적합 (상수 모델)
X = np.ones((len(df), 1))
y = df['count'].values

# 포아송 회귀 모델
poisson_model = Poisson(y, X)
poisson_results = poisson_model.fit(disp=0)

# 음이항 회귀 모델
try:
    nb_model = NegativeBinomial(y, X)
    nb_results = nb_model.fit(disp=0)
    
    print("\n모델 비교 (AIC 및 BIC):")
    print(f"포아송 모델 AIC: {poisson_results.aic:.4f}")
    print(f"음이항 모델 AIC: {nb_results.aic:.4f}")
    print(f"포아송 모델 BIC: {poisson_results.bic:.4f}")
    print(f"음이항 모델 BIC: {nb_results.bic:.4f}")
    print(f"더 적합한 모델: {'음이항 분포' if nb_results.aic < poisson_results.aic else '포아송 분포'} (AIC 기준)")
except:
    print("\n음이항 회귀 모델을 적합할 수 없습니다. 데이터가 충분하지 않거나 모델이 수렴하지 않습니다.")

# 시간대별 분석
df['hour'] = df['timestamp'].dt.hour
df['time_of_day'] = pd.cut(df['hour'], 
                          bins=[0, 12, 18, 24], 
                          labels=['아침(0-12시)', '오후(12-18시)', '저녁(18-24시)'],
                          include_lowest=True)

time_stats = df.groupby('time_of_day')['count'].agg(['mean', 'var', 'count'])
time_stats['var/mean'] = time_stats['var'] / time_stats['mean']

print("\n시간대별 통계:")
print(time_stats)

# 시간대별 분포 시각화
plt.figure(figsize=(10, 6))
sns.boxplot(x='time_of_day', y='count', data=df)
plt.title('시간대별 횡단보도 건너는 사람 수 분포')
plt.xlabel('시간대')
plt.ylabel('사람 수')

plt.tight_layout()
plt.show()

# 결론
print("\n분포 분석 결론:")
if var_count > mean_count:
    print(f"분산({var_count:.4f})이 평균({mean_count:.4f})보다 크므로 과산포(overdispersion)가 존재합니다.")
    print("따라서 포아송 분포보다 음이항 분포가 더 적합할 수 있습니다.")
elif var_count < mean_count:
    print(f"분산({var_count:.4f})이 평균({mean_count:.4f})보다 작으므로 과소산포(underdispersion)가 존재합니다.")
    print("이 경우 포아송 분포보다 이항 분포가 더 적합할 수 있습니다.")
else:
    print(f"분산({var_count:.4f})이 평균({mean_count:.4f})과 거의 같으므로 포아송 분포에 적합합니다.")

  • 모델 비교 (AIC 및 BIC) ->  더 적합한 모델: 음이항 분포 (AIC 기준)
    • 포아송 모델 AIC: 126.3588
    • 음이항 모델 AIC: 111.9646
    • 포아송 모델 BIC: 127.4498
    • 음이항 모델 BIC: 114.1467
  • 모수 추정 방법: 모먼트 방법과 최대 우도 추정법 존재
    • 모멘트 방법
# 데이터의 평균과 분산 계산
mean = np.mean(data)
var = np.var(data, ddof=1)  # 표본 분산

# 모수 추정
p = mean / var
r = mean * p / (1 - p)

5. 결론

  • 눈으로 보기엔 포아송 분포가 더 잘 맞는거같은데 AIC기준으로는 음이항 분포가 더 잘맞다고 한다.
  • 카이제곱을 검정을 쓰는 이유는 관측된 빈도와 분포의 기대 빈도와 비교하는 방법이기 떄문
  • AIC/BIC가 사용된 이유는 다른 통계 모델의 상대적 품질을 비교하는 지표이기 때문(상대적 평가)
  • 음이항 분포의 모수
    • r(성공 횟수): 1.94
    • p(성공 확률): 0.32
위 내용을 가지고 95%의 신뢰도로 보행자 수는 11명  -> 엥? 너무 많고 비현실적임, 수집된 데이터중 13,14명인 사례는 출퇴근,점심시간 이였으며 10명 이상 건너는 상황은 잘 발견되지 않음, 50%정도만 넘어도 기다릴 가치는 있다고 생각되어 수정
from scipy.stats import nbinom

# 95% 확률 범위의 최대값 계산
max_count_95 = nbinom.ppf(0.95, r, p)
max_count_95
# 11명

# 50% 확률 범위의 최대값 계산
max_count_95 = nbinom.ppf(0.5, r, p)
max_count_95
# 3명

누적확률 50% 이상은 3명, 3명 있으면 기다릴 가치가 있다.

6. 회고

  • GPT는 GP"F"로 바꿔야함. 앞뒤 서론이 너무 길고 공감 투가 너무 많음. Claude는 코딩능력은 좋은데 필요 이상으로 스켈레톤 코드를 제공해주며, 단순 수식 계산도 틀린 점이 자주 발견됨
  • 포아송,음이항 분포 좀 다시 고찰할 필요가 있음. 내 상황에 적절한 분포를 선정하고 데이터를 수집하는게 좋겠다.
  • 균등한 데이터 수집을 위해서 하루종일 혹은 대표성을 띄는 시간에 주기적으로 측정하는게 좋겠다.(평소에 부지런하자!)
    • Ex) 평일 오전 출근시간 가정 8-9시 집중관찰

 

본 주제는 매달 한 번씩 호기심을 주제로 분석하는 모임 <월간 데이터 노트>의 결과입니다.
관심이 있으시면 다음 링크를 확인해 보세요!!

 

지난번 통계편에 이은 머신러닝문서입니다! 잘못된 내용이나 모호한 내용, 추가되어야하는 내용이 있으면 지적해주세요! 다들 시험에 합격하시길 바라요~


0. 목차

클릭시 해당 컨텐츠로 이동합니다.

목차

    https://snowgot.tistory.com/181

     

    ADP 빅분기 이론과 Python 모듈, 실습코드 정리 - 통계편

    ADP 시험을 준비하기 위하여 정리했던 방법론과 코드를 저장합니다.빅분기는 범위가 더 좁지만 공통된 부분이 많습니다. 선형계획법이나 기타 내용은 저도 몰라서 못넣었습니다 🤣 잘못된 내용

    snowgot.tistory.com

     

    1. 모듈별 디렉토리 정리

    •  ADP 지원 버전: Python 3.7.(공지사항)
    • ADP 기본제공 Python Package & version
    pip install ipykernel=5.1.0
    pip install numpy==1.12.6
    pip install pandas==1.1.2
    pip install scipy==1.7.3
    pip install matplotlib==3.0.3
    pip install seaborn==0.9.0
    pip install statsmodels==0.13.2
    pip install scikit-learn==0.23.2
    pip install imbalanced-learn==0.5.0

    [제34회ADP실기]기본제공_python_package_list.txt
    0.01MB

    2. 머신러닝 - 지도학습

    • 전체 프로세스
      1. 데이터 로딩 및 탐색
      2. 결측치 처리
      3. 이상치 탐지 및 처리
      4. 범주형 변수 인코딩
      5. 데이터 스케일링
      6. 불균형 데이터 처리
      7. 모델링 및 성능 평가

    2.1. 결측치 처리 방법

    방법 설명 예시
    제거 결츷치가 적을 경우 df.dropna()
    평균/중앙값 대치 연속형 변수 df['column'].fillna(_mean)
    최빈값 대치 범주형 변수 df['column'].fillna(_mode)
    KNN Imputer 패턴 반영대체 from sklearn.impute import KNNImputer
    Simple Imputer 간단한 대치 from sklearn.impute import SimpleImputer
    • KNN 대치
    from sklearn.impute import KNNImputer
    imputer = KNNImputer(n_neighbors= 2)
    y = df[['y']]
    X = df.drop(columns= 'y')
    X_impute = imputer.fit_transform(X)
    
    #fit_transform결과가 ndarray로 나오므로 데이터프레임으로 원복
    pd.DataFrame(X_impute, columns = X.columns, index = df.index)
    • Simple Imputer 대치
    import numpy as np
    from sklearn.impute import SimpleImputer
    imp_mean = SimpleImputer(missing_values=np.nan, strategy='mean')
    imp_mean.fit([[7, 2, 3], [4, np.nan, 6], [10, 5, 9]])
    SimpleImputer()
    X = [[np.nan, 2, 3], [4, np.nan, 6], [10, np.nan, 9]]
    print(imp_mean.transform(X))
    
    [[ 7.   2.   3. ]
     [ 4.   3.5  6. ]
     [10.   3.5  9. ]]

    2.2. 이상치 처리 방법

    기법 설명 예시
    IQR 방식  사분위수 기반 Q1 - 1.5*IQR, Q3 + 1.5*IQR
    Z-score  표준정규분포 기반  abs(z) > 3
    시각화 boxplot, scatter 사용 sns.boxplot( x= df['col'])
    • IQR을 이용한 이상치 처리
    import numpy as np
    # 1. IQR(Interquartile Range) 방법을 이용한 이상치 탐지
    def detect_outliers_iqr(df, col):
        Q1 = df[col].quantile(0.25)
        Q3 = df[col].quantile(0.75)
        IQR = Q3 - Q1
        lower_bound = Q1 - 1.5 * IQR
        upper_bound = Q3 + 1.5 * IQR
        return df[(df[col] < lower_bound) | (df[col] > upper_bound)]
    
    # 각 수치형 변수별 이상치 확인
    for col in df_impute_X.columns:
        outliers = detect_outliers_iqr(df_impute_X, col)
        print(f'Column {col} has {len(outliers)} outliers')
    
    # 2. 이상치 처리 방법
    # 방법 1: 상한/하한 경계값으로 대체 (Capping)
    def treat_outliers_capping(df, col):
        Q1 = df[col].quantile(0.25)
        Q3 = df[col].quantile(0.75)
        IQR = Q3 - Q1
        lower_bound = Q1 - 1.5 * IQR
        upper_bound = Q3 + 1.5 * IQR
        df[col] = np.where(df[col] < lower_bound, lower_bound, df[col])
        df[col] = np.where(df[col] > upper_bound, upper_bound, df[col])
        return df
    
    # 수치형 변수에 대해 이상치 처리
    data_cleaned_cap = df_impute_X.copy()
    for col in df_impute_X.columns:
        data_cleaned_cap = treat_outliers_capping(data_cleaned_cap, col)
    
    # 방법 2: 이상치가 있는 행 제거 (데이터가 충분할 경우만)
    def remove_outliers(df, cols):
        df_clean = df.copy()
        for col in cols:
            Q1 = df[col].quantile(0.25)
            Q3 = df[col].quantile(0.75)
            IQR = Q3 - Q1
            lower_bound = Q1 - 1.5 * IQR
            upper_bound = Q3 + 1.5 * IQR
            df_clean = df_clean[(df_clean[col] >= lower_bound) & (df_clean[col] <= upper_bound)]
        return df_clean
    
    # 중요 변수에 대해서만 이상치 행 제거 (모든 변수에 적용하면 데이터 손실이 클 수 있음)
    important_numeric_cols = ['ALB', 'ALP', 'ALT', 'AST', 'BIL', 'CHE', 'CHOL', 'CREA', 'GGT', 'PROT']
    data_no_outliers = remove_outliers(df_impute_X, important_numeric_cols)
    display(data_no_outliers)

    2.3. 범주형 변수인코딩

    방법 예시 비고
    Label Encoding 순서형 변수 from sklearn.preprocessing import LabelEncoder
    One-hot Encoding 명목형 변수 import pd
    pd.get_dummies()
    • Label Encoding 방법
    from sklearn import preprocessing
    le = preprocessing.LabelEncoder()
    le.fit([1, 2, 2, 6])
    LabelEncoder()
    le.classes_
    #array([1, 2, 6])
    le.transform([1, 1, 2, 6])
    #array([0, 0, 1, 2]...)

    2.3. 데이터 시각화

    sns.heatmap(df.corr(), annot= True, cmap = "YlGnBu" )

    2.4. PCA

    • 차원축소기법으로 데이터의 분산을 최대한 보존하면서 고차원이 데이터를 저차원으로 변환
    • 판단 기준
      • 분산 설명률: 80~90%의 분산을 설명하는데 필요한 주성분이 원래 특성 수의 절반 이하인가?
      • 모델 성능: PCA적용 후 모델 성능이 유지되거나 향상되었는가?
      • 다중공선성: 원본 특성간에 높은 상관관계가 많이 존재하는가?
    • 절차
      1. 결측치, 이상치 처리, 원-핫 인코딩 사전 진행
      2. 스케일링
      3. PCA 적용
      4. 설명된 분산확인 및 분산비율 시각화
      5. 적절한 주성분의 갯수 선택
      6. 선택된 주성분의 갯수로 PCA다시 적용
      7. 각 주성분이 원래 특성에 얼마나 기여하는지 확인
      8. 상위 5개 특성확인
    from sklearn.decomposition import PCA
    import matplotlib.pyplot as plt
    import numpy as np
    import pandas as pd
    from sklearn.preprocessing import StandardScaler
    
    # 데이터는 이미 전처리되었다고 가정 (결측치, 이상치 처리, 원-핫 인코딩 등)
    # X는 전처리된 특성 데이터
    
    # 1. 스케일링 (PCA 적용 전 반드시 필요)
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)
    
    # 2. PCA 적용
    pca = PCA()
    X_pca = pca.fit_transform(X_scaled)
    
    # 3. 설명된 분산 비율 확인
    explained_variance_ratio = pca.explained_variance_ratio_
    cumulative_variance_ratio = np.cumsum(explained_variance_ratio)
    
    # 4. 설명된 분산 비율 시각화
    plt.figure(figsize=(10, 6))
    plt.plot(range(1, len(explained_variance_ratio) + 1), cumulative_variance_ratio, marker='o', linestyle='-')
    plt.xlabel('Number of Principal Components')
    plt.ylabel('Cumulative Explained Variance Ratio')
    plt.title('Explained Variance by Components')
    plt.axhline(y=0.8, color='r', linestyle='--', label='80% Variance Threshold')
    plt.axhline(y=0.9, color='g', linestyle='--', label='90% Variance Threshold')
    plt.legend()
    plt.grid(True)
    plt.show()
    
    # 5. 적절한 주성분 개수 선택 (예: 80% 분산 유지)
    n_components = np.argmax(cumulative_variance_ratio >= 0.8) + 1
    print(f"80% 분산을 유지하기 위한 주성분 개수: {n_components}")
    
    # 6. 선택된 주성분 개수로 PCA 다시 적용
    pca_selected = PCA(n_components=n_components)
    X_pca_selected = pca_selected.fit_transform(X_scaled)
    
    # 7. 각 주성분이 원래 특성에 얼마나 기여하는지 확인
    components_df = pd.DataFrame(
        pca_selected.components_,
        columns=X.columns
    )
    
    # 8. 각 주성분에 대한 기여도가 높은 상위 5개 특성 확인
    for i, component in enumerate(components_df.values):
        sorted_indices = np.argsort(np.abs(component))[::-1]
        top_features = [X.columns[idx] for idx in sorted_indices[:5]]
        top_values = [component[idx] for idx in sorted_indices[:5]]
        print(f"PC{i+1} 주요 특성:")
        for feature, value in zip(top_features, top_values):
            print(f"  {feature}: {value:.4f}")
        print()
    
    # 9. 2D 시각화 (처음 두 개 주성분으로)
    plt.figure(figsize=(10, 8))
    plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y, cmap='viridis', alpha=0.8)
    plt.title('First Two Principal Components')
    plt.xlabel('PC1')
    plt.ylabel('PC2')
    plt.colorbar(label='Target Class')
    plt.grid(True)
    plt.show()

    • 판단근거1: 설명된 분산비율
      • 하기의 누적분산 비율이 총 컬럼 12개에서 7개를 담아야지 80%를 설명하므로 차원축소가 효과적이지 않다는 판단 가능
    # 누적 설명된 분산 비율을 확인
    print("각 주성분별 설명된 분산 비율:")
    for i, ratio in enumerate(explained_variance_ratio):
        print(f"PC{i+1}: {ratio:.4f}")
    
    print("\n누적 설명된 분산 비율:")
    for i, ratio in enumerate(cumulative_variance_ratio):
        print(f"PC1-PC{i+1}: {ratio:.4f}")
        
        
    각 주성분별 설명된 분산 비율:
    PC1: 0.2566
    PC2: 0.1327
    PC3: 0.1071
    PC4: 0.0823
    PC5: 0.0815
    PC6: 0.0719
    PC7: 0.0608
    PC8: 0.0567
    PC9: 0.0472
    PC10: 0.0388
    PC11: 0.0335
    PC12: 0.0308
    
    누적 설명된 분산 비율:
    PC1-PC1: 0.2566
    PC1-PC2: 0.3893
    PC1-PC3: 0.4964
    PC1-PC4: 0.5787
    PC1-PC5: 0.6602
    PC1-PC6: 0.7321
    PC1-PC7: 0.7930
    PC1-PC8: 0.8497
    PC1-PC9: 0.8969
    PC1-PC10: 0.9357
    PC1-PC11: 0.9692
    PC1-PC12: 1.0000
    • 스크리 플롯(Scree Plot)
      • Elbow 지점 확인: 대략 4번 지점이 후보군
    plt.figure(figsize=(10, 6))
    plt.plot(range(1, len(explained_variance_ratio) + 1), explained_variance_ratio, marker='o', linestyle='-')
    plt.xlabel('Principal Component')
    plt.ylabel('Explained Variance Ratio')
    plt.title('Scree Plot')
    plt.grid(True)
    plt.show()

    • 원본데이터와 PCA데이터 성능비교
      • cross_val_score의 경우 이진 분류만 가능하기에 다중클래스는 안됨(변환 필요)
    from sklearn.model_selection import cross_val_score
    from sklearn.ensemble import RandomForestClassifier
    
    # 원본 스케일링된 데이터에 대한 교차 검증
    clf_original = RandomForestClassifier(random_state=42)
    scores_original = cross_val_score(clf_original, X_scaled, y, cv=5, scoring='roc_auc')
    
    # PCA 적용 데이터에 대한 교차 검증
    clf_pca = RandomForestClassifier(random_state=42)
    scores_pca = cross_val_score(clf_pca, X_pca_selected, y, cv=5, scoring='roc_auc')
    
    print(f"원본 데이터 평균 ROC-AUC: {scores_original.mean():.4f} (±{scores_original.std():.4f})")
    print(f"PCA 데이터 평균 ROC-AUC: {scores_pca.mean():.4f} (±{scores_pca.std():.4f})")
    • 다중공선성 검사
      • 특성간 높은 상관관계 발견(0.7 이상) 되면 PCA가 유용할 수 있음
      • 해당 높은 특성들은 독립적인 주성분으로 변환
    from sklearn.preprocessing import StandardScaler
    from scipy.stats import spearmanr
    import seaborn as sns
    
    # 상관관계 행렬 계산
    correlation_matrix = X.corr()
    
    # 상관관계 히트맵 표시
    plt.figure(figsize=(12, 10))
    sns.heatmap(correlation_matrix, annot=False, cmap='coolwarm', center=0)
    plt.title('Feature Correlation Matrix')
    plt.tight_layout()
    plt.show()
    
    # 높은 상관관계(0.7 이상)를 가진 특성 쌍 찾기
    high_corr_pairs = []
    for i in range(len(correlation_matrix.columns)):
        for j in range(i+1, len(correlation_matrix.columns)):
            if abs(correlation_matrix.iloc[i, j]) > 0.7:
                high_corr_pairs.append((correlation_matrix.columns[i], correlation_matrix.columns[j], correlation_matrix.iloc[i, j]))
    
    print("높은 상관관계를 가진 특성 쌍:")
    for pair in high_corr_pairs:
        print(f"{pair[0]} - {pair[1]}: {pair[2]:.4f}")

    2.3. 스케일링

    방법 설명 특장점
    Standard Scaler(정규화) 각 데이터에 평균을 뺴고 표준편차 나누기
    (x - x_bar / x_std)
    이상치, 분포가 skewed되어있을때
    MinMax Scaler(표준화) 데이터를 0과 1사이로 조정
    ( x- x_min / x_max - x_min)
    이상치에 영향을 많이 받음
    Robust Scaler 중앙 값과 IQR를 이용해 조정
    (x - median / IQR)
    이상치에 덜 민감
    • 정규화 스케일링
    from sklearn.preprocessing import StandardScaler
    data = [[0, 0], [0, 0], [1, 1], [1, 1]]
    scaler = StandardScaler()
    print(scaler.fit(data))
    StandardScaler()
    print(scaler.mean_)
    #[0.5 0.5]
    print(scaler.transform(data))
    #[[-1. -1.]
    # [-1. -1.]
    # [ 1.  1.]
    # [ 1.  1.]]

    2.4. 데이터 샘플링

    방법 설명 사용법
    언더샘플링 다수 클래스 줄임 from imblearn.under_sampling import RandomUnderSampler

    SMOTE 클래스 대신 사용하면 됨
    오버샘플링 소수 클래수 복제 SMOTE
    클래스 가중치 모델이 직접 적용, 데이터가 작아 오버샘플링이 부담스러울 때 class_weight = 'balanced'
    (Logistic, RF, SVC, GBC 등 지원)
    • SMOTE 코드
    from imblearn.over_sampling import SMOTE
    
    X = df.drop(columns=['Category', 'target'])
    y = df['target']
    smote = SMOTE(random_state = 42)
    X_resampled, y_resampled = smote.fit_resample(X, y)

    2.4. 평가지표 metrics

    메트릭 설명 사용시점
    Precision Positive  예측 한 것 중 실제 Positive FP가 중요한 문제(스팸 필터처럼 잘못 걸러내면 안됄 때)
    Recall(Sensitivity) 실제 Positive중 Positive로 예측한 비율 FN가 중요힌 문제(암 진단처럼 놓지면 안될 때)
    F1-score Precision과 Recall의 조화 평균 둘 다 중요할 때
    AUC-ROC 다양한 threshhold에서 TPR  vs FPR 곡선의 아래 면적 전체적인 분류 성능이 중요할 때
    PR-AUC Precision vs Recall 곡선의 면적 불균형이 매우 심하여 Positive가 극소수 일때
    • 메트릭 코드 모음

    # 분류모델
    from sklearn.metrics import accuracy_score, precision_score,recall_score
    from sklearn.metrics import f1_score, confusion_matrix, roc_auc_score, classification_report
    # 회귀모델
    from sklearn.metrics import mean_squared_error, mean_absolute_error
    from sklearn.metrics import r2_score, adjusted_rand_score
    
    # 비지도학습 군집
    from sklearn.metrics import silhouette_score
    • classification_report 읽기
      • support: 해당 클래스의 샘플 수 
      • macro avg: 일반적인 지표
      • weight avg: 샘플수 가중치를 더한 지표

    • ROC Curve
    import matplotlib.pyplot as plt
    from sklearn.metrics import roc_curve, roc_auc_score
    
    # 모델 학습 (예시)
    model = RandomForestClassifier(random_state=42)
    model.fit(X_train, y_train)
    
    # 테스트 데이터에 대한 예측 확률
    y_pred_proba = model.predict_proba(X_test)[:, 1]  # 양성 클래스(1)에 대한 확률
    
    # ROC 커브 계산
    fpr, tpr, thresholds = roc_curve(y_test, y_pred_proba)
    
    # AUC 계산
    roc_auc = roc_auc_score(y_test, y_pred_proba)
    
    # ROC 커브 그리기
    plt.figure()
    plt.plot(fpr, tpr, color='blue', label=f'ROC curve (AUC = {roc_auc:.2f})')
    plt.plot([0, 1], [0, 1], color='gray', linestyle='--')  # 대각선 기준선
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.title('Receiver Operating Characteristic')
    plt.legend(loc="lower right")
    plt.show()

    2.5. 머신러닝 스켈레톤 코드 - 분류

    from sklearn.linear_model import LogisticRegression
    from sklearn.ensemble import RandomForestClassifier
    from sklearn.svm import SVC
    from sklearn.model_selection import train_test_split
    
    X = df.drop(columns = ['Kwh8','date','datetime'])
    y = df['Kwh8']
    
    X_train, X_test, y_train, y_test = train_test_split(X,y, random_state= 42, stratify= y, test_size= 0.2)
    print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)
    
    # 모델 리스트와 이름
    models = [
    	('Logistic', LogisticRegression(solver='liblinear')), #sklearn 0.23. 에러 대응
        ('RandomForest', RandomForestClassifier(random_state=42)),
        ('SVM', SVC(random_state=42))
    ]
    
    # 모델 학습 및 평가
    for model_name, model in models:
        model.fit(X_train, y_train)
        
        # 예측 수행
        y_pred_train = model.predict(X_train)
        y_pred_test = model.predict(X_test)
        
        # 성능 평가 (train)
        print(f'Train - {model_name} Model')
        print(classification_report(y_train, y_pred_train))
        
        # 성능 평가 (test)
        print(f'Test - {model_name} Model')
        print(classification_report(y_test, y_pred_test))

    2.5. 머신러닝 스켈레톤 코드 - 분류(cv)

    from sklearn.metrics import classification_report, accuracy_score
    from sklearn.model_selection import cross_val_score, KFold, StratifiedKFold
    
    # 데이터 분할
    X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42, stratify=y, test_size=0.3)
    print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)
    
    # 교차 검증 설정
    cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
    
    # 모델 리스트와 이름
    models = [
        ('Logistic', LogisticRegression(solver='liblinear')), #sklearn 0.23. 에러 대응
        ('RandomForest', RandomForestClassifier(random_state=42)),
        ('SVM', SVC(random_state=42))
    ]
    
    # 모델 학습 및 평가
    for model_name, model in models:
        # 교차 검증 수행
        cv_scores = cross_val_score(model, X_train, y_train, cv=cv, scoring='accuracy')
        print(f'\n{model_name} Cross-Validation:')
        print(f'CV Scores: {cv_scores}')
        print(f'Mean CV Score: {cv_scores.mean():.4f} ± {cv_scores.std():.4f}')
        
        # 전체 훈련 데이터로 모델 학습
        model.fit(X_train, y_train)
        
        # 예측 수행
        y_pred_train = model.predict(X_train)
        y_pred_test = model.predict(X_test)
        
        # 성능 평가 (train)
        print(f'\nTrain - {model_name} Model')
        print(classification_report(y_train, y_pred_train))
        
        # 성능 평가 (test)
        print(f'\nTest - {model_name} Model')
        print(classification_report(y_test, y_pred_test))
        
        print('-' * 70)
    
    # 다양한 교차 검증 방법 비교 (선택적)
    print("\n다양한 교차 검증 방법 비교 (RandomForest 모델):")
    cv_methods = {
        'KFold (k=5)': KFold(n_splits=5, shuffle=True, random_state=42),
        'StratifiedKFold (k=5)': StratifiedKFold(n_splits=5, shuffle=True, random_state=42),
        'StratifiedKFold (k=10)': StratifiedKFold(n_splits=10, shuffle=True, random_state=42)
    }
    
    rf_model = RandomForestClassifier(random_state=42)
    for cv_name, cv_method in cv_methods.items():
        cv_scores = cross_val_score(rf_model, X_train, y_train, cv=cv_method, scoring='accuracy')
        print(f'{cv_name}: {cv_scores.mean():.4f} ± {cv_scores.std():.4f}')

    2.6. 종속변수에 영향 해석하기

    • 로지스틱 회귀 베타계수 추정
      • 양수는 변수가 증가감에 따라 간염 화률의 증가, 음수는 감소
      • 로즈오즈 해석:  계수가 0.5이면 1단위 증가할 때 간염 발생 로그오즈 0.05 증가
      • 오즈비 해석: exp(계수)값으로 계산하며 1.5이면 변수 1단위 증가할 때 간염 발생 오즈 50% 증가 0.8이면 20% 감소, 1이면 영향 미치지 않음 
    # 로지스틱 회귀 모델 학습
    log_model = LogisticRegression(solver='liblinear')
    log_model.fit(X_train, y_train)
    
    # 계수 및 변수명 가져오기
    coefficients = log_model.coef_[0]
    feature_names = X.columns
    
    # 계수 크기순으로 정렬하여 출력
    coef_df = pd.DataFrame({'Feature': feature_names, 'Coefficient': coefficients})
    coef_df = coef_df.sort_values('Coefficient', ascending=False)
    print("로지스틱 회귀 계수 (양수=간염 위험 증가, 음수=간염 위험 감소):")
    print(coef_df)
    
    # 오즈비(Odds Ratio) 계산 - 해석이 더 직관적임
    coef_df['Odds_Ratio'] = np.exp(coef_df['Coefficient'])
    print("\n오즈비 (1보다 크면 위험 증가, 1보다 작으면 위험 감소):")
    print(coef_df[['Feature', 'Odds_Ratio']])
    • Feature Importance 
    # Random Forest 모델 학습
    rf_model = RandomForestClassifier(random_state=42)
    rf_model.fit(X_train, y_train)
    
    # 특성 중요도 추출 및 시각화
    importances = rf_model.feature_importances_
    indices = np.argsort(importances)[::-1]
    
    feature_importance_df = pd.DataFrame({
        'Feature': feature_names[indices],
        'Importance': importances[indices]
    })
    
    plt.figure(figsize=(10, 6))
    sns.barplot(x='Importance', y='Feature', data=feature_importance_df[:10])
    plt.title('Top 10 Important Features for Hepatitis Prediction (Random Forest)')
    plt.tight_layout()
    plt.show()
    • SHAP value
      • 게임 이론이 기반, 각 특성이 모델 예측에 미치는 기여도를 배분
      • 양수면 해당 특성이 예측 확률을 증가시키는 방향으로 기여,  음수는 감소시키는 방향으로 기여
      • 개별 예측 분석, 상호작용 효과 파악, 모든 모델에 적용할 수 있는 장점
    #!pip install shap
    import shap
    
    # SHAP 값 계산 (예: Random Forest 모델)
    explainer = shap.TreeExplainer(rf_model)
    shap_values = explainer.shap_values(X_test)
    
    # SHAP 값 요약 시각화
    shap.summary_plot(shap_values, X_test, feature_names=feature_names)

    2.6. tensorflow 1.13.1 기준 지도학습 코드

    import tensorflow as tf
    import numpy as np
    import os
    
    # TensorFlow 1.x 환경에서 Eager Execution을 비활성화
    tf.compat.v1.disable_eager_execution()
    
    # 데이터 생성 (이진 분류용)
    X_data = np.random.rand(100, 2)  # 100개의 샘플, 2개의 특성
    y_data = np.random.randint(2, size=(100, 1))  # 100개의 이진 타겟 값 (0 또는 1)
    
    # 모델 정의 (Sequential API)
    model = tf.keras.models.Sequential()
    model.add(tf.keras.layers.Dense(10, input_dim=2, activation='relu'))  # 은닉층
    model.add(tf.keras.layers.Dense(1, activation='sigmoid'))  # 이진 분류를 위한 sigmoid 출력층
    
    # 모델 컴파일
    model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
    
    # 체크포인트 설정
    checkpoint_path = "training_checkpoints/cp.ckpt"
    checkpoint_dir = os.path.dirname(checkpoint_path)
    
    # 체크포인트 콜백 설정
    cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_path,
                                                     save_weights_only=True,
                                                     verbose=1)
    # 모델 학습 (체크포인트 콜백 적용)
    model.fit(X_data, y_data, epochs=10, batch_size=10, callbacks=[cp_callback])
    # 모델 저장 (전체 모델 저장)
    model.save('saved_model/my_model')
    # 모델 불러오기
    new_model = tf.keras.models.load_model('saved_model/my_model')
    # 불러온 모델 평가
    loss, accuracy = new_model.evaluate(X_data, y_data)
    print(f"불러온 모델 정확도: {accuracy:.4f}")
    # 체크포인트에서 가중치만 불러오기
    new_model.load_weights(checkpoint_path)
    # 불러온 체크포인트의 가중치로 다시 평가
    loss, accuracy = new_model.evaluate(X_data, y_data)
    print(f"체크포인트에서 불러온 모델 정확도: {accuracy:.4f}")
    # 분류 예측
    predictions = new_model.predict(X_data)
    print(f"예측값 (0 또는 1로 변환): \n{(predictions > 0.5).astype(int)}")

    3. 머신러닝 - 비지도학습

    3.1. Kmeans

    import pandas as pd
    from sklearn.cluster import KMeans
    
    # KMeans 설정 및 클러스터링 수행
    kmeans = KMeans(n_clusters=3, random_state=42)
    kmeans.fit(data)
    
    # 클러스터 결과를 데이터프레임에 추가
    data['cluster'] = kmeans.labels_
    
    # 결과 확인
    print(data.head())

    3.2. DBSCAN

    import pandas as pd
    from sklearn.cluster import DBSCAN
    
    # DBSCAN 설정 및 클러스터링 수행
    dbscan = DBSCAN(eps=0.5, min_samples=5)
    dbscan.fit(data)
    
    # 클러스터 결과를 데이터프레임에 추가
    data['cluster'] = dbscan.labels_
    
    # 결과 확인
    print(data.head())

    3.3. Elbow method

    # 클러스터 수에 따른 inertia (군집 내 거리 합) 계산
    inertia = []
    K = range(1, 11)  # 1~10개의 클러스터를 테스트
    for k in K:
        kmeans = KMeans(n_clusters=k, random_state=42)
        kmeans.fit(data)
        inertia.append(kmeans.inertia_)
    
    # Elbow Method 그래프 시각화
    plt.figure(figsize=(8, 6))
    plt.plot(K, inertia, 'bx-')
    plt.xlabel('Number of clusters (k)')
    plt.ylabel('Inertia')
    plt.title('Elbow Method For Optimal k')
    plt.show()

    3.4. 실루엣 계수

    from sklearn.metrics import silhouette_score
    
    # 실루엣 계수를 저장할 리스트
    silhouette_avg = []
    
    # 2~10개의 클러스터를 테스트
    K = range(2, 11)
    for k in K:
        kmeans = KMeans(n_clusters=k, random_state=42)
        labels = kmeans.fit_predict(data)
        silhouette_avg.append(silhouette_score(data, labels))
    
    # 실루엣 계수 시각화
    plt.figure(figsize=(8, 6))
    plt.plot(K, silhouette_avg, 'bx-')
    plt.xlabel('Number of clusters (k)')
    plt.ylabel('Silhouette Score')
    plt.title('Silhouette Score For Optimal k')
    plt.show()

    4. 생존 분석

    생존분석 정리글입니다.

    https://snowgot.tistory.com/137

     

    생존분석과 lifeline 패키지 활용 - LogRank, 카플란-마이어, 콕스비례위험모형

    1. 생존분석이란시간-이벤트 데이터(예: 생존 시간, 고장 시간 등)를 분석하는 데 사용됨주요 목표는 생존 시간 분포를 추정하고, 생존 시간에 영향을 미치는 요인을 식별하며, 여러 그룹 간의 생

    snowgot.tistory.com

    4.1. 생존 분석 분류

    • 생존 분석: 다양한 모델링 기법을 포함하는 광범위한 분야로, 시간-이벤트 데이터를 분석
      • 카플란-마이어 추정법: 특정 시간까지 이벤트가 발생하지 않을 확률을 추정하는 방법입니다. 사건이 독립적이라는 가정
      • Log-Rank 테스트: 두 그룹 간의 시간에 따른 생존율 차이를 검정
      • 콕스 비례 위험 모형: 시간에 따른 위험을 모델링하며, 공변량이 시간에 따라 비례적으로 위험률에 영향을 미친다는 가정을 기반

    4.2. 카플란 마이어

    • 특정 시간까지 이벤트가 발생하지 않을 확률(생존 함수)을 비모수적으로 추정하는 방법
    • 각 시간 점에서 생존 확률을 계산하고, 이를 통해 전체 생존 곡선을 작성.
    • 사건이 독립적이라는 가정이 있지만, 실제로는 이 가정이 항상 만족되지 않을 수 있음(실제로 병은 누적되는 대미지가 있으므로)
    • $\hat{S}(t) = \prod_{t_i \leq t} \left(1 - \frac{d_i}{n_i}\right)$

    import pandas as pd
    import matplotlib.pyplot as plt
    from lifelines import KaplanMeierFitter
    
    # 예시 데이터 생성
    data = {
        'duration': [5, 6, 6, 7, 8, 8, 10, 12, 14, 15, 18, 20, 25],
        'event': [1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1]
    }
    
    df = pd.DataFrame(data)
    
    # Kaplan-Meier Fitter 생성
    kmf = KaplanMeierFitter()
    
    # 생존 함수 적합
    kmf.fit(durations=df['duration'], event_observed=df['event'])
    
    # 생존 곡선 시각화
    plt.figure(figsize=(10, 6))
    kmf.plot_survival_function()
    plt.title('Kaplan-Meier Survival Curve')
    plt.xlabel('Time')
    plt.ylabel('Survival Probability')
    plt.grid(True)
    plt.show()

    4.3. Log-Rank

    • 카플란 마이어 기법의 응용
    • 두 그룹 간의 시간에 따른 생존율 차이를 검정하는 방법
    • 각 시간 점에서 관찰된 사건 수와 기대 사건 수를 비교하여(카이제곱검정), 두 그룹의 생존 곡선이 통계적으로 유의미하게 다른지를 평가
    • $\chi^2 = \frac{(O_1 - E_1)^2}{V_1} + \frac{(O_2 - E_2)^2}{V_2}$

    import pandas as pd
    from lifelines import KaplanMeierFitter
    from lifelines.statistics import logrank_test
    
    # 데이터 생성
    data = {
        'group': ['A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B'],
        'duration': [6, 7, 10, 15, 23, 5, 8, 12, 18, 22],
        'event': [1, 0, 1, 0, 1, 1, 1, 0, 1, 1]
    }
    
    df = pd.DataFrame(data)
    
    # 두 그룹 나누기
    groupA = df[df['group'] == 'A']
    groupB = df[df['group'] == 'B']
    
    # Kaplan-Meier Fitter 생성
    kmf_A = KaplanMeierFitter()
    kmf_B = KaplanMeierFitter()
    
    # 생존 함수 적합
    kmf_A.fit(durations=groupA['duration'], event_observed=groupA['event'], label='Group A')
    kmf_B.fit(durations=groupB['duration'], event_observed=groupB['event'], label='Group B')
    
    # Log-Rank 테스트 수행
    results = logrank_test(groupA['duration'], groupB['duration'], event_observed_A=groupA['event'], event_observed_B=groupB['event'])
    results.print_summary()

    • 시각화 코드
    import pandas as pd
    import matplotlib.pyplot as plt
    from lifelines import KaplanMeierFitter
    
    # 데이터 생성
    data = {
        'group': ['A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B'],
        'duration': [6, 7, 10, 15, 23, 5, 8, 12, 18, 22],
        'event': [1, 0, 1, 0, 1, 1, 1, 0, 1, 1]
    }
    
    df = pd.DataFrame(data)
    
    # 두 그룹 나누기
    groupA = df[df['group'] == 'A']
    groupB = df[df['group'] == 'B']
    
    # Kaplan-Meier Fitter 생성
    kmf_A = KaplanMeierFitter()
    kmf_B = KaplanMeierFitter()
    
    # 생존 함수 적합
    kmf_A.fit(durations=groupA['duration'], event_observed=groupA['event'], label='Group A')
    kmf_B.fit(durations=groupB['duration'], event_observed=groupB['event'], label='Group B')
    
    # 생존 곡선 시각화
    plt.figure(figsize=(10, 6))
    kmf_A.plot_survival_function()
    kmf_B.plot_survival_function()
    plt.title('Kaplan-Meier Survival Curves')
    plt.xlabel('Time (months)')
    plt.ylabel('Survival Probability')
    plt.legend()
    plt.grid(True)
    plt.show()

    4.4. 콕스비레 위험모형

    • 카플란마이어 추정법은 사건이 독립적이라는 가정의 한계 따라서 시간에 따른 사건 발생 위험률(위험 함수)을 모델링
    • 공변량(독립변수)이 시간에 따라 비례적으로 위험률에 영향을 미친다는 가정
    • 기준 위험률와 공변량의 선형 결합을 지수 함수 형태로 결합하여 위험률을 표현
      • 기준위험률(λ0(t)): 시간 t 기준 위험률로 공변량의 영향을 제거한 상태의 기본적인 위험률
      • 공변량(Covariates, X): 사건 발생에 영향을 미칠 수 있는 변수들
    • 시간-의존적 위험률을 모델링할 수 있으며, 공변량이 생존 시간에 미치는 영향을 평가하는 데 유용
    • $\lambda(t \mid X) = \lambda_0(t) \exp(\beta_1 X_1 + \beta_2 X_2 + \cdots + \beta_p X_p)​$

    import pandas as pd
    from lifelines import CoxPHFitter
    import matplotlib.pyplot as plt
    
    # 예시 데이터 생성
    ```
    duration: 환자가 생존한 기간(개월 수)
    event: 사건 발생 여부(1 = 사망, 0 = 생존)
    age: 환자의 나이
    treatment: 치료 방법(0 = 치료 A, 1 = 치료 B)
    ```
    data = {
        'duration': [5, 6, 6, 7, 8, 8, 10, 12, 14, 15, 18, 20, 25, 5, 7, 12, 13, 14, 16, 20],
        'event': [1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1],
        'age': [50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 55, 60, 65, 70, 75, 80, 85],
        'treatment': [0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1]
    }
    
    df = pd.DataFrame(data)
    
    # 콕스 비례 위험 모형 적합
    cph = CoxPHFitter()
    cph.fit(df, duration_col='duration', event_col='event')
    
    # 모형 요약 출력
    cph.print_summary()
    
    # 생존 곡선 시각화
    cph.plot()
    plt.title('Hazard Ratios')
    plt.show()

     

    지난장 배치에서는 유닉스의 배치처리 방식과 맵리듀스를 기반으로 한 분산파일 시스템에대해서 알아보았습니다. 많이 혼란스럽고 어려운 장이였는데요, 이번 장에서는 스트림 처리에 대해서 알아봅니다. 배치를 기반으로 하는 설명이 나온다고 해서 많이 어려울 줄 알았는데 일부 읽을만한 내용들이 있어서 재밌었습니다.


     

     

    목차

       

       

      신규개념(New)

      개념 설명
      스트림(stream) 시간 흐름에 따라 점진적으로 생산된 데이터
      레코드(record) 이벤트, 특정 시점에 일어난 사건에 대한 세부사항을 포함하는 작고 독립된 불변 객체
      생산자(producer) publisher, sender
      소비자(consumer) subscrier, recipient
      트리거(trigger) 테이블에 대한 이벤트에 반응해 자동으로 실행되는 작업
      메시징 시스템(Messaging system) 로그 데이터, 이벤트 메시지 등 API로 호출할 때 보내는 데이터를 처리하는 시스템
      윈도우(window) 집계하는 시간의 간격

      1. 배치와 스트림 처리의 차이

        배치 스트림
      정의 입력으로 파일 집합을 읽어 출력으로 새로운 파일 집합을 생성하는 기술 연속적으로 발생하는 데이터를 실시간 혹은 준 실시간으로 처리하는 기술
      활용예시 검색 색인, 추천시스템, 분석 실시간 로그 분석, 거래 모니터링, 실시간 알림 시스템 등
      출력 파생 데이터(요약 데이터ㅡ 결과 리포트 등) 실시간으로 처리된 이벤트 결과(알림 등)
      특징  끝이 있음(작업 단위가 명확) 영원히 끝나지 않음(데이터 흐름이 지속적으로 발생)

      2. 메시징 시스템

      2.1 메시징 시스템 기본

      새로운 이벤트에 대해서 소비자에게 알려주고 쓰이는 일반적인 방법은 메시징 시스템(messaging system) 이다. 메시징 시스템을 이해하기 위해서 다음 블로그에서 글 발췌했다.

      (Amendment) 메시징 시스템의 이해

      https://victorydntmd.tistory.com/343
       

      메시징 시스템( Messaging System )의 이해

      요즘 관심있게 살펴보는 주제가 MSA인데요, MSA에서는 데이터 송수신 방법으로 메시징 시스템을 사용합니다.메시징 시스템은 Kafka, RabbitMQ, Active MQ, AWS SQS, Java JMS 등이 있는데요.MSA에서는 시스템

      victorydntmd.tistory.com

       

      위는 자동 메일 발송 시스템으로 3가지 시스템이 혼재

      • 회원가입을 했을 때, 이메일을 발송하는 MemberService
      • 주문완료가 되었을 때, 이메일을 발송하는 OrderService
      • 메일을 실제 발송하는 MailService

      이렇게 서비스가 분리되었을 때 프로세스는 다음과 같습니다.

      1. MemberService에서 회원가입, OrderService에서 주문완료 이벤트가 발생
      2. Messaging Client로 메일 전송에 필요한 데이터( 받는/보내는 사람 이메일 주소, 메시지 제목/내용 등.. )를 API 호출
      3. Messaging Client에서 MOM을 구현한 소프트웨어(ex. kafka)로 메시지를 생산
      4. MailService에서 메시지가 존재하는지 구독하고 있다가 메시지가 존재하면 메시지를 소비
      5. MailService에서 API 정보들을 통해 User에게 메일 발송
      이러한 구조를 Publish/Subscribe 또는 Producer/Consumer라고 합니다.
      인용 끝

      발행(publish), 구독(subsribe) 모델에서 시스템에서  생산자가 소비자가 메시지를 처리하는 속도보다 빠르게 메시지를 전송하는 경우 3가지 선택지 존재

      • 메시지를 버림
      • 큐에 메시지를 버퍼링
      • 배압(backpressure), 흐름제어(flow control) 
        • 유닉스 파이프와 TCP의 예시

      이 경우 메시지의 유실이 허용할지 말지 등의 규칙은 어플리케이션마다 다르며 이는 배치 시스템와 대조적인 특징. 배치 시스템은 실패한 태스크를 자동으로 재시도하고 실패한 태스크가 남긴 출력을 자동으로 폐기하기 때문

      2.2. 생산자 -> 소비자로 메시지 전달하기

      • 직접 전달하기
        • 대부분의 메시지 시스템이 중간노드 없이 전달
        • 소비자가 오프라인인 경우 잃어버릴 가능성 있음. 생산자 장비가 죽어있는 경우 재시도하려고 하는 버퍼를 잃어버릴 가능 성 있음
      • 메시지 브로커(메시지 큐) 이용하기
        • 메시지 스트림을 처리하는데 최적화된 데이터베이스의 일종
        • 브로커(서버)에 데이터가 모이기 때문에 클라이언트의 상태변경(접속, 접속 해제, 장애)등에 대응 하기 좋음
        • 큐 대기시 소비자는 일반적으로 비동기로 동작
          • 생산자는 브로커가 해당 메시지를 버퍼에 넣었는지만 확인하고 소비자가 메시지를 처리하기까지 기다리지 않는 장점

      3. 스트림 처리

      3.1. 스트림이 할 수 있는 일

      1. 이벤트에서 데이터를 꺼내 데이터베이스(또는 캐시, 검색 색인 등)에 기록하고 다른 클라이언트가 시스템에 해당 데이터를 질의하게 하는 일
      2. 이벤트를 직접 사용자에게 보내는 일(Ex 이메일 경고, 푸시 알람)
      3. 하나 이상의 입력 스트림을 처리해 하나 이상의 출력 스트림을 생산
        • 이 스트림을 처리하는 코드 뭉치를 연산자(Operator), 작업(job)이라 불림

      3.2. 스트림의 예시

      • 사기 감시 시스템: 신용카드 사용 패턴의 변화로 카드 도난 감지
      • 거래 시스템: 금융시장의 Rule base 거래
      • 제조 시스템: 공장 기계상태 모니터링 및 오작동 감지
      • 군사 첩보 시스템: 침략자의 공격 신호가 있으면 경보 발령

      3.3. 스트림 분석

      • 특정 유형의 이벤트 빈도 측정
      • 특정 기간에 걸친 이동 평균(Rolling aveage) 계산
      • 이전 시간 간격과 현재 통계 값의 비교(추세를 감지, 높거나 낮은 지표 경고)

      (Amendment)스트리밍 분석이란?

      https://cloud.google.com/learn/what-is-streaming-analytics?hl=ko

       

      https://cloud.google.com/learn/what-is-streaming-analytics?hl=ko

       

      cloud.google.com

       

      4. 참고문헌

      ADP 시험을 준비하기 위하여 정리했던 방법론과 코드를 저장합니다.빅분기는 범위가 더 좁지만 공통된 부분이 많습니다.  선형계획법이나 기타 내용은 저도 몰라서 못넣었습니다 🤣 잘못된 내용이나 모호한 내용, 추가되어야하는 내용이 있으면 지적해주세요! 다들 시험에 합격하시길 바라요~


      0. 목차

      클릭시 해당 컨텐츠로 이동합니다.

          https://snowgot.tistory.com/189

           

          ADP 빅분기 이론과 Python 모듈, 실습코드 정리 - 머신러닝 편

          지난번 통계편에 이은 머신러닝문서입니다! 잘못된 내용이나 모호한 내용, 추가되어야하는 내용이 있으면 지적해주세요! 다들 시험에 합격하시길 바라요~0. 목차클릭시 해당 컨텐츠로 이동합니

          snowgot.tistory.com

          1. 모듈별 디렉토리 정리

          •  ADP 지원 버전: Python 3.7.(공지사항)
          • ADP 기본제공 Python Package & version
          pip install ipykernel=5.1.0
          pip install numpy==1.12.6
          pip install pandas==1.1.2
          pip install scipy==1.7.3
          pip install matplotlib==3.0.3
          pip install seaborn==0.9.0
          pip install statsmodels==0.13.2
          pip install pmdarima==2.0.1
          pip scikit-learn==0.23.2

          [제34회ADP실기]기본제공_python_package_list.txt
          0.01MB

          • 기본 모듈 로드
          # 일반모듈
          import warnings
          warnings.filterwarnings('ignore')
          import pandas as pd
          import numpy as np
          import os
          import datetime as dt
          
          # 머신러닝 - 전처리
          from sklearn.preprocessing import StandardScaler, MinMaxScaler, 
          LabelEncoder, OneHotEncoder, RobustScaler
          from sklearn.impute import SimpleImputer, KNNImputer
          from imblearn.over_sampling import SMOTE, RandomOverSampler
          from imblearn.under_sampling import RandomUnderSampler
          from sklearn.model_selection import train_test_split, GridSearchCV, cross_val_score, 
          KFold, StratifiedKFold
          
          # 머신러닝 - 모델
          from sklearn.linear_model import LogisticRegression, RidgeClassifier, LinearRegression, 
          Lasso, Ridge, ElasticNet
          from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier, 
          RandomForestRegressor, GradientBoostingRegressor
          from sklearn.ensemble import VotingClassifier
          from sklearn.svm import SVC, SVR
          from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor
          from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor
          
          # 머신러닝 - 비지도
          from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
          from sklearn.decomposition import PCA
          
          # 머신러닝 - 평가
          from sklearn.metrics import (classification_report, confusion_matrix, 
          roc_auc_score, precision_recall_curve, auc,
          mean_squared_error, mean_absolute_error, r2_score, accuracy_score, 
          precision_score, recall_score, f1_score)
          
          from sklearn.metrics.pairwise import cosine_similarity
          
          # 시각화
          import matplotlib.pyplot as plt
          import seaborn as sns
          
          # 통계 - scipy
          import scipy.stats as stats
          from scipy.stats import ttest_ind, ttest_rel, f_oneway, chi2_contingency, 
          mannwhitneyu, wilcoxon, pearsonr, spearmanr, normaltest
          
          # 통계 - statsmodels
          import statsmodels.api as sm
          import statsmodels.formula.api as smf
          from statsmodels.stats.multicomp import pairwise_tukeyhsd
          from statsmodels.stats.outliers_influence import variance_inflation_factor
          from statsmodels.stats.diagnostic import het_breuschpagan
          
          # 시계열
          from statsmodels.tsa.stattools import adfuller, acf, pacf, kpss
          from statsmodels.tsa.seasonal import seasonal_decompose
          from statsmodels.tsa.arima.model import ARIMA
          from statsmodels.tsa.holtwinters import ExponentialSmoothing, SimpleExpSmoothing
          from statsmodels.tsa.statespace.sarimax import SARIMAX
          from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
          
          # pmdarima (자동 ARIMA)
          from pmdarima import auto_arima
          • 한글폰트 설정(선택사항)
          import matplotlib.pyplot as plt
          import platform
          
          def set_matplotlib_font():
              system = platform.system()
          
              if system == "Windows":
                  plt.rc('font', family='Malgun Gothic')
              elif system == "Darwin":  # macOS
                  plt.rc('font', family='AppleGothic')
              elif system == "Linux":
                  plt.rc('font', family='NanumGothic')
              else:
                  print("Unknown system. Please set font manually.")
          
              plt.rcParams['axes.unicode_minus'] = False
          
          # 폰트 설정 함수 호출
          set_matplotlib_font()

          1.1. datetime

          datetime
          │
          ├── datetime                   # 날짜와 시간을 모두 처리하는 클래스
          │   ├── now()                  # 현재 날짜와 시간을 반환
          │   ├── strptime()             # 문자열을 datetime 객체로 변환
          │   ├── strftime()             # datetime 객체를 문자열로 변환
          │   ├── year,month, day        # 년도, 월, 일 추출
          │   ├── hour,minute, second    # 시간, 분, 초 추출
          │   ├── date(), time()         # 날짜 (년, 월, 일)만, 시간 (시, 분, 초)만 반환
          │   ├── replace()              # 특정 날짜/시간 값을 변경하여 새로운 datetime 객체 반환
          │   ├── weekday()              # 요일 반환 (월: 0, 일: 6)
          │   └── combine()              # 날짜와 시간을 결합하여 새로운 datetime 객체 생성
          │
          ├── date                       # 날짜만 처리하는 클래스
          │   ├── today()                # 오늘 날짜 반환
          │   ├── fromisoformat()        # ISO 형식의 문자열에서 date 객체 생성
          │   ├── year,month, day        # 년도, 월, 일 추출
          │
          ├── time                       # 시간만 처리하는 클래스
          │   ├── hour,minute, second    # 시간, 분, 초 추출
          │   └── microsecond            # 마이크로초 추출
          │
          ├── timedelta                  # 두 날짜 또는 시간 간의 차이를 계산하는 클래스
          │   ├── days                   # 차이 나는 일수
          │   ├── seconds                # 차이 나는 초
          │   └── total_seconds()        # 총 차이 시간을 초 단위로 반환
          │
          └── timezone                   # 시간대 정보를 다루는 클래스
              ├── utc                    # UTC 시간대 객체
              └── tzinfo                 # 사용자 정의 시간대 설정을 위한 클래스

          1.2. numpy==1.21.6

          numpy
          │
          ├── 기본 통계 함수
          │   ├── mean()                 # 데이터의 평균값 계산
          │   ├── median()               # 데이터의 중앙값 계산
          │   ├── std()                  # 데이터의 표준편차 계산
          │   ├── var()                  # 데이터의 분산 계산
          │   ├── sum()                  # 데이터의 합계 계산
          │   ├── prod()                 # 데이터의 곱 계산
          │
          ├── 퍼센타일 및 백분위 함수
          │   ├── percentile()           # 데이터의 특정 퍼센타일 값 계산
          │   ├── quantile()             # 데이터의 특정 분위 값 계산
          │
          ├── 최소값/최대값 관련 함수
          │   ├── min()                  # 데이터의 최소값 반환
          │   ├── max()                  # 데이터의 최대값 반환
          │   ├── argmin()               # 최소값의 인덱스 반환
          │   ├── argmax()               # 최대값의 인덱스 반환
          │
          ├── 데이터 생성 및 처리 함수
          │   ├── histogram()            # 데이터의 히스토그램 계산
          │   ├── unique()               # 데이터에서 고유 값 반환
          │   ├── bincount()             # 정수 배열의 값의 빈도 계산
          │
          ├── 랜덤 데이터 생성 (통계적 실험 시 사용 가능)
          │   ├── random.randn()         # 표준 정규분포를 따르는 랜덤 값 생성
          │   ├── random.normal()        # 정규분포를 따르는 랜덤 값 생성
          │   ├── random.randint()       # 정수 범위에서 랜덤 값 생성
          │   ├── random.choice()        # 데이터에서 랜덤 샘플 추출

          1.3. scipy==1.7.3

          scipy
          │
          ├── stats                      # 통계 분석과 확률 분포 관련 함수 제공
          │   ├── norm                   # 정규분포 관련 함수 (PDF, CDF, 랜덤 샘플링 등)
          |   |── uniform                # 균등분포
          |   |── bernoulli              # 베르누이 분포
          |   |── binom                  # 이항분포
          │   ├── ttest_ind              # 독립 두 표본에 대한 t-검정
          │   ├── ttest_rel              # 대응표본 t-검정
          │   ├── mannwhitneyu           # Mann-Whitney U 비모수 검정
          │   ├── chi2_contingency        # 카이제곱 독립성 검정
          │   ├── shapiro                # Shapiro-Wilk 정규성 검정
          │   ├── kstest                 # Kolmogorov-Smirnov 검정 (분포 적합성 검정)
          │   ├── probplot               # Q-Q plot 생성 (정규성 시각화)
          │   ├── pearsonr               # Pearson 상관계수 계산
          │   ├── spearmanr              # Spearman 순위 상관계수 계산
          │   └── describe               # 기술 통계량 제공 (평균, 표준편차 등)
          │
          ├── optimize                   # 함수 최적화 및 곡선 피팅 관련 모듈
          │   ├── minimize               # 다변수 함수의 최소값을 찾는 최적화 도구
          │   ├── curve_fit              # 비선형 곡선 피팅 (최소제곱법 기반)
          │   └── root                   # 방정식의 근을 찾는 도구
          │
          ├── linalg                     # 선형대수 관련 함수
          │   ├── inv                    # 행렬의 역행렬 계산
          │   ├── det                    # 행렬식 계산
          │   └── svd                    # 특이값 분해(SVD) 수행
          │
          ├── interpolate                # 데이터 보간 관련 함수
          │   ├── interp1d               # 1차원 선형 보간 함수
          │   └── griddata               # 다차원 보간 수행 (비정형 데이터에 사용)
          │
          └── special                    # 특수 함수 (감마 함수, 베타 함수 등)
              ├── gamma                  # 감마 함수
              └── beta                   # 베타 함수

          1.4. pandas==1.1.2

          pandas
          │
          ├── melt: pivot 형태를 다시바꾸기
          │
          ├── DataFrame                  # 2차원 데이터 구조, 테이블 형태의 데이터 관리
          │   ├── groupby                # 데이터프레임 그룹핑
          │   ├── merge                  # SQL 스타일로 데이터프레임 병합
          │   ├── join                   # 데이터프레임 조인 (인덱스를 기준)
          │   ├── pivot_table            # 피벗 테이블 생성
          │   ├── apply                  # 사용자 정의 함수를 데이터프레임에 적용
          │   ├── isnull                 # 결측치 여부 확인
          │   ├── fillna                 # 결측치 대체
          │   └── drop                   # 행 또는 열 삭제
          │
          ├── Series                     # 1차원 데이터 구조, 배열 형태의 데이터 관리
          │   ├── value_counts           # 고유 값의 빈도수 반환
          │   └── unique                 # 고유 값 반환
          │
          ├── time_series                # 시계열 데이터 처리 도구
          │   ├── to_datetime            # 문자열을 날짜 형식으로 변환
          │   ├── resample               # 시계열 데이터 리샘플링
          │   ├── shift                  # 데이터를 앞이나 뒤로 이동
          │   └── rolling                # 이동 평균 등 롤링 윈도우 계산
          │
          └── plotting                   # 데이터 시각화 도구
              ├── plot                   # 라인 플롯, 기본 시각화 함수
              ├── hist                   # 히스토그램 생성
              ├── boxplot                # 박스 플롯 생성
              └── scatter                # 산점도 생성

          1.5. statsmodel==0.13.2

          statsmodels
          │
          ├── api                        # 주요 모듈에 접근하기 위한 API 제공
          │
          ├── stats                      # 통계적 테스트 및 유틸리티
          │   ├── diagnostic            # 모델 진단 도구 (자기상관, 이분산성 등)
          │   ├── stattools             # 통계 도구 (자기상관 함수, 부분자기상관 함수 등)
          │   ├── multivariate          # 다변량 통계 분석
          │   ├── anova                 # 분산 분석(ANOVA) 관련 함수
          │   │   ├── anova_lm          # 선형 모델에 대한 ANOVA 테이블
          │   │   └── multicomp         # 다중 비교 방법
          │   └── nonparametric         # 비모수 통계 검정 (KDE, 커널 회귀 등)
          │
          ├── duration                   # 생존 분석(Survival Analysis) 관련 모듈
          │   ├── hazard_regression     # 위험 회귀 모델
          │   │   ├── cox               # Cox 비례 위험 모델
          │   │   └── phreg             # 비례 위험 회귀 모델
          │   ├── survfunc              # 생존 함수 추정 (Kaplan-Meier 등)
          │   │   ├── kaplan_meier      # 카플란-마이어 추정량
          │
          ├── tsa                        # 시계열 분석(Time Series Analysis) 관련 모듈
          │   ├── arima                  # ARIMA 모델 구현
          │   ├── statespace            # 상태 공간 모델 (State Space Models)
          │   ├── vector_ar             # 벡터 자기회귀(VAR) 모델
          │   ├── seasonal              # 계절성 분해 (SARIMAX 등)
          │   └── filters               # 칼만 필터, HP 필터 등

          1.6. sklearn==0.23.2

          scikit-learn
          │
          ├── model_selection          # 모델 평가, 검증, 및 데이터 분할
          │   ├── train_test_split      # 데이터를 훈련 세트와 테스트 세트로 분할
          │   ├── cross_val_score       # 교차 검증 점수 계산
          │   ├── GridSearchCV          # 하이퍼파라미터 최적화
          │   └── KFold                 # K-폴드 교차 검증
          │
          ├── preprocessing             # 데이터 전처리 및 스케일링
          │   ├── StandardScaler        # 데이터 표준화 (평균 0, 표준편차 1로 스케일링)
          │   ├── MinMaxScaler          # 데이터 값을 0과 1 사이로 스케일링
          │   ├── LabelEncoder          # 범주형 데이터를 숫자로 변환
          │   └── OneHotEncoder         # 범주형 데이터를 원-핫 인코딩
          │
          ├── decomposition             # 차원 축소 기법
          │   ├── PCA                   # 주성분 분석 (Principal Component Analysis)
          │   ├── TruncatedSVD          # 차원 축소 (Singular Value Decomposition)
          │   └── NMF                   # 비음수 행렬 분해 (Non-negative Matrix Factorization)
          │
          ├── metrics                   # 모델 평가 지표
          │   ├── accuracy_score        # 정확도 평가
          │   ├── confusion_matrix      # 혼동 행렬 계산
          │   ├── classification_report # 분류 모델 평가 보고서
          │   ├── roc_auc_score         # ROC AUC 점수 계산
          │   └── mean_squared_error    # 회귀 모델의 MSE 계산
          │
          ├── linear_model              # 선형 모델
          │   ├── LinearRegression      # 선형 회귀
          │   ├── LogisticRegression    # 로지스틱 회귀
          │   ├── Ridge                 # 릿지 회귀 (L2 정규화)
          │   ├── Lasso                 # 라쏘 회귀 (L1 정규화)
          │   └── ElasticNet            # 엘라스틱넷 회귀 (L1 + L2 정규화)
          │
          ├── ensemble                  # 앙상블 학습
          │   ├── RandomForestClassifier# 랜덤 포레스트 분류기
          │   ├── GradientBoostingClassifier # 그래디언트 부스팅 분류기
          │   ├── RandomForestRegressor # 랜덤 포레스트 회귀
          │   └── GradientBoostingRegressor # 그래디언트 부스팅 회귀
          │
          ├── neighbors                 # 최근접 이웃 알고리즘
          │   ├── KNeighborsClassifier  # K-최근접 이웃 분류기
          │   ├── KNeighborsRegressor   # K-최근접 이웃 회귀
          │   └── NearestNeighbors      # 최근접 이웃 검색
          │
          ├── svm                       # 서포트 벡터 머신
          │   ├── SVC                   # 서포트 벡터 분류기
          │   └── SVR                   # 서포트 벡터 회귀
          │
          ├── tree                      # 결정 트리 알고리즘
          │   ├── DecisionTreeClassifier # 결정 트리 분류기
          │   └── DecisionTreeRegressor  # 결정 트리 회귀
          │
          └── cluster                   # 클러스터링 알고리즘
              ├── KMeans                # K-평균 클러스터링
              ├── DBSCAN                # 밀도 기반 클러스터링
              └── AgglomerativeClustering # 계층적 클러스터링

          2. 데이터 전처리

          • datetime 자료형
            • 날짜와 시간을 표현하는 자료형
            • datetime(2023, 10, 9, 14, 30, 0) # 2023-10-09 14:30:00
            • ts.to_pydatetime() 함수로 ts→ dt변환 가능
          • timstamp 자료형
            • 유닉스 타임 스탬프로, 1970년 1월 1일로부터 경과 시간을 측정
            • pd.Timestamp() 함수로 dt → ts 변환 가능
          • 문자형 자료형 -> datetime
            • pd.to_datetime('2020-01-01 01:00', format = ‘%Y-%m-%d %H:%M’)
          • 날짜의 기간별로 만들기
            • pd.date_range(start='2020-01-01', end='2020-01-31', freq = 'D')
          • 예시
          from datetime import datetime
          
          # 문자열을 datetime 객체로 변환
          date_string = '01/01/2018 00:15'
          date_object = datetime.strptime(date_string, '%m/%d/%Y %H:%M')
          
          # 년, 월, 일, 시간, 분 추출
          year = date_object.year
          month = date_object.month
          day = date_object.day
          hour = date_object.hour
          minute = date_object.minute
          • 특정 시간 기준으로 시간 간격 구하기
          #실용 예제1
          time_mid = '2024-10-09 00:00:00'
          from datetime import datetime
          time_stamp = datetime.strptime(time_mid, '%Y-%m-%d %H:%M:%S')
          (datetime.now() - time_stamp).total_seconds()/60
          
          #실용예제2
          from datetime import datetime
          y = df['datetime'] - df['datetime'].apply(lambda x : datetime.combine(x, datetime.min.time()))
          df['nsd'] = y.apply(lambda td :td.total_seconds()/60)
          • 피벗
          pd.pivot_table(values, index, columns, aggfunc='mean')
          • 언피벗
          pd.melt(id_vars = [기준컬럼], value_vars = [값컬럼], value_name = '값이름')
          • 머지
          pd.merge(df1, df2, how = 'left', on = 'key')

          3. 통계 추정

          3.1. 점 추정

          • 모수를 하나의 값으로 추정하는 방법
          • 모평균에 대한 점추정: $\bar{x} = \frac{1}{n}\sum_{i=1}^{n}x_{i}$
          import numpy as np
          sample_mean = np.mean(data)
          • 모분산에 대한 점추정: $s^{2} = \frac{1}{n-1}\sum_{i=1}^{n}(x_{i} - \bar{x})^2$
            • n-1을 자유도로 나누어 불편성을 보장
          sample_std = np.var(data, ddof = 1) #자유도 n-1
          • 모비율에 대한 점추정: $p^{2} = \frac{x}{n}$
            • $x$: 표본의 사건 횟수
          x = np.sum(x)
          n = len(data)
          
          x/n

          3.2. 구간 추정

          • 모수가 특정 구간 내에 있을 것이라는 신뢰 구간을 제시하는 방법
          • 모평균에 대한 구간 추정
            • $\sigma^{2}$ Known: $ci = \bar{x} \pm z_{\frac{\alpha}{2}}(\frac{\sigma}{\sqrt{n}})$
            • $\sigma^{2}$ UnKnown: $ci = \bar{x} \pm t_{\frac{\alpha}{2}, n-1}(\frac{\sigma}{\sqrt{n}})$
          import numpy as np
          from scipy.stats import t
          
          # 신뢰수준 설정
          confidence_level = 0.95
          alpha = 1 - confidence_level
          
          # 1. 모분산을 알때
          z = stats.norm.ppf(1 - alpha/2)
          margin_of_error = z * (sample_std/np.sqrt(n))
          print(sample_mean- margin_of_error, sample_mean + margin_of_error)
          
          # 2. 모분산을 모를 때
          t_crit = t.ppf(1-alpha /2, df = n-1)
          margin_of_error_t = t_crit *(sample_std/np.sqrt(n))
          print(sample_mean- margin_of_error_t, sample_mean + margin_of_error_t
          
          # 3. t.interval 사용
          stats.t.interval(confidnece_level, df = n-1, loc = sample_min, scale = sample_std/np.sqrt(n))
          • 모 분산에 대한 구간 추정
            • $\left( \frac{(n - 1) s^2}{\chi^2_{1 - \alpha/2, \; n - 1}}, \; \frac{(n - 1) s^2}{\chi^2_{\alpha/2, \; n - 1}} \right)$
          # 자유도
          df = n - 1
          
          # 카이제곱 값 계산
          chi2_lower = stats.chi2.ppf(alpha / 2, df)
          chi2_upper = stats.chi2.ppf(1 - alpha / 2, df)
          
          # 신뢰구간 계산
          lower_bound = (df * sample_variance) / chi2_upper
          upper_bound = (df * sample_variance) / chi2_lower
          
          print(f"{confidence_level*100}% 신뢰구간: ({lower_bound}, {upper_bound})")
          • 모 비율에 대한 구간 추정
            • $\hat{p} \pm z_{\alpha/2}\sqrt{\frac{\hat{p}(1-\hat{p})}{n}}$
          from statsmodels.stats.proportion import proportion_confint
          # 성공 횟수와 전체 시행 횟수
          x = np.sum(data)  # 성공 횟수
          n = len(data)     # 전체 시행 횟수
          
          lower_bound, upper_bound = proportion_confint(success_count, n, alpha=alpha, method='normal')
          
          print(f"{confidence_level*100}% 신뢰구간: ({lower_bound}, {upper_bound})")

          4. 통계 검정 - 모수적 방법

          통계 검정에 대한 마인드맵은 다음 글 참고하세요

          https://snowgot.tistory.com/153

           

          (8) Khan Academy: 유의성 검정과 절차, 통계방법론 정리

          이번 글에서는 추론통계의 핵심 유의성 검정과 등장하는 개념, 그리고 일반적인 통계방법론을 정리해본다.1. 글목차유의성 검정에 필요한 개념1종오류와 2종오류유의성 검정 절차통계검정 절차

          snowgot.tistory.com

          • 정규성 검정
            • $H_o:$ 데이터가 정규분포를 따른다.
            • $H_1:$ 데이터가 정규분포를 따르지 않는다.
            1. shapiro-wilk : 작은 표본(n≤ 50) 에 적합
            2. Kolmogorov-Smirnov: 표본이 클 때 사용, shaprio보다 덜 강력
          • t-test검정을 위한 정규성을 시행할 시, 두 그룹 중에 하나라도 정규성을 만족하지 않으면 바로 맨-휘트니 방법으로 넘어감
          #정규성 검정 - Shapiro-Wilk
          from scipy.stats import shapiro
          statistic, p_value = shapiro(data)
          
          #정규성 검정 - Kolmogorov-Smirnov 검정 
          from scipy.stats import kstest
          statistic, p_value = kstest(data, 'norm')
          
          print(f"Test Statistic: {statistic}")
          print(f"p-value: {p_value}")
          
          if p_value > alpha:
              print("정규성을 따른다고 볼 수 있습니다.")
          else:
              print("정규성을 따른다고 볼 수 없습니다.")
          • 시각화 예제
          from scipy.stats import shapiro, probplot
          import matplotlib.pyplot as plt
          import seaborn as sns
          
          man = df3[df3['성별'] =='남']['급여']
          woman = df3[df3['성별'] =='여']['급여']
          
          # Shapiro-Wilk test
          statistic_m, p_value_m = shapiro(man)
          statistic_w, p_value_w = shapiro(woman)
          
          # Figure 생성
          plt.figure(figsize=(12, 6))
          
          # 1. 남성 그룹 히스토그램과 Q-Q plot
          plt.subplot(2, 2, 1)
          sns.histplot(man, kde=True, label='man', color='blue')
          plt.title(f'Man Salary Distribution\nShapiro p-value: {p_value_m:.3f}')
          plt.legend()
          
          plt.subplot(2, 2, 2)
          probplot(man, dist="norm", plot=plt)
          plt.title('Man Q-Q Plot')
          
          # 2. 여성 그룹 히스토그램과 Q-Q plot
          plt.subplot(2, 2, 3)
          sns.histplot(woman, kde=True, label='woman', color='green')
          plt.title(f'Woman Salary Distribution\nShapiro p-value: {p_value_w:.3f}')
          plt.legend()
          
          plt.subplot(2, 2, 4)
          probplot(woman, dist="norm", plot=plt)
          plt.title('Woman Q-Q Plot')
          
          plt.tight_layout()
          plt.show()
          • 등분산 검정
            • $H_o:$ 두 집단의 분산이 같다.
            • $H_1:$ 두 집단의 분산이 다르다.
          검정 이름 정규성 가정 이상치에 대한 민감도 특징
          Levene 필요 없음 중간 여러 그룹의 분산이 동일한지 검정
          Bartlett 필요함 민감함 정규성 만족 시 Levene보다 검정력 높음
          Fligner 필요 없음 강건함 순위 기반 비모수 검정, 이상치 존재 시 유리
          from scipy.stats import levene, bartlett, fligner
          
          statistic, p_value = levene(data1, data2)
          # statistic, p_value = bartlett(data1, data2)
          # statistic, p_value = fligner(data1, data2)
          • 일표본 t 검정
            • $H_o:$ 표본의 평균이 특정 값과 같다.
            • $H_1:$ 표본의 평균의 특정 값과 같지 않다.
          • 이표본 t 검정
            • $H_o:$ 두 집단의 평균이 같다.
            • $H_1:$ 두 집단의 평균이 다르다.
          이표본 t검정일 때 정규성 & 등분산 검정을 진행하게 되는데, 등분산일 경우 두 표본의 분산을 하나로 합쳐서 추정하는 "합동 분산"활용이 가능해짐. 이는 검정통계량의 분산을 하나로 합치게 되고 좀 더 정확한 추정이 가능하게함. 하지만 등분산이 만족되지 않은 경우 비모수 검정인 맨-휘트니를 진행함
          # 독립표본 t 검정
          from scipy.stats import ttest_ind,ttest_rel
          # 등분산성을 만족하는 경우
          statistic, p_value = ttest_ind(data1, data2, equal_var=True)
          # 등분산성을 만족하지 않는 경우
          # statistic, p_value = ttest_ind(data1, data2, equal_var=False)
          
          #대응표본 t검정
          # statistic, p_value = ttest_rel(data_before, data_after)
          
          print(statistic, p_value)
          • 분산 분석
            • 목적: 그룹간 분산과그룹내 분산을 비교
            • $H_0:$ 모든 그룹의 평균값이 같다.
            • $H_1:$ 적어도 하나 그룹의 평균값이 다른 그룹과 다르다.
          import pandas as pd
          import numpy as np
          
          # 그룹별 데이터 생성 (예시 데이터)
          np.random.seed(123)
          
          group_A = np.random.normal(loc=50, scale=5, size=30)
          group_B = np.random.normal(loc=55, scale=5, size=30)
          group_C = np.random.normal(loc=60, scale=5, size=30)
          
          # 데이터 프레임 생성
          df = pd.DataFrame({
              'score': np.concatenate([group_A, group_B, group_C]),
              'group': ['A'] * 30 + ['B'] * 30 + ['C'] * 30
          })
          
          import statsmodels.api as sm
          from statsmodels.formula.api import ols
          
          # ANOVA 모델 생성
          model = ols('score ~ C(group)', data=df).fit()
          
          # 분산분석표 생성
          anova_table = sm.stats.anova_lm(model, typ=2)
          
          # 결과 출력
          print(anova_table)
          • 사후 분석
            • Tukey’s HSD 검정
          from statsmodels.stats.multicomp import pairwise_tukeyhsd
          
          # 일원분산분석의 사후분석 예시
          tukey_result = pairwise_tukeyhsd(endog=df['score'], groups=df['group'], alpha=0.05)
          
          print(tukey_result)
          • two-way anova
          import statsmodels.api as sm
          from statsmodels.formula.api import ols
          
          # 이원분산분석 모델 생성 (교호작용 포함)
          model = ols('값 ~ C(요인1) * C(요인2)', data=df).fit()
          
          # 분산분석표 생성
          anova_table = sm.stats.anova_lm(model, typ=2)
          # 결과 출력
          print(anova_table)
          • Welch Anova
            • 목적: 그룹 간의 등분산성이 가정이 필요없는 경우 사용
            • $H_0:$ 모든 그룹의 평균이 같다.
            • $H_1:$ 적어도 하나 그룹의 평균값이 다른 그룹과 다르다.
          import numpy as np
          import pandas as pd
          import scipy.stats as stats
          import statsmodels.api as sm
          from statsmodels.formula.api import ols
          
          # 기술 통계량 확인
          print(data.groupby('group').agg(['mean', 'std', 'count']))
          
          # 등분산성 검정 (Levene's test)
          levene_stat, levene_p = stats.levene(group1, group2, group3)
          print(f"\nLevene's test: statistic={levene_stat:.4f}, p-value={levene_p:.4f}")
          if levene_p < 0.05:
              print("The variances are significantly different. Welch ANOVA is appropriate.")
          else:
              print("The variances are not significantly different. Standard ANOVA can be used.")
          
          # Welch ANOVA 수행
          welch_stat, welch_p = stats.f_oneway(group1, group2, group3, welch=True)
          print(f"\nWelch ANOVA: F-statistic={welch_stat:.4f}, p-value={welch_p:.4f}")
          
          if welch_p < 0.05:
              print("Reject null hypothesis: At least one group mean is different.")
          else:
              print("Fail to reject null hypothesis: No significant difference between group means.")
          
          # 사후 검정 (Games-Howell post-hoc test)
          from statsmodels.stats.multicomp import pairwise_tukeyhsd
          # 참고: statsmodels에는 Games-Howell 검정이 직접 구현되어 있지 않아 Tukey HSD를 대신 사용
          # 실제 연구에서는 Games-Howell 테스트 사용 권장 (등분산 가정 필요 없음)
          tukey_results = pairwise_tukeyhsd(data['value'], data['group'], alpha=0.05)
          print("\nPost-hoc test (Tukey HSD, note: Games-Howell would be better for unequal variances):")
          print(tukey_results)

          4. 통계검정 - 비모수적 방법

          4.1. 비모수방법 표 정리

          • 데이터가 특정 분포를 따르지 않거나 데이터 양이 적어 분포가정을 만족시키기 어려울때 사용. 데이터의 순위나 중앙값을 사용
          검정명 대응 표본 그룹 수 데이터 예시 연관 있는 검정
          카이제곱 X 2 or 3 범주형 성별에 따른 선호도 차이 -
          피셔 정확검정 O 2 범주형 성공/실패 비율 비교 카이제곱
          맨 휘트니 X 2 연속형 두 집단의 성적 중앙값 비교 독립표본 t 검정
          크루스칼 왈리스 X 3이상 연속형 여러 그룹의 성적비교 ANOVA
          윌콕슨 O 2 연속형 치료 전후의 차이 대응표본 t검정
          맥니마 O 2 이항형 치료 전후의 호전 약화 여부 대응표본 카이제곱
          부호검정 O 2 연속형/이항형 치료 전후의 비율 변화 대응표본 t검정
          프리드먼 O 3이상 연속형 세 시점에서의 결과 비교 반복측정 ANOVA
          코크란Q O 3이상 이항형 세 시점에서의 성공/실패 비교 반복측정 카이제곱

          4.2. 카이제곱 검정

          1. 독립성 검정
          • $H_0$: 두 범주형 변수는 독립이다.
          • $H_1$: 두 범주형 변수는 독립이 아니다.

          2. 적합도 검정

          • 수식: $\chi^{2} = \sum^{n}{i}\frac{(O{i} - E_{i})^{2}}{E_{i}}$
          • $H_0$: 관측과 기대 비율은 차이가 없다.
          • $H_1$: 관측과 기대 비율은 차이가 있다.

          import numpy as np
          from scipy.stats import chisquare
          
          # 주장하는 방문 비율(%)을 기반으로 기대값을 계산
          expected_ratios = np.array([10, 10, 15, 20, 30, 15])
          total_visits = 200
          expected_counts = (expected_ratios / 100) * total_visits
          
          # 관찰된 방문 수
          observed_counts = np.array([30, 14, 34, 45, 57, 20])
          
          # 카이제곱 적합도 검정을 수행
          chi2_stat, p_val = chisquare(f_obs=observed_counts, f_exp=expected_counts)
          
          # 결과 출력
          print(f"Chi-squared Statistic: {chi2_stat:.3f}") # 11.442
          print(f"P-value: {p_val:.3f}") # 0.043

          3. 동질성 검정

          import numpy as np
          from scipy.stats import chi2_contingency
          
          # 관찰된 데이터: 각 학교에서 학생들이 선호하는 과목의 빈도수
          data = np.array([
              [50, 60, 55],  # 수학 선호
              [40, 45, 50],  # 과학 선호
              [30, 35, 40]   # 문학 선호
          ])
          
          # 카이제곱 통계량, p-value, 자유도, 기대값을 계산
          chi2_stat, p_val, dof, expected = chi2_contingency(data)
          
          # 결과 출력
          print(f"Chi-squared Statistic: {chi2_stat:.3f}"): 0.817
          print(f"P-value: {p_val:.3f}") : 0.936
          print(f"Degrees of Freedom: {dof:.3f}") # 0.400
          print("Expected frequencies:")
          print(expected.round(3))
          
          '''
          [[48.889 57.037 59.074]
           [40.    46.667 48.333]
           [31.111 36.296 37.593]]
          '''

          4.3. Wilcoxon 검정

          • 목적: 짝지어진 관측 값(대응 표본) 이 차이가 0이라는 가설 검정
          • 가정: 데이터는 연속적이거나 ordinal, 데이터는 대칭
          • $H_o:$ 두 그룹의 중앙값 차이는 0이다.
          • $H_1:$ 두 그룹의 중앙값 차이는 0이 아니다.
          from scipy.stats import wilcoxon
          
          # 예시 데이터 (대응 표본)
          before = [20, 21, 22, 23, 24, 25]
          after = [21, 22, 19, 23, 26, 25]
          
          # Wilcoxon 부호 순위 검정
          stat, p_value = wilcoxon(before, after)

          4.4 Mann-Whitney U 검정

          • 목적: 독립된 두 표본간의 중위수 차이 검정
          • 가정: 데이터는 연속적이거나 ordinal, 두 표본은 독립
          • $H_o:$ 두 그룹의 중위 수 차이는 0이다.
          • $H_1:$ 두 그룹의 중위수 차이는 0 이 아니다.
          from scipy.stats import mannwhitneyu
          
          # 예시 데이터 (독립 표본)
          group1 = [14, 16, 18, 20, 22]
          group2 = [18, 20, 22, 24, 26]
          # Mann-Whitney U 검정
          stat, p_value = mannwhitneyu(group1, group2, alternative='two-sided')

          4.5. Kruskal-Wallis 검정

          • 목적: 세 개 이상의 독립된 표본의 중앙값을 비교. anova의 대응
          • 가정: 각 표본은 독립, 데이터는 ordinal
          • $H_o:$ 모든 그룹의 중위수 차이가 0이다.
          • $H_1:$ 적어도 하나 그룹의 중앙값이 다른 그룹과 다르다.
          from scipy.stats import kruskal
          
          # 예시 데이터 (세 그룹의 독립 표본)
          group1 = [10, 12, 14, 16, 18]
          group2 = [15, 17, 19, 21, 23]
          group3 = [24, 26, 28, 30, 32]
          
          # Kruskal-Wallis H 검정
          stat, p_value = kruskal(group1, group2, group3)

          4.6. 맥니마 검정

          • 목적: 이항 데이터에서 전후 또는 대응된 두 집단의 비율 변화
          • 가정: 데이터는 binary, 대응 표본 존재, 표본크기 충분히 커야함
          • $H_0$: 두 대응된 이항 분포간 변화 없음
          • $H_1$: 두 대응된 이항 분포간 변화 있음
          import numpy as np
          from statsmodels.stats.contingency_tables import mcnemar
          
          # 2x2 분할표 생성 (a, b, c, d)
          # 예시 데이터: (성공/실패 전후)
          # [[성공 전/성공 후, 성공 전/실패 후],
          #  [실패 전/성공 후, 실패 전/실패 후]]
          table = np.array([[40, 10],  # 성공 전/후
                            [5, 45]])  # 실패 전/후
          
          # 맥니마 검정 수행
          result = mcnemar(table, exact=True)  # exact=True: 정확한 검정 수행 (표본 크기가 작을 경우)
          
          # 결과 출력
          print('맥니마 검정 통계량:', result.statistic)
          print('p-value:', result.pvalue)

          4.7. 피셔의 정확검정

          • 목적: 작은 표본에서 두 범주형 변수간의 독립성의 검정
          • 가정: 각셀 기대빈도(N≤5) 이항 분포 또는 범주형 자료료 표현됨, 행과 열의 합은 고정
          • $H_0$: 두 변수는 독립적이다.
          • $H_1$: 두 변수는 독립적이지 않다.
          import numpy as np
          from scipy import stats
          
          # 2x2 분할표 데이터 (예시)
          # [[그룹 A, 그룹 B],
          #  [그룹 A, 그룹 B]]
          table = np.array([[8, 2],  # 결과 X
                            [1, 5]]) # 결과 Y
          
          # 피셔의 정확검정 수행
          odds_ratio, p_value = stats.fisher_exact(table, alternative='two-sided')
          
          # 결과 출력
          print('오즈비:', odds_ratio)
          print('p-value:', p_value)

          4.8. 프리드먼 검정

          • 목적: 반복 측정된 세개 그룹 간의 차이를 비교하는 검정
          • 가정: 연속형 or 순위형 데이처 사용, 각 그룹은 동일한 주제/대상에 여러번 측정된 결과
          • $H_0$ : 세 개 이상의 관련된 그룹 간의 차이가 없다.
          • $H_1$: 적어도 하나의 그룹에서 다른 그룹과 유의미한 차이가 있다.
          import numpy as np
          from scipy.stats import friedmanchisquare
          
          # 예시 데이터: 세 개의 처리에 대한 측정값
          # 동일한 대상(피험자)에 대해 세 번의 처리를 반복적으로 측정한 값
          # 행은 피험자, 열은 각각의 처리
          data1 = [10, 20, 30, 40]  # 처리 1
          data2 = [12, 21, 29, 35]  # 처리 2
          data3 = [14, 22, 27, 37]  # 처리 3
          
          # 프리드먼 검정 수행
          statistic, p_value = friedmanchisquare(data1, data2, data3)
          
          # 결과 출력
          print('프리드먼 검정 통계량:', statistic)
          print('p-value:', p_value)

          4.9. 부호검정

          • 목적: 관련된 두 그룹 간의 차이를 평가하는 검정, 중앙 값의 차이를 평가
          • 가정: 대응된 표본
          • $H_0:$ 두 그룹간 중앙값 차이가 없다.
          • $H_1$ : 두 그룹간 중앙값 차이가 있다.
          import numpy as np
          from scipy import stats
          
          # 예시 데이터: 두 그룹에 대한 대응된 표본 (처치 전후 데이터)
          before = [85, 70, 90, 80, 65, 75, 95, 70]  # 처리 전
          after  = [80, 65, 88, 82, 63, 78, 92, 72]  # 처리 후
          
          # 부호검정 수행
          result = stats.binom_test((before > after).sum(), n=len(before), p=0.5, alternative='two-sided')
          
          # 결과 출력
          print('p-value:', result)

          4.10. 코크란 q검정

          • 목적: 세개 이상의 이항 처리에 대한 대응 표본의 비율의 차이를 비교하기 위한 검정
          • 가정: 데이터는 binary, 대응된 표본
          • $H_0$: 세 개 이상의 처리 간에 차이가 없다.
          • $H_1$ : 적어도 하나의 처리에서는 다른 처리와 유의미한 차이가 있다.
          import numpy as np
          import pandas as pd
          from statsmodels.sandbox.stats.runs import cochrans_q
          
          # 예시 데이터: 세 가지 처리에 대한 이항형 결과 (성공/실패)
          # 각 행은 개별 피험자, 각 열은 다른 처리
          data = pd.DataFrame({
              'treatment1': [1, 0, 1, 1, 0],  # 처리 1 결과 (성공: 1, 실패: 0)
              'treatment2': [0, 1, 1, 1, 0],  # 처리 2 결과
              'treatment3': [1, 0, 1, 0, 1],  # 처리 3 결과
          })
          
          # 코크란 Q 검정 수행
          statistic, p_value = cochrans_q(data)
          
          # 결과 출력
          print('코크란 Q 검정 통계량:', statistic)
          print('p-value:', p_value)

          5. 회귀 분석

          • 선형성 가정: 독립변수와 종속변수와 선형
            1. sns.pariplot 혹은 변수간 산점도를 이용해 확인
            2. sns.heatmap(df.corr()) 로 확인
          • 독립성 가정: 관측치들간에 서로 독립
            • 더빈왓슨(Durbin Watson)통계량
              • $DW = \frac{\sum_{t=2}^{n} (e_t - e_{t-1})^2}{\sum_{t=1}^{n} e_t^2}$
              • $H_0$: 잔차 간에는 자기 상관이 없다.
              • $H_1$: 잔차 간에는 자기 상관이 존재 한다.
              • 해석
                • 0에 가까움: 잔차들 간에 양의 자기 상관이 존재
                • 2에 가까움: 잔차들 간 자기 상관이 없다(독립성 가정 만족)
                • 4에 가까움: 잔차들 간의 음의 자기 상관이 존재
          • 등분산 가정: 잔차의 분산은 모든 수준의 독립변수에 대해서 일정
            1. 잔차 vs 예측값 plot
            2. Breush-Pagan 검정
              • $H_0$: 잔차의 분산이 일정하다(등분산성이 있다)
              • $H_1$: 잔차의 분산이 일정하지 않다.(이분산성이 있다)
          import statsmodels.stats.api as sms
          import statsmodel.api as sm
          model = sm.OLS(y,X).fit
          static , pvalue, f_static , f_pv = sms.het_breuschpagan(model.resid,model.model.exog)
          • 정규성 가정: 잔차항이 정규분포를 따름
            1. QQ플랏
            2. sharpiro-wilk 검정
              • $H_0:$ 정규성을 따른다.
              • $H_1:$ 정규성을 따르지 않는다.
          from scipy.stats import shapiro
          static, pvalue = shapiro(model.resid)
          • 전체 코드
          # 1. 잔차 vs 예측값 : 잔차의 등분산성 가정 
          # 2. 잔차의 히스토그램: 잔차의 정규성 가정
          # 3. QQPLOT: 잔차의 정규성 가정
          # 4. 다중공선성: 독립변수의 독립성 가정
          # 5. 더빈왓슨 검정: 잔차의 독립성 가정
          import statsmodels.api as sm
          import matplotlib.pyplot as plt
          import seaborn as sns 
          from statsmodels.stats.outliers_influence import variance_inflation_factor as VIF
          
          def review_lr(X,y, add_const = True):
              if add_const:
                  X = sm.add_constant(X)
                  model = sm.OLS(y, X).fit()
              else:
                  model = sm.OLS(y, X).fit()
              
          
              residuals = model.resid
              fitted_values = model.fittedvalues
          
              plt.figure(figsize = (10,6))
              #1. 잔차 vs 예측값
              plt.subplot(2,2,1)
              sns.residplot(x = fitted_values, y = residuals, lowess = True)
              plt.title('Residual vs Fitted')
          
              # 2. 잔차의 히스토그램
              plt.subplot(2,2,2)
              sns.histplot(residuals, kde = True)
              plt.title('Residuals Distribution')
          
              # 3. 잔차의 QQplot
              plt.subplot(2,2,3)
              sm.qqplot(residuals, line = '45', fit = True, ax=plt.gca())
              plt.title('QQ plot)')
          
              # 4. 다중공선성
              plt.subplot(2,2,4)
              vif_data = pd.DataFrame()
              vif_data['VIF'] = [VIF(X.values,i) for i in range(X.shape[1])]
              vif_data['Feature'] = X.columns
          
              sns.barplot(x = 'VIF', y = 'Feature', data = vif_data)
              plt.title('VIF')
              plt.show()
          
              # 5. 잔차의 독립성 검정: 더빈왓슨검정
              # -> 기본적으로 sm.OLS(Y,X).summary()에서 제공
              dw_stat = sm.stats.durbin_watson(residuals)
              print(f"Durbin-Watson statistic: {dw_stat:.3f}")
              '''
              0에 가까우면 잔차의 양의 자기 상관성
              2에 가까우면 잔차의 독립성 가정 만족
              4에 가까우면 잔차가 음의 자기 상관성을 가짐
              '''
          
              #6. 잔차의 정규성 검정: # Shapiro-Wilk 검정
              from scipy import stats
              residuals = model.resid
              shapiro_test = stats.shapiro(residuals)
          
              print(f"Shapiro-Wilk Test Statistic: {shapiro_test.statistic:3f}")
              print(f"p-value: {shapiro_test.pvalue:3f}")
          
              # 결과 해석
              if shapiro_test.pvalue > 0.05:
                  print("p-value가 0.05보다 큼: 잔차가 정규분포를 따를 가능성이 높음 (귀무가설을 기각할 수 없음).")
              else:
                  print("p-value가 0.05보다 작음: 잔차가 정규분포를 따르지 않을 가능성이 높음 (귀무가설 기각).")
          		
          		return model.summary()
          y = df['MEDV']
          X = df.drop(columns=['MEDV'])
          review_lr(X,y)

          Durbin-Watson statistic: 1.246
          Shapiro-Wilk Test Statistic: 0.917579
          p-value: 0.000000
          p-value가 0.05보다 작음: 잔차가 정규분포를 따르지 않을 가능성이 높음 (귀무가설 기각).

           

          • 선형회귀 다중공선성 관리 방법
            • 상관관계가 0.7 이상인 것 기준
            • 도메인지식을 바탕으로 중요하다고 생각되는 변수를 유지
            • 다중공선성이 높은 변수들이 많다면 차원 축소 진행
          corr_df = df.drop(columns =['MEDV']).corr().unstack().reset_index()
          corr_df.columns =['col1','col2','corr']
          corr_df =corr_df[corr_df['col1'] != corr_df['col2']]
          	.sort_values('corr').reset_index(drop=True)[::2].reset_index(drop=True)
          display(corr_df)
          
          print('''
          강한 양의 상관관계 및 강한 음의 상관관계를 가지는 컬럼들이 다수 존재한다. 다중공선성이 발생할 여지가 크기에 이를 해결하기 위해 두가지 접근을 진행한다.
          1. vif값을 기준으로 변수를 제거하는 방식
          2. pca를 이용해서 변수간 선형관계를 줄인 축소된 차원으로 분석을 진행
          ''')

          • VIF
            • $VIF_i = \frac{1}{1 - R_i^2}$
            • $R^{2}_{i}$: VIF를 측정하고 싶은 독립변수를 종속변수로 놓고, 나머지 독립변수를 이용해 회귀분석을 진행한 결정계수
            • 즉 결정계수가 90%이 된다는 것은 VIF가 10이 된다는 것
          from statsmodels.stats.outliers_influence import variance_inflation_factor
          
          df_vif = df.drop(columns =['MEDV'])
          vif  = pd.DataFrame()
          vif['VIF Factor'] = [variance_inflation_factor(df_vif.values,i) for i in range(df_vif.shape[1])]
          vif['features'] = df_vif.columns
          display(vif)
          • 방법1) 변수 제거
          • 방법2) 차원축소(PCA)
            • 거리기반의 알고리즘이기 때문에 정규화 필요
            • 전체 변수를 넣어도, 부분변수를 넣어도되나, 전자의 경우 해석력이 많이 손상됌
          from sklearn.decomposition import PCA
          from sklearn.preprocessing import StandardScaler
          
          sc =StandardScaler()
          
          scaled_df = sc.fit_transform(df.drop(columns =['MEDV']))
          pca = PCA(n_components=7)
          pca_df = pca.fit_transform(scaled_df)
          
          X = pca_df
          y = df['MEDV']
          X = sm.add_constant(X)
          
          model = sm.OLS(y,X).fit()
          display(model.summary())
          • 방법3) Lidge/Lasso 회귀
            • Lasso(L1, 절대값):
              • 변수를 선택하고 싶은 경우
              • 일부 변수만 중요하고, 나머지는 무시해도 되는 상황
              • 희소한 데이터셋에서 불필요한 변수를 제거하여 모델의 해석력을 높이고 싶은 경우
            • Ridge(L2, 제곱):
              • 모든 변수가 모델에 중요할 때
              • 다중공선성 문제를 해결하고 과적합을 방지하고 싶은 경우
              • 변수 선택보다는 모델 성능 향상이 더 중요한 경우

          6. 시계열 검정

          • 정상성 검정
          import pandas as pd
          import numpy as np
          from statsmodels.tsa.stattools import adfuller
          
          # ADF 검정 수행
          result = adfuller(data)
          
          print('ADF Statistic:', result[0])
          print('p-value:', result[1])
          • 잔차 분석
          arima_model.plot_diagnostics
          plt.show()
          • 잔차의 자기상관성 확인:Ljung-box 검정
            • $H_0$: 잔차끼리의 자기 상관이 없다
            • $H_1$: 잔차끼리의 자기 상관이 있다
          from statsmodels.stats.diagnostic import acorr_ljungbox
          
          # Ljung-Box 검정 (잔차의 독립성 검정)
          ljung_box_test = acorr_ljungbox(arima_model.resid, lags=[10], return_df=True)
          print(ljung_box_test)
          • 시계열 분해
          from statsmodels.tsa.seasonal import seasonal_decompose
          
          # 시계열 분해 (Additive 모델)
          # period는 데이터의 계절성 주기 설정 (예: 월별 데이터의 경우 12)
          decomposition = seasonal_decompose(data, model='additive', period=12)
          
          # 분해 결과 시각화
          decomposition.plot()
          plt.show()
          • 지수 평활 모형
          from statsmodels.tsa.holtwinters import ExponentialSmoothing
          
          # 단순 지수 평활 모델 적합
          model = ExponentialSmoothing(data, trend=None, seasonal=None)
          model_fit = model.fit(smoothing_level=0.8, optimized=False)
          
          # 예측값 생성
          fitted_values = model_fit.fittedvalues
          
          # 원본 데이터와 예측값 비교
          plt.figure(figsize=(12, 6))
          plt.plot(data, label='원본 데이터')
          plt.plot(fitted_values, label='예측값', color='red')
          plt.legend()
          plt.show()

          6.1. 자기회귀(AR) 모형

          • 현재 값이 과거 값들의 선형 조합으로 표현될 수 있다고 가정
          • 자기상관에 강한 시계열과 추세나 패턴이 지속되는 경향이 있을때 유용(Ex 주식, 기온 데이터)
          • PACF: p 시차 이후 급격히 0에 가까워지며, ACF는 점진적으로 감소
          from statsmodels.tsa.ar_model import AutoReg
          model_ar = AutoReg(data, lags=p).fit()
          forecast = model_ar.forecast(steps=10)

          6.1. PACF

          • 편자기상관함수는 현재 값과 특정 시점이 값의 상관관계를 구하기 위한 함수
          • 시차가 2라면 $y_t$와 $y_{t-2}$의 두 시차를 고려하며 중간 시차 $y_{t-1}$이 영향을 제거
          • 첫 번째 선은 차수 1(lag1)
          • 다음 그래프를 보고 AR(2) 결정

          파란선: 유의성 경계

          6.3.  MA 모형

          • 현재 값이 과거의 오차항(예측 오류)의 선형 조합으로 표현된다고 가정
          • 단기적인 충격이나 이벤트의 반응을 모델링 하는데 유용(주가 실적 발표)
          • PACF: 점진적으로 감소, ACF: q  시차 이후 급격히 0에 가까워짐
          # MA(q) 모델 - ARMA로 구현
          from statsmodels.tsa.arima.model import ARIMA
          model_ma = ARIMA(data, order=(0,0,q)).fit()
          forecast = model_ma.forecast(steps=10)

          6.4. ACF(Auto Correlation Function)

          • 자기 상관 함수는 각 시차(lag)에 대해 현재 값과 과거 값의 상관관계를 계산, 시차 q를 결정
          • 첫 번째 세로 선은 차수 0으로 자기와의 상관관계(항상 1.0)
          • 다음은 MA(3)이 결정되는 ACF 그림

          • ACF, PACF 그리기 
          import matplotlib.pyplot as plt
          from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
          
          # 한 그림에 두 플롯 함께 표시하기
          fig, axes = plt.subplots(2, 1, figsize=(12, 8))
          plot_acf(data, lags=40, alpha=0.05, ax=axes[0])
          axes[0].set_title('ACF')
          plot_pacf(data, lags=40, alpha=0.05, ax=axes[1])
          axes[1].set_title('PACF')
          plt.tight_layout()
          plt.show()

          6.5. ARIMA

          • ARIMA
          from statsmodels.tsa.arima.model import ARIMA
          import matplotlib.pyplot as plt
          
          # ARIMA 모델 적합 (p=1, d=1, q=1 파라미터로 예시)
          model = ARIMA(data, order=(1, 1, 1))
          model_fit = model.fit()
          
          # 예측값 생성
          fitted_values = model_fit.fittedvalues
          
          # 원본 데이터와 예측값 비교
          plt.figure(figsize=(12, 6))
          plt.plot(data, label='원본 데이터')
          plt.plot(fitted_values, label='ARIMA 예측값', color='red')
          plt.legend()
          plt.title('ARIMA 모델 적합 결과')
          plt.show()
          
          # 모델 요약 출력 (선택사항)
          print(model_fit.summary())
          • SARIMA
          from statsmodels.tsa.statespace.sarimax import SARIMAX
          
          # SARIMA 모델 적합 (p, d, q, P, D, Q, m 설정)
          p = 1  # AR 차수
          d = 1  # 차분 차수
          q = 1  # MA 차수
          P = 1  # 계절 AR 차수
          D = 1  # 계절 차분 차수
          Q = 1  # 계절 MA 차수
          m = 12  # 계절 주기 (예: 월별 데이터의 경우 12)
          
          model = SARIMAX(data, order=(p, d, q), seasonal_order=(P, D, Q, m))
          model_fit = model.fit()
          
          print(model_fit.summary())
          
          # 예측
          forecast = model_fit.forecast(steps=12)
          print(forecast)

          6.6 auto ARIMA

          import pmdarima as pm
          from statsmodels.tsa.arima.model import ARIMA
          
          data = df.copy()
          # 자동으로 최적의 ARIMA 모델 찾기
          auto_model = pm.auto_arima(data, 
                                    start_p=0, start_q=0,
                                    max_p=5, max_q=5,
                                    d=None, max_d=2,
                                    seasonal=False,
                                    trace=True,
                                    error_action='ignore',
                                    suppress_warnings=True,
                                    stepwise=True)
          
          print(f"최적의 ARIMA 모델 파라미터: {auto_model.order}")
          
          # 최적 모델 적합
          best_model = ARIMA(data, order=auto_model.order)
          best_model_fit = best_model.fit()
          
          # 예측값 생성
          best_fitted_values = best_model_fit.fittedvalues
          
          # 원본 데이터와 예측값 비교
          plt.figure(figsize=(12, 6))
          plt.plot(data, label='원본 데이터')
          plt.plot(best_fitted_values, label=f'최적 ARIMA{auto_model.order} 예측값', color='red')
          plt.legend()
          plt.title('optimization result')
          
          #진단코드
          model.plot_diagnostics(figsize=(12, 8))
          plt.tight_layout()
          plt.show()

           

          • VAR 모형
          from statsmodels.tsa.vector_ar.var_model import VAR
          
          # 데이터 분할 (학습 및 테스트 데이터)
          train_data = df[:-10]
          test_data = df[-10:]
          
          # VAR 모델 적합
          model = VAR(train_data)
          model_fit = model.fit(maxlags=15, ic='aic')
          
          print(model_fit.summary())
          
          # 예측
          lag_order = model_fit.k_ar
          forecast_input = train_data.values[-lag_order:]
          forecast = model_fit.forecast(y=forecast_input, steps=10)
          
          # 예측 결과를 데이터프레임으로 변환
          forecast_df = pd.DataFrame(forecast, index=test_data.index, columns=df.columns)
          
          print(forecast_df)

          7. 문서기록

          • 2025.04.18. 최초 작성
          • 2025.04.22. 머신러닝 항목 분리
          • 2025.04.25. stastsmodel 디렉토리 등 추가

           

           

          이번 장에는 batch processing이라고 불리우는 일괄처리 방법에 대해서 기술한다. 분산 저장 시스템에서 함께 등장하는 맵리듀스 방식과 과거 유닉스 방식의 유사점을 비교하면서 설명하는 장이다.


          신규개념

          개념 설명
          데몬 프로세스 멀티태스킹 운영 체재에서 사용자가 직접 제어하지 않고, 백그라운드에서 작업을 하는 프로그램
          NAS Network Attached Storage, 여러 사용자가 TCP/IP 네트워크로 파일을 저장하고 공유할 수 있는 중앙 집중식 파일 서버
          SAN Storage Area Network, 서버, 스토리지 시스템, 소프트웨어 및 서비스를 결합하여 특정 환경에 맞게 조정된 전용 네트워크
          RAID 여러개의 디스크를 묶어 하나의 디스크처럼 사용하는 기술
          맵리듀스 워크플로 Work Flow, 맵 리듀스 작업을 관리하는 워크 플로우 시스템

           

          1. 요청응답 방식의 시스템 3가지

          • 서비스(온라인 시스템)

          서비스는 클라이언트로부터 요청이나 지시가 올 때까지 기다린다. 요청이 들어오면 가능한 빨리 요청을 처리하며 응답시간이 그 지표가된다.

          • 일괄 처리 시스템(오프라인 시스템)

          매우 큰 입력 데이터를 받아 처리하는 작업을 수행하고 결과 데이터를 생산한다. 하루에 한번 수행과 같이 반복적인 일정으로 수행하며, 성능지표는 처리량이다.

          • 스트림 처리 시스템(준실시간 시스템)

          스트림 처리는 입력 이벤트가 발생한 후 바로 작동한다. 일괄 처리 시스템보다는 지연시간이 낮지만 기반은 같다.

          2. 일괄 처리 시스템의 예시

          맵 리듀스는 검색 엔진에서 사용할 색인을 위하여 처음 사용됨.  색인은 용어 사전 파일로 효율적으로 특정 키워드를 조회해 키워드가 포함된 문서 ID의 모록을 찾아 검색 결과를 관련성 순으로 순위를 매기는 등의 일을 함.  머신러닝 시스템(스팸 필터, 이상 검출, 이미지 인식)을 구축하거나 추천시스템이 그 예

           

          3. 유닉스의 철학과 단점

          1. 각 프로그램이 한가지 일만 하도록 작성하라.
          2. 모든 프로그램의 출력은 아직 알려지지 않은 다른 프로그램의 입력으로 쓰일 수 있다고 생각하라.
          3. 소프트웨어를 빠르게 써볼 수 있게하고 구축하라.
          4. 프로그래밍 작업을 줄이려면 미숙한 도움보다는 도구를 사용하라.

          유닉스 도구는 입력 파일에 손상을 주지 않으며, 어느 시점이든 less로 보내 원하는 형태의 출력이 확인 가능하여 디버깅이 유용하고, 특정 파이프라인 단계의 출력에 파일을 쓰고 다음 단계의 입력으로 사용가능하여 전체 파이프라인 재시작이 필요없는 장점 등이 있다. 하지만 이런 장점에도 불구하고 단일장비에서만 실행된다는 점 때문에 하둡 같은 도구의 필요성이 생겼다.

           

          4. 맵리듀스와 분산파일 시스템

          하둡 맵리듀스 구현에서 파일 시스템은 HDFS(Hadoop Distributed File System)이라고 하는데 GFS(Google File System)를 재구현한 오픈소스이다. HDFS는 비공유 원칙을 기반으로 하기 때문에 NAS, SAN의 아키텍쳐와 다른 점이다. 이때 2가지 콜백 함수로 구현되어있다.

          • 매퍼(Mapper): 모든 입력 레코드마다 한번씩 실행되며, 입력 레코드로 부터 키와 값을 추출하는 작업이다. 
          • 리듀서(Reducer): 맵리듀스 프레임워크는 매퍼가 생산한 키-값 쌍을 받아 같은 키를 가지느 레코드를 모으고 해당 값의 집합을 반복해 리듀서 함수를 호출한다. 리듀서는 이때 출력 레코드를 생산한다.

          https://blog.naver.com/bellepoque7/222984401079

           

          [개발] Hadoop vs Spark 등장 배경과 차이

          1. 데이터 분산 처리 시스템의 등장 - Hadoop Hadoop과 Spark를 알기전에 등장배경을 알면 이해하기 쉽...

          blog.naver.com

          • 조인 알고리즘
            • 정렬 병합 조인
            • 브로드캐스트 해시 조인
            • 파티션 해시 조인

          5. 분산 일괄 처리 프레임워크가 해결해야할 문제

          • 파티셔닝

          맵리듀스에서 맵퍼는 입력 파일 블록에 따라서 파티셔닝됨. 매퍼의 출력은 재파티셔닝해 정렬하고 리듀셔 파이션으로 병합한다. 이 과정의 목적은 모든 데이터(같은 키 값을 가지는 모든 레코드)를 같은 장소로 가져오는 것이다. 맵리듀스 이후 데이터플로 엔진은 필요한 경우가 아니라면 정렬하지 않는다. 그렇지 않다면 대체로 비슷한 방법으로 파티셔닝 한다.

          • 내결함성

          맵리듀스는 빈번히 디스크에 기록한다. 디스크에 기록하면 개별 태스크가 실패하더라도 전체 작업을 재수행하지 않고 쉽게 복구할 수 있다. 하지만 작업이 실패하지 않는 경우 수행 시간이 느려지는 것은 감수해야한다. 데이터플로 엔진은 중간 상태를 최대한 구체화하지 않고 대신 메모리 상태를 유지한다. 이것은 특정 노드가 실패한다면 재계산 양이 늘어난다는 것을 의미한다. 결정적 연산자를 사용하면 재계산이 필요한 데이터의 양을 줄일 수 있다.

           

          6. 참고문헌

          글을 쓰는 것이 중요성을 신입 때는 몰랐습니다. 내 기억력은 충분히 좋을줄 알았고 대화로 모든 것을 해결할 수 있을 것 같았습니다. 하지만 기억은 휘발되고 일의 절차와 내용은 문서로 남겨야만 의미가 있다는 것을 알게 되었고 글쓰기 모임은 이런 저의 갈증을 해결할 수 있는 선택지였습니다. 2023년 봄, 글을 쓰지 않는 사람은 사기꾼 이라는 전 팀장님의 말이 떠올라 글또 신청서를 넣고 8, 9 10기까지 진행했습니다. 이번 글은 지난 3년간 개발자 글쓰기 모임을 하면서 했던 활동을 회고하여 좋았던 점과 나의 삶의 방향이 어떻게 바뀌었는지 한번 작성해보고자 합니다.


          1.  참여자로서의 8기

          • 왜 하필 글쓰기 모임인가?

          왜 하필 개발자글쓰기 모임일까? 저는 본래 공학에 대한 관심이 매우 많습니다. 하드웨어와 소프트웨어의 원리, 물체의 이치 등에 관심이 많은 사람이였고 자연스럽게 데이터 분야를 접하면서 이를 기반으로하는 개발 지식도 흡수하길 좋아했습니다. 또한 글쓰기의 중요성은 서두에 기술한 바있지만, 꽤나 생산적인 활동이고 꾸준히 내 지식을 축적해 나간다는 점에서 마음에 들었습니다. 재밌게도 이 기술 블로그의 출발은 대학교 시절 실험 연구 보고서를 아카이빙 하기 위한 목적으로 시작되었습니다.(실제로 화학생명공학 실험 키워드로 여전히 유입이 존재한다..) 

          • 운영진을 만나다.

          2주마다 글쓰는 것이 대단한 어떤 결과를 만들어준다고는 기대하기 어려울 수 있습니다. 하지만 인생은 적분이라고 작은 변화들이 모여 지금의 나를 크게 변화시켰던 것 같습니다. 특히 IT 와 데이터 과학분야는 변화의 발걸음이 커서 그때 마다 적절한 자료나 레퍼런스를 아카이빙하는 것이 꽤나 도움이 됩니다. 이렇게 조금씩 글을 작성해 나갈때 쯔음 자체 데이터 반상회 모임에서 운영 담당에 지원했습니다. 그렇게 만난 글또의 운영진 정희님께 왜 운영진을 하냐는 질문을 해보았습니다.

          성윤님을 옆에서 본다는 것. 그 이상의 이유가 필요할까요?

          단순하지만 명확했습니다. 사실 성윤님이라는 한 명의 가치 뿐 아니라, 커뮤니티의 가치에 대해서 생각해보았습니다. 데이터 직군은 신생된지 얼마 되지 않아서 어떤 북극성 지표가 필요했으며, 다른사람이 어떻게 하고 있나라는 메타인지를 통해서 스스로 발전하는 변화가 필요한직군입니다. 그런 점에서 옆에서 함께 많이 배울 수 있는 사람과 환경이라고 생각 되었고 곰곰히 생각한 끝에 9기에는 운영진으로 지원하기로 결심하게 되었습니다.
           

          2. 운영진으로서의 9기

          • Activation 크루 지원

          9기 운영진을 준비하기 위해 내가 무엇을 할 수 있는가 뾰족하게 고민했습니다. 뒤돌아보니 제가 했던 다양한 활동들과 장으로서 일했던 경험이 소규모의 동호회를 유지하고 발전시키는데 장점이 있음을 파악했습니다. 그래서 저는 한장짜리 제안서를 함께 제출했습니다.

          사실 이런 아이디어의 근간은 ChatGPT 가 활황이던 23년의 시대적 배경과 함께합니다. 8기 쯤하여 AI 코딩 어시스트 기술이 발전하고 대부분의 IT직군들이 일자리를 잃지 않을까하는 두려움에 다들 만나서 ChatGPT과 일자리에 대한 논의가 활발했습니다. 또한, 이직이나 조직생활 나아가서 밥먹고 사는 문제까지 다들 모여서 고민을을 나누는 시간들이 빈번했습니다. 저는 이런 자유로운 토론과 대화가 글또의 가치를 사용자들에게 주는 경험이라고 생각했고, 이를 좀 더 Global하게 키우는 것이 장기적으로 사용자의 리텐션 향상에 도움이 될 것이라는 가설을 세웠습니다. 실제로 지난 8기에서 오프라인으로 만난 참여자 6명은 글제출을 9.1번으로 평균 이상의 활동량을 보임을 확인했습니다. 이러한 경험을 바탕으로 다음 지원서를 작성했습니다.
           

          9기 운영진 지원 형식

           

          • 글또 하우스 2회 개최

          위 고민을 담아 9기에서 글또하우스를 2회 개최했습니다. 지난 서비스 "클럽하우스" 본따 소수의 스피커와 다수의 청중으로 이루어진 고민상담소로 기획했습니다. 실제로 좋은 반응을 보였고, 1회에는 53명이 지원할 들어올정도로 많은 호응을 받았습니다. 이때 몇가지 회고를 하자면

          • Zoom으로 진행되어 소수의 스피커로 집중되지만 채팅 참여가 활발해서 다수의 의견을 들을 수 있는 장점
          • 1차에는 인원이 많아서 정해진 시간에 쫓기는 느낌이 들어서 아쉬웠고, 참여자가 적은 2차는 여유로웠음
          • 진행자가 상담을 하는 상황이 자주 벌어지는데 모르는 분야에 대해서 코멘트 다는게 어려운 단점
          • 정해진 시간 내 탐색 -> 수렴 -> 결과 등의 진행방식의 필요성
          • 상담자들의 도전지향형/안정지향형이 해법이 섞여있어서 좋았음

          정도로 기억이 납니다. 이런 상담을 하는 와중에 진정 내가 이렇게 사람들에게 조언을 할 수 있는 똑똑한 혹은 대단한 사람인가에 대한 회의가 커졌고, 다음에는 pivot하여 다른 방법으로 기여해보자는 방향을 설정하게 됩니다.
           

          3. 10기의 시작

          • CRM 크루로 pivot과 글또 내 활동들

          Activation 크루는 재밌었고 좋았지만 좀 더 데이터 기반하여서 보길 원했습니다. 그래서 CRM 크루로 일단 이전을 신청했습니다. CRM의 사전적 의미는 고객관계관리이지만, 글또에서는 데이터를 가장 가까이 보고 관심있어하는분들이 많았습니다. 9기에 보냈던, 슬랙기반의 데이터를 활용한 글또 내 활동요약이 핵심 프로젝트 중 하나였고 역시 10기도 추진하게 되었습니다.

          https://geultto.github.io/blog/bokjumoney-message/

           

          글또 10기의 복주머니, 639명의 참여율을 높인 개인화 메시지 개발기

           

          geultto.github.io

           
          특히 10기에는 운영진 역할에 국한하지 않고 적극적으로 스터디와 4채널 활동을 많이 했던 기억이 납니다. 작은 호기심을 가지고 짧은 분석을 해보는 월간 데이터 노트, 테니스모임, 다진마늘 모임, 마인크래프트 서버 운영기, LLM 사이드프로젝트까지 오프라인 모임이 제약되는 상황에서도 다양한 활동을 할 수 있어서 매우 좋았습니다. 각 활동들에 대한 후기는 나중에 한번 정리를... 특히 월간 데이터 노트는 본격적으로 시스템화 해서 월별 루틴으로 올릴 예정이라서 나름 기대하고 있습니다.

          • 지금까지 작성한 글 정리 - 프로젝트
          번호 글제목 분류
          1 서버리스 환경 구상하기 AWS Lambda, RDS, ec2 프로젝트
          2 사이드프로젝트  중간 회고 & 서버 제공 서비스 비교하기(S3,Blob,Firebase) 프로젝트
          3 데이터 파이프라인 자동화 구축기 with 농수산물데이터, GCP, crontab 프로젝트
          4 제3회 기상-AI 부스트캠프 해커톤 후기 프로젝트
          5 시계열 Prophet 모형과 하이퍼파라미터 뜯어보기 with 주가데이터 프로젝트
          6 Google bigquery에 데이터를 적재하고 태블로로 데이터 가져오기 프로젝트
          7 연간 가계부 관리하기 (feat. 뱅크샐러드) 프로젝트
          8 슬랙 봇으로 커뮤너티 활성화하기(with CRM 메시지, 마인크래프트) 프로젝트

          처음에는 책과 같은 레퍼런스에서 얻을 수 있는 프로젝트가 다수인데 갈수록 내 호기심과 아이디어로 구현하가는 과정이 흥미롭습니다. 특히 8번 슬랙 봇 커뮤니티 활성하기는 순수하게 호기심과 재미로 시작한 것인데, 글또 내 큐레이션 선정도 되고 공감도 가장 높은 글이라서 작성하던 시기에도 후에도 보람찼던 경험이 있습니다. 역시 사람은 재밌는거 해야해?

          • 지금까지 작성한 글 정리 - 스터디 활동
          번호 글제목 분류
          1 분석알고리즘(머신러닝, 딥러닝) 간단 정리해보기 1편 스터디
          2 추천시스템 이론 정리하기 스터디
          3 추천시스템 실습 - 아이템기반 협업 필터링 스터디
          4 신뢰구간의 2가지 계산방법: t분포와 부트스트래핑 스터디
          5 기호주의와 연결주의로 알아보는 AI 발전 스터디
          6 LLM 관련 모듈 동향 살펴보기- OPEN AI, Langchain , Pandas AI 스터디
          7 왜 프로덕트 팀은 A/B테스트를 사랑할까? (feat 인과추론) 스터디
          8 DDIA Chapter 2: 데이터 모델과 질의 언어 스터디
          9 DDIA Chapter 3: 저장소와 검색 스터디
          10 DDIA Chapter 6: 파티셔닝 스터디

          사실 회사다닐 때는 책이나 스터디를 등한시 해왔습니다만, 갈수록 AI툴들이 수단을 고도화하고 기존의 툴을 대체하는 시대에 결국 살아남는 가치는 그 원리를 파악하고 잘 이용한는 방법이라는 결론에 도달하게 되었습니다. 에이 무슨 6년차에 스터디야 라고 생각했던 저는 다시 활자를 읽기 시작했고, 기존에 DA,DS에만 국한하던 스터디에서 CS까지 확장하게 되었습니다. 특히 데이터중심어플리케이션 설계(DDIA)는 백엔드 개발자들에게는 필독서로 추앙받는데, 함께 스터디하면서 전공을 다시듣는 그런 재미난 경험이었습니다. 

          • 지금까지 작성한 글 정리 - 일상생각, idea
          번호 글제목 분류
          1 나의 올해의 컨셉은 분발자로 정했다 idea
          2 데이터분석가의 끝은 어디일까? idea
          3 네? 데분 멘토링왔는데 알고리즘 2달 알려주라고요? idea
          4 글또 8기 활동과 개인 상반기를 정리하며 idea
          5 프리랜서로 산다는 것 2023 idea
          6 당신은 절대 글을 잘 쓸 수 없다 idea
          7 알고리즘은 데이터 분석가에게 필요할까? idea
          8 Analytics Engineer 직무 뜯어보기 idea
          9 데이터 분석을 위한 준비하고 배울 것들 idea
          10 글또 10기를 시작하며 다짐 글 idea
          11 일의 기쁨과 슬픔 idea
          12 치앙마이 워케이션 후기(업무장소, 음식, 휴식 추천) idea
          13 1인 사무실 한 달 후기 (ft 범계비욘드워크) idea

          글또의 대부분이 그러하든 나의 역할과 방향성에 고민을 하는 글들이 다수 발견됩니다. ㅋㅋ 생각이 많은 1인... 그런데 활자로 적는것 만큼 또 정리가 잘되는게 없습니다. 초반에는 아 뭐해먹고 살지 데이터분석은 뭘까 라는 등등에 대한 방향성으로 글을 써왔는데 점차 나의 목표가 뚜렷해지니 주제가 더 명확해지는 흐름이 보이네요. 이런 회고도 참 재밌습니다!
           

          4. 갈무리 - 정량적 성과보기

          • 블로그 성과 보기

          직무에 대한 고민 글과 프로젝트, 스터디를 하면서 올라간 내면의 질적인 향상도 있지만 명시적으로 오른 지표는 방문자 수와 조회 수입니다. 월 천명, 조회수 2천건의 블로그로 성장했습니다. 데이터 분야 입문할 때만해도 Ratsgo's Blog어쩐지 오늘은 같은 데이터 블로그 글들로 공부를 많이했는데 어쩌면 저도 그런 블로그가 될 수 있을까 싶습니다 ㅋ_ㅋ

          두번째로는 노출과 CTR입니다. 점점 글을 작성하면서 알게모르게 블로그의 랭크가 높아졌고, 메타 태그 설정, 구글 서치콘솔까지 연결하니 가장 인기 있는 글은 13%의 CTR를 달성하기도 했습니다.

          블로그 전체의 CTR

          물론 일반 웹서비스와 다르게, 기술블로그 특성상 적절한 주제라고 생각하면 클릭으로 전환하는 비용이 크지 않기 때문에 큰 값은 아니지만 그래도 꽤나 전환율이 좋은 글들이 많습니다. 특히 통계책으로 검색하면  일반 서점 사이트와 교수님이 쓰신 글! 까지 제치고 상위노출하는 쾌거를 이루어서 기분이 좋았네요.

          (좌) 상단노출되는 내 글 (우) 인기목록 Top10

          글또에서 양질의 컨텐츠를 얻으면서 배웠던 지식과 커피챗을 하면서 얻었던 아이디어등이 결합된 결과라고 생각됩니다. 물론 글또 뿐 아니라 다양한 글을 완성도에 집착하지 않고 일단 하자! 라는 결심으로 차곡히 쌓아온 결실이기도 하구요. 이런 환경을 만들어주신 성윤님께 무한 감사!

          • 그래서 앞으로는?

          아마 글또가 마무리되니 압박에 시달려서 2주마다 글을 쓰진 않을 수도 있을 것 같습니다. 하지만 글을 쓰는 것에 대한 가치는 더 커졌고 오픈소스처럼 나의 지식을 나누어주는 것에 대한 기쁨도 동반되고 있습니다. 문득 글을 정리하다가 10기의 목표를 "히언님의 친절한 데이터사이언티스트 되기 강좌" 과 같이 동영상보다 좋은 글을 남기자 였는데 이 목표에 지속해서 다가가려 합니다.  앞으로는 방통대를 입학해서 공부하고 추후에 대학원 고려도 해볼 수 있을 것 같은데 그때마다 떠오르는 생각과 기술들을 한번씩 적어보겠습니다. 그럼 20000

          앞으로 또 좋은 수영장을 찾아서 헤엄치고 발전하는 나를 위해서 한잔 🍻해 

          단일 시스템에서 다중시스템으로 확장하는 것은 단순히 공수의 N배가 아니라는 것을 느끼는 단원. 초반에는 네트워크장애에 대한 기술을 서술하나가는데 그 이후는 그만 정신이 혼미해지는 단원입니다. 그야말로 골칫거리군요. 후반은 읽다가 잠시 정신을 잃어서 이만.. 




          신규개념

          개념 설명
          타임아웃(Timeout) 서버로 요청을 보냈지만 일정 시간동안 답변을 받지 못할 때 발생시키는 오류
          TCP 전송 제어 프로토콜(Transmission Control Protocol), 애플리케이션 사이에서 안전하게 데이터를 통신하는 규약. IP(Internet Protocol)와 엮어서 TCP/IP라고 불리기도 하며, 통신속도를 높이면서도 안전성, 신뢰성을 보장할 수 있는 규약
          UDP 사용자 데이터그램 프로토콜 (Universial Datagram Protocol). TCP처럼 네트워크  프로토콜이지만 비연결형 프로토콜이라느 차이점.
          이더넷 다수의 시스템이 랜선 및 통신포트에 연결되어 통신이 가능한 네트워크 구조(인터넷 ㄴㄴ)
          패킷(Packet) 네트워크에서 주고받을 때 사용되는 데이터 조각들
          동기식 통신 Synchronous 동시에 일어남. Request를 보내면 얼마나 시간이 걸리든 그 자리에서 Resonse를 받음
          비동기식 통신 Asynchronous, Request 보내도 Response를 언제 받아도 상관 없다. 할일이 끝난 후 Callback을 통해 완료를 알림

           

          1. 비공유 시스템의 오류 관리 방법: 타임 아웃

           (New) 분산 시스템은 비공유시스템. 즉 네트워크로 연결된 다수의 장비이다. 네트워크는 장비들이 통신하는 유일한 수단이라는 특징을 가지고 있다. 인터넷과 이더넷은 비동기 패킷 네트워크(asynchronouse packet network)다. 이런 종류의 네트워크에서는 다른 노드로 메시지(패킷)을 보낼 수 있지만 네트워크는 메시지가 언제 도착할지 혹은 머세지가 도착하기는 할지 보장하지 않는다. 여기서 패킷이란 네트워크에서 주고받는 데이터의 조각이다. 

          약간 mRNA 닮은거는 기분탓인가?

          패킷의 기본 구조. 익숙한 단어가 보인다.

          요청을 보내고 응답을 기다릴 때 여러가지 오류의 상황이 발생할 수 있다.

          1. 요청이 손실되었을 수 있다.(누군가 네트워크 케이블을 뽑았을 수도)
          2. 요청이 큐에서 대기하다가 나중에 전송될 수 있다.(네트워크나 수신자에 과부화가 걸렸을 수 도 있다.)
          3. 원격 노드에 장애가 생겼을 수 있다.(죽었거나 전원이 나갔을 수 있다.)
          4. 원격 노드가 일시적으로 응답하기를 멈췄지만 나중에는 다시 응답하기 시작할 수 있다.
          5. 원격 노드가 요청을 처리했지만 응답이 네트워크에서 손실됐을 수 있다.
          6. 원격 노드가 요청을 처리했지만 응답이 지연되다가 나중에 전솔될 수 있다.(네트워크가 요청을 보낸 장비가 과부하가 걸렸을 수 있다.)

          (a) 요청의 손실 1번 사례, (b) 원격 노드 다운 3번 사례, (c) 응답이 손실 5번 사례

          이 문맥을 읽으면서 외계 탐사행성을 위해 발사된 보이저1호의 상황이 상상이 되었다. 응답할 것이라고 기대하고 보내는 전파가 돌아오지 않는 상황이 네트워크 오류와 비슷한 느낌이랄까?
          출처: https://www.thedailypost.kr/news/articleView.html?idxno=104231

          우리의 요청을 오기까지 그럼 무제한 대기해야할까? 그렇지 않다. 발송이 잘못되었는지, 처리는 되었는지 어느 부분에서 장애가 일어난지 모르니 타임아웃을 이용해서 관리한다고 책은 기술한다. 토스페이먼츠 API Read Timeout은 30-60초로 설정한다고 한다.  이는 경험적인 수치인듯?

          이런 문제를 다루는 흔한 방법은 타임아웃이다. 얼마간의 시간이 지나면 응답 대기를 멈추고 응답이 도착하지 않는다고(장애라고) 가정한다. 그렇다면 타임아웃은 얼마나 길어야할까? 타임아웃이 길면 노드가 죽었다고 선언될 때까지 기다리는 시간이 길어진다. 그 시간동안 사용자들은 기다리거나 오류 메시지를 보아야한다. 반면 타임아웃이 짧으면 결함을 빨리 발견하지만 노드가 일시적으로 느려졌을 뿐 결함이 아닌데도 죽었다고 선언할 확률이 높아진다. (뭐예요 나 아직 살아있어요) 단순히 오해에 그치는게 아니라 노드가 죽었다고 판단되면 그 역할이 다른 노드로 전달되는 내결함성으로 인하여 연쇄장애의 원인이 될 수 있다. 

          이때 인터넷 전화(VoIP)는 지연시간이 민감하기 때문에 TCP보다는 UDP를 선택하는 사례에 대해서 기술한다. UDP는 흐름제어를 따로 하지않고 속도가 빠르며 잃어버린 패킷에 대한 부분을 사람 계층에서 재시도 한다는 표현이 재밌었다. 대표적인 VoIP 서비스인 디스코드가 UDP 통신을 선택하고 있다.

           

          2. 패킷이 유실되지 않는 견고한 네트워크는 어때?

          (New)  네트워크에서 패킷이 손실될 수 있다는 점이 단점으로 명확해지는데 그럼 견고한 네트워크는 어디 없을까? 이를 고정회선 네트워크의 예시를 가져온다. 전화 네트워크는 극단적인 신뢰성을 가져서 음성이 지연되거나 통화가 유실되는 매우 드물다. (고양이가 전화선을 뜯지 않는다면 말이지)

          이러한 견고함을 유지할 수 있는 이유는 통화를 하는 두 명사이에 있는 전체 보장된 대역폭이 할당된다. 언제까지? 통화가 명시적으로 끝날때까지. 이런 네트워크는 동기식으로 큐 대기가 없으므로 종단 지연시간의 최대치가 고정되어 있다. (제한 있는 지연) 따라서 특정 시간의 간격이 지나도 응답이 오지 않는다면 명백한 통신실패임이 확실하다. 

          반면 TCP연결은 가용한 네트워크 폭을 변화하면서 사용한다. 그러니까 연결이 없다면 어떤 대역폭도 사용하지 않는다. 연결이 있다면 기약없는 지연이 발생할 수 없는 구조이다. 그저 빨리 완료되길 기도하는 메타이다. 

          3. 출처

          (Amendment)

          사이드프로젝트 혹은 사내 작은 프로젝트를 하기 위한 클라우드 서비스별 크레딧을 정리해보았습니다. 하기 내용은 정확하지 않을 수 있으며, 해당 서비스의 공식홈페이지를 참고하시는 것을 추천드립니다. 


          문서버전: 2025-03-24 최초 등록

          일반 사용자

          • NCP 10만원, 3개월
          접근성이 쉬움 + Naver API 사용하기도 좋음
          • GCP 300 달러 크레딧, 3개월
          GCP는 Computing resource 하나 조건무관 제공 (Disk 30GiB)
          • AWS 프리티어 12개월 무료(쿠폰적용시 100달러)
          GCP와 달리 1년 지나면 무료 해지인듯!
          • Azure 200 달러
          후발 주자여서 그런지 지원 정책이 올라오고 있는 중

           

          사업자/스타트업

          개인의견:

          사업자 & 스타트업 신청은 굳이라고 생각하실 수도 있지만, 개인사업자의 경우 국내 홈택스에 쉽게 사업자를 낼 수 있습니다. 다만 청년창업소득세면제와 같은 쿠폰을 써버리는게 아깝다고 사업자를 내는걸 꺼려할 수도 있지만 사실 뭘 안하는것보다 일단 하는게 좋다는 생각입니다.  법인사업자의 경우 절차와 신고 등이 부가적인 것이기 때문에 굳이 추천 드리진 않습니다. 

           

           

          + Recent posts