【Python】数据分析优秀案例&项目经历-用数据分析能力构建高分学生人群画像

2022-09-27 10:49:14 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈

(有需要完整代码和数据的可以评论留下你的邮箱,我会尽快发送给你!)

大家早上好,本人姓吴,如果觉得文章写得还行的话也可以叫我吴老师。欢迎大家跟我一起走进数据分析的世界,一起学习! 感兴趣的朋友可以关注我或者我的数据分析专栏,里面有许多优质的文章跟大家分享哦。


提问:大家觉得成绩的高低都和哪些因素有关呢?男女生之间在科目上是否有明显的差异呢?

前言

又到了每周末知识分享环节。这次给大家分享的是kaggle上的一个非常有意思的项目,我们希望从中发现学生的测验表现与标签之间的关系。

总之,本次项目干货满满,除了通过绘图等常规手段之外,也用到了t检验等假设检验的方法来力求让结论更具说服力。

下面开始项目的正式介绍。

目录

  • 前言
  • 1.项目介绍
    • 1.1 项目介绍
    • 1.2 数据介绍
  • 2. 数据整理
  • 3. 学生成绩分析
    • 3.1 学生整体成绩分布
    • 3.2 不同学科成绩间的关联度以及不同学生人群擅长科目
    • 3.3 高分学生人群画像
      • 3.3.1 父母学历
      • 3.3.2 学生性别
  • 结束语
代码语言:javascript复制
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots 
import scipy.stats as stats
import scipy
from scipy.stats import ttest_ind
from scipy.stats import chisquare
from scipy.stats import chi2_contingency
import numpy as np

1.项目介绍

1.1 项目介绍

本文数据集来自竞赛平台Kaggle,共拥有1000条数据,并已经过脱敏处理。数据集共包含9个标签,我们希望从中发现学生的测验表现与标签之间的关系。

1.2 数据介绍

以下标签解释:

  • Unnamed:学生编号
  • race/ethnicity:种族
  • parental level of education:父母受教育程度
    • bachelor’s degree: 学士学位
    • some college:大学肄业
    • master’s degree:硕士学位
    • associate’s degree:副学士
    • high school:高中
    • some high school:高中肄业
  • lunch:午餐花费
    • Standard: 标准
    • free/reduced: 学校免费提供或低于标准
  • test preparation course: 是否完成与考试相关课程
    • none: 未完成
    • completed: 完成
  • math percentage:数学成绩
  • reading score percentage: 阅读成绩
  • writing score percentage: 写作成绩
  • sex: 性别

2. 数据整理

代码语言:javascript复制
data = pd.read_csv('Student Performance new.csv')
代码语言:javascript复制
data.info()
代码语言:javascript复制
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 9 columns):
 #   Column                       Non-Null Count  Dtype  
---  ------                       --------------  -----  
 0   Unnamed: 0                   1000 non-null   int64  
 1   race/ethnicity               1000 non-null   object 
 2   parental level of education  1000 non-null   object 
 3   lunch                        1000 non-null   object 
 4   test preparation course      1000 non-null   object 
 5   math percentage              1000 non-null   float64
 6   reading score percentage     1000 non-null   float64
 7   writing score percentage     1000 non-null   float64
 8   sex                          1000 non-null   object 
dtypes: float64(3), int64(1), object(5)
memory usage: 70.4  KB

本数据集共包含1000条数据,无数据缺失,数据类型包括整数,浮点数与对象类型

代码语言:javascript复制
data.sample(n=5) # 随机抽取数据查看

Unnamed: 0

race/ethnicity

parental level of education

lunch

test preparation course

math percentage

reading score percentage

writing score percentage

sex

113

113

group D

some college

standard

none

0.51

0.58

0.54

F

342

342

group B

high school

standard

completed

0.69

0.76

0.74

F

475

475

group D

bachelor’s degree

standard

completed

0.71

0.76

0.83

F

181

181

group C

some college

