Python数据分析学习

转摘:https://segmentfault.com/a/1190000015440560

一、数据初探

首先导入要使用的科学计算包numpy,pandas,可视化matplotlib,seaborn,以及机器学习包

 1 import pandas as pd
 2 import numpy as np
 3 import seaborn as sns
 4 import matplotlib as mpl
 5
 6 import matplotlib.pyplot as plt
 7 from IPython.display import display
 8 plt.style.use("fivethirtyeight")
 9 sns.set_style({‘font.sans-serif‘:[‘simhei‘,‘Arial‘]})
10 %matplotlib inline
11
12
13 # 检查Python版本
14 from sys import version_info
15 if version_info.major != 3:
16     raise Exception(‘请使用Python 3 来完成此项目‘)

引入模块与包

然后导入数据,并进行初步的观察,这些观察包括了解数据特征的缺失值异常值,以及大概的描述性统计

1 # 导入二手房数据
2 lianjia_df = pd.read_csv(‘lianjia.csv‘)
3 display(lianjia_df.head(n=2))

读取CSV数据

初步观察到一共有11个特征变量,Price 在这里是我们的目标变量,然后我们继续深入观察一下。

1 # 检查缺失值情况
2 lianjia_df.info()

检查缺失值情况

发现了数据集一共有23677条数据,其中Elevator特征有明显的缺失值。

lianjia_df.describe()

数据初始描述信息

上面结果给出了特征值是数值的一些统计值,包括平均数标准差中位数最小值最大值25%分位数75%分位数。这些统计结果简单直接,对于初始了解一个特征好坏非常有用,比如我们观察到 Size 特征 的最大值为1019平米,最小值为2平米,那么我们就要思考这个在实际中是不是存在的,如果不存在没有意义,那么这个数据就是一个异常值,会严重影响模型的性能。

 1 # 添加新特征房屋均价
 2 df = lianjia_df.copy()
 3 df[‘PerPrice‘] = lianjia_df[‘Price‘]/lianjia_df[‘Size‘]
 4
 5 # 重新摆放列位置
 6 columns = [‘Region‘, ‘District‘, ‘Garden‘, ‘Layout‘, ‘Floor‘, ‘Year‘, ‘Size‘, ‘Elevator‘, ‘Direction‘, ‘Renovation‘, ‘PerPrice‘, ‘Price‘]
 7 df = pd.DataFrame(df, columns = columns)
 8
 9 # 重新审视数据集
10 display(df.head(n=2))

添加新特征房屋均价

发现 Id 特征其实没有什么实际意义,所以将其移除。由于房屋单价分析起来比较方便,简单的使用总价/面积就可得到,所以增加一个新的特征 PerPrice(只用于分析,不是预测特征)。另外,特征的顺序也被调整了一下,看起

来比较舒服。

二、可视化分析

Region特征分析:对于区域特征,我们可以分析不同区域房价和数量的对比。

 1 # 对二手房区域分组对比二手房数量和每平米房价
 2 df_house_count = df.groupby(‘Region‘)[‘Price‘].count().sort_values(ascending=False).to_frame().reset_index()
 3 df_house_mean = df.groupby(‘Region‘)[‘PerPrice‘].mean().sort_values(ascending=False).to_frame().reset_index()
 4
 5 f, [ax1,ax2,ax3] = plt.subplots(3,1,figsize=(20,15))
 6 sns.barplot(x=‘Region‘, y=‘PerPrice‘, palette="Blues_d", data=df_house_mean, ax=ax1)
 7 ax1.set_title(‘北京各大区二手房每平米单价对比‘,fontsize=15)
 8 ax1.set_xlabel(‘区域‘)
 9 ax1.set_ylabel(‘每平米单价‘)
10
11 sns.barplot(x=‘Region‘, y=‘Price‘, palette="Greens_d", data=df_house_count, ax=ax2)
12 ax2.set_title(‘北京各大区二手房数量对比‘,fontsize=15)
13 ax2.set_xlabel(‘区域‘)
14 ax2.set_ylabel(‘数量‘)
15
16 sns.boxplot(x=‘Region‘, y=‘Price‘, data=df, ax=ax3)
17 ax3.set_title(‘北京各大区二手房房屋总价‘,fontsize=15)
18 ax3.set_xlabel(‘区域‘)
19 ax3.set_ylabel(‘房屋总价‘)
20
21 plt.show()

区域特征

使用了pandas的网络透视功能 groupby 分组排序。区域特征可视化直接采用 seaborn 完成,颜色使用调色板 palette 参数,颜色渐变,越浅说明越少,反之越多。
可以观察到:

    • 二手房均价:西城区的房价最贵均价大约11万/平,因为西城在二环以里,且是热门学区房的聚集地。其次是东城大约10万/平,然后是海淀大约8.5万/平,其它均低于8万/平。
    • 二手房房数量:从数量统计上来看,目前二手房市场上比较火热的区域。海淀区和朝阳区二手房数量最多,差不多都接近3000套,毕竟大区,需求量也大。然后是丰台区,近几年正在改造建设,有赶超之势。
    • 二手房总价:通过箱型图看到,各大区域房屋总价中位数都都在1000万以下,且房屋总价离散值较高,西城最高达到了6000万,说明房屋价格特征不是理想的正太分布。

