내일배움 본캠프

[내일배움 본캠프]좋은 가설 세우기와 코호트 분석

hyeon-ji 2026. 5. 18. 20:13

오늘은 팀원들과 함께 진행했던 커리어 스터디를 발표하는 날이었다. 사다리타기로 발표자를 정했는데 나로 뽑혀서 발표를 맡아 진행했다.

그동안 팀 프로젝트로 다 듣지 못했던 강의를 완강하였다¡ 강의에서는 좋은 가설이 무엇인지와 코호트 분석 하는 방법에 대해서 학습을 진행했다.


  좋은 가설이란¿

더보기

  풀고자 하는 문제의 방향성과 일치하는 가설 → 무슨 문제를 풀고자 하는지 명확히 알 것

  테스트 가능한 가설 → 데이터로 확인이 가능한 가설을 세울 것

● 액션으로 이어질 수 있는 가설 → 가설 검증 후 결과에 따라 특정 액션으로 유도될 수 있을 것

▶ 이번 강의를 통해 풀어야할 문제

  • 8월 중순부터 웹개발 종합반의 완주율이 크게 떨어진 이유를 밝혀야 한다.
  • 완주가 가능한 수강생들이 수강을 할 수 있도록 적절한 포지셔닝을 고민해야 한다.

  예측할 수 있는 것¡

  • 2-30대가 우리 프로덕트에 관심이 있다.
  • 2-30대가 완주율이 다른 나이대에 비해 낮을 가능성이 있다.
  • 2-30대가 완주율이 낮은 이유는 다른 나이대에 비해 바쁘기 때문일 수 있다.

→  집단적으로 관심은 있었는데 지속적으로 수강을 할 수 없는 것은 애초에 시간 부족 등의 이유로 참여하기 어려운 상황이었을 확률이 높습니다.

 

가설 세우기

→ 다른 연령대에 비해 바쁜 20~30대의 수강 완주율이 상대적으로 낮을 것이다.

import pandas as pd
import matplotlib.pyplot as plt
plt.rc('font', family='NanumBarunGothic')

sparta_data = pd.read_csv('파일경로')
sparta_data.tail()

#나이대별로 수강률 합 구하기
progress_rate_by_age = sparta_data.groupby('age')['progress_rate'].sum()

#나이대별 수강인원 구하기
number_people_by_age = sparta_data.groupby('age')['_id'].count()

#나이대별 완주율 평균 구하기
average = progress_rate_by_age/number_people_by_age

#바 그래프로 시각화하기
plt.figure(figsize=(6,6))

#그래프의 x축 눈금 설정
plt.xticks([10,20,30,40,50])

#plt.bar(X축값, Y축값)
plt.bar(average.index, average,width=8)


#그래프의 바에 각 수치율을 추가 해 볼까요?
bar = plt.bar(average.index, average,width=8)
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=15,pad=20)

#그래프의 x축 라벨 이름
plt.xlabel('나이',fontsize=12,labelpad=20)

#그래프의 y축 라벨 이름
plt.ylabel('수강생(명)',fontsize=14,rotation=360,labelpad=35)

#그래프를 화면에 나타나도록 합니다.
plt.show()

→ 2-30대의 완주율 평균이 다른 나이대롸 비슷한 비율이라는 점을 확인할 수 있다. 프로덕트가 적절한 고객에게 가지 못해 불만족이 발생하고 있지는 않을지 우려하였지만, 프로덕트 개선이나 광고 메인 타겟 변경 등은 고혀에서 배제해도 좋을 것 같다.


▶ 전화나 카톡 등으로 고객을 밀접관리를 한다면 완주율이 높아질까¿

▶ 이번 강의를 통해 풀어야할 문제

  • 8월 중순부터 웹개발 종합반의 완주율이 크게 떨어진 이유를 밝혀야 한다.
  • 고객이 수강을 완료할 수 있도록 적절한 동기부여를 주어야 한다.
  •  

  위 상황에서 알 수 있는 것

  • 밀접관리와 완주율이 동시에 감소했으므로 연관이 있을 가능성이 있다.

 가설 세우기

