特征工程——数据清洗——字符串数值特征提取(1)
近月重点研习了特征工程相关的数据清洗工作的核心内容。在实际操作过程中遇到了以往从未见过的问题,并且只是自己刚开始接触这方面的知识,对于Python相关知识掌握还不够熟练。
数据来自于某数据集——北京房价预测的数据清洗实践。
导入数据集,并看数据信息,变量,数据类型等。
data = pd.read_csv('houseInfo_2018_09_10.csv')
bj_data = data[data['region'] == 'bj']
print(bj_data.info())
print(bj_data.head())
Int64Index: 48324 entries, 418423 to 627466
Data columns (total 22 columns):
introduction_house 48324 non-null object
community_house 48324 non-null object
href_house 48324 non-null object
unit_house 48324 non-null object
size_house 48324 non-null object
direction_house 48324 non-null object
decoration_house 48313 non-null object
elevator_house 47437 non-null object
type_house 48324 non-null object
years_house 48311 non-null object
area_house 48324 non-null object
interests_house 48324 non-null object
watch_times 48324 non-null object
submit_period 0 non-null float64
years_period 39804 non-null object
tax_free 45673 non-null object
total_price 48324 non-null float64
smeter_price 48324 non-null object
region 48324 non-null object
info_cluster 0 non-null object
info_flood 0 non-null object
info_follow 0 non-null object
introduction_house community_house href_house unit_house size_house direction_house decoration_house elevator_house type_house years_house area_house interests_house watch_times submit_period years_period tax_free total_price smeter_price region info_cluster info_flood info_follow
418423 长城脚下,精装修别墅,满五唯一,业主诚意出售。 清凉盛景 https://bj.lianjia.com/ershoufang/101103236018.html 5室3厅 269.93平米 南 北 精装 NaN 底层(共2层) 暂无数据 延庆其它 3人关注 0次带看 NaN NaN 房本满五年 998.0 单价36973元/平米 bj NaN NaN NaN
418424 商品房 满五年 家庭名下一套住房 环境好 清凉盛景 https://bj.lianjia.com/ershoufang/101102750912.html 4室2厅 269.93平米 南 北 精装 NaN 2层 2010年建暂无数据 延庆其它 14人关注 0次带看 NaN NaN 房本满五年 1250.0 单价46309元/平米 bj NaN NaN NaN
418425 湖南小区 2室1厅 179万 湖南小区 https://bj.lianjia.com/ershoufang/101103256056.html 2室1厅 71.45平米 南 北 其他 NaN 中楼层(共6层) 1996年建暂无数据 延庆其它 8人关注 0次带看 NaN NaN 房本满五年 179.0 单价25053元/平米 bj NaN NaN NaN
418426 清凉盛景 4室2厅 1180万 清凉盛景 https://bj.lianjia.com/ershoufang/101103166425.html 4室2厅 252.16平米 南 北 精装 NaN 底层(共2层) 2010年建暂无数据 延庆其它 0人关注 0次带看 NaN NaN 房本满五年 1180.0 单价46796元/平米 bj NaN NaN NaN
424545 君山别墅 边户独栋 391平毛坯房 君山别墅 https://bj.lianjia.com/ershoufang/101101025128.html 3室2厅 391.86平米 南 北 毛坯 NaN 底层(共2层) 2011年建暂无数据 密云其它 107人关注 1次带看 NaN 房本满两年 房本满五年 1000.0 单价25520元/平米 bj NaN NaN NaN
观察结果表明
进一步审视数据中的缺失情况后发现
stats = []
for col in bj_data.columns:
stats.append((col, bj_data[col].nunique(), bj_data[col].isnull().sum() * 100 / bj_data.shape[0],
bj_data[col].value_counts(normalize=True, dropna=False).values[0] * 100, bj_data[col].dtype))
stats_df = pd.DataFrame(stats, columns=['Feature', 'Unique_values', 'Percentage of missing values',
'Percentage of values in the biggest category', 'type'])
print(stats_df.sort_values('Percentage of missing values', ascending=False)[:23])
Feature Unique_values Percentage of missing values Percentage of values in the biggest category type
21 info_follow 0 100.000000 100.000000 object
13 submit_period 0 100.000000 100.000000 float64
20 info_flood 0 100.000000 100.000000 object
19 info_cluster 0 100.000000 100.000000 object
14 years_period 1 17.630991 82.369009 object
15 tax_free 1 5.485887 94.514113 object
7 elevator_house 6 1.835527 62.018873 object
9 years_house 210 0.026902 3.186822 object
6 decoration_house 15 0.022763 49.163976 object
18 region 1 0.000000 100.000000 object
17 smeter_price 35405 0.000000 0.066220 object
16 total_price 1804 0.000000 1.338879 float64
0 introduction_house 47779 0.000000 0.014486 object
12 watch_times 93 0.000000 33.451287 object
1 community_house 5674 0.000000 0.360070 object
10 area_house 235 0.000000 3.029551 object
8 type_house 291 0.000000 8.718235 object
5 direction_house 207 0.000000 45.348067 object
4 size_house 15283 0.000000 0.091052 object
3 unit_house 79 0.000000 36.845046 object
2 href_house 48323 0.000000 0.004139 object
11 interests_house 638 0.000000 2.460475 object
如果某个特征的数据缺失率(percentage of missing values)过高(达到或超过70%),则应予以删除;而当其数据缺失率达到或超过90%,同样也可予以删除。具体情况下,该特征对因变量的影响程度如何将直接影响模型性能。在筛选阶段需减少特征数量时,在本例中我们还需进一步清理一些非相关数据项(例如网站链接、别墅类信息等),这些问题均由网络爬虫遗留所致。
依然搬轮子,并打印看看。
bj_data.drop(['info_follow', 'submit_period', 'info_flood',
'introduction_house','community_house','region',
'info_cluster','href_house'], axis=1, inplace=True)
print(bj_data.shape)
print(bj_data.head())
(48324, 14)
unit_house size_house direction_house decoration_house elevator_house type_house years_house area_house interests_house watch_times years_period tax_free total_price smeter_price
418423 5室3厅 269.93平米 南 北 精装 NaN 底层(共2层) 暂无数据 延庆其它 3人关注 0次带看 NaN 房本满五年 998.0 单价36973元/平米
418424 4室2厅 269.93平米 南 北 精装 NaN 2层 2010年建暂无数据 延庆其它 14人关注 0次带看 NaN 房本满五年 1250.0 单价46309元/平米
418425 2室1厅 71.45平米 南 北 其他 NaN 中楼层(共6层) 1996年建暂无数据 延庆其它 8人关注 0次带看 NaN 房本满五年 179.0 单价25053元/平米
418426 4室2厅 252.16平米 南 北 精装 NaN 底层(共2层) 2010年建暂无数据 延庆其它 0人关注 0次带看 NaN 房本满五年 1180.0 单价46796元/平米
424545 3室2厅 391.86平米 南 北 毛坯 NaN 底层(共2层) 2011年建暂无数据 密云其它 107人关注 1次带看 房本满两年 房本满五年 1000.0 单价25520元/平米
可以看出数据集中的特征减少情况已经显现出来。进一步发现,在这些特征中有大量数据存在于文本内容中,并且需要从文本内容中首先提取相关信息。
数据清洗的核心原则是"早抛早回收";其中的主要做法是优先获取数值型数据,并尽可能在早期进行处理。
为了更好地从文本中提取出一系列数字特征,请问您具体指的是哪些?包括watch-times、size-house等指标。具体方法是通过应用正则表达式或者字符串切片的方法来获取这些数值,并将这些数值转换为数值型数据。
首先关注字符串切片的方法(例如size-house),它位于一列中其主要目的在于从该列中提取房屋尺寸的数据。例如269.93平方米这个数值即提取269.93这个数值自然会想到遍历该列中的所有元素放于一个新的列表中,并对这些字符串中的数字进行数值装换。
size_house0 = []
for sh1 in bj_data['size_house']:
size_house0.append(sh1[-8:-2])
但print size-house发现,这个切片sh1[-8:-2],根据结果有如下,
'281.64', '4室', '281.64', '280.65', '4室', '146.3'
基于索引的位置并不理想, 因为数据集中该特征存在错误. 实在糟糕极了. 因此之后我们采用正则化方法提取数值特征.
from funtools import reduce
def str2float(s):
return reduce(lambda x,y:x+int2dec(y),map(str2int,s.split('.')))
def char2num(s):
return {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}[s]
def str2int(s):
return reduce(lambda x,y:x*10+y,map(char2num,s))
def intLen(i):
return len('%d'%i)
def int2dec(i):
return i/(10**intLen(i))
size_house0 = []
for sh1 in bj_data['size_house']:
size_house0.append(str2float(''.join(re.findall(r"\d+\.?\d*",sh1))))
print(size_house0)
结果:269.93, 269.93, 71.45, 252.16, 391.86, 96.65···
又增添了另一个步骤,在遍历过程中识别出相应的数值。然而这些数值是以字符串形式存在的接下来将其转换为数值型数据。然后将其整合进数据集可以选择替换原有的数据集但由于新手不太熟悉操作流程所以采取了一种较为繁琐的手动方法。
bj_data.insert(2,'size_house1',size_house0)
bj_data.drop('size_house',axis=1, inplace=True)
print(bj_data.info())
print(bj_data.head())
结果:
········
total_price 48324 non-null float64
smeter_price 48324 non-null object
dtypes: float64(2), object(12)
memory usage: 5.5+ MB
None
unit_house size_house1 direction_house decoration_house elevator_house type_house years_house area_house interests_house watch_times years_period tax_free total_price smeter_price
418423 5室3厅 269.93 南 北 精装 NaN 底层(共2层) 暂无数据 延庆其它 3人关注 0次带看 NaN 房本满五年 998.0 单价36973元/平米
418424 4室2厅 269.93 南 北 精装 NaN 2层 2010年建暂无数据 延庆其它 14人关注 0次带看 NaN 房本满五年 1250.0 单价46309元/平米
418425 2室1厅 71.45 南 北 其他 NaN 中楼层(共6层) 1996年建暂无数据 延庆其它 8人关注 0次带看 NaN 房本满五年 179.0 单价25053元/平米
418426 4室2厅 252.16 南 北 精装 NaN 底层(共2层) 2010年建暂无数据 延庆其它 0人关注 0次带看 NaN 房本满五年 1180.0 单价46796元/平米
424545 3室2厅 391.86 南 北 毛坯 NaN 底层(共2层) 2011年建暂无数据 密云其它 107人关注 1次带看 房本满两年 房本满五年 1000.0 单价25520元/平米
此时输出用会发现float多一列,说明是成功的。
使用相同的策略来提取特征interests_house、watch_times以及smeter_price中的数值信息。
intertest_house0 =[]
for ih in bj_data['interests_house']:
intertest_house0.append(str2float(''.join(re.findall(r"\d+\.?\d*",ih))))
# print(intertest_house0)
bj_data.insert(2,'interests_house1',intertest_house0)
bj_data.drop('interests_house',axis=1, inplace=True)
watch_times0 = []
for wt in bj_data['watch_times']:
watch_times0.append(str2float(''.join(re.findall(r"\d+\.?\d*",wt))))
# print(watch_times0)
bj_data.insert(2,'watch_times1',watch_times0)
bj_data.drop('watch_times',axis=1, inplace=True)
smeter_price0 = []
for sp in bj_data['smeter_price']:
smeter_price0.append(str2float(''.join(re.findall(r"\d+\.?\d*",sp))))
# print(watch_times0)
bj_data.insert(2,'smeter_price1',smeter_price0)
bj_data.drop('smeter_price',axis=1, inplace=True)
结果:
····
unit_house size_house1 smeter_price1 watch_times1 interests_house1 direction_house decoration_house elevator_house type_house years_house area_house years_period tax_free total_price
418423 5室3厅 269.93 36973 0 3 南 北 精装 NaN 底层(共2层) 暂无数据 延庆其它 NaN 房本满五年 998.0
418424 4室2厅 269.93 46309 0 14 南 北 精装 NaN 2层 2010年建暂无数据 延庆其它 NaN 房本满五年 1250.0
418425 2室1厅 71.45 25053 0 8 南 北 其他 NaN 中楼层(共6层) 1996年建暂无数据 延庆其它 NaN 房本满五年 179.0
418426 4室2厅 252.16 46796 0 0 南 北 精装 NaN 底层(共2层) 2010年建暂无数据 延庆其它 NaN 房本满五年 1180.0
424545 3室2厅 391.86 25520 1 107 南 北 毛坯 NaN 底层(共2层) 2011年建暂无数据 密云其它 房本满两年 房本满五年 1000.0
看见相关数字都背提取出来了。
随后运用相同的方法对unit_house执行数值提取操作。其结果构成一个嵌套列表结构,在此过程中需要将5室3厅划分为三个独立特征维度。即构建新的特征维度:房屋所拥有的房间数量为5间;卧室数量为3间;总卧室数量则为8间。在此阶段自然需要对该列表进行解构处理以获取所需数据信息;然而经过多种尝试后仍未能达到预期效果
bedroom = []
for bd in bj_data['unit_house']:
bedroom.append(re.findall(r"\d+\.?\d*",bd))
print(bedroom)
结果:
···
[['5', '3'], ['4', '2'], ['2', '1'], ['4', '2'],···]
采用将该列表展平,并结合以下方式处理:遍历每个房间中的物品(i for j in bedroom for i in j),随后使用/t.extend(i)将其展平;同时在处理多维数组时需注意以下几点:例如,在访问bedroom[0][0]或x[1](其中x遍历bedroom)时可能会遇到索引越界等问题。目前尝试的方法尚未找到理想解决方案。(欢迎大神提出改进方案建议!小白在此表示期待中!谢谢)
通过以上的实践经验可以看出,在完成了数据预处理后才能继续后续分析流程
但这也是一项具有挑战性的尝试;这也是我们团队首次涉及的数据处理工作。后续需要我们对上文中指出的问题进行改进和完善。
在这个过程中, 数据清理的主要目标是尽可能地提取数字特征, 最终会获得一个数值型的多维列表, 并将其导入到算法计算中。后续将学习如何处理类别数据, 时间、地理位置以及相关属性的数据清理学习
程序的编写需通过编写代码来实现特定功能。然而,在开始编码之前,请先明确目标并了解所需实现的功能。这条路依然漫长且充满挑战。
(继续实践)
在使用x[1] for x in bedroom时出现了数组越界问题,在Python中发现这是由于数据量过大导致的内存不足情况。具体表现为数组索引超出范围或者内存溢出等问题。这方面的底层机制尚不明确。虽然在网络上找到一些解决方案但实际效果并不理想因此也打算尝试将该方法应用于本项目以便更好地了解其运行机制之后再进行优化操作。另外通过将bedroom的数据保存至本地CSV文件并在读取时避免直接处理整个数组的方式能够有效缓解相关性能问题随后计划从CSV文件中导入数据并构建新的特征字段rooms并将其与原有特征进行融合从而提升模型的整体表现效果
temp=pd.DataFrame(data=bedroom)
temp.to_csv('1.csv',encoding='utf-8')
data1 = pd.read_csv('1.csv')
bedroom1 = []
for b in data1['0']:
bedroom1.append(b)
#print(bedroom1)
bj_data.insert(2,'bedroom',bedroom1)
living_room1 = []
for l in data1['1']:
living_room1.append(l)
#print(living_room1)
bj_data.insert(2,'living_room',living_room1)
rooms1 = [bedroom1[i] +living_room1[i] for i in range(0,len(living_room1))]
#print(rooms1)
bj_data.insert(2,'rooms',rooms1)
bj_data.drop('unit_house',axis=1, inplace=True)
结果·······
size_house1 rooms living_room bedroom smeter_price1 watch_times1 interests_house1 direction_house decoration_house elevator_house type_house years_house area_house years_period tax_free total_price
418423 269.93 8.0 3.0 5.0 36973 0 3 南 北 精装 NaN 底层(共2层) 暂无数据 延庆其它 NaN 房本满五年 998.0
418424 269.93 6.0 2.0 4.0 46309 0 14 南 北 精装 NaN 2层 2010年建暂无数据 延庆其它 NaN 房本满五年 1250.0
418425 71.45 3.0 1.0 2.0 25053 0 8 南 北 其他 NaN 中楼层(共6层) 1996年建暂无数据 延庆其它 NaN 房本满五年 179.0
418426 252.16 6.0 2.0 4.0 46796 0 0 南 北 精装 NaN 底层(共2层) 2010年建暂无数据 延庆其它 NaN 房本满五年 1180.0
424545 391.86 5.0 2.0 3.0
观察到新增了三列数据。但该方法存在一个需要注意的问题:在原始列表中可能存在缺失值。如果发现有缺失值,则应在处理前进行填补;否则会导致索引失配,并可能导致计算后的结果与因变量的数据关系出现偏差。即使如此,在每个缺失值处也需要谨慎处理
接下来的重点是对**categorical data类型的变量进行处理,例如房子朝向.此外还涉及到了更为复杂的文本类型数据的处理.
(未完待续)