Size特征分析 :

1 f, [ax1,ax2] = plt.subplots(1, 2, figsize=(15, 5))
2 # 建房面积的分布情况
3 sns.distplot(df[‘Size‘], bins=20, ax=ax1, color=‘r‘)
4 sns.kdeplot(df[‘Size‘], shade=True, ax=ax1)
5 # 建房面积和出售价格的关系
6 sns.regplot(x=‘Size‘, y=‘Price‘, data=df, ax=ax2)
7 plt.show()

面积特征分析

Size 分布

通过 distplot kdeplot 绘制柱状图观察 Size 特征的分布情况,属于长尾类型的分布,这说明了有很多面积很大且超出正常范围的二手房。

Size 与 Price 的关系

通过 regplot 绘制了 Size 和 Price 之间的散点图,发现 Size 特征基本与Price呈现线性关系,符合基本常识,面积越大,价格越高。但是有两组明显的异常点:1. 面积不到10平米,但是价格超出10000万;2. 一个点面积超过了1000平米,价格很低,需要查看是什么情况。

1 df.loc[df[‘Size‘]< 10]

过滤面积小于10

df.loc[df[‘Size‘]>1000]

过滤面积大于1000

经观察这个异常点不是普通的民用二手房,很可能是商用房,所以才有1房间0厅确有如此大超过1000平米的面积,这里选择移除。

df = df[(df[‘Layout‘]!=‘叠拼别墅‘)&(df[‘Size‘]<1000)]

过滤数据

重新进行可视化发现就没有明显的异常点了

 Layout特征分析:

f, ax1= plt.subplots(figsize=(20,20))
sns.countplot(y=‘Layout‘, data=df, ax=ax1)
ax1.set_title(‘房屋户型‘,fontsize=15)
ax1.set_xlabel(‘数量‘)
ax1.set_ylabel(‘户型‘)
plt.show()

房型物征

这个特征真是不看不知道,各种厅室组合搭配,竟然还有9室3厅,4室0厅等奇怪的结构。其中,2室一厅占绝大部分,其次是3室一厅,2室2厅,3室两厅。但是仔细观察特征分类下有很多不规则的命名,比如2室一厅与2房间1卫,还有别墅,没有统一的叫法。这样的特征肯定是不能作为机器学习模型的数据输入的,需要使用特征工程进行相应的处理。

Renovation 特征分析

1 df[‘Renovation‘].value_counts()

装修特征

精装 11345

简装 8497

其他 3239

毛坯 576

发现Renovation装修特征中竟然有南北,它属于朝向的类型,可能是因为爬虫过程中一些信息位置为空,导致“Direction”朝向特征出现在这里,所以需要清除或替

# 去掉错误数据“南北”,因为爬虫过程中一些信息位置为空,导致“Direction”的特征出现在这里,需要清除或替换
df[‘Renovation‘] = df.loc[(df[‘Renovation‘] != ‘南北‘), ‘Renovation‘]

# 画幅设置
f, [ax1,ax2,ax3] = plt.subplots(1, 3, figsize=(20, 5))
sns.countplot(df[‘Renovation‘], ax=ax1)
sns.barplot(x=‘Renovation‘, y=‘Price‘, data=df, ax=ax2)
sns.boxplot(x=‘Renovation‘, y=‘Price‘, data=df, ax=ax3)
plt.show()

按装修分类统计图

观察到,精装修的二手房数量最多,简装其次,也是我们平日常见的。而对于价格来说,毛坯类型却是最高,其次是精装修

Elevator 特征分析

初探数据的时候,我们发现 Elevator 特征是有大量缺失值的,这对于我们是十分不利的,首先我们先看看有多少缺失值:

misn = len(df.loc[(df[‘Elevator‘].isnull()), ‘Elevator‘])
print(‘Elevator缺失值数量为:‘+ str(misn))

电梯特征分析

Elevator 缺失值数量为:8237

这么多的缺失值怎么办呢?这个需要根据实际情况考虑,常用的方法有平均值/中位数填补法,直接移除,或者根据其他特征建模预测等。

这里我们考虑填补法,但是有无电梯不是数值,不存在平均值和中位数,怎么填补呢?这里给大家提供一种思路:就是根据楼层 Floor 来判断有无电梯,一般的楼层大于6的都有电梯,而小于等于6层的一般都没有电梯。有了这个标准,那么剩下的就简单了。

 1 # 由于存在个别类型错误,如简装和精装,特征值错位,故需要移除
 2 df[‘Elevator‘] = df.loc[(df[‘Elevator‘] == ‘有电梯‘)|(df[‘Elevator‘] == ‘无电梯‘), ‘Elevator‘]
 3
 4 # 填补Elevator缺失值
 5 df.loc[(df[‘Floor‘]>6)&(df[‘Elevator‘].isnull()), ‘Elevator‘] = ‘有电梯‘
 6 df.loc[(df[‘Floor‘]<=6)&(df[‘Elevator‘].isnull()), ‘Elevator‘] = ‘无电梯‘
 7
 8 f, [ax1,ax2] = plt.subplots(1, 2, figsize=(20, 10))
 9 sns.countplot(df[‘Elevator‘], ax=ax1)
