1. 程式人生 > >Python資料分析--Iris資料集實戰

Python資料分析--Iris資料集實戰

本次主要圍繞Iris資料集進行一個簡單的資料分析, 另外在資料的視覺化部分進行了重點介紹.

環境

win8, python3.7, jupyter notebook

正文

1. 專案背景

鳶尾屬(拉丁學名:Iris L.), 單子葉植物綱, 鳶尾科多年生草本植物, 開的花大而美麗, 觀賞價值很高. 鳶尾屬約300種, Iris資料集中包含了其中的三種: 山鳶尾(Setosa),  雜色鳶尾(Versicolour), 維吉尼亞鳶尾(Virginica), 每種50個數據, 共含150個數據. 在每個資料包含四個屬性: 花萼長度,花萼寬度,花瓣長度,花瓣寬度, 可通過這四個屬性預測鳶尾花卉屬於 (山鳶尾, 雜色鳶尾, 維吉尼亞鳶尾) 哪一類.

2. 資料概覽

資料來源: https://www.kaggle.com/benhamner/python-data-visualizations/data

2.1 讀取資料

資料為csv檔案, 讀取資料:

import pandas as pd
df_Iris = pd.read_csv('Iris.csv')

2.2 檢視前/後5行資料

#前5行
df_Iris.head()
#後5行
df_Iris.tail()

 

通過這10行資料也就大致確定資料維度150行X6列以及各特徵內的基本資訊:

Id: 鳶尾花編號

SepaLengthCm: 花萼長度, 單位cm

SepalWidthCm: 花萼寬度, 單位cm

PetalLengthCm: 花瓣長度, 單位cm

PetalWidthCm; 花瓣寬度, 單位cm

Species: 鳶尾花種類.

2.3 檢視資料整體資訊

#檢視資料整體資訊
df_Iris.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 150 entries, 0 to 149
Data columns (total 6 columns):
Id               150 non-null int64
SepalLengthCm    150 non-null float64
SepalWidthCm     150 non-null float64
PetalLengthCm    150 non-null float64
PetalWidthCm     150 non-null float64
Species          150 non-null object
dtypes: float64(4), int64(1), object(1)
memory usage: 7.1+ KB

得出資訊: 150行, 6列,4個64位浮點數, 1個64位整型, 1個python物件, 資料中無缺失值.

2.4 描述性統計

df_Iris.describe()

花萼長度最小值4.30, 最大值7.90, 均值5.84, 中位數5.80, 右偏

花萼寬度最小值2.00, 最大值4.40, 均值3.05, 中位數3.00, 右偏

花瓣長度最小值1.00, 最大值6.90, 均值3.76, 中位數4.35, 左偏

花瓣寬度最小值0.10, 最大值2.50, 均值1.20, 中位數1.30, 左偏

按中位數來度量: 花萼長度 > 花瓣長度 > 花萼寬度 > 花瓣寬度

#注意這裡是大寫的字母O, 不是數字0.
df_Iris.describe(include =['O']).T

總數150, 3個種類, 最大頻數為50, 也就是每種都為50個. 注意top裡的指的不是Iris-versicolor最多, 是在頻數相同的基礎上按照字串長度進行排名.

可以通過這樣對每種進行計數:

df_Iris.Species.value_counts()
Iris-versicolor    50
Iris-virginica     50
Iris-setosa        50
Name: Species, dtype: int64

通過以上, 大致瞭解資料的基本資訊, 現想把Species特徵中的'Iris-'字元去掉, 進入特徵工程環節.

3. 特徵工程

3.1 資料清洗

去掉Species特徵中的'Iris-'字元.

#第一種方法: 替換
# df_Iris['Species']= df_Iris.Species.str.replace('Iris-','')
#第二種方法: 分割
df_Iris['Species']= df_Iris.Species.apply(lambda x: x.split('-')[1])
df_Iris.Species.unique()
array(['setosa', 'versicolor', 'virginica'], dtype=object)

