一、下载数据
import os
import tarfile
from six.moves import urllib
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()
fetch_housing_data()
现在,每当你调用fetch_housing_data(),会自动在工作区创建一个datasets/housing目录,然后下载housing.tgz文件,并将housing.csv解压到这个目录。
现在用pandas加载数据:
import pandas as pd
def load_housing_data(housing_path=HOUSING_PATH):
csv_path=os.path.join(housing_path,"housing.csv")
return pd.read_csv(csv_path)
这个函数会返回一个包含所有数据的Pandas DataFrame对象。
二、快速查看数据结构
使用DataFrame的head()方法查看前五行:
housing = load_housing_data()
housing.head()
每一行代表一个区,总共有10个属性:longitude,latitude,housing_median_age,total_rooms,total_bed_rooms,population,households,meidan_income,
median_house_value以及ocean_prroximity
通过info()方法可以快速捕获数据集的简单描述,特别是总行数,每个属性的类型和非空值的数量。
housing.info()
使用value_counts()方法查看有多少种分类存在,每种类别下分别有多少区域:
housing["ocean_proximity"].value_counts()
housing.describe()
另一种快速了解数据类型的方法是绘制每个数值属性的直方图。直方图用来显示给定值的范围(横轴)的实例数量(纵轴)。你可以一次绘制一个属性,也可以在整个数据集上调用hist()方法,绘制每个属性的直方图。
import matplotlib.pyplot as plt
housing.hist(bins=50,figsize=(20,15))
plt.show()
三、创建测试集
理论上,创建测试集非常简单:只要随机选择一些实例,通常是数据集的20%,然后将它们放在一边:
import numpy as np
def split_train_test(data,test_ratio):
shuffled_indices = np.random.permutation(len(data))
test_set_size = int(len(data)*test_ratio)
test_indices = shuffled_indices[:test_set_size]
train_indices = shuffled_indices[test_set_size:]
return data.iloc[train_indices], data.iloc[test_indices]
train_set, test_set = split_train_test(housing, 0.2)
print(len(train_set), "train+", len(test_set), "test")
但这样子并不完美:如果在运行一遍,它会产生一个不同的数据集!
解决方案有两个:1.在第一次运行程序之后即保存测试集,随后的运行只是加载它而已
2.在调用np.random.permutation()之前设置一个随机数生成器的种子(例如,np.random.seed(42)),从而让他始终生成相同的碎机索引。
但是这两种方法在获得下一次更新的数据时都会中断。
解决办法:每个实例都使用一个标识符(identifier)来决定是否进入测试集(假定每个实例都有一个唯一且不变的标识符)。
举例来说:可以计算每个实例标识符的hash值,只取hash的最后一个字节如果该值小于等于51(约256的20%),则将实例放入测试集中。
import hashlib
def test_set_check(identifier, test_ratio, hash):
return hash(np.int64(identifer)).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() #adds an 'index'column
train_set, test_set = split_train_test_by_id(housing_with_id,0.2,"index")
如果使用行索引作为唯一标识符,你需要确保在数据集的末尾添加新数据,并且不会删除任何行。如果不能保证这点,可以尝试使用某个最稳定的特征来创建唯一的标识符。例如一个地区的经纬度:
housing_with_id["id"] = housing["longitude"]*1000 + housing["latitude"]
train_set, test_set = split_train_test_by_id(housing_with_id,0.2,"id")
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)
分层抽样:将人口划分为均匀的子集,每个子集被称为一层,然后从每层抽取正确的实例数量,以确保测试集和代表了总的人口比例。
创建收入类别属性:
将收入中位数除以1.5(限制收入类别的数量),然后使用ceil进行取整(得到离散类别),然后将所有大于5的类别合并为类别5:
housing["income_cat"] = np.ceil(housing["median_income"]/1.5)
housing["income_cat"].where(housing["income_cat"]<5,5.0, inplace=True)
现在可以根据收入类别进行分层抽样,使用Scikit-Learn的Stratified Shuffle Split类:
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"]):
strat_train_set = housing.loc[train_index]
strat_test_set = housing.loc[test_index]