10 ax1.set_title(‘有无电梯数量对比‘,fontsize=15)
11 ax1.set_xlabel(‘是否有电梯‘)
12 ax1.set_ylabel(‘数量‘)
13 sns.barplot(x=‘Elevator‘, y=‘Price‘, data=df, ax=ax2)
14 ax2.set_title(‘有无电梯房价对比‘,fontsize=15)
15 ax2.set_xlabel(‘是否有电梯‘)
16 ax2.set_ylabel(‘总价‘)
17 plt.show()

画统计图

结果观察到,有电梯的二手房数量居多一些,毕竟高层土地利用率比较高,适合北京庞大的人群需要,而高层就需要电梯。相应的,有电梯二手房房价较高,因为电梯前期装修费和后期维护费包含内了(但这个价格比较只是一个平均的概念,比如无电梯的6层豪华小区当然价格更高了)。

Year 特征分析:

1 grid = sns.FacetGrid(df, row=‘Elevator‘, col=‘Renovation‘, palette=‘seismic‘,size=4)
2 grid.map(plt.scatter, ‘Year‘, ‘Price‘)
3 grid.add_legend()

画散点图

在Renovation和Elevator的分类条件下,使用 FaceGrid 分析 Year 特征,观察结果如下:

  • 整个二手房房价趋势是随着时间增长而增长的;
  • 2000年以后建造的二手房房价相较于2000年以前有很明显的价格上涨;
  • 1980年之前几乎不存在有电梯二手房数据,说明1980年之前还没有大面积安装电梯;
  • 1980年之前无电梯二手房中,简装二手房占绝大多数,精装反而很少;

Floor 特征分析:

f, ax1= plt.subplots(figsize=(20,5))
sns.countplot(x=‘Floor‘, data=df, ax=ax1)
ax1.set_title(‘房屋户型‘,fontsize=15)
ax1.set_xlabel(‘数量‘)
ax1.set_ylabel(‘户型‘)
plt.show()

楼层特征统计图

可以看到,6层二手房数量最多,但是单独的楼层特征没有什么意义,因为每个小区住房的总楼层数都不一样,我们需要知道楼层的相对意义。另外,楼层与文化也有很重要联系,比如中国文化七上八下,七层可能受欢迎,房价也贵,而一般也不会有4层或18层。当然,正常情况下中间楼层是比较受欢迎的,价格也高,底层和顶层受欢迎度较低,价格也相对较低。所以楼层是一个非常复杂的特征,对房价影响也比较大。

  转摘:https://segmentfault.com/a/1190000015613967

  本篇将继续上一篇数据分析之后进行数据挖掘建模预测,这两部分构成了一个简单的完整项目。结合两篇文章通过数据分析和挖掘的方法可以达到二手房屋价格预测的效果。

  下面从特征工程开始讲述。

二、特征工程

  特征工程包括的内容很多,有特征清洗,预处理,监控等,而预处理根据单一特征或多特征又分很多种方法,如归一化,降维,特征选择,特征筛选等等。这么多的方法,为的是什么呢?其目的是让这些特征更友好的作为模型的输入,处理数据的好坏会严重的影响模型性能,而好的特征工程有的时候甚至比建模调参更重要。

  下面是继上一次分析之后对数据进行的特征工程,博主将一个一个帮大家解读。

 1 """
 2 特征工程
 3 """
 4 # 移除结构类型异常值和房屋大小异常值
 5 df = df[(df[‘Layout‘]!=‘叠拼别墅‘)&(df[‘Size‘]<1000)]
 6
 7 # 去掉错误数据“南北”,因为爬虫过程中一些信息位置为空,导致“Direction”的特征出现在这里,需要清除或替换
 8 df[‘Renovation‘] = df.loc[(df[‘Renovation‘] != ‘南北‘), ‘Renovation‘]
 9