→ 찐한관리를 받은 인원이 그렇지 않은 인원보다 완주율이 높을 것이다.

#라이브러리 불러오기
import pandas as pd
import matplotlib.pyplot as plt
plt.rc('font', family='NanumBarunGothic')

sparta_data = pd.read_table('/content/sparta_data.csv',sep=',')

#결측치 제거
sparta_data = sparta_data.dropna()

#데이터 확인 하기
sparta_data.tail() 
managed = ['TRUE','FALSE'] # 데이터 수정하기

# sparta_data.groupby('managed')['progress_rate'].count()
managed_data_avg = sparta_data.groupby('managed')['progress_rate'].sum()/sparta_data.groupby('managed')['_id'].count()

#바그래프 시각화하기
plt.figure(figsize=(6,6))

#plt.bar(X축값, Y축값)
plt.bar(managed_data_avg.index,managed_data_avg.values)

#그래프의 바에 각 수치율을 추가
bar = plt.bar(managed_data_avg.index,managed_data_avg.values)
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축 눈금 레이블 지정하기
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.show()

→ 밀접관리를 받는 인원과 받지 않는 인원을 나눈어서 각 그룹별 완주율의 평균을 비교했다. 그 결과 밀접 관리를 받은 그룹의 수강 완주율은 38.6%, 받지 않은 그룹의 수강 완주율은 68.5 %로 밀접 관리를 받은 그룹의 완주율이 월등히 높은 것을 확인할 수 있었다. 실제로 밀접 관리 신청 비율을 높이는 액션을 진행해보는 것이 완주율 개선에 효과적일 것이다 .


▶ 실제로 프로덕트 개선은 도움이 되었을까¿

▶ 이번 강의를 통해 풀어야할 문제

  • 고객이 수강을 완료할 수 있도록 흥미롭고 도움이 되는 콘텐츠를 제공해야 합니다.
  • 8월 중순부터 웹개발 종합반의 완주율이 크게 떨어진 이유를 밝혀야 합니다.
  •  

  위 상황에서 알 수 있는 것

  • 8월 중순부터 웹개발 종합반의 완주율이 크게 떨어졌으므로, 비슷한 시기에 진행한 프로덕트 개선이 영향을 미쳤을 가능성이 있어보입니다.

 가설 세우기

8월 둘째 주 부터 변경된 3주 차 강의의 완주율이 현저히 떨어졌을 것이다.

→ 정말 문제가 있었을 경우에는 ‘이전의 커리큘럼으로 돌아갈 것’이고, 문제가 없었을 경우 ‘다른 요인을 찾아볼 것’이다.

#라이브러리 불러오기
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
plt.rc('font', family='NanumBarunGothic')

# 한글깨짐 방지
plt.rc('font', family='NanumBarunGothic')
sparta_data = pd.read_table('파일경로',sep=',')
sparta_data.tail()

#날짜 데이터 타입 변경
format='%Y-%m-%dT%H:%M:%S.%f'
sparta_data['start_time'] = pd.to_datetime(sparta_data['created_at'], format=format,infer_datetime_format=True)
sparta_data.tail()

#시작 week 구하기
sparta_data['start_week']= sparta_data['start_time'].dt.isocalendar().week
sparta_data.tail()

#시작 주 범위 알기
category_range = set(sparta_data['start_week'])
category_range

# 범주화 하기
#번주화할 데이터 
progress_rate = list(sparta_data['progress_rate'])
progress_rate

#범주를 구분하는 기준 bins
bins = [0,4.11,26.03,41.10,61.64,80.82,100]

#구분한 범주의 라벨 labels
labes=[0,1,2,3,4,5]

#범주화에 사용하는 함수 pd.cut
cuts = pd.cut(progress_rate,bins, right=True,include_lowest=True, labels=labes)
cuts
cuts = pd.DataFrame(cuts)
cuts.tail()


# 표 합치기
sparta_data = pd.concat([sparta_data,cuts],axis=1, join='inner')
sparta_data.head()

#표 인덱스 변경하기
sparta_data.columns=['created_at','user_id','name','progress_rate','start_time','start_week',"week"]
sparta_data.head()

