TIL/내일배움캠프

히트맵으로 시각화하기

Kou_ 2026. 5. 15. 09:38

[내일배움캠프] QA/QC_6기 - 5일차 

  • TIL
    • 가설을 수립하고 파이썬으로 데이터 분석하기
    • 3개 속성에 대한 정보를 히트맵으로 파이썬으로 한 번에 시각화 하기 
  • To-Do List
    1. 데이터 분석 종합반 5강 수강
    2. 커리어 스터디 (Day 5)
    3. ADsP 공부

데이터 분석 종합반

5강. 데이터 분석으로 기획서 뽀개기 2

판매 타겟이 적절한가

  • 가설 수립
    • 문제 파악 : 웹개발 강의의 수강 완주율이 낮게 나타나고 있다.
    • 예측 : 시간이 부족하면 수강 완주율이 떨어지지 않을까? → 20~30대 확인하기 
    • 목표 결과 : 주요 타겟 연령대를 변경하거나 시청 완주율을 높이거나
    • 가설 : 20~30대의 수강 완주율이 상대적으로 낮을 것이다.
  • 코딩
    import pandas as pd
    pd.set_option('display.max_columns', None) #모든 열 표시
    pd.set_option('display.width', None) #출력 너비 제한 해제
    import matplotlib.pyplot as plt
    
    #파일 불러오기
    sparta_data=pd.read_csv('F:/Projects/data_set/sparta_data.csv')
    
    #한글 폰트 설정 및 깨짐 방지
    plt.rcParams['font.family'] = 'Malgun Gothic'
    plt.rcParams['axes.unicode_minus'] = False
    
    print(sparta_data.head())
    
    #나이대별로 수강률 합 구하기
    progress_rate_by_age = sparta_data.groupby('age')['progress_rate'].sum()
    print(progress_rate_by_age)
    #나이대별 수강인원 구하기
    number_people_by_age = sparta_data.groupby('age')['_id'].count()
    print(number_people_by_age)
    #나이대별 완주율 평균 구하기
    average = progress_rate_by_age/number_people_by_age
    print(average)
    
    #plt.figure(width, height) : 넓이와 높이 만큼 이미지를 생성
    plt.figure(figsize=(6,6))
    #그래프의 x축 눈금 설정
    plt.xticks([10,20,30,40,50])
    #plt.bar(X축값, Y축값)
    plt.bar(progress_rate_by_age.index,average)
    #바에 각 수치율 추가
    bar = plt.bar(progress_rate_by_age.index,average,width=7)
    for rect in bar:
        height = rect.get_height()
        plt.text(
            rect.get_x() + rect.get_width()/2.0,
                 height, '%.1f' % height,
                 ha='center',
                 va='bottom',
                 size = 12
                 )
    #그래프의 제목, 타이틀과 그래프와의 간격은 pad= 수치
    plt.title('[나이대 별 평균 수강율]',fontsize=15,pad=20)
    #그래프의 x축 라벨 이름
    #labelpad 파라미터는 축 레이블의 여백을 지정
    plt.xlabel('수강 연령대',fontsize=12,labelpad=20)
    #그래프의 y축 라벨 이름
    plt.ylabel('수강생(명)',fontsize=14,rotation=360,labelpad=35)
    plt.tight_layout()
    plt.show()
     
  • 시각화
  • 분석
    • 20~30대의 수강 완주율이 높다. (가설 기각)
      → 판매 타겟이 잘못되지는 않았다.
  • 신규 가설 수립
    • 문제 파악 : 8월 중순부터 웹개발 종합반의 완주율이 크게 떨어진 이유를 확인하고, 고객의 수강 완료 동기부여를 주어야 한다.
    • 정보 :  찐한관리(managed)와 완주율이 동시에 감소했으므로 연관이 있을 가능성이 있다.
    • 가설 : 찐한관리를 받은 인원이 그렇지 않은 인원보다 원주율이 높을 것이다.
  •  코드
    import pandas as pd
    pd.set_option('display.max_columns', None) #모든 열 표시
    pd.set_option('display.width', None) #출력 너비 제한 해제
    import matplotlib.pyplot as plt
    import numpy as np
    
    #파일 불러오기
    sparta_data=pd.read_csv('F:/Projects/data_set/sparta_data.csv')
    
    #한글 폰트 설정 및 깨짐 방지
    plt.rcParams['font.family'] = 'Malgun Gothic'
    plt.rcParams['axes.unicode_minus'] = False
    
    # 데이터 논리 연산자 → 문자열 수정
    managed = ['TRUE','FALSE']
    
    #관리 여부에 따라, 수강완료율 평균
    managed_data_avg = sparta_data.groupby('managed')['progress_rate'].sum()/sparta_data.groupby('managed')['_id'].count()
    print(managed_data_avg)
    
    #plt.figure(width, height) : 넓이와 높이 만큼 이미지를 생성
    plt.figure(figsize=(6,6))
    
    #각각 어떤 값이 들어가야 하는지 입력해 볼까요?
    #plt.bar(X축값, Y축값)
    plt.bar(managed_data_avg.index,managed_data_avg)
    
    #그래프의 바에 각 수치율을 추가 해 볼까요?
    bar = plt.bar(managed_data_avg.index,managed_data_avg)
    for rect in bar:
        height = rect.get_height()
        plt.text(
            rect.get_x() + rect.get_width()/2.0,
                 height, '%.1f' % height,
                 ha='center', va='bottom', size = 12
                 )
    #그래프의 제목
    plt.title('찐한관리 유무에 따른 평균 완주율',fontsize=14)
    #그래프의 x축 라벨 이름
    plt.xlabel('평균 완주율',fontsize=12)
    #x축 눈금 레이블 지정하기
    #기존의 0,1이라는 x축 레이블을, labels =["..."]로 변경 가능 합니다 :)
    plt.xticks([0,1], labels=["찐한관리 비 신청자","찐한관리 신청자"])
    #그래프의 y축 라벨 이름
    plt.ylabel('찐한관리 여부',fontsize=12,rotation=360,labelpad=35)
    #x축 눈금의 글씨를 45도 회전
    plt.xticks(rotation=45)
    #y축 눈금의 글씨를 360도 회전
    plt.yticks(rotation=360)
    plt.tight_layout()
    plt.show()
     
  •  시각화
  •  
  • 분석
    • 실제로 찐한 관리를 받은 수강생의 완주율이 약 1.77배 높았다. → 찐한관리 신청은 유의미