free/reduced

none

0.46

0.64

0.66

F

41

41

group C

associate’s degree

standard

none

0.58

0.73

0.68

F

代码语言:javascript复制
# 数据Unnamed列为学生编号,我们将其舍弃
data = data.drop(columns=['Unnamed: 0'], axis=1)

对于部分标签,存在多个变量, 我们需要对其进一步观察

代码语言:javascript复制
labels = ['race/ethnicity', 'parental level of education', 'lunch', 'test preparation course']
for label in labels:
    print(f'标签{ 
     label}情况:')
    print(f'共有{ 
     data[label].nunique()}个变量,分别为')
    print(data[label].unique())
    print('*'*20)
代码语言:javascript复制
标签race/ethnicity情况:
共有5个变量,分别为
['group B' 'group C' 'group A' 'group D' 'group E']
********************
标签parental level of education情况:
共有6个变量,分别为
["bachelor's degree" 'some college' "master's degree" "associate's degree"
 'high school' 'some high school']
********************
标签lunch情况:
共有2个变量,分别为
['standard' 'free/reduced']
********************
标签test preparation course情况:
共有2个变量,分别为
['none' 'completed']
********************

对于分数标签,我们增加一列平均分(average score)

代码语言:javascript复制
average_score = data[['math percentage', 'reading score percentage', 'writing score percentage']].mean(axis=1)
data.insert(7, 'average score', np.round(average_score, 2))

在此基础上,我们对average_score进行分箱,以0-59,60-69,70-79,80-89,90-100为分隔,将分数分为对应的F, D, C, B, A。

代码语言:javascript复制
performance_level = pd.cut(data['average score'], bins=[0, 0.59, 0.69, 0.79, 0.89, 100], 
                                   labels=['F', 'D', 'C', 'B', 'A'] )
data.insert(8, 'performance level', performance_level)
代码语言:javascript复制
data.groupby('sex')['sex'].count()
代码语言:javascript复制
sex
F    518
M    482
Name: sex, dtype: int64

本数据集男女比例为:48.2%比51.8%。我们认为其基本符合男女比例在美国的分布,为了进一步进行验证,我们可以引入卡方拟合度检验:

  • H0:在选取数据集时,男性与女性被选中的概率皆为50%
  • H0:上述概率不成立
代码语言:javascript复制
expected_number = [500, 500] # 理论上1000个样本中男女人数
observed_number = [482, 518] # 实际观察到的男女人数
result = stats.chisquare(f_obs=observed_number, f_exp=expected_number)
print(f'卡方拟合度检验的P值为:{ 
     result[1]}')
代码语言:javascript复制
卡方拟合度检验的P值为:0.25494516431731784

据此,我们可以认为,如果从男女比例出发,这份数据为随机抽取。

至此,数据整理结束,我们再次查看此时的数据情况。

代码语言:javascript复制
data.sample(n=5)

race/ethnicity

parental level of education

lunch

test preparation course

math percentage

reading score percentage

writing score percentage

average score

performance level

sex

801

group C

some high school

standard

completed

0.76

0.80

0.73

0.76

C

M

200

group C

associate’s degree

standard

completed

0.67

0.84

0.86

0.79

C

F

417

group C

associate’s degree

standard

none

0.74

0.73

0.67

0.71

C

M

629

group C

some high school

standard

completed

0.44

0.51

0.55

0.50

F

F

944

group B

high school

standard

none

0.58

0.68

0.61

0.62

D

F

3. 学生成绩分析

3.1 学生整体成绩分布

代码语言:javascript复制
fig = plt.figure(figsize=(5,10))
sns.set_style('darkgrid')
subjects = ['math percentage', 'reading score percentage', 'writing score percentage', 'average score']
color = ['green', 'blue', 'orange', 'purple']
column = 1
for subject in subjects:
    plt.subplot(len(subjects), 1, column)
    sns.kdeplot(data=data, x=subject, color=color[column - 1])
    column = column   1