10 # 由于存在个别类型错误,如简装和精装,特征值错位,故需要移除
11 df[‘Elevator‘] = df.loc[(df[‘Elevator‘] == ‘有电梯‘)|(df[‘Elevator‘] == ‘无电梯‘), ‘Elevator‘]
12
13 # 填补Elevator缺失值
14 df.loc[(df[‘Floor‘]>6)&(df[‘Elevator‘].isnull()), ‘Elevator‘] = ‘有电梯‘
15 df.loc[(df[‘Floor‘]<=6)&(df[‘Elevator‘].isnull()), ‘Elevator‘] = ‘无电梯‘
16
17 # 只考虑“室”和“厅”,将其它少数“房间”和“卫”移除
18 df = df.loc[df[‘Layout‘].str.extract(‘^\d(.*?)\d.*?‘) == ‘室‘]
19
20 # 提取“室”和“厅”创建新特征
21 df[‘Layout_room_num‘] = df[‘Layout‘].str.extract(‘(^\d).*‘, expand=False).astype(‘int64‘)
22 df[‘Layout_hall_num‘] = df[‘Layout‘].str.extract(‘^\d.*?(\d).*‘, expand=False).astype(‘int64‘)
23
24 # 按中位数对“Year”特征进行分箱
25 df[‘Year‘] = pd.qcut(df[‘Year‘],8).astype(‘object‘)
26
27 # 对“Direction”特征
28 d_list_one = [‘东‘,‘西‘,‘南‘,‘北‘]
29 d_list_two = [‘东西‘,‘东南‘,‘东北‘,‘西南‘,‘西北‘,‘南北‘]
30 d_list_three = [‘东西南‘,‘东西北‘,‘东南北‘,‘西南北‘]
31 d_list_four = [‘东西南北‘]
32 df[‘Direction‘] = df[‘Direction‘].apply(direct_func)
33 df = df.loc[(df[‘Direction‘]!=‘no‘)&(df[‘Direction‘]!=‘nan‘)]
34
35 # 根据已有特征创建新特征
36 df[‘Layout_total_num‘] = df[‘Layout_room_num‘] + df[‘Layout_hall_num‘]
37 df[‘Size_room_ratio‘] = df[‘Size‘]/df[‘Layout_total_num‘]
38
39 # 删除无用特征
40 df = df.drop([‘Layout‘,‘PerPrice‘,‘Garden‘],axis=1)
41
42 # 对于object特征进行onehot编码
43 df,df_cat = one_hot_encoder(df)

特征工程-数据处理

由于一些清洗处理在上一篇文章已经提到,所以从17行代码开始。

Layout  先看看没经处理的Layout特征值是什么样的:

1 df[‘Layout‘].value_counts()

显示房型分类统计信息

  大家也都看到了,特征值并不是像想象中的那么理想。有两种格式的数据,一种是"xx室xx厅",另一种是"xx房间xx卫",但是绝大多数都是xx室xx厅的数据。而对于像"11房间3卫"或者"5房间0卫"这些的Layout明显不是民住的二手房(不在我们的考虑范围之内),因此最后决定将所有"xx房间xx卫"格式的数据都移除掉,只保留"xx室xx厅"的数据。

Layout特征的处理如下:

  第2行的意思是只保留"xx室xx厅"数据,但是保留这种格式的数据也是不能作为模型的输入的,我们不如干脆将"室"和"厅"都提取出来,单独作为两个新特征(如第5和6行),这样效果可能更好。

具体的用法就是使用 str.extract() 方法,里面写的是正则表达式。

1 # 只考虑“室”和“厅”,将其它少数“房间”和“卫”移除
2 df = df.loc[df[‘Layout‘].str.extract(‘^\d(.*?)\d.*?‘) == ‘室‘]
3
4 # 提取“室”和“厅”创建新特征
5 df[‘Layout_room_num‘] = df[‘Layout‘].str.extract(‘(^\d).*‘, expand=False).astype(‘int64‘)
6 df[‘Layout_hall_num‘] = df[‘Layout‘].str.extract(‘^\d.*?(\d).*‘, expand=False).astype(‘int64‘)

提取所需要的数据

Year分析:为建房的年限时间。年限种类很多,分布在1950和2018之间,如果每个不同的 Year 值都作为特征值,我们并不能找出 Year 对 Price 有什么影响,因为年限划分的太细了。因此,我们只有将连续数值型特征 Year 离散化,做分箱处理。

如何分箱还要看实际业务需求,博主为了方便并没有手动分箱,而使用了pandas的 qcut 采用中位数进行分割,分割数为8等份。

1 # 按中位数对“Year”特征进行分箱
2 df[‘Year‘] = pd.qcut(df[‘Year‘],8).astype(‘object‘)

按year进行分箱

将 Year 进行分箱的结果:

Direction分析:这个特征没处理之前更乱,原以为是爬虫的问题,但是亲自到链家看过,朝向确实是这样的

  如上所见,像"西南西北北"或者"东东南南"这样的朝向是不符合常识的(反正我是理解不了)。因此,我们需要将这些凌乱的数据进行处理,具体实现方式是博主自己写了一个函数 direct_func,主要思想就是将各种重复但顺序不一样的特征值合并,比如"西南北""南西北",并将不合理的一些值移除,如"西南西北北"等。

  然后通过 apply() 方法将 Direction 数据格式转换,代码如下:

1 # 对“Direction”特征
2 d_list_one = [‘东‘,‘西‘,‘南‘,‘北‘]
3 d_list_two = [‘东西‘,‘东南‘,‘东北‘,‘西南‘,‘西北‘,‘南北‘]
4 d_list_three = [‘东西南‘,‘东西北‘,‘东南北‘,‘西南北‘]
5 d_list_four = [‘东西南北‘]
6 df[‘Direction‘] = df[‘Direction‘].apply(direct_func)
7 df = df.loc[(df[‘Direction‘]!=‘no‘)&(df[‘Direction‘]!=‘nan‘)]

数据转换

处理完结果如下,所有的内容相同而顺序不同的朝向都合并了,异常朝向也被移除了。

