노션은 여러 사람들이 사용하는 협업 툴로 데이터베이스 기능이 있습니다. 이번 글은 멘토링과 같은  다수의 인원의 일정을 구글캘린더에 동기화 하는 방법을 기술합니다. 이렇게 하면 매번 노션페이지에 들어가서 일정을 확인하고 내 캘린더에 기록할 번거로움이 사라집니다. 


목차

    1. 요구사항 정의

    저는 부업으로 멘토링을 하고 있습니다. 다수의 인원이 저에게 멘토링을 신청하는데, 기존에는 슬랙에 멘토링 가능시간을 올리면 답글로 신청하는 방식이였습니다. 하지만 여러 채널에서 멘토링 신청을 받는 상황이 와서 같은 시간에 약속을 잡는 상황이 발생할 수 도 있었습니다. 또한, 이런 멘토링 신청내용을 저의 구글 캘린더에 담아서 한번에 일정을 확인하고 싶어졌습니다. 

    기본적으로 Make라는 자동화 툴에서는 노션의 데이터베이스에 상호작용할 수 있는 인터페이스를 제공합니다. 데이터베이스 조회, 생성, 추가, 업데이트 등의 옵션을 제공합니다.

    최종적으로 완성시킬 Flow는 다음과 같습니다.

    노션 데이터베이스 -> Make -> 구글 / 노션 캘린더 기입

    2. 노션데이터베이스 기입 -> Make

    노션에 캘린더 형식으로 데이터베이스를 만들고, 속성으로 "멘티 이름"과 "날짜 시각"을 정확하게 기입하도록 유도합니다. 이 속성을 추후 Make툴의 파라미터로 받아서 구글 캘린더에 작성됩니다. 

    달력 데이터베이스
    신청 페이지

     

    2. Make 세부사항

    Make에서 Notion 홈페이지에 대한 권한을 줄 수 있습니다. 거기서 특정 데이터베이스 이름을 기준으로 데이터를 관찰할 수 있고, 새로운 이벤트가 열리면 이를 trigger 삼아 구글 캘린더에 전송하는 시스템입니다.

    • Connection: 연결할 노션 계정과 페이지
    • Watch Database Item:  관찰할 기준
    • Database ID: 데이터베이스 식별자, 노션 페이지를 지정해주면 자동으로 연결 가능한 DB가 확인됨
    • Limit: 1번 실행에 추가할 수

     

    • Connection: 연결할 구글 캘린더 계정
    • Create an Event: 간단하게(Quickly), 자세하게(In Detail)
    • Calender ID: 캘린더 이름
    • Event Name:  캘린더에 적을 제목으로, 멘티 이름 속성을 Notion에 받아와 추가했습니다( Ex [1on1] 홍길동)
    • Start Date: 노션에 기입한 날짜&시각을 기입, 노션 DB와 자료형 일치
    • Duration: 추가할 시간

    그 밖에도 노션에서 가져오는 정보들이 변수명으로 적혀있어서 필요에 따라 선택하여 넣을 수 있습니다.

    3. 시연

    왼쪽 하단에보면 매 15분 마다 Tigger가 작동합니다. 노션 데이터베이스를 관찰하는 빈도입니다.

    왼쪽 하단에 Run Once를 눌러서 즉시 실행을 시킬 수도 있으며,  트리거의 조건에 따라 기입했던 노션 데이터베이스의 정보를 받아온 것을 확인할 수 있습니다.  Propeties Value의 멘티이름과 날짜시각이 잘 들어온 것을 볼 수 있습니다.

    이를 구글 캘린더에 동기화되고 다시 노션에 자동으로 동기화가 된 모습입니다.

     

    Last Updated 2025.05.21.

    지난 통계학/데이터사이언스 책 추천에 이어서 엔지니어링 관련 책 추천을 해보려 합니다. 데이터 사이언스에서 CS, 백엔드, 프론트, 네트워크, 데이터 모델링 등 필수적이진 않지만 알면 알수록 본인의 성과와 확장성이 높아질 것입니다. 참고로 이전 책 추천은 다음 링크를 참고하세요.

    https://snowgot.tistory.com/135

     

    DataScience 책 추천(교양, 통계, 데이터과학, 머신러닝, 프로그래밍 등 )

    Last Updated 2025.02.10. 2018년 이후 데이터과학업계에 몸 담으면서 유용하게 공부했던 책과 컨텐츠 혹은 추천받는 책들을 모았습니다. 하지만 명심하세요 자신에게 잘 맞는 책은 지금 당장 도서관

    snowgot.tistory.com


    목차

    1. 엔지니어링 일반

    요즘은 개발이 추상화되고 AI의 지원으로 개발과 분석이 쉬워졌지만 그럼에도 불구하고 low level의 이해는 컴퓨터활용의 능력을 이끌어 올려줍니다.

    https://snowgot.tistory.com/192

     

    컴퓨터밑바닥: Chapter 1 프로그램 언어부터 실행까지, 이렇게 진행된다.

    데이터 중심 어플리케이션 설계에 이어 컴퓨터 밑바닥의 비밀이라는 책을 스터디 합니다. 1장은 프로그래밍 언어가 작동하는 기본을 설명하는 단원으로 컴파일러와 링커 그리고 추상화에에 대

    snowgot.tistory.com

    보통 백엔드 개발자들에게 필독서로 권장되지만 난이도가 낮은 편은 아닙니다. 하지만 데이터 관련 직군에게 설계의 관점으로 일부 챕터를 읽는 것을 권장할만큼 잘 쓰여진 책이라고 생각됩니다. 저는 3장 저장소에 대한 단원이 가장 좋았습니다.

    https://snowgot.tistory.com/173

     

    DDIA Chapter 3: 저장소와 검색

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

    snowgot.tistory.com

     

    2. 데이터 모델링

    갑자기 왠 자격증이냐 하겠지만, 1단원이 데이터 모델링에 대한 개념으로 입문자일 때 읽었던 경험보다 3년차 일때 읽었던 경험이 훨씬 더 좋았습니다. 겸사겸사 공부해보시는 것 추천합니다.

    데이터 엔지니어링 커뮤너티에서 추천 받은 책으로 40년 내공으로 쓰여진 지은이의 경험이 녹아 있는 책이라고 합니다.

    비전공자도 이해할 수 있는 DB 설계 강의를 만들었다고 한다.(Youtube)

     

    3. 기록

    • 2025.05.21. 최초 작성

    이번에 시간을 공유하는 삶이라는 타임 트래킹 소모임에 참여하게 되었습니다. 일단위로 시간을 기록하고 일주일 회고를 12주간 반복하는 모임입니다. 이 모임에서 아이디어를 얻어 나의 업무기록을 자동화하는 여정에 대해서 Make 툴을 중심으로 작성해보겠습니다.


    목차

    1. 요구사항 정의

    시간단위로 업무를 기록하는 모임이기 때문에 최대한 시간을 기록하는 것에 공수를 줄이고 싶었습니다. 매번 업무와 일상의 context change가 될 때마다 캘린더를 키고 기입하는 것이 아닌, 최소한의 트리거로 캘린더에 기입하는 것이 목적이였습니다. 일단 가장 루틴한 업무부터 기록해보자는 생각이 들었습니다. 루틴하게 9-6를 캘린더에 밀어넣고 사후 조정해도 되지만, 점심시간이나 집중하지 않을 때 혹은 사이드 프로젝트를 시작하고 종료하는 시간을 기록해보고자 했습니다.  예컨데 다음과 같은 절차로 캘린더 기입이 되었으면 했습니다.

    핸드폰 터치 -> (무엇인가) -> 노션 캘린더 기입 

    갤럭시에는 루틴와 굿락이라는 어플로 어느정도 자동화 할 수 있지만 "캘린더에 현재 시각을 기준으로 1시간동안 업무를 제목으로 기록해줘" 와 같은 디테일은 챙길 수 없습니다. 그래서 2가지 툴을 도입했습니다. 

    2. Tasker

    먼저 갤럭시에서 Custom 된 HTTP POST 요청을 보낼 수 있어야합니다. 이를 도와주는 도구로 Tasker라는 유료앱(4400원)이 존재합니다. UI는 투박한데 모바일에서 가장 자동화하기 좋은 어플로 평가받더라구요. 아이폰에서 단축어와 대응될 수 있겠습니다. 

    우편 == POST

    추가적으로 % 옵션을 이용하여 현재 시각 등등 매개변수를 POST요청에 태워서 보낼 수 있습니다. 참고로 google calendar에 기입하려면 summary와 text 키 값이 필수라고 합니다. 

    3. Make

    Make는 GUI로 구현하는 자동화툴로서 Notion, Gmail, ChatGPT, Calendar 등 다양한 서비스의 자동화를 지원합니다. 접근성이 쉽고  테스트하기 좋은 서비스입니다. 또한 노션캘린더와 구글캘린더는 양방향 동기화가 되기 때문에 고민이 덜어집니다. 결론적으로 다음과 같은 루틴이 완성됩니다.

    갤럭시  Tasker 자동화클릭 -> make -> Google Calender -> Notion Calender

    3. 시연

    Tasker에서 생성된 어플을 누르면 온종일 대기하고 있는 Make가 응답을 받고

     

    Calendar 노드가 정해진 규칙(제목: summary 변수, 시작 지금, 종료 15분 뒤) 으로 일정을 보내어

    구글 캘린더에 기입된 후 노션 캘린더에도 동기화 됩니다.

    4. 후기

    Tasker와 Maker 조합이면 핸드폰으로도 웬만한 자동화 모두 가능할 것 같고 사실 make단독으로도 루틴을 만들어서 생성하는게 꽤나 활용할 것 같습니다. 업무 외에도 휴식이나 수면과 같은 시간을 자동으로 기입해 보겠습니다.

    5. 참고자료

    https://tak0407.tistory.com/entry/%EC%9D%BC%EC%9D%BC-%ED%9A%8C%EA%B3%A0Daily-Reflection-%EC%9E%91%EC%84%B1%ED%95%98%EB%8A%94-%EC%97%90%EB%84%88%EC%A7%80-%EB%A0%88%EB%B2%A8%EC%9D%84-%EB%82%AE%EC%B6%B0%EB%B3%B4%EC%9E%90-2

     

    일일 회고(Daily Reflection) 작성하는 에너지 레벨을 낮춰보자 - 2

    목차 들어가며지난 글에서는내가 일일 회고를 쓰는 이유와 일일회고를 좀 더 간단하게 접근해보는 방식을 소개했다. 회고하고 싶은 항목에 대해 설문 폼을 만들고, 이를 Notion에 잘 연동되는지

    tak0407.tistory.com

     
     

    'Data Science > 자동화' 카테고리의 다른 글

    Make를 이용한 Notion Database -> 캘린더 자동화  (2) 2025.05.22

    데이터 중심 어플리케이션 설계에 이어 컴퓨터 밑바닥의 비밀이라는 책을 스터디 합니다. 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. 참고문헌

            + Recent posts