plt.tight_layout()
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zWSG3rHk-1639906825714)(output_26_0.png)]

整体来看,各学科成绩与平均成绩都符合正态分布,我们以样本均值加减两个标准差,可以得到约95%的学生成绩分区间:

代码语言:javascript复制
lower_bound = data['average score'].mean() - data['average score'].std()*2
upper_bound = data['average score'].mean()   data['average score'].std()*2
print(f'95%的学生成绩分布下限:{ 
     lower_bound}')
print(f'95%的学生成绩分布上限:{ 
     upper_bound}')
代码语言:javascript复制
95%的学生成绩分布下限:0.3924529214957264
95%的学生成绩分布上限:0.9627870785042743

即约有95%的学生成绩分布在0.39分至0.96分之间。

3.2 不同学科成绩间的关联度以及不同学生人群擅长科目

我们数据集中共拥有三门学科,分别为读写与数学。我们可以分别将其看做**“文科”“理科”**,并分别查看不同学科成绩之间的关联度。

代码语言:javascript复制
data[subjects].corr()

math percentage

reading score percentage

writing score percentage

average score

math percentage

1.000000

0.817580

0.802642

0.918442

reading score percentage

0.817580

1.000000

0.954598

0.970143

writing score percentage

0.802642

0.954598

1.000000

0.965643

average score

0.918442

0.970143

0.965643

1.000000

代码语言:javascript复制
fig = plt.figure()
plt.subplot()
sns.heatmap(data[subjects].corr(), annot=True)
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kWpxCHDy-1639906825715)(output_33_0.png)]

从上面的表与图中,我们可以看出,“文科”学科成绩之间的相关程度,要高于“文科”与“理科”学科成绩之间的相关程度。而且考虑到本数据集中“文科”的科目要多于“理科”的科目,“文科”成绩与平均成绩的相关程度更高。

一般意义而言,社会认为上男生更擅长理科,而女生更擅长文科。我们将使用统计学验证这一看法是否适用于本数据集。

我们引入卡方独立性检验,判断性别与学科掌握程度方面是否是独立不相关的。

  • H0:数学成绩与性别无关系。
  • H1: 数学成绩与性别有关系。
代码语言:javascript复制
# 将数学成绩进行分箱,分箱方式与前述对平均成绩的分箱方式相关
math_grading = pd.cut(data['math percentage'], bins=[0, 0.59, 0.69, 0.79, 0.89, 100], 
                                   labels=['F', 'D', 'C', 'B', 'A'] )
crosstab = pd.crosstab(math_grading, data['sex'])
代码语言:javascript复制
# 引入卡方独立性检验
result = stats.chi2_contingency(crosstab)
print(f'p值为:{ 
     result[1]}')
代码语言:javascript复制
p值为:3.052111718576621e-05

**原假设不成立,即学生的数学成绩与性别并不独立。**在此基础上,我们进一步查看不同性别下,学生在数学科目的表现。

代码语言:javascript复制
data.groupby('sex')[['math percentage']].agg([np.mean, np.median])

math percentage

mean

median

sex

F

0.636332

0.65

M

0.687282

0.69

代码语言:javascript复制
fig, ax  = plt.subplots(1,2, figsize=(10, 5))
sns.boxplot(data=data, y='math percentage', x='sex', palette='summer', ax=ax[0])
sns.histplot(data=data, x='math percentage', hue='sex', fill=True, ax=ax[1], stat='probability')
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qV7fOTYc-1639906825716)(output_41_0.png)]

在数学科目的平均分以及中位数两大统计指标上,我们可以看出,男性在该科目的确占有一定优势。两者的数学成绩分布大致都符合正态分布,但男性在样本方差明显更小,且在高分部分,男性出现的概率更大——男性在数学上的整体表现,要优于女性。

下面我们继续观察男女性在文科科目上的表现,在这里,我们选取writing score percentage标签,做为研究对象。