创建新特征:

  有时候仅靠已有的一些特征是不够的,需要根据对业务的理解,定义一些的新特征,然后尝试这些新特征对模型的影响,在实战中会经常使用这种方法。

这里尝试将"室"与"厅"的数量相加作为一个总数量特征,然后将房屋大小Size与总数量的比值作为一个新特征,可理解为 "每个房间的平均面积大小"。当然,新特征不是固定的,可根据自己的理解来灵活的定义。

1 # 根据已有特征创建新特征
2 df[‘Layout_total_num‘] = df[‘Layout_room_num‘] + df[‘Layout_hall_num‘]
3 df[‘Size_room_ratio‘] = df[‘Size‘]/df[‘Layout_total_num‘]
4
5 # 删除无用特征
6 df = df.drop([‘Layout‘,‘PerPrice‘,‘Garden‘],axis=1)

创建新特征

最后删除旧的特征 Layout,PerPrice,Garden

One-hot coding:

  这部分是 One-hot 独热编码,因为像 Region,Year(离散分箱后),Direction,Renovation,Elevator等特征都是定类的非数值型类型,而作为模型的输入我们需要将这些非数值量化。

在没有一定顺序(定序类型)的情况下,使用独热编码处理定类数据是非常常用的做法,在pandas中非常简单,就是使用 get_dummies() 方法,而对于像Size这样的定比数据则不使用独热,博主这里用了一个自己封装的函数实现了定类数据的自动量化处理。

对于定类,定序,定距,定比这四个非常重要的数据类型相信加入知识星球的伙伴都非常熟悉了,想要了解的同学可以扫描最后二维码查看。

1 # 对于object特征进行onehot编码
2 df,df_cat = one_hot_encoder(df)

one hot 编码

  以上的特征工程就完成了。

特征相关性

  下面使用 seabornheatmap 方法对特征相关性进行可视化。

1 # data_corr
2 colormap = plt.cm.RdBu
3 plt.figure(figsize=(20,20))
4 # plt.title(‘Pearson Correlation of Features‘, y=1.05, size=15)
5 sns.heatmap(df.corr(),linewidths=0.1,vmax=1.0, square=True, cmap=colormap, linecolor=‘white‘, annot=True)

可视化

颜色偏红或者偏蓝都说明相关系数较大,即两个特征对于目标变量的影响程度相似,即存在严重的重复信息,会造成过拟合现象。因此,通过特征相关性分析,我们可以找出哪些特征有严重的重叠信息,然后择优选择。

三、数据建模预测

为了方便理解,博主在建模上做了一些精简,模型策略方法如下:

  • 使用Cart决策树的回归模型对二手房房价进行分析预测
  • 使用交叉验证方法充分利用数据集进行训练,避免数据划分不均匀的影响。
  • 使用GridSearchCV方法优化模型参数
  • 使用R2评分方法对模型预测评分

上面的建模方法比较简单,旨在让大家了解建模分析的过程。随着逐渐的深入了解,博主会介绍更多实战内容。

  数据划分

1 # 转换训练测试集格式为数组
2 features = np.array(features)
3 prices = np.array(prices)
4
5 # 导入sklearn进行训练测试集划分
6 from sklearn.model_selection import train_test_split
7 features_train, features_test, prices_train, prices_test = train_test_split(features, prices, test_size=0.2, random_state=0)

训练测试集

  将以上数据划分为训练集和测试集,训练集用于建立模型,测试集用于测试模型预测准确率。使用sklearn的 model_selection 实现以上划分功能。

  建立模型

 1 from sklearn.model_selection import KFold
 2 from sklearn.tree import DecisionTreeRegressor
 3 from sklearn.metrics import make_scorer
 4 from sklearn.model_selection import GridSearchCV
 5
 6 # 利用GridSearchCV计算最优解
 7 def fit_model(X, y):
 8     """ 基于输入数据 [X,y],利于网格搜索找到最优的决策树模型"""
 9
10     cross_validator = KFold(10, shuffle=True)
11     regressor = DecisionTreeRegressor()
12
13     params = {‘max_depth‘:[1,2,3,4,5,6,7,8,9,10]}
14     scoring_fnc = make_scorer(performance_metric)
15     grid = GridSearchCV(estimator = regressor, param_grid = params, scoring = scoring_fnc, cv = cross_validator)
16
17     # 基于输入数据 [X,y],进行网格搜索
18     grid = grid.fit(X, y)
19 #     print pd.DataFrame(grid.cv_results_)
20     return grid.best_estimator_
21
22 # 计算R2分数
23 def performance_metric(y_true, y_predict):
24     """计算并返回预测值相比于预测值的分数"""
25     from sklearn.metrics import r2_score
26     score = r2_score(y_true, y_predict)
27
28     return score

mode

  使用了 KFold 方法减缓过拟合,GridSearchCV 方法进行最优参数自动搜查,最后使用R2评分来给模型打分。

  调参优化模型

 1 import visuals as vs
 2
 3 # 分析模型
 4 vs.ModelLearning(features_train, prices_train)
 5 vs.ModelComplexity(features_train, prices_train)
 6
 7 optimal_reg1 = fit_model(features_train, prices_train)
 8
 9 # 输出最优模型的 ‘max_depth‘ 参数
