資料分析:Python分析學生資料
本文為優達學城資料分析入門課程的mini專案,所用資料集為優達學城某段時間內的學生資料。
資料簡介
全部資料包含三個檔案,其內容分別為:
- enrollments.csv:
- daily-engagement.csv
- project-submissions.csv
各欄位的意義見table_desc.txt,文末附錄會給出。
載入資料
import csv
def readcsv_Dict(file):
with open(file) as fd:
reader=csv.DictReader(fd) #以字典形式讀取CSV,適用於帶headline的資料
return list(reader)
enrollments=readcsv_Dict("./enrollments.csv")
engagements=readcsv_Dict("./daily-engagement.csv")
submissions=readcsv_Dict("./project-submissions.csv")
print(len(enrollments),len(engagements),len(submissions))
輸出為:1640,136240,3642
預覽資料
enrollments[0]
engagements[0]
submissions [0]
資料處理
格式修正
從CSV中讀出的資料是以字串儲存在記憶體中的,需要對原資料中的數值型別與時間型別進行還原。
from datetime import datetime as dt
def parase_data(data):
if data=="":
return None
else:
return dt.strptime(data, '%Y/%m/%d')
def parase_maybe_int(i):
if i=="":
return None
else:
return int(i)
#字串轉換
for line in enrollments:
line['join_date']=parase_data(line['join_date'])
line['cancel_date']=parase_data(line['cancel_date'])
line['days_to_cancel']=parase_maybe_int(line['days_to_cancel'])
line['is_udacity']=(line['is_udacity']=='TRUE')
line['is_canceled']=(line['is_canceled']=='TRUE')
for line in engagements:
line['utc_date']=parase_data(line['utc_date'])
line['num_courses_visited']=parase_maybe_int(float(line['num_courses_visited']))
line['total_minutes_visited']=float(line['total_minutes_visited'])
line['lessons_completed']=parase_maybe_int(float(line['lessons_completed']))
line['projects_completed']=parase_maybe_int(float(line['projects_completed']))
for line in submissions:
line['creation_date']=parase_data(line['creation_date'])
line['completion_date']=parase_data(line['completion_date'])
# print(enrollments[0])
# print(engagements[0])
# print(submissions[0])
欄位修正
注意到engagements中的學生賬戶欄位標識與另兩檔案中的不一樣,將其修改為與另兩者一致的’account_key’。
#修改不一致的鍵值(這裡也可以直接對原檔案進行修改)
for line in engagements:
line['account_key']=line['acct']
del line['acct']
print(engagements[0])
計算學生數
計算三個檔案中分別有多少學生的資料。
def get_unique_stu(stu_list):
unique_stu=set()
for stu in stu_list:
unique_stu.add(stu['account_key'])
return unique_stu
unique_enroller=get_unique_stu(enrollments)
unique_engager=get_unique_stu(engagements)
unique_submitter=get_unique_stu(submissions)
print(len(unique_enroller),len(unique_engager),len(unique_submitter))
輸出為:1302,1237,743
處理異常值
可以看到enrollments中的學生數要多於engagement中的學生數,這是不合理的,正常來說應該是engagement中的學生數大於等於enrollments的學生數,所以資料中一定存在異常值。
找出在enrollments中存在卻不在engagement中存在的學生,這裡排除掉當天註冊又登出的學生。
def find_outlier():
for enroll_stu in enrollments:
stu=enroll_stu['account_key']
if stu not in unique_engager and enroll_stu['join_date']!=enroll_stu['cancel_date']:
print(enroll_stu)
find_outlier()
發現異常值有一個共同點:(‘is_udacity’, True),都為優達學城的測試賬號。
刪除掉這些測試賬號。
test_acct=[]
for stu in enrollments:
if stu['is_udacity']:
test_acct.append(stu['account_key'])
test_acct=list(set(test_acct))
def remove_test_acct(stu_list):
tmp=[]
for stu in stu_list:
if stu['account_key'] not in test_acct:
tmp.append(stu)
return tmp
enrollments=remove_test_acct(enrollments)
engagements=remove_test_acct(engagements)
submissions=remove_test_acct(submissions)
print(len(test_acct))
有6個測試賬號。
篩選資料
由於整個資料集中包含了七天免費試用的學生,這一部分學生資料明顯是沒有意義的,需要篩選出付費學生的資料。
paid_students_join_date={}
for stu in enrollments:
#未登出或者取消時間超過七天的學生
if not stu['is_canceled'] or stu['days_to_cancel']>7:
#當字典中不存在條目時建立,且只保留最新的join_date
if stu['account_key'] not in paid_students_join_date or stu['join_date']>paid_students_join_date[stu['account_key']]:
paid_students_join_date[stu['account_key']]=stu['join_date']
paid_acct=list(set(paid_students_join_date.keys()))
def remove_free_acct(stu_list):
tmp=[]
for stu in stu_list:
if stu['account_key'] in paid_acct:
tmp.append(stu)
return tmp
paid_enrollments=remove_free_acct(enrollments)
paid_engagements=remove_free_acct(engagements)
paid_submissions=remove_free_acct(submissions)
print(len(paid_acct))
共有995名付費學生。
分析資料
一週資料
這裡只分析一週內的資料。
paid_engagement_1stweek=[]
for stu in paid_engagements:
days=(stu['utc_date']-paid_students_join_date[stu['account_key']]).days
if (days>=0 and days<7):
paid_engagement_1stweek.append(stu)
print(len(paid_engagement_1stweek))
一週內有6919條資料。
學習時間
from collections import defaultdict
#整合同一學生賬戶的資訊
def group_data(data,key_name):
grouped_data=defaultdict(list) #value為列表的字典
for data_point in data:
grouped_data[data_point[key_name]].append(data_point) #將同一賬戶的資訊整合
return grouped_data
#在整合資訊中計算某個欄位的累加值
def count_total(grouped_data,field_name):
total_dic={}
for acct in grouped_data:
total=0
for info in grouped_data[acct]:
total+=info[field_name]
total_dic[acct]=total
return total_dic
engager_acct_1stweek=group_data(paid_engagement_1stweek,'account_key')
total_minutes_byacct=count_total(engager_acct_1stweek,'total_minutes_visited')
total_minutes=list(total_minutes_byacct.values())
import numpy as np
print(np.mean(total_minutes),np.max(total_minutes),np.min(total_minutes),np.std(total_minutes))
輸出:306.708326753 3564.7332645 0.0 412.99693341
完成課程數
total_lessons_byacct=count_total(engager_acct_1stweek,'lessons_completed')
total_lessons=list(total_lessons_byacct.values())
print(np.mean(total_lessons),np.max(total_lessons),np.min(total_lessons),np.std(total_lessons))
輸出:1.63618090452 36 0 3.00256129983
課程訪問量
total_courses_visited_byacct=count_total(engager_acct_1stweek,'num_courses_visited')
total_courses_visited=list(total_courses_visited_byacct.values())
print(np.mean(total_lessons),np.max(total_lessons),np.min(total_lessons),np.std(total_lessons))
輸出:1.63618090452 36 0 3.00256129983
學習天數
當’num_courses_visited’欄位不為零時則學習天數加一天。
total_studydays_byacct={}
for acct in engager_acct_1stweek:
total_studydays=0
for info in engager_acct_1stweek[acct]:
if info['num_courses_visited']==0:
continue
else:
total_studydays+=1
total_studydays_byacct[acct]=total_studydays
total_studydays=list(total_studydays_byacct.values())
print(np.mean(total_studydays),np.max(total_studydays),np.min(total_studydays),np.std(total_studydays))
輸出為:2.86733668342 7 0 2.25519800292
通過情況
以課程746169184與3176718735為例,計算通過這兩門課程的學生數。
passing_acct=set()
project_key=['746169184','3176718735']
pass_flag=['PASSED','DISTINCTION']
for stu in paid_submissions:
if (stu['lesson_key'] in project_key) and (stu['assigned_rating'] in pass_flag):
passing_acct.add(stu['account_key'])
print(len(passing_acct))
付費學生共有995名,最近一週有647名學生通過了這兩門課程。
兩群體資料對比
對比通過課程的學生與未通過學生的一系列資料,來發現規律。
#目前已有資料
# total_courses_visited_byacct
# total_lessons_byacct
# total_minutes_byacct
# total_studydays_byacct
def compare_stu_data(data_to_compare):
passing_stu_data={}
non_passing_stu_data={}
for stu_acct in data_to_compare:
if stu_acct in passing_acct:
passing_stu_data[stu_acct]=data_to_compare[stu_acct]
else:
non_passing_stu_data[stu_acct]=data_to_compare[stu_acct]
return passing_stu_data,non_passing_stu_data
課程瀏覽量
passing_stu_courses_visited,non_passing_stu_courses_visited=compare_stu_data(total_courses_visited_byacct)
passing_visited_data,non_passing_visited_data=list(passing_stu_courses_visited.values()),list(non_passing_stu_courses_visited.values())
print(np.mean(passing_visited_data),np.max(passing_visited_data),np.min(passing_visited_data),np.std(passing_visited_data))
print(np.mean(non_passing_visited_data),np.max(non_passing_visited_data),np.min(non_passing_visited_data),np.std(non_passing_visited_data))
輸出:
4.72642967543 25 0 3.7002397793
2.58908045977 18 0 2.90670969025
差異顯著。課程瀏覽量在一定程度上可以體現學生的學習興趣,通過課程的學生的學習興趣要比未通過的學生高。
課程完成量
passing_stu_lessons,non_passing_stu_lessons=compare_stu_data(total_lessons_byacct)
passing_lessons_data,non_passing_lessons_data=list(passing_stu_lessons.values()),list(non_passing_stu_lessons.values())
print(np.mean(passing_lessons_data),np.max(passing_lessons_data),np.min(passing_lessons_data),np.std(passing_lessons_data))
print(np.mean(non_passing_lessons_data),np.max(non_passing_lessons_data),np.min(non_passing_lessons_data),np.std(non_passing_lessons_data))
輸出:
2.05255023184 36 0 3.14222705558
0.862068965517 27 0 2.54915994183
差異顯著。這個沒什麼好解釋的。
學習時間
passing_stu_minutes,non_passing_stu_minutes=compare_stu_data(total_minutes_byacct)
passing_minutes_data,non_passing_minutes_data=list(passing_stu_minutes.values()),list(non_passing_stu_minutes.values())
print(np.mean(passing_minutes_data),np.max(passing_minutes_data),np.min(passing_minutes_data),np.std(passing_minutes_data))
print(np.mean(non_passing_minutes_data),np.max(non_passing_minutes_data),np.min(non_passing_minutes_data),np.std(non_passing_minutes_data))
輸出:
394.586046483 3564.7332645 0.0 448.49951933
143.326474266 1768.5227493 0.0 269.538619008
差異顯著。同樣無需解釋。
學習天數
passing_stu_studydays,non_passing_stu_studydays=compare_stu_data(total_studydays_byacct)
passing_studydays_data,non_passing_studydays_data=list(passing_stu_studydays.values()),list(non_passing_stu_studydays.values())
print(np.mean(passing_studydays_data),np.max(passing_studydays_data),np.min(passing_studydays_data),np.std(passing_studydays_data))
print(np.mean(non_passing_studydays_data),np.max(non_passing_studydays_data),np.min(non_passing_studydays_data),np.std(non_passing_studydays_data))
輸出:
3.38485316847 7 0 2.25882147092
1.90517241379 7 0 1.90573144136
差異顯著。
視覺化
%matplotlib inline
import matplotlib.pyplot as plt
學習時間
plt.xlabel("total_minutes")
plt.ylabel("num of stu")
plt.hist(passing_minutes_data,bins=30)
plt.hist(non_passing_minutes_data,bins=30)
學習天數
通過的學生:
plt.xlabel("study days")
plt.ylabel("num of stu")
plt.hist(passing_studydays_data,bins=7)
未通過的學生:
plt.xlabel("study days")
plt.ylabel("num of stu")
plt.hist(non_passing_studydays_data,bins=7)
附錄
enrollments.csv:
- account_key:學員帳號標識
- status:收集資料時,學員課程狀態的資料,可能的值為“已取消”(’canceled’)和“學習中”(’current’)。
- join_date:學員加入課程的時間。
- cancel_date:學員取消的時間,如果學員沒有取消則顯示空。
- days_to_cancel:加入課程時間和取消時間之間間隔的天數,如果學員沒有取消就顯示空。
- is_udacity:如果是優達學城官方的測試賬號,就顯示 True,如果不是,顯示 False。
- is_canceled:如果收集資料時,學員已經取消,就顯示 True,如果還沒有取消,顯示 False。
daily_engagement.csv:報名登錄檔中每一位學生,在資料分析納米學位的日常參與學習資料。
- acct:學員賬號的唯一識別符號,這是他們參與學習的資料。(匯入資料後修改為account_key)
- utc_date:收集資料的日期。
- num_courses_visited:這一天裡,學生訪問資料分析納米學位課程的數量(訪問時間至少 2 分鐘)。納米學位課程和免費課程,如果內容相同,也需要分開考慮。
- total_minutes_visited:在這一天,學生學習資料分析納米學位課程的總時間(分鐘)。
- lessons_completed:在這一天,學生訪問的資料分析納米學位中的課程總數。
- projects_completed:在這一天,學生完成的資料分析納米學位中的專案總數。
project_submissions.csv:關於在報名登錄檔中的每個學生提交資料分析納米學位專案的資料。
- creation_date:專案提交的日期。
- completion_date:專案被評估的日期。
- assigned_rating:這個欄位有4個可能的值:
blank - 專案沒有被評估。
INCOMPLETE - 專案不符合要求。
PASSED - 專案符合要求。
DISTINCTION – 專案超出要求。
UNGRADED – 專案無法被評估。(例如:包括損壞的檔案) - account_key:提交了該專案的學生賬號的唯一識別符號。
- lesson_key:提交了專案的唯一識別符號。(練習使用ID:746169184,3176718735)
- processing_state:這個欄位可能有2個可能的值:
CREATED - 專案已提交但沒有被評估。
EVALUATED – 專案已經被評估。