Kaggle泰坦尼克數據科學解決方案

原文地址以下:python

https://www.kaggle.com/startupsci/titanic-data-science-solutions正則表達式

----------------------------------------------------------------算法

泰坦尼克數據科學解決方案:數組

 

1. 工做流程步驟:app

在 Data Science Solutions book 這本書裏,描述了在解決一個競賽問題時所須要作的具體工做流程:less

 

  1. 問題的定義
  2. 獲取訓練數據以及測試數據
  3. 加工、準備以及清洗數據
  4. 分析、識別數據的模式,並對數據作可視化
  5. 建模、預測,並解決問題
  6. 對結果作可視化,生成報告,而且展現問題的解決步驟和最終的解決方案
  7. 提交結果

以上的工做流程僅僅描述了通常的問題解決步驟,然而依然會有一些特殊的案例並不嚴格遵循以上流程:dom

  1. 咱們可能會結合多個步驟。例如經過將數據可視化後直接進行分析
  2. 將某個步驟提早執行。例如在作數據加工前即開始對數據進行分析
  3. 在工做流程中屢次進行某個步驟。例如在整個流程中會屢次對數據進行可視化
  4. 徹底棄用一個步驟。例如在非競賽場合可能並不須要作提交結果的步驟

 

2. 問題的定義:機器學習

通常的競賽網站(如 Kaggle)會在提供訓練數據以及測試數據的同時,給出問題的定義。對與這次的「泰坦尼克號倖存者」問題的定義以下:學習

「在訓練數據中,提供了在泰坦尼克號上乘客的具體數據以及他們是否在那次災難中存活的信息。參賽者可否經過已有的訓練數據訓練出一個模型,此模型須要根據輸入的測試數據裏乘客信息,來預測此乘客是否能在災難中存活」測試

 

咱們可能也想要經過問題描述獲取更多有關此問題的信息。在此問題的描述中,比較有意義的描述以下:

  1. 泰坦尼克號在1912年4月15日與冰山碰撞後沉沒。在一共2224名乘客與船員裏,有1502人不幸逝世。這個信息即代表了這次事件中生還率爲32%。
  2. 一個使得在此次災難中有如此之大死亡率的緣由是:在船上沒有足夠的救生船提供給乘客以及船員
  3. 儘管在這次災難中生還存在運氣的成分,可是仍舊會有些羣體的生還率高於其餘人,如女人、小孩,以及上等倉的人

 

3. 工做流程裏的目標:

數據科學解決方案的流程主要有7個目標:

  1. 分類:咱們可能想對樣本分類,也但願根據須要解決的目標去理解不一樣類別之間的隱藏關係
  2. 相互關係:咱們能夠根據訓練集裏可用的特徵來解決一個問題。那到底在數據集裏的哪些特徵會對解決問題起着相當重要的做用呢?從統計學上來講,是否在某個特徵與問題的解之間存在某種聯繫?若是這個特徵的值改變後,相應問題的解是否也會改變呢?反過來的狀況是否也是如此呢?這個能夠經過對數據集裏的數值型以及離散型的特徵作測試來獲得。咱們可能也但願獲得特徵之間的關係,而不是直接獲得特徵與問題解之間的關係。找到一些特定屬性之間的關聯性可能會在建立、補全以及修正特徵上起到必定做用
  3. 轉換:在建模的階段,咱們須要準備數據。根據模型、算法的選擇,咱們可能須要將全部的特徵轉化爲同等的數值型特徵。例如將文本型數據轉換爲數值型數據
  4. 補全數據:在數據準備的階段中,咱們可能仍然須要預測某些特徵下丟失的數據值。更重要的是,模型可能在無丟失數據時表現的更好
  5. 修正數據:咱們可能仍然須要分析給定的訓練數據集裏某些特徵下的錯誤數據以及多是錯誤的數據,而且嘗試去修正這些數據或者除去這些包含錯誤數據的樣本。一個可行的方案是在咱們樣本里或特徵裏檢測全部異常值。若是某個特徵對咱們的問題分析毫無幫助,或者會對結果產生影響,那咱們可能還須要徹底丟棄這個特徵。
  6. 創造數據:咱們是否可以根據已存在的某個或某幾個特徵建立一個新的特徵呢?並讓新特徵聽從「相互關係」、「轉換」以及「數據完整(補全數據)」的目標
  7. 製圖:如何根據原數據集以及要解決問題,對數據作合適的可視化圖 

 

4. 會用到的庫:

如下是在接下來的實驗裏會用到的一些庫:

# data analysis and wrangling
import pandas as pd
import numpy as np
import random as rnd

# visualization
import seaborn as sns
import matplotlib.pyplot as plt

# machine learning
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC, LinearSVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import Perceptron
from sklearn.linear_model import SGDClassifier
from sklearn.tree import DecisionTreeClassifier

 

5. 獲取數據:

咱們能夠用python 的 Pandas 來幫助咱們處理數據。首先能夠將訓練數據以及測試數據讀入到Pandas 的 DataFrames 裏。咱們也會將這兩個數據集結合起來,用於在兩個數據集上同時作一些特定的操做。

 

# set pandas
pd.set_option('display.width', 1000)

# use pandas to manage data
train_df = pd.read_csv('data/train.csv')
test_df = pd.read_csv('data/test.csv')
combine = [train_df, test_df]

 

6. 經過描述數據來分析:

Pandas 也能夠幫助咱們描述數據集。咱們能夠經過如下問答的方式來查看數據集:

 

1. 在數據集中有哪些可用的特徵?

首先須要注意的是,數據集裏特徵的描述已經在問題描述裏給出了,這次數據集裏的特徵描述以下:

https://www.kaggle.com/c/titanic/data

 

------------------------------------------------------------------------------------------------------

主要內容爲:

Data Dictionary

Variable

Definition

Key

survival

Survival

0 = No, 1 = Yes

pclass

Ticket class

1 = 1st, 2 = 2nd, 3 = 3rd

sex

Sex

 

Age

Age in years

 

sibsp

# of siblings / spouses aboard the Titanic

 

parch

# of parents / children aboard the Titanic

 

ticket

Ticket number

 

fare

Passenger fare

 

cabin

Cabin number

 

embarked

Port of Embarkation

C = Cherbourg, Q = Queenstown, S = Southampton

Variable Notes