#시작주와, 수강 주차별 기준으로 표 grouping 하기
grouping = sparta_data.groupby(['start_week','week'])
grouping.head()

#시작주와, 수강 주차별에 해당하는 수강생 수 구하기
cohort_data = grouping['user_id'].apply(pd.Series.nunique)
cohort_data = pd.DataFrame(cohort_data)
cohort_data.head(10)

#각 주차별 수강한 수강생 총 합 구하기
k=31
for i in range(6):
  for j in range(5, 0, -1):
    cohort_data.at[(k,j-1), 'user_id'] = int(cohort_data.at[(k,j),'user_id']) +  int(cohort_data.at[(k,j-1),'user_id'])
  k=k+1
cohort_data = cohort_data.reset_index()
cohort_data.head()


cohort_counts = cohort_data.pivot(index="start_week",
                                  columns="week",
                                  values="user_id")
cohort_counts

# 앞서 만든 피벗 테이블을 retention 변수에 저장하기
retention = cohort_counts
#각 주(week) 별 최초 수강생 수만 가져오기
cohort_sizes = cohort_counts.iloc[:,0]
cohort_sizes.head()

# 최초 수강생 수를 각 데이터에 나눠주기
retention = cohort_counts.divide(cohort_sizes, axis=0)
retention

#각 수치 퍼센트로 변경하기
retention.round(3)*100

#테이블 크기 설정 하기
plt.figure(figsize=(10,8))

