机器学习项目 - 预测住房价格 (1)

这个系列是把《机器学习实践》书上的项目,以自己理解的形式梳理一遍,算是对自己学习过程的总结。

0.项目介绍

书中第一个项目,内容包括获取数据、数据可视化、分析数据、切分数据集、数据清理、流水线过程、选择和训练模型,走了一遍做一个完整的机器学习模型的大致流程。

1.数据查看及分析

数据加载:

下面代码中有两个函数,fetch_housing_data是自动下载网络文件并存放在本地中,load_housing_data是获取文件数据。

import os
import tarfile
from six.moves import urllib
import pandas as pd

DOWNLOAD_ROOT = "https://raw.githubusercontent.com/ageron/handson-ml/master/"
HOUSING_PATH = "datasets/housing"
HOUSING_URL = DOWNLOAD_ROOT + HOUSING_PATH + "/housing.tgz"

def fetch_housing_data(housing_url=HOUSING_URL, housing_path=HOUSING_PATH):
    if not os.path.isdir(housing_path):
        os.makedirs(housing_path)
    tgz_path = os.path.join(housing_path, "housing.tgz")
    urllib.request.urlretrieve(housing_url, tgz_path)
    housing_tgz = tarfile.open(tgz_path)
    housing_tgz.extractall(path=housing_path)
    housing_tgz.close()

def load_housing_data(housing_path=HOUSING_PATH):
    csv_path = os.path.join(housing_path,"housing.csv")
    return pd.read_csv(csv_path)

数据查看:

housing = load_housing_data() #housing是pandas的DataFrame数据类型
housing.head() # 查看数据前5行
housing.info()  # 查看数据的整体基本信息
housing['ocean_proximity'].value_counts() # 查看单一列的统计信息
housing.describe() #查看包括std(标准差)、mean、max、min、不同比例值的信息,如下图
数据的描述信息

切分数据:

要把数据切分成train_set(训练集)和test_set(测试集),有两种方式:随机切分、按层级切分。随机切分效果不好,一般是按层级切分,尽量保证测试集适应数据的特点。

随机切分(不推荐):
import numpy as np
import hashlib

# 1、手动划分数据集-纯随机方式
# 通过每一行数据的唯一标识id,取hash值,取hash值的最后一位是在0~255的值,如果它在256*测试比例大小内,那标记为True,即为测试集中的数据
def test_set_check(identifier,test_ratio,hash):
    return hash(np.int64(identifier)).digest()[-1] < 256 * test_ratio

def split_train_test_by_id(data, test_ratio, id_column, hash=hashlib.md5):
    ids = data[id_column]
    in_test_set = ids.apply(lambda id_: test_set_check(id_,test_ratio,hash))
    return data.loc[~in_test_set], data.loc[in_test_set]

housing_with_id = housing.reset_index()
train_set,test_set = split_train_test_by_id(housing_with_id, 0.2, "index")

# 2、使用Scikit-Learn来划分数据集-纯随机方式
from sklearn.model_selection import train_test_split
train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)
按层级切分(推荐):
# 3、将数据集进行分层抽取测试集和训练集,依据的标准是收入平均数,因为预测房价,收入中位数是个非常关键的属性。
# i:数据分析:从收入图上来看,数据集中在2~5之间,如果要按层级抽样,那每一个层级应该足够大最好。
#            书上是这样处理的,先把收入/1.5(限制类别的数量),并且取整,再把大于5的数据归到5的层级中。

housing['income_cat'] = np.ceil(housing['median_income'] / 1.5)
housing['income_cat'].where(housing['income_cat'] < 5, 5.0, inplace=True)

# 4、进行分层抽样
from sklearn.model_selection import StratifiedShuffleSplit
split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
for train_index, test_index in split.split(housing, housing['income_cat']):
    print(len(train_index))
    print(len(test_index))
    strat_train_set = housing.loc[train_index]
    strat_test_set = housing.loc[test_index]

以某属性为标准的前提下,按层级切分可使测试集最大满足数据的分布特点。可以通过对比完整数据集和测试集的分布概率来检验。如下图:
完整数据集和测试集的分布概率

数据可视化:

  • 把所有列按直方图形式查看:
%matplotlib inline
import matplotlib.pyplot as plt
housing.hist(bins=50,figsize=(20,25))
plt.show
#把所有列以直方图形式显示
  • 将地理数据可视化
#1.将地理数据可视化
housing = strat_train_set.copy()
housing.plot(kind='scatter',x='longitude',y='latitude', alpha=0.4,
            s=housing['population']/100, label='population',
            c='median_house_value',cmap=plt.get_cmap('jet'), colorbar=True)
plt.legend()
地理数据可视化

数据探索

  • 寻找数据属性间的相关性,又称皮尔逊相关性
# 查看所有属性跟房价中位数的相关性,范围-1~1,负数代表负相关
corr_matrix = housing.corr()
corr_matrix['median_house_value'].sort_values(ascending=False)
属性跟房价中位数的相关性
  • 尝试不同的属性组合