3.2 資料視覺化

Seaborn是一個python的視覺化庫, 它基於matplotlib, 這使得它能與pandas緊密結合, 並且提供了高階繪圖介面, 能更方便地完成探索性分析.

我想在這個專案上對seaborn多加練習, 因此, 會對這部分內容著重介紹.

3.2.1 relplot

import seaborn as sns
import matplotlib.pyplot as plt
#sns初始化
sns.set()
#設定散點圖x軸與y軸以及data引數
sns.relplot(x='SepalLengthCm', y='SepalWidthCm', data = df_Iris)
plt.title('SepalLengthCm and SepalWidthCm data analysize')

花萼的長度和寬度在散點圖上分了兩個簇, 而且兩者各自都有一定的關係. 鳶尾花又分為三個品種, 不妨看看關於這三個品種的分佈.

#hue表示按照Species對資料進行分類, 而style表示每個類別的標籤系列格式不一致.
sns.relplot(x='SepalLengthCm', y='SepalWidthCm', hue='Species', style='Species', data=df_Iris )
plt.title('SepalLengthCm and SepalWidthCm data by Species')

 

 可以看到setosa這種花的花萼長度和寬度有明顯的線性關係, 當然其他兩種也存在一定的關係, 花萼的屬性看完了, 看下花瓣的:

#花瓣長度與寬度分佈散點圖
sns.relplot(x='PetalLengthCm', y='PetalWidthCm', hue='Species', style='Species', data=df_Iris )
plt.title('PetalLengthCm and PetalWidthCm data by Species')

花的品種和花瓣的長度, 寬度之間存在一定的關係.

 另外, 還可以對比花萼與花瓣的長度, 花萼與花瓣的寬度之間的關係.

#花萼與花瓣長度分佈散點圖
# sns.relplot(x='SepalLengthCm', y='PetalLengthCm', hue='Species', style='Species', data=df_Iris )
#plt.title('SepalLengthCm and PetalLengthCm data by Species')
#花萼與花瓣寬度分佈散點圖
sns.relplot(x='SepalWidthCm', y='PetalWidthCm', hue='Species', style='Species', data=df_Iris )
plt.title('SepalWidthCm and PetalWidthCm data by Species')

花萼的長度與花瓣的寬度, 花萼的寬度與花瓣的長度之間應當也存在某種關係:

