可视化帕默群岛企鹅数据¶

分析目标¶

对帕默群岛上企鹅样本的相关变量进行可视化,从而探索和分析种类、性别、所在岛屿等因素,与企鹅的身体属性,包括体重、嘴峰长度和深度、鳍的长度之间的关系。

简介¶

原始数据Penguins.csv包括334个收集自南极洲帕尔默群岛的3个岛屿上的企鹅样本,以及企鹅相关属性数据,包括种类名、所在岛、嘴峰长度、嘴峰深度、鳍长度、体重、性别。

Penguins.csv每列的含义如下:

  • species:企鹅的种类
  • island:企鹅所在岛
  • culmen_length_mm:企鹅嘴峰的长度(单位为毫米)
  • culmen_depth_mm:企鹅嘴峰的深度(单位为毫米)
  • flipper_length_mm:企鹅鳍的长度(单位为毫米)
  • body_mass_g:企鹅体重(单位为克)
  • sex:企鹅性别

读取数据¶

导入数据分析所需要的库,并通过Pandas的read_csv函数,将原始数据文件Penguins.csv里的数据内容,解析为DataFrame并赋值给变量original_data。

In [1]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
In [2]:
original_data = pd.read_csv("Penguins.csv")
original_data.head()
Out[2]:
species island culmen_length_mm culmen_depth_mm flipper_length_mm body_mass_g sex
0 Adelie Torgersen 39.1 18.7 181.0 3750.0 MALE
1 Adelie Torgersen 39.5 17.4 186.0 3800.0 FEMALE
2 Adelie Torgersen 40.3 18.0 195.0 3250.0 FEMALE
3 Adelie Torgersen NaN NaN NaN NaN NaN
4 Adelie Torgersen 36.7 19.3 193.0 3450.0 FEMALE

评估和清理数据¶

对在上一部分建立的original_dataDataFrame所包含的数据进行评估和清理。

从两个方面进行:结构和内容,即整齐度和干净度。

数据的结构性问题指不符合“每个变量为一列,每个观察值为一行,每种类型的观察单位为一个表格”这三个标准;数据的内容性问题包括存在丢失数据、重复数据、无效数据等。

为了区分开经过清理的数据和原始的数据,我们创建新的变量cleaned_data,让它为original_data复制出的副本。我们之后的清理步骤都将被运用在cleaned_data上。

In [3]:
cleaned_data = original_data.copy()

数据整齐度¶

In [4]:
cleaned_data.head(10)
Out[4]:
species island culmen_length_mm culmen_depth_mm flipper_length_mm body_mass_g sex
0 Adelie Torgersen 39.1 18.7 181.0 3750.0 MALE
1 Adelie Torgersen 39.5 17.4 186.0 3800.0 FEMALE
2 Adelie Torgersen 40.3 18.0 195.0 3250.0 FEMALE
3 Adelie Torgersen NaN NaN NaN NaN NaN
4 Adelie Torgersen 36.7 19.3 193.0 3450.0 FEMALE
5 Adelie Torgersen 39.3 20.6 190.0 3650.0 MALE
6 Adelie Torgersen 38.9 17.8 181.0 3625.0 FEMALE
7 Adelie Torgersen 39.2 19.6 195.0 4675.0 MALE
8 Adelie Torgersen 34.1 18.1 193.0 3475.0 NaN
9 Adelie Torgersen 42.0 20.2 190.0 4250.0 NaN

从头部的10行数据来看,数据符合“每个变量为一列,每个观察值为一行,每种类型的观察单位为一个表格”,因此不存在结构性问题。

数据干净度¶

接下来通过info,对数据内容进行大致了解。

