特征工程——数据清洗——字符串中数值特征提取(2)
在(1)的基础上,继续。
本次练习中,在处理过程中会遇到以下特点:具体表现为数值型与类别型特征均嵌入到字符串之中,在同时需要对数据进行清洗的同时也需要进行特征提取。
为了从输入字符串中提取有用的数值或分类特征信息,在现有研究(1)的基础上主要采用正则表达式方法。同时尝试了截取片段的方法,并发现该数据存在较多噪声且格式混乱的问题无法形成统一的有效截取模式。利用循环结构对这些数据进行反复处理以期获得所需信息并完成这一目标
本次研究重点涉及基于direction_house、 decoration_house、 elevator_house 以及typehouse等关键变量的分析与筛选。
首先关注的是type_house这一类建筑结构。其原始特征如下:包含底层(共计2层)、二层、中楼层(共计6层)、底层(共计2层)等信息。分类主要包括底部型、中间型和高层型。值得注意的是,在某些数据行中可能出现仅有二层层高的情况。具体步骤如下:首先提取出具备低、中、高及顶盖特性的数据样本;如果缺失,则采用默认值1来填充;生成列表type_house1;随后的任务是从该列表中进一步筛选出具有低层层高、中间层层高及高层高的样本;通过索引位置定位这些特定特性的样本;筛选后得到新的列表type_house2;并统计各类别的占比情况;发现类别1的数据占比较高;考虑替换掉类别1的数据项;并尝试将高层与顶层特性合并作为单一属性处理;然后将其整合回原始数据集。
type_house1 = []
for tp1 in bj_data['type_house']:
if re.match(r"[底中高顶]", tp1):
type_house1.append(tp1)
else:
type_house1.append(1)
结果·········
['底层(共2层)', 1, '中楼层(共6层)', '底层(共2层)', '底层(共2层)'····]
type_house2 = []
for tp2 in type_house1:
if tp2 == 1:
type_house2.append(tp2)
else:
type_house2.append(tp2[0])
结果·········
['底', 1, '中', '底', '底', '高', '高', '高', '底', 1, 1, '顶'···]
中 15199
1 11482
高 9456
顶 7307
底 4880
type_house3 = [ '其他'if x==1 else x for x in type_house2]
type_house4 = ['高' if x=='顶' else x for x in type_house3]
bj_data.insert(2,'type_house4',type_house4)
bj_data.drop('type_house',axis=1, inplace=True)
接下来是years_house,原始数据信息主要体现了房子时间和楼类型(板楼、塔楼、板塔结合),因此需要从中提取两个特征,并对房子时间转为类型特征,定义2000年前的为老楼,2000-2010之间为中年楼,2010年后为年轻楼。采用方法还是匹配,进一步发现了新的匹配函数:re.sub(),匹配文字和数字,并将相应的数据特征进行归类统一。值得说明的是,匹配过程中造成了数值缺失,如匹配数字时,如果缺失,则用2002替代(为什么是2002,主要也是后面要将年份的平均值去填充,通过假设一个值,先填充,如果样本计算出来的和假设的相同,则为这个值,利用前后迭代的思想),将年份进行分类,主要通过fou循环和if判断实现;同时将楼分类的特征进行归类统一,和上面的方法类似。
years_house1 = []
for yh1 in bj_data['years_house']:
if re.sub(u"([^\u0030-\u0039])","",str(yh1)):
years_house1.append(yh1)
else:
years_house1.append(2002)
years_house3 = []
for yh3 in years_house1:
years_house3.append(re.sub(u"([^\u0030-\u0039])","",str(yh3)))
years_house31 = []
for yh31 in years_house3:
years_house31.append(int(yh31))
years_house32 = []
for yh32 in years_house31:
if yh32 <= 2000:
years_house32.append('老楼')
elif yh32 > 2000 & yh32<=2010:
years_house32.append('中年楼')
else:
years_house32.append('年轻楼')
bj_data.insert(2,'years_house32',years_house32)
# bj_data.drop('type_house',axis=1, inplace=True)
years_house2 = []
for yh2 in bj_data['years_house']:
years_house2.append(re.sub(u"([^\u4e00-\u9fa5])","",str(yh2)))
years_house4 = ['板楼' if x=='年建板楼' else x for x in years_house2]
years_house5 = ['塔楼' if x=='年建塔楼' else x for x in years_house4]
years_house6 = ['板塔结合' if x=='年建板塔结合' else x for x in years_house5]
years_house7 = ['暂无数据' if x=='年建暂无数据' else x for x in years_house6]
years_house8 = ['板楼' if x=='' else x for x in years_house7]
years_house9 = ['板楼' if x=='暂无数据' else x for x in years_house8]
bj_data.insert(2,'years_house9',years_house9)
bj_data.drop('years_house',axis=1, inplace=True)
接下来是对decoration_house的处理过程如下:首先会对房屋装修的数据信息进行分类整合,在原始数据中发现该类特征还存在一些干扰性数据(比如有电梯或无 elevator 的情况),因此需要首先挑选出有 elevator 或无 elevator 的情况,并将其与精装和简装类别统一处理。其余的情况则予以剔除。
decoration_house1 = ['精装' if x=='有电梯' else x for x in bj_data['decoration_house']]
decoration_house2 = ['简装' if x=='无电梯' else x for x in decoration_house1]
bj_data.insert(2,'decoration_house2',decoration_house2)
bj_data.drop('decoration_house',axis=1, inplace=True)
在elevator_house1中,在分析过程中需要考虑是否存在电梯设施。此外还有其他相关的噪声数据特征。经过分类整合后完成归并整理。
elevator_house1 = ['有电梯' if x=='精装' else x for x in bj_data['elevator_house']]
elevator_house2 = ['有电梯' if x=='毛坯' else x for x in elevator_house1]
elevator_house3 = ['无电梯' if x=='其他' else x for x in elevator_house2]
elevator_house4 = ['无电梯' if x=='简装' else x for x in elevator_house3]
bj_data.insert(2,'elevator_house4',elevator_house4)
bj_data.drop('elevator_house',axis=1, inplace=True)
进一步筛选装饰物数据集为 decoration_house;由于其占用了部分其他噪点数据特征(约占0.1%的数据特征),因此我们选择去除这些不具有代表性的数据并直接提取能够反映特征的数据行。
bj_data = bj_data[bj_data['decoration_house2'].isin(['精装','简装','其他','毛坯'])]
bj_data = bj_data[bj_data['years_house9'].isin(['板楼','塔楼','板塔结合'])]
补充缺失数据,并重点关注rooms和living_room这两个关键指标;同时会对相关的数值型特征实施对数处理以及标准化处理以提升数据质量
bj_data['total_price'] = np.log1p(bj_data['total_price'])
bj_data['smeter_price1'] = np.log1p(bj_data['smeter_price1'])
numerical = ['size_house1']
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
bj_data[numerical] = scaler.fit_transform(bj_data[numerical])
最终得到的特征向量为:
(48186, 14)
size_house1 rooms decoration_house2 elevator_house4 years_house9 years_house32 type_house4 living_room bedroom smeter_price1 watch_times1 interests_house1 direction_house total_price
418423 2.775302 8.0 精装 有电梯 板楼 中年楼 底 3.0 5.0 10.517970 0 3 南 北 6.906755
418424 2.775302 6.0 精装 有电梯 板楼 中年楼 其他 2.0 4.0 10.743113 0 14 南 北 7.131699
418425 -0.523372 3.0 其他 有电梯 板楼 老楼 中 1.0 2.0 10.128789 0 8 南 北 5.192957
418426 2.479970 6.0 精装 有电梯 板楼 中年楼 底 2.0 4.0 10.753574 0 0 南 北 7.074117
424545 4.801739 5.0 毛坯 有电梯 板楼 中年楼 底 2.0 3.0 10.147257 1 107 南 北 6.908755
值得指出的是,在本处将房子的位置数据进行了删除处理。具体原因在于原始数据存在诸多不规范之处(即特征较为混乱),而通过对其所在区域的数据特征进行行政区域划分的方法虽可行(但工作量较大),然而最终并未采取后续优化措施。
接下来主要进行对类别型数据进行数值化处理。具体来说,该方法主要涉及两种数值化的方法,即LabelEncoder和One-Hot编码技术,其中我们选用前者来进行具体实现,具体实现的代码如下:
category = ['decoration_house2','elevator_house4',
'years_house9','years_house32','type_house4',
'direction_house']
from sklearn.preprocessing import LabelEncoder
for c in category:
lbl = LabelEncoder()
lbl.fit(list(bj_data[c].values))
bj_data[c] = lbl.transform(list(bj_data[c].values))
至此为止,在完成了数据清洗阶段的特征工程工作基本完成的状态下,在这两个过程中相互交织地推进着相关的研究与进展。其中最为棘手的问题是如何从文本数据中提取所需的特征指标这一问题仍然存在较大的提升余地。运用较为基础的线性回归模型进行测试得分为0.174这一结果表明还需要进一步优化与改进工作才能达到预期的效果目标
y = bj_data.total_price
X = bj_data.drop('total_price',axis=1)
from sklearn.model_selection import KFold,cross_val_score
n_folds=5
def rmlse_cv(model,x_data,y_data):
kf = KFold(n_folds,shuffle=True,random_state=42).get_n_splits(x_data.values)
rmse = np.sqrt(-cross_val_score(model,x_data.values,y_data,scoring="neg_mean_squared_error",cv=kf))
return rmse
from sklearn.linear_model import LinearRegression,Ridge,Lasso
lin_reg = LinearRegression()
rmse_score = rmlse_cv(lin_reg,X,y)
print("\nLinearRegression 得分: {:.4f} ({:.4f})\n".format(rmse_score.mean(),rmse_score.std()))
通过这一过程不仅学到了相关操作而且也进行了实际操作以熟悉处理数据的方法。为了实现操作数据的功能并非亲自敲代码而是参考前人经验若有现成模块则直接引用以加快开发进度。因此首先要明确如何处理这些数据进而确定所需的具体功能与实现目标。由于刚起步的原因代码中存在一些不够规范的地方例如命名不够合理以及相同功能模块化封装不足等需要逐步改进和完善以提升整体开发质量
存在的不足和改进之处:
在特征提取过程中忽视了相关因素;由于这一过程耗费大量时间和精力,并对认知能力的要求较高;尤其是对于新手来说更为繁琐。
在数据特征方面需要识别或构建新的特征指标或属性以满足分析需求这一类问题同样存在多种解决方案例如在一个家庭住宅中包含多套房间的情况通常可以通过简单的加法运算来确定其余潜在的问题仍需进一步探索
③缺失值处理太过于草率,需要采用新的方法;
未对数据进行进一步的分箱处理, 旨在提升特征工程的效果; 同样没有将训练集拆分为训练集与验证集
算法方面相对简单,在探索其他算法时可考虑单模型的Ridge和Lasso等方法,并结合集成方法包括随机森林、stacking与bagging等技术以提升性能
⑥算法的手动或自动调参没有进行;
代码不够规范,在许多地方存在重复的功能未被封装,在模块化处理上也不够熟练,在Python编程语言方面缺乏足够的熟悉程度
⑧机器学习相关数据分析,模型构建流程执行还缺乏。
欢迎各位大佬提出改进之处和见解,一起分享,一起进步。
非常感谢下面的文章:
使用python提取中文数字和英文
<>
缺失值处理方法
<>
Python实现箱形图的绘制
<>
(还有写那个遍历数值型字符串并转换为数值型的文章,暂时没找到)
谢谢!