#花萼的長度與花瓣的寬度分佈散點圖
# sns.relplot(x='SepalLengthCm', y='PetalWidthCm', hue='Species', style='Species', data=df_Iris )
#plt.title('SepalLengthCm and PetalWidthCm data by Species')
#花萼的寬度與花瓣的長度分佈散點圖
sns.relplot(x='SepalWidthCm', y='PetalLengthCm', hue='Species', style='Species', data=df_Iris )
plt.title('SepalWidthCm and PetalLengthCm data by Species'

Id編號與花萼長度, 花萼寬度, 花瓣長度, 花瓣寬度之間有沒有關係呢:

#花萼長度與Id之間關係圖
sns.relplot(x="Id", y="SepalLengthCm",hue="Species", style="Species",kind="line", data=df_Iris)
plt.title('SepalLengthCm and Id data analysize')
#花萼寬度與Id之間關係圖
sns.relplot(x="Id", y="SepalWidthCm",hue="Species", style="Species",kind="line", data=df_Iris)
plt.title('SepalWidthCm and Id data analysize')
#花瓣長度與Id之間關係圖
sns.relplot(x="Id", y="PetalLengthCm",hue="Species", style="Species",kind="line", data=df_Iris)
plt.title('PetalLengthCm and Id data analysize')
#花瓣寬度與Id之間關係圖
sns.relplot(x="Id", y="PetalWidthCm",hue="Species", style="Species",kind="line", data=df_Iris)
plt.title('PetalWidthCm and Id data analysize')

可以得到資訊: Id中前50個為setosa, 51到100為versicolour, 101到150為Virginica, 以及每個種類對應屬性值的範圍, 每個種類中的屬性與其對應的Id沒有明確的關係.

3.2.2 jointplot

sns.jointplot(x='SepalLengthCm', y='SepalWidthCm', data=df_Iris)sns.jointplot(x='PetalLengthCm', y='PetalWidthCm', data=df_Iris)

散點圖和直方圖同時顯示, 可以直觀地看出哪組頻數最大, 哪組頻數最小.

對於頻數的值, 在散點圖上數點的話, 顯然效率太低, 還易出錯, 下面引出distplot

3.2.3 distplot

#繪製直方圖, 其中kde=False表示不顯示核函式估計圖,這裡為了更方便去檢視頻數而設定它為False.
# sns.distplot(df_Iris.SepalLengthCm,bins=8, hist=True, kde=False)
# sns.distplot(df_Iris.SepalWidthCm,bins=13, hist=True, kde=False)
# sns.distplot(df_Iris.PetalLengthCm, bins=5, hist=True, kde=False)
sns.distplot(df_Iris.PetalWidthCm, bins=5, hist=True, kde=False)

我這裡的分組是按照上面jointplot裡的組數進行設定, 現在就很直觀地看到各組對應的頻數

前面我們已經通過describe()方法計算出四個屬性所對應的四分位數, 最大值以及最小值等統計量. 這些均是以表格的形式展示, 我們下面就介紹怎麼以圖樣的形式展示四分位數.

3.2.4 boxplot

boxplot所繪製的就是箱線圖, 它能顯示出一組資料的最大值, 最小值, 四分位數以及異常點.

對於異常點的定義: 區間[Q1-1.5IQR, Q3+1.5IQR]之外的點, 其中Q1下四分位數(25%), Q3上四分位數(75%), IQR=Q3-Q1

在seaborn.boxplot中, 箱線圖的畫法分兩種情況

如果資料中無異常點, 那麼箱線圖的下邊緣就是資料中的最小值, 上邊緣就是資料中的最大值, 即下圖的實線部分(虛線以及紅點部分不會顯示)

如果資料中有異常點, 那麼箱線圖的下邊緣Limit1指的是區間[Q1-1.5IQR, Q3+1.5IQR]內的最小值, 上邊緣Limit2指的是區間內的最大值, 即下圖的實線部分(虛線以及紅點部分不會顯示)

#比如資料中的SepalLengthCm屬性
sns.boxplot(x='SepalLengthCm', data=df_Iris)
#比如資料中的SepalWidthCm屬性
sns.boxplot(x='SepalWidthCm', data=df_Iris)

 為了更直觀地對比四個屬性之間的關係, 我將四個屬性對應的數值合併在新的DataFrame Iris中.

#對於每個屬性的data建立一個新的DataFrame
Iris1 = pd.DataFrame({"Id": np.arange(1,151), 'Attribute': 'SepalLengthCm', 'Data':df_Iris.SepalLengthCm, 'Species':df_Iris.Species})
Iris2 = pd.DataFrame({"Id": np.arange(151,301), 'Attribute': 'SepalWidthCm', 'Data':df_Iris.SepalWidthCm, 'Species':df_Iris.Species})
Iris3 = pd.DataFrame({"Id": np.arange(301,451), 'Attribute': 'PetalLengthCm', 'Data':df_Iris.PetalLengthCm, 'Species':df_Iris.Species})
Iris4 = pd.DataFrame({"Id": np.arange(451,601), 'Attribute': 'PetalWidthCm', 'Data':df_Iris.PetalWidthCm, 'Species':df_Iris.Species})
#將四個DataFrame合併為一個.
Iris = pd.concat([Iris1, Iris2, Iris3, Iris4])
#繪製箱線圖
sns.boxplot(x='Attribute', y='Data', data=Iris)

對下圖做一下簡單分析: 就中位數來說, SepalLenthCm > PetalLengthCm > SepalWidthCm > PetalWidthCm; 就波動程度來說, PetalLengthCm > PetalWidthCm > SepalLengthCm > SepalWidthCm; 就異常值來說, 只有SepalWidthCm中存在異常值. 

將鳶尾花的三種種類再加入到箱線圖中:

sns.boxplot(x='Attribute', y='Data',hue='Species', data=Iris)

這樣就很容易能夠對比三個種類在四個屬性中的表現狀況:

除了SepalWidthCm屬性外, 中位數在其他屬性的三種花中均表現為: Virginica > versicolour >  setosa

除了setosa種類外, 中位數在其他種類的四個屬性中均表現為: SepalLengthCm > PetalLengthCm > SepalWidthCm > PetalWidthCm

下面將介紹一種更高階的四分位數展示方式: violinplot

3.2.5 violinplot

violinplot繪製的是琴圖, 是箱線圖與核密度圖的結合體, 既可以展示四分位數, 又可以展示任意位置的密度.

sns.violinplot(x='Attribute', y='Data', hue='Species', data=Iris )

上圖中具體細節顯示不是很明顯, 對於PetalWidthCm都有些模糊了, 下面將拆分成四個小圖, 另外為了和箱線圖對比, 將箱線圖也繪製出來.

#花萼長度
# sns.boxplot(x='Species', y='SepalLengthCm', data=df_Iris)
# sns.violinplot(x='Species', y='SepalLengthCm', data=df_Iris)
# plt.title('SepalLengthCm data by Species')
#花萼寬度
# sns.boxplot(x='Species', y='SepalWidthCm', data=df_Iris)
# sns.violinplot(x='Species', y='SepalWidthCm', data=df_Iris)
# plt.title('SepalWidthCm data by Species')
#花瓣長度
# sns.boxplot(x='Species', y='PetalLengthCm', data=df_Iris)
# sns.violinplot(x='Species', y='PetalLengthCm', data=df_Iris)
# plt.title('PetalLengthCm data by Species')
#花瓣寬度
sns.boxplot(x='Species', y='PetalWidthCm', data=df_Iris)
sns.violinplot(x='Species', y='PetalWidthCm', data=df_Iris)
plt.title('PetalWidthCm data by Species')

可以明顯看出, 琴圖中的白點就是中位數, 黑色矩形的上短邊則是上四分位數Q3, 黑色下短邊則是下四分位數Q1;  而貫穿矩形的黑線的上端點則代表最小非異常值, 下端點則代表最大非異常值; 黑色矩形外部形狀則表示核概率密度估計.

最後介紹一種圖形, 它能直接顯示各個特徵之間的不同關係

3.2.6 pairplot

#刪除Id特徵, 繪製分佈圖
sns.pairplot(df_Iris.drop('Id', axis=1), hue='Species')
#儲存圖片, 由於在jupyter notebook中太大, 不能一次截圖
plt.savefig('pairplot.png')
plt.show()

綜上, 花萼的長度, 花萼的寬度, 花瓣的長度, 花瓣的寬度與花的種類之間均存在一定的相關性, 且對於這三個種類的分佈, satosa在任何一種分佈中較其他兩者集中; 就同一種花的平均水平來看, 其花萼的長度最長, 花瓣的寬度最短; 就同一屬性的平均水平來看, 三種花在除了花萼的寬度外的屬性中平均水平均表現為: Virginica > versicolour >  setosa.

4. 構建模型

採用決策樹分類演算法.

from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
X = df_Iris[['SepalLengthCm','SepalWidthCm','PetalLengthCm','PetalWidthCm']]
y = df_Iris['Species']
#將資料按照8:2的比例隨機分為訓練集, 測試集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
#初始化決策樹模型
dt = DecisionTreeClassifier()
#訓練模型
dt.fit(X_train, y_train)
#用測試集評估模型的好壞
dt.score(X_test, y_test)
0.9666666666666667

在測試集上準確率達到97%,也還不錯, 此次沒有對決策樹模型設定引數, 如果引數設定好了, 想必準確率會更高.

參考: