特征工程与建模分析

奥斯汀动物中心是美国最大的无杀伤动物收容所,每年为18,000多只动物提供护理和庇护,并参与一系列县,市和全州保护和照护废弃物的举措,处于危险中,并放弃了动物。作为奥斯汀市开放数据计划的一部分,奥斯汀动物中心提供其收集的数据集,其中包含进入奥斯汀动物服务系统的动物的统计数据和结果。
Austin Animal Center Shelter Outcomes(奥斯汀动物中心避难所成果)数据集可以在Kaggle上找到。该数据集包含几种类型的动物和从10/1/2013到现在以每小时时间频率繁殖的栖息地结果。数据每天更新。
奥斯汀动物中心的原始数据集包括名称,出生日期,结果,动物类型,结果时的性别和年龄,品种和颜色等栏目。结果范围很广,包括收养和转移到其他庇护所等内容。
下面,我们就来分析分析此数据集。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
import seaborn as sns
%matplotlib inline
sns.set(color_codes=True)
plt.style.use("ggplot")
mpl.rcParams['xtick.labelsize'] = 16
mpl.rcParams['ytick.labelsize'] = 16
mpl.rcParams["axes.labelcolor"] = "k"
mpl.rcParams["axes.labelsize"] = 20
# parse_dates将columns变为时间序列
shelter_outcomes = pd.read_csv("E:\\database\\aac_shelter_cat_outcome_eng.csv", parse_dates=['date_of_birth', 'datetime'])
shelter_outcomes.head()

这里写图片描述

# 查看数据特征属性
shelter_outcomes.info()

这里写图片描述

# 去掉一些重复属性
shelter_outcomes = shelter_outcomes.drop(["animal_id", "monthyear", "name", "Spay/Neuter", "outcome_age_(years)", "dob_monthyear", "outcome_month", "outcome_year", "outcome_subtype"], axis=1)
shelter_outcomes.head()

这里写图片描述

# 查看每一特征的类别(注:pandas0.20.0版本以上才可以用这个函数)
shelter_outcomes.nunique()

这里写图片描述
可以看出此数据集的动物类型只有猫而且数量也为一只,所以,我们也将它们去除,只用知道此数据只是针对于小猫的收容就行了。下面我们继续分析。
对收容小猫的出生时间进行时间序列分析

shelter_outcomes["date_of_birth"].value_counts().sort_values().plot.line()

这里写图片描述
从图可以看出小猫居中出生在2014年到2018年。但是,此图不是很清晰。

shelter_outcomes["date_of_birth"].value_counts().resample("Y").sum().plot.line()

这里写图片描述
我们按照年份进行重采样,可以剔除很多不必要的噪声,得到了一条干净的曲线,我们可以清楚的看出:2015年小猫出生数量最多。

shelter_outcomes = shelter_outcomes.drop(["animal_type", "count"], axis=1)

可视化小猫最后被送出收容所的命运(outcome_types)

plt.figure(figsize=(12, 5))
sns.countplot(y=shelter_outcomes["outcome_type"].dropna(),
              palette="rainbow",
              order=shelter_outcomes["outcome_type"].value_counts().index)

这里写图片描述
可以看出小猫最后被转移和收养的处理方式最多。丢失的和被卖掉的最少。
可视化小猫最后被送出收容所的年龄区间(age_group)

plt.figure(figsize=(12, 6))
sns.countplot(y=shelter_outcomes["age_group"].dropna(),
              palette="Set3",
              order=shelter_outcomes["age_group"].value_counts().index)

这里写图片描述

plt.figure(figsize=(12, 6))
sns.countplot(y=shelter_outcomes["sex_upon_outcome"].dropna(),
              palette="Set3",
              order=shelter_outcomes["sex_upon_outcome"].value_counts().index)

这里写图片描述
我们再来看不同“性别”的小猫最后被处理的方式

shelter_outcomes["sex_upon_outcome"].unique()

array([‘Intact Male’, ‘Intact Female’, ‘Spayed Female’, ‘Unknown’,
‘Neutered Male’], dtype=object)

g = sns.FacetGrid(shelter_outcomes, row="sex_upon_outcome", aspect=5)
g.map(sns.countplot, 'outcome_type', palette="rainbow")