10 print("最理想模型的参数 ‘max_depth‘ 是 {} 。".format(optimal_reg1.get_params()[‘max_depth‘]))
11
12 predicted_value = optimal_reg1.predict(features_test)
13 r2 = performance_metric(prices_test, predicted_value)
14
15 print("最优模型在测试数据上 R^2 分数 {:,.2f}。".format(r2))

参数调优

  由于决策树容易过拟合的问题,我们这里采取观察学习曲线的方法查看决策树深度,并判断模型是否出现了过拟合现象。以下是观察到的学习曲线图形:

  通过观察,最理想模型的参数"max_depth"是10,此种情况下达到了偏差与方差的最优平衡,最后模型在测试数据上的R2分数,也即二手房房价预测的准确率为:0.81

转摘:https://www.cnblogs.com/palace/p/9598256.html

1. 引言

Pandas是一个开源的Python数据分析库。Pandas把结构化数据分为了三类:

  • Series,1维序列,可视作为没有column名的、只有一个column的DataFrame;

  • DataFrame,同Spark SQL中的DataFrame一样,其概念来自于R语言,为多column并schema化的2维结构化数据,可视作为Series的容器(container);

  • Panel,为3维的结构化数据,可视作为DataFrame的容器;

DataFrame较为常见,因此本文主要讨论内容将为DataFrame。DataFrame的生成可通过读取纯文本、Json等数据来生成,亦可以通过Python对象来生成:

import pandas as pd
import numpy as np

df = pd.DataFrame({‘total_bill‘: [16.99, 10.34, 23.68, 23.68, 24.59],
                   ‘tip‘: [1.01, 1.66, 3.50, 3.31, 3.61],
                   ‘sex‘: [‘Female‘, ‘Male‘, ‘Male‘, ‘Male‘, ‘Female‘]})

对于DataFrame,我们可以看到其固有属性:

# data type of columns
print df.dtypes
# indexes
print df.index
# return pandas.Index
print df.columns
# each row, return array[array]
print df.values
  • .index,为行索引

  • .columns,为列名称(label)

  • .dtype,为列数据类型

2. SQL操作

官方Doc给出了部分SQL的Pandas实现。在此基础上,本文给出了一些扩充说明。以下内容基于Python 2.7 + Pandas 0.18.1的版本。

select

SQL中的select是根据列的名称来选取;Pandas则更为灵活,不但可根据列名称选取,还可以根据列所在的position选取。相关函数如下:

  • loc,基于列label,可选取特定行(根据行index);

  • iloc,基于行/列的position;

print df.loc[1:3, [‘total_bill‘, ‘tip‘]]
print df.loc[1:3, ‘tip‘: ‘total_bill‘]
print df.iloc[1:3, [1, 2]]
print df.iloc[1:3, 1: 3]
  • at,根据指定行index及列label,快速定位DataFrame的元素;

  • iat,与at类似,不同的是根据position来定位的;

print df.at[3, ‘tip‘]
print df.iat[3, 1]
  • ix,为loc与iloc的混合体,既支持label也支持position;

print df.ix[1:3, [1, 2]]
print df.ix[1:3, [‘total_bill‘, ‘tip‘]]

此外,有更为简洁的行/列选取方式:

print df[1: 3]
print df[[‘total_bill‘, ‘tip‘]]
# print df[1:2, [‘total_bill‘, ‘tip‘]]  # TypeError: unhashable type

where

Pandas实现where filter,较为常用的办法为df[df[colunm] boolean expr],比如:

print df[df[‘sex‘] == ‘Female‘]
print df[df[‘total_bill‘] > 20]

# or
print df.query(‘total_bill > 20‘)

在where子句中常常会搭配and, or, in, not关键词,Pandas中也有对应的实现:

# and
print df[(df[‘sex‘] == ‘Female‘) & (df[‘total_bill‘] > 20)]
# or
print df[(df[‘sex‘] == ‘Female‘) | (df[‘total_bill‘] > 20)]
# in
print df[df[‘total_bill‘].isin([21.01, 23.68, 24.59])]
# not
print df[-(df[‘sex‘] == ‘Male‘)]
print df[-df[‘total_bill‘].isin([21.01, 23.68, 24.59])]
# string function
print df = df[(-df[‘app‘].isin(sys_app)) & (-df.app.str.contains(‘^微信\d+$‘))]

对where条件筛选后只有一行的dataframe取其中某一列的值,其两种实现方式如下:

total = df.loc[df[‘tip‘] == 1.66, ‘total_bill‘].values[0]
total = df.get_value(df.loc[df[‘tip‘] == 1.66].index.values[0], ‘total_bill‘)

distinct

drop_duplicates根据某列对dataframe进行去重:

df.drop_duplicates(subset=[‘sex‘], keep=‘first‘, inplace=True)

包含参数:

  • subset,为选定的列做distinct,默认为所有列;

  • keep,值选项{‘first‘, ‘last‘, False},保留重复元素中的第一个、最后一个,或全部删除;

  • inplace ,默认为False,返回一个新的dataframe;若为True,则返回去重后的原dataframe