In [5]:
cleaned_data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 344 entries, 0 to 343
Data columns (total 7 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   species            344 non-null    object 
 1   island             344 non-null    object 
 2   culmen_length_mm   342 non-null    float64
 3   culmen_depth_mm    342 non-null    float64
 4   flipper_length_mm  342 non-null    float64
 5   body_mass_g        342 non-null    float64
 6   sex                334 non-null    object 
dtypes: float64(4), object(3)
memory usage: 18.9+ KB

从输出结果来看,cleaned_data数据共有344条观察值,culmen_length_mm、culmen_depth_mm、flipper_length_mm、body_mass_g变量存在缺失值,将在后续进行评估和清理。

数据类型方面,我们已知species(企鹅种类)sex(企鹅性别)、island(企鹅所在岛)都是分类数据,因此可以把数据类型都转换为Category。

In [6]:
cleaned_data['species'] = cleaned_data['species'].astype("category")
cleaned_data['sex'] = cleaned_data['sex'].astype("category")
cleaned_data['island'] = cleaned_data['island'].astype("category")
In [7]:
cleaned_data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 344 entries, 0 to 343
Data columns (total 7 columns):
 #   Column             Non-Null Count  Dtype   
---  ------             --------------  -----   
 0   species            344 non-null    category
 1   island             344 non-null    category
 2   culmen_length_mm   342 non-null    float64 
 3   culmen_depth_mm    342 non-null    float64 
 4   flipper_length_mm  342 non-null    float64 
 5   body_mass_g        342 non-null    float64 
 6   sex                334 non-null    category
dtypes: category(3), float64(4)
memory usage: 12.3 KB

处理缺失数据¶

从info方法的输出来看,在cleaned_data中,culmen_length_mm、culmen_depth_mm、flipper_length_mm、body_mass_g、sex变量存在缺失值。

先提取出缺失这些变量的观察值进行查看。

In [8]:
cleaned_data.query("culmen_length_mm.isna()")
Out[8]:
species island culmen_length_mm culmen_depth_mm flipper_length_mm body_mass_g sex
3 Adelie Torgersen NaN NaN NaN NaN NaN
339 Gentoo Biscoe NaN NaN NaN NaN NaN
In [9]:
cleaned_data.query("culmen_depth_mm.isna()")
Out[9]:
species island culmen_length_mm culmen_depth_mm flipper_length_mm body_mass_g sex
3 Adelie Torgersen NaN NaN NaN NaN NaN
339 Gentoo Biscoe NaN NaN NaN NaN NaN
In [10]:
cleaned_data.query("flipper_length_mm.isna()")
Out[10]:
species island culmen_length_mm culmen_depth_mm flipper_length_mm body_mass_g sex
3 Adelie Torgersen NaN NaN NaN NaN NaN
339 Gentoo Biscoe NaN NaN NaN NaN NaN
In [11]:
cleaned_data.query("body_mass_g.isna()")
Out[11]:
species island culmen_length_mm culmen_depth_mm flipper_length_mm body_mass_g sex
3 Adelie Torgersen NaN NaN NaN NaN NaN
339 Gentoo Biscoe NaN NaN NaN NaN NaN

以上,可以看到索引为3和339的观察值,除了种类和所属岛屿外所有变量都为空,无法为探索企鹅身体属性相关因素提供价值,因此可以把这两行直接删除。

In [12]:
cleaned_data.drop(3, inplace=True)
cleaned_data.drop(339, inplace=True)
In [13]:
cleaned_data.query("sex.isna()")
Out[13]:
species island culmen_length_mm culmen_depth_mm flipper_length_mm body_mass_g sex
8 Adelie Torgersen 34.1 18.1 193.0 3475.0 NaN
9 Adelie Torgersen 42.0 20.2 190.0 4250.0 NaN
10 Adelie Torgersen 37.8 17.1 186.0 3300.0 NaN
11 Adelie Torgersen 37.8 17.3 180.0 3700.0 NaN
47 Adelie Dream 37.5 18.9 179.0 2975.0 NaN
246 Gentoo Biscoe 44.5 14.3 216.0 4100.0 NaN
286 Gentoo Biscoe 46.2 14.4 214.0 4650.0 NaN
324 Gentoo Biscoe 47.3 13.8 216.0 4725.0 NaN

缺失性别变量的观察值具备其它数据,仍然可以为分析提供价值。由于Pandas以及Matplotlib、Seaborn会自动忽略缺失值,可以保留这些行。

处理重复数据¶

根据数据变量的含义以及内容来看,允许变量重复,我们不需要对此数据检查是否存在重复值。

处理不一致数据¶

不一致数据可能存在于所有分类变量中,我们要查看是否存在不同值实际指代同一目标的情况。

In [14]:
cleaned_data["species"].value_counts()
Out[14]:
species
Adelie       151
Gentoo       123
Chinstrap     68
Name: count, dtype: int64
In [15]:
cleaned_data["island"].value_counts()
Out[15]:
island
Biscoe       167
Dream        124
Torgersen     51
Name: count, dtype: int64
In [16]:
cleaned_data["sex"].value_counts()
Out[16]:
sex
MALE      168
FEMALE    165
.           1
Name: count, dtype: int64

从以上输出来看,species和island列里并不存在不一致数据,但sex列里存在一个英文句号值,并不代表任何有效性别,我们应当把该值替换为NaN空值。

In [17]:
cleaned_data['sex'] = cleaned_data['sex'].cat.add_categories(['nan']).replace(".", 'nan')
#cleaned_data['sex']是分类(Categorical)类型,而 replace()方法的当前行为会隐式修改分类的类别(Categories)
C:\Users\25778\AppData\Local\Temp\ipykernel_2028\1051071439.py:1: FutureWarning: The behavior of Series.replace (and DataFrame.replace) with CategoricalDtype is deprecated. In a future version, replace will only be used for cases that preserve the categories. To change the categories, use ser.cat.rename_categories instead.
  cleaned_data['sex'] = cleaned_data['sex'].cat.add_categories(['nan']).replace(".", 'nan')

查看英文句号值是否还存在。

In [18]:
cleaned_data["sex"].value_counts()
Out[18]:
sex
MALE      168
FEMALE    165
nan         1
Name: count, dtype: int64

英文句号值已被替换为空值,因此sex列里不存在不一致数据。

处理无效或错误数据¶

可以通过DataFrame的describe方法,对数值统计信息进行快速了解。

In [19]:
cleaned_data.describe()
Out[19]:
culmen_length_mm culmen_depth_mm flipper_length_mm body_mass_g
count 342.000000 342.000000 342.000000 342.000000
mean 43.921930 17.151170 200.915205 4201.754386
std 5.459584 1.974793 14.061714 801.954536
min 32.100000 13.100000 172.000000 2700.000000
25% 39.225000 15.600000 190.000000 3550.000000
50% 44.450000 17.300000 197.000000 4050.000000
75% 48.500000 18.700000 213.000000 4750.000000
max 59.600000 21.500000 231.000000 6300.000000

从以上统计信息来看,cleaned_house_price里不存在脱离现实意义的数值。

数据探索¶

通过数据可视化,进行探索和分析,从图表中获得数据的相关洞察

In [20]:
# 设置图表色盘为粉彩pastel
sns.set_palette('pastel')
In [21]:
cleaned_data
Out[21]:
species island culmen_length_mm culmen_depth_mm flipper_length_mm body_mass_g sex
0 Adelie Torgersen 39.1 18.7 181.0 3750.0 MALE
1 Adelie Torgersen 39.5 17.4 186.0 3800.0 FEMALE
2 Adelie Torgersen 40.3 18.0 195.0 3250.0 FEMALE
4 Adelie Torgersen 36.7 19.3 193.0 3450.0 FEMALE
5 Adelie Torgersen 39.3 20.6 190.0 3650.0 MALE
... ... ... ... ... ... ... ...
338 Gentoo Biscoe 47.2 13.7 214.0 4925.0 FEMALE
340 Gentoo Biscoe 46.8 14.3 215.0 4850.0 FEMALE
341 Gentoo Biscoe 50.4 15.7 222.0 5750.0 MALE
342 Gentoo Biscoe 45.2 14.8 212.0 5200.0 FEMALE
343 Gentoo Biscoe 49.9 16.1 213.0 5400.0 MALE

342 rows × 7 columns

企鹅种类比例¶

In [22]:
cleaned_data.groupby('species',observed=False)['island'].count()
#未来版本中observed的默认值从False改为True,因此要设置observed=False
Out[22]:
species
Adelie       151
Chinstrap     68
Gentoo       123
Name: island, dtype: int64
In [23]:
species_count=cleaned_data['species'].value_counts()
species_count
Out[23]:
species
Adelie       151
Gentoo       123
Chinstrap     68
Name: count, dtype: int64
In [24]:
plt.pie(species_count,autopct='%.1f%%',labels=species_count.index)
plt.show()
No description has been provided for this image

样本中Adelie最多,Chinstrap最少

企鹅所属岛屿比例¶

In [25]:
island_count=cleaned_data['island'].value_counts()
plt.pie(species_count,autopct='%.1f%%',labels=island_count.index)
plt.show()
No description has been provided for this image

一半左右的企鹅都来自于Biscoe岛屿

企鹅性别比例¶

In [26]:
sex_count=cleaned_data['sex'].value_counts()
plt.pie(sex_count,autopct='%.1f%%',labels=sex_count.index)
plt.show()
No description has been provided for this image

性别占比持平,符合随机抽样

不同岛屿上的企鹅种类数量¶

In [27]:
sns.countplot(cleaned_data,x='island',hue='species')
plt.show()
No description has been provided for this image

不同岛屿上企鹅性别数量¶

In [28]:
sns.countplot(cleaned_data,x='island',hue='sex')
plt.show()
No description has been provided for this image

基本持平,符合随机抽样预期

In [29]:
sns.pairplot(cleaned_data)
plt.show()
No description has been provided for this image

在 seaborn.pairplot 的对角线直方图中:

  1. x 轴和 y 轴的含义
  • x 轴:表示该变量的 取值区间(分箱/bins)。

  • y 轴:表示落入每个区间的观测值数量(频数)。

变量名相同:因为这是单变量分布分析(自己 vs 自己),所以 x 轴和 y 轴标注相同,但实际含义不同(x=值,y=频数)。

  1. 直方图的“高度”代表什么?
  • 直接反映该区间内数据的数量,例如,若某个柱子的高度为 50,表示有 50 个数据点落在该区间。

  • 如果设置 density=True(或 kde=True),y 轴会转换为 概率密度(总面积=1),此时高度代表相对频率而非绝对数量。

在直方图中看出四个变量分布均不是正态分布,可能是样本数不够大或是包含多组存在差异的样本数据。

在散点图中看出明显的多个集群,可能与企鹅种类、性别等有关

根据种类查看数值之间的相关关系¶

In [30]:
sns.pairplot(cleaned_data,hue='species')
plt.show()
No description has been provided for this image

同一种类的企鹅数据在散点图是基本都聚在一起,说明同一种类的企鹅在这些变量上有相似性

In [31]:
sns.pairplot(cleaned_data,hue='species',kind='reg')
plt.show()
No description has been provided for this image
In [32]:
sns.pairplot(cleaned_data,hue='species',kind='reg',plot_kws={'scatter_kws':{'alpha':0.3}})
plt.show()
No description has been provided for this image

散点图结合线性回归线来看,同种企鹅的属性数据之间均成线性正比,可由此大致根据企鹅种类判断企鹅属性,或根据企鹅属性判断企鹅种类

对角线上的图为密度图,密度图的纵坐标不是直接的概率值,而是概率密度,单位是 “概率/单位X”(例如:概率/厘米、概率/美元等)

概率密度可以大于1(因为它是单位区间内的概率强度)

概率必须通过积分计算一定范围内的面积得到

峰值对应数据分布的最高概率密度区域,即数据最集中的位置,例如:身高数据的峰值在1.75m,说明大多数人的身高接近1.75m

总面积 = 1(所有可能事件的概率总和为1)

根据性别查看数值之间的相关关系¶

In [33]:
sns.pairplot(cleaned_data,hue='sex')
plt.show()
No description has been provided for this image
In [ ]: