在综合实战项目中,“北京链家网”租房数据的抓取任务已在 上一篇 完成,得到了数据表bj_lianJia.csv,如图1所示。该数据表包含ID、城区名(district)、街道名(street)、小区名(community)、楼层信息(floor)、有无电梯(lift)、面积(area)、房屋朝向(toward)、户型(model)、房租(rent)等信息。
■ 图1 “北京链家网”租房数据表的部分数据展示
本节将任务进行如下分解。
(1) 重复行的处理:删除重复行。
(2) 缺失值的处理:数据表中lift和rent列有缺失值,分别采用不同的缺失值处理方式。
(3) 内容格式清洗。
① 将area列的“m2”删掉,这样area列的数据变为数值型,方便后面的数据分析。
② 将toward列中字符间的空格删掉,例如删掉“南 北”中的空格,变为“南北”。
③ 将model列的内容格式转换为“*室*厅*卫”,例如“2房间1卫”转换为“2室0厅1卫”。
(4) 属性重构造:从floor列中分离出总楼层,形成一个新列,命名为“totalfloor”,例如“中楼层/6层”分离出总楼层“6”。
(5) 对房租rent列数据进行统计分析。
以上任务的实现步骤及代码如下。
(1) 导入库。其中re库为正则表达式库,是Python的标准库,主要用于字符串匹配。代码如下。
- import pandas as pd
- import re
(2) 读入数据。使用Pandas库的read_csv()方法读入“北京链家网”的租房数据集bj_lianJia.csv,其中,header=0表示数据表的第一行作为列名,usecols参数值表示使用数据表中列号为1~9的数据,也就是不使用列号为0的“ID”这一列数据。读入的数据列分别是:楼层(floor)、有无电梯(lift)、城区名(district)、街道名(street)、小区名(community)、面积(area)、房屋朝向(toward)、户型(model)、房租(rent)。代码如下。
- df=pd.read csv('bj lianJia.csv', encoding='gbk', header=0,usecols=[1,2,3,4
- 5,6,7,8,9])
- print(df)
输出结果为:
- floor lift district ... model
- 0 中楼层/6层 无 房山 ... 2室2厅1卫
- 1 低楼层/17层 有 顺义 ... 3室1卫
- 2 中楼层/6层 无 大兴 ... 2室1厅1卫
- ...
- 4338 高楼层/28层 有 朝阳 ... 2室1厅1卫
- 4339 低楼层/2层 有 怀柔 ... 5室2厅5卫
- 4340 低楼层/4层 无 通州 ... 4室2厅3卫
(3) 重复值处理。首先检测有无重复行,使用Pandas库的duplicated()方法。如果存在重复的行,使用drop_duplicates()方法删除这些重复行。代码如下。
- print ('----检测有无重复行----')
- print(len(dfldf.duplicated()])) # 原地修改 df
- df.drop duplicates(inplace=True)print('----打印删除重复行后 df 的行数----)
- print(len(df))
输出结果为:
- - ---检测有无重复行-
- 15
- ----打印删除重复行后 df 的行数----
- 4326
(4) 缺失值处理。首先统计含有缺失值的列及数量。代码如下。
- print ('----未做缺失值处理之前----'!)
- print(df.isnul1() .sum())
输出结果为:
- ----未做缺失值处理之前---
- floor 0
- lift 8
- district 0
- street 0
- community 0
- area 0
- toward 0
- mode1 0
- rent 4
- dtype: int64
可以看出,lift列有8个缺失值,rent列存在4个缺失值,分别采取不同的方法对缺失值进行处理:使用填充法,将lift列的缺失值填充为固定值“未知”;使用删除法,对rent列有缺失值的行进行直接删除处理。代码如下。
- print ('----将 lift 列的缺失值填充为"未知"---')
- df['lift'].fillna('未知’,inplace=True)
- print(df.isnul1() .sum())
- print ('----将 rent 有缺失值的行直接删除----')
- df.dropna(subset=['rent'],inplace=True)
- print(df.isnull() .sum() )
- print(len(df)) # 输出删除缺失值后 df 的行数
输出结果为:
- ----将lift列的缺失值填充为“未知”---
- floor 0
- lift 0
- district 0
- street 0
- community 0
- area 0
- toward 0
- mode1 0
- rent 4
- dtype: int64
-
- ----将rent列有缺失值的行直接删除---
- floor 0
- lift 0
- district 0
- street 0
- community 0
- area 0
- toward 0
- mode1 0
- rent 0
- dtype: int64
- 4322
删除带缺失值的行后,此时DataFrame不再是连续的索引,可以使用reset_index()方法重置索引。代码如下。
- df=df .reset index(drop=True)
- print(df)
输出结果为:
- floor lift district ... model
- 0 中楼层/6层 无 房山 ... 2室2厅1卫
- 1 低楼层/17层 有 顺义 ... 3室1卫
- 2 中楼层/6层 无 大兴 ... 2室1厅1卫
- ...
- 4319 中楼层/8层 有 朝阳 ... 3室1厅1卫
- 4320 高楼层/28层 有 朝阳 ... 2室2厅1卫
- 4321 低楼层/2层 无 怀柔 ... 5室2厅5卫
- [4322 rows x 9 columns]
(5) 内容格式清洗。
① 将area列的“m2”删掉。首先使用正则表达式库re的findall()方法将数据表中area列的数字提取出来,这时得到的area列表中的数据舍弃了“m2”,然后再将area列表的数据写回数据表中。代码如下。
- area= re.findall('d+. d+',a) for a in df 'area'].values.tolist()]
- df['area']=[i for jin range(len(area)) for i in arealj]]
- print(df.loc[:5,'area'])
输出结果为:
- 0 85.00
- 1 107.00
- 2 72
- 3 71.13
- 4 54.41
- 5 132.00
- Name: area,dtype: object
② 将toward列中字符间的空格删掉。这里用到了Series 对象的替换方法str.replace(),语法格式为series对象sr.replace(pat,repl),其中,参数pat表示要替换的字符串,repl表示新字符串。在下面的代码中,df['toward']得到的数据类型为Series类型,在replace()方法中,要替换的字符串使用了正则表达式'\\s+',其含义是匹配任意多个空格,被替换的新字符串为空字符串,所以使用replace()方法将查找到的空格替换成空串,即删除了空格。代码如下。
- print(df.loc[:5,'toward'])
- df['toward']=df['toward'].str.replace('\s+',')
输出结果为:
- 0 南北
- 1 南
- 2 南北
- 3 东
- 4 东
- 5 南北
- Name: toward,dtype: object
③ 将model列的内容格式转换为“*室*厅*卫”。由于原始数据表中户型model的取值为3室2厅1卫或2房间1卫形式,还有少数取值为“未知室1厅1卫”,户型的表现形式不统一,现将其统一为“*室*厅*卫”,转换规则是:房间表示为室,没有给出厅数目表示为0厅,未知室表示为0室。代码如下。
- print ("----首先将 model 列中'未知·替换为'0'----")
- dff=dfLdfl'model'].str.contains('未知)==True]
- print'替换前:\n',dff)
- df.loc[dff.index,'model =dffl'model'].str.replace('未知',0')
- print('替换后:\n',df.loc dff.index])
- print("----然后将 model 列统一为¥室¥厅*卫----")
- model_n=_re.findall('d+',m) for m in dfl'model'] .values.tolist()]
- new model=list()
- for m in model n:
- if len(m)==3:
- new model.append(m[0]+'室'+m[1]+厅'+m[2]+'卫')
- elif len(m)==2:
- new model.append(m[o] +室'+0厅'+m[1]+卫')
- dfl'model =new model
- print(df.loc[:5,'model'])
输出结果为:
- ----首先将 model 列中·未知,替换为 0'----
- 替换前:
- floor lift district ... model rent
- 3964 低楼层/25层 有 海淀 ... 未知室 1厅1卫 38000.0
- [1 rows x 9 columns]
- 替换后:
- floor lift district ... model rent
- 3964 低楼层/25层 有 海淀 ... 0室0厅0卫 38000.0
- [1 rows x 9 columns]
- ----然后将 model 列统一为*室*厅*卫----
- rent
- 38000.0
- 0 2室1厅1卫
- 1 3室0厅1卫
- 3 3室0厅2卫
- 4 2室1厅1卫
- 5 3室2厅2卫
- Name: model,dtype: object
(6) 属性重构造:从floor列中分离出总楼层,形成一个新列。这里使用了字符串分割方法split(),该方法通过指定分隔符对字符串进行切分,返回分割后的字符串列表,例如“中楼层/6层”被split()方法通过分隔符“/”切分为['中楼层','6层']。然后将楼层写回到df['floor']中,将总楼层中的数字使用slice()方法提取出来,写入df['totalfloor']。代码如下。
- dff=df['floor'].str.split('/',expand=True)
- df['floor =dff 0]
- df['totalfloor']=dff[1].str.slice(0,-1,1)
- print(df.loc[:5,['floor','totalfloor']])
输出结果为:
- floor totalfloor
- 0 中楼层 6
- 1 低楼层 17
- 2 中楼层 6
- 3 中楼层 8
- 4 中楼层 4
-
(7) 对房租rent列数据进行统计分析。
(8) 保存处理后的数据。代码如下。
df.to csv('newbj lianJia.csv',encoding='gbk',index label='ID')