pclass: A proxy for socio-economic status (SES)
1st = Upper
2nd = Middle
3rd = Lower

age: Age is fractional if less than 1. If the age is estimated, is it in the form of xx.5

sibsp: The dataset defines family relations in this way...
Sibling = brother, sister, stepbrother, stepsister
Spouse = husband, wife (mistresses and fiancés were ignored)

parch: The dataset defines family relations in this way...
Parent = mother, father
Child = daughter, son, stepdaughter, stepson
Some children travelled only with a nanny, therefore parch=0 for them.

 

------------------------------------------------------------------------------------------------------

 

在 Pandas裏:

>>> print(train_df.columns.values)

['PassengerId' 'Survived' 'Pclass' 'Name' 'Sex' 'Age' 'SibSp' 'Parch'

 'Ticket' 'Fare' 'Cabin' 'Embarked']

 

2. 哪些特徵是離散型的?

這些離散型的數值能夠將樣本分類爲一系列類似的樣本。在離散型特徵裏,它們的數值是基於名詞的?仍是基於有序的?又或是基於比率的?仍是基於間隔類的?除此以外,這個能夠幫助咱們爲數據選擇合適的圖形作可視化。

 

在這個問題中,離散型的變量有:Survived,Sex 和 Embarked。基於序列的有:Pclass

 

3. 哪些特徵是數值型?

哪些特徵是數值型的?這些數據的值隨着樣本的不一樣而不一樣。在數值型特徵裏,它們的值是離散的仍是連續的?又或者是基於時間序列?除此以外,這個能夠幫助咱們爲數據選擇合適的圖形作可視化。

 

在這個問題中,連續型的數值特徵有:Age,Fare。離散型數值有:SibSp,Parch

 

>>> train_df.head()

 

4. 哪些特徵是混合型數據?

數值型、字母數值型數據在同一特徵下面。這些有多是咱們須要修正的目標數據。

 

在這個問題中,Ticket是混合了數值型以及字母數值型的數據類型,Cabin是字母數值型數據

 

5. 哪些特徵可能包含錯誤數據或打字錯誤?

在大型數據集裏要發現這些可能比較困難,然而經過觀察小型的數據集裏少許的樣本,可能也能夠徹底告訴咱們哪些特徵須要修正。

 

在這個問題中,Name的特徵可能包含錯誤或者打字錯誤,由於會有好幾種方法來描述名字

 

>>> train_df.tail()

 

 

6. 哪些特徵包含空格,null或者空值

這些空格,null值或者空值極可能須要修正。

 

在這個問題中:

  1. 這些特徵包含null值的數量大小爲:Cabin > Age > Embarked
  2. 在訓練集裏有不完整數據的數量的大小爲:Cabin > Age 

 

7.每一個特徵下的數據類型是什麼?

這個能夠在咱們作數據轉換時起到較大的幫助。

 

在這個問題中:

  1. 7個特徵是int型或float 型。在測試數據集裏有6個
  2. 有5個特徵是string(object)類型

 

>>> train_df.info()

<class 'pandas.core.frame.DataFrame'>

RangeIndex: 891 entries, 0 to 890

Data columns (total 12 columns):

PassengerId    891 non-null int64

Survived       891 non-null int64

Pclass         891 non-null int64

Name           891 non-null object

Sex            891 non-null object

Age            714 non-null float64

SibSp          891 non-null int64

Parch          891 non-null int64

Ticket         891 non-null object

Fare           891 non-null float64

Cabin          204 non-null object

Embarked       889 non-null object

dtypes: float64(2), int64(5), object(5)

memory usage: 83.6+ KB

 

>>> test_df.info()

<class 'pandas.core.frame.DataFrame'>

RangeIndex: 418 entries, 0 to 417

Data columns (total 11 columns):

PassengerId    418 non-null int64

Pclass         418 non-null int64

Name           418 non-null object

Sex            418 non-null object

Age            332 non-null float64

SibSp          418 non-null int64

Parch          418 non-null int64

Ticket         418 non-null object

Fare           417 non-null float64

Cabin          91 non-null object

Embarked       418 non-null object

dtypes: float64(2), int64(4), object(5)

memory usage: 36.0+ KB

 

8. 在樣本里,數值型特徵的數值分佈是什麼樣的?

這個能夠幫助咱們初步瞭解:訓練數據集如何體現了實際問題。

 

在這個問題中:

  1. 一共有891個樣本
  2. Survived的標籤是經過0或1來區分
  3. 大概38%的樣本是survived
  4. 大多數乘客(>75%)沒有與父母或是孩子一塊兒旅行
  5. 大約30%的乘客有親屬和/或配偶一塊兒登船
  6. 票價的差異很是大,少許的乘客(<1%)付了高達$512的費用
  7. 不多的乘客(<1%)年紀在64-80之間

 

咱們能夠經過如下方式獲取上述信息:

>>> train_df.describe()

       PassengerId    Survived      Pclass         Age       SibSp       Parch        Fare

count   891.000000  891.000000  891.000000  714.000000  891.000000  891.000000  891.000000

mean    446.000000    0.383838    2.308642   29.699118    0.523008    0.381594   32.204208

std     257.353842    0.486592    0.836071   14.526497    1.102743    0.806057   49.693429

min       1.000000    0.000000    1.000000    0.420000    0.000000    0.000000    0.000000

25%     223.500000    0.000000    2.000000   20.125000    0.000000    0.000000    7.910400

50%     446.000000    0.000000    3.000000   28.000000    0.000000    0.000000   14.454200

75%     668.500000    1.000000    3.000000   38.000000    1.000000    0.000000   31.000000

max     891.000000    1.000000    3.000000   80.000000    8.000000    6.000000  512.329200

 

# 經過使用 percentiles=[.61, .62] 來查看數據集能夠了解到生存率爲 38%

>>> train_df.describe(percentiles=[.61, .62])

       PassengerId    Survived      Pclass         Age       SibSp       Parch        Fare

count   891.000000  891.000000  891.000000  714.000000  891.000000  891.000000  891.000000

mean    446.000000    0.383838    2.308642   29.699118    0.523008    0.381594   32.204208

std     257.353842    0.486592    0.836071   14.526497    1.102743    0.806057   49.693429

min       1.000000    0.000000    1.000000    0.420000    0.000000    0.000000    0.000000

50%     446.000000    0.000000    3.000000   28.000000    0.000000    0.000000   14.454200