这里写图片描述
从整体来看除了完整的雌性小猫最多被转移了,其他的都是被领养的居多。

接下来我们看看这些小猫的品种
首先我们将domestic_breed和cfa_breed合并为一个新的列breed_origin(表明猫的品种是来自国内还是国外)

shelter_outcomes["breed_origin"] = shelter_outcomes["domestic_breed"]
shelter_outcomes.loc[shelter_outcomes["domestic_breed"] == 1, "breed_origin"] = "domestic_breed"
shelter_outcomes.loc[shelter_outcomes["domestic_breed"] == 0, "breed_origin"] = "foreign_breed"
shelter_outcomes["breed_origin"]
# 统计10只以上猫的品种的来源
shelter_outcomes.groupby(["breed_origin", "breed"]).size().sort_values(ascending=False)[:18]

这里写图片描述

breed = shelter_outcomes[["breed_origin", "breed"]]
plt.figure(figsize=(12, 6))
sns.countplot(y=breed["breed"],
              hue=breed["breed_origin"],
              order=breed["breed"].value_counts().index[:10])

这里写图片描述

shelter_outcomes['days_of_age'] = (shelter_outcomes["datetime"] - shelter_outcomes["date_of_birth"]).dt.days
# 统计收容小猫的天数
shelter_outcomes["days_of_age"].describe()

count 29421.000000
mean 533.636858
std 983.298965
min -97.000000
25% 64.000000
50% 108.000000
75% 448.000000
max 8036.000000
Name: days_of_age, dtype: float64
从上面统计来看最小值为负的,明显这是不对的。

shelter_outcomes[shelter_outcomes["days_of_age"] < 0] = 0
plt.subplots(figsize=(12, 6))
g = sns.boxplot(data=shelter_outcomes, 
                x="outcome_type", 
                y="days_of_age", 
                hue="sex_upon_outcome",
                palette="Set2")
labels = g.get_xticklabels()
g.set_xticklabels(labels, rotation=45)
plt.show()

这里写图片描述
我们再来看看小猫出收容所的星期数哪一天最多。

plt.figure(figsize=(12, 4))
sns.countplot(y=shelter_outcomes["outcome_weekday"].dropna(),
              palette="BuGn_r",
              order=shelter_outcomes["outcome_weekday"].value_counts().index)

这里写图片描述

plt.figure(figsize=(12,6))
sns.countplot(data=shelter_outcomes,
              x= "outcome_weekday",
              hue= "outcome_type",
              palette= "Set1")
plt.legend(loc='upper right')

这里写图片描述

g = sns.FacetGrid(shelter_outcomes[shelter_outcomes['days_of_age']<2000], 
                  hue="outcome_type", 
                  size=10)
g.map(sns.kdeplot, "days_of_age")
g.add_legend()
g.set(xlim=(0,1200), xticks=range(0,1200,365))
plt.show(g)

这里写图片描述


构建模型
先对数据预处理

# 看上面数据中特征属性,我们还可以再次删除没有用的特征。
shelter_outcomes.drop(['date_of_birth','datetime', 'outcome_age_(days)', 'dob_year', 'dob_month', 'breed2', 'cfa_breed', 'domestic_breed', 'coat_pattern', 'age_group'], axis=1, inplace=True)
shelter_outcomes.info()

这里写图片描述
color2缺失值太多,在去掉它

shelter_outcomes = shelter_outcomes.drop("color2", axis=1)
shelter_outcomes_new = shelter_outcomes.loc[shelter_outcomes["color"].isna() != 1,:]
shelter_outcomes_new.isnull().sum()

这里写图片描述
从上面可以看出outcome_type还有3个缺失值,因为缺失值较少,那么我们就用outcome_type类别最多的填充它。

shelter_outcomes_new.[:, "outcome_type"] = shelter_outcomes_new[:, "outcome_type"].fillna("Transfer") 

# 处理符号
shelter_outcomes_new["color"] = shelter_outcomes_new["color"].map(lambda x: str(x).strip().replace(" ", ""))
shelter_outcomes_new["color"] = shelter_outcomes_new["color"].apply(lambda x: str(x).replace("/", " "))
# 去除首字母大写
shelter_outcome_cols = ['age_upon_outcome', 'breed', 'color', 'outcome_type',
       'sex_upon_outcome', 'sex', 'Cat/Kitten (outcome)', 'sex_age_outcome', 
       'outcome_weekday', 'breed1', 'color1', 'coat', 'breed_origin']
