精华内容
下载资源
问答
  • 中位数具有唯一性吗
    千次阅读
    2018-10-15 23:02:08

    在项目开发中,我们常常会需要生成一些包含随机数字或字符、并在项目中具有唯一性的数字或字符串,如订单号、收支流水号等等,方法有很多,以下分享我的一种思路。

     

    
        /**
         * 随机生成流水号函数:
         * @id int 数据表中具有唯一性的ID号
         * @return string 返回具有唯一性的22位纯数字字符串
         */
        public function getFlowNumber($id)
        {
            $str = date('YmdHis') . substr($id . rand(100000000, 9999999999), 0, 8);
            return $str;
        }
    
    

     

    说明:

    1,由于传入的$id本身取自数据表中的唯一ID,事实上相同帐号操作生成的字符串基本上可以保证唯一性了,即使多点登陆,同时操作生成,重复的概率也较低,当然,后边再拼接随机数并取其中的8位,其唯一性应该是可以保证的;

    2,如果需要的是数字字母混合的字符串,也可以使用其它随机种子或MD5等方式生成随机部分的字符串;

    3,写入数数据库时注意选用适当的数据类型;

    如果文章对你有帮助,请打开支付宝搜索535149388,领取马云的红包,使用余额宝消费可抵扣,大家好,才是真的好。

     

     

    更多相关内容
  • 一组数据中如果有特别大的数或特别小的数时,一般用中位数 一组数据比较多(20个以上),范围比较集中,一般用众数 其余情况一般还是平均数比较精确 一、联系与区别:  1、平均数是通过计算得到的,因此它会因...

    原文链接:http://www.360doc.com/content/18/0717/09/57858800_771067787.shtml

    个人理解,说简单点:
    一组数据中如果有特别大的数或特别小的数时,一般用中位数
    一组数据比较多(20个以上),范围比较集中,一般用众数
    其余情况一般还是平均数比较精确

    一、联系与区别:

      1、平均数是通过计算得到的,因此它会因每一个数据的变化而变化。

      2、中位数是通过排序得到的,它不受最大、最小两个极端数值的影响.中位数在一定程度上综合了平均数和中位数的优点,具有比较好的代表性。部分数据的变动对中位数没有影响,当一组数据中的个别数据变动较大时,常用它来描述这组数据的集中趋势。另外,因中位数在一组数据的数值排序中处中间的位置,

      3、众数也是数据的一种代表数,反映了一组数据的集中程度.日常生活中诸如“最佳”、“最受欢迎”、“最满意”等,都与众数有关系,它反映了一种最普遍的倾向.

    二、平均数、中位数和众数它们都有各自的的优缺点.

    平均数:
    (1)需要全组所有数据来计算;
    (2)易受数据中极端数值的影响.

    中位数:
    (1)仅需把数据按顺序排列后即可确定;
    (2)不易受数据中极端数值的影响.

    众数:
    (1)通过计数得到;
    (2)不易受数据中极端数值的影响

    关于“中位数、众数、平均数”这三个知识点的理解,我简单谈谈自己的认识和理解。
    ⒈众数。
    一组数据中出现次数最多的那个数据,叫做这组数据的众数。
    ⒉众数的特点。
    ①众数在一组数据中出现的次数最多;②众数反映了一组数据的集中趋势,当众数出现的次数越多,它就越能代表这组数据的整体状况,并且它能比较直观地了解到一组数据的大致情况。但是,当一组数据大小不同,差异又很大时,就很难判断众数的准确值了。此外,当一组数据的那个众数出现的次数不具明显优势时,用它来反映一组数据的典型水平是不大可靠的。
    3.众数与平均数的区别。
    众数表示一组数据中出现次数最多的那个数据;平均数是一组数据中表示平均每份的数量。
    4.中位数的概念。
    一组数据按大小顺序排列,位于最中间的一个数据(当有偶数个数据时,为最中间两个数据的平均数)叫做这组数据的中位数。
    5.众数、中位数及平均数的求法。
    ①众数由所给数据可直接求出;②求中位数时,首先要先排序(从小到大或从大到小),然后根据数据的个数,当数据为奇数个时,最中间的一个数就是中位数;当数据为偶数个时,最中间两个数的平均数就是中位数。③求平均数时,就用各数据的总和除以数据的个数,得数就是这组数据的平均数。
    6.中位数与众数的特点。
    ⑴中位数是一组数据中唯一的,可能是这组数据中的数据,也可能不是这组数据中的数据;
    ⑵求中位数时,先将数据有小到大顺序排列,若这组数据是奇数个,则中间的数据是中位数;若这组数据是偶数个时,则中间的两个数据的平均数是中位数;
    ⑶中位数的单位与数据的单位相同;
    ⑷众数考察的是一组数据中出现的频数;
    ⑸众数的大小只与这组数的个别数据有关,它一定是一组数据中的某个数据,其单位与数据的单位相同;
    (6)众数可能是一个或多个甚至没有;
    (7)平均数、众数和中位数都是描述一组数据集中趋势的量。
    7.平均数、中位数与众数的异同:
    ⑴平均数、众数和中位数都是描述一组数据集中趋势的量;
    ⑵平均数、众数和中位数都有单位;
    ⑶平均数反映一组数据的平均水平,与这组数据中的每个数都有关系,所以最为重要,应用最广;
    ⑷中位数不受个别偏大或偏小数据的影响;
    ⑸众数与各组数据出现的频数有关,不受个别数据的影响,有时是我们最为关心的数据。
    8.统计量。
    平均数、众数和中位数都叫统计量,它们在统计中,有着广泛的应用。
    9.举手表决法。
    在生活中,往往会有由多数人来从众多答案中选择一个的情形,一般都利用“举手表决”方式来解决问题。即在统计出所有提议及相应票数的情况下,看各票数的众数是否超过总票数的一半,如果众数超过了总票数的一半,选择的最终答案就是这个众数。如果出现了双众数(两个众数),可对这两个众数采用抓阄、抽签或投掷硬币等办法选出最终的答案。
    10.平均数、众数和中位数三种统计数据在生活中的意义。
    平均数说明的是整体的平均水平;众数说明的是生活中的多数情况;中位数说明的是生活中的中等水平。
    11.如何通过平均数、众数和中位数对表面现象到背景材料进行客观分析。
    在个别的数据过大或过小的情况下,“平均数”代表数据整体水平是有局限性的,也就是说个别极端数据是会对平均数产生较大的影响的,而对众数和中位数的影响则不那么明显。所以,这时要用众数活中位数来代表整体数据更合适。即:如果在一组相差较大的数据中,用中位数或众数作为表示这组数据特征的统计量往往更有意义。

    算数平均数、中位数与众数——统计量背后的故事

    现代经济社会的数字化程度越来越高,我们会发现在我们生活的这个世界里充斥着各种各样的数字。人们在描述事物或过程时,人们也已经习惯性的偏好于接受数字信息以及对于各种数字的整理和分析。因此,社会经济统计越发的重要。统计学一定是基于现实经济社会发展的需要牵引而不断发展的。在运用统计方法、观察统计数字时不能仅仅看到数字,更要看到数字背后的故事。其实统计学作为一门工具能够帮助我们更为深刻的理解抽象的社会经济现象。当我们仔细发掘其中涵义就会发现,其实自然科学与社会科学并不是相隔千里,它们有着很多地方可以相互的对应,存在普遍而深刻的联系。
    笔者曾在为一些本科学生讲授统计学而准备教案时,产生了一些似乎有些勉强,但的确可以训练思维的想法。下面以对于如何理解“算数平均数、中位数与众数”之间的关系为例说一说统计量背后的故事。这三个统计量都是用来描述样本集中趋势的,但三者描述的机制和所表达出来的内涵有不小的区别。算数平均数这样一个统计量反映了样本内所有个体的信息,尽管反映的程度因个体在整体中所占比重不同而不同。在政治过程中,算数平均数与完全的平均主义、严格的每人一票、“全民公投”等相对应。中位数指的在是从小到大排序之后的样本序列中,位于中间的数值,它并不能反映所有样本个体的信息,仅仅考虑的是在相对位置上中间的样本的信息。在一个社会中,按照财富和社会地位进行排序位于中间位置的是中产阶级。中产阶级的意见受到重视的社会是一个较为稳定的社会,是一个有了较高发展程度的社会。众数指的则是在样本中出现次数做多的个体。很明显,在政治过程中这是与“少数服从多数”相对应的。出现次数最多的个体信息被表达出来,其他个体的所有信息完全被忽视。那个个体票数最多,它的利益得以实现,而少数人的利益则不能够得到保证。这恰恰证明了所谓民主的局限之一,即“多数人对少数人的暴政”。
    在一个社会里,完全的平均主义会使人们失去进取的动力,“全民公投”的成本极高并且也不能保证个体表达出其真实意愿,因此这并不是理想的政治过程。在改革开放之前实行的计划经济体制最终走下了历史舞台也正是因为我们清楚地认识到了这样的问题;我们反对台湾当局针对台湾是否独立实行“全民公投”也正是基于这一点。那么美国式的民主,即“少数服从多数”是否理想呢?民主是有局限性的,如此的政治过程不能够保护少数人的利益,正是其重要的缺陷之一。况且如果需要政府来保障那些不能通过政治过程实现自身利益的个体,成本极高。相对而言,使中产阶级的利益得以表达,将会形成一个稳定的社会结构,市较为理想的政治过程。人们会有不断进取的心态使自己成为中产阶级,同时最富裕的阶层也受到了一定限制,从而不会凭借其财富垄断社会的公共资源,为整个社会提供了一套阶层之间相互流动的渠道和机制。当然,如此的政治过程仍然是具有一定局限性的。比如仍然会有部分弱势群体的利益得不到保护。但是,相对于“少数服从多数”的政治过程,政府出面保护弱势群体的成本将低得多了。那么我们能不能为社会提供一个最为理想的政治过程呢,哪怕那仅仅是一种理想呢?或许可以。在统计学中,最理想的情况是反映集中趋势的三个统计量相互重合,即算数平均数、中位数和众数相等。这种情况下的社会结构分布可以被看作为正态分布。中产阶级的在数量上占整体的多数,即为富裕与极贫困者皆为少数;中产阶级通过民主的政治过程表达出自身的利益取向;平均看来整个社会在一个较高的发展水平上运行。

    教参上说了他们三者的联系

    “重视理解平均数、中位数与众数的联系与区别。
    描述一组数据的集中趋势,可以用平均数、中位数和众数,它们有各自不同的特点。
    平均数应用最为广泛,用它作为一组数据的代表,比较可靠和稳定,它与这组数据中的每一个数据都有关系,能够最为充分地反映这组数据所包含的信息,在进行统计推断时有重要的作用;但容易受到极端数据的影响。
    中位数在一组数据的数值排序中处于中间的位置,故其在统计学分析中也常常扮演着“分水岭”的角色,人们由中位数可以对事物的大体趋势进行判断和掌控。
    众数着眼于对各数据出现的频数的考察,其大小仅与一组数据中的部分数据有关,当一组数据中有不少数据多次重复出现时,它的众数往往是我们关心的一种统计量。
    在这部分知识的教学中,要注意讲清上述三个量的联系与区别。使学生知道它们都是描述一组数据集中趋势的统计量,但描述的角度和适用范围有所不同,在具体的问题中究竟采用哪种统计量来描述一组数据的集中趋势,要根据数据的特点及我们所关心的问题来确定。”

    有个顺口溜 分析数据平中众,比较接近选平均,相差较大看中位,频数较大用众数;
       所有数据定平均,个数去除数据和,即可得到平均数;大小排列知中位;
       整理数据顺次排,单个数据取中问,双个数据两平均;频数最大是众数

    展开全文
  • 如何生成具有唯一性的uuid

    千次阅读 2018-05-31 16:23:32
    Python使用UUID库生成唯一ID UUID是128的全局唯一标识符... 它可以保证时间和空间的唯一性,也称为GUID,全称为: UUID —— Universally Unique IDentifier Python 叫 UUID GUID —— Globally Unique IDenti...

    Python使用UUID库生成唯一ID

        UUID是128位的全局唯一标识符,通常由32字节的字符串表示。
        它可以保证时间和空间的唯一性,也称为GUID,全称为:
                UUID —— Universally Unique IDentifier      Python 中叫 UUID
                GUID —— Globally Unique IDentifier          C#  中叫 GUID
    
        它通过MAC地址、时间戳、命名空间、随机数、伪随机数来保证生成ID的唯一性。
        UUID主要有五个算法,也就是五种方法来实现:
    
           1、uuid1()——基于时间戳
    
                   由MAC地址、当前时间戳、随机数生成。可以保证全球范围内的唯一性,
                   但MAC的使用同时带来安全性问题,局域网中可以使用IP来代替MAC。
    
           2、uuid2()——基于分布式计算环境DCE(Python中没有这个函数)
    
                    算法与uuid1相同,不同的是把时间戳的前4位置换为POSIX的UID。
                    实际中很少用到该方法。
    
          3、uuid3()——基于名字的MD5散列值
    
                    通过计算名字和命名空间的MD5散列值得到,保证了同一命名空间中不同名字的唯一性,
                    和不同命名空间的唯一性,但同一命名空间的同一名字生成相同的uuid。    
    
           4、uuid4()——基于随机数
    
                    由伪随机数得到,有一定的重复概率,该概率可以计算出来。
    
           5、uuid5()——基于名字的SHA-1散列值
    
                    算法与uuid3相同,不同的是使用 Secure Hash Algorithm 1 算法
    
    使用方面:
    
        首先,Python中没有基于DCE的,所以uuid2可以忽略;
        其次,uuid4存在概率性重复,由无映射性,最好不用;
        再次,若在Global的分布式计算环境下,最好用uuid1;
        最后,若有名字的唯一性要求,最好用uuid3或uuid5。
    
    编码方法:
        import uuid
    
    
        print(uuid.uuid1())  # 带参的方法参见Python Doc
        print(uuid.uuid1())  # 带参的方法参见Python Doc
        print(uuid.uuid1())  # 带参的方法参见Python Doc
        print(uuid.uuid1())  # 带参的方法参见Python Doc
        print(uuid.uuid1())  # 带参的方法参见Python Doc
        print(uuid.uuid1())  # 带参的方法参见Python Doc


    
       
    展开全文
  • 如果你咨询专家,他们会告诉你,要预测房价中位数,收入中位数是个非常重要的属性。于是你希望确保在收入属性上,测试集能够代表整个数据集中各种不同类型的收入。 我们由上面直方图可以看到:大部分收入中位数值...

    参照《机器学习实战》第二版

    1、下载数据

    import os
    import tarfile
    import urllib.request
    
    DOWNLOAD_ROOT = "https://raw.githubusercontent.com/ageron/handson-ml2/master/"
    HOUSING_PATH = os.path.join("datasets", "housing")
    HOUSING_URL = DOWNLOAD_ROOT + "datasets/housing/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()
    

    2、读取下载的数据

    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)
    

    2.1、数据显示

    housing = load_housing_data()
    housing.head()  # 默认显示前五列
    
    longitudelatitudehousing_median_agetotal_roomstotal_bedroomspopulationhouseholdsmedian_incomemedian_house_valueocean_proximity
    0-122.2337.8841.0880.0129.0322.0126.08.3252452600.0NEAR BAY
    1-122.2237.8621.07099.01106.02401.01138.08.3014358500.0NEAR BAY
    2-122.2437.8552.01467.0190.0496.0177.07.2574352100.0NEAR BAY
    3-122.2537.8552.01274.0235.0558.0219.05.6431341300.0NEAR BAY
    4-122.2537.8552.01627.0280.0565.0259.03.8462342200.0NEAR BAY

    2.2、查看每列属性

    housing.info()
    
    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 20640 entries, 0 to 20639
    Data columns (total 10 columns):
     #   Column              Non-Null Count  Dtype  
    ---  ------              --------------  -----  
     0   longitude           20640 non-null  float64
     1   latitude            20640 non-null  float64
     2   housing_median_age  20640 non-null  float64
     3   total_rooms         20640 non-null  float64
     4   total_bedrooms      20433 non-null  float64
     5   population          20640 non-null  float64
     6   households          20640 non-null  float64
     7   median_income       20640 non-null  float64
     8   median_house_value  20640 non-null  float64
     9   ocean_proximity     20640 non-null  object 
    dtypes: float64(9), object(1)
    memory usage: 1.6+ MB
    

    2.3、查看某列数值统计

    housing["ocean_proximity"].value_counts()
    
    <1H OCEAN     9136
    INLAND        6551
    NEAR OCEAN    2658
    NEAR BAY      2290
    ISLAND           5
    Name: ocean_proximity, dtype: int64
    

    2.4、查看数值列属性摘要

    housing.describe()
    
    longitudelatitudehousing_median_agetotal_roomstotal_bedroomspopulationhouseholdsmedian_incomemedian_house_value
    count20640.00000020640.00000020640.00000020640.00000020433.00000020640.00000020640.00000020640.00000020640.000000
    mean-119.56970435.63186128.6394862635.763081537.8705531425.476744499.5396803.870671206855.816909
    std2.0035322.13595212.5855582181.615252421.3850701132.462122382.3297531.899822115395.615874
    min-124.35000032.5400001.0000002.0000001.0000003.0000001.0000000.49990014999.000000
    25%-121.80000033.93000018.0000001447.750000296.000000787.000000280.0000002.563400119600.000000
    50%-118.49000034.26000029.0000002127.000000435.0000001166.000000409.0000003.534800179700.000000
    75%-118.01000037.71000037.0000003148.000000647.0000001725.000000605.0000004.743250264725.000000
    max-114.31000041.95000052.00000039320.0000006445.00000035682.0000006082.00000015.000100500001.000000
    1. 上面看到total_bedrooms这一列的count的数值为20433而不是20640,是因为不统计为空的单元格,所以后面需要处理为空的数据。
    2. std行:显示的是标准差,用来测量数值的离散程度,也就是方差的平方根,一般符合高斯分布
    3. 25%、50%、75%:显示相应的百分位数,表示一组观测值中给定百分比的观测值都低于该值;50% 即 中位数。

    2.5、快速了解数组类型的方法(直方图)

    %matplotlib inline
    import matplotlib.pyplot as plt
    housing.hist(bins=50, figsize=(20, 15))
    plt.show()
    

    请添加图片描述

    3、创建测试集

    理论上,创建测试集非常简单,只需要随机选择一些实例,通常是数据集的20%(如果数据量巨大,比例将更小)

    • 为了即使在更新数据集之后也有一个稳定的训练测试分割,常见的解决方案是每个实例都使用一个标识符来决定是否进入测试集(假定每个实例都一个唯一且不变的标识符)
    • 你可以计算每个实例的标识符的哈希值,如果这个哈希值小于或等于最大哈希值的20%,则将该实例放入测试集。这样可以保证测试集在多个运行里都是一致的,即便更新数据集也依然一致。新实例的20%将被放如新的测试集,而之前训练集中的实例也不会被放入新测试集。

    3.1、手动随机生成

    from zlib import crc32
    import numpy as np
    
    
    def test_set_check(identifier, test_ratio):
        return crc32(np.int64(identifier)) & 0xffffffff < test_ratio * 2**32
    
    
    def splet_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()
    train_set, test_set = splet_train_test_by_id(housing_with_id, 0.2, "index")
    

    3.2、使用 Scikit-Learn 提供的方法 train_test_split 随机生成

    • 最简单的方法就是使用:train_test_split(),它与前面定义的 split_train_test() 几乎相同,除了几个额外特征。
    from sklearn.model_selection import train_test_split
    
    # random_state: 设置随机生成器种子
    train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)
    
    • 到目前为止,我们考虑的是纯随机的抽样方法。如果数据集足够庞大(特别是相较于属性的数量而言),这种方法通常不错
    • 如果不是,则有可能会导致明显的抽样偏差。即 应该按照比例分层抽样。

    如果你咨询专家,他们会告诉你,要预测房价中位数,收入中位数是个非常重要的属性。于是你希望确保在收入属性上,测试集能够代表整个数据集中各种不同类型的收入。

    我们由上面直方图可以看到:大部分收入中位数值聚集在1.5~6左右,但也有一部分超过了6,在数据集中,每个层都要有足够数量的数据,这一点至关重要,不然数据不足的层,其重要程度佷有可能会被错估。

    3.3、使用 Scikit-Learn 提供的方法 StratifiedShuffleSplit 按类别比例生成

    # 用 pd.cut() 来创建 5个收入类别属性(用 1~5 来做标签),0~1.5是类别 1, 1.5~3是类别2
    # np.inf 代表无穷大
    housing["income_cat"] = pd.cut(housing["median_income"],
                                   bins=[0, 1.5, 3, 4.5, 6, np.inf],
                                   labels=[1, 2, 3, 4, 5])
    
    housing["income_cat"].hist()
    

    请添加图片描述
    现在根据收入类进行分层抽样,使用 Scikit-Learn 的 StratifiedShuffleSplit

    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]
    

    看看上面运行是否如我们所料

    compare_pd = pd.DataFrame()
    # 全部数据:按收入分类的比例
    compare_pd["全部数据"] = housing["income_cat"].value_counts() / len(housing)
    # 按收入分类的比例 获取测试集比例
    compare_pd["分类抽样"] = strat_test_set["income_cat"].value_counts() / len(strat_test_set)
    # 随机获取测试集比例
    _, test_set = train_test_split(housing, test_size=0.2, random_state=42)
    compare_pd["随机抽样"] = test_set["income_cat"].value_counts() / len(test_set)
    
    compare_pd
    
    全部数据分类抽样随机抽样
    30.3505810.3505330.358527
    20.3188470.3187980.324370
    40.1763080.1763570.167393
    50.1144380.1145830.109496
    10.0398260.0397290.040213

    由上面数据我们看到,随机抽样的测试集,收入类别比例分布有些偏差。

    现在可以删除 income_cat 属性,将数据恢复原样了:

    for set_ in (strat_train_set, strat_test_set):
        set_.drop("income_cat", axis=1, inplace=True)
    

    我们花了相当长的时间在测试集的生成上,理由很充分:这是及机器学习项目中经常忽视但是却至关重要的一部分。并且,当讨论到交叉验证时,这里谈到的许多想法也对其大有裨益。

    4、从数据探索和可视化中获取洞见

    如果训练集非常庞大,你可以抽样一个探索集,这样后面的操作更简单快捷一些,不过我们这个案例的数据集非常小,完全可以直接在整个训练集上操作。让我们先创建一个副本,这样可以随便尝试而不损害训练集:

    housing = strat_train_set.copy()
    

    4.1、将地理数据可视化

    housing.plot(kind="scatter", x="longitude", y="latitude")
    

    在这里插入图片描述

    # alpha=0.1 可以更清楚的看出高密度数据点的位置
    housing.plot(kind="scatter", x="longitude", y="latitude", alpha=0.1)
    

    在这里插入图片描述

    housing.plot(kind="scatter", x="longitude", y="latitude", alpha=0.4,
                 s=housing["population"]/100, label="population", figsize=(10, 7),
                 c="median_house_value", cmap=plt.get_cmap("jet"), colorbar=True)
    

    在这里插入图片描述
    现在,再看看房价。每个圆的半径大小代表了每个区域的人口数量(选项 s),颜色代表价格(选项 c)。我们使用一个名叫jet的预定义颜色表(选项 cmap)来进行可视化,颜色范围从蓝(低)到红(高)

    4.2、寻找相关性

    由于数据集不太大,你可以使用 corr() 方法轻松计算出没对属性之间的标准相关系数(也称皮尔逊 r

    corr_matrix = housing.corr()
    

    现在查看每个属性与房价中位数的相关性分别是多少:

    corr_matrix["median_house_value"].sort_values(ascending=False)
    
    median_house_value    1.000000
    median_income         0.687160
    total_rooms           0.135097
    housing_median_age    0.114110
    households            0.064506
    total_bedrooms        0.047689
    population           -0.026920
    longitude            -0.047432
    latitude             -0.142724
    Name: median_house_value, dtype: float64
    

    相关系数的范围从 -1 变化到 1。

    • 越接近 1,表示有越强的正相关。当收入中位数上升时,房价中位数也趋于上升

    • 越接近 -1,表示有越强的负相关。可以发现纬度和房价中位数呈现轻微的负相关,也就是说,越往北走,房价倾向于下降

    • 越接近 0,表示两者之间没有线性相关性。可以发现纬度和房价中位数呈现轻微的负相关,也就是说,越往北走,房价倾向于下降
      在这里插入图片描述

    上图可知,相关系数仅测量线性相关性,所以她有可能彻底遗漏非线性相关性。注意最下面一排图像,他们的相关系数都是0,但是显然我们可以看出横轴和纵轴之间的关系并不是完全独立的。此外前两行,需要注意的是这个相关性跟斜率完全无关

    还有一种方法可以检测属性之间的相关性,也就是使用pandasscatter_matix函数,它会绘制出每个数值属性相对于其他数值属性的相关性。现在我们有11个数值属性,可以得到 11^2 = 121 个图像,这里我们只关注这些与房价中位数属性最相关的,可算作最有潜力的属性

    from pandas.plotting import scatter_matrix
    
    # 选择了相关性靠前的 4 个属性
    attributes = ["median_house_value", "median_income", "total_rooms", "housing_median_age"]
    scatter_matrix(housing[attributes], figsize=(12, 8))
    

    在这里插入图片描述
    最有潜力能预测房价中位数的属性是收入中位数,所以我们放大开看看其相关属性的散点图

    housing.plot(kind="scatter", x="median_income", y="median_house_value", alpha=0.1)
    

    在这里插入图片描述

    1. 上图可以明显的看到上升趋势,并且点也不是太分散。
    2. 明显可以看到几条水平线,比如:50万、45万、35万、30万以下还有几条不太明显的线。
    3. 为了避免你的算法学习之后重现这些怪异数据,可以尝试删除这些相应区域。

    4.3、试验不同属性的组合(为 5.3 自定义转换器的编写做准备)

    应于发现其他有意思的数据关系。

    # 房屋总数 / 住户
    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)
    
    median_house_value          1.000000
    median_income               0.687160
    rooms_per_household         0.146285
    total_rooms                 0.135097
    housing_median_age          0.114110
    households                  0.064506
    total_bedrooms              0.047689
    population_per_household   -0.021985
    population                 -0.026920
    longitude                  -0.047432
    latitude                   -0.142724
    bedrooms_per_room          -0.259984
    Name: median_house_value, dtype: float64
    

    这一轮的探索不一定要多么彻底,关键是迈开这一步,快速获得洞见,这将有助于你获得非常非常好的第一个原型。这也是一个不断迭代的过程:

    一旦你的原型产生并且开始运行,你可以分析它的输出以获得更多洞见,然后再次回到这个探索步骤。

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

    现在,终于是时候给你的机器学习算法准备数据了。这里你应该编写函数来执行,而不是手动操作,原因如下:

    • 你可以在任何数据集上轻松重现这些转换(比如,获得更新之后的数据集之后)
    • 你可以逐渐建立起一个转换函数函数库,可以在以后的项目中重用。
    • 你可以在实现系统中使用这些函数来转换新数据,在输入给算法。
    • 你可以轻松尝试多种转换方式,查看哪种转换的组合效果最佳。

    现在,让我们先回到一个干净的训练集(再次复制 strat_train_set),然后将预测期标签分开,因为这里我们不一定对它们使用相同的转换方式(需要注意drop()会创建一个数据副本,但是不影响 strat_train_set):

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

    5.1、数据清理

    5.1.1、常规方法(四种)

    大部分的机器学习算法无法在缺失的特征上工作,所以我们要创建一些函数来辅助它。前面我们已经注意到total_bedrooms属性有部分值缺失,所以我们要先解决它。有一下三种选择:

    1. 放弃这些相应的区域
    2. 放弃整个属性
    3. 将缺失值设置为某个值(0、平均值或者中位数等)
    4. 将缺失值按分组设置为组内某个值(0、平均值或者中位数等)(我自己加的)

    通过DataFramedropan()drop()fillan()groupby()方法,可以轻松完成这些操作:

    # 获取有缺失值的行,方便显示
    sample_incomplete_rows = housing[housing.isnull().any(axis=1)].head()
    sample_incomplete_rows
    
    longitudelatitudehousing_median_agetotal_roomstotal_bedroomspopulationhouseholdsmedian_incomeocean_proximity
    4629-118.3034.0718.03759.0NaN3296.01462.02.2708<1H OCEAN
    6068-117.8634.0116.04632.0NaN3038.0727.05.1762<1H OCEAN
    17923-121.9737.3530.01955.0NaN999.0386.04.6328<1H OCEAN
    13656-117.3034.056.02155.0NaN1039.0391.01.6675INLAND
    19252-122.7938.487.06837.0NaN3468.01405.03.1662<1H OCEAN

    5.1.1.1、方法一:dropna()

    sample_incomplete_rows.dropna(subset=["total_bedrooms"])
    
    longitudelatitudehousing_median_agetotal_roomstotal_bedroomspopulationhouseholdsmedian_incomeocean_proximity

    5.1.1.2、方法二:drop()

    sample_incomplete_rows.drop("total_bedrooms", axis=1)
    
    longitudelatitudehousing_median_agetotal_roomspopulationhouseholdsmedian_incomeocean_proximity
    4629-118.3034.0718.03759.03296.01462.02.2708<1H OCEAN
    6068-117.8634.0116.04632.03038.0727.05.1762<1H OCEAN
    17923-121.9737.3530.01955.0999.0386.04.6328<1H OCEAN
    13656-117.3034.056.02155.01039.0391.01.6675INLAND
    19252-122.7938.487.06837.03468.01405.03.1662<1H OCEAN

    5.1.1.3、方法三:fillna()

    median = housing["total_bedrooms"].median()
    sample_incomplete_rows["total_bedrooms"].fillna(median, inplace=True)
    sample_incomplete_rows
    
    longitudelatitudehousing_median_agetotal_roomstotal_bedroomspopulationhouseholdsmedian_incomeocean_proximity
    4629-118.3034.0718.03759.0433.03296.01462.02.2708<1H OCEAN
    6068-117.8634.0116.04632.0433.03038.0727.05.1762<1H OCEAN
    17923-121.9737.3530.01955.0433.0999.0386.04.6328<1H OCEAN
    13656-117.3034.056.02155.0433.01039.0391.01.6675INLAND
    19252-122.7938.487.06837.0433.03468.01405.03.1662<1H OCEAN

    5.1.1.4、方法四:groupby()

    housing_group = housing.copy()
    housing_group["income_cat"] = pd.cut(housing["median_income"],
                                         bins=[0, 1.5, 3, 4.5, 6, np.inf],
                                         labels=[1, 2, 3, 4, 5])
    housing_group[housing.isnull().any(axis=1)].head()
    
    longitudelatitudehousing_median_agetotal_roomstotal_bedroomspopulationhouseholdsmedian_incomeocean_proximityincome_cat
    4629-118.3034.0718.03759.0NaN3296.01462.02.2708<1H OCEAN2
    6068-117.8634.0116.04632.0NaN3038.0727.05.1762<1H OCEAN4
    17923-121.9737.3530.01955.0NaN999.0386.04.6328<1H OCEAN4
    13656-117.3034.056.02155.0NaN1039.0391.01.6675INLAND2
    19252-122.7938.487.06837.0NaN3468.01405.03.1662<1H OCEAN3
    housing_group_median = housing_group.groupby("income_cat").transform(lambda x: x.fillna(x.median()))
    housing_group_median[housing.isnull().any(axis=1)].head()
    
    longitudelatitudehousing_median_agetotal_roomstotal_bedroomspopulationhouseholdsmedian_income
    4629-118.3034.0718.03759.0444.03296.01462.02.2708
    6068-117.8634.0116.04632.0427.03038.0727.05.1762
    17923-121.9737.3530.01955.0427.0999.0386.04.6328
    13656-117.3034.056.02155.0444.01039.0391.01.6675
    19252-122.7938.487.06837.0453.03468.01405.03.1662

    如果选择方法三,你需要计算出训练集的中位数,然后用它填充训练集中的缺失值,但也别忘了保存这个计算出的中位数,因为后面可能需要用到。当重新评估系统时,你需要更换测试集中的缺失值;或者在系统上线时,需要使用新数据替代缺失值。

    5.1.2、Scikit-Learn 提供的 SimpleImputer 方法

    Scikit-Learn提供了一个非常容易上手的类来处理缺失值:SimpleImputer。使用方法如下:首先,你需要创建一个 SimpleImputer 实例,指定你要用属性的中位数值替换该属性的缺失值:

    from sklearn.impute import SimpleImputer
    
    imputer = SimpleImputer(strategy="median")
    

    由于中位数只能在数值属性上计算,所以我们需要创建一个没有文本属性 ocean_proximity 的数据副本:

    housing_num = housing.drop("ocean_proximity", axis=1)
    

    使用fit()方法将imputer实例适配到训练数据:

    imputer.fit(housing_num)
    
    SimpleImputer(strategy='median')
    

    这里imputer仅仅只是计算了每个属性的中位数值,并将结果储存在其实例变量statistics_中。虽然只是total_bedrooms这个属性存在缺失值,所以稳妥起见,还是将imputer应用于所有的数值属性:

    # imputer 计算的每列中位数
    imputer.statistics_
    
    array([-118.51  ,   34.26  ,   29.    , 2119.5   ,  433.    , 1164.    , 408.    ,    3.5409])
    
    # 直接计算的中位数
    housing_num.median().values
    
    array([-118.51  ,   34.26  ,   29.    , 2119.5   ,  433.    , 1164.    , 408.    ,    3.5409])
    

    现在,你可以使用这个“训练有素”的imputer将缺失值替换成中位数从而完成训练集转换:

    X = imputer.transform(housing_num)
    type(X)
    
    numpy.ndarray
    

    结果是一个包含转换后特征的Numpy数组。如果你想将它放回Pandas DataFrame,也很简单:

    housing_tr = pd.DataFrame(X, columns=housing_num.columns, index=housing_num.index)
    housing_tr.loc[sample_incomplete_rows.index.values]
    
    longitudelatitudehousing_median_agetotal_roomstotal_bedroomspopulationhouseholdsmedian_income
    4629-118.3034.0718.03759.0433.03296.01462.02.2708
    6068-117.8634.0116.04632.0433.03038.0727.05.1762
    17923-121.9737.3530.01955.0433.0999.0386.04.6328
    13656-117.3034.056.02155.0433.01039.0391.01.6675
    19252-122.7938.487.06837.0433.03468.01405.03.1662

    5.2、处理文本和分类属性

    5.2.1、使用 Scikit-Learn 的 OrdinalEncoder 类

    到目前为止,我们只处理数值属性,但现在让我们看一下文本属性。在此数据集中,只有一个:ocean_proximity属性。前面我们一直到它不是任意文本,而是有限个可能的取值,每个值代表一类别。因此,此属性是分类属性。大多数机器学习算法更喜欢使用数字,因此让我们将这些类别从文件转到数字。为此,我们可以使用Scikit-LearnOrdinalEncoder类:

    housing["ocean_proximity"].value_counts()
    
    <1H OCEAN     7276
    INLAND        5263
    NEAR OCEAN    2124
    NEAR BAY      1847
    ISLAND           2
    Name: ocean_proximity, dtype: int64
    
    from sklearn.preprocessing import OrdinalEncoder
    
    housing_cat = housing[["ocean_proximity"]]
    ordinal_encoder = OrdinalEncoder()
    housing_cat_encoded = ordinal_encoder.fit_transform(housing_cat)
    housing_cat_encoded[:10]
    
    array([[0.],
           [0.],
           [4.],
           [1.],
           [0.],
           [1.],
           [0.],
           [1.],
           [0.],
           [0.]])
    

    你可以使用categories_实例变量获取类别列表。这个列表包含每个类别属性的维一数组(在这种情况下,这个列表包含一个数组,因为只有一个类别属性):

    ordinal_encoder.categories_
    
    [array(['<1H OCEAN', 'INLAND', 'ISLAND', 'NEAR BAY', 'NEAR OCEAN'], dtype=object)]
    

    这种表征方式产生的一个问题是,机器学习算法会认为两个相近的比值比两个离得较远的值更为相似一些,在某种情况下这是对的(比如一些有序类别,像“优”、“良”、“中”、“差”),但是对ocean_proximity而言情况并非如此(例如,类别0和类别4之间就比类别0和类别1之间的相似度更高)

    5.2.2、使用 Scikit-Learn 的 OneHotEncoder 类

    为了解决这个问题,常见的解决方案是给每个类别创建一个二进制的属性:

    • 当类别是 “<1H OCEAN” 时,一个属性为 1(其他属性为 0)
    • 当类别是 “INLAND” 时,另一个属性为 1(其他属性为 0)
    • 以此类推

    这就是独热编码,因为只有一个属性为 1(热),其他均为 0(冷)。新的属性有事成为哑(dummy)属性。Scikit-LearnOneHotEncoder编码器,可以将整数类别转换为独热向量(新版本支持其他类别转换)。我们用它来将类别编码为独热向量

    from sklearn.preprocessing import OneHotEncoder
    
    cat_encoder = OneHotEncoder()
    housing_cat_1hot = cat_encoder.fit_transform(housing_cat)
    housing_cat_1hot
    
    <16512x5 sparse matrix of type '<class 'numpy.float64'>'
    	with 16512 stored elements in Compressed Sparse Row format>
    

    <16512x5 ‘<class ‘numpy.float64’>’ 类型的稀疏矩阵以压缩稀疏行格式存储 16512 个元素>

    注意到这里的输出是一个SciPy稀疏矩阵,而不是一个NumPy数组。当你有成千上万个类别属性时,这个函数会非常有用。因为在独热编码完成之后,我们会得到一个几千列的矩阵,并且全部是0,每行仅有一个1。占用大量内存来储存0是一件非常浪费的事情,因此稀疏矩阵选择仅储存非零元素的位置。而你依旧可以像使用一个普通的二维数组那样来使用它。

    如果你想把它转成一个(密集的)NumPy 数组,只需要调用toarray()方法即可:

    housing_cat_1hot.toarray()
    
    array([[1., 0., 0., 0., 0.],
           [1., 0., 0., 0., 0.],
           [0., 0., 0., 0., 1.],
           ...,
           [0., 1., 0., 0., 0.],
           [1., 0., 0., 0., 0.],
           [0., 0., 0., 1., 0.]])
    

    再次使用编码器的categories_实例变量获取类别列表:

    cat_encoder.categories_
    
    [array(['<1H OCEAN', 'INLAND', 'ISLAND', 'NEAR BAY', 'NEAR OCEAN'], dtype=object)]
    

    5.3、自定义转换器

    虽然Scikit-Learn提供了许多有用的转换器,但是你依然需要为一些诸如自定义清理操作组合特定属性等任务编写自己的转换器。你当然希望让自己的转换器与Scikit-Learn自身功能(比如流水线)无缝衔接,而由于Scikit-Learn依赖于鸭子类型的编译,而不是继承,所以你所需要的只是创建一个类,然后应用一下三种方法:fit()(返回self)、transform()fit_transform()

    你可以通过添加TransformerMixin作为基类,直接得到最后一种方法。同时,如果添加BaseEstimator作为基类(并在构造函数中避免 *args**kargs),你还能额外获得两个非常有用的自动调整超参数的方法(get_params()set_params())。

    例如,我们前面讨论过得组合属性,这里有个简单的转换器类,用来添加组合后的属性:

    from sklearn.base import BaseEstimator, TransformerMixin
    
    # 对应数据所在列的位置,从0开始计数
    # rooms_ix, bedroom_ix, population_ix, households_ix = 3, 4, 5, 6
    col_names = "total_rooms", "total_bedrooms", "population", "households"
    # get the column indices
    rooms_ix, bedrooms_ix, population_ix, households_ix = [housing.columns.get_loc(c) for c in col_names]
    
    class CombinedAttributesAdder(BaseEstimator, TransformerMixin):
        def __init__(self, add_bedroom_per_room=True):
            # 没有 *args 和 **kargs
            self.add_bedroom_per_room = add_bedroom_per_room
    
        def fit(self, X, y=None):
            # 不做任何处理
            return self
    
        def transform(self, X):
            """
            X[:, rooms_ix]:X数组,":"所有行,取rooms_ix列的数据(即,取第rooms_ix列的全部数据,从0开始计数)
            ------------
            np.r_是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等。
            np.c_是按行连接两个矩阵,就是把两矩阵左右相加,要求行数相等。即添加计算后的新列数据
            例:
                a = np.array([[1, 2, 3],
                              [7,8,9]])
                b = np.array([[4,5,6],
                              [1,2,3]])
                c = np.c_[a,b]
    
                >>> print(c)
                array([[1, 2, 3, 4, 5, 6],
                       [7, 8, 9, 1, 2, 3]])
            """
            rooms_per_household = X[:, rooms_ix] / X[:, households_ix] # 即 第 3 列数据 ÷ 第 6 列数据
            population_per_household = X[:, population_ix] / X[:, households_ix]
            if self.add_bedroom_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_bedroom_per_room=False)
    housing_extra_attribs = attr_adder.transform(housing.values)
    
    • housing
      在这里插入图片描述
    • housing.values(即 X)
      在这里插入图片描述
    • housing_extra_attribs(即 转换结果)
      在这里插入图片描述
      本案例中,转换器有一个超参数add_bedroom_per_room默认设置为True(提供合理的默认值通用是很有帮助的)。
    1. 这个参数可以让你轻松知晓添加这个属性是否有助于机器学习算法。
    2. 更一般地,如果你对数据准备的步骤没有充分的信心,就可以添加这个超参数来进行把关。
    3. 这些数据准备步骤的执行越是自动化,你自动尝试的组合也就越多,从而有更大可能从中找到一个重要的组合(还节省了大量时间)。

    5.4、特征缩放

    最重要也最需要应用到数据上的转换就是特征缩放。如果输入的数值属性具有非常大的比例差异,往往会导致机器学习算法的性能表现不佳,当然也有极少数特例。案例中的房屋数据就是这样:房屋总数的范围从 6~39320,而收入中位数的范围是 0~15。注意,目标值通常不需要缩放。

    同比例缩放所有属性的两种常用方法是最小值-最大值缩放标准化

    5.4.1、最小值-最大值缩放(归一化):MinMaxScaler

    • 概念:将训练集中某一列数值特征(假设是第i列)的值缩放到0和1之间。
    • 算法:将值减去最小值并除以最大值和最小值的差。
      z i j ← x i j − m i n ( x i ) m a x ( x i ) − m i n ( x i ) z_{ij} ← \frac{x_{ij} - min(x_i)}{max(x_i) - min(x_i)} zijmax(xi)min(xi)xijmin(xi)

    其中 x i j x_{ij} xij代表 x i x_i xi的第𝑗个条目,同样的 z i j z_{ij} zij代表 z i ∈ R 𝑛 z_i∈ℝ^𝑛 ziRn的第𝑗个条目, 𝐙 ‾ = ( 1 , 𝐳 1 , ⋯ , 𝐳 𝑑 ) ∈ R 𝑛 × ( 𝑑 + 1 ) \overline{𝐙}=(1,𝐳_1,⋯,𝐳_𝑑)∈ℝ^{𝑛×(𝑑+1)} Z=(1,z1,,zd)Rn×(d+1),max和min是按列求每一列的最大和最小值。

    • Scikit-Learn:MinMaxScaler转换器。如果处于某种原因,你希望范围不是 0~1。那么可以通过调整超参数feature_range进行更改。

    5.4.1.1、数据处理前后对比

    在这里插入图片描述

    对于线性model来说,数据归一化后,最优解的寻优过程明显会变得平缓,更容易正确的收敛到最优解。
    比较这两个图,前者是没有经过归一化的,在梯度下降的过程中,走的路径更加的曲折,而第二个图明显路径更加平缓,收敛速度更快。

    5.4.2、标准化:StandardScaler(本案例采用)

    • 概念:将训练集中某一列数值特征(假设是第i列)的值缩放成均值为0,方差为1的状态。
    • 算法:首先减去平均值(所以表转换值的均值总是零),然后除以方差。从而使得结果的分布具备单位方差。

    按如下方法标准化Data Matirx矩阵的每一列 x i x_i xi of 𝐗 ( 1 ≤ 𝑖 ≤ 𝑑 ) 𝐗(1≤𝑖≤𝑑) X(1id):(这里解释一下为什么是按列标准化:数据矩阵的每一列就代表了样本的每一维,我们想通过标准化来更好的处理该维度的特征,可以想想按行标准化是什么效果:make no sense)

    z i j ← x i j − m e a n ( x i ) s t d ( x i ) z_{ij} ← \frac{x_{ij} - mean(x_i)}{std(x_i)} zijstd(xi)xijmean(xi)

    其中 x i j x_{ij} xij代表 x i x_i xi的第𝑗个条目,同样的 z i j z_{ij} zij代表 z i ∈ R 𝑛 z_i∈ℝ^𝑛 ziRn的第𝑗个条目, 𝐙 ‾ = ( 1 , 𝐳 1 , ⋯ , 𝐳 𝑑 ) ∈ R 𝑛 × ( 𝑑 + 1 ) \overline{𝐙}=(1,𝐳_1,⋯,𝐳_𝑑)∈ℝ^{𝑛×(𝑑+1)} Z=(1,z1,,zd)Rn×(d+1), mean和std就是按列求每一列的均值和方差。

    • Scikit-Learn:StandardScaler转换器。

    5.4.2.1、数据处理前后对比

    在这里插入图片描述

    适用于:如果数据的分布本身就服从正态分布,就可以用这个方法。
    通常这种方法基本可用于有outlier的情况,但是,在计算方差和均值的时候outliers仍然会影响计算。所以,在出现outliers的情况下可能会出现转换后的数的不同feature分布完全不同的情况。
    如上图,经过StandardScaler之后,横坐标与纵坐标的分布出现了很大的差异,这可能是outliers造成的。

    5.5、转换流水线

    5.5.1、数值属性的流水线(Scikit-Learn:Pipeline)

    正如你所见,许多数据转换的步骤需要以正确的顺序来执行。而Scikit-Learn正好提供了Pipeline类来支持这样的转换。下面是一个数值属性的流水线示例:

    from sklearn.pipeline import Pipeline
    from sklearn.preprocessing import StandardScaler
    
    num_pipeline = Pipeline([
        ('imputer', SimpleImputer(strategy="median")),
        ('attribs_adder', CombinedAttributesAdder()),
        ('std_scaler', StandardScaler()),
    ])
    housing_num_tr = num_pipeline.fit_transform(housing_num)
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZhfTjGpV-1635090372784)(attachment:image.png)]
    Pipeline构造函数会通过一系列名称/估值器的配对来定义步骤序列。除了最后一个是估值器之外,前面都必须是转换器(也就是说,必须有fit_transform()方法)。至于命名可以随意(只要他们是独一无二的,不含双下划线),它们稍后在超参数调整中会有用。

    当调用流水线的fit()方法时,会在所有转换器上按照顺序依次调用fit_transform(),将一个调用的输出作为参数传递给下一个调用的方法,直到传递到最终的估值器,则只会调用fit()方法。

    流水线的方法和最终的估值器的方法相同。在本例中,最后一个估值器是StandardScaler,这是一个转换器,因此流水线有一个transform()方法,可以按顺序将所有的转换应用到数据中(这也是我们用过的fit_transform()方法)。

    5.5.2、所有属性的流水线(Scikit-Learn:ColumnTransformer)

    到目前为止,我们分别处理了类别列数值列。拥有一个能够处理所有列的转换器会更方便,将适当的转换应用于每一列。Scikit-Learn为此引入了ColumnTransformer,好消息是它与Pandas DataFrames一起使用时效果很好。让我们来用它将所有转换应用到房屋数据:

    from sklearn.compose import ColumnTransformer
    
    num_attribs = list(housing_num) # 获取列头
    cat_attribs = ["ocean_proximity"]
    
    full_pipeline = ColumnTransformer([
        ("num", num_pipeline, num_attribs),
        ("cat", OneHotEncoder(), cat_attribs),
    ])
    housing_prepared = full_pipeline.fit_transform(housing)
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TdnHCnay-1635090372785)(attachment:image.png)]
    首先导入ColumnTransformer类,接下来获得数值列和类别列名称列表,然后构造一个ColumnTransformer。构造函数需要一个元组列表,其中每个元组都包含一个名称(与流水线一样)、一个转换器,以及一个该转换器能够应用的列名称(或索引)的列表。在此示例中,我们指定数值列使用之前定义的num_pipeline进行转换,类别列使用OneHotEncoder进行转换。最后,我们将ColumnTransformer应用到房屋数据:它将每个转换器应用于适当的列,并沿第二个轴合并输出(转换器必须返回相同数据的行)。

    请注意,OneHotEncoder返回一个稀疏矩阵,而num_pipeline返回一个密集矩阵。当稀疏矩阵密集矩阵混合在一起时,ColumnTransformer会估算最终矩阵的密度(即单元格的非零比率),如果密度低于给定的阈值,则返回一个稀疏矩阵(通过默认值为sparse_threshold = 0.3)。在此示例中,返回一个密集矩阵。我们有一个预处理流水线,该流水线可以获取全部房屋数据并对每一列进行适当的转换。

    6、选择和训练模型

    6.1、训练和评估训练集

    6.1.1、LinearRegression 线性回归模型

    首先,我们先训练一个线性回归模型:

    from sklearn.linear_model import LinearRegression
    
    lin_reg = LinearRegression()
    lin_reg.fit(housing_prepared, housing_labels)
    
    LinearRegression()
    

    现在你有一个可以工作的线性回归模型了,让我们用几个训练集的实例试试:

    some_data = housing[:5] # 获取前5行数据
    some_labels = housing_labels.iloc[:5] # 获取前5行标签,用于验证模型结果
    some_data_prepared = full_pipeline.transform(some_data) # 流水线处理数据
    print("Predictions:", lin_reg.predict(some_data_prepared)) # 模型预测数据
    print("Labels:", list(some_labels)) # 真实结果
    
    Predictions: [210644.60459286 317768.80697211 210956.43331178  59218.98886849 189747.55849879]
    Labels: [286600.0, 340600.0, 196900.0, 46300.0, 254500.0]
    

    可以工作了,虽然预测还不是很准确(实际上。。。一点都不准。。。)。我们可以使用Scikit-Learnmean_squared_error()函数来测量整个训练集上回归模型的RMSE(均方根误差):

    RMSE

    • Root Mean Square Error,均方根误差
    • 是观测值与真值偏差的平方和与观测次数m比值的平方根。
    • 是用来衡量观测值同真值之间的偏差

    MAE

    • Mean Absolute Error ,平均绝对误差
    • 是绝对误差的平均值
    • 能更好地反映预测值误差的实际情况.

    SD

    • Standard Deviation ,标准差
    • 是方差的算数平方根
    • 是用来衡量一组数自身的离散程度
      在这里插入图片描述
    from sklearn.metrics import mean_squared_error
    
    housing_predictions = lin_reg.predict(housing_prepared) # 使用模型获取训练集全部预测数据
    lin_mse = mean_squared_error(housing_labels, housing_predictions) # 真实数据 与 模型预测数据 的 均方根误差
    lin_rmse = np.sqrt(lin_mse) # 平方根,预测误差值
    lin_rmse
    
    68628.19819848922
    

    median_housing_values分布在 120000~265000 美元之间,所以典型的预测误差达到 68628 美元只能说明差强人意。

    这就是一个典型的模型对训练数据欠拟合的案例。这种情况发生时,通常意味着这些:

    1. 特征可能无法提供足够的信息来做出更好的预测;
    2. 或者是模型本身不够强大。

    想要修正欠拟合,可以通过:

    1. 选择更强大的模型;
    2. 或为算法训练提供更好的特征;
    3. 又或者减少模型的限制等方法。

    我们这个模型不是一个正则化的模型,所以可以排除最后一个选项。你可以尝试添加更多的特征(比如,人口数量的日志),但首选,让我们尝试一个更复杂的模型,看看它到底是怎样工作的。

    6.1.2、DecisionTreeRegressor 决策树模型

    我们来训练一个DecisionTreeRegressor。这是一个非常强大的模型,它能够从数据中找到复杂的非线性关系。使用方法与上面的线性回归模型相同:

    from sklearn.tree import DecisionTreeRegressor
    
    tree_reg = DecisionTreeRegressor()
    tree_reg.fit(housing_prepared, housing_labels)
    
    DecisionTreeRegressor()
    

    训练集评估结果

    housing_predictions = tree_reg.predict(housing_prepared)
    tree_mse = mean_squared_error(housing_labels, housing_predictions)
    tree_rmse = np.sqrt(tree_mse)
    print(tree_rmse)
    
    0.0
    

    结果是0,没有预测误差,这个模型真的可以做到绝对完美么?当然,更可能是这个模型对数据严重过拟合了。我们应该怎么确认?前面提到过,在你有信心启动模型之前,都不要触碰测试集。

    所有这里,你需要那训练集中的一部分用于训练,另一部分用于模型验证。

    6.1.2.1、使用交叉验证来更好地进行评估

    评估决策树模型的一种方法是使用train_test_split函数将训练集分为较小的训练集和验证集,然后根据这些较小的训练集来训练模型,并对其进行评估。这虽然有一些工作量,但是也不会太难,并且非常有效。

    另一个不错的选择市使用Scikit-LearnK-折交叉验证功能。以下是执行K-折交叉验证的代码:

    1. 它将训练集随机分割成10个不同的子集,每个子集称为一个折叠;
    2. 对决策树模型进行10次训练和评估 – 每次挑选1个折叠进行评估,使用另外的9个折叠进行训练;
    3. 产生的结果是一个包含10次评估分数的数组。
    def display_scores(scores):
        print("分  数:", scores)
        print("平均值:", scores.mean())
        print("标准差:", scores.std())
    
    from sklearn.model_selection import cross_val_score
    
    # neg_mean_squared_error: 负均方误差
    scores = cross_val_score(tree_reg, housing_prepared, housing_labels,
                             scoring="neg_mean_squared_error", cv=10)
    tree_rmse_scores = np.sqrt(-scores)
    display_scores(tree_rmse_scores)
    
    分  数: [70062.5222628  66736.98017719 70439.6047592  70474.78239772 71238.21621992 
            74620.75350778 71398.18416741 70620.80936843 77492.84330946 71004.28109473]
    平均值: 71408.89772646272
    标准差: 2712.8600596563356
    

    Scikit-LearnK-折交叉验证功能更倾向于使用效用函数(越大越好)而不是成本函数(越小越好),所以计算分数的函数实际上负的MSE(一个负值)函数,这就是为什么上面的代码在计算平方根之前会先计算出-scores

    这次的决策树模型好像不如之前的表现得好,事实上,它看起来简直比线性回归模型还要糟糕。请注意,交叉验证不仅可以得到一个模型性能的评估值,还可以衡量该评估的精准度(即其标准差)。这里该决策树得到的评分约为71407,上下浮动±2439。如果你只使用了一个验证集,就收不到这样的结果信息。交叉验证的代价就是要多次训练模型,因此也不是永远都行得通。

    保险起见,让我们也计算一下线性回归模型的评分:

    lin_scores = cross_val_score(lin_reg, housing_prepared, housing_labels, 
                                 scoring="neg_mean_squared_error", cv=10)
    lin_rmse_scores = np.sqrt(-lin_scores)
    display_scores(lin_rmse_scores)
    
    分  数: [66782.73843989 66960.118071 70347.95244419 74739.57052552 68031.13388938 
            71193.84183426 64969.63056405 68281.61137997 71552.91566558 67665.10082067]
    平均值: 69052.46136345083
    标准差: 2731.674001798347
    

    没错,决策树模型的确严重过拟合了,以至于表现得比线性回归模型还要糟糕。

    6.1.3、RndomForestRegressor 随机森林模型

    我们再来试试最后一个模型RndomForestRegressor。随机森林的工作原理:通过对特征的随机子集进行许多个决策树的训练,然后对其预测取平均。在多个模型的基础之上建立模型,称之为集成学习,这是进一步推动机器学习算法的好方法。这里我们将跳过大部分代码,因为与其他模型基本相同:

    from sklearn.ensemble import RandomForestRegressor
    
    forest_reg = RandomForestRegressor()
    forest_reg.fit(housing_prepared, housing_labels)
    
    RandomForestRegressor()
    
    housing_predictions = forest_reg.predict(housing_prepared)
    forest_mse = mean_squared_error(housing_labels, housing_predictions)
    forest_rmse = np.sqrt(forest_mse)
    print(forest_rmse)
    
    18725.06655956781
    
    forest_scores = cross_val_score(forest_reg, housing_prepared, housing_labels, 
                                    scoring="neg_mean_squared_error", cv=10)
    forest_rmse_scores = np.sqrt(-forest_scores)
    display_scores(forest_rmse_scores)
    
    分  数: [49809.66295035 47362.11725625 50097.32716715 51971.44001152 49498.75378409 
            53481.17484005 49051.91408781 48402.93749135 52812.15210493 50385.42811085]
    平均值: 50287.290780434654
    标准差: 1842.3191312235808
    

    当前数据看起来是目前对好的。但是,请注意,训练集上的分数(18725)仍然远低于验证集(50287),这意味着该模型仍然对训练集过拟合。过拟合的可能解决方案包括:

    1. 简化模型
    2. 约束模型(即使其正规化)
    3. 或获得更多的训练数据

    不过在深入探索随机森林之前,你应该先尝试一遍各种机器学习算法的其他模型(几种具有不同内核的支持向量机,比如神经网络模型等),但记住,别花太多时间去调整超参数,我们的目的是筛选出几个(2~5个)有效的模型。

    每一个尝试过的模型都应该妥善的保存,以便将来可以轻松回顾。通过Pythonpickle模块或joblib库,你可以轻松保存Scikit-Learn模型,这样可以更有效地将大型NumPy数组序列号。

    7、微调模型

    假设你现在有了一个有效模型的候选列表。现在你需要对它们进行微调。我们来看几个可行的方法。

    7.1、网格搜索

    一种微调的方式是以手动调整超参数,直到找到一组很好的超参数组合。这个过程非常枯燥乏味,你可以坚持不到足够的时间来探索出各种组合。
    相反,你可以用Scikit-LearnGridSearchCV来替你进行探索。你所需要做的只是告诉它你要进行实验的超参数是什么,以及需要尝试的值,它将会使用交叉验证来评估超参数的所有可能组合。例如,下面这段代码搜索RandomForestRegressor的超参数数值的最佳组:

    from sklearn.model_selection import GridSearchCV
    
    param_grid = [
        {"n_estimators": [3, 10, 30], "max_features": [2, 4, 6, 8]},
        {"bootstrap": [False], "n_estimators": [3, 10], "max_features": [2, 3, 4]},
    ]
    forest_reg = RandomForestRegressor()
    grid_search = GridSearchCV(forest_reg, param_grid, cv=5,
                               scoring="neg_mean_squared_error",
                               return_train_score=True)
    grid_search.fit(housing_prepared, housing_labels)
    
    GridSearchCV(cv=5, estimator=RandomForestRegressor(),
                 param_grid=[{'max_features': [2, 4, 6, 8],
                              'n_estimators': [3, 10, 30]},
                             {'bootstrap': [False], 'max_features': [2, 3, 4],
                              'n_estimators': [3, 10]}],
                 return_train_score=True, scoring='neg_mean_squared_error')
    

    当你不知道超参数应该赋什么值时,一个简单的方法是尝试10次连续幂次方(如果你想要得到更细粒度的搜索,可以使用更小的数,参考这个示例中n_estimators超参数。

    这个param_grid告诉Scikit-Learn,首先评估第一个dict中的n_estimatorsmax_features的所有 3×4=12 种超参数组合;接着,尝试第二个dict中超参数的所有 1×2×3=6 种组合,但这次超参数bootstrap需要设置为False而不是True

    总而言之,网格探索将探索RandomForestRegressor超参数值的 12+6=18 种组合,并对每个模型进行5次训练(cv=5)。换句话,总共完成 18×5=90 次训练。

    但是训练完成后你就可以获得最佳的参数组合:

    grid_search.best_params_
    
    {'max_features': 6, 'n_estimators': 30}
    

    你可以直接得到最好的估算器:

    grid_search.best_estimator_
    
    RandomForestRegressor(max_features=6, n_estimators=30)
    

    如果GridSearchCV被初始化为refit=True(默认值),那么一旦通过交叉验证找到最佳估算器,它将在整个训练集上重新训练。这通常是个好方法,因为提供更多的数据很可能提升其性能。

    当然还有评估分数:

    cvres = grid_search.cv_results_
    for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
        print(np.sqrt(-mean_score), params)
    
    64219.37103342171 {'max_features': 2, 'n_estimators': 3}
    55757.44574868692 {'max_features': 2, 'n_estimators': 10}
    52912.46028198916 {'max_features': 2, 'n_estimators': 30}
    60369.36943060073 {'max_features': 4, 'n_estimators': 3}
    53342.610401252685 {'max_features': 4, 'n_estimators': 10}
    50755.23490862702 {'max_features': 4, 'n_estimators': 30}
    59396.83436658384 {'max_features': 6, 'n_estimators': 3}
    52375.46588717245 {'max_features': 6, 'n_estimators': 10}
    50133.101632717895 {'max_features': 6, 'n_estimators': 30}
    58851.03261455543 {'max_features': 8, 'n_estimators': 3}
    52154.38996091269 {'max_features': 8, 'n_estimators': 10}
    50142.71940679718 {'max_features': 8, 'n_estimators': 30}
    63061.98058118926 {'bootstrap': False, 'max_features': 2, 'n_estimators': 3}
    54457.63242342584 {'bootstrap': False, 'max_features': 2, 'n_estimators': 10}
    59490.18437223276 {'bootstrap': False, 'max_features': 3, 'n_estimators': 3}
    52951.47441756218 {'bootstrap': False, 'max_features': 3, 'n_estimators': 10}
    59440.60460822187 {'bootstrap': False, 'max_features': 4, 'n_estimators': 3}
    51717.31614272946 {'bootstrap': False, 'max_features': 4, 'n_estimators': 10}
    

    在本例中,我们得到的最佳解决方案是将超参数max_features设置为6,n_estimators设置为30。这个组合的RMSE分数为50133,略优于之前默认参数的分数50287

    7.2、随机搜索

    如果探索的组合数量太少(例如上一个案例),那么网格搜索是一个不错的方法。但是当超参数的搜索范围比较大时,通过会优先选择使用RandomizedSearchCV。这个类用起来与GridSearchCV类大致相同,但是它不会尝试所有可能的组合,而是在每次迭代中为每个超参数选择一个随机值,然后对一定数量的随机组合进行评估。这种方法有两个显著好处:

    1. 如果运行随机搜索1000个迭代,那么将会探索每个超参数的1000个不同的值。
    2. 通过简单的设置迭代次数,可以更好地控制要分配给超参数搜索的计算预算。
    from sklearn.model_selection import RandomizedSearchCV
    from scipy.stats import randint
    
    param_distribs = {
        'n_estimators': randint(low=1, high=200),
        'max_features': randint(low=1, high=8),
    }
    
    forest_reg = RandomForestRegressor()
    rnd_search = RandomizedSearchCV(forest_reg, param_distributions=param_distribs,
                                    n_iter=10, cv=5, scoring='neg_mean_squared_error')
    rnd_search.fit(housing_prepared, housing_labels)
    
    RandomizedSearchCV(cv=5, estimator=RandomForestRegressor(),
                       param_distributions={'max_features': <scipy.stats._distn_infrastructure.rv_frozen object at 0x7fb713ccb610>,
                                            'n_estimators': <scipy.stats._distn_infrastructure.rv_frozen object at 0x7fb702234210>},
                       scoring='neg_mean_squared_error')
    
    cvres = rnd_search.cv_results_
    for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
        print(np.sqrt(-mean_score), params)
    
    49182.98287156724 {'max_features': 6, 'n_estimators': 142}
    49256.64243951438 {'max_features': 7, 'n_estimators': 157}
    49705.21035567195 {'max_features': 7, 'n_estimators': 59}
    68431.80112151649 {'max_features': 1, 'n_estimators': 3}
    49547.2056799527 {'max_features': 4, 'n_estimators': 144}
    51481.190769293426 {'max_features': 5, 'n_estimators': 13}
    59848.891702369874 {'max_features': 1, 'n_estimators': 9}
    49482.15331762217 {'max_features': 4, 'n_estimators': 188}
    50134.64676419512 {'max_features': 3, 'n_estimators': 162}
    55151.747332409956 {'max_features': 1, 'n_estimators': 47}
    

    7.3、集成方法

    还有一种微调系统的方法是将表现最优的模型组合起来。组合(或“集成”)方法通过最佳的单一模型更好(就像随机森林比任何单个的决策树模型更好一样),特别是当单一模型会产生不同类型误差时更是如此。

    7.4、分析最佳模型及其误差

    通过检查最佳模型,你总是可以得到一些好的洞见。例如在进行准确预测时,RandomForestRegressor可以指出每个属性的相对重要程度:

    feature_importances = grid_search.best_estimator_.feature_importances_
    feature_importances
    
    array([8.20349643e-02, 7.08313931e-02, 4.31025707e-02, 1.82135239e-02,
           1.66778827e-02, 1.83580953e-02, 1.58181207e-02, 2.93820957e-01,
           5.79152902e-02, 1.08181525e-01, 1.01867756e-01, 1.83167523e-02,
           1.43188327e-01, 4.00992699e-05, 4.73585356e-03, 6.89688957e-03])
    

    将这些重要性分数显示在对应的属性名称旁边:

    extra_attribs = ["rooms_per_hhold", "pop_per_hhold", "bedrooms_per_room"]
    #cat_encoder = cat_pipeline.named_steps["cat_encoder"] # 旧版本
    cat_encoder = full_pipeline.named_transformers_["cat"]
    cat_one_hot_attribs = list(cat_encoder.categories_[0])
    attributes = num_attribs + extra_attribs + cat_one_hot_attribs
    sorted(zip(feature_importances, attributes), reverse=True)
    
    [(0.2938209569026082, 'median_income'),
     (0.14318832667800896, 'INLAND'),
     (0.10818152506358511, 'pop_per_hhold'),
     (0.10186775584763018, 'bedrooms_per_room'),
     (0.08203496427830204, 'longitude'),
     (0.07083139305733996, 'latitude'),
     (0.057915290153382135, 'rooms_per_hhold'),
     (0.043102570688951285, 'housing_median_age'),
     (0.01835809530124593, 'population'),
     (0.018316752295000915, '<1H OCEAN'),
     (0.01821352394132179, 'total_rooms'),
     (0.0166778826835988, 'total_bedrooms'),
     (0.015818120710775083, 'households'),
     (0.006896889571023202, 'NEAR OCEAN'),
     (0.0047358535572835985, 'NEAR BAY'),
     (4.009926994286412e-05, 'ISLAND')]
    

    7.5、通过测试集评估系统

    通过一段时间的训练,你终于有了一个表现足够优秀的系统。现在是用测试集评估最终模型的时候了。这个过程没有什么特别的,只需要从测试集中获取预测器和标签,运行full_pipline来转换数据(调用transform()而不是fit_transform()),然后在测试集上评估最终模型:

    final_model = rnd_search.best_estimator_
    
    X_test = strat_test_set.drop("median_house_value", axis=1)
    y_test = strat_test_set["median_house_value"].copy()
    
    X_test_prepared = full_pipeline.transform(X_test)
    final_predictions = final_model.predict(X_test_prepared)
    
    final_mse = mean_squared_error(y_test, final_predictions)
    final_rmse = np.sqrt(final_mse)
    final_rmse
    
    46979.86059266281
    

    如果想知道这个估计的精确度。为此,你可以使用scipy.stats.t.interval()计算泛化误差的95%置信区间:

    from scipy import stats
    
    confidence = 0.95
    squared_errors = (final_predictions - y_test) ** 2
    np.sqrt(stats.t.interval(confidence, len(squared_errors) - 1,
                             loc=squared_errors.mean(),
                             scale=stats.sem(squared_errors)))
    
    array([45009.06645486, 48871.24450507])
    

    如果之前进行过大量的超参数调整,这时的评估结果通常会略逊色与之前使用的交叉验证时的表现结果(因为通过不断调整,系统在验证数据上终于表现良好,在未知数据集上可能达不到那么好的效果)。这时不要再继续调整超参数,因为这些改进在泛化到新数据集时又会变成无用功。(这里我做了上万次的超参数调整,提升不明显)

    在本案例中,系统的最终性能可能并不比专家估算的效果更好,通过会下降20%左右,但是依然可以为专家腾出大量时间,投入到其他任务上。

    8、上述完整关键代码总结

    import os
    import tarfile
    import urllib.request
    
    import pandas as pd
    import numpy as np
    from sklearn.model_selection import StratifiedShuffleSplit
    from sklearn.impute import SimpleImputer
    from sklearn.preprocessing import OneHotEncoder
    from sklearn.base import BaseEstimator, TransformerMixin
    from sklearn.pipeline import Pipeline
    from sklearn.preprocessing import StandardScaler
    from sklearn.compose import ColumnTransformer
    from sklearn.linear_model import LinearRegression
    from sklearn.metrics import mean_squared_error
    from sklearn.tree import DecisionTreeRegressor
    from sklearn.model_selection import cross_val_score
    from sklearn.ensemble import RandomForestRegressor
    from sklearn.model_selection import GridSearchCV
    from sklearn.model_selection import RandomizedSearchCV
    from scipy.stats import randint
    from scipy import stats
    
    DOWNLOAD_ROOT = "https://raw.githubusercontent.com/ageron/handson-ml2/master/"
    HOUSING_PATH = os.path.join("datasets", "housing")
    HOUSING_URL = DOWNLOAD_ROOT + "datasets/housing/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)
    
    
    # "total_rooms", "total_bedrooms", "population", "households"
    rooms_ix, bedroom_ix, population_ix, households_ix = 3, 4, 5, 6
    
    
    class CombinedAttributesAdder(BaseEstimator, TransformerMixin):
    	"""
    	自定义转换器
    	"""
        def __init__(self, add_bedroom_per_room=True):
            self.add_bedroom_per_room = add_bedroom_per_room
    
        def fit(self, X, y=None):
            return self
    
        def transform(self, X):
            """
            X[:, rooms_ix]:X数组,":"所有行,取rooms_ix列的数据(即,取第rooms_ix列的全部数据,从0开始计数)
            ------------
            np.r_是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等。
            np.c_是按行连接两个矩阵,就是把两矩阵左右相加,要求行数相等。即添加计算后的新列数据
            例:
                a = np.array([[1, 2, 3],
                              [7,8,9]])
                b = np.array([[4,5,6],
                              [1,2,3]])
                c = np.c_[a,b]
    
                >>> print(c)
                array([[1, 2, 3, 4, 5, 6],
                       [7, 8, 9, 1, 2, 3]])
            """
            rooms_per_household = X[:, rooms_ix] / X[:, households_ix]
            population_per_household = X[:, population_ix] / X[:, households_ix]
            if self.add_bedroom_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]
    
    
    def display_scores(scores):
        print("分  数:", scores)
        print("平均值:", scores.mean())
        print("标准差:", scores.std())
    
    
    if __name__ == '__main__':
        # 下载数据
        fetch_housing_data()
        # 读取数据
        housing = load_housing_data()
        # 按收入分组
        housing["income_cat"] = pd.cut(housing["median_income"],
                                       bins=[0, 1.5, 3, 4.5, 6, np.inf],
                                       labels=[1, 2, 3, 4, 5])
        # 按income_cat类别比例抽取 20% 的测试集 和 80% 的训练集
        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]
        # 删除收入分组,恢复数据
        for set_ in (strat_train_set, strat_test_set):
            set_.drop("income_cat", axis=1, inplace=True)
        # 寻找数据相关性
        corr_matrix = housing.corr()
        corr_matrix["median_house_value"].sort_values(ascending=False)
        """
        数据处理:
            1、训练集拆分成 预测集(housing)和标签(housing_labels);
            2、预测集再拆分出 数字属性(housing_num)和 文本属性(ocean_proximity)
        """
        housing = strat_train_set.drop("median_house_value", axis=1)
        housing_labels = strat_train_set["median_house_value"].copy()
        housing_num = housing.drop("ocean_proximity", axis=1)
        """
        数值属性流水线:
            1、SimpleImputer(中位数填充缺失值);
            2、CombinedAttributesAdder(自定义转化器);
            3、StandardScaler(特征缩放)
        """
        num_pipeline = Pipeline([
            ('imputer', SimpleImputer(strategy="median")),
            ('attribs_adder', CombinedAttributesAdder()),
            ('std_scaler', StandardScaler()),
        ])
        housing_num_tr = num_pipeline.fit_transform(housing_num)
    
        num_attribs = list(housing_num)  # 获取数字集列头列表
        cat_attribs = ["ocean_proximity"]
        """
        所有属性的流水线:
            1、num_pipeline(数值属性流水线);
            2、OneHotEncoder(文本属性 转换为 独热向量)
        """
        full_pipeline = ColumnTransformer([
            ("num", num_pipeline, num_attribs),
            ("cat", OneHotEncoder(), cat_attribs),
        ])
        housing_prepared = full_pipeline.fit_transform(housing)
        
        print("********************* 线性回归模型 *********************")
        # 线性回归模型
        lin_reg = LinearRegression()
        lin_reg.fit(housing_prepared, housing_labels)
        housing_predictions = lin_reg.predict(housing_prepared)
        lin_mse = mean_squared_error(housing_labels, housing_predictions)
        lin_rmse = np.sqrt(lin_mse)
        print(lin_rmse)
        # K-交叉验证 - 线性回归模型
        lin_scores = cross_val_score(lin_reg, housing_prepared, housing_labels, scoring="neg_mean_squared_error", cv=10)
        lin_rmse_scores = np.sqrt(-lin_scores)
        display_scores(lin_rmse_scores)
        
        print("********************* 决策树模型 *********************")
        # 决策树模型
        tree_reg = DecisionTreeRegressor()
        tree_reg.fit(housing_prepared, housing_labels)
        housing_predictions = tree_reg.predict(housing_prepared)
        tree_mse = mean_squared_error(housing_labels, housing_predictions)
        tree_rmse = np.sqrt(tree_mse)
        print(tree_rmse)
        # K-交叉验证 - 决策树模型
        scores = cross_val_score(tree_reg, housing_prepared, housing_labels, scoring="neg_mean_squared_error", cv=10)
        tree_rmse_scores = np.sqrt(-scores)
        display_scores(tree_rmse_scores)
        
        print("********************* 随机森林模型 *********************")
        # 随机森林模型
        forest_reg = RandomForestRegressor()
        forest_reg.fit(housing_prepared, housing_labels)
        housing_predictions = forest_reg.predict(housing_prepared)
        forest_mse = mean_squared_error(housing_labels, housing_predictions)
        forest_rmse = np.sqrt(forest_mse)
        print(forest_rmse)
        # K-交叉验证 - 随机森林模型
        forest_scores = cross_val_score(forest_reg, housing_prepared, housing_labels, scoring="neg_mean_squared_error", cv=10)
        forest_rmse_scores = np.sqrt(-forest_scores)
        display_scores(forest_rmse_scores)
        
        print("********************* 超参数微调 随机森林模型 网格搜索 *********************")
        """
        模型参数微调:
            模型:随机森林模型
            方法:网格搜索
        """
        param_grid = [
            {"n_estimators": [3, 10, 30], "max_features": [2, 4, 6, 8]},
            {"bootstrap": [False], "n_estimators": [3, 10], "max_features": [2, 3, 4]},
        ]
        forest_reg = RandomForestRegressor()
        grid_search = GridSearchCV(forest_reg, param_grid, cv=5, scoring="neg_mean_squared_error", return_train_score=True)
        grid_search.fit(housing_prepared, housing_labels)
        # 微调结果
        cvres = grid_search.cv_results_
        for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
            print(np.sqrt(-mean_score), params)
        
        print("********************* 超参数微调 随机森林模型 随机搜索 *********************")
        """
        模型参数微调:
            模型:随机森林模型
            方法:随机搜索
        """
        param_distribs = {
            'n_estimators': randint(low=1, high=200),
            'max_features': randint(low=1, high=8),
        }
        forest_reg = RandomForestRegressor()
        rnd_search = RandomizedSearchCV(forest_reg, param_distributions=param_distribs,
                                        n_iter=10, cv=5, scoring='neg_mean_squared_error')
        rnd_search.fit(housing_prepared, housing_labels)
        # 微调结果
        cvres = rnd_search.cv_results_
        for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
            print(np.sqrt(-mean_score), params)
        
        print("********************* 随机森林最优模型 测试集评估系统 *********************")
        final_model = rnd_search.best_estimator_
    
        X_test = strat_test_set.drop("median_house_value", axis=1)
        y_test = strat_test_set["median_house_value"].copy()
    
        X_test_prepared = full_pipeline.transform(X_test)
        final_predictions = final_model.predict(X_test_prepared)
    
        final_mse = mean_squared_error(y_test, final_predictions)
        final_rmse = np.sqrt(final_mse)
        print(final_rmse)
        # 评估的精确度,计算泛化误差的95%置信区间:
        confidence = 0.95
        squared_errors = (final_predictions - y_test) ** 2
        np.sqrt(stats.t.interval(confidence, len(squared_errors) - 1,
                                 loc=squared_errors.mean(),
                                 scale=stats.sem(squared_errors)))
    
    
    展开全文
  • 深入浅出是课程的主要特点,整个课程都是通过简单的算术数字分布例子来讲述统计学概念,视频课程从均值、中位数、众数等简单的概论说起,灵活的运用抛硬币的事例深入浅出的讲述了一个个统计概念,开篇的讲述的如何教...
  • 当数据呈对称分布或接近对称分布时,均值、中位数、众数相等或接近相等,这时应选择均值作为集中趋势的代表值,因为均值包含了全部数据的信息 中位数 中间位置上的代表值。其特点是不受数据极端值...
  • 描述:中位数就是将数据按大小顺序排列后,处于中间位置的数 如果数据总个数为奇数,中位数就是中间位置的数 如果数据总个数为偶数,中位数就是中间两个数据的平均值 众数 描述:数据中出现次数最多的数 一组数据...
  • 为了保证UUID的唯一性,规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机、时序等元素。当然,你要说UUID是不是绝对的不会出现重复的,这个也不能这样说的(我下面会提到)。 UUID具有...
  • GUID生成Int64值后是否还具有唯一性测试 2.   Random生成高唯一性随机码   今天分享的主题是:如何在高并发分布式系统生成全局唯一 Id。 但这篇博文实际上是“半分享半讨论”的博文: 1)  半...
  • 数据的概括度量 数据分布的特征可以从三个方面进行测度和描述: ①分布的集中趋势,反映各数据向其中心值靠拢或聚集的程度 ②分布的离散程度,反映各数据远离其中心值的趋势 ③分布的形状,反映数据分布的偏态和峰...
  • 直接得出: 根据数组建立平衡二叉搜索树 java整体打印二叉树 判断平衡二叉树 判断完全二叉树 判断二叉搜索树 二叉搜索树实现 堆的简单实现 堆应用例题三连 一个数据流中,随时可以取得中位数。 金条 项目最大收益...
  • 一、题文计算机常用的十六进制是一种逢进的计数制,采用数字0到9和字母A到F共个计数符号,这些符号与十进制的数字的对应关系如下表:十六进制01234567十进制01234567十六进制89ABCDEF十进制89101112131415例如,用...
  • 超硬核!操作系统学霸笔记,考试复习面试全靠它

    万次阅读 多人点赞 2021-03-22 18:43:49
    进程可以具有并行(在多处理器的系统),但是程序没有 进程是竞争系统资源的基本单位 进程与程序的关系: 一个程序对应多个进程,一个进程又可以为多个程序服务。 进程控制 1.基本知识 进程控制是进程管理最...
  • uuid是128的全局唯一标识符(univeral unique identifier),通常用32的一个字符串的形式来表现。有时也称guid(global unique identifier)。python自带了uuid模块来进行uuid的生成和管理工作。 python的...
  • 8、16、32操作系统的区别

    万次阅读 多人点赞 2018-05-20 19:29:51
    简单的说32的CPU的数据线、地址线、控制线都是32的,即CPU一次能够传送、处理32的二进制,所以理论上它能够寻址的范围是2的32次方,即4个GB,所以即使你的电脑能够安装8个GB的内存,系统也只能使用前4个GB的...
  • 数据分析之描述统计分析

    千次阅读 2021-07-02 14:40:03
    描述统计分析1、概述2、数据的集中趋势分析2.1 定量数据:平均2.2 顺序数据2.2.1.描述统计分析 1、概述 \quad \quad描述统计分析(Description Statistics)是通过图表或数学方法,对数据资料进行整理、...
  • 超硬核!小白读了这篇文章,就能在算法圈混了

    万次阅读 多人点赞 2021-03-29 10:21:58
    1.2运算 1.3打擂台 1.4morris遍历 第二节 2.1睡眠排序 2.2会死的兔子 2.3矩阵快速幂 2.4摔手机/摔鸡蛋 时空复杂度目录 二分 尝试较优的策略 归纳表达式 写出暴力递归 改为动态规划 压缩空间 ...
  • 数据集中趋势 在统计研究,需要搜集大量数据并对其进行加工整理,大多数情况下数据都会呈现...根据统计学知识,集中趋势指平均,是一组数据有代表的值,这些数值趋向于落在数值大小排列的数据中心,被称为...
  • 在前一章,我们看到了一个具有临界点的系统的例子,并且我们探索了临界系统 - 分形几何的一个共同特性。 在本章中,我们将探讨临界系统的另外两个性质:重尾分布,我们在第五章中见过,和粉红噪声...
  • 在分布式Session存储方案,提到了分布式环境下,Session的四种存储方案,其中Session数据集中存储的方案是将Session集中存储在单独的服务器或集群上,也是比较常用的方式之一,但是这里面涉及到应用服务器每次请求...
  • 一文搞懂MySQL索引所有知识点(建议收藏)

    万次阅读 多人点赞 2020-10-24 12:19:05
    主键具备唯一性(后面不会有的数据),不需再向后查找,查询终止。将结果集返回给用户。 可以看到B+树可以保证等值和范围查询的快速查找,MySQL的索引就采用了B+树的数据结构。 Mysql的索引实现 介绍完了索引数据...
  • 各种获取设备唯一标识的方法介绍, 实现获取唯一标识的最好的方法。
  • 有很多场景和需求你需要用到手机设备的唯一标识符。 在Android,有以下几种方法获取这样的ID。 1. The IMEI: 仅仅只对Android手机有效: 1 2 TelephonyManager TelephonyMgr = (TelephonyManager)...
  • 深入理解Java枚举类型(enum)

    万次阅读 多人点赞 2017-05-13 18:27:14
    枚举类型是Java 5新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁、安全以及便捷。...
  • 华为C语言编程规范(精华总结)

    万次阅读 多人点赞 2020-03-24 09:48:55
    所有头文件都应当使用#define 防止头文件被多重包含,命名格式为FILENAME_H,为了保证唯一性,更好的命名是PROJECTNAME_PATH_FILENAME_H。 注:没有在宏最前面加上单下划线"_",是因为一般以单下划线"_"和双下划线"_...
  • 从数字艺术品到 NFT

    万次阅读 2022-02-18 09:23:26
    过去几年里,数字艺术品探索了采用 NFT(非同质化代币,Non Fungible Token)这一新的数字媒介形式进行资产交易...类似于传统的物理创作的艺术品,数字艺术品被认为具备唯一性和收藏价值。由于数字艺术品的交易多通过加
  • 我们都知道微控制器只能使用...但是,如果我们需要将数字量转换成模拟信号,那么就需要DAC(模转换器)。 使用数字转模拟转换器的简单示例是在工作室录制一首歌曲,歌手使用麦克风唱歌。这些模拟声波被转换成数...
  • 虽然我不知道为什么,但是看起来很不舒服,我认为我应该只保留小数点后两数字找到了一种方法: 将{0}更改为{: .2f}# 一家商场在降价促销。如果购买金额50-100元(包含50元和100元)之间,# 会给10%的折扣,如果购买...
  • 唯一ID生成算法剖析

    千次阅读 2019-10-08 14:21:58
    引在业务开发,大量场景需要唯一ID来进行标识:用户需要唯一身份标识;商品需要唯一标识;消息需要唯一标识;事件需要唯一标识…等等,都需要全局唯一ID,尤其是分布式场景下。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 192,585
精华内容 77,034
热门标签
关键字:

中位数具有唯一性吗