61%     543.900000    0.000000    3.000000   32.000000    0.000000    0.000000   23.225000

62%     552.800000    1.000000    3.000000   32.000000    0.000000    0.000000   24.150000

max     891.000000    1.000000    3.000000   80.000000    8.000000    6.000000  512.329200

 

# 經過使用 percentiles=[.75, .8] 來查看Parch的分佈

>>> train_df.describe(percentiles=[.75, .8])

       PassengerId    Survived      Pclass         Age       SibSp       Parch        Fare

count   891.000000  891.000000  891.000000  714.000000  891.000000  891.000000  891.000000

mean    446.000000    0.383838    2.308642   29.699118    0.523008    0.381594   32.204208

std     257.353842    0.486592    0.836071   14.526497    1.102743    0.806057   49.693429

min       1.000000    0.000000    1.000000    0.420000    0.000000    0.000000    0.000000

50%     446.000000    0.000000    3.000000   28.000000    0.000000    0.000000   14.454200

75%     668.500000    1.000000    3.000000   38.000000    1.000000    0.000000   31.000000

80%     713.000000    1.000000    3.000000   41.000000    1.000000    1.000000   39.687500

max     891.000000    1.000000    3.000000   80.000000    8.000000    6.000000  512.329200

 

# 經過使用 percentile=[.68, .69] 來查看SibSp的分佈

>>> train_df.describe(percentiles=[.68, .69])

       PassengerId    Survived      Pclass         Age       SibSp       Parch        Fare

count   891.000000  891.000000  891.000000  714.000000  891.000000  891.000000  891.000000

mean    446.000000    0.383838    2.308642   29.699118    0.523008    0.381594   32.204208

std     257.353842    0.486592    0.836071   14.526497    1.102743    0.806057   49.693429

min       1.000000    0.000000    1.000000    0.420000    0.000000    0.000000    0.000000

50%     446.000000    0.000000    3.000000   28.000000    0.000000    0.000000   14.454200

68%     606.200000    1.000000    3.000000   35.000000    0.000000    0.000000   26.307500

69%     615.100000    1.000000    3.000000   35.000000    1.000000    0.000000   26.550000

max     891.000000    1.000000    3.000000   80.000000    8.000000    6.000000  512.329200

 

# 經過使用 percentile=[.1, .2, .3, .4, .5, .6, .7, .8, .9, .99] 來查看Age和Fare的分佈

>>> train_df.describe(percentiles=[.1, .2, .3, .4, .5, .6, .7, .8, .9, .99])

       PassengerId    Survived      Pclass         Age       SibSp       Parch        Fare

count   891.000000  891.000000  891.000000  714.000000  891.000000  891.000000  891.000000

mean    446.000000    0.383838    2.308642   29.699118    0.523008    0.381594   32.204208

std     257.353842    0.486592    0.836071   14.526497    1.102743    0.806057   49.693429

min       1.000000    0.000000    1.000000    0.420000    0.000000    0.000000    0.000000

10%      90.000000    0.000000    1.000000   14.000000    0.000000    0.000000    7.550000

20%     179.000000    0.000000    1.000000   19.000000    0.000000    0.000000    7.854200

30%     268.000000    0.000000    2.000000   22.000000    0.000000    0.000000    8.050000

40%     357.000000    0.000000    2.000000   25.000000    0.000000    0.000000   10.500000

50%     446.000000    0.000000    3.000000   28.000000    0.000000    0.000000   14.454200

60%     535.000000    0.000000    3.000000   31.800000    0.000000    0.000000   21.679200

70%     624.000000    1.000000    3.000000   36.000000    1.000000    0.000000   27.000000

80%     713.000000    1.000000    3.000000   41.000000    1.000000    1.000000   39.687500

90%     802.000000    1.000000    3.000000   50.000000    1.000000    2.000000   77.958300

99%     882.100000    1.000000    3.000000   65.870000    5.000000    4.000000  249.006220

max     891.000000    1.000000    3.000000   80.000000    8.000000    6.000000  512.329200

 

8. 在樣本里,離散型數據的分佈是什麼?

在這個問題中:

  1. 各個乘客的Name 屬性徹底是惟一的(count=unique=891
  2. Sex特徵裏65%爲男性(top=male,fre=577/count=891
  3. Cabin的count與unique並不相等,即說明有些乘客會共享一個cabin
  4. Embarked一共有種取值,其中從S港口登船的人最多
  5. Ticket的特徵下,有22%左右的重複值(unique=681

 

能夠經過如下方法得到以上信息:

>>> train_df.describe(include=['O'])

                              Name   Sex    Ticket Cabin Embarked

count                          891   891       891   204      889

unique                         891     2       681   147        3

top     Andrew, Mr. Edgardo Samuel  male  CA. 2343    G6        S

freq                             1   577         7     4      644

 

7. 基於以上數據分析後的假設

根據以上的數據分析步驟後,咱們能夠暫時得出如下假設。固然,咱們也能夠在以後驗證這些假設。

 

相互關係:

咱們想知道每一個特徵與Survival的相關性如何。咱們但願可以今早的作這一步,而且將這些相關性特徵匹配到建模後的相關性特徵上。

 

補全數據:

  1. 咱們可能會去補全Age特徵下的數據,由於它必定是與存活率是相關的
  2. 咱們可能會去補全Embarked特徵下的數據,由於它可能與存活率或者其餘重要的特徵之間存在相關性

 

修正數據:

  1. Ticket特徵可能須要從咱們的分析中丟棄,由於它的數值重複率高達22%,而且Ticket與survival之間極可能並無聯繫
  2. Cabin特徵可能也須要丟棄,由於它的數值很是不完整,而且在訓練集以及測試集裏均包含較多的null值
  3. PassengerId特徵可能也須要被丟棄,由於它對survival沒任何做用
  4. Name特徵相對來講不是特別規範,而且頗有可能與survival之間沒有直接聯繫,因此可能也應該被丟棄

 

創造數據:

  1. 咱們能夠根據Parch和SibSp的特徵來建立一個新的Family特徵,以此獲得每一個乘客有多少家庭成員登了船
  2. 咱們能夠對Name特徵作進一步加工,提取出名字裏的Title做爲一個新的特徵
  3. 咱們能夠爲Age特徵建立一個新的特徵,將它本來的連續型數值特徵轉換爲有序的離散型特徵
  4. 咱們也能夠建立一個票價(Fare)範圍的特徵,若是它對咱們的分析有幫助的話

 

分類:

根據以前的問題描述或者已有的數據,咱們也能夠提出如下假設:

  1. 女人(Sex=female)更有可能存活
  2. 孩子(Age<?)也更有可能存活
  3. 上等倉的乘客(Pclass=1)有更大的存活率

 

8. 基於pivoting features的分析

爲了驗證以前的觀察與假設,咱們能夠經過pivoting feature的方法簡單的分析一下特徵之間的相關性。

這種方法僅僅對那些沒有特別多空值的屬性有效,而且僅僅對那些分類型的(Sex)、有序型的(Pclass)以及離散型(SibSp,Parch)的特徵纔有意義。

 

1. Pclass:咱們觀察到Pclass=1與Survived的相關性較大(>0.5),因此能夠考慮將此特徵放入到以後的模型裏

2. Sex:咱們能夠確認Sex=female有着高達74%的生存率

3. SibSp 和 Parch:這些特徵下有些值與survived有相關性,可是有些又毫無相關性。因此咱們可能須要基於這些單獨的特徵或一系列特徵建立一個新特徵,以作進一步分析

 

以上結論能夠經過下面的操做獲取:

>>> train_df[['Pclass', 'Survived']].groupby(['Pclass'], as_index=False).mean().sort_values(by='Survived', ascending=False)

 

   Pclass  Survived

0       1  0.629630

1       2  0.472826

2       3  0.242363

 

 

>>> train_df[['Sex', 'Survived']].groupby(['Sex'], as_index=False).mean().sort_values(by='Survived', ascending=False)

 

      Sex  Survived

0  female  0.742038

1    male  0.188908

 

>>> train_df[['SibSp', 'Survived']].groupby(['SibSp'], as_index=False).mean().sort_values(by='Survived', ascending=False)

 

   SibSp  Survived

1      1  0.535885

2      2  0.464286

0      0  0.345395

3      3  0.250000

4      4  0.166667

5      5  0.000000

6      8  0.000000

 

>>> train_df[['Parch', 'Survived']].groupby(['Parch'], as_index=False).mean().sort_values(by='Survived', ascending=False)

 

   Parch  Survived

3      3  0.600000

1      1  0.550847

2      2  0.500000

0      0  0.343658

5      5  0.200000

4      4  0.000000

6      6  0.000000

 

9.經過將數據可視化進行分析

如今咱們能夠經過將數據可視化對數據作進一步分析,並繼續驗證咱們以前的假設是否正確

 

數值型特徵與Survived之間的聯繫:

柱狀圖在用於分析連續型的數值特徵時很是有用,如特徵Age,它的柱狀圖數值範圍(不一樣的年齡範圍)能夠幫助咱們識別一些有用的模式。

經過使用默認或自定的數值範圍(年齡範圍),柱狀圖能夠幫助咱們描繪出樣本所遵循的分佈。

它能夠幫助咱們發現是否某些特定的年齡範圍(如嬰兒)有更高的存活率。

 

咱們能夠經過如下代碼來畫出Age的柱狀圖:

>>> g = sns.FacetGrid(train_df, col='Survived')

>>> g.map(plt.hist, 'Age', bins=20)

>>> plt.show()

 

 

 

觀察:

  1. 嬰兒(Age<=4)有較高的生存率
  2. 老人(Age=80)所有生還
  3. 大量的15-25年紀的乘客沒有生還
  4. 乘客主要在15-35的年紀範圍內

 

結論:

以上簡單的分析驗證了咱們以前的假設:

  1. 咱們須要將Age考慮到訓練模型裏
  2. 爲Age特徵補全null
  3. 咱們應該band不一樣的年齡層

 

數值型與序列型特徵之間的聯繫:

咱們能夠將多個特徵組合,而後經過一個簡單的圖來識別它們之間的關係。這種方法能夠應用在數值型以及分類型(Pclass)的特徵裏,由於它們的值都是數值型。

 

咱們能夠經過如下代碼來畫出Pclass的柱狀圖:

>>> grid = sns.FacetGrid(train_df, col='Survived', row='Pclass', size=2.2, aspect=1.6)

>>> grid.map(plt.hist, 'Age', alpha=.5, bins=20)

>>> grid.add_legend()

>>> plt.show()

 

 

 

觀察:

  1. Pclass=3 有着最多的乘客,可是他們大多數卻沒有存活。這也驗證了咱們以前在「分類」裏的假設 #2
  2. Pclass=2和Pclass=3中,大多數嬰兒活了下來,進一步驗證了咱們以前在「分類」裏的假設 #2
  3. 大多數Pclass=1的乘客存活,驗證咱們以前在「分類」裏的假設 #3
  4. Pclass根據Age的分佈而改變

 

結論:

  1. 考慮將Pclass特徵加入模型訓練

 

離散型特徵與Survived之間的聯繫:

如今咱們能夠查看離散型特徵與survived之間的關係

 

咱們能夠經過如下方式將數據可視化:

>>> grid = sns.FacetGrid(train_df, row='Embarked', size=2.2, aspect=1.6)

>>> grid.map(sns.pointplot, 'Pclass', 'Survived', 'Sex', palette='deep')

>>> grid.add_legend()

>>> plt.show()

 

 

 

觀察:

  1. 女性乘客相對於男性乘客有着更高的存活率
  2. 惟一在Embarked=C中是例外,其中男性的生存率高於女性。從這點來看,Pclass和Embarked之間可能有聯繫
  3. Embarked和Survived之間可能並無直接的聯繫。 
  4. Males had better survival rate in Pclass=3 when compared with Pclass=2 for C and Q ports
  5. 對於Pclass=3以及男性乘客來講,Embarked的港口不一樣會致使存活率的不一樣

 

結論:

  1. Sex特徵加入訓練模型
  2. 補全Embarked特徵下的數據並將此特徵加入訓練模型

 

離散型特徵與數值型特徵之間的聯繫:

咱們可能也想找出離散型與數值型特徵之間的關係。

咱們能夠考慮查看Embarked(離散非數值型),Sex(離散非數值型),Fare(連續數值型)與Survived(離散數值型)之間的關係

 

咱們能夠經過下方式將數據可視化:

>>> grid = sns.FacetGrid(train_df, row='Embarked', col='Survived', size=2.2, aspect=1.6)

>>> grid.map(sns.barplot, 'Sex', 'Fare', alpha=.5, ci=None)

>>> grid.add_legend()

>>> plt.show()

 

 

 

觀察:

  1. 1. 付了高票價的乘客有着更高的生存率,驗證了咱們以前的假設
  2. 2. Embarked與生存率相關,驗證了咱們以前所作的假設 

結論:

  1. 1. 考慮將Fare特徵作不一樣的區間

 

10.加工數據

咱們根據數據集以及題目的要求已經收集了一些假設與結論。到如今爲止,咱們暫時尚未對任何特徵或數據進行處理。

接下來咱們會根據以前作的假設與結論,以「修正數據」、「創造數據」以及「補全數據」爲目標,對數據進行處理。

 

經過丟棄特徵來修正數據:

這個步驟比較好的一個開始。經過丟棄某些特徵,可讓咱們處理更少的數據點,並讓分析更簡單。

 

根據咱們以前的假設和結論,咱們但願丟棄Cabin和Ticket這兩個特徵。

 

在這裏須要注意的是,爲了保持數據的一致,咱們須要同時將訓練集與測試集裏的這兩個特徵均丟棄。

 

具體步驟以下:

>>> print("Before", train_df.shape, test_df.shape, combine[0].shape, combine[1].shape)

Before (891, 12) (418, 11) (891, 12) (418, 11)

 

>>> train_df = train_df.drop(['Ticket', 'Cabin'], axis=1)

>>> test_df = test_df.drop(['Ticket', 'Cabin'], axis=1)

>>> combine = [train_df, test_df]

>>> print('After', train_df.shape, test_df.shape, combine[0].shape, combine[1].shape)

After (891, 10) (418, 9) (891, 10) (418, 9)

 

經過已有的特徵建立新特徵:

咱們在丟棄Name與PassengerId這兩個特徵以前,但願從Name特徵裏提取出Titles的特徵,並測試Titles與survival之間的關係。

 

在下面的代碼中,咱們經過正則提取了Title特徵,正則表達式爲(\w+\.),它會在Name特徵裏匹配第一個以「.」號爲結束的單詞。

同時,指定expand=False的參數會返回一個DataFrame。

 

>>> for dataset in combine:

        dataset['Title'] = dataset.Name.str.extract('([A-Za-z]+)\.', expand=False)

>>> pd.crosstab(train_df['Title'], train_df['Sex'])

 

Sex       female  male

Title                 

Capt           0     1

Col            0     2

Countess       1     0

Don            0     1

Dr             1     6

Jonkheer       0     1

Lady           1     0

Major          0     2

Master         0    40

Miss         182     0

Mlle           2     0

Mme            1     0

Mr             0   517

Mrs          125     0

Ms             1     0

Rev            0     6

Sir            0     1

 

咱們可使用高一些更常見的名字或「Rare」來代替一些Title,如:

for dataset in combine:
    dataset['Title'] = dataset['Title'].replace(['Lady', 'Countess', 'Capt',
                                                 'Col', 'Don', 'Dr', 'Major',
                                                 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare')
    dataset['Title'] = dataset['Title'].replace('Mlle', 'Miss')
    dataset['Title'] = dataset['Title'].replace('Ms', 'Miss')
    dataset['Title'] = dataset['Title'].replace('Mme', 'Mrs')

 

>>> train_df[['Title', 'Survived']].groupby(['Title'], as_index=False).mean()

 

    Title  Survived

0  Master  0.575000

1    Miss  0.702703

2      Mr  0.156673

3     Mrs  0.793651

4    Rare  0.347826

 

進一步的,咱們能夠將這些離散型的Title轉換爲有序的數值型:

# convert categorical titles to ordinal
title_mapping = {"Mr":1, "Miss":2, "Mrs":3, "Master":4, "Rare":5}
for dataset in combine:
    dataset['Title'] = dataset['Title'].map(title_mapping)
    dataset['Title'] = dataset['Title'].fillna(0)

 

>>> train_df.head()

 

 

 

如今咱們能夠放心的從訓練集與測試集裏丟棄Name特徵。同時,咱們也再也不須要訓練集裏的PassengerId特徵:

>>> train_df = train_df.drop(['Name', 'PassengerId'], axis=1)

>>> test_df = test_df.drop(['Name'], axis=1)

>>> combine = [train_df, test_df]

>>> train_df.shape, test_df.shape

((891, 9), (418, 9))

 

>>> train_df.head()

   Survived  Pclass     Sex   Age  SibSp  Parch     Fare Embarked  Title

0         0       3    male  22.0      1      0   7.2500        S      1

1         1       1  female  38.0      1      0  71.2833        C      3

2         1       3  female  26.0      0      0   7.9250        S      2

3         1       1  female  35.0      1      0  53.1000        S      3

4         0       3    male  35.0      0      0   8.0500        S      1

 

新的發現:

當咱們畫出Title,Age和Survived的圖後,咱們有了如下新的發現:

  1. Most titles band Age groups accurately. For example: Master title has Age mean of 5 years
  2. Survival among Title Age bands varies slightly
  3. 某些特定的title如Mme,Lady,Sir的乘客存活率較高,但某些title如Don,Rev,Jonkheer的乘客存活率不高

 

結論:

  1. 咱們決定保留這個新的Title特徵並加入到訓練模型

 

轉換一個離散型的特徵

如今咱們能夠將一些包含字符串數據的特徵轉換爲數值型特徵,由於在不少建模算法裏,輸入的參數要求爲數值型。

這個步驟可讓咱們達到補全數據的目標。

 

咱們能夠從轉換Sex特徵開始,將female轉換爲1,male轉換爲0。咱們能夠將新的特徵命名爲Gender:

for dataset in combine:
    dataset['Sex'] = dataset['Sex'].map({'female':1, 'male':0}).astype(int)

 

>>> train_df.head()

   Survived  Pclass  Sex   Age  SibSp  Parch     Fare Embarked  Title

0         0       3    0  22.0      1      0   7.2500        S      1

1         1       1    1  38.0      1      0  71.2833        C      3

2         1       3    1  26.0      0      0   7.9250        S      2

3         1       1    1  35.0      1      0  53.1000        S      3

4         0       3    0  35.0      0      0   8.0500        S      1

 

補全連續數值型特徵

如今咱們能夠開始爲那些含null值或者丟失值的特徵補全數據。咱們首先會爲Age特徵補全數據。

 

如今咱們總結一下三種補全連續數值型特徵數據的方法:

 

1. 一個簡單的方法是產生一個隨機數,這個隨機數的範圍在這個特徵的平均值以及標準差之間

2. 更精準的一個作法是使用與它相關的特徵來作一個猜想。在這個案例中,咱們發現Age,Gender和Pclass之間有關聯。

因此咱們會使用一系列Pclass和Gender特徵組合後的中值,做爲猜想的Age值。

因此咱們會有一系列的猜想值如:當Pclass=1且Gender=0時,當Pclass=1且Gender=1時,等等

3. 第三種方法是結合以上兩種方法。咱們能夠根據一系列Pclass與Gender的組合,並使用第一種方法裏提到的隨機數來猜想缺失的Age值

 

方法1與方法3會在模型裏引入隨機噪音,屢次的結果可能會有所不一樣。因此咱們在這更傾向於使用方法2:

 

>>> grid = sns.FacetGrid(train_df, row='Pclass', col='Sex', size=2.2, aspect=1.6)

>>> grid.map(plt.hist, 'Age', alpha=.5, bins=20)

>>> grid.add_legend()

>>> plt.show()

 

 

 

 

咱們先準備一個空的數組來存儲猜想的年齡,由於是Pclass與Gender的組合,因此數組大小爲2x3:

>>> guess_ages = np.zeros((2, 3))

 

而後咱們能夠對Sex(0或1)和Pclass(1,2,3)進行迭代,並計算出在6中組合下所獲得的猜想(Age)值:

 

for dataset in combine:
    for i in range(0, 2):
        for j in range(0, 3):
            guess_df = dataset[(dataset['Sex'] == i) & (dataset['Pclass'] == j+1)]['Age'].dropna()

            age_guess = guess_df.median()

            # Convert random age float to nearest .5 age
           
guess_ages[i, j] = int(age_guess / 0.5 + 0.5) * 0.5

    for i in range(0, 2):
            for j in range(0, 3):
                dataset.loc[ (dataset.Age.isnull()) & (dataset.Sex == i) & (dataset.Pclass == j+1),
                             'Age'] = guess_ages[i,j]

    dataset['Age'] = dataset['Age'].astype(int)

 

>>> train_df.head()

   Survived  Pclass  Sex  Age  SibSp  Parch     Fare Embarked  Title

0         0       3    0   22      1      0   7.2500        S      1

1         1       1    1   38      1      0  71.2833        C      3

2         1       3    1   26      0      0   7.9250        S      2

3         1       1    1   35      1      0  53.1000        S      3

4         0       3    0   35      0      0   8.0500        S      1

 

如今咱們對Age分段,並查看每段與Survived之間的相關性:

>>> train_df['AgeBand'] = pd.cut(train_df['Age'], 5)

>>> train_df[['AgeBand', 'Survived']].groupby(['AgeBand'], as_index=False).mean().sort_values(by='AgeBand', ascending=True)

 

         AgeBand  Survived

0  (-0.08, 16.0]  0.550000

1   (16.0, 32.0]  0.337374

2   (32.0, 48.0]  0.412037

3   (48.0, 64.0]  0.434783

4   (64.0, 80.0)  0.090909

 

而後咱們根據上面的分段,使用有序的數值來替換Age裏的值:

for dataset in combine:
    dataset.loc[ dataset['Age'] <= 16, 'Age'] = 0
    dataset.loc[(dataset['Age'] > 16) & (dataset['Age'] <= 32), 'Age'] = 1
    dataset.loc[(dataset['Age'] > 32) & (dataset['Age'] <= 48), 'Age'] = 2
    dataset.loc[(dataset['Age'] > 48) & (dataset['Age'] <= 64), 'Age'] = 3
    dataset.loc[ dataset['Age'] > 64, 'Age']

 

>>> train_df.head()

   Survived  Pclass  Sex  Age  SibSp  Parch     Fare Embarked  Title       AgeBand

0         0       3    0    1      1      0   7.2500        S      1  (16.0, 32.0]

1         1       1    1    2      1      0  71.2833        C      3  (32.0, 48.0]

2         1       3    1    1      0      0   7.9250        S      2  (16.0, 32.0]

3         1       1    1    2      1      0  53.1000        S      3  (32.0, 48.0)

4         0       3    0    2      0      0   8.0500        S      1  (32.0, 48.0)

 

接着咱們能夠丟棄AgeBand特徵:

>>> train_df = train_df.drop(['AgeBand'], axis=1)

>>> combine = [train_df, test_df]

>>> train_df.head()

 

   Survived  Pclass  Sex  Age  SibSp  Parch     Fare Embarked  Title

0         0       3    0    1      1      0   7.2500        S      1

1         1       1    1    2      1      0  71.2833        C      3

2         1       3    1    1      0      0   7.9250        S      2

3         1       1    1    2      1      0  53.1000        S      3

4         0       3    0    2      0      0   8.0500        S      1

 

經過已有的特徵組合出新特徵

如今咱們能夠經過組合Parch和SibSp特徵,建立一個新的FamilySize特徵。這個步驟可讓咱們從數據集裏丟棄Parch與SibSp特徵。

 

for dataset in combine:
    dataset['FamilySize'] = dataset['SibSp'] + dataset['Parch'] + 1

 

>>> train_df[['FamilySize', 'Survived']].groupby(['FamilySize'], as_index=False).mean().sort_values(by='Survived', ascending=False)

 

  FamilySize  Survived

3           4  0.724138

2           3  0.578431

1           2  0.552795

6           7  0.333333

0           1  0.303538

4           5  0.200000

5           6  0.136364

7           8  0.000000

8          11  0.000000

 

接着咱們能夠建立另外一個名爲IsAlone的特徵:

for dataset in combine:
    dataset['IsAlone'] = 0
    dataset.loc[dataset['FamilySize'] == 1, 'IsAlone'] = 1

 

>>> train_df[['IsAlone', 'Survived']].groupby(['IsAlone'], as_index=False).mean()

 

   IsAlone  Survived

0        0  0.505650

1        1  0.303538

 

 

基於上面的數據表現,咱們如今能夠丟棄Parch、SibSp以及FamilySize的特徵,保留IsAlone的特徵:

>>> train_df = train_df.drop(['Parch', 'SibSp', 'FamilySize'], axis=1)

>>> test_df = test_df.drop(['Parch', 'SibSp', 'FamilySize'], axis=1)

>>> combine = [train_df, test_df]

>>> train_df.head()

 

   Survived  Pclass  Sex  Age     Fare Embarked  Title  IsAlone

0         0       3    0    1   7.2500        S      1        0

1         1       1    1    2  71.2833        C      3        0

2         1       3    1    1   7.9250        S      2        1

3         1       1    1    2  53.1000        S      3        0

4         0       3    0    2   8.0500        S      1        1

 

咱們還能夠經過結合Pclass 和 Age來建立一個新的特徵:

for dataset in combine:
    dataset['Age*Class'] = dataset.Age * dataset.Pclass

 

>>> train_df.loc[:, ['Age*Class', 'Age', 'Pclass']].head(10)

   Age*Class  Age  Pclass

0          3    1       3

1          2    2       1

2          3    1       3

3          2    2       1

4          6    2       3

5          3    1       3

6          3    3       1

7          0    0       3

8          3    1       3

9          0    0       2

 

補全一個離散型的特徵

Embarked特徵主要有三個值,分別爲S,Q,C,對應了三個登船港口。在訓練集裏,這個有2個缺失值,咱們會使用頻率最高的值來填充這個缺失值。

 

>>> freq_port = train_df.Embarked.dropna().mode()[0]

>>> freq_port

'S'

 

for dataset in combine:
    dataset['Embarked'] = dataset['Embarked'].fillna(freq_port)

 

>>> train_df[['Embarked', 'Survived']].groupby(['Embarked'], as_index=False).mean().sort_values(by='Survived', ascending=False)

 

  Embarked  Survived

0        C  0.553571

1        Q  0.389610

2        S  0.339009

 

將離散型特徵轉換爲數值型

咱們如今能夠將離散型的Embarked特徵轉換爲數值型特徵

 

for dataset in combine:
    dataset['Embarked'] = dataset['Embarked'].map({'S': 0, 'C': 1, 'Q': 2}).astype(int)

 

>>> train_df.head()

   Survived  Pclass  Sex  Age     Fare  Embarked  Title  IsAlone  Age*Class

0         0       3    0    1   7.2500         0      1        0          3

1         1       1    1    2  71.2833         1      3        0          2

2         1       3    1    1   7.9250         0      2        1          3

3         1       1    1    2  53.1000         0      3        0          2

4         0       3    0    2   8.0500         0      1        1          6

 

補全數值型特徵

如今咱們能夠開始爲測試集裏的Fare特徵補全數據。在補全時,咱們可使用最頻繁出現的數據用於補全缺失值。

(咱們也能夠將Fare的數值作四捨五入,將它精確到2位)

 

>>> test_df['Fare'].fillna(test_df['Fare'].dropna().median(), inplace=True)

>>> test_df.head()

   PassengerId  Pclass  Sex  Age     Fare  Embarked  Title  IsAlone  Age*Class

0          892       3    0    2   7.8292         2      1        1          6

1          893       3    1    2   7.0000         0      3        0          6

2          894       2    0    3   9.6875         2      1        1          6

3          895       3    0    1   8.6625         0      1        1          3

4          896       3    1    1  12.2875         0      3        0          3

 

接下來咱們將Fare分段:

>>> train_df['FareBand'] = pd.qcut(train_df['Fare'], 4)

>>> train_df[['FareBand', 'Survived']].groupby(['FareBand'], as_index=False).mean().sort_values(by='FareBand', ascending=True)

          FareBand  Survived

0   (-0.001, 7.91]  0.197309

1   (7.91, 14.454]  0.303571

2   (14.454, 31.0]  0.454955

3  (31.0, 512.329)  0.581081

 

根據分段後的特徵FareBand,將Fare轉換爲有序的數值型特徵:

for dataset in combine:
    dataset.loc[ dataset['Fare'] <= 7.91, 'Fare'] = 0
    dataset.loc[(dataset['Fare'] > 7.91) & (dataset['Fare'] <= 14.454), 'Fare'] = 1
    dataset.loc[(dataset['Fare'] > 14.454) & (dataset['Fare'] <= 31), 'Fare'] = 2
    dataset.loc[dataset['Fare'] > 31, 'Fare'] = 3
    dataset['Fare'] = dataset['Fare'].astype(int)

 

>>> train_df = train_df.drop(['FareBand'], axis=1)

>>> combine = [train_df, test_df]

>>> train_df.head(10)

 

   Survived  Pclass  Sex  Age  Fare  Embarked  Title  IsAlone  Age*Class

0         0       3    0    1     0         0      1        0          3

1         1       1    1    2     3         1      3        0          2

2         1       3    1    1     1         0      2        1          3

3         1       1    1    2     3         0      3        0          2

4         0       3    0    2     1         0      1        1          6

5         0       3    0    1     1         2      1        1          3

6         0       1    0    3     3         0      1        1          3

7         0       3    0    0     2         0      4        0          0

8         1       3    1    1     1         0      3        0          3

9         1       2    1    0     2         1      3        0          0

 

 

11.建模,預測,並解決問題

如今咱們已經作好了訓練模型的準備,在模型訓練完後,咱們便可將其應用到解決問題中。對於預測的問題,咱們至少有60多種算法可供選擇。

因此咱們必須理解問題的類型和解決方案的需求,這樣才能縮小模型的選擇範圍。如今這個問題是一個分類與迴歸的問題,

咱們但願找出輸出(即Survived)與其餘特徵(即Gender,Age,Port等)之間的關係。由於給定了訓練集,因此這在機器學習裏是一個有監督學習。

因此如今對算法的需求是:有監督學習加上分類與迴歸。根據這個條件,咱們有如下模型可供選擇:

  1. Logistic Regression
  2. kNN 
  3. SVM
  4. Naïve Bayes classifier
  5. Decision Tree
  6. Random Forrest
  7. Perceptron
  8. Artificial neural network
  9. RVM or Relevance Vector Machine

 

如今咱們將訓練集與測試集再作一下區分:

>>> X_train = train_df.drop('Survived', axis=1)

>>> Y_train = train_df['Survived']

>>> X_test = test_df.drop('PassengerId', axis=1).copy()

>>> X_train, Y_train, X_test

((891, 8), (891,), (418, 8))

 

Logistic Regression 是一個很是有用的模型,能夠在工做流程裏優先使用。它經過使用估計機率的方法衡量了離散型特徵與其餘特徵之間的關係,是一個漸增型的邏輯分佈。

 

>>> logreg = LogisticRegression()

>>> logreg.fit(X_train, Y_train)

>>> Y_pred = logreg.predict(X_test)

>>> acc_log = round(logreg.score(X_train, Y_train) * 100, 2)

>>> acc_log

80.359999999999999

 

咱們能夠用Logistic Regression來驗證咱們之間作的假設與結論。這個能夠經過計算特徵值的係數來達成。正係數能夠提高對數概率(因此增加了機率),負係數會下降對數概率(所以下降了機率):

 

>>> coeff_df = pd.DataFrame(train_df.columns.delete(0))

>>> coeff_df.columns = ['Feature']

>>> coeff_df["Correlation"] = pd.Series(logreg.coef_[0])

>>> coeff_df.sort_values(by='Correlation', ascending=False)

     Feature  Correlation

1        Sex     2.201527

5      Title     0.398234

2        Age     0.287162

4   Embarked     0.261762

6    IsAlone     0.129140

3       Fare    -0.085150

7  Age*Class    -0.311202

0     Pclass    -0.749007

 

從上面的結果咱們能夠看出:

  1. Sex是有最高正係數的特徵。這個表面當Sex 的值增長時(從male:0到female:1),Survived=1的機率增長最多
  2. 相反的,當Pclass增長時,Survived=1的機率減小最多
  3. 從結果來看,咱們建立的新特徵Age*Class很是有用,由於它與Survived的負相關性是第二高的
  4. Title是第二高的正係數特徵

 

下一步咱們使用SVM來分析數據並作分類與迴歸分析。

>>> svc = SVC()

>>> svc.fit(X_train, Y_train)

>>> Y_pred = svc.predict(X_test)

>>> acc_svc = round(svc.score(X_train, Y_train) * 100, 2)

>>> acc_svc

83.840000000000003

 

能夠看到使用SVM後的正確率獲得了提高。

 

在模式識別中,KNN算法是一種非參數的方法,用於作分類與迴歸。使用KNN來分析此問題的話:

>>> knn = KNeighborsClassifier(n_neighbors = 3)

>>> knn.fit(X_train, Y_train)

>>> Y_pred = knn.predict(X_test)

>>> acc_knn = round(knn.score(X_train, Y_train) * 100, 2)

84.739999999999995

 

能夠看到使用KNN的正確率比Logistic Regression更高,可是比SVM更低

 

下面咱們試試樸素貝葉斯:

>>> gaussian = GaussianNB()

>>> gaussian.fit(X_train, Y_train)

>>> Y_pred = gaussian.predict(X_test)

>>> acc_gaussian = round(gaussian.score(X_train, Y_train) * 100, 2)

72.280000000000001

 

看來在這個問題中使用樸素貝葉斯不是一個很好的選擇,從當前來看,它的正確率是最低的。

 

接下來咱們試試 perceptron(感知機)算法,它能夠用於二分類問題:

>>> perceptron = Perceptron()

>>> perceptron.fit(X_train, Y_train)

>>> Y_pred = perceptron.predict(X_test)

>>> acc_perceptron = round(perceptron.score(X_train, Y_train) * 100, 2)

78.0

 

能夠看到perceptron的正確率也不高

 

接下來試試Linear SVC:

>>> linear_svc = LinearSVC()

>>> linear_svc.fit(X_train, Y_train)

>>> Y_pred = linear_svc.predict(X_test)

>>> acc_linear_svc = round(linear_svc.score(X_train, Y_train) * 100, 2)

79.010000000000005

 

與隨機梯度降低分類器:

>>> sgd = SGDClassifier()

>>> sgd.fit(X_train, Y_train)

>>> Y_pred = sgd.predict(X_test)

>>> acc_sgd = round(sgd.score(X_train, Y_train) * 100, 2)

78.230000000000004

 

這幾個算法計算到的正確率都不夠理想。

 

接下來咱們看看很常見的決策樹算法:

>>> decision_tree = DecisionTreeClassifier()

>>> decision_tree.fit(X_train, Y_train)

>>> Y_pred = decision_tree.predict(X_test)

>>> acc_decision_tree = round(decision_tree.score(X_train, Y_train) * 100, 2)

86.760000000000005

 

能夠看到,使用決策樹的算法使得正確率達到了一個更高的值。在目前爲止,它的正確率是最高的。

 

而後咱們看看隨機森林,隨機森林經過組合多個決策樹算法來完成:

>>> random_forest = RandomForestClassifier(n_estimators=100)

>>> random_forest.fit(X_train, Y_train)

>>> Y_pred = random_forest.predict(X_test)

>>> acc_random_forest = round(random_forest.score(X_train, Y_train) * 100, 2)

86.760000000000005

 

經過比較模型的正確率,咱們決定使用最高正確率的模型,即隨機森林的輸出做爲結果提交。

 

12.模型評價

如今咱們能夠對以上的模型的正確率進行排名,並從中選擇一個正確率最高的模型:

 

models = pd.DataFrame({
    'Model': ['Support Vector Machines', 'KNN', 'Logistic Regression',
              'Random Forest', 'Naive Bayes', 'Perceptron',
              'Stochastic Gradient Decent', 'Linear SVC',
              'Decision Tree'],
    'Score': [acc_svc, acc_knn, acc_log,
              acc_random_forest, acc_gaussian, acc_perceptron,
              acc_sgd, acc_linear_svc, acc_decision_tree]})

 

>>> models.sort_values(by='Score', ascending=False)

                        Model  Score

3               Random Forest  86.76

8               Decision Tree  86.76

1                         KNN  84.74

0     Support Vector Machines  83.84

2         Logistic Regression  80.36

7                  Linear SVC  79.01

6  Stochastic Gradient Decent  78.23

5                  Perceptron  78.00

4                 Naive Bayes  72.28

 

其中決策樹與隨機森林的正確率最高,可是咱們在這裏會選擇隨機森林算法,由於它相對於決策樹來講,彌補了決策樹有可能過擬合的問題。

 

最後咱們作提交:

>>> submission = pd.DataFrame({"PassengerId": test_df["PassengerId"], "Survived": Y_pred})

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息