代码语言:javascript复制
data.groupby('sex')[['writing score percentage']].agg([np.mean, np.median])

writing score percentage

mean

median

sex

F

0.724672

0.74

M

0.633112

0.64

代码语言:javascript复制
fig = px.histogram(
    data, x='writing score percentage', 
    marginal='box', opacity=0.6,
    color='sex',
    histnorm='probability',
    title='男生与女生在文科上的表现',
    template='plotly_white'
)

fig.update_layout(barmode='overlay', width=800)
fig.show()

从所估计的概率密度图上看,女生在writing score percentage的高分领域,女性出现的概率要远高于男生,而在低分领域则正好相反。综合来看男生,男生的确更擅长理科,而女生则相反。

3.3 高分学生人群画像

3.3.1 父母学历

下面我们分析高分(均分高于90分)考生的画像,首先我们探究高分与父母受教育程度间的关系。

代码语言:javascript复制
honor_students = data.loc[data['average score']>=0.9] # 选取均分高于0.9的学生,组成子数据集honor_students
代码语言:javascript复制
honor_count = honor_students['parental level of education'].value_counts().sort_index()
total_count = data['parental level of education'].value_counts().sort_index()

fig = make_subplots(rows=1, cols=2, specs=[[dict(type='domain'),{ 
   'type':'domain'}]])

fig.add_pie(
    values=total_count.values, hole=0.4, labels=total_count.index, 
            row=1, col=1, name='整体学生父母受教育程度'
)
fig.add_pie(
    values=honor_count.values, hole=0.4, labels=honor_count.index, 
            row=1, col=2, name='高分学生父母受教育程度'
)

fig.update_layout(
    title_text="学生父母受教育程度",
    annotations=[dict(text='整体父母', x=0.15, y=0.5, font_size=20, showarrow=False),
                 dict(text='高分父母', x=0.85, y=0.5, font_size=20, showarrow=False)],
    width=900
)
fig.show()

从上图所示,我们发现,高分考生父母的教育程度,要高于整体考生父母的教育程度,其中高分考生父母拥有副学士、学士、硕士的比例,相较于整体考生,分别从22.9%, 11.8%, 5.9%上升至31.5%, 24.1%, 11.1%。

整体来看,高分学生的父母,约有90%都曾接受过大学教育。

不仅仅是高分学生父母的所受教育程序较高,实际上,在本数据集中,所有学生的平均分,皆与父母的教育程度正相关。下表给出了不同教育程度的父母,以及对应考生群体平均分。其中,其中学历为硕士与高中的父母,子女的平均分分别为73分及63分。

代码语言:javascript复制
data.groupby('parental level of education')['average score'].mean().sort_values()
代码语言:javascript复制
parental level of education
high school           0.631224
some high school      0.650726
some college          0.684469
associate's degree    0.695586
bachelor's degree     0.719492
master's degree       0.735763
Name: average score, dtype: float64
代码语言:javascript复制
parents = data['parental level of education'].unique()
sample_data = []
for parent in parents:
    member = data[data['parental level of education'] == parent].sample(n=30, random_state=1)
    sample_data.append(member)
parent_data = pd.concat(sample_data, axis=0)

fig = px.scatter(
    parent_data, x='math percentage', y='writing score percentage',
    color='parental level of education',
    size='average score',
    title='父母教育程度对学生成绩影响',
    template='plotly_dark'
)
fig.show()

上述气泡图抽取各个教育水平的父母各30名,并观察其子女成绩表现。不难看出,学生的成绩表现与父母受教育程度成正相关关系,即父母受教育程度越高,子女的学业表现越好。

为了进一步在统计学上证明这一点,我们引入卡方独立性检验:

  • H0: 学生成绩表现与父母受教育程序无关
  • H1: 学生成绩表现与父母受程序程度相关