sns.heatmap(data="필요한 데이터 입력하기",
           annot=True,
           fmt='.0%',
           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.show()

→ 4주차 컬럼에서만 떨어진 것이 아니라 전체적으로 떨어진 것을 확인할 수 있다.  추가적으로 4주차 컬럼이 일정하게 떨어져서 유지되고 있지 않다. 다른 요인 때문에 완주율이 떨어졌다고 보는 것이 맞을 것이다.


▶ 아래의 오른쪽 차트와 같은 형태로 시각화 하기

#라이브러리 불러오기
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
plt.rc('font', family='NanumBarunGothic')

# 한글깨짐 방지
plt.rc('font', family='NanumBarunGothic')
sparta_data = pd.read_table('파일경로',sep=',')
sparta_data.tail()

#날짜 데이터 타입 변경
format='%Y-%m-%dT%H:%M:%S.%f'
sparta_data['start_time'] = pd.to_datetime(sparta_data['created_at'], errors='coerce')
sparta_data.tail()

#시작 week 구하기
sparta_data['start_week']= sparta_data['start_time'].dt.isocalendar().week
sparta_data.tail()

#시작 주 범위 알기
category_range = set(sparta_data['start_week'])
category_range

# 범주화 하기
#번주화할 데이터 
progress_rate = list(sparta_data['progress_rate'])
progress_rate

#범주를 구분하는 기준 bins
bins = [0,4.11,26.03,41.10,61.64,80.82,100]

#구분한 범주의 라벨 labels
labes=[0,1,2,3,4,5]

#범주화에 사용하는 함수 pd.cut
cuts = pd.cut(progress_rate,bins, right=True,include_lowest=True, labels=labes)
cuts
cuts = pd.DataFrame(cuts)
cuts.tail()


# 표 합치기
sparta_data = pd.concat([sparta_data,cuts],axis=1, join='inner')
sparta_data.head()

#표 인덱스 변경하기
sparta_data.columns=['created_at','user_id','name','progress_rate','start_time','start_week',"week"]
sparta_data.head()

#시작주와, 수강 주차별 기준으로 표 grouping 하기
grouping = sparta_data.groupby(['start_week','week'])
grouping.head()

#시작주와, 수강 주차별에 해당하는 수강생 수 구하기
cohort_data = grouping['user_id'].apply(pd.Series.nunique)
cohort_data = pd.DataFrame(cohort_data)
cohort_data.head(10)

#각 주차별 수강한 수강생 총 합 구하기
k=31
for i in range(6):
  for j in range(5, 0, -1):
    cohort_data.at[(k,j-1), 'user_id'] = int(cohort_data.at[(k,j),'user_id']) +  int(cohort_data.at[(k,j-1),'user_id'])
  k=k+1
cohort_data = cohort_data.reset_index()
cohort_data.head()


cohort_counts = cohort_data.pivot(index="start_week",
                                  columns="week",
                                  values="user_id")
cohort_counts

# 앞서 만든 피벗 테이블을 retention 변수에 저장하기
retention = cohort_counts
#각 주(week) 별 최초 수강생 수만 가져오기
cohort_sizes = cohort_counts.iloc[:,0]
cohort_sizes.head()

# 최초 수강생 수를 각 데이터에 나눠주기
retention = cohort_counts.divide(cohort_sizes, axis=0)
retention

#각 수치 퍼센트로 변경하기
retention.round(3)*100

#주차별 수강 전환율 구하기
w=31
for i in range(6):
  for j in range(1, 6):
	  if retention.at[(w, j - 1)] != 0:
		  retention.at[(2, j)] = retention.at[(w, j)] / retention.at[(w, j- 1)]
  w=w+1


retention

#주차별 수강 전환율 히트맵 
plt.figure(figsize=(10,8))

sns.heatmap(data=retention,
           annot=True,
           fmt='.0%',
           vmin=0,
           vmax=1,
           cmap="BuGn")



plt.title('개강일별 주차 간 전환율', fontsize=20)
plt.xlabel('주차', fontsize=14,labelpad=30)
plt.ylabel('개강일', fontsize=14,rotation=360,labelpad=30)
plt.yticks(rotation=360)

plt.show()

오늘 갑작스럽게 발표를 맡게되어 좀 부담스러운 부분이 있었지만, 커리어 스터디 발표자료를 만들 때 팀원들과 내용을 정리해둔 것이 있어 참고하며 막힘없이 대본을 작성할 수 있었다. 또 이번 강의는 좀 난이도가 높은 편이었다고 느껴졌다. 코호트차트로 시각화하는 것에 어려움이 있었어서 복습을 통해 익숙해져야겠다고 생각했다.

 

첫 주차를 마무리하며 KPT회고하는 시간을 가졌다.

 

K 「Keep (잘했던 점 / 유지하고 싶은 것)」 : 관심 있는 도메인에 대해 자세히 분석하며 해당 도메인에서 요구하는 것이 무엇이고, 발생하는 문제점이 무엇인지 알아가 본점

→ 팀의 성과 : 채용 공고 분석을 통해 관심 도메인의 실무적 문제점과 공통 요구 역량을 파악하며 산업에 대한 이해도를 높임. 이를 바탕으로 데이터 분석 역량의 실무적 가치를 깨닫고 구체적인 취업 준비 방향과 전략을 수립할 수 있었음.

P「Problem (개선이 필요하거나 아쉬웠던 점)」 : 도메인에서 발생하는 데이터와 활용방법까지는 도출해내었지만, 사용되는 통계기법과 같이 구체적으로 알아보지 못한 점

팀 차원의 아쉬움 :도메인 데이터의 전반적인 활용 방식은 파악했으나, 실제 기업의 세부 적용 사례나 통계 기법 및 자료까지 연계하여 분석을 못한 점. 외부 조사의 제약으로 인해 실무 현장에서 다루는 디테일한 업무 내용이나 문제 해결 과정을 상세히 파악하는 데에는 정보의 한계가 존재했음.

T「Try (앞으로 시도해보고 싶은 것)」: 이번에 진행한 커리어 스터디에서 알아낸 공정에서 발생하는 데이터를 추후 프로젝트에서 다뤄보고 싶음

문제 해결 방안 :전문 사이트나 산업 리포트 등 다양한 자료를 활용해 타겟 기업과 직무 탐색의 폭을 넓히고, 각 도메인에 대해서 깊이 있는 전문성을 확보할 것. 이번 스터디에서 파악한 도메인 데이터와 향후 데이터 역량을 결합하여, 실제 프로젝트에 적용하고 기업 트렌드에 맞는 차별화된 포트폴리오를 구축할 것.