group

group一般会配合合计函数(Aggregate functions)使用,比如:count、avg等。Pandas对合计函数的支持有限,有count和size函数实现SQL的count:

print df.groupby(‘sex‘).size()
print df.groupby(‘sex‘).count()
print df.groupby(‘sex‘)[‘tip‘].count()

对于多合计函数,

select sex, max(tip), sum(total_bill) as total
from tips_tb
group by sex;

实现在agg()中指定dict:

print df.groupby(‘sex‘).agg({‘tip‘: np.max, ‘total_bill‘: np.sum})

# count(distinct **)
print df.groupby(‘tip‘).agg({‘sex‘: pd.Series.nunique})

as

SQL中使用as修改列的别名,Pandas也支持这种修改:

# first implementation
df.columns = [‘total‘, ‘pit‘, ‘xes‘]
# second implementation
df.rename(columns={‘total_bill‘: ‘total‘, ‘tip‘: ‘pit‘, ‘sex‘: ‘xes‘}, inplace=True)

其中,第一种方法的修改是有问题的,因为其是按照列position逐一替换的。因此,我推荐第二种方法。

join

Pandas中join的实现也有两种:

# 1.
df.join(df2, how=‘left‘...)

# 2.
pd.merge(df1, df2, how=‘left‘, left_on=‘app‘, right_on=‘app‘)

第一种方法是按DataFrame的index进行join的,而第二种方法才是按on指定的列做join。Pandas满足left、right、inner、full outer四种join方式。

order

Pandas中支持多列order,并可以调整不同列的升序/降序,有更高的排序自由度:

print df.sort_values([‘total_bill‘, ‘tip‘], ascending=[False, True])

top

对于全局的top:

print df.nlargest(3, columns=[‘total_bill‘])

对于分组top,MySQL的实现(采用自join的方式):

select a.sex, a.tip
from tips_tb a
where (
    select count(*)
    from tips_tb b
    where b.sex = a.sex and b.tip > a.tip
) < 2
order by a.sex, a.tip desc;

Pandas的等价实现,思路与上类似:

# 1.
df.assign(rn=df.sort_values([‘total_bill‘], ascending=False)
          .groupby(‘sex‘)
          .cumcount()+1)    .query(‘rn < 3‘)    .sort_values([‘sex‘, ‘rn‘])

# 2.
df.assign(rn=df.groupby(‘sex‘)[‘total_bill‘]
          .rank(method=‘first‘, ascending=False))     .query(‘rn < 3‘)     .sort_values([‘sex‘, ‘rn‘])

replace

replace函数提供对dataframe全局修改,亦可通过where条件进行过滤修改(搭配loc):

# overall replace
df.replace(to_replace=‘Female‘, value=‘Sansa‘, inplace=True)

# dict replace
df.replace({‘sex‘: {‘Female‘: ‘Sansa‘, ‘Male‘: ‘Leone‘}}, inplace=True)

# replace on where condition
df.loc[df.sex == ‘Male‘, ‘sex‘] = ‘Leone‘

自定义

除了上述SQL操作外,Pandas提供对每列/每一元素做自定义操作,为此而设计以下三个函数:

  • map(func),为Series的函数,DataFrame不能直接调用,需取列后再调用;

  • apply(func),对DataFrame中的某一行/列进行func操作;

  • applymap(func),为element-wise函数,对每一个元素做func操作

print df[‘tip‘].map(lambda x: x - 1)
print df[[‘total_bill‘, ‘tip‘]].apply(sum)
print df.applymap(lambda x: x.upper() if type(x) is str else x)

3. 实战

环比增长

现有两个月APP的UV数据,要得到月UV环比增长;该操作等价于两个Dataframe left join后按指定列做减操作:

def chain(current, last):
    df1 = pd.read_csv(current, names=[‘app‘, ‘tag‘, ‘uv‘], sep=‘\t‘)
    df2 = pd.read_csv(last, names=[‘app‘, ‘tag‘, ‘uv‘], sep=‘\t‘)
    df3 = pd.merge(df1, df2, how=‘left‘, on=‘app‘)
    df3[‘uv_y‘] = df3[‘uv_y‘].map(lambda x: 0.0 if pd.isnull(x) else x)
    df3[‘growth‘] = df3[‘uv_x‘] - df3[‘uv_y‘]
    return df3[[‘app‘, ‘growth‘, ‘uv_x‘, ‘uv_y‘]].sort_values(by=‘growth‘, ascending=False)

差集

对于给定的列,一个Dataframe过滤另一个Dataframe该列的值;相当于集合的差集操作:

def difference(left, right, on):
    """
    difference of two dataframes
    :param left: left dataframe
    :param right: right dataframe
    :param on: join key
    :return: difference dataframe
    """
    df = pd.merge(left, right, how=‘left‘, on=on)
    left_columns = left.columns
    col_y = df.columns[left_columns.size]
    df = df[df[col_y].isnull()]
    df = df.ix[:, 0:left_columns.size]
    df.columns = left_columns
    return df

原文地址:https://www.cnblogs.com/abdm-989/p/12129135.html