완주율을 상승시키기 위한 방법 찾기

  •  가설 수립
    • 문제 상황 : 8월 중순부터 완주율이 감소했다.
    • 정보 : 8월쯤 프로덕트 개선으로 3주차 강의의 컨텐츠 변경이 이루어졌다.
    • 가설 : 3주차 강의의 컨텐츠 변경이 완주율에 영향을 줬을 것이다.
      → 개강반에 따른 1~5주차 강의의 진도율을 비교하여 8월 둘째 주 개강반 이후 현저한 완주율 감소가 나타나는가?
  • 코드 (X: 개강 주차(created_at, 커리큘럼, Y: 진도율(progress_rate → 코호트 분석, 히트맵 시각화)
    import pandas as pd
    pd.set_option('display.max_columns', None) #모든 열 표시
    pd.set_option('display.width', None) #출력 너비 제한 해제
    import seaborn as sns
    import matplotlib.pyplot as plt
    
    #파일 불러오기
    sparta_data=pd.read_csv('F:/Projects/data_set/cohort_data.csv')
    
    #한글 폰트 설정 및 깨짐 방지
    plt.rcParams['font.family'] = 'Malgun Gothic'
    plt.rcParams['axes.unicode_minus'] = False
    
    #수강 시작 일 문자열 → 시계열 데이터로 변환
    format = "%Y. %m. %d"
    sparta_data['start_time'] = (
        pd.to_datetime(sparta_data['created_at'],
                       format='mixed')
    )
    #수강 시작 주 구하고, 테이블의 열로 추가 하기
    sparta_data['start_week']=(
        sparta_data['start_time'].dt.isocalendar().week
    )
    
    #set()은 set안의 데이터는 순서가 정해져있지 않고, 중복되지 않는 고유한 요소를 가져dha
    category_range = set(sparta_data['start_week'])
    #범주화 데이터 리스트 만들기 (진도율)
    progress_rate = list(sparta_data['progress_rate'])
    
    #범주를 구분하는 기준 bins 처음(0)과 끝(100)
    bins = [0,4.11,26.03,41.10,61.64,80.82,100]
    #구분한 범주의 라벨 labels
    labels=[0,1,2,3,4,5]
    #범주화에 사용하는 함수 pd.cut
    cuts=pd.cut(progress_rate,bins,
                right=True,include_lowest=True,
                labels=labels
                )
    #결과물을 테이블로 변경하기
    cuts = pd.DataFrame(cuts)
    
    #concat() 함수를 이용하여, sparta_data 테이블과, cuts 테이블 병합
    sparta_data = pd.concat(
        [sparta_data,cuts],axis=1, join='inner'
    )
    #원하는 컬럼의 이름을 다 작성
    sparta_data.columns=['created_at','user_id','name','progress_rate','start_time','start_week',"week"]
    print(sparta_data.head())
    
    #기존의 테이블을, start_week와, week로 묶기
    grouping = sparta_data.groupby(['start_week','week'])
    #수강 수 구하고 테이블로 변경
    cohort_data = grouping['user_id'].apply(pd.Series.nunique)
    cohort_data = pd.DataFrame(cohort_data)
    
    #5주차를 4주차에 더하고, ..., 더한 1주차를 0주차로 더하고 반복 (시작 주차 == 31주차)
    f=31
    #처음 수강 시작한 주의 범위가 {31,32,33,34,35,36} 이니, range(6)으로 합시다!
    for i in range(6):
        #5주차의 강의가 마지막이고, 0주차까지 이니, 시작은 5에서 시작해 1씩 0까지 감소 시킬수 있어요!
      for j in range(5, 0, -1):
        cohort_data.at[(f,j-1), 'user_id'] = (
                int(cohort_data.at[(f,j),'user_id']) +
                int(cohort_data.at[(f,j-1),'user_id'])
        )
        #주차는(31부터 32 33..) 1씩 늘어남
      f=f+1
    #인덱스 재정렬
    cohort_data = cohort_data.reset_index()
    
    #피벗 테이블 만들기
    cohort_counts = cohort_data.pivot(index="start_week",
                                      columns="week",
                                      values="user_id")
    #앞서 만든 피벗 테이블을 retention 변수에 저장하기
    retention = cohort_counts
    #각 주(week) 별 최초 수강생 수 (퍼센트 분모)
    cohort_sizes = cohort_counts.iloc[:,0]
    #표의 단일 데이터에 최초 수강생의 수를 나누어, 각 주당 수강생 수강율 나타내기
    retention = cohort_counts.divide(cohort_sizes, axis=0)
    #각 수치 퍼센트로 변경하기, round 함수로 3자리 수에서 반올림 한 후, 100을 곱하기
    retention.round(3)*100
    
    #히트맵 시각화
    #테이블 크기 설정 하기
    plt.figure(figsize=(10,8))
    sns.heatmap(data=retention,
               annot=True,
               fmt='.2%',
               vmin=0,
               vmax=1,
               cmap="BuGn")
    plt.xlabel('주차', fontsize=14,labelpad=30)
    plt.ylabel('개강일', fontsize=14,rotation=360,labelpad=30)
    plt.yticks(rotation=360)
    plt.tight_layout()
    plt.show()
     
  •  시각화
  •  
  • 분석
    • 32주차(8월 둘째 주) 이후 3주차 강의의 수강율 저하가 딱히 현저하게 관측되지 않는다.
    • 컨텐츠 변경이 문제인 것 같지 않다. (가설 기각)

직전 주차 대비 진도율로 변경하여 시각화하기

  • 코드
    
    import pandas as pd
    pd.set_option('display.max_columns', None) #모든 열 표시
    pd.set_option('display.width', None) #출력 너비 제한 해제
    import seaborn as sns
    import matplotlib.pyplot as plt
    
    #파일 불러오기
    sparta_data=pd.read_csv('F:/Projects/data_set/cohort_data.csv')
    
    #한글 폰트 설정 및 깨짐 방지
    plt.rcParams['font.family'] = 'Malgun Gothic'
    plt.rcParams['axes.unicode_minus'] = False
    
    #수강 시작 일 문자열 → 시계열 데이터로 변환
    format = "%Y. %m. %d"
    sparta_data['start_time'] = (
        pd.to_datetime(sparta_data['created_at'],
                       format='mixed')
    )
    #수강 시작 주 구하고, 테이블의 열로 추가 하기
    sparta_data['start_week']=(
        sparta_data['start_time'].dt.isocalendar().week
    )
    
    #set()은 set안의 데이터는 순서가 정해져있지 않고, 중복되지 않는 고유한 요소를 가져dha
    category_range = set(sparta_data['start_week'])
    #범주화 데이터 리스트 만들기 (진도율)
    progress_rate = list(sparta_data['progress_rate'])
    
    #범주를 구분하는 기준 bins 처음(0)과 끝(100)
    bins = [0,4.11,26.03,41.10,61.64,80.82,100]
    #구분한 범주의 라벨 labels
    labels=[0,1,2,3,4,5]
    #범주화에 사용하는 함수 pd.cut
    cuts=pd.cut(progress_rate,bins,
                right=True,include_lowest=True,
                labels=labels
                )
    #결과물을 테이블로 변경하기
    cuts = pd.DataFrame(cuts)
    
    #concat() 함수를 이용하여, sparta_data 테이블과, cuts 테이블 병합
    sparta_data = pd.concat(
        [sparta_data,cuts],axis=1, join='inner'
    )
    #원하는 컬럼의 이름을 다 작성
    sparta_data.columns=['created_at','user_id','name','progress_rate','start_time','start_week',"week"]
    print(sparta_data.head())
    
    #기존의 테이블을, start_week와, week로 묶기
    grouping = sparta_data.groupby(['start_week','week'])
    #수강 수 구하고 테이블로 변경
    cohort_data = grouping['user_id'].apply(pd.Series.nunique)
    cohort_data = pd.DataFrame(cohort_data)
    
    #5주차를 4주차에 더하고, ..., 더한 1주차를 0주차로 더하고 반복 (시작 주차 == 31주차)
    f=31
    #처음 수강 시작한 주의 범위가 {31,32,33,34,35,36} 이니, range(6)으로 합시다!
    for i in range(6):
        #5주차의 강의가 마지막이고, 0주차까지 이니, 시작은 5에서 시작해 1씩 0까지 감소 시킬수 있어요!
      for j in range(5, 0, -1):
        cohort_data.at[(f,j-1), 'user_id'] = (
                int(cohort_data.at[(f,j),'user_id']) +
                int(cohort_data.at[(f,j-1),'user_id'])
        )
        #주차는(31부터 32 33..) 1씩 늘어남
      f=f+1
    #인덱스 재정렬
    cohort_data = cohort_data.reset_index()
    
    #피벗 테이블 만들기
    cohort_counts = cohort_data.pivot(index="start_week",
                                      columns="week",
                                      values="user_id")
    #앞서 만든 피벗 테이블을 retention 변수에 저장하기
    retention = cohort_counts
    #각 주(week) 별 최초 수강생 수 (퍼센트 분모)
    cohort_sizes = cohort_counts.iloc[:,0]
    #표의 단일 데이터에 최초 수강생의 수를 나누어, 각 주당 수강생 수강율 나타내기
    retention = cohort_counts.divide(cohort_sizes, axis=0)
    #각 수치 퍼센트로 변경하기, round 함수로 3자리 수에서 반올림 한 후, 100을 곱하기
    retention.round(3)*100
    
    w=31
    for i in range(6):
        for j in range(1, 6, 1):
            retention.at[(w,j)] = (
                    retention.at[(w,j)]/retention.at[(w,j-1)]
            )
        w=w+1
    
    #히트맵 시각화
    #테이블 크기 설정 하기
    plt.figure(figsize=(10,8))
    sns.heatmap(data=retention,
               annot=True,
               fmt='.2%',
               vmin=0,
               vmax=1,
               cmap="BuGn")
    plt.xlabel('주차', fontsize=14,labelpad=30)
    plt.ylabel('개강일', fontsize=14,rotation=360,labelpad=30)
    plt.yticks(rotation=360)
    plt.tight_layout()
    plt.show()
     
  •  시각화

출처
내일배움캠프 교육 자료