for col in shelter_outcome_cols:
    shelter_outcomes_new[col] = shelter_outcomes_new[col].map(lambda x: str(x).lower())

将字符串转化为数值型,因为模型的输入数据只能是数值型

from sklearn.preprocessing import LabelEncoder

def encoder(df, columns):
    for col in columns:
        label_encoder = LabelEncoder()
        label_encoder.fit(df[col])
        df[col] = label_encoder.transform(df[col])
    return df
columns = ['age_upon_outcome', 'breed', 'color', 'outcome_type', 'sex_age_outcome', 
       'outcome_weekday', 'breed1', 'color1', 'coat', 'breed_origin',
       'sex_upon_outcome', 'sex', 'Cat/Kitten (outcome)']
data = encoder(df=shelter_outcomes_new, columns=columns)   
shelter_outcomes_new.head() 

这里写图片描述

from sklearn.model_selection import train_test_split
y = shelter_outcomes_new["sex_upon_outcome"]
X = shelter_outcomes_new.drop("sex_upon_outcome", axis=1)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0, test_size=0.2, stratify=y)
from sklearn.tree import DecisionTreeClassifier
# 首先使用缺省的决策树
dtree = DecisionTreeClassifier()
dtree.fit(X_train, y_train)
predictions = dtree.predict(X_test)
from sklearn.metrics import classification_report, confusion_matrix
print(classification_report(y_test, predictions))

这里写图片描述
咦,没有想到缺省的决策树的效果这么好,F1得分为1。(真是一场意外啊。)
我们再来看看特征重要性选择

# 查看特征重要性
df = pd.DataFrame({"feature": list(X_train.columns), "importance": list(dtree.feature_importances_.T)})
df.sort_values(by="importance", ascending= False)

这里写图片描述
我们现在换一换预测的类别,对outcome_type进行预测

y = shelter_outcomes_new["outcome_type"]
X = shelter_outcomes_new.drop("outcome_type", axis=1)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0, test_size=0.2, stratify=y)
dtree = DecisionTreeClassifier()
dtree.fit(X_train, y_train)
predictions = dtree.predict(X_test)
print(classification_report(y_test, predictions))
print("--------------------------------------------------------")
print("\n")
print("                  Confusion Matrix                  ")
print("\n")
cnf_matrix = confusion_matrix(y_test, predictions)
print(cnf_matrix)

这里写图片描述
这个效果就没有之前的好了,我们接下来看看特征重要性

df = pd.DataFrame({"feature": list(X_train.columns), "importance": list(dtree.feature_importances_.T)})
df.sort_values(by="importance", ascending= False)

这里写图片描述
我们将不是那么重要的特征除去掉,再做决策树建模

y = shelter_outcomes_new["outcome_type"]
X = shelter_outcomes_new.drop(["outcome_type", "sex_upon_outcome", "sex", "breed_origin", "Cat/Kitten (outcome)", 'Periods', "age_upon_outcome", "breed1", "breed", "Period Range"], axis=1)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0, test_size=0.2, stratify=y)
dtree = DecisionTreeClassifier()
dtree.fit(X_train, y_train)
predictions = dtree.predict(X_test)
print(classification_report(y_test, predictions))
print("--------------------------------------------------------")
print("\n")
print("                  Confusion Matrix                  ")
print("\n")
cnf_matrix = confusion_matrix(y_test, predictions)
print(cnf_matrix)

这里写图片描述
可以看出模型的F1得分有一点点提升。

df = pd.DataFrame({"Feature": list(X_train.columns), "Importance": list(dtree.feature_importances_.tolist())})
plt.subplots(figsize=(8, 6))
g = sns.barplot(data=df, x='Feature', y='Importance')
labels = g.get_xticklabels()
g.set_xticklabels(labels,rotation=45)

这里写图片描述
到此先告一段落,接下来也可以试试其他的分类模型,如随机森林、AdaBoost、GBDT等模型。

猜你喜欢

转载自blog.csdn.net/llh_1178/article/details/80168837