<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>kim__1323.log</title>
        <link>https://velog.io/</link>
        <description>데이터 분석 스쿨 블로그 입니다.</description>
        <lastBuildDate>Thu, 01 Aug 2024 09:49:29 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>kim__1323.log</title>
            <url>https://velog.velcdn.com/images/kim__1323/profile/90d3d3d4-cc19-4704-b0f8-da2c41ac10fc/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. kim__1323.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/kim__1323" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[과제 1번]]></title>
            <link>https://velog.io/@kim__1323/%EA%B3%BC%EC%A0%9C-1%EB%B2%88</link>
            <guid>https://velog.io/@kim__1323/%EA%B3%BC%EC%A0%9C-1%EB%B2%88</guid>
            <pubDate>Thu, 01 Aug 2024 09:49:29 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/kim__1323/post/efaff75b-9ec6-4fa4-8d4c-bd91aae13e31/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/45e64cc2-dea3-4631-b094-0848acbdeb12/image.png" alt=""></p>
<p><img src="blob:https://velog.io/c5e60bc5-1064-40a8-a49f-79a76bff0ea8" alt="업로드중.."></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[3주차 과제 아고다]]></title>
            <link>https://velog.io/@kim__1323/3%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C-%EC%95%84%EA%B3%A0%EB%8B%A4</link>
            <guid>https://velog.io/@kim__1323/3%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C-%EC%95%84%EA%B3%A0%EB%8B%A4</guid>
            <pubDate>Wed, 31 Jul 2024 04:04:00 GMT</pubDate>
            <description><![CDATA[<p>Week3. 2주차에 선정한 기업을 기반으로 직무분석, 고객분석 진행하기                                                        </p>
<ul>
<li>의도: SQL?, 파이썬? 언어에 대한 기술역량도 매우 중요하지만 가장 먼저 마인드셋이 되어야 하는 부분은 문제 정의 역량이며 개인 프로젝트 진행 전 간략히 비즈니스의 모델을 파악하기                                                </li>
<li>세부주제: </li>
</ul>
<p><strong>- 선정한 기업은 어떤 서비스를 제공 하나요? 우리에게 어떤 문제점을 해결해주나요?</strong>
(1) 올리브영 서비스를 제공받는 사람을 총 두 부류로 나뉠 수 있다.
여행자, 숙박 공급업체 주, 항공사, 기타 여행 서비스 제공업체이다. </p>
<p>여행자는 호텔 예약, 항공편 예약, 패키지 여행, 렌터카 예약 등을 편리하게 할 수 있고 전 세계의 고객들이 남긴 상세한 리뷰도 제공받고 있다.</p>
<p>그리고 추천 시스템, 또는 개인화된 검색 시스템을 제공해서 원하는 호텔, 항공권 패키지를 추천받거나 직접 검색할 수 있고 수시로 특별 프로모션 및 할인을 제공한다. 다만 대부분의 프로모션이 환불 불가, 예약 취소 불가의 형태로 제공된다.</p>
<p>그리고 호스트에게 직접 문자를 보낼 수도 있고 선불 결제가 아닌 현장 결제도 가능한점이 있다.</p>
<p>숙박, 항공사, 기타 여행 서비스 제공업체는 프로모션을 진행하되 비환불 요금제 및 특별 할인 프로모션 서비스를 제공하기에 업체 입장에서는 예약취소의 부담도 줄일 수 있고 유연한 가격 설정이 가능한 장점이 있다.</p>
<p>그리고 아고다 플랫폼은 글로벌 플랫폼이기에 전세계 다양한 여행자에게 마케팅을 할 수 있고 노출이 가능하다는 장점이 있다.</p>
<p>호텔 운영의 경우 전문적으로 교육받은 호텔리어들이 체계적으로 운영/관리를 수행하지만, 호텔을 제외한 모텔, 펜션, 게스트하우스 등은 대부분 사장님 혼자서 운영. 사장님들이 직접 예약도 관리하고, 청소도 하고, 장도 보고해야 해서 결제 내역 관리, 오버부킹, 시간 관리 등 예약 관리가 매우 어려움. 아고다는 목록 관리 시스템과 모든 예약 절차 및 시스템은 아고다에서 관리 및 제공하고 호텔 측에서 요금과 객실 여부를 관리하는 편리한 기능을 제공한다. 고객이 예약한 내용은 귀하 호텔의 예약 팀에게 이메일로 자동으로 전달된다.</p>
<p><strong>- 대략적으로 해당 기업이 봐야하는 주요 데이터의 지표가 무엇이 있을까요?</strong></p>
<p>• SQL을 활용한 데이터 마이닝 / 분석 / 리포팅 능력 
• 제플린, 리대시 혹은 유사 Tool 사용 경험 
• App 내 로그 분석 경험
• 퍼널 분석, 코호트 분석 등에 대한 이해와 실무 경험 
• 데이터 관점으로 스스로 WHY를 묻고, 문제 해결 과정을 즐기며 진취적인 성향</p>
<p>강력한 기본 통계 지식(신뢰 구간, T-검정, 회귀 등)
데이터 분석과 A/B 테스트를 기반으로 제품에 대한 결정을 내리는 경험이 있습니다.</p>
<p><strong>- 데이터를 통해 앞으로 이 기업은 어떤 문제를 해결해 나가면 좋을까요?</strong>                    </p>
<p>플랫폼 운영을 통해 온라인 판매 노하우도 생겼고 여유자금도 생겼습니다. 이제 이쯤 되면 직접 숙박시설을
운영해보고 싶은 욕심이 생겨납니다. 일단 숙박시설만 오픈하면 자체 플랫폼을 통해 고객을 가득 채우면 되니
까요. 실제로 하나투어, 모두투어, 야놀자, 여기어때, 한인텔 등 여러 플랫폼 사업자가 직영 숙소를 운영하고
있습니다.</p>
<p>· 하나투어 : 티마크 호텔 4개, 센터마크호텔
· 모두투어 : 스타즈 호텔 1,2,3호점, 제주도 로베로 호텔
· 야놀자 프랜차이즈 (가맹점 108개)</p>
<ul>
<li>HOTEL YAM (가맹비 약 2.5억)</li>
<li>HOTEL YAJA (가맹비 약 9.2억)</li>
<li>H AVENUE (가맹비 약 38.7억)
· 여기어때 : HOTEL 여기어때</li>
</ul>
<p>· 한인텔 : 홍콩 더스테이</p>
<p>(참조 - 야놀자 프랜차이즈)</p>
<p>모텔 관련 부동산 시장의 거래 절반이 야놀자와 엮여있다고 할 정도로 야놀자 파워가 대단한데요. 여기어때가
올해 10월 HOTEL 여기어때 1호점을 오픈한 것을 기점으로 3년 내에 직영 및 가맹점 200곳을 확보할 것이라
고 야심찬 포부를 밝혔습니다. 온라인에 이어 오프라인 경쟁도 불이 붙을 전망입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[3주차 과제 올리브영]]></title>
            <link>https://velog.io/@kim__1323/3%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C-%EC%98%AC%EB%A6%AC%EB%B8%8C%EC%98%81</link>
            <guid>https://velog.io/@kim__1323/3%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C-%EC%98%AC%EB%A6%AC%EB%B8%8C%EC%98%81</guid>
            <pubDate>Wed, 31 Jul 2024 03:40:11 GMT</pubDate>
            <description><![CDATA[<p>Week3. 2주차에 선정한 기업을 기반으로 직무분석, 고객분석 진행하기                                                        </p>
<ul>
<li>의도: SQL?, 파이썬? 언어에 대한 기술역량도 매우 중요하지만 가장 먼저 마인드셋이 되어야 하는 부분은 문제 정의 역량이며 개인 프로젝트 진행 전 간략히 비즈니스의 모델을 파악하기                                                        </li>
<li>세부주제: </li>
</ul>
<p><strong>- 선정한 기업은 어떤 서비스를 제공 하나요? 우리에게 어떤 문제점을 해결해주나요?</strong>
(1) 올리브영 서비스를 제공받는 사람을 총 두 부류로 나뉠 수 있다.
화장품 고객, 화장품 입점 브랜드이다. </p>
<p>에뛰드와 같이 10여 년 전까지만 해도 길을 가다 보면 이 브랜드들을 꼭 하나씩 볼 수 있었지만, 요즘엔 거의 보이지 않는다. 대신에 올리브영이라는 새로운 브랜드가 등장했다. 올리브영은 깨끗하고 세련된 이미지로 웬만한 물건은 다 있는 느낌을 주며, 전국에 많은 매장을 보유하고 있다.</p>
<p>올리브영은 약국과 비슷하게 의사 처방 없이 살 수 있는 의약품, 화장품, 생활용품등을 판매하는 한국형 드럭스토어로 출발, 외국과 다르게 한국에서는 화장품 중심으로 발전했다.</p>
<p>과거에는 화장품을 주로 판매자들이 방문 판매하여 구매했는데 2000년대 들어 오프라인 채널이 등장하며 저가형 원브랜드 로드샵이 등장, 대표적으로 미샤가 오프라인 매장에서 2002년에 저가 제품을 내놓으며 인기를 끌음, 하지만 국내 경제 불황과 맞물려 초저가 제품이 모여있는 멀티숍 형태의 올리브영이 급성장.</p>
<p>올리브영의 성공 비결은 첫째, 시장 선점이다. 랄라블라와 롭스가 등장했을 때 이미 올리브영은 많은 매장을 보유하고 있었고, 위치 선정과 MD 능력에서 큰 차이가 있었다. 올리브영은 신생 브랜드를 적극 발굴하여 차별적인 MD를 선보였다. 또한, 접근성에서 우위를 점하고 있어 소비자들이 올리브영을 선호하게 되었다.</p>
<p>올리브영의 또 다른 성공 전략은 온라인과 오프라인을 결합한 옴니채널 전략이다. 2018년에 처음으로 선보인 &#39;오늘드림&#39; 서비스는 소비자가 모바일 앱으로 주문하면 가장 가까운 매장에서 즉시 포장해 3시간 안에 배달해주는 서비스로, 큰 인기를 끌었다. 올리브영의 온라인 채널 성장률은 50%를 넘고, 전체 매출에서 온라인 비중도 23%까지 올랐다.</p>
<p>올리브영은 좋은 브랜드, 신생 브랜드 발굴을 동시에 하여 올리브영으로 모았고 올리브영 매장을 늘리고 좋은 위치에 매장을 세웠다. 그리고 온라인과 오프라인을 결합한 옴니채널 전략을 활용해 2018년부터 <code>오늘드림</code> 서비스를 소비자에게 재공했다. 모바일 앱으로 주문하면 가장 가까운 매장에서 즉시 포장해 3시간 안에 배달해주는 서비스로 큰 일기를 끌었다. 올리브영의 온라인 채널 성장률은 50%를 넘기는 등 소비자는 올리브영 온라인 서비스를 활용 중. <strong>그래서 소비자는 값이 싸고 좋은 제품을 좋은 접근성, 편리한 배송 서비스를 받고 있다.</strong></p>
<p>그래서 올리브영의 온라인 채널 성장률은 50%를 넘기는 등 소비자는 올리브영 온라인 서비스를 적극활용하고 있다.</p>
<p>올리브영이 이렇게 잘 나가다 보니 화장품 브랜드 업계에서는 너나 할 거 없이 올리브영에 입점하는 것이 최대 과제이자 꿈이다. 예를 들어 코스트코에 상품들이 입점했다는 것만으로도 소비자들에게 신뢰를 받는 것이다. 올리브영도 화장품 업계에서 같은 위치. 그래서 올리브영 입점 브랜드들도 하나의 서비스 대상자라고 할 수 있다.</p>
<p>하지만 직매입 구조로 높은 마진을 남기면서도 재고 리스크를 납품업체에 떠넘기려는 사례가 여러 번 발생했다. 공정위에서도 과징금 처분을 내렸지만, 문제는 법을 악용하는 데 있다는 지적이 계속되고 있다. 많은 협력업체가 중소업체인 만큼, 이들과의 공정한 거래를 지켜야 할 필요가 있다.</p>
<p>올리브영의 현재 최대 관심사는 온라인 강화이다. 오프라인 점포 수 증가세가 둔화된 대신, 물류 거점을 늘려 &#39;오늘드림&#39; 서비스를 확대하고 있다. 올리브영이 온라인에서도 압도적인 장악력을 가지게 된다면 기업 가치를 크게 제고할 수 있을 것이다. 올리브영이 추구하는 젊고 건강한 세상을 위한 가치대로 스스로도 건강한 브랜드로 거듭나길 바란다.</p>
<p><strong>- 대략적으로 해당 기업이 봐야하는 주요 데이터의 지표가 무엇이 있을까요?</strong></p>
<p>올리브영에서 데이터로 말하고 일할 수 있도록, 전사 분석과제 및 비즈니스에 필요한 데이터를 기획/설계 및 개발하여 서비스 합니다. 또한 항상 새로운 데이터를 수집하기 위해 다양한 부서와 커뮤니케이션 하며, 이렇게 생성된 데이터로 인사이트를 제공하고 전략을 수립할 수 있도록 지원합니다. 이 밖에도 전사 데이터 활용/시각화 교육을 꾸준히 진행하여 올리브영의 Self Service Analytics 문화를 만드는 역할을 하고 있습니다.</p>
<p>데이터 분석 직무의 매력은 무엇일까요?</p>
<p>데이터 직무는 누구보다 먼저 새로운 서비스를 확인하고, 생성된 데이터를 해석하여 가치 있는 정보로 만드는 중심축 역할을 한다고 생각합니다. 올리브영은 온오프라인 매장부터 글로벌 서비스까지 지속적으로 성장하고 있는 회사이기 때문에 무궁무진한 데이터를 가지고 있습니다. 이런 데이터를 핸들링 해볼 수 있는 많은 기회가 있고 새로운 트렌드를 발견하여 의사결정, 비즈니스 성과 향상에 직접적으로 기여할 수 있다는 점이 큰 매력입니다. </p>
<p>입사 전에도 올리브영에서 제품을 구매했지만, 단순히 많은 매장을 가지고 있어 편리한 H&amp;B 드럭스토어라고 생각했던 것 같습니다. 입사 하고보니 옴니채널, 글로벌몰, 라이프스타일 확장 등 정말 새로운 시도를 끊임없이 하고 있는 회사라는게 온몸으로 느껴집니다. 또한 여러 조직문화 활동들을 장려하여 동료들과 교류, 협력할 수 있는 업무 환경을 만들어 나가고 있는 점이 긍정적인 이미지를 주는 것 같습니다. 기본적인 SQL, Python, Tableau 등 스킬을 익히고 데이터 수집부터 분석까지 전반적인 데이터 아키텍처를 이해할 수 있도록 노력했습니다. 데이터 분야는 계속해서 발전하고 있기 때문에 지속적인 자기개발이 필요한 것 같아요.
무엇보다 중요한 건 다른 부서와 협업하는 경우가 많기 때문에, 비즈니스에 대한 이해를 바탕으로 커뮤니케이션하는 능력이 필요합니다. 올리브영의 서비스에 관심을 가지고 데이터의 흐름을 파악할 수 있다면 업무 시 많은 도움이 됩니다.</p>
<p>올리브영의 데이터 흐름을 맡고 있는 데이터 플랫폼팀입니다. 올리브영의 구성원들이 필요로 하는 다양한 데이터를 수집(파이프라인)하고, 적재/가공하여 데이터가 적재적소에서 쉽게 활용될 수 있도록 데이터 사용자들을 도와줍니다. 이를 통해 올리브영이 고객에 대한 이해를 바탕으로 데이터를 통한 비즈니스 성장을 견인하는 역할을 담당하고 있습니다.</p>
<p>올리브영의 데이터 흐름을 맡고 있는 데이터 플랫폼팀입니다. 올리브영의 구성원들이 필요로 하는 다양한 데이터를 수집(파이프라인)하고, 적재/가공하여 데이터가 적재적소에서 쉽게 활용될 수 있도록 데이터 사용자들을 도와줍니다. 이를 통해 올리브영이 고객에 대한 이해를 바탕으로 데이터를 통한 비즈니스 성장을 견인하는 역할을 담당하고 있습니다.</p>
<p>② 데이터 분석 및 시각화 
● 직무 경험 </p>
<ul>
<li>제조영역 분석 TFT 이후 → 스마트팩토리 분석팀 신설 → 분석과제 PM 수행</li>
<li>제조 빅데이터 분석 과제 수행 : 불량원인분석, 최적공정찾기, 설비장애예측
(특정 공정의 불량원인 인자를 찾아 불량률 90% 감소)</li>
<li>Tableau 대시보드 개발
● 직무관련 기타 </li>
<li>SAS코리아에서 2개월간의 기초통계 및 데이터마이닝, ML등의 커리큘럼을 이수</li>
</ul>
<p>직무정보 채용공고 상세 목록 회사명 직무명 Data Analyst 근무형태 정규직 채용구분 경력 근무지 서울 직무소개 - 올리브영 고객 이용 행태 Data 정제, 가공, 분석 및 리포팅</p>
<p> 목표 달성을 위한 지표 및 가설 설정 및 A/B테스트 설계 진행
 다양한 팀과 협업을 통해 Data-driven 의사결정을 위한 정량적 수치 분석 결과 제공
 각 VC 및 전사 차원의 서비스/제품 분석을 통한 인사이트 도출 및 문제 해결 방안 실행</p>
<p>지원자격 - SQL, 엑셀 등을 활용하여 데이터 추출, 가공 및 분석 가능</p>
<p> 데이터 시각화 및 리포팅 경험 보유
 Amplitude, Appsflyer, Braze 등 3rd Party 트래킹 솔루션 사용 경험 보유
 논리적 사고에 근거한 문제 해결 및 분석 경험 보유
 유연한 커뮤니케이션 능력 보유</p>
<p>우대사항 - Tableau, Redash 등 시각화 Tool을 활용한 분석 경험이 있는 분</p>
<p> Python, R 등의 데이터 처리 언어를 활용한 분석 경험이 있는 분
 분석 기반 도출한 인사이트를 개선으로 이끈 경험이 있는 분</p>
<p>데이터 분석 통해 조직의 전략적 방향을 지원하고 성과를 측정
 글로벌 MD/마케팅/프로모션 운영 개선 포인트 도출 위한 지표 개발 및 보완
 데이터 분석 기반 경영진 의사결정 지원
 데이터 시각화 및 인터랙티브 대시보드 구축 통한 데이터 주도 문화 조성</p>
<p><strong>- 데이터를 통해 앞으로 이 기업은 어떤 문제를 해결해 나가면 좋을까요?</strong>                    </p>
<p>개인적으로 제품 성분에 따른 검색을 할 수 있으면 좋겠다는 생각을 했다.                                                        </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[3주차 과제 패션커머스]]></title>
            <link>https://velog.io/@kim__1323/3%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C</link>
            <guid>https://velog.io/@kim__1323/3%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C</guid>
            <pubDate>Mon, 29 Jul 2024 10:53:38 GMT</pubDate>
            <description><![CDATA[<p>Week3. 2주차에 선정한 기업을 기반으로 직무분석, 고객분석 진행하기                                                        </p>
<ul>
<li>의도: SQL?, 파이썬? 언어에 대한 기술역량도 매우 중요하지만 가장 먼저 마인드셋이 되어야 하는 부분은 문제 정의 역량이며 개인 프로젝트 진행 전 간략히 비즈니스의 모델을 파악하기                                                        </li>
<li>세부주제: </li>
</ul>
<p><strong>- 선정한 기업은 어떤 서비스를 제공 하나요? 우리에게 어떤 문제점을 해결해주나요?</strong>
(1) 무신사의 서비스를 제공받는 사람을 총 두 부류로 나뉠 수 있다.
패션 고객과, 무신사 입점 브랜드이다. </p>
<p>무신사는 패션을 소비하는 고객에게 아주 쉽고 편리하고 다양하고 욕구에 맞는 쇼핑 선택지를 제공하기 위해 물품 정렬 알고리즘 개발, 추천 서비스, 랭킹 서비스를 제공하고 있다.
그리고 광고, 매거진, 유튜브 등 패션 커머스와 패션 커뮤니티의 파이를 더욱 넓히고 소비자가 쉽게 패션을 접할 수 있게 노력을 기울이고 있다.</p>
<p>&quot;무신사에는 3000만건 이상의 후기를 비롯해 페이지뷰, 좋아요 수 등의 패션과 관련해 가장 풍부하고 방대한 데이터를 갖추고 있다&quot;라며, &quot;현재도 개인화 추천 등의 데이터를 기반으로 고객 맞춤형 서비스를 제공</p>
<p>무신사는 또한 입점 브랜드의 초반 성장을 지원하는 여러 서비스를 지원하고 있다.
패션 기업에 최적화된 무신사 오피스 운영, 신입 브랜드 룩북 촬영 및 기획전 진행, 브랜드 스토리 콘텐츠 제작, 신입 브랜드 대출, 패션 장학생 선발 및 교육, 입점 브랜드 마케팅 유통 전략 교육 등의 사업을 진행하고 있다.
그래서 입점 브랜드를 성장시켜 패션 커머스의 파이를 함께 성장시키고 무신사도 함께 성장하는 구조를 가지고 있다.</p>
<p>또한 무신사는 입점 브랜드들이 개별적으로 진행하는 페이스북, 인스타그램 광고에서 효율을 높이기 위한 목적으로 정밀한 타겟팅 설계를 위한 내부 데이터를 공유했다. 입점 브랜드들이 무신사의 상품 데이터를 기반으로 <code>메타</code> API를 통한 고도화된 마케팅 솔루션을 사용할 수 있게 된 것이다. 유통 플랫폼이 입점 파트너 업체를 위해 내부 데이터를 개방하고 공유한 상생 사례로 볼 수 있다.</p>
<p>실제 무신사 입점 브랜드인 △드로우핏 △리(LEE) △코드그라피 △키뮤어 △1993스튜디오 등은 지난 8월 15일부터 9월 30일까지 브랜드당 2주씩 메타 협력광고를 진행했다. 이 브랜드들은 동기간에 평소 집행해왔던 트래픽 증대 캠페인도 병행했는데, 메타 협력광고의 광고 효율이 구매전환 측면에서 6.5배 우수한 것으로 분석된 것이다.
입점 브랜드 입장에서는 실질적으로 매출까지 이어질 수 있도록 구매 전환에 최적화된 광고 운영이 가능함으로써 재투자 등의 선순환까지 이끌어낼 수 있다. </p>
<p><strong>- 대략적으로 해당 기업이 봐야하는 주요 데이터의 지표가 무엇이 있을까요?</strong></p>
<p>코호트 분석, A/B 테스트, 퍼널 분석 등의 분석 방법론에 대한 이해 및 실무 적용 경험이 있으신 분
분석 결과를 누구나 이해할 수 있도록 쉽게 커뮤니케이션 할 수 있는 분
분석을 통해 제품과 비즈니스 기회를 계량화하고 이를 바탕으로 서비스 지표 상승이나 문제해결 경험
A/B 테스트, 인과 추론, 추천시스템에 대한 통계적 이해</p>
<p><strong>- 데이터를 통해 앞으로 이 기업은 어떤 문제를 해결해 나가면 좋을까요?</strong>                    </p>
<p>서로 다른 취향을 가진 고객들이 관심을 가질 만한 상품과 브랜드를 잘 찾는 것은 물론, 새로운 취향을 제안하는 개인화 영역에 초점을 맞추고 있습니다.</p>
<p>커머스의 본질은 좋은 물건을 가져와서 잘 판매하는 것이라 생각해요. 특히 온라인 커머스에서는 한정된 앱 화면에서 수많은 브랜드, 상품이 돋보일 수 있도록 구성해야 하거든요. 이러한 문제를 효율적으로 해결할 수 있는 방법이 바로 개인화 추천 시스템이라 생각합니다.</p>
<p>그리고 함께 일하는 회사 내 동료들이 데이터를 더 잘 활용할 수 있게 돕는것</p>
<p>일단은 남성물 측면에서는 독보적이라고 봐야 될 것 같아요.매출액은 꾸준히 잘 늘어나고 있고요. 지금 2022년 기준으로 매출액 7천억 정도 하고 있거든요.여전히 전년도 대비 54%가 성장을 하고 엄청 엄청나다 진짜 아직도 독보적인데 다만 문제는 영업이익이 31억원으로 줄어들거든요.전년도에 585억을 벌었던 회사가 31억으로 줄어요.</p>
<p>패션 전체 시장에서 여성이 차지하는 율이 70프로나 되는데 여성 패션 소비자들은 여러 곳을 돌아다니며 가격비교를 하며 완전 다른브랜드에서 사기도 한다. 그래서 무신사가 독보적인 위치를 가지고 있지 못하다. 그래서 여성 패션 고객들을 어떻게 무신사로 모셔올 것인가를 생각해야 한다.</p>
<p>무신사는 고객 충성도가 낮아 경쟁이 심화되고 있으며, 남성 소비자들은 단순한 쇼핑 패턴을 보이는 반면, 여성 소비자들은 다양한 브랜드를 비교하며 구매 결정을 내린다. 무신사는 AI 기반 추천 시스템을 도입하려 하고 있지만, 소비자들의 취향을 정확히 반영하기 어려운 문제도 있다.</p>
<ul>
<li>활용 요소: 2개월차 진행 되는 개인 프로젝트의 기획력과 문제 정의 능력을 미리 경험                                                        </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로젝트] 영화 순위 및 장르 분석]]></title>
            <link>https://velog.io/@kim__1323/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%98%81%ED%99%94-%EC%88%9C%EC%9C%84-%EB%B0%8F-%EC%9E%A5%EB%A5%B4-%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@kim__1323/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%98%81%ED%99%94-%EC%88%9C%EC%9C%84-%EB%B0%8F-%EC%9E%A5%EB%A5%B4-%EB%B6%84%EC%84%9D</guid>
            <pubDate>Sun, 28 Jul 2024 08:02:59 GMT</pubDate>
            <description><![CDATA[<p>아래는 A 영화 회사의 신규 영화 촬영 시기가 다가오고 제작비를 효율적으로 관리하고 수익을 최대화하고 싶어한다. 매출을 극대화할 수 있는 성우를 찾기위해 영화의 순위와 영화에 캐스팅된 성우들에 대한 분석을 진행한 프로젝트이다.</p>
<p>여러 개의 csv 파일로 분석을 진행하는 복잡한 프로젝트이다.</p>
<br>

<h3 id="문제-상황은-이렇다"><strong>문제 상황은 이렇다:</strong></h3>
<p>🔖
A사는 이번에 신규 영화를 촬영할 계획을 가지고 있다. 제작비가 크게 들어가는 만큼 그 만큼의 수익이 발생해야하는 상황이기 때문에 최대한 Risk를 덜 가져가려고 하고 있다. 이에 과거 상영한 영화의 데이터를 활용하여 감독, 성우에 따라 매출을 극대화하는 캐스팅을 진행하려고 한다.</p>
<p><strong>⛳ 문제정의</strong></p>
<p>▶ 신규 영화 제작을 위한 캐스팅 Line-up 불분명</p>
<p><strong>⛳ 기대효과</strong></p>
<p>▶ 매출을 극대화 할 수 있는 감독 및 배우 캐스팅 및 영화 흥행</p>
<p><strong>⛳ 해결방안</strong></p>
<p>▶ 과거 영화 데이터 활용 매출 극대화 캐스팅</p>
<p><strong>⛳ 성과측정</strong></p>
<p>▶ 캐스팅 후 영화 제작 및 상영 후 매출 모니터링</p>
<p><strong>⛳ 운영</strong></p>
<p>▶ 캐스팅 결과 활용</p>
<hr>
<h3 id="데이터셋-정의"><strong>데이터셋 정의</strong></h3>
<BR>

<pre><code class="language-python"># ▶ Movie total gross
import pandas as pd
df_movie_gross = pd.read_csv(&#39;S_PJT14_disney_movies_total_gross.csv&#39;)

print(df_movie_gross.shape)
df_movie_gross.head()

   index                     movie_title  release_date         genre     MPAA_rating     total_gross inflation_adjusted_gross
0      0  Snow White and the Seven Dwarfs  Dec 21, 1937    Musical            G   $184,925,485            $5,228,953,251
1      1                        Pinocchio   Feb 9, 1940  Adventure            G    $84,300,000             $2,188,229,052
2      2                         Fantasia  Nov 13, 1940    Musical            G    $83,320,000             $2,187,090,808
3      3                Song of the South  Nov 12, 1946  Adventure            G    $65,000,000             $1,078,510,579
4      4                       Cinderella  Feb 15, 1950      Drama            G    $85,000,000               $920,608,730




</code></pre>
<br>



<table>
<thead>
<tr>
<th align="center">index</th>
<th align="center">movie_title</th>
<th align="center">release_date</th>
<th align="center">genre</th>
<th align="center">MPAA_rating</th>
<th align="center">total_gross</th>
<th align="center">inflation_adjusted_gross</th>
</tr>
</thead>
<tbody><tr>
<td align="center">인덱스</td>
<td align="center">영화제목</td>
<td align="center">출시일</td>
<td align="center">장르</td>
<td align="center">영화시청등급</td>
<td align="center">해당 영화 총 수익</td>
<td align="center">인플레이션 대비 수익</td>
</tr>
</tbody></table>
<hr>
<h3 id="데이터-전처리-및-eda"><strong>데이터 전처리 및 EDA</strong></h3>
<h3 id="1-movie_gross-data-shape형태-확인">(1) movie_gross Data shape(형태) 확인</h3>
<pre><code class="language-python"># ▶ Data 형태 확인
# ▶ 7,043 row, 21 col로 구성됨
df_movie_gross.shape

&gt; (579, 7)</code></pre>
<ul>
<li>영화의 매출 및 수익 데이터는 579개의 행과 7개의 열 데이터가 있다.</li>
</ul>
<h3 id="2-movie_gross-data-type-확인">(2) movie_gross Data type 확인</h3>
<pre><code class="language-python"># ▶ Data type 확인
df.info()

&lt;class &#39;pandas.core.frame.DataFrame&#39;&gt;
RangeIndex: 579 entries, 0 to 578
Data columns (total 7 columns):
 #   Column                    Non-Null Count  Dtype 
---  ------                    --------------  ----- 
 0   index                     579 non-null    int64 
 1   movie_title               579 non-null    object
 2   release_date              579 non-null    object
 3   genre                     562 non-null    object
 4   MPAA_rating               523 non-null    object
 5   total_gross               579 non-null    object
 6   inflation_adjusted_gross  579 non-null    object
dtypes: int64(1), object(6)
memory usage: 31.8+ KB</code></pre>
<p>총 21개의 열 데이터가 있으며 <code>total_gross</code>, <code>inflation_adjusted_gross</code> 라는 돈의 데이터가 있는 값들이 숫자형 데이터가 아닌 <code>object</code> 문자형 데이터로 선언이 돼있다.</p>
<p>그리고 <code>release_date</code>와 같이 날짜형 데이터 타입이어야 할 데이터가 <code>object</code> 문자형 데이터로 선언이 되어있다.  </p>
<p>필요에 따라 데이터 분석 진행 시 <code>int</code> 타입 그리고 날짜형 데이터는 <code>date</code> 타입으로 변경시키는게 좋을 거 같다.</p>
<h3 id="3-movie_gross-null값-확인--빈-값의-data">(3) movie_gross Null값 확인 (※ 빈 값의 Data)</h3>
<pre><code class="language-python">df_movie_gross.isnull().sum()

index                        0
movie_title                  0
release_date                 0
genre                       17
MPAA_rating                 56
total_gross                  0
inflation_adjusted_gross     0
dtype: int64</code></pre>
<ul>
<li><code>null</code>값이 존재하며 해당 <code>null</code>값들은 제거해주거나 문자열 <code>none</code> 또는 0 으로 대체해줄 것이다.</li>
</ul>
<p><strong>문자열 데이터 전처리 및 Null 처리</strong></p>
<pre><code class="language-python"># ▶ 문자열 데이터 전처리 및 Null 처리
df_movie_gross[&#39;inflation_adjusted_gross&#39;] = df_movie_gross[&#39;inflation_adjusted_gross&#39;].str.replace(&#39;$&#39;, &#39;&#39;)
df_movie_gross[&#39;inflation_adjusted_gross&#39;] = df_movie_gross[&#39;inflation_adjusted_gross&#39;].str.replace(&#39;,&#39;, &#39;&#39;)
df_movie_gross[&#39;inflation_adjusted_gross&#39;] = df_movie_gross[&#39;inflation_adjusted_gross&#39;].str.replace(&#39;\r&#39;, &#39;&#39;)
df_movie_gross[&#39;inflation_adjusted_gross&#39;] = df_movie_gross[&#39;inflation_adjusted_gross&#39;].astype(int)

df_movie_gross[&#39;total_gross&#39;] = df_movie_gross[&#39;total_gross&#39;].str.replace(&#39;$&#39;, &#39;&#39;)
df_movie_gross[&#39;total_gross&#39;] = df_movie_gross[&#39;total_gross&#39;].str.replace(&#39;,&#39;, &#39;&#39;)
df_movie_gross[&#39;total_gross&#39;] = df_movie_gross[&#39;total_gross&#39;].str.replace(&#39;\r&#39;, &#39;&#39;)
df_movie_gross[&#39;total_gross&#39;] = df_movie_gross[&#39;total_gross&#39;].astype(int)

df_movie_gross[&#39;genre&#39;].fillna(&#39;none&#39;, inplace = True)
df_movie_gross[&#39;MPAA_rating&#39;].fillna(&#39;none&#39;, inplace = True)

df_movie_gross = df_movie_gross.drop([&#39;index&#39;], axis=1)
df_movie_gross.head(5)</code></pre>
<pre><code class="language-python">df_movie_gross.info()
&lt;class &#39;pandas.core.frame.DataFrame&#39;&gt;
RangeIndex: 579 entries, 0 to 578
Data columns (total 6 columns):
 #   Column                    Non-Null Count  Dtype 
---  ------                    --------------  ----- 
 0   movie_title               579 non-null    object
 1   release_date              579 non-null    object
 2   genre                     579 non-null    object
 3   MPAA_rating               579 non-null    object
 4   total_gross               579 non-null    int64 
 5   inflation_adjusted_gross  579 non-null    int64 
dtypes: int64(2), object(4)
memory usage: 27.3+ KB</code></pre>
<p>문자열 데이터 타입이었던 데이터들을 <code>int64</code> 숫자형으로 변형시켜주었다.</p>
<pre><code class="language-python"># ▶ 시간 데이터 변환
df_movie_gross[&#39;release_date&#39;] = pd.to_datetime(df_movie_gross[&#39;release_date&#39;])
df.info()

&lt;class &#39;pandas.core.frame.DataFrame&#39;&gt;
RangeIndex: 579 entries, 0 to 578
Data columns (total 6 columns):
 #   Column                    Non-Null Count  Dtype         
---  ------                    --------------  -----         
 0   movie_title               579 non-null    object        
 1   release_date              579 non-null    datetime64[ns]
 2   genre                     579 non-null    object        
 3   MPAA_rating               579 non-null    object        
 4   total_gross               579 non-null    int64         
 5   inflation_adjusted_gross  579 non-null    int64         
dtypes: datetime64[ns](1), int64(2), object(3)
memory usage: 27.3+ KB</code></pre>
<p><code>release_date</code> 도 날짜형 데이터 타입으로 변형해주었다.</p>
<br>



<h3 id="4-movie_gross-데이터-중복-데이터-확인"><strong>(4) movie_gross 데이터 중복 데이터 확인</strong></h3>
<pre><code class="language-python"># ▶ 영화별 중복 데이터 존재
df_movie_gross[&#39;movie_title&#39;].value_counts()

The Jungle Book                 3
Freaky Friday                   2
Cinderella                      2
Bad Company                     2
101 Dalmatians                  2
                               ..
Quiz Show                       1
A Simple Twist of Fate          1
It&#39;s Pat                        1
Camp Nowhere                    1
Rogue One: A Star Wars Story    1
Name: movie_title, Length: 573, dtype: int64</code></pre>
<ul>
<li>확인 결과 일부 영화들이 중복된 데이터가 있는 것으로 보인다.</li>
</ul>
<pre><code class="language-python"># ▶ 정글북이여도, 장르와 발매년도에 따라 여러개의 데이터가 존재
df_movie_gross[df_movie_gross[&#39;movie_title&#39;] == &#39;The Jungle Book&#39;]

                     movie_title  release_date        genre    MPAA_rating        total_gross    inflation_adjusted_gross
13                 The Jungle Book    1967-10-18      Musical      Not Rated          141843000                   789612346
194                 The Jungle Book    1994-12-25    Adventure             PG           44342956                    88930321
567                 The Jungle Book    2016-04-15    Adventure             PG          364001123                   364001123</code></pre>
<ul>
<li>구체적으로 예를 들어 같은 정글북 영화여도 장르와 발매년도에 따라 여러개의 데이터가 존재하고 있었다.</li>
</ul>
<br>

<pre><code class="language-python"># ▶ 같은 영화라면 최신에 개봉한 영화만 남기기
df_movie_gross = df_movie_gross.sort_values(by=[&#39;movie_title&#39;, &#39;release_date&#39;], ascending=[True, False])
df_movie_gross = df_movie_gross.drop_duplicates(&#39;movie_title&#39;, keep=&#39;first&#39;)
df_movie_gross.head(5)

|       movie_title        | release_date |    genre   | MPAA_rating | total_gross | inflation_adjusted_gross |
|--------------------------|--------------|------------|-------------|-------------|--------------------------|
| 101 Dalmatians           | 1996-11-27   | Comedy     | G           | 136189294   | 258728898                |
| 102 Dalmatians           | 2000-11-22   | Comedy     | G           | 66941559    | 104055039                |
| 1492: Conquest of Paradise| 1992-10-09   | Adventure  | PG-13       | 7099531     | 14421454                 |
| 20,000 Leagues Under the Sea | 1954-12-23 | Adventure  | none        | 28200000    | 528279994                |
| 25th Hour                | 2002-12-19   | Drama      | R           | 13084595    | 18325463                 |</code></pre>
<pre><code class="language-python"># ▶ 중복제거 확인
df_movie_gross[&#39;movie_title&#39;].value_counts().head(5)

101 Dalmatians       1
Stakeout             1
Song of the South    1
Sorority Boys        1
Spaced Invaders      1
Name: movie_title, dtype: int64</code></pre>
<p>그래서 중복되는 다른 영화 데이터는 제거하고 가장 최신에 개봉한 영화만 남기도록 하였다.</p>
<hr>
<h3 id="1-voice-actor-data-data-shape형태-확인">(1) Voice actor data Data shape(형태) 확인</h3>
<pre><code class="language-python"># ▶ Voice actor
import pandas as pd
df_voice_actor = pd.read_csv(&#39;S_PJT14_disney_voice_actors.csv&#39;)
print(df_voice_actor.shape)
df_voice_actor.head(5)

&gt; (935, 4)
index           character          voice-actor                             movie
0    0        Abby Mallard            Joan Cusack                  Chicken Little
1    1      Abigail Gabble         Monica Evans                   The Aristocats
2    2            Abis Mal      Jason Alexander            The Return of Jafar
3    3    Abu                         Frank Welker                        Aladdin
4    4            Achilles                  NaN    The Hunchback of Notre Dame</code></pre>
<ul>
<li>영화 성우의 데이터는 935개의 행과 4개의 열 데이터가 있다.</li>
</ul>
<pre><code class="language-python"># ▶ Col 재정비
df_voice_actor = df_voice_actor[[&#39;movie&#39;, &#39;character&#39;, &#39;voice-actor&#39;]]
df_voice_actor.columns = [&#39;movie_title&#39;, &#39;character&#39;, &#39;voice_actor&#39;]
df_voice_actor.head(5)

                    movie_title    character    voice_actor
0                  Chicken Little    Abby Mallard    Joan Cusack
1                  The Aristocats    Abigail Gabble    Monica Evans
2    The Return of Jafar    Abis Mal    Jason Alexander
3    Aladdin    Abu    Frank Welker
4    The Hunchback of Notre Dame    Achilles    None</code></pre>
<p>불필요한 인덱스 컬럼이 존재해 제거해주었다.</p>
<h3 id="2voice-actor-data-null값-확인--빈-값의-data">(2)Voice actor data Null값 확인 (※ 빈 값의 Data)</h3>
<pre><code class="language-python">df_voice_actor.isnull().sum()

index          0
character      0
voice-actor    0
movie          0
dtype: int64</code></pre>
<ul>
<li><code>null</code>값이 존재하지 않는다.</li>
</ul>
<br>



<h3 id="3-voice-actor-data-데이터-중복-데이터-확인"><strong>(3) Voice actor data 데이터 중복 데이터 확인</strong></h3>
<pre><code class="language-python"># ▶ 하나의 영화에 참여만 여러명의 성우가 존재한다.
df_voice_actor[&#39;movie_title&#39;].value_counts()

DuckTales             31
Zootopia              22
Hercules              22
Wreck-It Ralph        21
The Little Mermaid    20
                      ..
The Plow Boy           1
Hold That Pose         1
Grin and Bear It       1
Saludos Amigos         1
The Pirate Fairy       1
Name: movie_title, Length: 139, dtype: int64</code></pre>
<ul>
<li>확인 결과 중복 데이터가 있지만 하나의 영화에 참여한 여러명의 성우가 존재하는 것으로 보이며 한 영화의 제목에 여러명의 성우가 존재하는 것이기에 중복이라고 보기 어렵다.</li>
</ul>
<hr>
<h3 id="1--director-data-data-shape형태-확인">(1)  Director data Data shape(형태) 확인</h3>
<pre><code class="language-python"># ▶ Data 형태 확인
# ▶ Movie director
df_director = pd.read_csv(&#39;S_PJT14_disney_director.csv&#39;)
print(df_director.shape)
df_director.head(5)

&gt; (56, 3)
index                               name          director
0    0    Snow White and the Seven Dwarfs        David Hand
1    1                          Pinocchio    Ben Sharpsteen
2    2                           Fantasia      full credits
3    3                              Dumbo    Ben Sharpsteen
4    4                              Bambi        David Hand
</code></pre>
<ul>
<li>감독에 대한 데이터는 56개의 행과 3개의 열 데이터가 있다.</li>
</ul>
<pre><code class="language-python">df_director = df_director[[&#39;name&#39;, &#39;director&#39;]]
df_director.columns = [&#39;movie_title&#39;, &#39;director&#39;]
df_director.head(5)

                               name          director
0    Snow White and the Seven Dwarfs        David Hand
1                          Pinocchio    Ben Sharpsteen
2                           Fantasia      full credits
3                              Dumbo    Ben Sharpsteen
4                              Bambi        David Hand</code></pre>
<p>불필요한 인덱스 컬럼이 존재해 인덱스 컬럼을 제거해주었다.</p>
<br>

<h3 id="2-director-data-null값-확인--빈-값의-data">(2) Director data Null값 확인 (※ 빈 값의 Data)</h3>
<pre><code class="language-python">df_director.isnull().sum()

index       0
name        0
director    0
dtype: int64</code></pre>
<ul>
<li><code>null</code>값이 존재하지 않는다.</li>
</ul>
<br>


<h3 id="3-director-data-데이터-중복-데이터-확인"><strong>(3) Director data 데이터 중복 데이터 확인</strong></h3>
<pre><code class="language-python"># ▶ 중복없음
df_director[&#39;movie_title&#39;].value_counts().head(5)

Snow White and the Seven Dwarfs    1
Pinocchio                          1
Aladdin                            1
The Lion King                      1
Pocahontas                         1
Name: movie_title, dtype: int64</code></pre>
<ul>
<li>각각의 영화에서 중복된 감독은 존재하지 않는다.</li>
</ul>
<hr>
<h3 id="1-song-datadata-shape형태-확인">(1) Song dataData shape(형태) 확인</h3>
<pre><code class="language-python"># ▶ Movie song
df_song = pd.read_csv(&#39;S_PJT14_disney_characters.csv&#39;)
print(df_song.shape)
df_song.head(5)

&gt; (56, 6)
|   index |            movie_title             |   release_date   |    hero     |   villian   |             song              |
|---------|------------------------------------|------------------|-------------|-------------|-------------------------------|
|       0 | \nSnow White and the Seven Dwarfs    | December 21, 1937| Snow White  | Evil Queen  | Some Day My Prince Will Come  |
|       1 | \nPinocchio                          | February 7, 1940 | Pinocchio   | Stromboli   | When You Wish upon a Star     |
|       2 | \nFantasia                           | November 13, 1940| NaN         | Chernabog   | NaN                           |
|       3 | \nDumbo                              | October 23, 1941 | Dumbo       | Ringmaster  | Baby Mine                     |
|       4 | \nBambi                              | August 13, 1942  | Bambi       | Hunter      | Love Is a Song                |

</code></pre>
<ul>
<li><p>영화에 쓰인 노래 또는 OST에 대한 데이터는 56개의 행과 6개의 열 데이터가 있다.</p>
</li>
<li><p>해당 데이터셋의 영화 제목을 보니 <code>\n</code> 이라는 문자열이 포함되어 있는 것으로 보아 해당 문자열을 제거해주어야 할 것으로 보인다.</p>
</li>
</ul>
<br>

<h3 id="2-song-data-null값-확인--빈-값의-data">(2) Song data Null값 확인 (※ 빈 값의 Data)</h3>
<pre><code class="language-python">df_song.isnull().sum()

index            0
movie_title      0
release_date     0
hero             4
villian         10
song             9
dtype: int64</code></pre>
<ul>
<li><code>hero</code> villian<code></code>song<code>열에 일부</code>null<code>값이 존재하는 것으로 보인다.</code>none` 값으로 대체해주어야 할 것으로 보인다.</li>
</ul>
<br>

<p><strong>데이터 전처리</strong></p>
<pre><code># ▶ 문자열 데이터 처리
import re
df_song[&#39;movie_title&#39;] = df_song[&#39;movie_title&#39;].str.replace(&#39;\n&#39;, &#39;&#39;)
df_song.fillna(&#39;none&#39;, inplace=True)
df_song.drop([&#39;index&#39;], axis=1, inplace =True)
df_song.head(5)


|            movie_title             |   release_date   |    hero     |   villian   |             song              |
|------------------------------------|------------------|-------------|-------------|-------------------------------|
| Snow White and the Seven Dwarfs    | December 21, 1937| Snow White  | Evil Queen  | Some Day My Prince Will Come  |
| Pinocchio                          | February 7, 1940 | Pinocchio   | Stromboli   | When You Wish upon a Star     |
| Fantasia                           | November 13, 1940| NaN         | Chernabog   | NaN                           |
| Dumbo                              | October 23, 1941 | Dumbo       | Ringmaster  | Baby Mine                     |
| Bambi                              | August 13, 1942  | Bambi       | Hunter      | Love Is a Song                |
</code></pre><ul>
<li><p>앞서 영화 제목에 있던 <code>\n</code>을 제거해주고 불필요한 <code>index</code> 컬럼을 제거해주었다.</p>
</li>
<li><p><code>null</code> 값 데이터는 <code>none</code>으로 변경해주었다.</p>
</li>
</ul>
<br>


<h3 id="3-song-data-데이터-중복-데이터-확인"><strong>(3) Song data 데이터 중복 데이터 확인</strong></h3>
<pre><code class="language-python"># ▶ 중복 없음
df_song[&#39;movie_title&#39;].value_counts().head(5)

Snow White and the Seven Dwarfs    1
Pinocchio                          1
Aladdin                            1
The Lion King                      1
Pocahontas                         1
Name: movie_title, dtype: int64</code></pre>
<ul>
<li>각각의 영화에 쓰인 중복된 영화는 존재하지 않는다.</li>
</ul>
<br>


<h3 id="5-데이터-eda"><strong>(5) 데이터 EDA</strong></h3>
<p><strong>Data 연결</strong></p>
<p>현재 영화 데이터는 총 4개로 쪼개져 있는 상황으로 효율적인 분석을 위해서는 4개의 데이터를 하나로 <code>merge</code> 병합해야 한다. 각각의 데이터의 특징은 이렇다.</p>
<ul>
<li>df_movie_gross : 영화 수익 (Base data) - 중복 X</li>
<li>df_voice_actor : 영화 캐릭터 별 성우 - 중복 O</li>
<li>df_director : 영화 감독 - 중복 X</li>
<li>df_song : 영화 OST 및 히어로/빌런 - 중복 X</li>
</ul>
<p>그래서 중복이 없는 데이터를 우선하여 병합을 해주었다.   </p>
<pre><code class="language-python"># ▶ 중복 없는 데이터 우선 merge
|       movie_title        | release_date |    genre   | MPAA_rating | total_gross | inflation_adjusted_gross |       director        |
|--------------------------|--------------|------------|-------------|-------------|--------------------------|------------------------|
| 101 Dalmatians           | 1996-11-27   | Comedy     | G           | 136189294   | 258728898                | Wolfgang Reitherman    |
| 102 Dalmatians           | 2000-11-22   | Comedy     | G           | 66941559    | 104055039                | NaN                    |
| 1492: Conquest of Paradise| 1992-10-09   | Adventure  | PG-13       | 7099531     | 14421454                 | NaN                    |
| 20,000 Leagues Under the Sea | 1954-12-23 | Adventure  | none        | 28200000    | 528279994                | NaN                    |
| 25th Hour                | 2002-12-19   | Drama      | R           | 13084595    | 18325463                 | NaN                    |


Churn
No     5174
Yes    1869
Name: count, dtype: int64</code></pre>
<p>다음으로 <code>df_song</code> 데이터 프레임을 병합하기 위해 겹치는 데이터인 <code>release_date</code> 열을 제거해주었다.</p>
<pre><code class="language-python"># ▶ df_song에 release_date 중복 col 이기에 제거 후 merge
df_song = df_song.drop([&#39;release_date&#39;], axis=1)
df_merge = pd.merge(df_merge, df_song, how=&#39;left&#39;, on=&#39;movie_title&#39;)
df_merge.head(5)

|       movie_title        | release_date |    genre   | MPAA_rating | total_gross | inflation_adjusted_gross |       director        |  hero | villian | song |
|--------------------------|--------------|------------|-------------|-------------|--------------------------|------------------------|-------|---------|------|
| 101 Dalmatians           | 1996-11-27   | Comedy     | G           | 136189294   | 258728898                | Wolfgang Reitherman    |  NaN  |   NaN   | NaN  |
| 102 Dalmatians           | 2000-11-22   | Comedy     | G           | 66941559    | 104055039                | NaN                    |  NaN  |   NaN   | NaN  |
| 1492: Conquest of Paradise| 1992-10-09   | Adventure  | PG-13       | 7099531     | 14421454                 | NaN                    |  NaN  |   NaN   | NaN  |
| 20,000 Leagues Under the Sea | 1954-12-23 | Adventure  | none        | 28200000    | 528279994                | NaN                    |  NaN  |   NaN   | NaN  |
| 25th Hour                | 2002-12-19   | Drama      | R           | 13084595    | 18325463                 | NaN                    |  NaN  |   NaN   | NaN  |
</code></pre>
<p>데이터를 병합해보니 전체적인 <code>null</code> 값이 많은 것으로 보여 각 데이터들의 <code>shape</code> 형태를 확인해보았다.</p>
<pre><code># ▶ Target 숫자 데이터로 변환
# ▶ 영화 흥행 수익 데이터 대비 감독과 음악(OST)가 현저히 적음
df_movie_gross.shape,df_director.shape, df_song.shape


&gt; ((573, 6), (56, 2), (56, 4))</code></pre><ul>
<li><p>영화 흥행 수익 데이터 대비 감독과 음악(OST)가 현저히 적어서 <code>null</code> 값이 발생하는 것으로 보인다.</p>
</li>
<li><p>이렇게 데이터가 서로 대응하지 않을 정도로 일치하지 않는 경우에는 데이터를 더 수집하거나 데이터 분석을 멈춰야 하는 것이 바람직하다.</p>
</li>
<li><p>일단 다른 중복 값이 있어 아직 병합하지 않은 <code>voice</code> 데이터의 전처리를 마무리 해보기로 했다.</p>
</li>
</ul>
<br>


<p><strong>(5)- 1 성우 데이터 처리</strong></p>
<p>성우 데이터의 경우 한 명이 여러 캐릭터를 중복에서 담당한 경우가 많아 일단 <code>hero</code> 성우와 <code>villan</code> 성우 해당하는 성우들을 리스트 형태로 출력해보았다.</p>
<pre><code class="language-python"># ▶ voice actor의 경우 중복 데이터가 많기 때문에 Hero에 성우와 Villian의 성우만 join
hero_list = list(df_merge[df_merge[&#39;hero&#39;].notnull()][&#39;hero&#39;])
Villian_list = list(df_merge[df_merge[&#39;villian&#39;].notnull()][&#39;villian&#39;])

print(hero_list)
print(Villian_list)

[&#39;Aladdin&#39;, &#39;Alice&#39;, &#39;Milo Thatch&#39;, &#39;Belle&#39;, &#39;Hiro Hamada&#39;, &#39;Bolt&#39;, &#39;Kenai&#39;, &#39;Ace Cluck&#39;, &#39;Cinderella&#39;, &#39;Aladar&#39;, &#39;none&#39;, &#39;Elsa&#39;, &#39;Hercules&#39;, &#39;Maggie&#39;, &#39;Lady and Tramp&#39;, &#39;Lilo and Stitch&#39;, &#39;Lewis&#39;, &#39;Moana&#39;, &#39;Mulan&#39;, &#39;Oliver&#39;, &#39;Pinocchio&#39;, &#39;Pocahontas&#39;, &#39;Aurora&#39;, &#39;Snow White&#39;, &#39;Rapunzel&#39;, &#39;Tarzan&#39;, &#39;Thomas and Duchess&#39;, &#39;Taran&#39;, &#39;Kuzco&#39;, &#39;Tod and Copper&#39;, &#39;Basil&#39;, &#39;Quasimodo&#39;, &#39;Mowgli&#39;, &#39;Simba&#39;, &#39;Ariel&#39;, &#39;Winnie the Pooh&#39;, &#39;Tiana&#39;, &#39;Bernard and Miss Bianca&#39;, &#39;Bernard and Miss Bianca&#39;, &#39;Arthur&#39;, &#39;Jim Hawkins&#39;, &#39;Winnie the Pooh&#39;, &#39;Ralph&#39;, &#39;Judy Hopps&#39;]
[&#39;Jafar&#39;, &#39;Queen of Hearts&#39;, &#39;Commander Rourke&#39;, &#39;Gaston&#39;, &#39;Professor Callaghan&#39;, &#39;Dr. Calico&#39;, &#39;Denahi&#39;, &#39;Foxy Loxy&#39;, &#39;Lady Tremaine&#39;, &#39;Kron&#39;, &#39;Chernabog&#39;, &#39;Prince Hans&#39;, &#39;Hades&#39;, &#39;Alameda Slim&#39;, &#39;Si and Am&#39;, &#39;none&#39;, &#39;Doris&#39;, &#39;none&#39;, &#39;Shan Yu&#39;, &#39;Sykes&#39;, &#39;Stromboli&#39;, &#39;Governor Ratcliffe&#39;, &#39;Maleficent&#39;, &#39;Evil Queen&#39;, &#39;Mother Gothel&#39;, &#39;Clayton&#39;, &#39;Edgar Balthazar&#39;, &#39;Horned King&#39;, &#39;Yzma&#39;, &#39;Amos Slade&#39;, &#39;Professor Ratigan&#39;, &#39;Claude Frollo&#39;, &#39;Kaa and Shere Khan&#39;, &#39;Scar&#39;, &#39;Ursula&#39;, &#39;none&#39;, &#39;Dr. Facilier&#39;, &#39;Madame Medusa&#39;, &#39;Percival C. McLeach&#39;, &#39;Madam Mim&#39;, &#39;John Silver&#39;, &#39;none&#39;, &#39;Turbo&#39;, &#39;none&#39;]</code></pre>
<br>


<pre><code class="language-python"># ▶ 주인공 성우도 2명이 진행한 이력이 있다.
df_voice_actor[df_voice_actor[&#39;character&#39;].isin(hero_list)][&#39;movie_title&#39;].value_counts().head(5)

Tarzan                       2
The Lion King                2
Dinosaur                     1
Home on the Range            1
The Princess and the Frog    1
Name: movie_title, dtype: int64</code></pre>
<ul>
<li>그래서 확인 결과 타잔과 라이온킹 영화에 두 명의 성우가 겹치게 담당을 했던 것을 확인할 수 있었다.</li>
</ul>
<pre><code class="language-python"># ▶ 중복이라면 두명의 배우를 모두 넣기 위해 이름을 구분자 ;
df_hero = pd.DataFrame(df_voice_actor[df_voice_actor[&#39;character&#39;].isin(hero_list)].groupby([&#39;movie_title&#39;, &#39;character&#39;]).agg(lambda x:&quot;; &quot;.join(x)))
df_hero = df_hero.reset_index()
df_hero = df_hero[[&#39;character&#39;, &#39;voice_actor&#39;]]
df_hero.columns = [&#39;character&#39;, &#39;hero_actor&#39;]
df_hero.head(10)

            character                  hero_actor
0              Aladdin    Scott Weinger; Brad Kane
1                Alice            Kathryn Beaumont
2          Milo Thatch              Michael J. Fox
3                Belle                Paige O&#39;Hara
4                 Bolt               John Travolta
5                Kenai             Joaquin Phoenix
6           Cinderella                 Ilene Woods
7               Aladar               D. B. Sweeney
  8                 Elsa                Idina Menzel
9             Hercules    Tate Donovan; Joshua Keaton</code></pre>
<ul>
<li>한 영화에 두 명의 성우가 중복되지만 두 명의 성우를 모두 넣기 위해 이름을 구분자 <code>;</code>로 하여 <code>voice_actor</code>에 넣어주었다.</li>
</ul>
<br>

<pre><code class="language-python"># ▶ 빌런, 중복이 없다.
df_voice_actor[df_voice_actor[&#39;character&#39;].isin(Villian_list)][&#39;movie_title&#39;].value_counts().head(5)  

Home on the Range        1
The Fox and the Hound    1
The Little Mermaid       1
Oliver &amp; Company         1
Pinocchio                1
Name: movie_title, dtype: int64</code></pre>
<ul>
<li>빌런을 담당한 성우는 작품이 겹치는 경우가 없는 것으로 보인다.</li>
</ul>
<pre><code class="language-python"># ▶ 빌런 Data
df_villian = pd.DataFrame(df_voice_actor[df_voice_actor[&#39;character&#39;].isin(Villian_list)])
df_villian = df_villian[[&#39;character&#39;, &#39;voice_actor&#39;]]
df_villian.columns = [&#39;character&#39;, &#39;villian_actor&#39;]
df_villian.head(5)</code></pre>
<ul>
<li>중복이 되지 않는 히어로 데이터와 빌런 데이터를 병합해주기 위해 각각 빌런 데이터 프레임과 히어로 데이터 프레임을 만들어 주었다.</li>
</ul>
<br>

<pre><code class="language-python"># ▶ 성우 데이터 merge

# ▶ Hero
df_merge = pd.merge(df_merge, df_hero, how=&#39;left&#39;, left_on=&#39;hero&#39;, right_on=&#39;character&#39;)

# ▶ villian
df_merge = pd.merge(df_merge, df_villian, how=&#39;left&#39;, left_on=&#39;villian&#39;, right_on=&#39;character&#39;)

# ▶ 중복 col 삭제
df_merge = df_merge.drop([&#39;character_x&#39;, &#39;character_y&#39;], axis=1)  

df_merge.head(5)

|       movie_title        | release_date |    genre   | MPAA_rating | total_gross | inflation_adjusted_gross |       director        |  hero | villian | song | hero_actor | villian_actor |
|--------------------------|--------------|------------|-------------|-------------|--------------------------|------------------------|-------|---------|------|------------|---------------|
| 101 Dalmatians           | 1996-11-27   | Comedy     | G           | 136189294   | 258728898                | Wolfgang Reitherman    |  NaN  |   NaN   | NaN  |     NaN    |      NaN      |
| 102 Dalmatians           | 2000-11-22   | Comedy     | G           | 66941559    | 104055039                | NaN                    |  NaN  |   NaN   | NaN  |     NaN    |      NaN      |
| 1492: Conquest of Paradise| 1992-10-09   | Adventure  | PG-13       | 7099531     | 14421454                 | NaN                    |  NaN  |   NaN   | NaN  |     NaN    |      NaN      |
| 20,000 Leagues Under the Sea | 1954-12-23 | Adventure  | none        | 28200000    | 528279994                | NaN                    |  NaN  |   NaN   | NaN  |     NaN    |      NaN      |
| 25th Hour                | 2002-12-19   | Drama      | R           | 13084595    | 18325463                 | NaN                    |  NaN  |   NaN   | NaN  |     NaN    |      NaN      |</code></pre>
<ul>
<li>이렇게 최종적으로 영화 제작을 위한 성우 선별작업이 용이하게 만든 데이터 프레임이 완성되었다.</li>
</ul>
<hr>
<h3 id="6-데이터-eda--2"><strong>(6) 데이터 EDA -2</strong></h3>
<table>
<thead>
<tr>
<th align="center">movie_title</th>
<th align="center">release_date</th>
<th align="center">genre</th>
<th align="center">MPAA_rating</th>
<th align="center">total_gross</th>
<th align="center">inflation_adjusted_gross</th>
</tr>
</thead>
<tbody><tr>
<td align="center">영화제목</td>
<td align="center">출시일</td>
<td align="center">장르</td>
<td align="center">영화시청등급</td>
<td align="center">해당 영화 총 수익</td>
<td align="center">인플레이션 반영 수익</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="center">hero</th>
<th align="center">villain</th>
<th align="center">song</th>
<th align="center">director</th>
<th align="center">hero_actor</th>
<th align="center">villian_actor</th>
</tr>
</thead>
<tbody><tr>
<td align="center">주인공</td>
<td align="center">빌런</td>
<td align="center">음악</td>
<td align="center">감독</td>
<td align="center">hero 성우</td>
<td align="center">villian 성우</td>
</tr>
</tbody></table>
<pre><code class="language-python"># ▶ 프리미엄 요금 회원일 수록 우수고객이다.
sns.distplot(df[&#39;MonthlyCharges&#39;]);</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/08c1350c-189b-4535-af1a-e6cf9a1174de/image.png" alt=""></p>
<p><code>MonthlyCharges</code>은 우수 고객 즉, 프리미엄 회원의 가입 요금을 의미하며 총 20 ~ 120 달러까지 요금을 내고 가입한 회원들이 존재하는 것으로 보인다. 구간화를 통해서 <code>MonthlyCharges</code>을 그룹핑하여 각 회원의 납부 요금 별로 이탈률을 계산해보자.</p>
<pre><code class="language-python"># ▶ 구간화
import numpy as np
df[&#39;MonthlyCharges_gp&#39;] = np.where (df[&#39;MonthlyCharges&#39;] &lt;= 40, &#39;40 이하&#39;,
                           np.where(df[&#39;MonthlyCharges&#39;] &lt;= 80, &#39;40-80 이하&#39;, &#39;80 초과&#39;))

df[[&#39;MonthlyCharges&#39;,&#39;MonthlyCharges_gp&#39;]].head(5)

    MonthlyCharges    MonthlyCharges_gp
0             29.85               40 이하
1             56.95            40-80 이하
2             53.85            40-80 이하
3             42.30            40-80 이하
4             70.70            40-80 이하</code></pre>
<pre><code class="language-python"># ▶ 프리미엄 요금 회원들이 이탈률이 더 높다.
df_gp = df.groupby(&#39;MonthlyCharges_gp&#39;)[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp[&#39;lift&#39;] = round(df_gp[&#39;ratio&#39;] / 27,1)
df_gp

            count    sum      ratio      lift
tenure_gp                
 40 이하        1838    214      11.6      0.4
40-80 이하    2539    749      29.5      1.1
 80 초과        2666    906      34.0      1.3
</code></pre>
<p>구간화를 통해 확인해본 결과 프리미엄 요금을 내는 회원들의 <code>ratio</code> 즉 이탈률이 높은 것을 확인해볼 수 있으며 요금을 적개 내는 회원들의 이탈률이 낮은 것을 확인할 수 있어다.</p>
<p>이탈률이 27프로인것에 비해서 각 요금 그룹별로 몇배 더 이탈률이 높은 것인지 확인해본 결과 80 이상의 요금을 내는 고객의 이탈률이 1.3배나 높은 것을 확인할 수 있다.</p>
<p>그래서 프리미엄 고객의 이탈률이 많아지는 이유에 대해서 생각해볼 필요가 있다.</p>
<pre><code class="language-python"># ▶ 이용개월수 및 프리미엄 요금 회원 조합에 따른 이탈률 분석
df_gp = df.groupby([&#39;tenure_gp&#39;,&#39;MonthlyCharges_gp&#39;])[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp

                              count    sum    ratio
tenure_gp    MonthlyCharges_gp            
20 이하            40 이하         873  187  21.4
                40-80 이하     1300  597  45.9
                  80 초과          705  467    66.2
20-60 이하         40 이하         674   26    3.9
                40-80 이하      922  140    15.2
                  80 초과         1162  359    30.9
60 초과            40 이하         291    1   0.3
                40-80 이하      317    12     3.8
                 80 초과          799    80    10.0</code></pre>
<p>이용개월수 및 프리미엄 요금 회원 조합에 따른 이탈률 분포에 대해서도  함께 확인해보았다.</p>
<p>가입 개월 수를 기준으로 프리미엄 요금 회원 조합에 따라서도 프리미엄 회원 가입자의 이탈률이 높은 것을 확인할 수 있다.</p>
<hr>
<br>

<br>



<h3 id="이탈-고객-특성-분석"><strong>이탈 고객 특성 분석</strong></h3>
<p>고객의 이탈률의 분포에 대한 정보는 대략적으로 확인해보았으며 그렇다면 이탈을 하는 고객의 개별적 특성을 분석해 이탈하는 고객을 분류해보고자 한다. 아래의 세 가지를 분석할 것이다.</p>
<p>**인구통계학적 특성 - 이탈률 분석</p>
<p>부가서비스 사용 - 이탈률 분석</p>
<p>계약 형태, 요금 - 이탈률 분석**</p>
<h3 id="1-인구통계학적-특성---이탈률-분석"><strong>(1) 인구통계학적 특성 - 이탈률 분석</strong></h3>
<p>먼저 전체 데이터에서 고객의 개별적 특성을 나타낼만한 데이터들인 별/실버고객/결혼여부/부양가족여부를 데이터 프레임화 한 후 막대 그래프로 분포를 확인해 보았다.</p>
<pre><code class="language-python"># ▶ 인구통계학적 특성 성별/실버고객/결혼여부/부양가족여부
df[[&#39;gender&#39;, &#39;SeniorCitizen&#39;, &#39;Partner&#39;, &#39;Dependents&#39;]].head(10)

    gender    SeniorCitizen    Partner    Dependents
0    Female                0        Yes            No
1      Male                0        No            No
2      Male                0        No            No
3      Male                0        No            No
4    Female                0        No            No
5    Female                0        No            No
6      Male                0        No           Yes
7    Female                0        No            No
8    Female                0        Yes            No
9      Male                0        No           Yes</code></pre>
<pre><code class="language-python"># ▶ gender(성별)
sns.catplot(x=&quot;gender&quot;, hue=&quot;Churn&quot;, kind=&quot;count&quot;,palette=&quot;pastel&quot;, edgecolor=&quot;.6&quot;,data=df);
plt.gcf().set_size_inches(10, 3)


df_gp = df.groupby(&#39;gender&#39;)[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp[&#39;lift&#39;] = round(df_gp[&#39;ratio&#39;] / ((df[&#39;Churn&#39;].sum() / len(df))*100) ,1)
print(df_gp)

        count  sum  ratio  lift
gender                         
Female   3488  939   26.9   1.0
Male     3555  930   26.2   1.0</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/28934e02-1e7e-4ed9-ad1e-16a7cebd1cec/image.png" alt=""></p>
<ul>
<li>성별 이탈률을 확인해보니 성별 이탈률의 차이는 거의 동일하여 큰 의미가 없는 것으로 보인다.</li>
</ul>
<br>  

<pre><code class="language-python"># ▶ SeniorCitizen(노인가구여부)
sns.catplot(x=&quot;SeniorCitizen&quot;, hue=&quot;Churn&quot;, kind=&quot;count&quot;,palette=&quot;pastel&quot;, edgecolor=&quot;.6&quot;,data=df);
plt.gcf().set_size_inches(10, 3)


df_gp = df.groupby(&#39;SeniorCitizen&#39;)[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp[&#39;lift&#39;] = round(df_gp[&#39;ratio&#39;] / ((df[&#39;Churn&#39;].sum() / len(df))*100) ,1)
print(df_gp)

               count   sum  ratio  lift
SeniorCitizen                          
0               5901  1393   23.6   0.9
1               1142   476   41.7   1.6</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/d9d325c6-23f2-4dd4-a6b4-083d33c0d18b/image.png" alt=""></p>
<ul>
<li>노인가구별 이탈률을 확인해보니 노인가구에 해당하는 가입자가 훨씬 더 높은 이탈률을 보이고 있는 것을 확인해볼 수있다. </li>
</ul>
<br> 

<pre><code class="language-python"># ▶ Partner(결혼여부)
sns.catplot(x=&quot;Partner&quot;, hue=&quot;Churn&quot;, kind=&quot;count&quot;,palette=&quot;pastel&quot;, edgecolor=&quot;.6&quot;,data=df);
plt.gcf().set_size_inches(10, 3)


df_gp = df.groupby(&#39;Partner&#39;)[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp[&#39;lift&#39;] = round(df_gp[&#39;ratio&#39;] / ((df[&#39;Churn&#39;].sum() / len(df))*100) ,1)
print(df_gp)

          count   sum  ratio  lift
Partner                          
No        3641  1200   33.0   1.2
Yes       3402   669   19.7   0.7</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/cc93073c-dca1-4019-987c-ef1868444136/image.png" alt=""></p>
<ul>
<li>결혼여부별 이탈률을 확인해보니 미혼자에 해당하는 가입자가 훨씬 더 낮은 이탈률을 보이고 있는 것을 확인해볼 수있다.</li>
</ul>
<br> 

<pre><code class="language-python"># ▶ Dependents(부양가족 여부)
sns.catplot(x=&quot;Dependents&quot;, hue=&quot;Churn&quot;, kind=&quot;count&quot;,palette=&quot;pastel&quot;, edgecolor=&quot;.6&quot;,data=df);
plt.gcf().set_size_inches(10, 3)


df_gp = df.groupby(&#39;Dependents&#39;)[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp[&#39;lift&#39;] = round(df_gp[&#39;ratio&#39;] / ((df[&#39;Churn&#39;].sum() / len(df))*100) ,1)
print(df_gp)

              count   sum  ratio  lift
Dependents                          
No           4933  1543   31.3   1.2
Yes          2110   326   15.5   0.6</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/137fa918-8bc6-4bfc-a188-ee6ab81b47b0/image.png" alt=""></p>
<ul>
<li>부양가족이 있는지 별로 이탈률을 확인해보니 부양가족이 없음에 해당하는 가입자가 훨씬 더 높은 이탈률을 보이고 있는 것을 확인해볼 수있다.</li>
</ul>
<hr>
<h3 id="2-부가서비스-사용---이탈률-분석"><strong>(2) 부가서비스 사용 - 이탈률 분석</strong></h3>
<p>먼저 전체 데이터에서 가입 시 부가서비스를 이용하는지 확인할 수 있는 데이터들인 온락인백업서비스/기기보험서비스/기술지원서비스/스트리밍TV/스트리밍영화 서비스데이터 프레임화 한 후 막대 그래프로 분포를 확인해 보았다.</p>
<pre><code class="language-python"># ▶ 부가서비스 col, 온락인백업서비스/기기보험서비스/기술지원서비스/스트리밍TV/스트리밍영화 서비스
df[[&#39;OnlineBackup&#39;, &#39;DeviceProtection&#39;, &#39;TechSupport&#39;, &#39;StreamingTV&#39;, &#39;StreamingMovies&#39;]]

    OnlineBackup    DeviceProtection    TechSupport    StreamingTV    StreamingMovies
0             Yes                  No             No             No                 No
1              No                 Yes             No             No                 No
2             Yes                  No             No             No                 No
3              No                 Yes            Yes             No                 No
4              No                  No             No             No                 No</code></pre>
<br>  

<pre><code class="language-python"># ▶ for문 활용 한 번에 출력

col_list = [&#39;OnlineBackup&#39;, &#39;DeviceProtection&#39;, &#39;TechSupport&#39;, &#39;StreamingTV&#39;, &#39;StreamingMovies&#39;]

for i in col_list :
  val = i

  sns.catplot(x=val, hue=&quot;Churn&quot;, kind=&quot;count&quot;,palette=&quot;pastel&quot;, edgecolor=&quot;.6&quot;,data=df);
  plt.gcf().set_size_inches(10, 3)


  df_gp = df.groupby(val)[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
  df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
  df_gp[&#39;lift&#39;] = round(df_gp[&#39;ratio&#39;] / ((df[&#39;Churn&#39;].sum() / len(df))*100) ,1)
  print(df_gp)
  print(&quot;---------------------------------------&quot;)

                     count   sum  ratio  lift
OnlineBackup                                 
No                    3088  1233   39.9   1.5
No internet service   1526   113    7.4   0.3
Yes                   2429   523   21.5   0.8
---------------------------------------
                     count   sum  ratio  lift
DeviceProtection                             
No                    3095  1211   39.1   1.5
No internet service   1526   113    7.4   0.3
Yes                   2422   545   22.5   0.8
---------------------------------------
                     count   sum  ratio  lift
TechSupport                                  
No                    3473  1446   41.6   1.6
No internet service   1526   113    7.4   0.3
Yes                   2044   310   15.2   0.6
---------------------------------------
                     count  sum  ratio  lift
StreamingTV                                 
No                    2810  942   33.5   1.3
No internet service   1526  113    7.4   0.3
Yes                   2707  814   30.1   1.1
---------------------------------------
                     count  sum  ratio  lift
StreamingMovies                             
No                    2785  938   33.7   1.3
No internet service   1526  113    7.4   0.3
Yes                   2732  818   29.9   1.1
---------------------------------------</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/6fc44b5d-dd33-479c-b5d7-6da6f1146383/image.png" alt="">
<img src="https://velog.velcdn.com/images/kim__1323/post/a46d4eb7-0a71-4d8a-8093-45970cc5c1e3/image.png" alt="">
<img src="https://velog.velcdn.com/images/kim__1323/post/f21161ac-d36c-49ed-bc80-b6c7d93b6d3a/image.png" alt="">
<img src="https://velog.velcdn.com/images/kim__1323/post/81c14e36-0352-4638-b422-3125ccb367c3/image.png" alt="">
<img src="https://velog.velcdn.com/images/kim__1323/post/e0544a07-e3ed-467f-9c3a-7f99bb5cbb1f/image.png" alt=""></p>
<ul>
<li><p>for문을 활용해 온락인백업서비스/기기보험서비스/기술지원서비스/스트리밍TV/스트리밍영화 서비스 의 이탈률을 한눈에 그래프로 확인해보았다.</p>
</li>
<li><p>전체적으로 서비스를 이용하지 않는 고객의 이탈률이 가장 높은 것으로 나타난다.</p>
</li>
<li><p>하지만 스트리밍 서비스, 스트리밍 영화 서비스를 이용하는 고객이나 이용하지 않는 고객의 이탈률은 크게 차이가 나지 않는 것으로 보인다.</p>
</li>
</ul>
<br> 

<p>그래서 전체적으로 부가서비스를 아예 이용하지 않는 고객의 이탈률이 낮은 것을 확인하기 위해 이탈률을 확인해보니</p>
<pre><code class="language-python"># ▶ 부가서비스를 모두 이용하지 않는 고객에 이탈률 분석
df_no = df[(df[&#39;OnlineBackup&#39;] ==&#39;No&#39;) &amp; (df[&#39;DeviceProtection&#39;] ==&#39;No&#39;) &amp; (df[&#39;TechSupport&#39;] ==&#39;No&#39;) &amp; (df[&#39;StreamingTV&#39;] ==&#39;No&#39;) &amp; (df[&#39;StreamingMovies&#39;] ==&#39;No&#39;)]
df_no[col_list]

    OnlineBackup    DeviceProtection    TechSupport    StreamingTV    StreamingMovies
4              No                  No             No             No                 No
7              No                  No             No             No                 No
10              No                  No             No             No                 No
36              No                  No             No             No                 No
...    ...    ...    ...    ...    ...
7026          No                  No             No             No                 No
7032          No                  No             No             No                 No
7033          No                  No             No             No                 No
7040          No                  No             No             No                 No
7041          No                  No             No             No                 No</code></pre>
<pre><code class="language-python"># ▶ 부가서비스 형태가 모두 No인고객은 47.6% 이탈률, Lift 약 1.8
df_no[&#39;Churn&#39;].sum() / len(df_no)</code></pre>
<ul>
<li>그래서 부가서비스를 모두 이용하지 않는 고객에 이탈률을 분석해본 결과 부가서비스를 모두 이용하지 않는 고객의 이탈률이 모두 47.6%로 절반을 보이며 부가서비스를 이용하지 않는 고객들의 이탈률이 상당히 높은 것을 확인할 수 있었다.</li>
</ul>
<br> 


<h3 id="3-계약-형태-요금---이탈률-분석-">*<em>(3) 계약 형태, 요금 - 이탈률 분석 *</em></h3>
<p>다음으로는 전체 데이터에서 계약형태를 확인할 수 있는 데이터들인 계약기간/종이없는청구/결제수단 등의 데이터를 데이터 프레임화 한 후 분포를 확인해보았다.</p>
<pre><code class="language-python"># ▶ 계약형태 col, 계약기간/종이없는청구/결제수단
df[[&#39;Contract&#39;, &#39;PaperlessBilling&#39;, &#39;PaymentMethod&#39;]]

              Contract    PaperlessBilling                   PaymentMethod
0      Month-to-month                 Yes             Electronic check
1            One year                  No                 Mailed check
2      Month-to-month                 Yes                 Mailed check
3            One year                  No    Bank transfer (automatic)
4      Month-to-month                 Yes             Electronic check
...                 ...                 ...                          ...
7038        One year                 Yes                 Mailed check
7039        One year                 Yes      Credit card (automatic)
7040  Month-to-month                 Yes               Electronic check
7041  Month-to-month                 Yes                 Mailed check
7042        Two year                 Yes    Bank transfer (automatic)</code></pre>
<br>  

<pre><code class="language-python"># ▶ for문 활용 한 번에 출력
col_list = [&#39;Contract&#39;, &#39;PaperlessBilling&#39;, &#39;PaymentMethod&#39;]

for i in col_list :
  val = i

  df_gp = df.groupby(val)[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
  df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
  df_gp[&#39;lift&#39;] = round(df_gp[&#39;ratio&#39;] / ((df[&#39;Churn&#39;].sum() / len(df))*100) ,1)
  print(df_gp)
  print(&quot;---------------------------------------------------&quot;)

                     count   sum  ratio  lift
Contract                                
Month-to-month        3875  1655   42.7   1.6
One year               1473   166   11.3   0.4
Two year              1695    48    2.8   0.1
---------------------------------------------------
                  count   sum  ratio  lift
PaperlessBilling                          
No                 2872   469   16.3   0.6
Yes                4171  1400   33.6   1.3
---------------------------------------------------
                           count   sum  ratio  lift
PaymentMethod                                      
Bank transfer (automatic)   1544   258   16.7   0.6
Credit card (automatic)     1522   232   15.2   0.6
Electronic check            2365  1071   45.3   1.7
Mailed check                1612   308   19.1   0.7
---------------------------------------------------</code></pre>
<ul>
<li><p>확인 결과 월 단위 가입자의 이탈률이 당연히 높은 것을 확인 할 수 있다.</p>
</li>
<li><p>종이 청구서를 이용하지 않는 고객의 이탈률이 더 높은 것을 확인할 수 있다.</p>
</li>
<li><p><code>Electonice check</code>를 이용해 결제하는 고객의 이탈률이 가장 많이 높은 것을 확인할 수 있다.</p>
</li>
</ul>
<p><br><br></p>
<hr>
<p><strong>결론</strong></p>
<ul>
<li><p>넷플릭스는 최근 몇 년 동안 급성장세를 보였으며 2016년을 시작으로 2020년까지 다수의 컨텐츠를 제작하고 있는 것을 확인했다.</p>
</li>
<li><p>12월에 가장 많은 컨텐츠가 추가되었으며, 10월 1월에도 비슷한 수준이지만 상대적으로 다른 월에 비해 많은 컨텐츠가 추가된 것을 확인할 수 있다.</p>
</li>
<li><p>그래서 10월~내년도 1월까지 특히 연말에 컨텐츠 추가가 집중되는 경향이 있는 것으로 보인다. 이는 연말 시즌에 맞춘 콘텐츠 업데이트 전략을 반영할 수 있을 것으로 보인다.  </p>
</li>
<li><p>넷플릭스에서 제작된 컨텐츠들은 티비쇼보다는 영화 제작에 더 집중을 하고 있으며 컨텐츠는 대부분을 성인을 타깃으로 하는 컨텐츠를 제작한 것으로 보인다.</p>
</li>
<li><p>영화 컨텐츠의 제작 길이는 80분에서 120분 사이의 길이를 가지고 있어 극장 영화보다 짧은 시간의 길이를 가지고 있다.  </p>
</li>
<li><p>아무래도 미국 회사다 보니 미국이 압도적으로 컨텐츠 제작이 많으며 그 다음은 영화산업이 발달 돼 있는 인도가 위치해 있으며 그 다음으로 영국, 일본이 뒤를 이었다. 이는 넷플릭스의 컨텐츠가 주로 이 세 국가에서 제작됐음을 확인할 수 있다.</p>
</li>
</ul>
<ul>
<li>장르별 컨텐츠의 분포를 확인해보니 가장 인기 있는 장르는 <code>&#39;International Movies&#39;</code> 국제 영화와 <code>&#39;Dramas&#39;</code> 드라마로 나타났디. 이는 넷플릭스가 국제적인 시청자를 대상으로 다양한 영화를 제공하고 있음을 시사한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로젝트] 통신사 이탈 고객 분석
]]></title>
            <link>https://velog.io/@kim__1323/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%86%B5%EC%8B%A0%EC%82%AC-%EC%9D%B4%ED%83%88-%EA%B3%A0%EA%B0%9D-%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@kim__1323/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%86%B5%EC%8B%A0%EC%82%AC-%EC%9D%B4%ED%83%88-%EA%B3%A0%EA%B0%9D-%EB%B6%84%EC%84%9D</guid>
            <pubDate>Sat, 27 Jul 2024 06:23:14 GMT</pubDate>
            <description><![CDATA[<p>아래는 A 통신사가 다양한 경쟁사가 등장함에 따라 M/S가 떨어지고 있고, 타 통신사로의 이탈률이 높아져가는 상황에서 이탈 가능성이 높은 고객을 분석하는 프로젝트이다.</p>
<br>

<h3 id="문제-상황은-이렇다"><strong>문제 상황은 이렇다:</strong></h3>
<p>🔖
A통신사는 업계 1위의 통신사였으나 그 명성이 무색해져가고 있다. 다양한 경쟁사가 등장함에 따라 M/S가 떨어지고 있고, 타 통신사로의 이탈 고객이<br>증가하고 있는 상황이다. 이에 이탈 가능성이 높은 고객을 예측하고, 해지 방어 활동들을 전개하려고 한다.</p>
<p><strong>⛳ 문제정의</strong></p>
<p>▶ 고객 이탈</p>
<p><strong>⛳ 기대효과</strong></p>
<p>▶ 고객 이탈 방어</p>
<p><strong>⛳ 해결방안</strong></p>
<p>▶ 이탈 가능성 높은 고객 예측 및 해지 방어</p>
<p><strong>⛳ 성과측정</strong></p>
<p>▶ 이탈 가능성 높은 고객에 대해 관리 전/후 이탈률 모니터링</p>
<p><strong>⛳ 운영</strong></p>
<p>▶ Model에 Input하기 위한 Data mart 생성</p>
<hr>
<h3 id="데이터셋-정의"><strong>데이터셋 정의</strong></h3>
<BR>

<pre><code class="language-python"># ▶ Data read
import pandas as pd
df = pd.read_csv(&#39;S_PJT13_DATA.csv&#39;)
df.head()

  customerID  gender  SeniorCitizen Partner Dependents  tenure PhoneService  \
0 7590-VHVEG  Female              0     Yes         No       1           No   
1 5575-GNVDE    Male              0      No         No      34          Yes   
2 3668-QPYBK    Male              0      No         No       2          Yes   
3 7795-CFOCW    Male              0      No         No      45           No   
4 9237-HQITU  Female              0      No         No       2          Yes   

    MultipleLines InternetService OnlineSecurity  ... DeviceProtection  \
0  No phone service            DSL             No  ...               No   
1                No            DSL            Yes  ...              Yes   
2                No            DSL            Yes  ...               No   
3  No phone service            DSL            Yes  ...              Yes   
4                No    Fiber optic             No  ...               No   

  TechSupport StreamingTV StreamingMovies      Contract PaperlessBilling  \
0          No          No              No  Month-to-month             Yes   
1          No          No              No       One year              No   
2          No          No              No  Month-to-month             Yes   
3         Yes          No              No       One year              No   
4          No          No              No  Month-to-month             Yes   

             PaymentMethod  MonthlyCharges  TotalCharges Churn  
0           Electronic check           29.85         29.85    No  
1               Mailed check           56.95       1889.50    No  
2               Mailed check           53.85        108.15   Yes  
3  Bank transfer (automatic)           42.30       1840.75    No  
4           Electronic check           70.70        151.65   Yes  

[5 rows x 21 columns]


</code></pre>
<br>


<table>
<thead>
<tr>
<th align="center">customerID</th>
<th align="center">gender</th>
<th align="center">SeniorCitizen</th>
<th align="center">Partner</th>
<th align="center">Dependents</th>
<th align="center">tenure</th>
</tr>
</thead>
<tbody><tr>
<td align="center">고객ID</td>
<td align="center">성별</td>
<td align="center">노인여부</td>
<td align="center">결혼여부</td>
<td align="center">부양가족여부</td>
<td align="center">회원개월수</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="center">PhoneService</th>
<th align="center">MultipleLines</th>
<th align="center">InternetService</th>
<th align="center">OnlineSecurity</th>
<th align="center">OnlineBackup</th>
<th align="center">DeviceProtection</th>
</tr>
</thead>
<tbody><tr>
<td align="center">전화서비스 여부</td>
<td align="center">다회선 여부</td>
<td align="center">인터넷 서비스 공급자</td>
<td align="center">온라인 보안 여부</td>
<td align="center">온라인 백업 여부</td>
<td align="center">기기보험여부</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="center">TechSupport</th>
<th align="center">StreamingTV</th>
<th align="center">StreamingMovies</th>
<th align="center">Contract</th>
<th align="center">PaperlessBilling</th>
<th align="center">PaymentMethod</th>
</tr>
</thead>
<tbody><tr>
<td align="center">기술지원여부</td>
<td align="center">스트리밍TV여부</td>
<td align="center">스트리밍영화여부</td>
<td align="center">계약기간</td>
<td align="center">종이없는청구여부</td>
<td align="center">결제수단</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="center">MonthlyCharges</th>
<th align="center">TotalCharges</th>
<th align="center">Churn</th>
</tr>
</thead>
<tbody><tr>
<td align="center">월청구금액</td>
<td align="center">청구된 총 금액</td>
<td align="center">이탈여부</td>
</tr>
<tr>
<td align="center"><strong><strong>__</strong></strong></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<h3 id="데이터-전처리-및-eda"><strong>데이터 전처리 및 EDA</strong></h3>
<h3 id="1-data-shape형태-확인">(1) Data shape(형태) 확인</h3>
<pre><code class="language-python"># ▶ Data 형태 확인
# ▶ 7,043 row, 21 col로 구성됨
print(&#39;df&#39;, df.shape)

&gt; df (7787, 12)</code></pre>
<ul>
<li>7,043 개의 데이터가 있으며 21개의 컬럼으로 이루어져 있음.</li>
</ul>
<h3 id="2-data-type-확인">(2) Data type 확인</h3>
<pre><code class="language-python"># ▶ Data type 확인
df.info()

&lt;class &#39;pandas.core.frame.DataFrame&#39;&gt;
RangeIndex: 7043 entries, 0 to 7042
Data columns (total 21 columns):
 #   Column            Non-Null Count  Dtype  
---  ------            --------------  -----  
 0   customerID        7043 non-null   object 
 1   gender            7043 non-null   object 
 2   SeniorCitizen     7043 non-null   int64  
 3   Partner           7043 non-null   object 
 4   Dependents        7043 non-null   object 
 5   tenure            7043 non-null   int64  
 6   PhoneService      7043 non-null   object 
 7   MultipleLines     7043 non-null   object 
 8   InternetService   7043 non-null   object 
 9   OnlineSecurity    7043 non-null   object 
 10  OnlineBackup      7043 non-null   object 
 11  DeviceProtection  7043 non-null   object 
 12  TechSupport       7043 non-null   object 
 13  StreamingTV       7043 non-null   object 
 14  StreamingMovies   7043 non-null   object 
 15  Contract          7043 non-null   object 
 16  PaperlessBilling  7043 non-null   object 
 17  PaymentMethod     7043 non-null   object 
 18  MonthlyCharges    7043 non-null   float64
 19  TotalCharges      7043 non-null   object 
 20  Churn             7043 non-null   object 
dtypes: float64(1), int64(2), object(18)
memory usage: 1.1+ MB</code></pre>
<p>총 21개의 열 데이터가 있으며 <code>TotalCharges</code>라는 숫자형이나 날짜형 타입의 데이터가 <code>object</code> 문자형 데이터로 선언이 돼있다.</p>
<p>필요에 따라 데이터 분석 진행 시 <code>int</code> 타입으로 변경시키는게 좋을 거 같다.</p>
<h3 id="3-null값-확인--빈-값의-data">(3) Null값 확인 (※ 빈 값의 Data)</h3>
<pre><code class="language-python">customerID          0
gender              0
SeniorCitizen       0
Partner             0
Dependents          0
tenure              0
PhoneService        0
MultipleLines       0
InternetService     0
OnlineSecurity      0
OnlineBackup        0
DeviceProtection    0
TechSupport         0
StreamingTV         0
StreamingMovies     0
Contract            0
PaperlessBilling    0
PaymentMethod       0
MonthlyCharges      0
TotalCharges        0
Churn               0
dtype: int64</code></pre>
<ul>
<li><code>null</code>값이 존재하지 않는 데이터이다.</li>
</ul>
<br>



<h3 id="4-outlier-확인-이상치-정상적인-범주를-벗어난-data"><strong>(4) Outlier 확인 (이상치, 정상적인 범주를 벗어난 Data)</strong></h3>
<pre><code class="language-python"># ▶ Outlier 확인
df.describe()

      SeniorCitizen         tenure        MonthlyCharges
count    7043.000000    7043.000000           7043.000000
mean       0.162147      32.371149             64.761692
std           0.368612      24.559481             30.090047
min           0.000000       0.000000             18.250000
25%           0.000000       9.000000             35.500000
50%           0.000000      29.000000             70.350000
75%           0.000000      55.000000             89.850000
max           1.000000      72.000000            118.750000  
</code></pre>
<ul>
<li><code>min</code> 값을 확인해 outlier를 확인해본 결과 존재하지 않는 것으로 보인다.</li>
</ul>
<h3 id="5-데이터-eda"><strong>(5) 데이터 EDA</strong></h3>
<p><strong>우량고객 분포 탐색</strong></p>
<p>우량고객이라 함은 이탈률이 낮은 고객군을 의미한다. 그래서 이탈여부를 나타내는 <code>Churn</code> 열의 데이터로 이탈률을 계산하고 이후 장기회원과 프리미엄 요금 이용 회원으로 우량고객의 분포를 살펴보고자 한다.</p>
<pre><code class="language-python"># ▶ 우량고객이라 함은 이탈률이 낮은 고객군
df[&#39;Churn&#39;].value_counts()

Churn
No     5174
Yes    1869
Name: count, dtype: int64</code></pre>
<p>이탈하지 않은 고객은 5174명, 이탈한 고객은 1869명이라고 할 수 있다. 이를 아래와 같이 비율로 나타내 이탈률을 계산해보면</p>
<pre><code class="language-python"># ▶ 약 27%의 이탈률
1869 / (1869 + 5174)

0.2653698707936959</code></pre>
<p>전체 고객에서 이탈률은 총 27프로로 나타났다.</p>
<p>이후 데이터 모델링이 필요할 경우를 생각해 <code>Churn</code>  타겟 데이터를 숫자 데이터 0, 1로 변환했다.</p>
<pre><code># ▶ Target 숫자 데이터로 변환
import numpy as np
df[&#39;Churn&#39;] = np.where(df[&#39;Churn&#39;] == &#39;Yes&#39;, 1, 0)
df[&#39;Churn&#39;].value_counts()

Churn
0    5174
1    1869
Name: count, dtype: int64</code></pre><br>

<p>(1) 장기회원</p>
<p>더 이상의 고객의 이탈률을 낮추기 위해 현재 존재하는 장기회원의 이탈률을 더 낮춰야 할 것이다 </p>
<p>따라서 우리는 장기고객일수록 이탈률이 낮을 것이다 </p>
<p>라는 가설을 바탕으로 장기회원의 분포를 살펴보고자 한다.</p>
<pre><code class="language-python"># ▶ 가설1) 장기고객일수록 이탈률이 낮을 것이다.
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
plt.style.use([&#39;dark_background&#39;])

sns.distplot(df[&#39;tenure&#39;]);</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/6a5fd886-4610-4b7b-8698-3d2af05451b6/image.png" alt=""></p>
<p><code>tenure</code>은 회원의 가입 개월 수를 의미하며 총 0~ 70개월까지 가입한 회원들이 존재하는 것으로 보인다. 구간화를 통해서 <code>tenure</code>을 그룹핑하여 각 회원의 가입 개월 수 별로 이탈률을 계산해보자.</p>
<pre><code class="language-python"># ▶ 구간화
import numpy as np
df[&#39;tenure_gp&#39;] = np.where (df[&#39;tenure&#39;] &lt;= 20, &#39;20 이하&#39;,
                           np.where(df[&#39;tenure&#39;] &lt;= 60, &#39;20-60 이하&#39;, &#39;60 초과&#39;))

df[[&#39;tenure&#39;,&#39;tenure_gp&#39;]].head(5)

&gt; tenure    tenure_gp
0       1       20 이하
1      34    20-60 이하
2       2       20 이하
3      45    20-60 이하
4       2       20 이하
</code></pre>
<pre><code class="language-python"># ▶ 장기고객일 수록 우수고객이다.
df_gp = df.groupby(&#39;tenure_gp&#39;)[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp[&#39;lift&#39;] = round(df_gp[&#39;ratio&#39;] / 27,1)
df_gp

            count    sum     ratio    lift
tenure_gp                
 20 이하        2878  1251     43.5    1.6
20-60 이하    2758   525     19.0    0.7
 60 초과        1407    93      6.6    0.2</code></pre>
<p>구간화를 통해 확인해본 결과 가입 개월 수가 낮을 수록 <code>ratio</code> 즉 이탈률이 높은 것을 확인해볼 수 있으며 가입 개월수가 늘어날 수록 이탈률이 낮은 것을 확인할 수 있다.</p>
<p>이탈률이 27프로인것에 비해서 각 개월 수 그룹별로 몇배 더 이탈률이 높은 것인지 확인해본 결과 20개월 이하는 1.6배나 높은 것을 확인할 수 있다.</p>
<br>

<p><strong>(2) 프리미엄 요금 회원</strong></p>
<pre><code class="language-python"># ▶ 프리미엄 요금 회원일 수록 우수고객이다.
sns.distplot(df[&#39;MonthlyCharges&#39;]);</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/08c1350c-189b-4535-af1a-e6cf9a1174de/image.png" alt=""></p>
<p><code>MonthlyCharges</code>은 우수 고객 즉, 프리미엄 회원의 가입 요금을 의미하며 총 20 ~ 120 달러까지 요금을 내고 가입한 회원들이 존재하는 것으로 보인다. 구간화를 통해서 <code>MonthlyCharges</code>을 그룹핑하여 각 회원의 납부 요금 별로 이탈률을 계산해보자.</p>
<pre><code class="language-python"># ▶ 구간화
import numpy as np
df[&#39;MonthlyCharges_gp&#39;] = np.where (df[&#39;MonthlyCharges&#39;] &lt;= 40, &#39;40 이하&#39;,
                           np.where(df[&#39;MonthlyCharges&#39;] &lt;= 80, &#39;40-80 이하&#39;, &#39;80 초과&#39;))

df[[&#39;MonthlyCharges&#39;,&#39;MonthlyCharges_gp&#39;]].head(5)

    MonthlyCharges    MonthlyCharges_gp
0             29.85               40 이하
1             56.95            40-80 이하
2             53.85            40-80 이하
3             42.30            40-80 이하
4             70.70            40-80 이하</code></pre>
<pre><code class="language-python"># ▶ 프리미엄 요금 회원들이 이탈률이 더 높다.
df_gp = df.groupby(&#39;MonthlyCharges_gp&#39;)[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp[&#39;lift&#39;] = round(df_gp[&#39;ratio&#39;] / 27,1)
df_gp

            count    sum      ratio      lift
tenure_gp                
 40 이하        1838    214      11.6      0.4
40-80 이하    2539    749      29.5      1.1
 80 초과        2666    906      34.0      1.3
</code></pre>
<p>구간화를 통해 확인해본 결과 프리미엄 요금을 내는 회원들의 <code>ratio</code> 즉 이탈률이 높은 것을 확인해볼 수 있으며 요금을 적개 내는 회원들의 이탈률이 낮은 것을 확인할 수 있어다.</p>
<p>이탈률이 27프로인것에 비해서 각 요금 그룹별로 몇배 더 이탈률이 높은 것인지 확인해본 결과 80 이상의 요금을 내는 고객의 이탈률이 1.3배나 높은 것을 확인할 수 있다.</p>
<p>그래서 프리미엄 고객의 이탈률이 많아지는 이유에 대해서 생각해볼 필요가 있다.</p>
<pre><code class="language-python"># ▶ 이용개월수 및 프리미엄 요금 회원 조합에 따른 이탈률 분석
df_gp = df.groupby([&#39;tenure_gp&#39;,&#39;MonthlyCharges_gp&#39;])[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp

                              count    sum    ratio
tenure_gp    MonthlyCharges_gp            
20 이하            40 이하         873  187  21.4
                40-80 이하     1300  597  45.9
                  80 초과          705  467    66.2
20-60 이하         40 이하         674   26    3.9
                40-80 이하      922  140    15.2
                  80 초과         1162  359    30.9
60 초과            40 이하         291    1   0.3
                40-80 이하      317    12     3.8
                 80 초과          799    80    10.0</code></pre>
<p>이용개월수 및 프리미엄 요금 회원 조합에 따른 이탈률 분포에 대해서도  함께 확인해보았다.</p>
<p>가입 개월 수를 기준으로 프리미엄 요금 회원 조합에 따라서도 프리미엄 회원 가입자의 이탈률이 높은 것을 확인할 수 있다.</p>
<hr>
<br>

<br>



<h3 id="이탈-고객-특성-분석"><strong>이탈 고객 특성 분석</strong></h3>
<p>고객의 이탈률의 분포에 대한 정보는 대략적으로 확인해보았으며 그렇다면 이탈을 하는 고객의 개별적 특성을 분석해 이탈하는 고객을 분류해보고자 한다. 아래의 세 가지를 분석할 것이다.</p>
<p>**인구통계학적 특성 - 이탈률 분석</p>
<p>부가서비스 사용 - 이탈률 분석</p>
<p>계약 형태, 요금 - 이탈률 분석**</p>
<h3 id="1-인구통계학적-특성---이탈률-분석"><strong>(1) 인구통계학적 특성 - 이탈률 분석</strong></h3>
<p>먼저 전체 데이터에서 고객의 개별적 특성을 나타낼만한 데이터들인 별/실버고객/결혼여부/부양가족여부를 데이터 프레임화 한 후 막대 그래프로 분포를 확인해 보았다.</p>
<pre><code class="language-python"># ▶ 인구통계학적 특성 성별/실버고객/결혼여부/부양가족여부
df[[&#39;gender&#39;, &#39;SeniorCitizen&#39;, &#39;Partner&#39;, &#39;Dependents&#39;]].head(10)

    gender    SeniorCitizen    Partner    Dependents
0    Female                0        Yes            No
1      Male                0        No            No
2      Male                0        No            No
3      Male                0        No            No
4    Female                0        No            No
5    Female                0        No            No
6      Male                0        No           Yes
7    Female                0        No            No
8    Female                0        Yes            No
9      Male                0        No           Yes</code></pre>
<pre><code class="language-python"># ▶ gender(성별)
sns.catplot(x=&quot;gender&quot;, hue=&quot;Churn&quot;, kind=&quot;count&quot;,palette=&quot;pastel&quot;, edgecolor=&quot;.6&quot;,data=df);
plt.gcf().set_size_inches(10, 3)


df_gp = df.groupby(&#39;gender&#39;)[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp[&#39;lift&#39;] = round(df_gp[&#39;ratio&#39;] / ((df[&#39;Churn&#39;].sum() / len(df))*100) ,1)
print(df_gp)

        count  sum  ratio  lift
gender                         
Female   3488  939   26.9   1.0
Male     3555  930   26.2   1.0</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/28934e02-1e7e-4ed9-ad1e-16a7cebd1cec/image.png" alt=""></p>
<ul>
<li>성별 이탈률을 확인해보니 성별 이탈률의 차이는 거의 동일하여 큰 의미가 없는 것으로 보인다.</li>
</ul>
<br>  

<pre><code class="language-python"># ▶ SeniorCitizen(노인가구여부)
sns.catplot(x=&quot;SeniorCitizen&quot;, hue=&quot;Churn&quot;, kind=&quot;count&quot;,palette=&quot;pastel&quot;, edgecolor=&quot;.6&quot;,data=df);
plt.gcf().set_size_inches(10, 3)


df_gp = df.groupby(&#39;SeniorCitizen&#39;)[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp[&#39;lift&#39;] = round(df_gp[&#39;ratio&#39;] / ((df[&#39;Churn&#39;].sum() / len(df))*100) ,1)
print(df_gp)

               count   sum  ratio  lift
SeniorCitizen                          
0               5901  1393   23.6   0.9
1               1142   476   41.7   1.6</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/d9d325c6-23f2-4dd4-a6b4-083d33c0d18b/image.png" alt=""></p>
<ul>
<li>노인가구별 이탈률을 확인해보니 노인가구에 해당하는 가입자가 훨씬 더 높은 이탈률을 보이고 있는 것을 확인해볼 수있다. </li>
</ul>
<br> 

<pre><code class="language-python"># ▶ Partner(결혼여부)
sns.catplot(x=&quot;Partner&quot;, hue=&quot;Churn&quot;, kind=&quot;count&quot;,palette=&quot;pastel&quot;, edgecolor=&quot;.6&quot;,data=df);
plt.gcf().set_size_inches(10, 3)


df_gp = df.groupby(&#39;Partner&#39;)[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp[&#39;lift&#39;] = round(df_gp[&#39;ratio&#39;] / ((df[&#39;Churn&#39;].sum() / len(df))*100) ,1)
print(df_gp)

          count   sum  ratio  lift
Partner                          
No        3641  1200   33.0   1.2
Yes       3402   669   19.7   0.7</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/cc93073c-dca1-4019-987c-ef1868444136/image.png" alt=""></p>
<ul>
<li>결혼여부별 이탈률을 확인해보니 미혼자에 해당하는 가입자가 훨씬 더 낮은 이탈률을 보이고 있는 것을 확인해볼 수있다.</li>
</ul>
<br> 

<pre><code class="language-python"># ▶ Dependents(부양가족 여부)
sns.catplot(x=&quot;Dependents&quot;, hue=&quot;Churn&quot;, kind=&quot;count&quot;,palette=&quot;pastel&quot;, edgecolor=&quot;.6&quot;,data=df);
plt.gcf().set_size_inches(10, 3)


df_gp = df.groupby(&#39;Dependents&#39;)[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp[&#39;lift&#39;] = round(df_gp[&#39;ratio&#39;] / ((df[&#39;Churn&#39;].sum() / len(df))*100) ,1)
print(df_gp)

              count   sum  ratio  lift
Dependents                          
No           4933  1543   31.3   1.2
Yes          2110   326   15.5   0.6</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/137fa918-8bc6-4bfc-a188-ee6ab81b47b0/image.png" alt=""></p>
<ul>
<li>부양가족이 있는지 별로 이탈률을 확인해보니 부양가족이 없음에 해당하는 가입자가 훨씬 더 높은 이탈률을 보이고 있는 것을 확인해볼 수있다.</li>
</ul>
<hr>
<h3 id="2-부가서비스-사용---이탈률-분석"><strong>(2) 부가서비스 사용 - 이탈률 분석</strong></h3>
<p>먼저 전체 데이터에서 가입 시 부가서비스를 이용하는지 확인할 수 있는 데이터들인 온락인백업서비스/기기보험서비스/기술지원서비스/스트리밍TV/스트리밍영화 서비스데이터 프레임화 한 후 막대 그래프로 분포를 확인해 보았다.</p>
<pre><code class="language-python"># ▶ 부가서비스 col, 온락인백업서비스/기기보험서비스/기술지원서비스/스트리밍TV/스트리밍영화 서비스
df[[&#39;OnlineBackup&#39;, &#39;DeviceProtection&#39;, &#39;TechSupport&#39;, &#39;StreamingTV&#39;, &#39;StreamingMovies&#39;]]

    OnlineBackup    DeviceProtection    TechSupport    StreamingTV    StreamingMovies
0             Yes                  No             No             No                 No
1              No                 Yes             No             No                 No
2             Yes                  No             No             No                 No
3              No                 Yes            Yes             No                 No
4              No                  No             No             No                 No</code></pre>
<br>  

<pre><code class="language-python"># ▶ for문 활용 한 번에 출력

col_list = [&#39;OnlineBackup&#39;, &#39;DeviceProtection&#39;, &#39;TechSupport&#39;, &#39;StreamingTV&#39;, &#39;StreamingMovies&#39;]

for i in col_list :
  val = i

  sns.catplot(x=val, hue=&quot;Churn&quot;, kind=&quot;count&quot;,palette=&quot;pastel&quot;, edgecolor=&quot;.6&quot;,data=df);
  plt.gcf().set_size_inches(10, 3)


  df_gp = df.groupby(val)[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
  df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
  df_gp[&#39;lift&#39;] = round(df_gp[&#39;ratio&#39;] / ((df[&#39;Churn&#39;].sum() / len(df))*100) ,1)
  print(df_gp)
  print(&quot;---------------------------------------&quot;)

                     count   sum  ratio  lift
OnlineBackup                                 
No                    3088  1233   39.9   1.5
No internet service   1526   113    7.4   0.3
Yes                   2429   523   21.5   0.8
---------------------------------------
                     count   sum  ratio  lift
DeviceProtection                             
No                    3095  1211   39.1   1.5
No internet service   1526   113    7.4   0.3
Yes                   2422   545   22.5   0.8
---------------------------------------
                     count   sum  ratio  lift
TechSupport                                  
No                    3473  1446   41.6   1.6
No internet service   1526   113    7.4   0.3
Yes                   2044   310   15.2   0.6
---------------------------------------
                     count  sum  ratio  lift
StreamingTV                                 
No                    2810  942   33.5   1.3
No internet service   1526  113    7.4   0.3
Yes                   2707  814   30.1   1.1
---------------------------------------
                     count  sum  ratio  lift
StreamingMovies                             
No                    2785  938   33.7   1.3
No internet service   1526  113    7.4   0.3
Yes                   2732  818   29.9   1.1
---------------------------------------</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/6fc44b5d-dd33-479c-b5d7-6da6f1146383/image.png" alt="">
<img src="https://velog.velcdn.com/images/kim__1323/post/a46d4eb7-0a71-4d8a-8093-45970cc5c1e3/image.png" alt="">
<img src="https://velog.velcdn.com/images/kim__1323/post/f21161ac-d36c-49ed-bc80-b6c7d93b6d3a/image.png" alt="">
<img src="https://velog.velcdn.com/images/kim__1323/post/81c14e36-0352-4638-b422-3125ccb367c3/image.png" alt="">
<img src="https://velog.velcdn.com/images/kim__1323/post/e0544a07-e3ed-467f-9c3a-7f99bb5cbb1f/image.png" alt=""></p>
<ul>
<li><p>for문을 활용해 온락인백업서비스/기기보험서비스/기술지원서비스/스트리밍TV/스트리밍영화 서비스 의 이탈률을 한눈에 그래프로 확인해보았다.</p>
</li>
<li><p>전체적으로 서비스를 이용하지 않는 고객의 이탈률이 가장 높은 것으로 나타난다.</p>
</li>
<li><p>하지만 스트리밍 서비스, 스트리밍 영화 서비스를 이용하는 고객이나 이용하지 않는 고객의 이탈률은 크게 차이가 나지 않는 것으로 보인다.</p>
</li>
</ul>
<br> 

<p>그래서 전체적으로 부가서비스를 아예 이용하지 않는 고객의 이탈률이 낮은 것을 확인하기 위해 이탈률을 확인해보니</p>
<pre><code class="language-python"># ▶ 부가서비스를 모두 이용하지 않는 고객에 이탈률 분석
df_no = df[(df[&#39;OnlineBackup&#39;] ==&#39;No&#39;) &amp; (df[&#39;DeviceProtection&#39;] ==&#39;No&#39;) &amp; (df[&#39;TechSupport&#39;] ==&#39;No&#39;) &amp; (df[&#39;StreamingTV&#39;] ==&#39;No&#39;) &amp; (df[&#39;StreamingMovies&#39;] ==&#39;No&#39;)]
df_no[col_list]

    OnlineBackup    DeviceProtection    TechSupport    StreamingTV    StreamingMovies
4              No                  No             No             No                 No
7              No                  No             No             No                 No
10              No                  No             No             No                 No
36              No                  No             No             No                 No
...    ...    ...    ...    ...    ...
7026          No                  No             No             No                 No
7032          No                  No             No             No                 No
7033          No                  No             No             No                 No
7040          No                  No             No             No                 No
7041          No                  No             No             No                 No</code></pre>
<pre><code class="language-python"># ▶ 부가서비스 형태가 모두 No인고객은 47.6% 이탈률, Lift 약 1.8
df_no[&#39;Churn&#39;].sum() / len(df_no)</code></pre>
<ul>
<li>그래서 부가서비스를 모두 이용하지 않는 고객에 이탈률을 분석해본 결과 부가서비스를 모두 이용하지 않는 고객의 이탈률이 모두 47.6%로 절반을 보이며 부가서비스를 이용하지 않는 고객들의 이탈률이 상당히 높은 것을 확인할 수 있었다.</li>
</ul>
<br> 


<h3 id="3-계약-형태-요금---이탈률-분석-">*<em>(3) 계약 형태, 요금 - 이탈률 분석 *</em></h3>
<p>다음으로는 전체 데이터에서 계약형태를 확인할 수 있는 데이터들인 계약기간/종이없는청구/결제수단 등의 데이터를 데이터 프레임화 한 후 분포를 확인해보았다.</p>
<pre><code class="language-python"># ▶ 계약형태 col, 계약기간/종이없는청구/결제수단
df[[&#39;Contract&#39;, &#39;PaperlessBilling&#39;, &#39;PaymentMethod&#39;]]

              Contract    PaperlessBilling                   PaymentMethod
0      Month-to-month                 Yes             Electronic check
1            One year                  No                 Mailed check
2      Month-to-month                 Yes                 Mailed check
3            One year                  No    Bank transfer (automatic)
4      Month-to-month                 Yes             Electronic check
...                 ...                 ...                          ...
7038        One year                 Yes                 Mailed check
7039        One year                 Yes      Credit card (automatic)
7040  Month-to-month                 Yes               Electronic check
7041  Month-to-month                 Yes                 Mailed check
7042        Two year                 Yes    Bank transfer (automatic)</code></pre>
<br>  

<pre><code class="language-python"># ▶ for문 활용 한 번에 출력
col_list = [&#39;Contract&#39;, &#39;PaperlessBilling&#39;, &#39;PaymentMethod&#39;]

for i in col_list :
  val = i

  df_gp = df.groupby(val)[&#39;Churn&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
  df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
  df_gp[&#39;lift&#39;] = round(df_gp[&#39;ratio&#39;] / ((df[&#39;Churn&#39;].sum() / len(df))*100) ,1)
  print(df_gp)
  print(&quot;---------------------------------------------------&quot;)

                     count   sum  ratio  lift
Contract                                
Month-to-month        3875  1655   42.7   1.6
One year               1473   166   11.3   0.4
Two year              1695    48    2.8   0.1
---------------------------------------------------
                  count   sum  ratio  lift
PaperlessBilling                          
No                 2872   469   16.3   0.6
Yes                4171  1400   33.6   1.3
---------------------------------------------------
                           count   sum  ratio  lift
PaymentMethod                                      
Bank transfer (automatic)   1544   258   16.7   0.6
Credit card (automatic)     1522   232   15.2   0.6
Electronic check            2365  1071   45.3   1.7
Mailed check                1612   308   19.1   0.7
---------------------------------------------------</code></pre>
<ul>
<li><p>확인 결과 월 단위 가입자의 이탈률이 당연히 높은 것을 확인 할 수 있다.</p>
</li>
<li><p>종이 청구서를 이용하지 않는 고객의 이탈률이 더 높은 것을 확인할 수 있다.</p>
</li>
<li><p><code>Electonice check</code>를 이용해 결제하는 고객의 이탈률이 가장 많이 높은 것을 확인할 수 있다.</p>
</li>
</ul>
<p><br><br></p>
<hr>
<p><strong>결론</strong></p>
<ul>
<li><p>넷플릭스는 최근 몇 년 동안 급성장세를 보였으며 2016년을 시작으로 2020년까지 다수의 컨텐츠를 제작하고 있는 것을 확인했다.</p>
</li>
<li><p>12월에 가장 많은 컨텐츠가 추가되었으며, 10월 1월에도 비슷한 수준이지만 상대적으로 다른 월에 비해 많은 컨텐츠가 추가된 것을 확인할 수 있다.</p>
</li>
<li><p>그래서 10월~내년도 1월까지 특히 연말에 컨텐츠 추가가 집중되는 경향이 있는 것으로 보인다. 이는 연말 시즌에 맞춘 콘텐츠 업데이트 전략을 반영할 수 있을 것으로 보인다.  </p>
</li>
<li><p>넷플릭스에서 제작된 컨텐츠들은 티비쇼보다는 영화 제작에 더 집중을 하고 있으며 컨텐츠는 대부분을 성인을 타깃으로 하는 컨텐츠를 제작한 것으로 보인다.</p>
</li>
<li><p>영화 컨텐츠의 제작 길이는 80분에서 120분 사이의 길이를 가지고 있어 극장 영화보다 짧은 시간의 길이를 가지고 있다.  </p>
</li>
<li><p>아무래도 미국 회사다 보니 미국이 압도적으로 컨텐츠 제작이 많으며 그 다음은 영화산업이 발달 돼 있는 인도가 위치해 있으며 그 다음으로 영국, 일본이 뒤를 이었다. 이는 넷플릭스의 컨텐츠가 주로 이 세 국가에서 제작됐음을 확인할 수 있다.</p>
</li>
</ul>
<ul>
<li>장르별 컨텐츠의 분포를 확인해보니 가장 인기 있는 장르는 <code>&#39;International Movies&#39;</code> 국제 영화와 <code>&#39;Dramas&#39;</code> 드라마로 나타났디. 이는 넷플릭스가 국제적인 시청자를 대상으로 다양한 영화를 제공하고 있음을 시사한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[list comprehenssion 2]]></title>
            <link>https://velog.io/@kim__1323/list-comprehenssion-2</link>
            <guid>https://velog.io/@kim__1323/list-comprehenssion-2</guid>
            <pubDate>Mon, 22 Jul 2024 07:32:43 GMT</pubDate>
            <description><![CDATA[<h1 id="리스트-컴프리헨션의-활용-방법">리스트 컴프리헨션의 활용 방법</h1>
<p>리스트 컴프리헨션은 파이썬에서 리스트를 간결하게 생성할 수 있는 강력한 문법이다. 특히 <code>if</code>문을 결합하여 특정 조건에 맞는 값만 필터링할 수 있다.</p>
<h2 id="기본-예시">기본 예시</h2>
<ol>
<li><strong>0부터 19까지의 리스트 생성</strong>:<pre><code class="language-python"> numbers = [i for i in range(20)]
 print(numbers)  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]</code></pre>
</li>
</ol>
<br>

<ol start="2">
<li><strong>0부터 19까지의 홀수 리스트 생성</strong>:<pre><code class="language-python"> # 일반적인 반복문과 if문을 사용한 경우
 odd_numbers = []
 for i in range(20):
     if i % 2 == 1:
         odd_numbers.append(i)
 print(odd_numbers)  # [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]</code></pre>
</li>
</ol>
<br>

<ol start="3">
<li><strong>리스트 컴프리헨션을 사용한 경우</strong>:<pre><code class="language-python"> odd_numbers = [i for i in range(20) if i % 2 == 1]
 print(odd_numbers)  # [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]</code></pre>
<ul>
<li><code>for i in range(20) if i % 2 == 1</code>: 0부터 19까지의 숫자 중 홀수만 리스트에 추가</li>
<li>단 한 줄로 리스트 생성</li>
</ul>
</li>
</ol>
<p><br><br></p>
<h2 id="조건부-리스트-컴프리헨션">조건부 리스트 컴프리헨션</h2>
<p>리스트 컴프리헨션에 <code>if</code>문을 결합하여 특정 조건에 맞는 값만을 필터링할 수 있다.</p>
<ol start="4">
<li><strong>짝수 리스트 생성</strong>:<pre><code class="language-python"> even_numbers = [i for i in range(20) if i % 2 == 0]
 print(even_numbers)  # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]</code></pre>
<ul>
<li><code>if i % 2 == 0</code>: 조건을 추가하여 짝수만 리스트에 포함</li>
</ul>
</li>
</ol>
<p><br><br></p>
<h2 id="복잡한-문제-해결">복잡한 문제 해결</h2>
<p>더 복잡한 데이터 처리에도 리스트 컴프리헨션을 사용할 수 있다.</p>
<ol start="5">
<li><p><strong>30일간의 일매출 데이터 생성</strong>:</p>
<pre><code class="language-python"> import random

 sales = [random.randint(1500, 45000) for _ in range(30)]
 print(sales)  # 예시 출력: [32034, 15278, 27600, ..., 21045]</code></pre>
<ul>
<li><code>random.randint(1500, 45000)</code>: 1500부터 45000 사이의 무작위 정수 생성</li>
<li><code>for _ in range(30)</code>: 이를 30번 반복하여 리스트에 추가</li>
</ul>
</li>
</ol>
<br>

<ol start="6">
<li><strong>평균을 초과하는 매출 찾기</strong>:<pre><code class="language-python"> average_sales = sum(sales) / len(sales)
 above_average_sales = [sale for sale in sales if sale &gt; average_sales]
 print(above_average_sales)  # 예시 출력: [32034, 27600, ..., 21045]</code></pre>
<ul>
<li><code>average_sales</code>: 매출 리스트의 평균값 계산</li>
<li><code>sale for sale in sales if sale &gt; average_sales</code>: 평균을 초과하는 매출만 리스트에 추가</li>
</ul>
</li>
</ol>
<p><br><br></p>
<h2 id="조건에-따른-값-매핑">조건에 따른 값 매핑</h2>
<p>리스트 컴프리헨션을 사용하여 조건에 따른 값을 매핑할 수 있다.</p>
<ol start="7">
<li><strong>매출 목표 달성 여부 판단</strong>:<pre><code class="language-python"> sales_status = [&quot;매출 달성&quot; if sale &gt; average_sales else &quot;매출 목표 미달&quot; for sale in sales]
 print(sales_status)  # 예시 출력: [&quot;매출 달성&quot;, &quot;매출 목표 미달&quot;, ..., &quot;매출 달성&quot;]</code></pre>
<ul>
<li><code>if-else</code>를 결합하여 조건에 따른 값 매핑</li>
</ul>
</li>
</ol>
<br>

]]></description>
        </item>
        <item>
            <title><![CDATA[데이터셋 재구조화, pivot table, df.melt]]></title>
            <link>https://velog.io/@kim__1323/%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%85%8B-%EC%A7%84%ED%96%89-%EB%82%B4%EC%9A%A9-%EB%B0%8F-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@kim__1323/%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%85%8B-%EC%A7%84%ED%96%89-%EB%82%B4%EC%9A%A9-%EB%B0%8F-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Sat, 20 Jul 2024 12:18:57 GMT</pubDate>
            <description><![CDATA[<h2 id="데이터셋-전처리-과정">데이터셋 전처리 과정</h2>
<p>이제부터 파이썬을 사용하여 데이터를 전처리하는 과정을 상세히 설명하겠다. 데이터셋의 문제점을 해결하고 이를 재구조화하는 과정을 단계별로 진행해보자.</p>
<br>

<h3 id="1-데이터-재구조화-피벗-테이블-및-멜트">1. 데이터 재구조화: 피벗 테이블 및 멜트</h3>
<p>피벗 테이블을 사용하여 데이터를 요약하고, </p>
<p>멜트(melt) 메서드를 사용하여 데이터를 다시 세로로 긴 형태로 재구조화한다.</p>
<p><strong>2.1 피벗 테이블 생성</strong></p>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/e8ec51f5-5a71-4e62-8a85-72d25b3c621b/image.png" alt=""></p>
<p>피벗 테이블은 엑셀의 피버 테이블과 동일하며 세로로 긴 형태의 데이터를 집계해 주는 역할을 한다.</p>
<p>우리의 지금 데이터셋은 이 피버 테이블이 된 후에 데이터셋 형태를 갖고 있다라고 볼 수 있다. </p>
<p>어떤 카테고리를 기준으로 집계할 것인지를 지정할 수 있고 어떤 값을 집계할 것인지를 지정할 수 있고, 그리고 이 값을 집계할 때 어떤 방식으로 집계할 것인지 예를 들어 값들의 평균 값을 낼 것인지 값들의 총합을 구할 것인지, 값들 중 최댓값을 구할 것인지 이러한 집계 방식을 지정할 수 있다.</p>
<pre><code class="language-python"># 예시 피벗 테이블 생성
pivot_df = pd.pivot_table(df, values=&#39;거래액&#39;, index=[&#39;상품군&#39;], columns=[&#39;운영 형태&#39;], aggfunc=&#39;sum&#39;)
print(pivot_df.head())

df.pivot_table(values=None, 집계값
               index=None,  집계기준(행)
              columns=None, 집계기준(열)
            aggfunc=&#39;mean&#39;) 집계방식
</code></pre>
<br>

<p><strong>2.2 멜트 메서드를 이용한 재구조화</strong></p>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/0959a863-782b-429e-9802-e9535601fd49/image.png" alt=""></p>
<p>이 피버 테이블을 반대로 만드는 역할이 판다스의 멜트이다.  판다스의 멜트는 집계된 형태의 데이터를 다시 세로로 길게 펴주는 그런 역할을 하는 메서드이다.</p>
<p>멜트는 집계된 형태의 데이터셋을 다시 세로로 길게 늘어뜨려주는 역할을 하며 그래서 크게 지정할 수 있는 것들은 벨트를 통해서 유지한 컬럼을 지정할 수 있고 변환할 컬럼, 즉 오른쪽으로 긴 형태였는데 세로로 길게 만들어줄 컬럼을 지정할 수 있다.</p>
<pre><code class="language-python"># 데이터 멜트 (재구조화)
melted_df = df.melt(id_vars=[&#39;상품군&#39;], var_name=&#39;운영 형태&#39;, value_name=&#39;거래액&#39;)
print(melted_df.head())

df.melt(id_vars=None,      유지할 컬럼
        value_vars=None,   변환할 컬럼
        var_name=None,     변환 후 생성되는 컬럼 이름
        value_name=&#39;value&#39;) 변환 후 생성되는 값 컬럼의 이름
</code></pre>
<br>

<h3 id="2-라이브러리-임포트-및-파일-불러오기">2. 라이브러리 임포트 및 파일 불러오기</h3>
<p>우선 필요한 라이브러리를 임포트하고 데이터를 불러오는 과정이다.</p>
<pre><code class="language-python">import pandas as pd

# 데이터 파일 경로
file_path = &#39;온라인 쇼핑몰 운영 형태별 상품군별 거래액.csv&#39;

# 데이터 읽기 (인코딩 설정)
df = pd.read_csv(file_path, encoding=&#39;cp949&#39;)

# 데이터 확인
print(df.head())</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로젝트] 온라인 화장품 shop 고객분석]]></title>
            <link>https://velog.io/@kim__1323/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%98%A8%EB%9D%BC%EC%9D%B8-%ED%99%94%EC%9E%A5%ED%92%88-shop-%EA%B3%A0%EA%B0%9D%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@kim__1323/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%98%A8%EB%9D%BC%EC%9D%B8-%ED%99%94%EC%9E%A5%ED%92%88-shop-%EA%B3%A0%EA%B0%9D%EB%B6%84%EC%84%9D</guid>
            <pubDate>Sat, 20 Jul 2024 06:39:09 GMT</pubDate>
            <description><![CDATA[<p>아래는 온라인 화장품 shop  A사의 판매 전략을 개선하기 위해 고객의 행동 패턴과 선호도를 분석한 프로젝트이다.</p>
<br>

<h3 id="문제-상황은-이렇다"><strong>문제 상황은 이렇다:</strong></h3>
<p>🔖
A사는 온라인 화장품 shop의 판매 전략을 개선하기 위해 고객의 행동 패턴과 선호도를 분석하려 한다.</p>
<p><strong>⛳ 문제정의</strong></p>
<p>▶ 고객 이해에 대한 정보 부족
▶ Q. 어떤 제품 또는 브랜드가 가장 인기가 있을까?
▶ Q. 고객들이 카트에 담았으나 구매하지 않는 제품들은 무엇일까?
▶ Q. 구매 전환률을 높이기 위한 전략은 무엇일까?</p>
<p><strong>⛳ 기대효과</strong></p>
<p>▶ 매출 증대와 높은 고객 만족도를 달성
▶ 효과적인 마케팅 전략 수립
▶ 재고 관리 및 제품 전략의 효율화</p>
<p><strong>⛳ 해결방안</strong></p>
<p>▶ 데이터를 통한 고객의 제품 및 브랜드 선호도 파악
▶ 카트에 담기만 하고 구매되지 않는 제품들의 공통점 분석
▶ 구매 전환률을 높일 수 있는 마케팅 전략 제안</p>
<p><strong>⛳ 성과측정</strong>
▶ 구매 전환률의 증가
▶ 고객 만족도 조사를 통한 평가
▶ 매출 증대 및 재구매율 증가</p>
<p><strong>⛳ 운영</strong>
▶ 주기적으로 데이터를 수집하고 분석하여 전략을 수정 및 적용
▶ 고객의 피드백을 수집하여 서비스 개선
▶ 새로운 제품 또는 브랜드의 반응을 모니터링하여 전략 반영</p>
<hr>
<h3 id="데이터셋-정의"><strong>데이터셋 정의</strong></h3>
<BR>

<pre><code class="language-python"># ▶ pd.set option
import numpy as np
import pandas as pd
pd.set_option(&#39;display.max_columns&#39;,100)
pd.set_option(&#39;display.max_rows&#39;,100)

# ▶ Data read
df = pd.read_csv(&#39;S_PJT11_DATA.csv&#39;)
df.head()

 event_time        event_type  product_id          category_id  category_code      brand  price    user_id                          user_session
0  2019-12-01 00:00:00 UTC  remove_from_cart     5712790  1487580005268456287           None      f.o.x   6.27  576802932  51d85cb0-897f-48d2-918b-ad63965c12dc
1  2019-12-01 00:00:00 UTC              view     5764655  1487580005411062629           None        cnd  29.05  412120092  8adff31e-2051-4894-9758-224bfa8aec18
2  2019-12-01 00:00:02 UTC              cart        4958  1487580009471148064           None     runail   1.19  494077766  c99a50e8-2fac-4c4d-89ec-41c05f114554
3  2019-12-01 00:00:05 UTC              view     5848413  1487580007675986893           None  freedecor   0.79  348405118  722ffea5-73c0-4924-8e8f-371ff8031af4
4  2019-12-01 00:00:07 UTC              view     5824148  1487580005511725929           None       None   5.56  576005683  28172809-7e4a-45ce-bab0-5efa90117cd5
``` &amp;#8203;:citation[oaicite:0]{index=0}&amp;#8203;
</code></pre>
<br>


<ul>
<li>컬럼 정보<pre><code>event_time: 이벤트가 발생한 시간
event_type: 이벤트 유형= [view, cart, remove_from_cart, purchase] 중 하나
product_id: 제품 ID
category_id: 제품 카테고리 ID
category_code: 의미 있는 카테고리 이름 (있는 경우)
brand: 브랜드 이름 (소문자로, 있을 경우)
price: 제품 가격
user_id: 영구 사용자 ID
user_session: 사용자 세션 ID
</code></pre></li>
</ul>
<pre><code>
__________

### **데이터 전처리 및 EDA**


### (1) Data shape(형태) 확인

```python
# ▶ Data 형태 확인
# ▶ 3,533,286 row, 9 col로 구성됨
print(&#39;df&#39;, df.shape)

&gt; df (3533286, 9)</code></pre><ul>
<li>3,533,286 개의 데이터가 있으며 9개의 컬럼으로 이루어져 있음.</li>
</ul>
<h3 id="2-data-type-확인">(2) Data type 확인</h3>
<pre><code class="language-python"># ▶ Data type 확인
df.info()

&lt;class &#39;pandas.core.frame.DataFrame&#39;&gt;
RangeIndex: 3533286 entries, 0 to 3533285
Data columns (total 9 columns):
 #   Column         Dtype  
---  ------         -----  
 0   event_time     object 
 1   event_type     object 
 2   product_id     int64  
 3   category_id    int64  
 4   category_code  object 
 5   brand          object 
 6   price          float64
 7   user_id        int64  
 8   user_session   object 
dtypes: float64(1), int64(3), object(5)
memory usage: 242.6+ MB</code></pre>
<p>총 9개의 열 데이터가 있으며 <code>event_time</code> 시간형 타입의 데이터가 <code>object</code> 문자형 데이터로 선언이 돼있다. 그리고 <code>user_id</code> 와 같이 고객의 고유한 정보 문자열 데이터여야 할 것이 <code>int64</code> 정수형으로 선언이 되 있다.</p>
<p>필요에 따라 데이터 분석 진행 시 데이터 타입을 변경시키는게 좋을 거 같다.</p>
<h3 id="3-null값-확인--빈-값의-data">(3) Null값 확인 (※ 빈 값의 Data)</h3>
<pre><code class="language-python">event_time             0
event_type             0
product_id             0
category_id            0
category_code    3474821
brand            1510289
price                  0
user_id                0
user_session         779
dtype: int64</code></pre>
<ul>
<li><code>category_code</code>, <code>brand</code>, <code>user_session</code> 등의 데이터는 <code>null</code> 값을 삭제했을 때 데이터의 변형이 일어나 정확한 데이터 분석을 하기가 어렵기에 삭제하기 보다는 <code>null</code> 값을 다른 문자열<code>None</code> 이나 0으로 치환하는 것이 좋다. </li>
</ul>
<p><strong>null 값 처리</strong></p>
<pre><code class="language-python"># ▶ Null value 다른 값으로 치환
df[&#39;category_code&#39;].fillna(&#39;None&#39;, inplace = True)
df[&#39;brand&#39;].fillna(&#39;None&#39;, inplace = True)
df[&#39;user_session&#39;].fillna(&#39;None&#39;, inplace = True)</code></pre>
<br>



<h3 id="4-outlier-확인-이상치-정상적인-범주를-벗어난-data"><strong>(4) Outlier 확인 (이상치, 정상적인 범주를 벗어난 Data)</strong></h3>
<pre><code class="language-python"># ▶ Outlier 확인
df.describe()

          product_id     category_id           price         user_id
count    3.533286e+06    3.533286e+06    3.533286e+06    3.533286e+06
mean    5.473054e+06    1.555023e+18    8.871856e+00    5.223318e+08
std        1.331331e+06    1.689262e+17    1.986474e+01    8.494819e+07
min        3.752000e+03    1.487580e+18    -7.937000e+01    1.180452e+06
25%        5.726191e+06    1.487580e+18    2.060000e+00    4.866830e+08
50%        5.811429e+06    1.487580e+18    4.210000e+00    5.566496e+08
75%        5.859462e+06    1.487580e+18    7.140000e+00    5.828019e+08
max        5.917178e+06    2.235524e+18    3.277800e+02    5.954145e+08  
</code></pre>
<ul>
<li>가격과 관련된 데이터인 <code>price</code>의 최솟값 <code>min</code> 값이 <code>-7.937000e+01</code>로 이상치로 보이지만 취소 매출이나 할인된 값이 함께 있는 데이터인지 확실하지 않아 삭제하지 않았다.</li>
</ul>
<h3 id="5-데이터-eda-퍼널-분석--고객-구매-데이터-와-제품-및-브랜드별-판매-정보"><strong>(5) 데이터 EDA, 퍼널 분석 =&gt; 고객 구매 데이터 와 제품 및 브랜드별 판매 정보</strong></h3>
<p><strong>기본 판매 및 고객 구매지표 확인</strong></p>
<p>A 온라인 화장품 판매 회사의 고객들이 제품을 구매할 때 발생하는 전체 이벤트 유형별 발생 횟수를 알아보았다. </p>
<pre><code class="language-python"># ▶ 이벤트 타입 횟수
event_counts = df[&#39;event_type&#39;].value_counts()
event_counts

event_type
view                1728331
cart                 927124
remove_from_cart     664655
purchase             213176
Name: count, dtype: int64  
</code></pre>
<ul>
<li>view, 즉 조회는 1728331회</li>
<li>cart, 즉 장바구니로 옮긴 것은 927124회</li>
<li>remove_from_cart, 즉 장바구니로 옮겼다가 구매하지 않은 건은 664655</li>
<li>purchase, 즉 최중 구매 건은 213176과 같다.</li>
</ul>
<p>이 정보를 통해 우리는 고객들이 우리가 설계한 유저의 구매 경험 루트를 잘 따라가고 있는지, 최초 조회 및 장바구니부터 구매까지 단계를 나눠 살펴보아 이탈율이 얼마인지 알아볼 필요가 있다.</p>
<p>이때 사용하는 분석법이 바로 퍼널분석(Funnel Analysis) 이다.</p>
<p>제품을 구매할 때 최초 조회 -&gt; 장바구니 -&gt; 구매 까지 각 단계를 통과할 때마다 제품을 구매하는 유저 수는 당연히 줄어드는 모양세를 보인다. 위처럼 처음 조회 횟수는 약 170만명이나 되지만 최중 구매는 21만명만 나온 것처럼 말이다.</p>
<p>그리고 여기서 조회 -&gt; 장바구니 -&gt; 구매
             ↑          ↓
               ↑    장바구니에서 삭제
               ←←←←← ←
각각의 단계를 넘어가는 것을 전환(Conversion) 이라고 부르고 그 비율은 전환율이(Conversion rate)이 된다.</p>
<p>퍼널 분석에서 이렇게 각 단계별 이탈율이나 <code>Conversion rate</code>를 살펴보는 것은 중요하다. 아래에서 고객의 구매 정보를 통해 퍼널 분석을 진행해보겠다.</p>
<pre><code class="language-python"># ▶ 각 단계에서의 이탈률 계산, 퍼널 분석

# 조회만 하고 이탈한 고객 데이터
view_to_cart_dropout = (event_counts[&#39;view&#39;] - event_counts[&#39;cart&#39;]) / event_counts[&#39;view&#39;] * 100

# 장바구니에서 구매까지 이어지지 않고 이탈한 고객 데이터 비율
cart_to_purchase_dropout = (event_counts[&#39;cart&#39;] - event_counts[&#39;purchase&#39;]) / event_counts[&#39;cart&#39;] * 100

# 장바구니에서 상품을 삭제하고 이탈한 고객 데이터 비율
cart_to_remove_dropout = (event_counts[&#39;remove_from_cart&#39;]) / event_counts[&#39;cart&#39;] * 100

dropout_rates = {
    &#39;View to Cart Dropout Rate (%)&#39;: view_to_cart_dropout,
    &#39;Cart to Purchase Dropout Rate (%)&#39;: cart_to_purchase_dropout,
    &#39;Cart to Remove Dropout Rate (%)&#39;: cart_to_remove_dropout
}

dropout_rates

{&#39;View to Cart Dropout Rate (%)&#39;: 46.35726605609689,
 &#39;Cart to Purchase Dropout Rate (%)&#39;: 77.00674343453518,
 &#39;Cart to Remove Dropout Rate (%)&#39;: 71.68997890249848}</code></pre>
<ul>
<li><p>이탈률 확인 결과 조회만 하고 이탈한 고객이 46프로, 장바구니에서 구매하지 않은 고객이 77프로, 장바구니에서 삭제하고 이탈한 고객이 71프로를 차지했다.</p>
</li>
<li><p>각 이탈률을 어떻게 줄일 것인지 전략이 필요할 것이다.</p>
</li>
</ul>
<br>

<h3 id="사용자별-평균-구매-횟수"><strong>사용자별 평균 구매 횟수</strong></h3>
<pre><code class="language-python"># ▶ 사용자별 구매 횟수 계산
user_purchase_counts = df[df[&#39;event_type&#39;] == &#39;purchase&#39;].groupby(&#39;user_id&#39;).size()

# ▶ 사용자별 평균 구매 횟수 계산
average_purchase_per_user = user_purchase_counts.mean()

average_purchase_per_user

&gt; 8.322960996369032</code></pre>
<ul>
<li><p>사용자별 평균 구매 횟수는 평균 약 8회 정도이다.</p>
<hr>
</li>
</ul>
<br>



<h3 id="제품별-판매량-top-10"><strong>제품별 판매량 TOP 10</strong></h3>
<p>판매 데이터 에서 각 제품 별로 어떤 제품이 가장 인기가 있는지 알아보기 위해 판매량 TOP10 제품을 확인해보았다.</p>
<p>일단 <code>&#39;reservation_status&#39;</code> 즉 전체적인 예약의 현황을 살펴보았다.</p>
<pre><code class="language-python"># ▶ 가장 많이 판매된 상품 TOP 10 계산
top_sold_products = df[df[&#39;event_type&#39;] == &#39;purchase&#39;][&#39;product_id&#39;].value_counts().head(10)
top_sold_products

product_id
5809910    1659
5854897     786
5802432     714
5700037     621
5809912     620
5833330     594
5304        549
5751422     548
5815662     521
5751383     435
Name: count, dtype: int64</code></pre>
<ul>
<li>가장 많이 판매된 상품은 5809910 이라는 상품 코드로 나타났는데 어떤 종류의 상품이지 정확히 알 수가 없어 제품의 카테고리 별로 많이 판매된 상품 TOP10 제품을 다시 알아봐야겠다.</li>
</ul>
<h3 id="제품-카테고리종류별-판매량-top-10"><strong>제품 카테고리(종류별) 판매량 TOP 10</strong></h3>
<pre><code class="language-python"># ▶ 가장 많이 판매된 상품 TOP 10 계산 (NA가 많아 의미가 없음)
top_sold_products = df[df[&#39;event_type&#39;] == &#39;purchase&#39;][&#39;category_code&#39;].value_counts(dropna=False).head(20)
top_sold_products

category_code
None                                      210470
stationery.cartrige                          984
apparel.glove                                771
appliances.environment.vacuum                636
furniture.bathroom.bath                      183
accessories.bag                               42
furniture.living_room.cabinet                 40
accessories.cosmetic_bag                      34
appliances.personal.hair_cutter               14
appliances.environment.air_conditioner         2
Name: count, dtype: int64</code></pre>
<ul>
<li>가장 많이 판매된 제품 카테고리가 <code>&#39;None&#39;</code> 값으로 사실상 어떤 제품이 많이 판매되는지 알아보기가 어려워 제품의 인기도를 알아보는건 의미가 없어졌다. 그래서 판매 데이터는 신뢰성이 적다고 할 수 있다.  </li>
</ul>
<br> 


<h3 id="제품별-판매-매출-top-10"><strong>제품별 판매 매출 TOP 10</strong></h3>
<pre><code class="language-python"># ▶ 상품별 매출 계산
df[&#39;sales&#39;] = df[&#39;price&#39;] * df[&#39;event_type&#39;].apply(lambda x: 1 if x == &#39;purchase&#39; else 0)


# ▶ 가장 많은 매출을 기록한 상품 TOP 10
top_sales_products = df.groupby(&#39;product_id&#39;).sum()[&#39;sales&#39;].sort_values(ascending=False).head(10)
# product_id 열을 그룹화하고 이후 sales 열의 값들의 합계를 계산하고 내림차순으로 정렬하는 조건을 건 것
# 또는 top_sales_products = df.groupby(&#39;product_id&#39;)[&#39;sales&#39;].sum().sort_values(ascending=False).head(10)
top_sales_products

product_id
5850281    10471.28
5560754    10110.88
5809910     8693.16
5751422     6000.60
5751383     4489.20
5877454     4473.29
5909810     4326.63
5846437     4297.80
5849033     4066.08
5792800     4066.08
Name: sales, dtype: float64
</code></pre>
<ul>
<li><p>제품의 매출을 살펴보니 판매량과는 조금 순위가 바뀐 것을 확인할 수 있으며, 이는 판매량보다는 제품의 가격에 따라 순위가 책정된 것이라고 할 수 있겠다.</p>
</li>
<li><p>하지만 제품명이 숫자로만 명시되어 있어 정확히 어떤 제품이 매출이 많은지 알기 어렵기에 정확한 브랜드별 판매량 및 매출을 확인해보고자 한다.</p>
</li>
</ul>
<h3 id="제품-카테고리종류별-판매량-top-10-1"><strong>제품 카테고리(종류별) 판매량 TOP 10</strong></h3>
<pre><code class="language-python"># ▶ 브랜드별 판매량 계산
brand_sales_count = df[df[&#39;event_type&#39;] == &#39;purchase&#39;].groupby(&#39;brand&#39;).size()

# ▶ 브랜드별 매출 계산
brand_sales_revenue = df[df[&#39;event_type&#39;] == &#39;purchase&#39;].groupby(&#39;brand&#39;).sum()[&#39;sales&#39;]

brand_sales = pd.DataFrame({
    &#39;Sales Count&#39;: brand_sales_count,
    &#39;Sales Revenue&#39;: brand_sales_revenue
}).sort_values(by=&#39;Sales Revenue&#39;, ascending=False)

brand_sales.head(10)

        Sales Count    Sales Revenue
brand        
None          91695        440207.57
runail          18199         58177.26
grattol           8171         43793.50
irisk          10583         35291.51
uno               2780         29084.76
estel           4116         24474.19
jessnail       1823         24075.00
strong            120          22266.41
masura           6985         19809.19
cnd               1299         19378.92</code></pre>
<ul>
<li><p>가장 많이 매출을 낸 제품 브랜드가 <code>&#39;None&#39;</code> 값으로 브랜드가 없는 제품에 대한 데이터가 가장 많다.</p>
</li>
<li><p>이를 제외하고 <code>runail</code> 브랜드가 가장 많은 매출을 달성했으며 그 다음은 <code>grattol</code> 매출이 뒤를 이었다. <code>estl</code> 브랜드부터 하위 브랜드들은 매출의 큰 차이가 없는 것으로 보인다.</p>
</li>
</ul>
<br>


<h3 id="6-판매전략-개선"><strong>(6) 판매전략 개선</strong></h3>
<p>최종적으로 우리의 목표는 구매전환율을 올리는 것으로 현재 카테고리 별, 브랜드 별 인기도 및 선호도를 살펴본 결과 의미가 없는 데이터가 많아 전략을 짤 수가 없었다.</p>
<h3 id="상품별-conversion-rate-분석"><strong>상품별 Conversion Rate 분석</strong></h3>
<p>그래서 상품id 별로 조회, 장바구니, 구매 전환율이 어떻게 되는지 확인하여 이를 토대로 판매전략을 마련하고자 한다.</p>
<pre><code class="language-python"># ▶ 상품별 조회, 장바구니 추가, 구매 데이터 계산
product_events = df.groupby([&#39;product_id&#39;, &#39;event_type&#39;]).size().unstack().fillna(0)

# ▶ 구매 전환률 계산: 구매수 / 조회수
product_events[&#39;conversion_rate&#39;] = product_events[&#39;purchase&#39;] / product_events[&#39;view&#39;] * 100

# ▶ 상품별 조회수가 10회 이상인 상품들만 필터링하여 전환률 TOP 10 확인
top_conversion_products = product_events[product_events[&#39;view&#39;] &gt;= 10].sort_values(by=[&#39;view&#39;, &#39;conversion_rate&#39;], ascending=[False, True]).head(10)

top_conversion_products[[&#39;view&#39;, &#39;cart&#39;, &#39;purchase&#39;, &#39;conversion_rate&#39;]]

event_type        view      cart    purchase    conversion_rate
product_id                
5809910         24419.0    6513.0      1659.0           6.793890
5909810          8473.0    675.0       231.0           2.726307
5877454          6621.0    519.0       101.0           1.525449
5809912          5944.0    2634.0       620.0          10.430686
5886282          5488.0    459.0        90.0           1.639942
5877456          5022.0    161.0        15.0           0.298686
5649236          4372.0    621.0       148.0           3.385178
5809911          4307.0    1757.0       386.0           8.962155
5769877          4100.0    188.0        27.0           0.658537
5856186          4015.0    245.0        42.0           1.046077</code></pre>
<ul>
<li><p>제품 조회수가 가장 높은 TOP10 제품별로 구매 전환율을 볼 수 있는 지표를 만들어보았다.</p>
</li>
<li><p>조회 결과 <code>5809910</code>, <code>5809912</code>, <code>5909810</code>, <code>5809911</code> 제품 조회 수 대비 구매 전환율이 6프로나 10프로 정도가 된다.</p>
</li>
<li><p><code>5877456</code>, <code>5769877</code>, <code>5856186</code> 제품은 각각 6번째, 9번째, 10번째로 조회가 많이 됐지만 구매 전환율이 낮은 것을 볼 수 있다. 따라서 해당 제품들의 구매 전환율을 높히기 위한 전략을 마련해야 할 것이다.</p>
</li>
</ul>
<br>  

<h4 id="시간대별-고객-구매-행동-패턴분석"><strong>시간대별 고객 구매 행동 패턴분석</strong></h4>
<p>상품 별 분석은 마쳤으니 이제 효율적인 판매 마케팅 전략을 마련할 수 있도록 고객의 시간대별 구매 행동 패턴을 분석해보자</p>
<pre><code class="language-python"># ▶ event_time에서 시간대 정보 추출
df[&#39;hour&#39;] = df[&#39;event_time&#39;].astype(str).str[11:13]

# ▶ 시간대별 이벤트 유형별 발생 횟수 계산
hourly_events = df.groupby([&#39;hour&#39;, &#39;event_type&#39;]).size().unstack().fillna(0)

# ▶ 시간대별 구매 전환률 계산: 구매수 / 조회수
hourly_events[&#39;conversion_rate&#39;] = hourly_events[&#39;purchase&#39;] / hourly_events[&#39;view&#39;] * 100

hourly_events[[&#39;view&#39;, &#39;cart&#39;, &#39;purchase&#39;, &#39;conversion_rate&#39;]]

# ▶ gradient 시각화
styled_all_columns = hourly_events.style.background_gradient(cmap=&#39;coolwarm&#39;, subset=[&#39;conversion_rate&#39;])
styled_all_columns</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/5ce5bf91-6e2a-42a5-8d7a-793cc268b6d3/image.png" alt=""></p>
<ul>
<li><p><code>gradient</code> 시각화로 데이터 프레임 내의 구매 전환율이 가장 높은 시간대를 살펴보니 오전 7시~11시 사이에 고객의 구매 전환율이 가장 높게 나오는 것을 확인할 수 있다.</p>
</li>
<li><p>따라서 해당 시간대에 할인 이벤트나 제품 이벤트, 배너 광고 이벤트 등을 타겟 마케팅 하는 전략을 세울 필요가 있어 보인다.</p>
</li>
</ul>
<br>

<hr>
<p><strong>결론</strong></p>
<ul>
<li><p>이탈률 확인 결과 조회만 하고 이탈한 고객이 46프로, 장바구니에서 구매하지 않은 고객이 77프로, 장바구니에서 삭제하고 이탈한 고객이 71프로를 차지했다.</p>
</li>
<li><p>각 이탈률을 어떻게 줄일 것인지 전략이 필요할 것이다.  </p>
</li>
<li><p>가장 많이 판매되는 제품 및 제품의 카테고리가 <code>&#39;None&#39;</code> 값으로 사실상 어떤 제품이 많이 판매되는지 알아보기가 어려워 제품의 인기도를 알아보는건 의미가 없어졌다. 그래서 판매 데이터는 신뢰성이 적다고 할 수 있다.  </p>
</li>
<li><p>그리고 가장 많이 매출을 낸 제품 브랜드가 <code>&#39;None&#39;</code> 값으로 브랜드가 없는 제품에 대한 데이터가 가장 많다.</p>
</li>
<li><p>이를 제외하고 <code>runail</code> 브랜드가 가장 많은 매출을 달성했으며 그 다음은 <code>grattol</code> 매출이 뒤를 이었다. <code>estl</code> 브랜드부터 하위 브랜드들은 매출의 큰 차이가 없는 것으로 보인다.</p>
</li>
<li><p>조회 결과 <code>5809910</code>, <code>5809912</code>, <code>5909810</code>, <code>5809911</code> 제품 조회 수 대비 구매 전환율이 6프로나 10프로 정도가 된다.</p>
</li>
<li><p><code>5877456</code>, <code>5769877</code>, <code>5856186</code> 제품은 각각 6번째, 9번째, 10번째로 조회가 많이 됐지만 구매 전환율이 낮은 것을 볼 수 있다. 따라서 해당 제품들의 구매 전환율을 높히기 위한 전략을 마련해야 할 것이다.</p>
</li>
<li><p>이탈률 확인 결과 조회만 하고 이탈한 고객이 46프로, 장바구니에서 구매하지 않은 고객이 77프로, 장바구니에서 삭제하고 이탈한 고객이 71프로를 차지했다.</p>
</li>
</ul>
<ul>
<li>따라서 해당 시간대에 할인 이벤트나 제품 이벤트, 배너 광고 이벤트 등을 타겟 마케팅 하는 전략을 세울 필요가 있어 보인다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Feature selection 기법]]></title>
            <link>https://velog.io/@kim__1323/Feature-selection-%EA%B8%B0%EB%B2%95</link>
            <guid>https://velog.io/@kim__1323/Feature-selection-%EA%B8%B0%EB%B2%95</guid>
            <pubDate>Fri, 19 Jul 2024 13:05:57 GMT</pubDate>
            <description><![CDATA[<p><strong>피처 셀렉션 기법</strong></p>
<h3 id="1-차원의-저주curse-of-dimensionality">1. 차원의 저주(Curse of Dimensionality)</h3>
<ul>
<li><p><strong>정의</strong>: 변수(피처)가 많아질수록 데이터의 복잡도가 높아지며, 이를 표현하기 위해서는 많은 데이터가 필요하게 되는 현상.</p>
</li>
<li><p><strong>문제점</strong>: 피처가 많아지면 모델이 오버피팅될 가능성이 높아짐. 트레이닝 데이터에서는 정확도가 높지만 테스트 데이터에서는 성능이 낮아짐.</p>
</li>
<li><p><strong>해결 방법</strong>: 피처 셀렉션을 통해 필요한 변수만 선택하여 모델의 복잡도를 줄이고, 오버피팅을 방지한다.</p>
</li>
</ul>
<br>

<h3 id="2-피처-셀렉션의-필요성">2. 피처 셀렉션의 필요성</h3>
<ul>
<li><p>피처가 많을수록 모델의 복잡도가 높아지며, 바이어스는 낮아지고 베리언스는 높아진다.</p>
</li>
<li><p>피처 셀렉션을 통해 바이어스와 베리언스의 트레이드오프를 최적화하여 모델의 성능을 향상시킨다.</p>
</li>
</ul>
<br>

<h3 id="3-피처-셀렉션-기법">3. 피처 셀렉션 기법</h3>
<h3 id="31-완전-탐색exhaustive-search">3.1. 완전 탐색(Exhaustive Search)</h3>
<ul>
<li><p>정의: 가능한 모든 피처 조합을 시도하여 가장 좋은 성능을 내는 조합을 선택.</p>
</li>
<li><p>장점: 최적의 피처 조합을 찾을 수 있음.</p>
</li>
<li><p>단점: 피처 수가 많아지면 계산량이 급격히 증가하여 현실적으로 사용이 어려움.</p>
</li>
</ul>
<br>

<h3 id="32-전진-선택법forward-selection">3.2. 전진 선택법(Forward Selection)</h3>
<ul>
<li><p>정의: 피처를 하나씩 추가하며 모델의 성능을 평가, 가장 성능이 좋은 피처를 선택.</p>
</li>
<li><p>방법:</p>
<ul>
<li>초기 모델에 피처가 없음.</li>
<li>각 피처를 하나씩 추가하며 모델의 성능(R-제곱, MSE 등)을 평가.</li>
<li>가장 성능이 좋은 피처를 선택하고, 이를 고정한 상태로 다음 피처를 추가.</li>
<li>이 과정을 반복하여 더 이상의 성능 향상이 없을 때까지 진행.</li>
<li>장점: 비교적 계산량이 적고, 피처가 많을 때 유용.</li>
<li>단점: 초기 선택이 잘못되면 전체 성능에 영향을 미침.</li>
</ul>
</li>
</ul>
<br>

<h3 id="33-후진-제거법backward-elimination">3.3. 후진 제거법(Backward Elimination)</h3>
<ul>
<li><p>정의: 모든 피처를 포함한 모델에서 시작하여, 하나씩 제거하며 모델의 성능을 평가.</p>
</li>
<li><p>방법:</p>
<ul>
<li>초기 모델에 모든 피처를 포함.</li>
<li>각 피처를 하나씩 제거하며 모델의 성능을 평가.</li>
<li>성능에 큰 영향을 미치지 않는 피처를 제거.</li>
<li>이 과정을 반복하여 더 이상의 성능 저하가 없을 때까지 진행.</li>
</ul>
</li>
<li><p>장점: 모든 피처를 처음에 포함하여 시작하므로, 중요한 피처가 제거될 가능성이 낮음.</p>
</li>
<li><p>단점: 계산량이 많고, 피처 수가 많을 때는 비효율적.</p>
</li>
</ul>
<br>

<h3 id="34-단계적-선택법stepwise-selection">3.4. 단계적 선택법(Stepwise Selection)</h3>
<ul>
<li><p>정의: 전진 선택법과 후진 제거법을 번갈아 가며 수행.</p>
</li>
<li><p>방법:</p>
<ul>
<li>초기 모델에 피처가 없음.</li>
<li>전진 선택법을 통해 피처를 추가.</li>
<li>후진 제거법을 통해 성능에 영향을 미치지 않는 피처를 제거.</li>
<li>이 과정을 반복하여 최적의 피처 조합을 찾음.</li>
</ul>
</li>
<li><p>장점: 전진 선택법과 후진 제거법의 장점을 모두 활용하여, 더 나은 피처 조합을 찾을 가능성이 높음.</p>
</li>
<li><p>단점: 계산량이 많고, 시간이 오래 걸릴 수 있음.</p>
</li>
</ul>
<br>

<h3 id="4-예시">4. 예시</h3>
<h3 id="완전-탐색">완전 탐색:</h3>
<ul>
<li><p>피처 3개: <strong><code>x1, x2, x3</code></strong></p>
</li>
<li><p>가능한 조합: <strong><code>x1, x2, x3, x1+x2, x1+x3, x2+x3, x1+x2+x3</code></strong></p>
</li>
<li><p>각 조합에 대해 모델의 성능을 평가하고, 최적의 조합을 선택.</p>
</li>
</ul>
<h3 id="전진-선택법">전진 선택법:</h3>
<ul>
<li><p>초기 모델: 없음</p>
<ul>
<li>첫 번째 단계: x1, x2, x3 중에서 가장 성능이 좋은 피처 선택 (예: x2)</li>
<li>두 번째 단계: x2를 고정하고 x1, x3 중에서 추가할 피처 선택 (예: x7)</li>
<li>세 번째 단계: x2와 x7을 고정하고 나머지 피처 중에서 추가할 피처 선택 (예: x4)</li>
</ul>
</li>
</ul>
<h3 id="후진-제거법">후진 제거법:</h3>
<ul>
<li><p>초기 모델: <strong><code>x1, x2, x3, x4, x5, x6, x7, x8</code></strong></p>
<ul>
<li>첫 번째 단계: 각 피처를 제거하며 성능을 평가, 가장 영향이 적은 피처 제거 (예: x3)</li>
<li>두 번째 단계: x3을 제외한 나머지 피처 중에서 성능에 영향을 미치지 않는 피처 제거 (예: x5)</li>
<li>세 번째 단계: 이 과정을 반복하여 최적의 피처 조합을 찾음.</li>
</ul>
</li>
</ul>
<h3 id="단계적-선택법">단계적 선택법:</h3>
<ul>
<li>초기 모델: 없음<ul>
<li>첫 번째 단계: 전진 선택법으로 피처 선택 (예: x2)</li>
<li>두 번째 단계: 후진 제거법으로 피처 제거 (예: x7)</li>
<li>세 번째 단계: 전진 선택법으로 피처 추가 (예: x4)
이 과정을 반복하여 최적의 피처 조합을 찾음.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Model 평가 및 지표 해석
]]></title>
            <link>https://velog.io/@kim__1323/Model-%ED%8F%89%EA%B0%80-%EB%B0%8F-%EC%A7%80%ED%91%9C-%ED%95%B4%EC%84%9D</link>
            <guid>https://velog.io/@kim__1323/Model-%ED%8F%89%EA%B0%80-%EB%B0%8F-%EC%A7%80%ED%91%9C-%ED%95%B4%EC%84%9D</guid>
            <pubDate>Fri, 19 Jul 2024 12:28:42 GMT</pubDate>
            <description><![CDATA[<p>모델의 성능을 평가하고 비교하는 것은 모델 개발 과정에서 매우 중요하다. 여기서는 다양한 평가 지표와 그 해석 방법을 설명한다.</p>
<h3 id="1-r-제곱-r-squared">1. R-제곱 (R-Squared)</h3>
<ul>
<li><p>정의: R-제곱 값은 모델이 종속 변수의 변동성을 얼마나 설명하는지를 나타낸다.</p>
</li>
<li><p>범위: 0부터 1까지. 1에 가까울수록 모델이 데이터를 잘 설명한다.</p>
</li>
<li><p>해석:</p>
<ul>
<li>R-제곱 값이 높을수록 모델의 설명력이 높다.</li>
<li>일반적으로 R-제곱 값이 0.3 이상이면 실무에서 유의미한 모델로 간주될 수 있다.</li>
<li>예시: R-제곱 값이 0.81이면 모델이 종속 변수의 변동성을 81% 설명한다는 의미이다.</li>
</ul>
</li>
</ul>
<br>

<h3 id="2-평균-제곱-오차-mse-mean-squared-error">2. 평균 제곱 오차 (MSE, Mean Squared Error)</h3>
<ul>
<li>정의: 실제 값과 예측 값의 차이의 제곱을 평균한 값.</li>
<li>공식: <blockquote>
</blockquote>
</li>
<li><em><code>MSE = (1/n) * Σ(yi - y^i)^2</code>*</em></li>
</ul>
<p>여기서,</p>
<ul>
<li><p><code>n</code>은 데이터 포인트의 수</p>
</li>
<li><p><code>yi</code>는 실제 값</p>
</li>
<li><p><code>y^i</code>는 예측 값</p>
</li>
<li><p>해석: 값이 낮을수록 모델의 예측이 실제 값에 가깝다는 의미이다.</p>
</li>
</ul>
<br>

<h3 id="3-평균-절대-오차-mae-mean-absolute-error"><strong>3. 평균 절대 오차 (MAE, Mean Absolute Error)</strong></h3>
<ul>
<li><p>정의: 실제 값과 예측 값의 차이의 절대값을 평균한 값.</p>
</li>
<li><p>공식:</p>
</li>
</ul>
<p><strong><code>MAE = (1/n) * Σ|yi - y^i|</code></strong></p>
<ul>
<li>해석: 값이 낮을수록 모델의 예측이 실제 값에 가깝다는 의미이다. MSE와 달리 MAE는 오차의 크기를 절대값으로 취해 평균을 내므로, 큰 오차에 덜 민감하다.</li>
</ul>
<br>

<h3 id="4-평균-절대-비율-오차-mape-mean-absolute-percentage-error">4. 평균 절대 비율 오차 (MAPE, Mean Absolute Percentage Error)</h3>
<ul>
<li><p>정의: 실제 값과 예측 값의 차이를 실제 값으로 나누어 퍼센트로 나타낸 값의 평균.</p>
</li>
<li><p>공식:</p>
</li>
</ul>
<p><strong><code>MAPE = (100/n) * Σ| (yi - y^i) / yi |</code></strong></p>
<ul>
<li>해석: 값이 낮을수록 모델의 예측이 실제 값에 가깝다는 의미이다. 단위가 퍼센트이므로, 다른 데이터셋 간의 비교가 용이하다.</li>
</ul>
<br>

<h3 id="5-평균-제곱근-오차-rmse-root-mean-squared-error">5. 평균 제곱근 오차 (RMSE, Root Mean Squared Error)</h3>
<ul>
<li><p>정의: 평균 제곱 오차의 제곱근.</p>
</li>
<li><p>공식:</p>
<blockquote>
</blockquote>
</li>
<li><p><em><code>RMSE = sqrt((1/n) * Σ(yi - y^i)^2)</code>*</em></p>
</li>
<li><p>해석: 값이 낮을수록 모델의 예측이 실제 값에 가깝다는 의미이다. MSE보다 해석이 직관적이며, 큰 오차에 민감하다.</p>
</li>
</ul>
<br>

<h3 id="6-모델-평가-및-해석-순서">6. 모델 평가 및 해석 순서</h3>
<h4 id="1-모델-성능-평가">(1) 모델 성능 평가:</h4>
<ul>
<li><p>*<em>R-제곱, MSE, RMSE, MAE, MAPE *</em>등을 사용하여 모델의 성능을 평가한다.</p>
</li>
<li><p><strong>R-제곱</strong> 값이 높고 <strong>MSE, RMSE, MAE, MAPE</strong> 값이 낮을수록 좋은 모델이다.</p>
</li>
</ul>
<h4 id="2-p-값-검정">(2) p-값 검정:</h4>
<ul>
<li><p>베타 계수의 유의성을 검정한다.</p>
</li>
<li><p>p-값이 0.05 이하이면 해당 베타 계수가 유의미하다고 판단한다.</p>
</li>
<li><p>p-값이 0.05 이상이면 해당 베타 계수가 유의미하지 않다고 판단한다.</p>
</li>
<li><p>예시:</p>
<ul>
<li>p-값이 0.03이면 유의미한 변수로 판단한다.</li>
<li>p-값이 0.07이면 유의미하지 않은 변수로 판단한다.</li>
</ul>
</li>
</ul>
<h4 id="3데이터-전처리-및-피처-선택">(3)데이터 전처리 및 피처 선택:</h4>
<ul>
<li><p>모델 성능이 낮을 경우 데이터 전처리를 통해 데이터를 정제하고, 피처 선택을 통해 중요한 변수를 추출한다.</p>
</li>
<li><p>스케일 조정이나 데이터 변환을 통해 모델의 성능을 향상시킬 수 있다.</p>
</li>
</ul>
<h4 id="4-다양한-모델-적용">(4) 다양한 모델 적용:</h4>
<ul>
<li>선형 회귀 모델 외에도 다양한 회귀 모델을 적용하여 성능을 비교한다.</li>
</ul>
<h4 id="5-해석-및-피처-중요도-판단">(5) 해석 및 피처 중요도 판단:</h4>
<ul>
<li><p>p-값을 통해 의미 있는 변수를 추출한다.</p>
</li>
<li><p>추출된 변수를 바탕으로 각 독립 변수가 종속 변수에 미치는 영향을 평가한다.</p>
</li>
<li><p>예를 들어, x가 1 단위 증가할 때 y에 얼마만큼 영향을 미치는지를 판단한다.</p>
</li>
</ul>
<br>

<h3 id="7-예시">7. 예시</h3>
<ul>
<li><p>두 데이터셋에서의 모델 평가 예시:</p>
</li>
<li><p>데이터셋 1: 실제 값이 100, 예측 값이 110인 경우, 오차는 10, 퍼센트 오차는 10%.</p>
</li>
<li><p>데이터셋 2: 실제 값이 10, 예측 값이 9인 경우, 오차는 1, 퍼센트 오차는 10%.</p>
</li>
<li><p>두 데이터셋 모두 퍼센트 오차는 동일하지만, MSE 계산 시 값이 달라질 수 있다.</p>
</li>
</ul>
<hr>
<p>이와 같이 다양한 지표를 활용하여 모델의 성능을 평가하고 해석함으로써, 모델의 예측 정확도를 높이고 신뢰성 있는 결과를 도출할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[계수(β) 추정법]]></title>
            <link>https://velog.io/@kim__1323/%EA%B3%84%EC%88%98-%EC%B6%94%EC%A0%95%EB%B2%95</link>
            <guid>https://velog.io/@kim__1323/%EA%B3%84%EC%88%98-%EC%B6%94%EC%A0%95%EB%B2%95</guid>
            <pubDate>Fri, 19 Jul 2024 11:56:46 GMT</pubDate>
            <description><![CDATA[<h3 id="1-선형-회귀-모델의-기본-개념">1. 선형 회귀 모델의 기본 개념</h3>
<p>선형 회귀 모델은 종속 변수(y)가 독립 변수(x)들의 선형 결합으로 표현되는 모델이다.</p>
<p>모델의 일반적인 형태는 다음과 같다:</p>
<blockquote>
</blockquote>
<p><strong><code>y = β0 + β1 * x1 + β2 * x2 + ... + βn * xn + e</code></strong></p>
<ul>
<li><code>y</code>는 종속 변수</li>
<li><code>x1, x2, ..., xn</code>은 독립 변수들</li>
<li><code>β0, β1, ..., βn</code>은 회귀 계수들</li>
<li><code>e</code>는 오차(에러) 항</li>
</ul>
<br>

<h3 id="2-베타-계수-추정-방법-최소-제곱법">2. 베타 계수 추정 방법 (최소 제곱법)</h3>
<p>베타 계수들을 추정하기 위해 가장 널리 사용되는 방법은 최소 제곱법이다.</p>
<p>최소 제곱법은 실제 값(y)과 예측 값(y^)의 차이(오차)의 제곱합을 최소화하는 계수를 찾는 방법이다.</p>
<p>수식으로 표현하면 다음과 같다:</p>
<blockquote>
</blockquote>
<p><strong><code>SSE = Σ(yi - y^i)^2</code></strong></p>
<ul>
<li><code>SSE</code>는 오차 제곱합 <code>(Sum of Squared Errors)</code></li>
<li><code>yi</code>는 실제 값</li>
<li><code>y^i</code>는 예측 값 <code>(y^i = β0 + β1 * xi1 + β2 * xi2 + ... + βn * xin)</code></li>
</ul>
<br>

<h3 id="3-베타-계수-추정-절차">3. 베타 계수 추정 절차</h3>
<p><strong>(1) 오차 제곱합(SSE) 식 작성:</strong></p>
<blockquote>
</blockquote>
<p><strong><code>SSE = Σ(yi - (β0 + β1 * xi1 + β2 * xi2 + ... + βn * xin))^2</code></strong></p>
<p><strong>(2)  베타 계수에 대한 편미분</strong>: 각 베타 계수에 대해 편미분을 수행하여 SSE를 최소화하는 값을 찾는다.</p>
<blockquote>
</blockquote>
<p><strong><code>∂SSE/∂βj = 0 (for j = 0, 1, 2, ..., n)</code></strong></p>
<p><strong>(3) 정규 방정식 도출</strong>: 편미분한 결과를 정리하면 다음과 같은 정규 방정식으로 표현할 수 있다.</p>
<blockquote>
</blockquote>
<p><strong><code>X^T * X * β = X^T * y</code></strong>
여기서,</p>
<ul>
<li>X는 독립 변수들의 행렬</li>
<li>y는 종속 변수들의 벡터</li>
<li>β는 베타 계수들의 벡터</li>
</ul>
<p><strong>(4) 베타 계수 계산</strong>: 정규 방정식을 풀어 베타 계수를 계산한다.</p>
<blockquote>
</blockquote>
<p><strong><code>β = (X^T * X)^(-1) * X^T * y</code></strong></p>
<br>

<h3 id="4-베타-계수의-유의성-검정">4. 베타 계수의 유의성 검정</h3>
<p>베타 계수의 유의성을 검정하기 위해 t-검정을 사용한다.</p>
<ul>
<li><p><strong>귀무가설(H0)</strong>: βj = 0 (베타 계수가 유의미하지 않음)</p>
</li>
<li><p><strong>대립가설(H1)</strong>: βj ≠ 0 (베타 계수가 유의미함)</p>
</li>
<li><p>t-값 계산:</p>
</li>
</ul>
<blockquote>
</blockquote>
<p><strong><code>t = βj / SE(βj)</code></strong></p>
<ul>
<li>SE(βj)는 베타 계수의 표준 오차</li>
<li>p-값 계산:</li>
</ul>
<p>p-값이 0.05 이하이면 귀무가설을 기각하고, 베타 계수가 유의미하다고 판단한다.</p>
<p>p-값이 0.05 이상이면 귀무가설을 기각하지 못하고, 베타 계수가 유의미하지 않다고 판단한다.</p>
<br>

<h3 id="5-모델-해석">5. 모델 해석</h3>
<ul>
<li><p>베타 계수: x가 한 단위 증가할 때 y에 미치는 영향을 나타낸다.</p>
</li>
<li><p>예를 들어, β1 = 10이면 x1이 한 단위 증가할 때 y가 10만큼 증가한다.</p>
</li>
<li><p>p-값: 베타 계수가 유의미한지를 판단한다.</p>
</li>
<li><p>p-값이 0.05 이하이면 해당 베타 계수가 유의미하다고 본다.</p>
</li>
<li><p>p-값이 0.05 이상이면 해당 베타 계수가 유의미하지 않다고 본다.</p>
</li>
</ul>
<br>

<h3 id="6-주요-변수-중요도-비교">6. 주요 변수 중요도 비교</h3>
<p>변수 중요도를 비교할 때는 각 변수의 스케일을 고려해야 한다.</p>
<p>베타 계수만으로 변수의 중요도를 비교하는 것은 어렵다.</p>
<p>표준화된 계수(Standardized Coefficients)를 사용하여 비교:</p>
<p>모든 변수들을 표준화(평균 0, 표준편차 1)하여 회귀 분석을 수행하면, 베타 계수의 크기로 변수 중요도를 비교할 수 있다.</p>
<br>

<h3 id="7-예시">7. 예시</h3>
<p><strong>베타 계수의 의미</strong>:</p>
<blockquote>
</blockquote>
<p><strong><code>y = 5 + 3 * x1 - 2 * x2</code></strong></p>
<ul>
<li><p>β0 = 5 (상수항, y 절편)</p>
</li>
<li><p>β1 = 3 (x1이 한 단위 증가할 때 y는 3만큼 증가)</p>
</li>
<li><p>β2 = -2 (x2가 한 단위 증가할 때 y는 2만큼 감소)</p>
</li>
<li><p><strong>p-값 해석</strong>:</p>
</li>
</ul>
<p>만약 x1의 p-값이 0.03이라면, x1이 y에 유의미한 영향을 미친다고 판단할 수 있다.</p>
<p>만약 x2의 p-값이 0.07이라면, x2가 y에 유의미한 영향을 미친다고 판단할 수 없다.</p>
<p><strong>모델 해석:</strong></p>
<ul>
<li><p>β1이 3이고, p-값이 0.03이므로 x1은 유의미한 변수이다.</p>
</li>
<li><p>β2가 -2지만, p-값이 0.07이므로 x2는 유의미하지 않다.</p>
</li>
</ul>
<br>

<p>이와 같이 선형 회귀 분석을 통해 베타 계수를 추정하고, p-값을 이용하여 베타 계수의 유의성을 검정하며, 모델을 해석할 수 있다. </p>
<p>이를 통해 변수들의 중요도를 판단하고, 예측 모델을 구축하는데 활용할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Linear regression, loss function]]></title>
            <link>https://velog.io/@kim__1323/Linear-regression-loss-function</link>
            <guid>https://velog.io/@kim__1323/Linear-regression-loss-function</guid>
            <pubDate>Fri, 19 Jul 2024 11:11:22 GMT</pubDate>
            <description><![CDATA[<h3 id="데이터의-종류와-데이터-분석"><strong>데이터의 종류와 데이터 분석</strong></h3>
<p>데이터 분석을 시작하기 전에, 데이터의 종류를 이해하는 것이 중요하다. 데이터는 크게 두 가지로 나눌 수 있다: &#39;</p>
<h3 id="정형-데이터와-비정형-데이터">정형 데이터와 비정형 데이터.</h3>
<p><strong>정형 데이터</strong>: 엑셀에서 볼 수 있는 테이블형 데이터로, 일반적으로 행과 열로 구성되어 있다. 이 데이터를 N x P 매트릭스라고도 부른다.</p>
<ul>
<li>N: 데이터의 개수(행의 수)</li>
<li>P: 변수의 개수(열의 수)</li>
</ul>
<p><strong>비정형 데이터</strong>: 텍스트, 이미지, 오디오 등 구조화되지 않은 데이터. 이 강의에서는 주로 정형 데이터를 다룬다.</p>
<br>

<h3 id="정형-데이터의-변수-종류">정형 데이터의 변수 종류</h3>
<p><strong>정형 데이터</strong>는 크게 두 가지 유형의 변수로 나뉜다: </p>
<p><strong>연속형 변수(numerical variables)와 범주형 변수(categorical variables).</strong></p>
<p><strong>연속형 변수</strong>: 값들 사이에 대소 관계가 존재하며, 숫자 간의 차이가 의미가 있는 변수. 예를 들어, 나이, 키, 소득 등이 있다.</p>
<p><strong>범주형 변수</strong>: 값들 사이에 대소 관계가 없으며, 서로 다른 카테고리를 나타내는 변수. 예를 들어, 성별, 옷의 종류(티셔츠, 바지) 등이 있다.</p>
<br>

<h3 id="머신러닝">머신러닝</h3>
<p>머신러닝은 데이터를 기반으로 모델을 학습시키는 과정으로, 크게 
<strong>지도 학습(Supervised Learning)과 비지도 학습(Unsupervised Learning)</strong>으로 나뉜다.</p>
<p><strong>(1) 지도 학습(Supervised Learning)</strong>: 주어진 데이터와 그에 따른 정답(종속 변수, y)으로부터 모델을 학습시키는 방법. 예를 들어, 선형 회귀(Linear Regression)와 분류(Classification)가 이에 해당된다.</p>
<ul>
<li>선형 회귀(Linear Regression): 연속형 변수 예측</li>
<li>분류(Classification): 이산형 변수 예측</li>
</ul>
<p><strong>(2) 비지도 학습(Unsupervised Learning)</strong>: 데이터에 정답이 없이, 데이터의 구조를 학습시키는 방법. 예를 들어, 클러스터링(Clustering)과 차원 축소(Dimensionality Reduction)가 이에 해당된다.</p>
<ul>
<li><strong>클러스터링(Clustering)</strong>: 데이터 군집화</li>
<li><strong>차원 축소(Dimensionality Reduction)</strong>: 데이터의 주요 특징 추출</li>
</ul>
<br>

<h3 id="좋은-알고리즘의-기준">좋은 알고리즘의 기준</h3>
<p>좋은 알고리즘의 기준은 에러가 낮은 것이다. 즉, 알고리즘이 <strong>예측한 값(𝑦^)</strong>과 <strong>실제 값(y)</strong> 사이의 차이가 작을수록 좋은 알고리즘이라 할 수 있다.</p>
<ul>
<li><p><strong>알고리즘</strong>: 인풋 데이터(x)를 입력받아, 프로세스를 거쳐 아웃풋 데이터(𝑦^)를 출력하는 것.</p>
</li>
<li><p><strong>인풋</strong>: 독립 변수(x들)</p>
</li>
<li><p><strong>아웃풋</strong>: 예측값(𝑦^)</p>
</li>
<li><p>** 좋은 알고리즘**: 예측값(𝑦^)이 실제 값(y)과 매우 근접한 경우</p>
</li>
</ul>
<br>

<h3 id="에러-계산">에러 계산</h3>
<p>에러는 예측값과 실제값 사이의 차이를 의미하며, 에러가 낮을수록 모델의 성능이 좋다고 할 수 있다.</p>
<ul>
<li>에러 계산: Error = y - 𝑦^</li>
<li>평균 제곱 오차(MSE): 에러의 제곱을 평균하여 계산하는 방법 :<blockquote>
</blockquote>
</li>
<li><em><code>MSE = (1/N) * Σ(yi - 𝑦^i)^2</code>*</em></li>
</ul>
<p>에러 계산 시, 왜 제곱을 사용하는지에 대한 이유는 다음과 같다:</p>
<ul>
<li><p><strong>음수 에러 제거</strong>: 에러의 절대값을 사용하면 양수와 음수 에러가 상쇄되는 것을 방지할 수 있다.</p>
</li>
<li><p><strong>큰 에러 강조</strong>: 제곱을 통해 큰 에러의 영향력을 더 크게 반영할 수 있다.</p>
</li>
</ul>
<p>예를 들어, 다음과 같은 예측값과 실제값이 있다고 가정하자:</p>
<ul>
<li><strong>실제값(y)</strong>: -2, 2</li>
<li><strong>예측값(𝑦^)</strong>: -1, 3</li>
</ul>
<p>에러를 계산해보면:</p>
<ul>
<li><strong>첫 번째 데이터</strong>: 에러 = -2 - (-1) = -1</li>
<li><strong>두 번째 데이터</strong>: 에러 = 2 - 3 = -1</li>
</ul>
<p>이 경우, 에러의 제곱은:</p>
<ul>
<li><strong>첫 번째 데이터</strong>: (-1)^2 = 1</li>
<li><strong>두 번째 데이터</strong>: (-1)^2 = 1</li>
</ul>
<p>에러의 합은 2가 된다. 만약 절대값을 사용한다면, 에러의 합은 여전히 2이지만, 제곱을 사용함으로써 큰 에러의 영향을 더 크게 반영할 수 있다.</p>
<br>

<h3 id="모델-학습과-예측">모델 학습과 예측</h3>
<p>지도 학습에서, 우리가 알고리즘을 사용해 x와 y의 관계를 학습시키면, 새로운 x 값을 입력받았을 때 y 값을 예측할 수 있다. 이때 예측된 y 값을 𝑦^라고 한다.</p>
<blockquote>
</blockquote>
<ul>
<li><code>y</code>: 실제값 (정답 데이터)</li>
<li><code>𝑦^</code>: 예측값 (모델이 예측한 값)</li>
</ul>
<p>모델이 새로운 데이터를 학습할 때, 손실 함수를 통해 예측값과 실제값 사이의 차이를 최소화하려고 노력한다. 이를 통해 모델의 성능을 지속적으로 개선할 수 있다.</p>
<br>


<h3 id="바이어스와-분산">바이어스와 분산</h3>
<p>에러를 구성하는 두 가지 주요 요소는 <strong>바이어스(Bias)</strong>와 <strong>분산(Variance)</strong>이다. 이 두 요소의 균형을 맞추는 것이 중요하다.</p>
<ul>
<li><p>** 바이어스(Bias)**: 예측값의 평균과 실제 값 사이의 차이. 높은 바이어스는 모델이 단순화되어 실제 데이터의 패턴을 잘 잡아내지 못하는 경우를 나타낸다.</p>
</li>
<li><p><strong>분산(Variance)</strong>: 예측값들이 얼마나 흩어져 있는지, 즉 예측값의 변동성을 나타낸다. 높은 분산은 모델이 데이터의 노이즈까지 학습하여 새로운 데이터에 대해 불안정한 예측을 하는 경우를 나타낸다.</p>
</li>
</ul>
<br>

<h3 id="바이어스와-분산의-관계">바이어스와 분산의 관계</h3>
<p>모델의 복잡도와 바이어스, 분산의 관계는 다음과 같다:</p>
<ul>
<li>Low Bias, Low Variance: 이상적인 상황. 예측값이 실제값에 매우 가깝고, 예측값들의 변동성도 낮다.</li>
<li>Low Bias, High Variance: 예측값의 평균은 실제값에 가깝지만, 예측값들이 크게 흩어져 있는 경우.</li>
<li>High Bias, Low Variance: 예측값들이 실제값에서 멀리 떨어져 있지만, 예측값들 간의 변동성은 낮은 경우.</li>
<li>High Bias, High Variance: 최악의 상황. 예측값들이 실제값에서 멀리 떨어져 있고, 예측값들 간의 변동성도 높은 경우.</li>
</ul>
<br>

<h3 id="바이어스와-분산을-고려한-에러-분석">바이어스와 분산을 고려한 에러 분석</h3>
<blockquote>
</blockquote>
<ul>
<li><p><strong><code>에러(Error) = 바이어스(Bias) + 분산(Variance) + 노이즈(Noise)</code></strong></p>
</li>
<li><p>바이어스를 낮추기 위해 모델의 복잡도를 높이고, 분산을 낮추기 위해 모델의 복잡도를 줄이는 트레이드오프를 이해해야 한다. 노이즈는 데이터의 품질에 영향을 받으므로, 데이터 클렌징을 통해 최소화해야 한다.</p>
</li>
</ul>
<br>

<h3 id="언더피팅과-오버피팅">언더피팅과 오버피팅</h3>
<ul>
<li><p>언더피팅(Underfitting): 모델이 데이터의 패턴을 잘 학습하지 못한 경우. 바이어스가 높고, 분산이 낮다.</p>
</li>
<li><p>오버피팅(Overfitting): 모델이 데이터의 노이즈까지 학습하여 새로운 데이터에 대해 불안정한 예측을 하는 경우. 바이어스가 낮고, 분산이 높다.</p>
</li>
</ul>
<br>

<h3 id="선형-회귀-linear-regression">선형 회귀 (Linear Regression)</h3>
<p>선형 회귀는 두 변수 사이의 관계를 직선으로 모델링하는 방법이다. 가장 단순한 형태인 단순 <strong>선형 회귀(Simple Linear Regression)</strong>는 독립 변수가 하나(x)인 경우를 다룬다.</p>
<p><strong>예시: 아버지의 키(x)가 아들의 키(y)에 어떤 영향을 미치는지 분석</strong></p>
<blockquote>
</blockquote>
<ul>
<li>모델: <strong><code>y = β0 + β1x + ε</code></strong></li>
<li><strong><code>β0</code></strong>: y 절편, 즉 x가 0일 때 y의 값</li>
<li><strong><code>β1</code></strong>: x가 y에 미치는 영향, 즉 x가 1 단위 증가할 때 y가 변화하는 정도</li>
<li><strong><code>ε</code></strong>: 오차(term), 실제 값과 예측 값 사이의 차이</li>
</ul>
<br>

<h3 id="모델-피팅-과정">모델 피팅 과정</h3>
<p>선형 회귀 모델을 피팅하는 과정은 다음과 같다:</p>
<blockquote>
</blockquote>
<ul>
<li><strong>데이터 수집</strong>: 독립 변수(x)와 종속 변수(y) 데이터를 수집</li>
<li><strong>모델 설정</strong>: y = β0 + β1x 형태의 모델을 설정</li>
<li><strong>손실 함수 설정</strong>: 평균 제곱 오차(MSE)를 사용하여 모델의 예측 값과 실제 값 사이의 차이를 계산</li>
<li><strong>최적화</strong>: MSE를 최소화하는 β0와 β1 값을 찾음</li>
<li><strong>모델 평가</strong>: 새로운 데이터를 사용하여 모델의 예측 성능 평가</li>
</ul>
<h3 id="손실-함수와-에러-최소화"><strong>손실 함수와 에러 최소화</strong></h3>
<p>손실 함수는 모델의 예측 값과 실제 값 사이의 차이를 측정하는 함수이다. 선형 회귀에서 일반적으로 사용하는 손실 함수는 평균 제곱 오차(MSE)이다.</p>
<ul>
<li>손실 함수: <strong><code>MSE = (1/N) * Σ(yi - 𝑦^i)^2</code></strong></li>
<li><strong><code>yi</code></strong>: 실제 값</li>
<li><strong><code>𝑦^i</code></strong>: 예측 값</li>
<li><strong><code>N</code></strong>: 데이터의 개수</li>
</ul>
<p>선형 회귀 모델은 이 손실 함수를 최소화하는 방향으로 파라미터(β0, β1)를 조정하여 데이터를 가장 잘 설명하는 직선을 찾는다.</p>
<br>

<h3 id="에러-최소화">에러 최소화</h3>
<p>우리는 손실 함수(Loss Function)를 최소화하는 방향으로 모델을 피팅시킨다. 이를 위해, 평균 제곱 오차(MSE)를 최소화하는 β0와 β1을 찾는 것이 목표이다.</p>
<blockquote>
</blockquote>
<ul>
<li>손실 함수: MSE = (1/N) * Σ(yi - 𝑦^i)^2</li>
<li>yi: 실제 값</li>
<li>𝑦^i: 예측 값</li>
<li>N: 데이터의 개수</li>
</ul>
<p>여기서 𝑦^i를 β0 + β1xi로 표현할 수 있다. 이때 yi는 실제 값이고, 𝑦^i는 모델이 예측한 값이다. 이를 바탕으로 에러를 최소화하는 것이 목표이다.</p>
<blockquote>
</blockquote>
<ul>
<li>모델: 𝑦^i = β0 + β1xi</li>
<li>β0: y 절편 (x가 0일 때 y의 값)</li>
<li>β1: 기울기 (x가 1 단위 증가할 때 y의 변화량)
에러 최소화 과정</li>
<li>에러 계산: yi - 𝑦^i (실제 값과 예측 값의 차이)
손실 함수 설정: 에러의 제곱을 합산하여 최소화하는 방향으로 β0와 β1 값을 찾는다.</li>
<li>최적화: β0와 β1 값을 조정하여 손실 함수의 값을 최소화한다.
이 과정을 통해 우리는 데이터에 가장 잘 맞는 직선을 찾게 된다. 이때 에러는 yi와 𝑦^i의 차이이며, 이를 제곱하여 합산한 값을 최소화하는 것이 목표이다.</li>
</ul>
<br>

<h3 id="다중-선형-회귀-multiple-linear-regression">다중 선형 회귀 (Multiple Linear Regression)</h3>
<p>단순 선형 회귀는 독립 변수가 하나인 경우를 다루지만, 다중 선형 회귀는 독립 변수가 여러 개인 경우를 다룬다. 예를 들어, x1과 x2 두 개의 독립 변수가 있는 경우를 생각해보자.</p>
<blockquote>
</blockquote>
<ul>
<li>모델: y = β0 + β1x1 + β2x2</li>
<li>β0: y 절편</li>
<li>β1: x1의 기울기</li>
<li>β2: x2의 기울기</li>
</ul>
<p>다중 선형 회귀에서는 x1과 x2가 3차원 공간에서 평면으로 표현된다. 이 평면이 데이터 포인트들과의 에러가 최소가 되도록 피팅되는 것이 목표이다.</p>
<p><strong>에러 최소화 과정</strong></p>
<blockquote>
</blockquote>
<ul>
<li>모델 설정: y = β0 + β1x1 + β2x2 형태의 모델 설정
손실 함수 설정: MSE를 사용하여 모델의 예측 값과 실제 값 사이의 차이를 계산</li>
<li>최적화: MSE를 최소화하는 β0, β1, β2 값을 찾는다</li>
</ul>
<br>

<h3 id="에러와-기울기">에러와 기울기</h3>
<p>모델의 예측 값(𝑦^i)은 다음과 같이 표현된다:</p>
<ul>
<li>예측 값: 𝑦^i = β0 + β1x1 + β2x2</li>
</ul>
<p>실제 값(y)을 다음과 같이 분해할 수 있다:</p>
<ul>
<li>실제 값: yi = β0 + β1x1 + β2x2 + εi</li>
</ul>
<p>여기서 εi는 실제 값과 예측 값 사이의 에러를 나타낸다. 에러를 최소화하기 위해 우리는 손실 함수의 값을 최소화하는 방향으로 β0, β1, β2를 조정한다.</p>
<br>

<h3 id="에러-최소화의-이유">에러 최소화의 이유</h3>
<p>손실 함수로 평균 제곱 오차(MSE)를 사용하는 이유는 다음과 같다:</p>
<ul>
<li><p><strong>미분 가능성</strong>: MSE는 미분 가능하기 때문에 기울기가 0인 지점을 찾을 수 있다.</p>
</li>
<li><p><strong>큰 에러 강조</strong>: 제곱을 통해 큰 에러의 영향력을 더 크게 반영할 수 있다.</p>
</li>
</ul>
<p>절대값을 사용하는 MAE는 미분이 불가능하기 때문에 MSE를 사용하는 것이 일반적이다. MSE는 미분을 통해 에러가 최소화되는 지점을 찾기 용이하며, 이 지점에서 β0, β1, β2 값을 추정할 수 있다.</p>
<br>

<h3 id="결론">결론</h3>
<p>데이터 분석을 진행할 때 정형 데이터와 비정형 데이터를 구분하고, 각각의 특성에 맞는 분석 기법을 적용하는 것이 중요하다. </p>
<p>선형 회귀와 손실 함수는 정형 데이터를 분석하는 데 유용한 도구이며, 비정형 데이터는 이미지, 텍스트, 오디오 등의 다양한 형태로 존재하며 고도의 분석 기법이 필요하다. </p>
<p>머신러닝의 지도 학습과 비지도 학습 개념을 이해하고, 손실 함수를 통해 모델을 평가하고 개선하는 방법을 배우는 것이 데이터 분석의 핵심이다. </p>
<p>이를 통해 우리는 다양한 데이터를 효과적으로 분석하고 유의미한 인사이트를 도출할 수 있다. 좋은 알고리즘은 에러가 낮은 알고리즘이며, 이를 위해 에러를 제곱하여 계산하는 방법을 사용한다.</p>
<p>다음 시간에는 β0와 β1, β2 계수를 추정하는 방법을 다룰 것이다. 이를 위해 우리는 미분을 통해 기울기가 0인 지점을 찾아야 한다. 이 지점이 에러가 가장 낮은 점이므로, 이를 기반으로 최적의 β 값을 추정할 수 있다.</p>
<p>이를 통해 우리는 선형 회귀 모델을 완성하고, 데이터에 가장 잘 맞는 직선을 찾게 된다. 다중 선형 회귀의 경우에도 동일한 방법을 적용하여 독립 변수들이 여러 개인 경우에도 모델을 피팅할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로젝트] 넷플릭스 사용자 선호 컨텐츠 분석]]></title>
            <link>https://velog.io/@kim__1323/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%88%99%EB%B0%95%EC%98%88%EC%95%BD-%EC%88%98%EC%9A%94-%EB%B6%84%EC%84%9D-f40z5rqb</link>
            <guid>https://velog.io/@kim__1323/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%88%99%EB%B0%95%EC%98%88%EC%95%BD-%EC%88%98%EC%9A%94-%EB%B6%84%EC%84%9D-f40z5rqb</guid>
            <pubDate>Thu, 18 Jul 2024 09:30:51 GMT</pubDate>
            <description><![CDATA[<p>아래는 2008~2021년 의 기간 동안 수집한 넷플리스의 메타데이터를 분석해 A회사가 컨텐츠 제작 및 구매 전략을 만들기 위해 사용자의 선호도와 시청패턴에 대해 이해하고 인사이트를 도출하기 위한 데이터 분석 프로젝트이다.</p>
<br>

<h3 id="문제-상황은-이렇다"><strong>문제 상황은 이렇다:</strong></h3>
<p>🔖
A사는 넷플릭스 플랫폼에서 제공하는 컨텐츠에 대한 사용자 선호도와 관련된 데이터 분석 작업을 통해 시청자 행동과 선호를 이해하고자 한다.</p>
<p><strong>⛳ 문제정의</strong></p>
<p>▶ 넷플릭스 사용자들의 대량의 컨텐츠에 대한 선호도를 분석하고, 이를 통해 컨텐츠 제작 및 구매 전략에 필요한 인사이트를 도출해야 함</p>
<p><strong>⛳ 기대효과</strong></p>
<p>▶ 데이터 분석을 통해 사용자의 선호와 시청 패턴에 대한 정확한 이해를 바탕으로 전략적인 컨텐츠 관리 및 마케팅 계획을 수립할 수 있음
▶ 시청자 데이터를 기반으로 한 인사이트 도출을 통해 넷플릭스의 컨텐츠 포트폴리오를 최적화할 수 있음</p>
<p><strong>⛳ 해결방안</strong></p>
<p>▶ 메타데이터를 활용한 다차원적인 데이터 분석 수행
▶ 사용자의 시청 기록과 프로파일 데이터를 분석하여 선호도 패턴 이해
▶ 인구통계학적 데이터와 결합하여 시청자 기반의 시장 분석 수행</p>
<p><strong>⛳ 성과측정</strong></p>
<p>▶ 분석 결과의 인사이트가 컨텐츠 관련 의사결정에 얼마나 영향을 미치는지 평가
▶ 데이터 기반 결정의 성과를 측정하기 위한 KPI 설정(예: 컨텐츠 관람수, 사용자 확보 수, 구독 전환율 등)</p>
<p><strong>⛳ 운영</strong></p>
<p>▶ 정기적인 데이터 분석을 통한 시장 동향 및 사용자 선호의 변화 모니터링
▶ 분석 결과를 컨텐츠 구매, 제작, 마케팅 전략에 실질적으로 적용</p>
<hr>
<h3 id="데이터셋-정의"><strong>데이터셋 정의</strong></h3>
<BR>

<pre><code class="language-python"># ▶ Data read
import pandas as pd
df = pd.read_csv(&#39;S_PJT13_DATA.csv&#39;)
df.head()

  show_id     type  title           director                                               cast        country        date_added  release_year rating  duration                                        listed_in                                       description
0      s1  TV Show     3%               None  João Miguel, Bianca Comparato, Michel Gomes, R...         Brazil    August 14, 2020          2020  TV-MA  4 Seasons  International TV Shows, TV Dramas, TV Sci-Fi &amp;...  In a future where the elite inhabit an island ...
1      s2    Movie   7:19  Jorge Michel Grau  Demián Bichir, Héctor Bonilla, Oscar Serrano, ...         Mexico  December 23, 2016          2016  TV-MA     93 min                       Dramas, International Movies  After a devastating earthquake hits Mexico Cit...
2      s3    Movie  23:59      Gilbert Chan  Tedd Chan, Stella Chung, Henley Hii, Lawrence ...      Singapore  December 20, 2018          2011      R     78 min                        Horror Movies, International Movies  When an army recruit is found dead, his fellow...
3      s4    Movie      9        Shane Acker  Elijah Wood, John C. Reilly, Jennifer Connelly...  United States  November 16, 2017          2009  PG-13     80 min  Action &amp; Adventure, Independent Movies, Sci-Fi...  In a postapocalyptic world, rag-doll robots hi...
4      s5    Movie     21   Robert Luketic  Jim Sturgess, Kevin Spacey, Kate Bosworth, Aar...  United States   January 1, 2020          2008  PG-13    123 min                                              Dramas  A brilliant group of students become card-coun...

</code></pre>
<br>


<ul>
<li>컬럼 정보<pre><code>show_id: 각 영화/TV 쇼의 고유 ID
type: 식별자 - 영화 또는 TV 쇼
title: 영화 / TV 쇼의 제목
director: 영화의 감독
cast: 영화/쇼에 참여한 배우들
country: 영화/쇼가 제작된 국가
date_added: Netflix에 추가된 날짜
release_year: 영화/쇼의 실제 출시 연도
rating: 영화/쇼의 TV 평점
duration: 총 시간 - 분 또는 시즌 수
</code></pre></li>
</ul>
<pre><code>
__________

### **데이터 전처리 및 EDA**


### (1) Data shape(형태) 확인

```python
# ▶ Data 형태 확인
# ▶ 7,787 row, 12 col로 구성됨
print(&#39;df&#39;, df.shape)

&gt; df (7787, 12)</code></pre><ul>
<li>7787 개의 데이터가 있으며 12개의 컬럼으로 이루어져 있음.</li>
</ul>
<h3 id="2-data-type-확인">(2) Data type 확인</h3>
<pre><code class="language-python"># ▶ Data type 확인
df.info()

&lt;class &#39;pandas.core.frame.DataFrame&#39;&gt;
RangeIndex: 7787 entries, 0 to 7786
Data columns (total 12 columns):
 #   Column        Non-Null Count  Dtype 
---  ------        --------------  ----- 
 0   show_id       7787 non-null   object
 1   type          7787 non-null   object
 2   title         7787 non-null   object
 3   director      5398 non-null   object
 4   cast          7069 non-null   object
 5   country       7280 non-null   object
 6   date_added    7777 non-null   object
 7   release_year  7787 non-null   int64 
 8   rating        7780 non-null   object
 9   duration      7787 non-null   object
 10  listed_in     7787 non-null   object
 11  description   7787 non-null   object
dtypes: int64(1), object(11)
memory usage: 730.2+ KB</code></pre>
<p>총 11개의 열 데이터가 있으며 <code>duration</code>, <code>date_added</code> 등 숫자형이나 날짜형 타입의 데이터들이 <code>object</code> 문자형 데이터로 선언이 돼있다.</p>
<p>필요에 따라 데이터 분석 진행 시 <code>int</code>나 <code>date</code> 타입으로 변경시키는게 좋을 거 같다.</p>
<h3 id="3-null값-확인--빈-값의-data">(3) Null값 확인 (※ 빈 값의 Data)</h3>
<pre><code class="language-python">show_id            0
type               0
title              0
director        2389
cast             718
country          507
date_added        10
release_year       0
rating             7
duration           0
listed_in          0
description        0
dtype: int64</code></pre>
<ul>
<li><code>director</code>, <code>cast</code>, <code>country</code>, 등의 데이터는 컨텐츠 분석을 위해 크게 중요한 변수는 아니기에 null값 처리를 따로 해주지는 않았다.</li>
</ul>
<p><strong>null 값 처리</strong></p>
<br>



<h3 id="4-outlier-확인-이상치-정상적인-범주를-벗어난-data"><strong>(4) Outlier 확인 (이상치, 정상적인 범주를 벗어난 Data)</strong></h3>
<pre><code class="language-python"># ▶ Outlier 확인
df.describe()
</code></pre>
<ul>
<li><code>release_year</code>는 출시년도를 의미하는 데이터로 이상치가 있다해도 크게 의미 없는 데이터라 이상치가 존재하지 않는다고 할 수 있다.</li>
<li><em>음수값 제거*</em></li>
</ul>
<h3 id="5-데이터-eda"><strong>(5) 데이터 EDA</strong></h3>
<p><strong>중요 변수 시각화 및 탐색</strong></p>
<p>사용자들의 컨텐츠 선호도를 분석하기 위해 중요한 변수들인 <code>type</code>, <code>release_year</code>, <code>rating</code>, <code>country</code>, <code>duration</code> 변수들의 분포를 시각적으로 확인해보았다.</p>
<pre><code class="language-python">import matplotlib.pyplot as plt
import seaborn as sns
plt.style.use(&#39;dark_background&#39;)

# ▶ 설정: Seaborn
sns.set(style=&quot;whitegrid&quot;)

# ▶ 데이터 프레임에서 &#39;duration&#39;의 숫자만 추출하기 위해 필요한 작업
df[&#39;duration_num&#39;] = df[&#39;duration&#39;].str.extract(&#39;(\d+)&#39;).astype(float)

#  ▶ 시각화를 위한 그래프 크기 설정
plt.figure(figsize=(20, 15))

# ▶ type 분포
plt.subplot(3, 2, 1)
sns.countplot(data=df, x=&#39;type&#39;)
plt.title(&#39;Distribution of Content Types&#39;)

# ▶ release_year 분포
plt.subplot(3, 2, 2)
sns.countplot(data=df, y=&#39;release_year&#39;, order=df[&#39;release_year&#39;].value_counts().index[:20])
plt.title(&#39;Distribution of Release Years&#39;)

# ▶ rating 분포
plt.subplot(3, 2, 3)
sns.countplot(data=df, x=&#39;rating&#39;, order=df[&#39;rating&#39;].value_counts().index)
plt.title(&#39;Distribution of Ratings&#39;)

# ▶ country 분포 (상위 20개)
plt.subplot(3, 2, 4)
top_countries = df[&#39;country&#39;].value_counts().index[:20]
sns.countplot(data=df, y=&#39;country&#39;, order=top_countries)
plt.title(&#39;Top 20 Countries with Most Content&#39;)

# ▶ duration 분포 (영화만)
plt.subplot(3, 2, 5)
sns.histplot(data=df[df[&#39;type&#39;] == &#39;Movie&#39;], x=&#39;duration_num&#39;, bins=30, kde=True)
plt.title(&#39;Distribution of Movie Durations&#39;)


# ▶ 그래프 보여주기
plt.tight_layout()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/dc6893d1-9cc8-4279-bcfc-b17b4fa125ba/image.png" alt=""></p>
<p>중요 변수 시각화 후 분석 결과는 다음과 같다:  </p>
<ul>
<li><strong>컨텐츠 유형 분포 (Distribution of Content Types)</strong>: &#39;Movie&#39;가 &#39;TV Show&#39;보다 훨씬 많은 것으로 나타난다.</li>
</ul>
<ul>
<li><p><strong>출시 연도 분포 (Distribution of Release Years)</strong>: 최근 몇 년 동안 출시된 컨텐츠의 수가 많으며, 특히 2018년과 2019년에 많은 컨텐츠가 출시된 것으로 보인다.</p>
</li>
<li><p><strong>평점 분포 (Distribution of Ratings)</strong>: &#39;TV-MA&#39; (성인 대상) 등급의 컨텐츠가 가장 많고, 그 다음으로 &#39;TV-14&#39; (14세 이상 대상) 등급의 컨텐츠가 많다.</p>
</li>
<li><p><strong>콘텐츠 제작 국가 분포 (Top 20 Countries with Most Content)</strong>: 미국이 압도적으로 많은 컨텐츠를 제작한 것으로 나타나며, 그 다음으로는 인도, 영국, 일본 순이다.</p>
</li>
<li><p><strong>영화 시간 분포 (Distribution of Movie Durations)</strong>:  대부분의 영화는 80분에서 120분 사이의 길이를 가지고 있으며, 특히 100분 주변에 많은 영화가 분포하고 있다.</p>
</li>
</ul>
<hr>
<br>

<br>



<h3 id="컨텐츠-제작-전략-수립을-위한-컨텐츠-분석"><strong>컨텐츠 제작 전략 수립을 위한 컨텐츠 분석</strong></h3>
<p>컨텐츠 제작 전략 수립을 위한 컨텐츠 분석을 위해 아래의 항목들을 분석 및 시각화해보았다.</p>
<ul>
<li>장르별 컨텐츠</li>
<li>국가별 컨텐츠</li>
<li>시청자 컨텐츠별 선호도</li>
<li>넷플릭스의 컨텐츠 업데이트 추세 패턴 </li>
</ul>
<h3 id="1-장르별-컨텐츠-분석"><strong>(1) 장르별 컨텐츠 분석</strong></h3>
<p>일단 <code>&#39;reservation_status&#39;</code> 즉 전체적인 예약의 현황을 살펴보았다.</p>
<pre><code class="language-python">from collections import Counter
plt.style.use(&#39;dark_background&#39;)
# ▶ 장르별 컨텐츠 분석을 위해 &#39;listed_in&#39; 열에서 각 장르를 분리하고 카운트합니다.
genre_counts = Counter()
df[&#39;listed_in&#39;].str.split(&#39;, &#39;).apply(genre_counts.update)
# 1. df[&#39;listed_in&#39;].str.split(&#39;, &#39;): &#39;listed_in&#39; 열의 각 항목을 &#39;, &#39; 기준으로 분리한다.
# 2. .apply(genre_counts.update): 분리된 각 장르를 Counter 객체인 genre_counts에 업데이트하여 카운트한다.


# ▶ 가장 많이 등장하는 장르 상위 10개를 추출합니다.
top_genres = genre_counts.most_common(10)

# ▶ 장르별 컨텐츠 시각화
plt.figure(figsize=(10, 5))
genres = pd.DataFrame(top_genres, columns=[&#39;Genre&#39;, &#39;Count&#39;])
sns.barplot(data=genres, x=&#39;Count&#39;, y=&#39;Genre&#39;)
plt.title(&#39;Top Genres on Netflix&#39;)
plt.xlabel(&#39;Number of Contents&#39;)
plt.ylabel(&#39;Genre&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/0436b14f-f0a9-43b0-bc54-0aa640997bab/image.png" alt=""></p>
<ul>
<li>장르별 컨텐츠의 분포를 확인해보니 가장 인기 있는 장르는 <code>&#39;International Movies&#39;</code> 국제 영화와 <code>&#39;Dramas&#39;</code> 드라마로 나타났디. 이는 넷플릭스가 국제적인 시청자를 대상으로 다양한 영화를 제공하고 있음을 시사한다.</li>
</ul>
<br> 


<h3 id="2-국가별-컨텐츠-분석"><strong>(2) 국가별 컨텐츠 분석</strong></h3>
<pre><code class="language-python"># ▶ 국가별 컨텐츠 분석을 위한 준비: &#39;country&#39; 열을 분리하고 카운트한다.
country_counts = Counter()
df[&#39;country&#39;].dropna().str.split(&#39;, &#39;).apply(country_counts.update)  # 결측치 제거 후 진행

# ▶ 가장 많은 컨텐츠를 생산한 국가 상위 10개를 추출한다.
top_countries = country_counts.most_common(10)

# ▶ 국가별 컨텐츠 시각화
plt.figure(figsize=(10, 5))
countries = pd.DataFrame(top_countries, columns=[&#39;Country&#39;, &#39;Count&#39;])
sns.barplot(x=&#39;Count&#39;, y=&#39;Country&#39;, data=countries)
plt.title(&#39;Top Countries Producing Content on Netflix&#39;)
plt.xlabel(&#39;Number of Contents&#39;)
plt.ylabel(&#39;Country&#39;)
plt.show()
</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/690d537c-6482-4721-afc6-28f82e549214/image.png" alt=""></p>
<ul>
<li>국가별로 제작된 컨텐츠의 비중을 확인해보니 미국이 가장 많은 컨텐츠를 생산한 국가로 나타나며, 인도와 영국이 그 뒤를 이었다. 이는 넷플릭스의 컨텐츠가 주로 이 세 국가에서 제작됐음을 확인할 수 있다.</li>
</ul>
<br> 


<h3 id="3-시청자-컨텐츠-타입별-선호도-분석"><strong>(3) 시청자 컨텐츠 타입별 선호도 분석</strong></h3>
<pre><code class="language-python"># ▶ 시청자 선호도 분석을 위한 준비: &#39;rating&#39; 열의 분포를 조사합니다.
rating_counts = df[&#39;rating&#39;].value_counts()

# ▶ 출시 연도와 컨텐츠 인기도의 관계 분석을 위한 준비: &#39;release_year&#39; 별 컨텐츠 수를 계산합니다.
release_year_counts = df[&#39;release_year&#39;].value_counts().sort_index()

# ▶ 시청자 선호도(등급별) 시각화
plt.figure(figsize=(10, 5))
sns.barplot(x=rating_counts.values, y=rating_counts.index)
plt.title(&#39;Distribution of Content Ratings on Netflix&#39;)
plt.xlabel(&#39;Number of Contents&#39;)
plt.ylabel(&#39;Rating&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/79d3ccee-3e5e-45f8-b69f-f8a0af133aa6/image.png" alt=""></p>
<ul>
<li>가장 많은 컨텐츠는 <code>&#39;TV-MA&#39; (성인 대상)</code> 등급이며, <code>&#39;TV-14&#39; (14세 이상 대상)</code> 등급이 그 뒤를 이어 두 번째 순서이다. 이는 넷플릭스가 성인 시청자를 주요 타겟으로 하고 있다고 해석할 수 있다.</li>
</ul>
<br>


<h3 id="4-넷플릭스의-컨텐츠-업데이트-추세-패턴"><strong>(4) 넷플릭스의 컨텐츠 업데이트 추세 패턴</strong></h3>
<pre><code class="language-python">#▶  컨텐츠 업데이트 패턴 분석을 위한 준비: &#39;date_added&#39; 열을 datetime으로 변환하고 연도별로 분석한다.
df[&#39;date_added&#39;] = pd.to_datetime(df[&#39;date_added&#39;], errors=&#39;coerce&#39;)
content_update_by_year = df[&#39;date_added&#39;].dt.year.value_counts().sort_index()
content_update_by_year.index = content_update_by_year.index.astype(int)


# ▶ 출시 연도별 컨텐츠 시각화
# ▶ 연도별 컨텐츠 업데이트 패턴 시각화
plt.figure(figsize=(10, 5))
sns.barplot(x=content_update_by_year.index, y=content_update_by_year.values)
plt.title(&#39;Yearly Content Update Pattern on Netflix&#39;)
plt.xlabel(&#39;Year&#39;)
plt.ylabel(&#39;Number of Contents Added&#39;)
plt.xticks(rotation=45)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/82b3e46a-544b-4591-afdc-9edb8f391f9b/image.png" alt=""></p>
<ul>
<li><p>넷플릭스는 2016년 이후로 매년 더 많은 컨텐츠를 추가하고 있으며, 2019년에 가장 많은 컨텐츠를 제작했다. </p>
</li>
<li><p>그렇다면 2008~2021년의 기간동안 월별로는 컨텐츠가 몇 편이나 제작됐는지 알아보자.</p>
</li>
</ul>
<br>  

<h4 id="넷플릭스-월별-컨텐츠-업데이트-분석"><strong>넷플릭스 월별 컨텐츠 업데이트 분석</strong></h4>
<pre><code class="language-python"># ▶ 월별 컨텐츠 업데이트 패턴 시각화

df[&#39;date_added&#39;] = pd.to_datetime(df[&#39;date_added&#39;], errors=&#39;coerce&#39;)
content_update_by_month = df[&#39;date_added&#39;].dt.month_name().value_counts()

plt.figure(figsize=(10, 5))
months_order = [&#39;January&#39;, &#39;February&#39;, &#39;March&#39;, &#39;April&#39;, &#39;May&#39;, &#39;June&#39;, &#39;July&#39;,
                &#39;August&#39;, &#39;September&#39;, &#39;October&#39;, &#39;November&#39;, &#39;December&#39;]
sns.barplot(x=content_update_by_month.index, y=content_update_by_month.values, order=months_order)
plt.title(&#39;Monthly Content Update Pattern on Netflix&#39;)
plt.xlabel(&#39;Month&#39;)
plt.ylabel(&#39;Number of Contents Added&#39;)
plt.xticks(rotation=45)
plt.show()</code></pre>
<p>12월에 가장 많은 컨텐츠가 추가되었으며, 10월 1월에도 비슷한 수준이지만 상대적으로 다른 월에 비해 많은 컨텐츠가 추가된 것을 확인할 수 있다.</p>
<p>그래서 10월~내년도 1월까지 특히 연말에 컨텐츠 추가가 집중되는 경향이 있는 것으로 보인다. 이는 연말 시즌에 맞춘 콘텐츠 업데이트 전략을 반영할 수 있을 것으로 보인다.</p>
<p><br><br></p>
<hr>
<p><strong>결론</strong></p>
<ul>
<li><p>넷플릭스는 최근 몇 년 동안 급성장세를 보였으며 2016년을 시작으로 2020년까지 다수의 컨텐츠를 제작하고 있는 것을 확인했다.</p>
</li>
<li><p>12월에 가장 많은 컨텐츠가 추가되었으며, 10월 1월에도 비슷한 수준이지만 상대적으로 다른 월에 비해 많은 컨텐츠가 추가된 것을 확인할 수 있다.</p>
</li>
<li><p>그래서 10월~내년도 1월까지 특히 연말에 컨텐츠 추가가 집중되는 경향이 있는 것으로 보인다. 이는 연말 시즌에 맞춘 콘텐츠 업데이트 전략을 반영할 수 있을 것으로 보인다.  </p>
</li>
<li><p>넷플릭스에서 제작된 컨텐츠들은 티비쇼보다는 영화 제작에 더 집중을 하고 있으며 컨텐츠는 대부분을 성인을 타깃으로 하는 컨텐츠를 제작한 것으로 보인다.</p>
</li>
<li><p>영화 컨텐츠의 제작 길이는 80분에서 120분 사이의 길이를 가지고 있어 극장 영화보다 짧은 시간의 길이를 가지고 있다.  </p>
</li>
<li><p>아무래도 미국 회사다 보니 미국이 압도적으로 컨텐츠 제작이 많으며 그 다음은 영화산업이 발달 돼 있는 인도가 위치해 있으며 그 다음으로 영국, 일본이 뒤를 이었다. 이는 넷플릭스의 컨텐츠가 주로 이 세 국가에서 제작됐음을 확인할 수 있다.</p>
</li>
</ul>
<ul>
<li>장르별 컨텐츠의 분포를 확인해보니 가장 인기 있는 장르는 <code>&#39;International Movies&#39;</code> 국제 영화와 <code>&#39;Dramas&#39;</code> 드라마로 나타났디. 이는 넷플릭스가 국제적인 시청자를 대상으로 다양한 영화를 제공하고 있음을 시사한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[머신러닝] : 선형대수학]]></title>
            <link>https://velog.io/@kim__1323/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EC%84%A0%ED%98%95%EB%8C%80%EC%88%98%ED%95%99</link>
            <guid>https://velog.io/@kim__1323/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EC%84%A0%ED%98%95%EB%8C%80%EC%88%98%ED%95%99</guid>
            <pubDate>Wed, 17 Jul 2024 03:41:48 GMT</pubDate>
            <description><![CDATA[<h3 id="1-선형대수학-linear-algebra"><strong>1. 선형대수학 (Linear Algebra)</strong></h3>
<p>선형대수학은 선형 연산을 다루는 수학 분야로, 선형대수학은 기초적으로는 <strong>연립방정식</strong>을 풀고, 행렬과 벡터의 연산을 다루는 것을 포함한다.</p>
<p>선형대수학은 머신러닝, 딥러닝, 그리고 검색 엔진 알고리즘 등 현대 인공지능 기술의 기초가 된다. 그리고 선형대수학은 인공지능 알고리즘을 이해하고 구현하는 데 필수적이다. 예를 들어 서포트 벡터 머신(SVM), 딥러닝, 그리고 검색 엔진의 알고리즘 등에서 사용된다.</p>
<br>

<h3 id="2-연립방정식-systems-of-linear-equations"><strong>2. 연립방정식 (Systems of Linear Equations)</strong></h3>
<p>연립방정식은 여러 개의 1차 방정식들이 모여 있는 시스템이다. </p>
<p>예를 들어 다음과 같은 연립방정식을 생각해보자:</p>
<pre><code>3x + 2y = 1
x - y = 2</code></pre><p>이런 시스템을 AX=B 형태로 표현할 수 있다. 여기서 아래와 같이 표현된다.:</p>
<ul>
<li><code>A</code>는 계수 행렬 (coefficient matrix)</li>
<li><code>X</code>는 변수 벡터 (variable vector)</li>
<li><code>B</code>는 상수 벡터 (constant vector)</li>
</ul>
<br>

<h3 id="3-행렬-matrix"><strong>3. 행렬 (Matrix)</strong></h3>
<p>행렬은 숫자들이 직사각형 배열로 나열된 형태로, 선형대수학의 기본 요소 중 하나이다.</p>
<p>예를 들어, 다음과 같이 3개의 행과 2개의 열로 이루어진 행렬을 3x2 행렬이라고 한다:</p>
<pre><code>| 1  2 |
| 3  4 |
| 5  6 |</code></pre><p>여기서 행(Row)과 열(Column)으로 구성된 이 숫자 배열을 통해 여러 연산을 수행할 수 있다.</p>
<br>

<h3 id="4-연립방정식의-행렬-표현-axb"><strong>4. 연립방정식의 행렬 표현 (AX=B)</strong></h3>
<p>연립방정식으로 행렬을 사용하여 AX=B 형태로 표현할 수 있다. 예를 들어:</p>
<pre><code class="language-python">3x + 2y = 1
x - y = 2</code></pre>
<p>이 방정식 시스템은 다음과 같은 형태로 변환된다:</p>
<pre><code class="language-python">| 3  2 |   | x |   =   | 1 |
| 1 -1 |   | y |       | 2 |</code></pre>
<ul>
<li><code>A</code>는 계수 행렬 (coefficient matrix)</li>
<li><code>X</code>는 변수 벡터 (variable vector)</li>
<li><code>B</code>는 상수 벡터 (constant vector)</li>
</ul>
<br>

<h3 id="5-행렬식-determinant"><strong>5. 행렬식 (Determinant)</strong></h3>
<p>행렬식은 행렬의 특성을 나타내는 값으로, 주로 행렬이 역행렬을 가지는지 확인할 때 사용된다. 예를 들어, 2x2 행렬의 행렬식은 다음과 같다:</p>
<pre><code>| a  b |
| c  d |

det(A) = a*d - b*c</code></pre><br>

<h3 id="6-행렬의-요소-matrix-elements"><strong>6. 행렬의 요소 (Matrix Elements)</strong></h3>
<p>행렬의 각 요소는 특정 행과 열에 위치하며, Aij 형태로 표현된다. i는 행의 갯수를 의미하고 j는 열의 객수를 의미한다. 그래서 </p>
<p>예를 들어,  A_33 같은 경우는 3X3 행렬을 의미하며 3x3 행렬의 요소는 다음과 같다:</p>
<pre><code class="language-PYTHON">| a11  a12  a13 |
| a21  a22  a23 |
| a31  a32  a33 |</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[머신러닝] : 인공지능, 인공신경망]]></title>
            <link>https://velog.io/@kim__1323/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EC%9D%B8%EA%B3%B5%EC%A7%80%EB%8A%A5-%EC%9D%B8%EA%B3%B5%EC%8B%A0%EA%B2%BD%EB%A7%9D</link>
            <guid>https://velog.io/@kim__1323/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EC%9D%B8%EA%B3%B5%EC%A7%80%EB%8A%A5-%EC%9D%B8%EA%B3%B5%EC%8B%A0%EA%B2%BD%EB%A7%9D</guid>
            <pubDate>Wed, 17 Jul 2024 02:42:07 GMT</pubDate>
            <description><![CDATA[<h3 id="인공신경망-artificial-neural-network-ann-이해하기"><strong>인공신경망 (Artificial Neural Network, ANN) 이해하기</strong></h3>
<h3 id="1-인공신경망의-기초"><strong>1. 인공신경망의 기초</strong></h3>
<ul>
<li>인공신경망(ANN): 실제 신경망을 모사한 것으로, 인공지능의 한 종류이다. 실제 신경망을 &quot;뉴럴 네트워크&quot;라고 부르며, 앞에 &quot;아티피셜&quot;을 붙여서 &quot;인공신경망&quot;이라고 한다. 실제 신경망은 다음과 같이 구성되어 있으며 인공신경망은 아래의 구성요소들을 수학적으로 묘사하는 것이다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/9051b8df-47ca-448a-b3b5-ce42244f4063/image.png" alt=""></p>
<ul>
<li><p><strong>뉴런(Neuron)</strong>: 뇌세포를 이루는 신경 전달 단위로, 덴드라이트, 소마, 액손으로 구성된다.</p>
</li>
<li><p><strong>덴드라이트(Dendrite)</strong>: 외부에서 들어오는 정보를 전기적 신호를 통해 입력신호를 다른 뉴런으로부터 신호를 받는 기능을 수행한다.</p>
</li>
<li><p><strong>소마(Soma)</strong>: 덴드라이트에서 받은 정보를 처리하는 역할을 한다.</p>
</li>
<li><p><strong>액손(Axon)</strong>: 처리된 정보를 다른 뉴런으로 전달하는 역할을 한다.</p>
</li>
</ul>
<br>

<h3 id="2-인공신경망의-구성-요소"><strong>2. 인공신경망의 구성 요소</strong></h3>
<ul>
<li><p><strong>입력부(Input Layer)</strong>: 실제신경망의 <strong>덴드라이트(Dendrite)</strong>와 같으며 
외부에서 들어오는 정보를 수학적으로 벡터 X (예: x1, x2, x3 등)로 표현되는 데이터를 전달받는 역할을 한다.</p>
</li>
<li><p><strong>가중치(Weights)</strong>: 실제신경망의 <strong>소마(Soma)</strong>와 같으며 입력부(Input Layer) 또는 덴드라이트(Dendrite)에서 얻은 입력 정보를 연산하거나 합산하는 기능을 수행한다. 즉 입력정보에 가중 weight(<code>w1, w2, w3</code> ) 라고 하는 것을 곱하여 합산하는 것으로, 정보의 중요도를 나타낸다.</p>
</li>
<li><p><strong>바이어스(Bias)</strong>: 가중합에 더해지는 값의 결과로, 신경망의 유연성을 높여준다.</p>
</li>
<li><p><strong>출력부(Output Layer)</strong>: 실제신경망의 <strong>액손(Axon)</strong>과 같으며 최종적으로 계산된 결과 값이 출력되는 부분을 의미하며 인공신경망에서는 <code>activation function f</code> 애 입력하여 계산된 결과 값을 <code>y1, y2</code> 로 전달하는 것을 의미한다.</p>
</li>
</ul>
<br>

<h3 id="3-인공신경망-예제"><strong>3. 인공신경망 예제</strong></h3>
<p>입력부에서 수면 시간, 운동 시간, 식사량 등의 데이터가 들어오고, 출력부에서 체중과 혈압을 예측하는 예제를 생각해보자.
<img src="https://velog.velcdn.com/images/kim__1323/post/39e0695a-198b-43ab-8bb0-6ab9c88b22ef/image.png" alt=""></p>
<p><strong>데이터 입력: 수면 시간, 운동 시간, 식사량 (x1, x2, x3)</strong></p>
<p><strong>예제 데이터</strong></p>
<ul>
<li>똘이: 수면 시간 6시간, 운동 시간 3시간, 식사량 2500kcal -&gt; 체중 70kg, 혈압 110</li>
<li>순이: 수면 시간 8시간, 운동 시간 1시간, 식사량 2000kcal-&gt; 체중 60kg, 혈압 100</li>
<li>토니: 수면 시간 10시간, 운동 시간 0시간, 식사량 4000kcal-&gt; 체중 100kg, 혈압 140
&#39;
이 데이터를 기반으로 체중과 혈압을 예측할 수 있는 인공지능 앱을 만들어 볼수도 있다.</li>
</ul>
<br>

<h3 id="4-인공신경망의-학습-과정"><strong>4. 인공신경망의 학습 과정</strong></h3>
<p><strong>(1) 데이터 입력층</strong>: 
수면 시간, 운동 시간, 식사량 입력 벡터: (x1, x2, x3)</p>
<p><strong>(2) 가중치 입력 데이터 초기화</strong>: 
임의의 값으로 설정된 가중치 벡터: (w1, w2, w3)와 바이어스 (b)
가중치(W)와 바이어스(b)를 임의의 값으로 초기화한다. 이 값들은 학습을 통해 조정된다.</p>
<p><strong>(3) 가중합 계산</strong>: 
가중치와 입력 벡터의 곱을 합산하고 바이어스를 더한다. 이 과정을 벡터와 행렬로 표현할 수 있다.</p>
<p>계산 예시: <code>z = w1*x1 + w2*x2 + w3*x3 + b</code></p>
<p><strong>(4) 활성화 함수 적용:</strong> </p>
<p>가중합 <code>Z</code>에 활성화 함수 <code>f</code>를 적용하여 출력 값 <code>Y</code>를 계산한다.</p>
<p><strong>예: y = f(z)</strong></p>
<p><strong>(5) 출력 값과 실제 값 비교</strong>: 
예측된 출력 값 <code>Y</code>와 실제 값 <code>Y&#39;</code>를 비교하여 오차(손실)를 계산한다.</p>
<p><strong>(6) 가중치 수정</strong>: </p>
<p>이후 오차가 존재한다면 오차를 최소화하도록 가중치 W와 바이어스 B 를 수정한다.</p>
<p><strong>(7) 반복 학습</strong>
오차가 최소화될 때까지 학습을 진행한다.
이 과정은 반복적으로 수행되며, 학습이 완료되면 예측 정확도가 높아진다.</p>
<blockquote>
</blockquote>
<p><strong>인공신경망의 학습 과정 요약</strong>
결과적으로 입력 벡터 <code>X</code>와 가중치 <code>W</code>, 바이어스 <code>b</code>를 사용한 가중합 계산과 활성화 함수 적용 반복하여 학습시키는 과정을 요약하면 다음과 같다.</p>
<blockquote>
</blockquote>
<p><strong>1.가중합 계산 *<em>: `z = w1</em>x1 + w2<em>x2 + w3</em>x3 + b`
**2.활성화 함수 적용:</strong> y = f(z)
<strong>3.오차 손실 게산</strong>: 예측된 출력 값 <code>Y</code>와 실제 값 <code>Y&#39;</code>를 비교하여 오차(손실)를 계산한다.
<strong>4. 가중치 수정
5. 반복 학습</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[머신러닝] : 머신러닝이란?]]></title>
            <link>https://velog.io/@kim__1323/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@kim__1323/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Wed, 17 Jul 2024 01:01:25 GMT</pubDate>
            <description><![CDATA[<h3 id="머신러닝이란"><strong>머신러닝이란?</strong></h3>
<p>머신러닝은 명시적으로 프로그래밍하지 않고 컴퓨터가 스스로 규칙을 학습하는 연구 분야이다. 이 개념을 이해하기 위해 중고 스마트폰의 적정 가격을 예로 들어 설명하겠다.</p>
<br>

<h3 id="중고-스마트폰-가격-결정-요인-예제"><strong>중고 스마트폰 가격 결정 요인 예제</strong></h3>
<p>중고 스마트폰의 가격을 결정하는 요소에는 다음과 같은 것들이 있다:</p>
<ul>
<li>제조사</li>
<li>모델명</li>
<li>제조 연월</li>
<li>화면 크기, CPU, 내장 저장 공간, 램, 색상 등의 스펙</li>
<li>사용 상태 (액정 파손, 번인, 찍힘, 생활 기스, 배터리 효율 등)</li>
</ul>
<blockquote>
</blockquote>
<h3 id="기존-프로그래밍-방식"><strong>기존 프로그래밍 방식</strong></h3>
<p>기존 방식에서는 아래처럼 사람이 직접 규칙을 정해서 프로그래밍한다. 예를 들어 <code>get_price</code>라는 함수를 만들어 스마트폰의 상태에 따라 최대 금액에서 차감하는 식으로 가격을 산정한다.</p>
<pre><code class="language-python">def get_price(max_price, is_screen_broken, has_burning, num_dents, has_scratches, low_battery):
    price = max_price
    if is_screen_broken:
        price -= 150000
    if has_burning:
        price -= 50000
    if num_dents &gt; 2:
        price -= 30000
    if has_scratches:
        price -= 5000
    if low_battery:
        price -= 20000
    return price

# 예시
max_price = 300000
is_screen_broken = False
has_burning = True
num_dents = 0
has_scratches = True
low_battery = False

final_price = get_price(max_price, is_screen_broken, has_burning, num_dents, has_scratches, low_battery)
print(final_price)  # 245000</code></pre>
<br>

<blockquote>
</blockquote>
<h3 id="머신러닝-방식"><strong>머신러닝 방식</strong></h3>
<p>머신러닝에서는 기존의 거래 데이터를 통해 기계가 스스로 규칙을 학습한다. 
예를 들어 다음과 같은 데이터가 주어졌을 때 기계는 이 데이터를 바탕으로 가격을 예측하는 모델을 만든다.</p>
<pre><code class="language-python"># 거래 데이터 예시
data = [
    {&quot;manufacturer&quot;: &quot;A&quot;, &quot;model&quot;: &quot;A1&quot;, &quot;year&quot;: 2021, &quot;spec&quot;: &quot;64GB&quot;, &quot;screen_broken&quot;: False, &quot;burning&quot;: True, &quot;dents&quot;: 1, &quot;scratches&quot;: True, &quot;price&quot;: 280000},
    {&quot;manufacturer&quot;: &quot;B&quot;, &quot;model&quot;: &quot;B1&quot;, &quot;year&quot;: 2019, &quot;spec&quot;: &quot;128GB&quot;, &quot;screen_broken&quot;: False, &quot;burning&quot;: False, &quot;dents&quot;: 0, &quot;scratches&quot;: False, &quot;price&quot;: 820000}
    # 추가 데이터...
]

# 머신러닝 학습 및 예측 예시
from sklearn.linear_model import LinearRegression

# 데이터 전처리
X = [[d[&quot;year&quot;], d[&quot;spec&quot;], d[&quot;screen_broken&quot;], d[&quot;burning&quot;], d[&quot;dents&quot;], d[&quot;scratches&quot;]] for d in data]
y = [d[&quot;price&quot;] for d in data]

# 모델 학습
model = LinearRegression()
model.fit(X, y)

# 예측
new_data = [2021, &quot;64GB&quot;, False, True, 0, True]
predicted_price = model.predict([new_data])
print(predicted_price)</code></pre>
<br>

<blockquote>
</blockquote>
<h3 id="머신러닝의-세-가지-주요-유형"><strong>머신러닝의 세 가지 주요 유형</strong></h3>
<p>머신러닝이 학습을 하는 방법도 다양한데 총 세 가지로 분류할 수 있다.</p>
<blockquote>
</blockquote>
<ul>
<li><strong>지도 학습 (Supervised Learning)</strong>: 정답이 있는 데이터를 통해 학습.
예시: 곤충 이미지와 정답(벌, 나비)을 통해 학습한 후 새로운 곤충 이미지를 예측.<blockquote>
</blockquote>
</li>
<li><strong>비지도 학습 (Unsupervised Learning)</strong>: 정답이 없는 데이터를 통해 패턴을 발견.
예시: 곤충 이미지를 두 그룹으로 나누어 벌과 나비를 구분.<blockquote>
</blockquote>
</li>
<li><strong>강화 학습 (Reinforcement Learning)</strong>: 환경에서 상과 벌을 통해 최적의 행동을 학습.
예시: 게임에서 벌과 나비를 만나며 경험을 쌓고 최적의 경로를 선택.</li>
</ul>
<pre><code># 지도 학습 예시 코드
from sklearn.tree import DecisionTreeClassifier

# 데이터 예시
X = [[0, 0], [1, 1], [2, 2], [3, 3]]
y = [0, 1, 1, 0]

# 모델 학습
clf = DecisionTreeClassifier()
clf.fit(X, y)

# 예측
print(clf.predict([[1.5, 1.5]]))  # [1]</code></pre><h3 id="요약"><strong>요약</strong></h3>
<p>그래서 머신러닝은 데이터를 통해 기계가 스스로 규칙을 학습하며, 이를 통해 다양한 예측 및 분류 작업을 수행하고 머신러닝은 지도 학습, 비지도 학습, 강화 학습의 세 가지 주요 유형으로 나뉘며 각각의 방식은 특정한 문제를 해결하는 데 효과적이다라고 정리할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로젝트] 숙박예약 수요 분석]]></title>
            <link>https://velog.io/@kim__1323/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%88%99%EB%B0%95%EC%98%88%EC%95%BD-%EC%88%98%EC%9A%94-%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@kim__1323/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%88%99%EB%B0%95%EC%98%88%EC%95%BD-%EC%88%98%EC%9A%94-%EB%B6%84%EC%84%9D</guid>
            <pubDate>Tue, 16 Jul 2024 10:50:47 GMT</pubDate>
            <description><![CDATA[<p>아래는 약 1년( 2015년 7월 ~ 2017년 8월 ) 의 기간 동안 수집한 A호텔의 고객 호텔 예약 데이터를 통해 노쇼/취소 고객의 증가를 막고 운영 비용을 노쇼와 취소고객을 예측한 결과에 맞춰 조정하기위해 <code>RandomForestClassifier</code> 모델을 사용한 머신러닝 예측 모델 프로젝트이다. </p>
<br>

<h3 id="문제-상황은-이렇다"><strong>문제 상황은 이렇다:</strong></h3>
<p>🏨🔖
A호텔은 요즘 코로나 여파로 인하여 고생을 겪고 있다. 객실 예약 건수도 줄고 있는 마당에 
노쇼/취소 고객도 증가있는 것이 가장 큰 문제이다.<br>캔슬 고객은 그나마 다행이지만 노쇼 고객의 경우 객실을 하루 날려버리는 것과 같아서, 비용적인 문제에서도 큰 영향을 끼치고 있다. 
따라서 노쇼와 취소고객을 사전 예측하고, 운영 비용을 상황에 맞춰 조정하려고 한다.</p>
<p><strong>⛳ 문제정의</strong></p>
<p>▶ 노쇼/취소 고객 증가로 인한 영업이익 감소</p>
<p><strong>⛳ 기대효과</strong></p>
<p>▶ 노쇼/취소 고객 손실 비용 절감, 영업이익 증가</p>
<p><strong>⛳ 해결방안</strong></p>
<p>▶ 노쇼/취소 고객 예측 모델로 고객을 예측하고, 운영 비용 조정</p>
<p><strong>⛳ 성과측정</strong></p>
<p>▶ 모델 활용 노쇼/취소 고객 관리 전/후 손실비용 비교</p>
<p><strong>⛳ 운영</strong></p>
<p>▶ Model에 Input하기 위한 Data mart 생성
▶ 예측 모델 활용 노쇼/취소 고객 추출
▶ 노쇼/취소 가능성이 높은 객실에 대해서는 다른 예약손님을 대체할 수 있도록 준비</p>
<hr>
<h3 id="데이터셋-정의"><strong>데이터셋 정의</strong></h3>
<BR>

<pre><code class="language-python"># ▶ Data read
import pandas as pd
df = pd.read_csv(&#39;S_PJT13_DATA.csv&#39;)
df.head()</code></pre>
<br>


<ul>
<li>컬럼 정보<pre><code># hotel: 호텔명 - 호텔의 이름을 나타낸다.
# is_canceled: 취소여부 - 예약이 취소되었는지 여부를 나타낸다. (0: 취소되지 않음, 1: 취소됨)
# lead_time: 입실까지 남은일 - 예약 시점부터 체크인까지 남은 일수를 나타낸다.
# arrival_date_year: 년 - 예약된 체크인 년도를 나타낸다.
# arrival_date_month: 월 - 예약된 체크인 월을 나타낸다.
# arrival_date_week_number: 주 - 예약된 체크인 주 번호를 나타낸다.
# arrival_date_day_of_month: 일 - 예약된 체크인 일을 나타낸다.
# stays_in_weekend_nights: 주말여부 - 투숙 기간 중 주말(금요일 및 토요일) 밤의 수를 나타낸다.
# stays_in_week_nights: 평일여부 - 투숙 기간 중 평일(일요일 ~ 목요일) 밤의 수를 나타낸다.
# adults: 성인 - 예약된 성인 수를 나타낸다.
# children: 어린이 - 예약된 어린이 수를 나타낸다.
# babies: 영유아 - 예약된 영유아 수를 나타낸다.
# meal: 식사 - 예약된 식사 옵션을 나타낸다.
# country: 나라 - 예약자의 국적을 나타낸다.
# market_segment: 예약유통채널상세 - 예약이 유입된 세부 채널을 나타낸다.
# distribution_channel: 예약유통채널 - 예약이 유입된 유통 채널을 나타낸다.
# is_repeated_guest: 기존고객여부 - 재방문 고객인지 여부를 나타낸다. (0: 신규 고객, 1: 재방문 고객)
# previous_cancellations: 과거 취소한 예약수 - 과거에 취소된 예약 횟수를 나타낸다.
# previous_bookings_not_canceled: 과거 취소하지않은 예약수 - 과거에 취소되지 않은 예약 횟수를 나타낸다.
# reserved_room_type: 예약객실타입 - 예약 시 지정된 객실 타입을 나타낸다.
# assigned_room_type: 배정된객실타입 - 실제 배정된 객실 타입을 나타낸다.
# booking_changes: 예약변경횟수 - 예약 후 변경된 횟수를 나타낸다.
# deposit_type: 보증금여부 - 보증금 정책을 나타낸다.
# agent: 여행사ID - 예약을 대행한 여행사의 ID를 나타낸다.
# company: 예약지불회사 - 예약 지불을 처리한 회사의 ID를 나타낸다.
# days_in_waiting_list: 대기자 명단에 있었던 일수 - 대기자 명단에 있었던 기간을 나타낸다.
# customer_type: 계약타입 - 고객의 계약 유형을 나타낸다.
# adr: 평균객실비용 - 평균 일일 객실 요금을 나타낸다.
# required_car_parking_spaces: 요구주차대수 - 요구된 주차 공간의 수를 나타낸다.
# total_of_special_requests: 특별요청수 - 고객이 요청한 특별 요청의 수를 나타낸다.
# reservation_status: 예약상태 - 예약의 현재 상태를 나타낸다. (예: 예약 완료, 체크인, 체크아웃, 취소)
# reservation_status_date: 예약상태 업데이트 날짜 - 예약 상태가 마지막으로 업데이트된 날짜를 나타낸다.
</code></pre></li>
</ul>
<pre><code>
__________

### **데이터 전처리 및 EDA**


### (1) Data shape(형태) 확인

```python
# ▶ Data 형태 확인
# ▶ 119,390 row, 32 col로 구성됨
print(&#39;df&#39;, df.shape)

&gt; df (119390, 32)</code></pre><ul>
<li>119390 개의 데이터가 있으며 32개의 컬럼으로 이루어져 있음.</li>
</ul>
<h3 id="2-data-type-확인">(2) Data type 확인</h3>
<pre><code class="language-python"># ▶ Data type 확인
df.info()

&lt;class &#39;pandas.core.frame.DataFrame&#39;&gt;
&lt;class &#39;pandas.core.frame.DataFrame&#39;&gt;
RangeIndex: 119390 entries, 0 to 119389
Data columns (total 32 columns):
 #   Column                          Non-Null Count   Dtype  
---  ------                          --------------   -----  
 0   hotel                           119390 non-null  object 
 1   is_canceled                     119390 non-null  int64  
 2   lead_time                       119390 non-null  int64  
 3   arrival_date_year               119390 non-null  int64  
 4   arrival_date_month              119390 non-null  object 
 5   arrival_date_week_number        119390 non-null  int64  
 6   arrival_date_day_of_month       119390 non-null  int64  
 7   stays_in_weekend_nights         119390 non-null  int64  
 8   stays_in_week_nights            119390 non-null  int64  
 9   adults                          119390 non-null  int64  
 10  children                        119386 non-null  float64
 11  babies                          119390 non-null  int64  
 12  meal                            119390 non-null  object 
 13  country                         118902 non-null  object 
 14  market_segment                  119390 non-null  object 
 15  distribution_channel            119390 non-null  object 
 16  is_repeated_guest               119390 non-null  int64  
 17  previous_cancellations          119390 non-null  int64  
 18  previous_bookings_not_canceled  119390 non-null  int64  
 19  reserved_room_type              119390 non-null  object 
 20  assigned_room_type              119390 non-null  object 
 21  booking_changes                 119390 non-null  int64  
 22  deposit_type                    119390 non-null  object 
 23  agent                           103050 non-null  float64
 24  company                         6797 non-null    float64
 25  days_in_waiting_list            119390 non-null  int64  
 26  customer_type                   119390 non-null  object 
 27  adr                             119390 non-null  float64
 28  required_car_parking_spaces     119390 non-null  int64  
 29  total_of_special_requests       119390 non-null  int64  
 30  reservation_status              119390 non-null  object 
 31  reservation_status_date         119390 non-null  object 
dtypes: float64(4), int64(16), object(12)
memory usage: 29.1+ MB</code></pre>
<p>총 31개의 열 데이터가 있으며 노쇼/취소를 할 만한 고객을 선별할 수 있는 의미있는 데이터는 <code>previous_cancellations</code>, <code>is_canceled</code>, <code>is_repeated_guest</code> 등이 있겠다.</p>
<p>그리고 이 중 날짜 데이터인 <code>reservation_status_date</code> 가 <code>object</code> 타입으로 선언되어 있는데 필요에 따라 데이터 분석 진행 시 <code>date</code> 타입으로 변경시키는게 좋을 거 같다.</p>
<h3 id="3-null값-확인--빈-값의-data">(3) Null값 확인 (※ 빈 값의 Data)</h3>
<pre><code class="language-python">hotel                                  0
is_canceled                            0
lead_time                              0
arrival_date_year                      0
arrival_date_month                     0
arrival_date_week_number               0
arrival_date_day_of_month              0
stays_in_weekend_nights                0
stays_in_week_nights                   0
adults                                 0
children                               4
babies                                 0
meal                                   0
country                              488
market_segment                         0
distribution_channel                   0
is_repeated_guest                      0
previous_cancellations                 0
previous_bookings_not_canceled         0
reserved_room_type                     0
assigned_room_type                     0
booking_changes                        0
deposit_type                           0
agent                              16340
company                           112593
days_in_waiting_list                   0
customer_type                          0
adr                                    0
required_car_parking_spaces            0
total_of_special_requests              0
reservation_status                     0
reservation_status_date                0
dtype: int64</code></pre>
<ul>
<li>일부 열 <code>company</code>와 <code>agent</code> 등의 값들에 null이 존재한다.</li>
<li><code>null</code> 값이 존재할 때 데이터를 처리하기 위해 가장 좋은 방법은 데이터를 완전히 삭제하는 것이 아닌 최대한 보존하는 방향이 좋다.</li>
<li>따라서 정수형 데이터는 0이나 문자형 데이터는 <code>none</code>과 같은 텍스트로 채워넣는 것이 좋다.</li>
</ul>
<p><strong>null 값 처리</strong></p>
<pre><code class="language-python"># ▶ 0 이나 None으로 해석될 수 있는 것은 Drop 하지 않고 채워 넣는다.
df[&#39;children&#39;].fillna(0, inplace=True)
df[&#39;country&#39;].fillna(&#39;none&#39;, inplace=True)
df[&#39;agent&#39;].fillna(0, inplace=True)
df[&#39;company&#39;].fillna(0, inplace=True)</code></pre>
<br>



<h3 id="4-outlier-확인-이상치-정상적인-범주를-벗어난-data"><strong>(4) Outlier 확인 (이상치, 정상적인 범주를 벗어난 Data)</strong></h3>
<pre><code class="language-python"># ▶ Outlier 확인
df.describe()
</code></pre>
<ul>
<li><code>describe()</code> 을 통해 전체 통계량을 확인해봤을 때 <code>ard</code> 즉 평균 일일 객실 요금을 나타내는 평균객실비용의 최소값 <code>min</code> 값이 음수값으로 분포되어 있어 해당 값을 제거해주어야 한다.</li>
</ul>
<p><strong>음수값 제거</strong></p>
<pre><code class="language-python"># ▶ 음수값 제거
df=df[df[&#39;adr&#39;]&gt;0]</code></pre>
<h3 id="5-데이터-eda"><strong>(5) 데이터 EDA</strong></h3>
<p><strong>객실 수요 파악</strong></p>
<pre><code class="language-python"># ▶ 년도별 객실 예약 현황
df[&#39;arrival_date_year&#39;].value_counts()

&gt; arrival_date_year
2016    55789
2017    40231
2015    21410
Name: count, dtype: int64</code></pre>
<p>2015년부터 2016년까지는 고객 예약이 급격하게 상승했다가 2017년부터 고객 예약 건수가 줄어든 것을 확인할 수 있다.</p>
<pre><code class="language-python"># ▶ 월별 객실 예약 현황
df[&#39;arrival_date_month&#39;].value_counts()

arrival_date_month
August       13711
July         12491
May          11611
April        10953
October      10929
June         10819
September    10351
March         9641
February      7921
November      6641
December      6561
January       5801
Name: count, dtype: int64</code></pre>
<p>휴가 시즌인 A호텔은 겨울에는 고객 예약수요가 적고 4월을 시작으로 점점 예약 수요가 많아지다 8월 여름 휴가 시즌부터 고객의 객실 예약 수요가 늘어나는 것을 확인할 수 있다.</p>
<p>*<em>년, 월별 객실 이용 현황 *</em> </p>
<pre><code class="language-python"># ▶ 년, 월별 객실 이용 현황 확인
df_reservation = pd.DataFrame(df.groupby([&#39;arrival_date_year&#39;, &#39;arrival_date_month&#39;], as_index=False)[&#39;hotel&#39;].count())
df_reservation.head(10)

    arrival_date_year    arrival_date_month    hotel
0                 2015                August     3794
1                 2015              December     2795
2                 2015                  July     2714
3                 2015              November     2274
4                 2015               October     4824
5                 2015             September     5009
6                 2016                 April     5346
7                 2016                August     5020
8                 2016              December     3766
9                 2016              February     3820  </code></pre>
<p>해당 월 데이터가 문자열로 되어 있어 어떤 달인지 확실히 식별이 어려워서 월 데이터를 재정의 
 해주었다.  </p>
<pre><code class="language-python"># ▶ replace 함수를 활용하여 월 데이터 재정의
df_reservation = df_reservation.replace({&#39;January&#39; : &#39;01.January&#39;,
                                         &#39;February&#39; : &#39;02.February&#39;,
                                         &#39;March&#39; : &#39;03.March&#39;,
                                         &#39;April&#39; : &#39;04.April&#39;,
                                         &#39;May&#39; : &#39;05.May&#39;,
                                         &#39;June&#39; : &#39;06.June&#39;,
                                         &#39;July&#39; : &#39;07.July&#39;,
                                         &#39;August&#39; : &#39;08.August&#39;,
                                         &#39;September&#39; : &#39;09.September&#39;,
                                         &#39;October&#39; : &#39;10.October&#39;,
                                         &#39;November&#39; : &#39;11.November&#39;,
                                         &#39;December&#39; : &#39;12.December&#39;})
df_reservation.head(5)  

    arrival_date_year    arrival_date_month    hotel
0                 2015             08.August     3794
1                 2015           12.December     2795
2                 2015               07.July     2714
3                  2015           11.November     2274
4                 2015            10.October     4824</code></pre>
<p>훨씬 보기 깔끔해짐. 이후 년, 월별 객실 이용 현황을 전체적으로 출력해보았다.  </p>
<pre><code class="language-python"># ▶ Plotting하기 어려움
df_reservation = df_reservation.sort_values(by=[&#39;arrival_date_year&#39;,&#39;arrival_date_month&#39;])
df_reservation  

    arrival_date_year    arrival_date_month    hotel
2                 2015               07.July    2714
0                 2015             08.August    3794
5                 2015           09.September    5009
4                 2015             10.October    4824
3                 2015            11.November    2274
1                 2015            12.December    2795
10                 2016             01.January    2183
9                 2016            02.February    3820
13                 2016                03.March    4739
6                 2016               04.April    5346
14                  2016                 05.May    5367
12                 2016                06.June    5227
11                 2016                07.July    4507
7                 2016              08.August    5020
17                 2016           09.September    5342
16                 2016             10.October    6105
15                 2016            11.November    4367
8                 2016            12.December    3766
21                 2017             01.January    3618
20                 2017            02.February    4101
24                 2017               03.March    4902
18                 2017               04.April    5607
25                 2017                 05.May    6244
23                 2017                06.June    5592
22                 2017                07.July    5270
19                 2017              08.August    4897  
</code></pre>
<ul>
<li><p>년, 월별 객실 이용 현황을 년도별로 <code>Sorting</code>하여 내림차순 한 결과, 2015년은 7월부터 데이터 존재, 2016년은 12개월 모두 존재, 2017년은 1~8월까지만 존재하는 것을 확인할 수 있다.</p>
</li>
<li><p>하지만 각 년, 월별로 이용 현황이 어떠한지 데이터가 너무 많아 분포를 확인하기가 어려워 시각화를 하였다.  </p>
</li>
</ul>
<pre><code class="language-python"># ▶ barplot, order 옵션을 활용하여 가독성 Up
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
plt.style.use([&#39;dark_background&#39;])

sns.barplot(x=&#39;arrival_date_month&#39;, y=&#39;hotel&#39;, hue=&#39;arrival_date_year&#39;, data = df_reservation,
            order = [&#39;01.January&#39;, &#39;02.February&#39;, &#39;03.March&#39;, &#39;04.April&#39;, &#39;05.May&#39;, &#39;06.June&#39;, &#39;07.July&#39;, &#39;08.August&#39;, &#39;09.September&#39;, &#39;10.October&#39;, &#39;11.November&#39;, &#39;12.December&#39;]);
plt.gcf().set_size_inches(20, 5);</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/f6f10f1c-3224-495b-921e-cc29a194fbea/image.png" alt=""></p>
<ul>
<li>데이터를 확인해보니 모든 데이터가 5월~8월 사이에 예약 수요가 가장 많이 급증한 것을 알 수 있으며 공통적으로 겨울 <code>novermber</code>, <code>december</code>에 고객 에약 수요가 줄어든 것을 확인할 수 가 있다.</li>
</ul>
<hr>
<br>

<br>



<h3 id="취소고객-노쇼-고객-특성-분석"><strong>취소고객, 노쇼 고객 특성 분석</strong></h3>
<p>이제 전체적인 데이터 EDA를 완료했으니 A호텔의 영업이익을 감소시키는 원인인 노쇼 고객 또는 예약 취소 고객을 분석해보겠다.</p>
<h3 id="1-취소-및-노쇼-비율"><strong>(1) 취소 및 노쇼 비율</strong></h3>
<p>일단 <code>&#39;reservation_status&#39;</code> 즉 전체적인 예약의 현황을 살펴보았다.</p>
<pre><code class="language-python">df[&#39;reservation_status&#39;].value_counts()

reservation_status
Check-Out    73419
Canceled     42830
No-Show       1181</code></pre>
<p>예약 현황을 확인해보니 check_out 건을 제외하고 취소건은 42830 건, 예약은 했지만 노쇼한 건 수가 1181개나 되는 것을 확인했다.</p>
<p>이렇게 숫자로 나타난 예약 현황들을 활용해 전체 예약 취소 건의 비율과 노쇼 건의 비율을 계산해 볼 수 있겠다.</p>
<pre><code class="language-python"># ▶ 캔슬 비율
print(&#39;Canceled :&#39;, 42830 / (73419 + 42830 + 1181))


# ▶ 노쇼 비율
print(&#39;No-Show :&#39;, 1181 / (73419 + 42830 + 1181))

&gt;Canceled : 0.36472792301796814
&gt;No-Show : 0.010057055266967554</code></pre>
<ul>
<li><p>확인을 해보니 취소 건수는 전체 예약 건수의 36프로나 차지하고 있었다.</p>
</li>
<li><p>노쇼의 비율은 전체 예약 건수에서 1프로 정도만 차지 하고 있다.</p>
</li>
<li><p>그래서 취소 건수와 노쇼 건을 따로 나눠서 예측하는 것보다 취소와 노쇼 건을 하나로 묶어서 예측 모델링을 만들기 위해 checkout 된 건수는 0으로 취소와 노쇼에 대한 건수는 1로 변경하기로 한다.</p>
</li>
</ul>
<pre><code class="language-python"># ▶ checkout된 예약 건수는 0으로 취소 노쇼에 대한 데이터는 1로 변경
import numpy as np
df[&#39;reservation_status&#39;] = np.where(df[&#39;reservation_status&#39;] != &#39;Check-Out&#39;, 1, 0)
df[&#39;reservation_status&#39;].value_counts()

reservation_status
0    73419
1    44011
Name: count, dtype: int64</code></pre>
<br> 


<h3 id="2-취소-및-노쇼-고객-특성-분석"><strong>(2) 취소 및 노쇼 고객 특성 분석</strong></h3>
<p>데이터 시각화를 통해 정상적으로 checkout된 건수와 취소 및 노쇼 건수의 월 별 분포를 확인해보았다.  </p>
<h4 id="a호텔-월-별-예약-현황-분포"><strong>A호텔 월 별 예약 현황 분포</strong></h4>
<pre><code class="language-python"># ▶ 월에 따른 취소/노쇼율 비교
sns.catplot(x=&quot;arrival_date_month&quot;, hue=&quot;reservation_status&quot;, kind=&quot;count&quot;,palette=&quot;pastel&quot;, edgecolor=&quot;.6&quot;,data=df,
            order = [&#39;January&#39;, &#39;February&#39;, &#39;March&#39;, &#39;April&#39;, &#39;May&#39;, &#39;June&#39;, &#39;July&#39;, &#39;August&#39;, &#39;September&#39;, &#39;October&#39;, &#39;November&#39;, &#39;December&#39;]);
plt.gcf().set_size_inches(20, 3)
</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/d7895148-396c-498a-84c2-9f4bdcfebbe5/image.png" alt=""></p>
<ul>
<li><p>육안으로 그래프 확인 결과 가장 많은 예약취소나 노쇼가 있는 달은 <code>August</code> 8월 달에 예약 건수가 가장 많은 만큼 예약취소나 노쇼가 많은 것을 확인할 수 있다.</p>
</li>
<li><p>정확한 월 별 취소/노쇼가 가장 많은 달이 어떤 것인지 확인하기 위해서는 취소/노쇼의 분포만 보는 것이 아닌 예약 건수에 따라 월 별로 취소/노쇼의 비율을 데이터 프레임화 해보았다.</p>
</li>
</ul>
<pre><code class="language-python"># ▶ 월에 따른 취소/노쇼율 비교
df_gp = df.groupby(&#39;arrival_date_month&#39;)[&#39;reservation_status&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp.head()

df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp = df_gp.sort_values(by=[&#39;ratio&#39;], ascending=False)
df_gp

                      count   sum  ratio
arrival_date_month                     
June               10819  4523   41.8
April              10953  4504   41.1
May                11611  4659   40.1
September          10351  4092   39.5
October            10929  4209   38.5
August             13711  5228   38.1
July               12491  4723   37.8
December            6561  2355   35.9
February            7921  2676   33.8
March               9641  3137   32.5
November            6641  2110   31.8
January             5801  1795   30.9</code></pre>
<ul>
<li>육안으로 그래프를 봤을 때는 8월 달이 가장 예약/취소 건수가 많았지만 실제 예약 건수에 따른 노쇼/취소 건수의 비율을 확인해보니 6월달, 4월달, 5월달이 예약 취소/노쇼가 가장 많은 것을 확인할 수 있다. </li>
</ul>
<br>



<h4 id="a호텔-호텔-타입에-따른-취소노쇼-예약-분포-및-비율"><strong>A호텔 호텔 타입에 따른 취소/노쇼 예약 분포 및 비율</strong></h4>
<pre><code class="language-python"># ▶ Resort Hotel과 City Hotel 비교
sns.catplot(x=&quot;hotel&quot;, hue=&quot;reservation_status&quot;, kind=&quot;count&quot;,palette=&quot;pastel&quot;, edgecolor=&quot;.6&quot;,data=df);
plt.gcf().set_size_inches(20, 3)</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/7275817f-960a-4ead-866c-4bf13ab5efcf/image.png" alt=""></p>
<p>호텔의 타입에 따른 예약 checkout 그리고 취소/노쇼 건수의 분포를 확인해보니 <code>city hotel</code>의 예약 취소/노쇼의 분포가 가장 많은 것을 확인할 수 있었다.</p>
<p>정확한 취소/노쇼의 비율을 데이터프레임화 해서 확인해 보았다.</p>
<pre><code class="language-python"># ▶ Resort Hotle과 City Hotel 비교
df_gp = df.groupby(&#39;hotel&#39;)[&#39;reservation_status&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp = df_gp.sort_values(by=[&#39;ratio&#39;], ascending=False)
df_gp

                   count   sum  ratio
            hotel                        
City Hotel           78122 32973     42.2
Resort Hotel       39308 11038     28.1</code></pre>
<p>마찬가지로 호텔의 타입에 따른 예약 checkout 그리고 취소/노쇼 건수의 비율을 확인해보니 
<code>city hotel</code>의 예약 취소/노쇼의 분포가 압도적으로 극단적으로 많은 것을 확인할 수 있었다.</p>
<p>그래서 <code>city hotel</code>의 예약 취소/노쇼의 원인을 파악하거나 운영을 관리가 필요할 것으로 보인다.</p>
<br>


<h4 id="a호텔-주말-예약-일수에-따른-노쇼취소-고객-비교"><strong>A호텔 주말 예약 일수에 따른 노쇼/취소 고객 비교</strong></h4>
<pre><code class="language-python"># ▶ 주말 예약 일수에 따른 비교
sns.catplot(x=&quot;stays_in_weekend_nights&quot;, hue=&quot;reservation_status&quot;, kind=&quot;count&quot;,palette=&quot;pastel&quot;, edgecolor=&quot;.6&quot;,data=df);
plt.gcf().set_size_inches(20, 3)</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/baaf16d8-018f-434f-a50a-16566efcf5a2/image.png" alt=""></p>
<p>호텔의 주말 예약 일수에 따른 분포를 비교해본 결과 거의 0~2일의 주말이 예약 일수에 끼어있는 예약이 많이 분포되어 있고 3일, 4일, 6일, 9일 등 주말 예약 일수가 많은 예약도 일부 존재하는 것을 확인할 수 있었다.</p>
<p>주말 예약 일수에 따른 정확한 취소/노쇼의 비율을 데이터프레임화 해서 확인해 보았다.</p>
<pre><code class="language-python"># ▶ 주말 예약 일수에 따른 비교
df_gp = df.groupby(&#39;stays_in_weekend_nights&#39;)[&#39;reservation_status&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp = df_gp.sort_values(by=[&#39;ratio&#39;], ascending=False)
df_gp

                           count   sum  ratio
stays_in_weekend_nights            
        9                    9      7      77.8
        7                   19     14      73.7
        8                   57     34      59.6
        6                  152     87      57.2
        5                   77     43      55.8
        0                50499  19361  38.3
        2                33150  12453  37.6
        1                30361  10967  36.1
        3                 1244     444  35.7
       16                    3       1  33.3
        4                 1844     597  32.4
       10                    7       2  28.6
       12                    5       1  20.0
       13                    1       0   0.0
       18                    1       0   0.0
       19                    1       0   0.0</code></pre>
<p>주말 예약 일수에 따른 취소/노쇼 비율을 확인해보니 9일 8일 8일 6일 등 주말 에약 일수가 많을 수록 취소/노쇼를 하는 고객의 비율이 많은 것을 확인할 수 있었다.  </p>
<p>그리고 주말 예약 일수가 0<del>2일이 있는 예약 건의 취소/노쇼 비율이 큰 차이가 없는 것으로 보아 주말이 없어 0인 평일에 예약한 건수들과 주말에 1</del>2일 예약한 건수들의 비율이 큰 차이가 없어 유의미한 데이터라고 보기가 어려울 거 같다.</p>
<p>그래서 더 넓은 분포 및 비율을 보기 위해 주말 예약 일수가 2일 이하인 것은 1, 8일 이하인 것은 2, 그 이외에는 3으로 그룹핑하여 구간화로 주말 일수별 예약 취소/노쇼 비율을 비교했다.</p>
<pre><code class="language-python"># ▶ 주말 예약 일수에 따른 비교(re-binning)
df_c = df.copy()

df_c[&#39;gp&#39;] = np.where(df_c[&#39;stays_in_weekend_nights&#39;] &lt;= 2, 1,
                      np.where(df_c[&#39;stays_in_weekend_nights&#39;] &lt;= 8, 2, 3))

df_gp = df_c.groupby(&#39;gp&#39;)[&#39;reservation_status&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp = df_gp.sort_values(by=[&#39;ratio&#39;], ascending=False)
df_gp


&gt;
    count    sum  ratio
gp                      
3      27     11   40.7
1  114010  42781   37.5
2    3393   1219   35.9</code></pre>
<ul>
<li><p>예약 취소/노쇼가 가장 많은 것은 주말 예약 일수가 9일 이상인 경우가 가장 많은 취소 비율을 보였고 그 다음으로 2일 이하인 경우 그리고 8일 이하인 경우가 가장 많이 차지했다.</p>
</li>
<li><p>하지만 9일 이상인 경우의 데이터의 수가 너무 작아서 27명의 데이터를 가지고만 주말 일수가 많음에 따라 취소/노쇼 비율이 높다라고 추정을 하기는 어렵겠다.</p>
</li>
</ul>
<br>


<h4 id="a호텔-객실-타입에-따른-예약-일수-취소-및-노쇼-비율"><strong>A호텔 객실 타입에 따른 예약 일수/ 취소 및 노쇼 비율</strong></h4>
<pre><code class="language-python">
# ▶ 객실타입에 따른 비교
df_gp = df.groupby(&#39;reserved_room_type&#39;)[&#39;reservation_status&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp = df_gp.sort_values(by=[&#39;ratio&#39;], ascending=False)
df_gp

                    count    sum  ratio
reserved_room_type                     
H                    595    245   41.2
A                  84573  33477   39.6
G                   2006    756   37.7
B                   1085    365   33.6
C                    913    306   33.5
L                      6      2   33.3
D                  19005   6086   32.0
F                   2824    873   30.9
E                   6423   1901   29.6
</code></pre>
<p>객실 타입에 따른 예약 취소/노쇼 비율을 확인해보니 H 룸타입과 A룸타입의 취소/노쇼 비율이 가장 높았다.</p>
<br>


<h4 id="a호텔의-lead-time-즉-예약일로부터-checkout-날까지-남은-기간에-따른-노쇼취소-비율"><strong>A호텔의 <code>lead time</code>, 즉 예약일로부터 checkout 날까지 남은 기간에 따른 노쇼/취소 비율</strong></h4>
<pre><code class="language-python"># ▶ lead time
sns.distplot(df[&#39;lead_time&#39;]);  </code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/f7e2eefe-a199-4ffd-b2ad-2ee76820ba01/image.png" alt=""></p>
<p>예약일로부터 checkout 날까지 남은 기간에 따른 노쇼/취소 고객의 분포를 확인해보니 비교적 예약일이 얼마 안남은 시점 순으로부터 노쇼/취소고객이 많은 것을 확인할 수 있었고 정확한 비율 확인을 위해 입실일로부터 100일이하가 남은 예약을 1, 200일 이하가 남은 예약을 2, 나머지 200일이 초과하는 예약을 3으로 지정하고 다시 비율을 만들어보았다.  </p>
<pre><code class="language-python"># ▶ lead time 구간화
df_c = df.copy()
df_c[&#39;gp&#39;] = np.where(df[&#39;lead_time&#39;] &lt;= 100, 1,
                       np.where(df[&#39;lead_time&#39;]&lt;=200, 2, 3))

df_gp = df_c.groupby(&#39;gp&#39;)[&#39;reservation_status&#39;].agg([&#39;count&#39;,&#39;sum&#39;])
df_gp[&#39;ratio&#39;] = round((df_gp[&#39;sum&#39;] / df_gp[&#39;count&#39;]) * 100, 1)
df_gp = df_gp.sort_values(by=[&#39;ratio&#39;], ascending=False)
df_gp

    count      sum    ratio
gp            
3    20526    12182     59.3
2    26586    11987     45.1
1    70318    19842     28.2</code></pre>
<ul>
<li>확인 결과 예약 건수에 비해서 입실일이 200일이 넘게 남은 고객들의 취소 비율이 가장 높았고 그 뒤로 200일 이하, 100일 이하 순으로 고객의 취소 비율이 이어졌다.</li>
</ul>
<p><br><br></p>
<hr>
<p><strong>결론</strong></p>
<ul>
<li><p>확인 결과 예약 건수에 비해서 입실일이 200일이 넘게 남은 고객들의 취소 비율이 가장 높았고 그 뒤로 200일 이하, 100일 이하 순으로 고객의 취소 비율이 이어졌다.</p>
</li>
<li><p>마찬가지로 호텔의 타입에 따른 예약 checkout 그리고 취소/노쇼 건수의 비율을 확인해보니 
<code>city hotel</code>의 예약 취소/노쇼의 분포가 압도적으로 극단적으로 많은 것을 확인할 수 있었다.
그래서 <code>city hotel</code>의 예약 취소/노쇼의 원인을 파악하거나 운영을 관리가 필요할 것으로 보인다.</p>
<ul>
<li>육안으로 그래프를 봤을 때는 8월 달이 가장 예약/취소 건수가 많았지만 실제 예약 건수에 따른 노쇼/취소 건수의 비율을 확인해보니 6월달, 4월달, 5월달이 예약 취소/노쇼가 가장 많은 것을 확인할 수 있다.   </li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[파이차트 strangle]]></title>
            <link>https://velog.io/@kim__1323/%ED%8C%8C%EC%9D%B4%EC%B0%A8%ED%8A%B8-strangle</link>
            <guid>https://velog.io/@kim__1323/%ED%8C%8C%EC%9D%B4%EC%B0%A8%ED%8A%B8-strangle</guid>
            <pubDate>Tue, 16 Jul 2024 03:13:56 GMT</pubDate>
            <description><![CDATA[<p><a href="https://zephyrus1111.tistory.com/29">https://zephyrus1111.tistory.com/29</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] stripplot, 스트립 플롯 / ]]></title>
            <link>https://velog.io/@kim__1323/Python-stripplot-%EC%8A%A4%ED%8A%B8%EB%A6%BD-%ED%94%8C%EB%A1%AF</link>
            <guid>https://velog.io/@kim__1323/Python-stripplot-%EC%8A%A4%ED%8A%B8%EB%A6%BD-%ED%94%8C%EB%A1%AF</guid>
            <pubDate>Tue, 16 Jul 2024 02:33:01 GMT</pubDate>
            <description><![CDATA[<p><strong>스트립플롯(strip plot)</strong>은 데이터 포인트를 각 카테고리별로 나란히 표시하는 플롯이다.</p>
<p>이를 통해 데이터의 분포와 밀도를 시각적으로 확인할 수 있다. 
주로 <code>seaborn</code> 라이브러리에서 제공하는 시각화 도구 중 하나이다.</p>
<br>

<h3 id="스트립플롯의-주요-특징"><strong>스트립플롯의 주요 특징:</strong></h3>
<p><strong>(1) 데이터 분포 확인</strong>: 각 데이터 포인트를 개별적으로 표시하여 데이터의 분포와 밀도를 파악할 수 있다.</p>
<p><strong>(2) 카테고리 비교</strong>: 서로 다른 카테고리 간의 분포 차이를 비교할 수 있다. 
<strong>중요한 것은 주로 범주형 변수에 따라서 연속형 변수가 어떻게 분포되어 있는지 확인할 수 있다.</strong></p>
<p><br><br></p>
<h3 id="스트립-플롯-그래프-예제-및-해석-방법"><strong>스트립 플롯 그래프 예제 및 해석 방법:</strong></h3>
<pre><code class="language-python">sns.stripplot(x = &quot;day&quot;, y = &quot;total_bill&quot;, data = df)</code></pre>
<p><img src="https://velog.velcdn.com/images/kim__1323/post/bb8baa80-48f8-4915-9712-d64f8e272ea8/image.png" alt=""></p>
<p>이 코드는 <code>seaborn</code>의 예제 데이터셋인 <code>&#39;tips&#39;</code>를 사용하여 <strong><code>요일별(day)</code></strong> <strong><code>청구금액(total_bill)</code></strong>의 분포를 스트립플롯으로 그린 것이다. 
각 데이터 포인트는 요일별로 나열되어 있으며, 청구 금액의 분포를 쉽게 파악할 수 있다.</p>
<p><strong>분포의 범위</strong>: 각 요일별로<code>total_bill</code>의 범위를 확인할 수 있다.
<strong>중첩 및 밀도</strong>: 데이터 포인트의 밀도가 높은 부분을 확인하여 특정 요일에 총 청구 금액이 집중되는지를 파악할 수 있다.
<strong>이상치 확인</strong>: 다른 데이터 포인트와 멀리 떨어져 있는 이상치를 확인할 수 있다.</p>
<h3 id="그래프-해석"><strong>그래프 해석</strong>:</h3>
<p><strong>(1) 요일별 total_bill 분포</strong>:</p>
<ul>
<li><p><strong>일요일(Sun)</strong>: 대부분의 <code>total_bill</code>이 10에서 30 사이에 분포되어 있다. 몇몇 큰 청구 금액도 존재한다.</p>
</li>
<li><p><strong>토요일(Sat)</strong>: 다른 요일에 비해 분포가 넓으며, 10에서 40 사이에 밀집되어 있다.</p>
</li>
<li><p><strong>목요일(Thur)</strong>: 다른 요일에 비해 낮은 청구 금액이 많고, 총 청구 금액이 적은 경향이 있다.</p>
</li>
<li><p><strong>금요일(Fri)</strong>: 데이터 포인트 수가 적으며 대체로 10에서 20 사이에 분포되어 있다.</p>
</li>
</ul>
<br>

<h3 id="데이터-분석가로서-스트립플롯-그래프에서-중점적으로-봐야-할-항목"><strong>데이터 분석가로서 스트립플롯 그래프에서 중점적으로 봐야 할 항목:</strong></h3>
<ul>
<li><p><strong>각 카테고리별 비교</strong>: 각 요일의 청구 금액 분포를 비교하여 특정 요일에 높은 청구 금액이 발생하는지를 파악할 수 있다.</p>
</li>
<li><p><strong>밀도 분석</strong>: 데이터 포인트의 밀도를 확인하여 특정 요일에 고객이 많이 방문하는지를 추정할 수 있다. 그래프에서 토요일 일요일의 고객이 많은 것을 확인할 수 있다.</p>
</li>
<li><p><strong>이상치 분석</strong>: 이상치를 식별하여 해당 이상치가 데이터 입력 오류인지 또는 특별한 이벤트에 의한 것인지 분석할 수 있다.</p>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>