代码语言:javascript复制
crosstab = pd.crosstab(data['parental level of education'], data['performance level'])
代码语言:javascript复制
result = stats.chi2_contingency(crosstab)
if result[1] < 0.05:
    print(f'p值为{ 
     result[1]}, 原假设不成立!')
else:
    print(f'p值为{ 
     result[1]}, 原假设成立!')
代码语言:javascript复制
p值为0.00014895457933147155, 原假设不成立!

结论:对于高分段的学生,其父母所受的教育程度要更高。

3.3.2 学生性别

代码语言:javascript复制
honor_students.groupby('sex')['sex'].count()
代码语言:javascript复制
sex
F    40
M    14
Name: sex, dtype: int64

从上图表格中,我们发现高分学生当中,女性的数量要明显多于男性数量。但考虑到在三门科目当中,写作与阅读都偏向于女生所擅长的文科类科目,这对于擅长数学的男生而言,显然是不利的,我们考虑选取一门文科与一门理科,取其均值,查看在这一情况下,高分学生在男女中的分布。

代码语言:javascript复制
condition = ((data['math percentage'] data['writing score percentage'])/2 >= 0.9)
data[condition].groupby('sex')['sex'].count()
代码语言:javascript复制
sex
F    35
M    18
Name: sex, dtype: int64

此时,女生仍然相较于男生,仍然拥有更大的优势!

代码语言:javascript复制
honor_index = honor_students.groupby('sex')['sex'].count().index
honor_value = honor_students.groupby('sex')['sex'].count().values
math_writing_index = data[condition].groupby('sex')['sex'].count().index
math_writing_value = data[condition].groupby('sex')['sex'].count().values
fig = go.Figure(data=[
    go.Bar(x=honor_index, y=honor_value, name='三科均分高于90分', text=honor_value,),
    go.Bar(x=math_writing_index, y=math_writing_value, name='数学写作均分高于90分', text=math_writing_value)
])
fig.update_layout(width=500, height=400)
fig.show()

从我们得到的结果来看,无论是哪种情况,女生高分情况都要远胜于男生,基于此,我们做出一个假设:尽管女生在数学方面整体不如男性,但在高分段,男女生在数学的表现基本一致。

代码语言:javascript复制
data[data['math percentage']>0.9].groupby('sex')['math percentage'].mean()
代码语言:javascript复制
sex
F    0.952857
M    0.948621
Name: math percentage, dtype: float64

上表所示为男生与女生在高分段的数学平均成绩。

代码语言:javascript复制
data[data['math percentage']>0.9].groupby('sex')['math percentage'].std()
代码语言:javascript复制
sex
F    0.031803
M    0.032373
Name: math percentage, dtype: float64

上表所示为男生与女生在高分段的数学样本方差。

从数学高分段的均值及方差来看,两者都十分接近。为了进一步验证我们的观点,我们引入t 检验,判断两者的均值是否相同。

  • H0: 在高分段,男生与女生在数学方面的均值一致
  • H1: 在高分段,男生与女生在数学方面的均值不一致
代码语言:javascript复制
honored_male_math = data.loc[(data['math percentage'] >= 0.9) & (data['sex'] == 'M'),'math percentage']
honored_female_math = data.loc[(data['math percentage'] >= 0.9) & (data['sex'] == 'F'),'math percentage']
result = ttest_ind(honored_male_math.values, honored_female_math.values, equal_var=True)
if result[1]>0.95:
    print(f'两者的均值一致,P值为{ 
     result[1]}')
else:
    print(f'两者的均值不一致,P值为{ 
     result[1]}')
代码语言:javascript复制
两者的均值一致,P值为1.0

从P值所反馈结果来看,女生尽管整体在数学方面不如男生,但在高分段,女生与男生的表,并无明显区别。

结论:高分段,女生比男生要更占优势,而男生的优势科目在高分段,优势并不明显。

结束语

看完这篇,还有更多知识点分享给你哦,自己慢慢找哈,就在下面链接。

推荐关注的专栏

0 人点赞