时间: 2024-09-29 11:35:01

Python数据分析学习的相关文章

Python数据分析学习路径图:堪称史上最全

Python是一种面向对象.直译式计算机程序设计语言,由Guido van Rossum于1989年底发明.由于他简单.易学.免费开源.可移植性.可扩展性等特点,Python又被称之为胶水语言.下图为主要程序语言近年来的流行趋势,Python受欢迎程度扶摇直上. 图片来源用Python玩转数据 由于Python拥有非常丰富的库,使其在数据分析领域也有广泛的应用.由于Python本身有十分广泛的应用,本期Python数据分析路线图主要从数据分析从业人员的角度讲述Python数据分析路线图.整个路线

python数据分析学习(2)pandas二维工具DataFrame讲解

目录 二:pandas数据结构介绍 ??下面继续讲解pandas的第二个工具DataFrame. 二:pandas数据结构介绍 2.DataFarme ??DataFarme表示的是矩阵的数据表,包含已排序的列集合,是一个二维数据工具.每一列可以是不同的数据类型值.它既有行索引又有列索引,可以看作是一组共享相同索引的Series对象.DataFarme的数组方法有很多,比如用index.name获取某列的值,用values获取行的值.这里先介绍一些常用的知识. ??(1)构建DataFrame

大数据分析学习之路

大数据分析学习之路 目录: 一.大数据分析的五个基本方面 二.如何选择适合的数据分析工具 三.如何区分三个大数据热门职业 四.从菜鸟成为数据科学家的 9步养成方案 五.从入门到精通--快速学会大数据分析                             一.大数据分析的五个基本方面 1,可视化分析 大数据分析的使用者有大数据分析专家,同时还有普通用户,但是他们二者对于大数据分析最基本的要求就是可视化分析,因为可视化分析能够直观的呈现大数据特点,同时能够非常容易被读者所接受,就如同看图说话一

Python 数据类型转换

函数                      描述      int(x [,base ])         将x转换为一个整数      long(x [,base ])        将x转换为一个长整数      float(x )               将x转换到一个浮点数      complex(real [,imag ])  创建一个复数      str(x )                 将对象 x 转换为字符串      repr(x )             

Python数据类型转换

Python数据类型之间的转换 函数 描述 int(x [,base]) 将x转换为一个整数 long(x [,base] ) 将x转换为一个长整数 float(x) 将x转换到一个浮点数 complex(real [,imag]) 创建一个复数 str(x) 将对象 x 转换为字符串 repr(x) 将对象 x 转换为表达式字符串 eval(str) 用来计算在字符串中的有效Python表达式,并返回一个对象 tuple(s) 将序列 s 转换为一个元组 list(s) 将序列 s 转换为一个

Python 数据图表工具的比较

Python 的科学栈相当成熟,各种应用场景都有相关的模块,包括机器学习和数据分析.数据可视化是发现数据和展示结果的重要一环,只不过过去以来,相对于 R 这样的工具,发展还是落后一些. 幸运的是,过去几年出现了很多新的Python数据可视化库,弥补了一些这方面的差距.matplotlib 已经成为事实上的数据可视化方面最主要的库,此外还有很多其他库,例如vispy,bokeh, seaborn,  pyga, folium 和networkx,这些库有些是构建在 matplotlib 之上,还有

Python 数据分析(二 本实验将学习利用 Python 数据聚合与分组运算,时间序列,金融与经济数据应用等相关知识

Python 数据分析(二) 本实验将学习利用 Python 数据聚合与分组运算,时间序列,金融与经济数据应用等相关知识 第1节 groupby 技术 第2节 数据聚合 第3节 分组级运算和转换 第4节 透视表和交叉表 第5节 时间序列 第6节 日期的规范.频率以及移动 第7节 时区处理 第8节 时期及算术运算 第9节 重采样及频率转换 第10节 时间序列绘图 groupby 技术 一.实验简介 Python 数据分析(二)需要同学们先行学完 Python 数据分析(一)的课程. 对数据集进行分

Python数据可视化编程实战——导入数据

1.从csv文件导入数据 原理:with语句打开文件并绑定到对象f.不必担心在操作完资源后去关闭数据文件,with的上下文管理器会帮助处理.然后,csv.reader()方法返回reader对象,通过该对象遍历所读取文件的所有行. 1 #!/usr/bin/env python 2 3 import csv 4 5 filename = 'ch02-data.csv' 6 7 data = [] 8 try: 9 with open(filename) as f: 10 reader = csv

大数据分析学习之使用R语言实战机器学习视频课程

大数据分析学习之使用R语言实战机器学习网盘地址:https://pan.baidu.com/s/1Yi9H6s8Eypg_jJJlQmdFSg 密码:0jz3备用地址(腾讯微云):https://share.weiyun.com/5tk6j1Z 密码:rdia9t 机器学习的核心主要侧重于找出复杂数据的意义.这是一个应用广泛的任务,机器学习的使用是非常广泛的. 本课程主要分为两个阶段,第一个部分:掌握R语言的处理数据的基本语法.第二部分:介绍机器学习原理,并通过大量的案例,为学员介绍R语言通过机