#尝试不同属性的组合
housing['rooms_per_household'] = housing['total_rooms']/housing['households']
housing['bedrooms_per_room'] = housing['total_bedrooms']/housing['total_rooms']
housing['population_per_household'] = housing['population']/housing['households']

corr_matrix = housing.corr()
corr_matrix['median_house_value'].sort_values(ascending=False)
#可以看到bedrooms_per_room比total_rooms和total_bedrooms的系数都高的多。
属性组合的相关性

2、机器学习算法的数据准备

这一部分最好要用函数,甚至流水线程序来执行,方便之后的项目重用。

housing = strat_train_set.drop('median_house_value', axis=1)
housing_labels = strat_train_set['median_house_value'].copy()

处理数据缺失

数据中当出现某些值缺失时,要考虑将其填充成平均值或者直接舍弃。


# 丢弃有三种方式:
# housing.dropna(subset=['total_bedrooms'])
# housing.drop('total_bedrooms', axis=1)
# median = housing['total_bedrooms'].median()
# housing['total_bedrooms'].fillna(median)

# 填充一般是按中位数填充
from sklearn.preprocessing import Imputer
imputer = Imputer(strategy='median')

housing_num = housing.drop('ocean_proximity', axis=1)
imputer.fit(housing_num)
# imputer.statistics_ 
# housing_num.median().values
X = imputer.transform(housing_num)
housing_tr = pd.DataFrame(X, columns=housing_num.columns)

处理文本和分类属性

有些属性的值不是数字,这时候最好把文本值转换成数字,方便之后的处理

from sklearn.preprocessing import LabelEncoder
encoder = LabelEncoder() #转换器
housing_cat = housing['ocean_proximity']
housing_cat_encoded = encoder.fit_transform(housing_cat)
housing_cat_encoded
所有转换后的数值
#查看所有类型
print(encoder.classes_)
转换钱的所有文本值类型

OneHot编码

如果只是把数据转换成0,1,2...这种连续的数字,机器学习算法会默认0和4的距离比2和3的距离要远,但在文本类型中不存在这种关系,所以要转成01编码,以10000,01000,00100...的这种方式,即为OneHot编码。最终为二维数组

from sklearn.preprocessing import OneHotEncoder
oneHotEncoder = OneHotEncoder()
housing_cat_1hot = oneHotEncoder.fit_transform(housing_cat_encoded.reshape(-1,1))
housing_cat_1hot.toarray()

可以一次性转换成OneHot形式:

from sklearn.preprocessing import LabelBinarizer
encoder = LabelBinarizer()
housing_cat_1hot = encoder.fit_transform(housing_cat)

特征缩放

两种方式:最小最大值缩放和标准化。
标准化受数据异常值的影响较小。

可以自定义转换器

scikit-learn中,可以创建自定义转换器,只要类中应用这三个方法:fit()、transform()、fit_transform()。如下代码:

#自定义转换器
from sklearn.base import BaseEstimator, TransformerMixin
rooms_ix, bedroom_ix, population_ix, household_ix = 3,4,5,6

class CombinedAttributesAdder(BaseEstimator, TransformerMixin):
    def __init__(self, add_bedrooms_per_room = True):
        self.add_bedrooms_per_room = add_bedrooms_per_room
    def fit(self, X, y=None):
        return self
    def transform(self, X, y=None):
        rooms_per_household = X[:, rooms_ix] / X[:, household_ix]
        population_per_household = X[:, population_ix] / X[:, household_ix]
        if self.add_bedrooms_per_room:
            bedrooms_per_room = X[:, bedroom_ix] / X[:, rooms_ix]
            return np.c_[X, rooms_per_household, population_per_household, bedrooms_per_room]
        else:
            return np.c_[X, rooms_per_household, population_per_household]
attr_adder = CombinedAttributesAdder(add_bedrooms_per_room=False) #为了将准备步骤更自动化,添加这个属性Flag
housing_extra_attribs = attr_adder.transform(housing.values)
        
class DataFrameSelector(BaseEstimator, TransformerMixin):
    def __init__(self, attribute_names):
        self.attribute_names = attribute_names
    def fit(self, X, y=None):
        return self
    def transform(self, X):
        return X[self.attribute_names].values

转换流水线

可以用流水线的方式来顺序执行数据处理步骤。

from sklearn.pipeline import FeatureUnion
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

num_attribs = list(housing_num)
cat_attribs = ['ocean_proximity']

num_pipeline = Pipeline([
    ('selector', DataFrameSelector(num_attribs)),
    ('imputer', Imputer(strategy='median')),
    ('attribs_adder', CombinedAttributesAdder()),
    ('std_scaler', StandardScaler())
])

cat_pipeline = Pipeline([
    ('selector', DataFrameSelector(cat_attribs)),
    ('label_binarizer', LabelBinarizer())
])

full_pipeline = FeatureUnion(transformer_list=[
    ('num_pipeline', num_pipeline),
    ('cat_pipeline', cat_pipeline)
])

housing_prepared = full_pipeline.fit_transform(housing)
housing_prepared.shape

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342