2.3.2 下载数据
我本来放在了这个位置:
即书上提供的github网址上直接下载的code包粘贴到了我的study文件夹。但后来还是换成了一下位置(直接在D盘),原因后续会提到:
注意在jupyter里面不要开启pytorch环境,因为我那里面还没pandas库:
按文章输入代码:
这里因为下载解压了数据文件,所以本地下载。当然没,没有按照书上的方法 选择此方法的最大原因是总是报错“URLError: <urlopen error [Errno 11004] getaddrinfo failed>”,经过搜搜发现是网络问题。将适配器ipv4的DNS修改为“114.114.114.114”、“8.8.8.8”、“8.8.4.4”等等都没有得到解决。打算连接vpn,却不知为何toline无法启动,遂作罢。
2.3.3 快速查看数据结构
1、查看前五行
2、获取数据集的简单描述
3、对object类型的属性ocean_proximity感兴趣,猜测是一个分类属性。想知道有多少种分类值:
4、显示数值属性的摘要:
使用 describe()
获取数值属性的描述,注意统计时的空值会被忽略。
5、绘制每个属性的直方图
绘制每个数值的直方图。直方图横轴表示数值范围,纵轴表示实例数量。这些直方图反映出很多问题。见p55页。
2.3.4 创建测试集
一、纯随机抽样
下面代码是随机选择一些实例创建测试集,选取数据集的20%(数据集越大,这个比例越小)。
但是这样每次运行都会产生一个不同的数据集,这样我的机器就会逐渐窥探到所有数据集,这是测试时不想看到的。
解决方案:每个实例都使用一个标识符,用以决定是否进入测试集。然后,可以计算每个实例这个标识符的哈希值(小于等于最大哈希值的20%就将其放入测试集)。这样的测试集在多个运行里都是一致的了,即使更新了数据集也一样——新的实例中有20%会被放入测试集,并不会将之前的实例放入新的测试集。
实现方式如下:
from zlib import crc32
def test_set_check(identifier, test_ratio):
return crc32(np.int64(identifier)) & 0xffffffff < test_ratio *2**32
def split_train_test_by_id(data, test_ratio, id_column):
ids = data[id_column]
in_test_set = ids.apply(lambda id_: test_set_check(id_, test_ratio))
return data.loc[~in_test_set], data.loc[in_test_set]
由于housing数据集没有设置标识符列,所以这里我们将行索引作为id:
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")
这有一个条件:确保在数据集的末尾加入新数据而不是插入(不能改变索引值),且不能删除任意一行。如果不能保证这点,那就试着利用万年不变的特征(本例中的经纬度)构造组合成一个id:
housing_with_id["id"] = housing["longitude"]*1000 + housing["latitude"]
train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, "index")
数据集拆分成多个子集:
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, 6]
之间,划分层次时,每层要有足够多的实例,因此,使用 pd.cut()
方法创建 5
个收入类别, 0-1.5
、 1.5-3
、 3-4.5
、 4.5-6
、 6- +∞
。
注意:每一层都要保证足够的样本数量,所以层数不应该太多。
下面是根据pd.cut()创建5个收入类别属性(标签1~5),0~1.5是类别1...类别如输出直方图所示:
housing["income_cat"] = pd.cut(housing["median_income"],
bins=[0., 1.5, 3.0, 4.5, 6., np.inf],
labels=[1, 2, 3, 4, 5])
# 根据income_cat数据绘制直方图
housing["income_cat"].hist()
我们根据 income_cat
列进行分层抽样,再使用 Scikit-learn
的 StratfiedShuffleSplit
类来实现。划分完测试集就可以将 income_cat
列删除。
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"]):
# train_index: [17606 18632 14650 ... 13908 11159 15775]
strat_train_set = housing.loc[train_index]
# test_index: [5241 10970 20351 ... 4019 12107 2398]
strat_test_set = housing.loc[test_index]
首先看一下测试集的收入类别比例分布(从多到少排序):
strat_test_set["income_cat"].value_counts() / len(strat_test_set)
好,现在可以删除income_cat属性,将数据恢复原样了:
for set_ in (strat_train_set, strat_test_set):
set_.drop("income_cat", axis=1, inplace=True)
这个shift+enter执行一次后就成功删除了。我这里忘记执行连按了两次,报错
KeyError: "['income_cat'] not found in axis
实际上它已经不存在了。除非在jupyter里面从from sklearn.model_selection import StratifiedShuffleSplit这一行开始重新执行回复income_cat再将其删除。
参数说明:
labels 就是要删除的行列的名字,用列表给定
axis 默认为0,指删除行,因此删除columns时要指定axis=1;
index 直接指定要删除的行
columns 直接指定要删除的列
inplace=False,默认该删除操作不改变原数据,而是返回一个执行删除操作后的新dataframe;
inplace=True,则会直接在原数据上进行删除操作,删除后无法返回。