데이터분석 6기/본캠프
2025-05-12 실전프로젝트 2 - 학습일 별로 데이터 분류
seyeon1130
2025. 5. 12. 21:07
최종 데이터 전처리
# 불필요한 컬럼 드랍
df.drop(['roles', 'incomplete_flag', 'nplay_video'], axis=1, inplace=True)
# 인적사항 결측치 제거
df.dropna(subset=['LoE_DI', 'YoB', 'gender'], inplace=True)
# ndays_act 조건에 맞는 행 삭제
df = df[~((df['ndays_act'].notnull()) & (df['ndays_act'] >= 2) & (df['last_event_DI'].isnull()))]
# last_event_DI를 start_time_DI로 대체
df['last_event_DI'].fillna(df['start_time_DI'], inplace=True)
# 나머지 결측치는 0으로 대체
df.fillna(0, inplace=True)
# datetime 형식으로 변환
date_columns = ['start_time_DI', 'last_event_DI']
for col in date_columns:
df[col] = pd.to_datetime(df[col], errors='coerce')
# last가 start보다 이전인 경우 제거
df = df[~(df['last_event_DI'] < df['start_time_DI'])]
# grade 숫자 변환 및 결측치 처리
df['grade'] = pd.to_numeric(df['grade'], errors='coerce')
df['grade'].fillna(0, inplace=True)
# 나이 컬럼 생성, 10세에서 100세까지
df['age'] = df['start_time_DI'].dt.year - df['YoB']
df = df[(df['age'] > 10) & (df['age'] < 100)]
#점수 최댓값은 1.0
df = df[df['grade']<=1.0]
#학습일 0일인데 수료한 경우
df = df[~((df['ndays_act']==0)&(df['certified']==1))]
전에꺼에 추가로
- 나이를 10세에서 100세까지로 추가로 정리함
- 학습일이 0일인데 수료한 경우 11건 결측치 처리함
- last_eventDI가 결측일 경우, 학습일이 0 혹은 1이기 때문에 start_time_DI로 대체함.
- last_event_DI가 start_time보다 이전일 경우 제거
- grade(점수)가 1.0 이상인 경우 제거
파생변수 생성
나이대 컬럼
#나이대 컬럼 생성
df['age_group'] = pd.cut(df['age'],
bins=[10, 19, 29, 39, 49, 59, 69, 79, 89],
labels=['10대', '20대', '30대', '40대', '50대', '60대', '70대', '80대'])
강의 명 컬럼
# course 이름
# / 를 기준으로 course_id를 세개의 컬럼으로 분류함
df[['university', 'course_name', 'term']] = df['course_id'].str.split('/', expand=True)
# 딕셔너리 생성해서 매칭
course_name_dict = {
'CS50x' : 'Introduction to computer science', # 컴퓨터 과학 입문
'PH207x' : 'Quantitative Methods in Clinical & Public Health Research', # 임상 및 공중 보건 연구의 정량적 방법
'PH278x' : 'Human Health and Global Environmental Change', # 인간 건강과 지구 환경 변화
'CB22x' :'The Ancient Greek Hero', # 고대 그리스 영웅 (문학)
'ER22x' : 'Michael Sandel, Justice', # 마이클 샌델의 정의론
'6.00x' : 'Introduction to Computer Science and Programming Using Python', # 컴퓨터 프로그래밍 & 파이썬 입문
'6.002x' : 'Circuits and Electronics', # 회로와 전자공학
'8.02x' : 'Electricity and Magnetism', # 전기와 자기 (물리학 입문)
'14.73x' : 'The Challenges of Global Poverty', # 세계 빈곤 위기
'7.00x' : 'Introduction to Biology', # 생물학 입문
'3.091x' : 'Introduction to Solid State Chemistry', # 고체 화학 입문
'8.MReV' : 'Mechanics ReView', # (물리학)
'2.01x' : 'Elements of Structures'# 구조요소 (구조역학 입문)
}
df['course_title'] = df['course_name'].map(course_name_dict)
학습 일자별 컬럼
def categorize_ndays_act(days):
if days == 0:
return "비학습"
elif days <= 3:
return "3일 이내 학습"
elif days <= 7:
return "3일 이상 학습"
elif days <= 30:
return "7일 이상 학습"
elif days <= 90:
return "30일 이상 학습"
else:
return "90일 이상 학습"
df['activity_category'] = df['ndays_act'].apply(categorize_ndays_act)
activity_order = [
"비학습",
"3일 이내 학습",
"3일 이상 학습",
"7일 이상 학습",
"30일 이상 학습",
"90일 이상 학습"
]
# activity_category를 Categorical 타입으로 변환 (순서 지정)
df['activity_category'] = pd.Categorical(df['activity_category'],
categories=activity_order,
ordered=True)
학습 상태 컬럼
df['status'] = ''
# 수강 신청만 하고 아예 보지 않는 수강생
df.loc[df['activity_category'] == '비학습', 'status'] = 'Non-Starter'
# 시청은 했지만 수료하지 못한 수강생
df.loc[(df['activity_category'] != '비학습') & (df['certified'] == 0), 'status'] = 'Non-Completer'
# 시청도 하고 수료도 한 수강생
df.loc[(df['activity_category'] != '비학습') & (df['certified'] == 1), 'status'] = 'Completer'
이후 다른 강의 들었는지 여부
#이후 다른 강의 들었는지 여부
# 사용자별 강의를 시간 순서로 정렬
df = df.sort_values(by=['userid_DI', 'start_time_DI'])
# 사용자별로 수강한 강의 순서 확인
df['next_course'] = df.groupby('userid_DI')['course_id'].shift(-1).notna()
내가 맡은 부분은 학습 일자별 학생 분류
위처럼 학생 일자별 컬럼을 만든 이후 분석 진행
분류 별 카운트
plt.figure(figsize=(12,5))
sns.countplot(x='activity_category',data= df)
for p in plt.gca().patches:
plt.gca().annotate(f'{int(p.get_height())}',
(p.get_x() + p.get_width() / 2., p.get_height()),
ha='center', va='baseline', fontsize=12)
plt.title('학습일 분류 카운트')
plt.show()
분류별 수료 수
c = df.groupby('activity_category')['certified'].sum().reset_index()
plt.figure(figsize=(12,5))
sns.barplot(x='activity_category',y='certified',data=c)
for p in plt.gca().patches:
plt.gca().annotate(f'{int(p.get_height())}',
(p.get_x() + p.get_width() / 2., p.get_height()),
ha='center', va='baseline', fontsize=12)
plt.title('학습일 별 수료 카운트')
plt.show()
수료는 30일 이상 학습자가 가장 많다.
분류별 수료율
# 수료 비율 카테고리별 시각화
certification_rate = df.groupby('activity_category')['certified'].mean().reset_index()
certification_rate['certified'] *= 100
plt.figure(figsize=(12, 6))
sns.barplot(x='activity_category', y='certified', data=certification_rate)
for p in plt.gca().patches:
plt.gca().annotate(f'{p.get_height():.2f}%',
(p.get_x() + p.get_width() / 2., p.get_height()),
ha='center', va='baseline', fontsize=12)
plt.title('카테고리별 수료율 (%)')
plt.show()
수료율은 90일 이상이 가장 많다. 시간이 지날수록 더 늘어나는 편
강의 콘텐츠를 본 비율은?
# 수료 비율 카테고리별 시각화
view_rate = df.groupby('activity_category')['viewed'].mean().reset_index()
view_rate['viewed'] *= 100
plt.figure(figsize=(12, 6))
sns.barplot(x='activity_category', y='viewed', data=view_rate)
for p in plt.gca().patches:
plt.gca().annotate(f'{p.get_height():.2f}%',
(p.get_x() + p.get_width() / 2., p.get_height()),
ha='center', va='baseline', fontsize=12)
plt.title('강의 콘텐츠를 본 비율 (%)')
plt.show()
비학습자도 꽤나 많이 본 것을 보아, 강의 콘텐츠는 강의자료인듯함.
초반만 슥 훑어보고 안본듯
강의 콘텐츠를 적극적으로 본 비율은?
# 콘텐츠 적극적으로 본 비율
explored_rate = df.groupby('activity_category')['explored'].mean().reset_index()
explored_rate['explored'] *= 100
plt.figure(figsize=(12, 6))
sns.barplot(x='activity_category', y='explored', data=explored_rate)
for p in plt.gca().patches:
plt.gca().annotate(f'{p.get_height():.2f}%',
(p.get_x() + p.get_width() / 2., p.get_height()),
ha='center', va='baseline', fontsize=12)
plt.title('강의 콘텐츠를 적극적으로 본 비율 (%)')
plt.show()
적극적으로 봤나 싶었더니 역시나 적극적으로 본 건 오래 공부한 사람들
다음 강의도 본 분류는?
# 사용자별 강의를 시간 순서로 정렬
df = df.sort_values(by=['userid_DI', 'start_time_DI'])
# 사용자별로 수강한 강의 순서 확인
df['next_course'] = df.groupby('userid_DI')['course_id'].shift(-1).notna()
# 액티비티 카테고리별 다음 강의 수강 비율 계산
activity_next_course = df.groupby('activity_category')['next_course'].mean().reset_index()
activity_next_course['next_course'] *= 100
# 시각화
plt.figure(figsize=(12, 6))
sns.barplot(x='activity_category', y='next_course', data=activity_next_course)
for p in plt.gca().patches:
plt.gca().annotate(f'{p.get_height():.2f}%',
(p.get_x() + p.get_width() / 2., p.get_height()),
ha='center', va='baseline', fontsize=12)
plt.title('액티비티 카테고리별 다음 강의 수강 비율 (시간적 순서 기준)')
plt.show()
오늘의 결론: 한 달이상, 특히 3개월 이상 학습할 경우 수료율도 높고 강의 재구매율이 높음. 따라서 학습을 오래 이끌 수 있는 방안 생각하기.
강의 콘텐츠를 자세히 볼 수록 학습이 길게 이어지는 모습이 보이긴 함.