数据挖掘与机器学习入门-以房价预测为例
背景
此时我们有两份CSV文件
houseprice_train.csv
:包含训练数据和房价数据houseprice_test.csv
:只包含测试数据不包括房价,将测试集真正房价对开发者不可见用于打分
数据处理
导入两份csv文件:
train=pd.read_csv('houseprice_train.csv')
test=pd.read_csv('houseprice_test.csv')
查看导入数据,有个初步的认知:
train.head() #查看文件头部信息
train.dtypes #查看所有列的类型信息
train.columns #查看所有行名
查看要训练集的目标值(标签),判断数据分布是否合理
prices = pd.DaraFrame({"price":train['target']})
prices.hist()
输出直方图如图:
发现标签不是很平滑,不是很符合正态分布,因此对标签进行平滑处理(正态化)
假设标签不符合正态分布,存在左偏或右偏都会影响到模型的预测,结果达不到标准。因为尾类的数据可能一定程度上影响到模型对正常数据的预测,因此符合正态分布是最好的。
使用公式:log(x+1)
对数据进行处理,结果如图:
y_train = np.log1p(train.pop('target'))
#train.pop('target') 将target列从DataFrame train中分离出来,值即为target列
#np.log1p 对分离出来的target列执行log(x+1)
由于此时执行了
log(x+1)
操作,因此在最后模型输出后要执行np.expm1()
操作还原数据。
特征工程
选择正确的特征,创建有用的新特征,除去冗余的无用特征都是非常重要的步骤。
类型变量
由于本数据集过于简单,没有涉及到类型变量,但是现实中的数据存在很多类型变量,对类型变量要进行特殊处理。
使用df['name'].dtype
方法检查特征的类型,假如这个特征是类型但是赋值是数字的就会产生误导,因为类与类之间是不应该进行数字层面上的比较、加减操作等,因此使用类型转化把当成数字的类型转化为字符串。
all_df['MSSubClass'] = all_df['MSSubClass'].astype(str)
#此时查看类型输出为
# Name: MSSubClass, Length: 2919, dtype: object
#做个数统计
all_df['MSSubClass'].value_counts()
#value_counts()方法进行统计操作,返回值相同的一类的个数降序输出
进一步使用one-hot
编码对类型数据进行处理
pd.get_dummies(all_df['MSSubClass'], prefix='MSSubClass').head()
pandas自带的
get_dummies
方法,当参数输入的是整个数据框,会默认对所有object
类型的列进行独热编码
异常处理
原始数据常常会存在许多异常:
-
缺失值:
all_dummy_df.isnull().sum().sort_values(ascending=False).head(10) #isnull()返回和原始框一样大小的ture/false矩阵 sum()求和默认是axis=0 输出每列特征存在null的个数总和 #有很多处理缺失值的方法 可以选择其一 这里选择使用均值替代缺失值 mean_cols = all_dummy_df.mean() #默认axis=0 输出对列的均值 all_dummy_df = all_dummy_df.fillna(mean_cols) #When filling using a DataFrame, replacement happens along the same column names and same indices
异常值:
使用箱线图对列元素进行可视化
pd.DataFrame(train['MedInc']).boxplot()
计算区间上下限:
obj = train['MedInc']
Q1 = obj.quantile(0.25)
Q3 = obj.quantile(0.75) # return float
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
筛选位于正常范围的行:
normal_df = train.loc[(train['MedInc'] < upper_bound) & (train['MedInc'] > lower_bound)]
normal_df.shape
# (train['MedInc'] < upper_bound) & (train['MedInc'] > lower_bound) return bool
#.loc方法根据布尔值选择出特定行
标准化
X = X − μ σ X=\frac{X-\mu}{\sigma} X=σX−μ
对于回归分类器最好把数据放入标准分布内,不让数据间距过大。
numeric_cols = all_df.columns[all_df.dtypes != 'object']
#筛选出数值部分的列名
numeric_col_means = all_dummy_df.loc[:, numeric_cols].mean()
numeric_col_std = all_dummy_df.loc[:, numeric_cols].std()
#算出每列对于的均值和方差
all_dummy_df.loc[:, numeric_cols] = (all_dummy_df.loc[:, numeric_cols] - numeric_col_means) / numeric_col_std
#赋值操作 直接运算是赋值到新的数据框而不是在原先数据框上更改
或者使用sklearn自带的标准化包:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
#对指定列标准化 记住同时对训练集和测试集操作
X_train.loc[:, 'Population'] = scaler.fit_transform(X_train[['Population']])
X_train.loc[:, 'MedInc'] = scaler.fit_transform(X_train[['MedInc']])
X_test.loc[:, 'Population'] = scaler.fit_transform(X_test[['Population']])
X_test.loc[:, 'MedInc'] = scaler.fit_transform(X_test[['MedInc']])
X_train.dtypes
训练
选择合适的模型进行训练,以xgboost和贝叶斯优化为例:
from bayes_opt import BayesianOptimization
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import r2_score
from xgboost import XGBRegressor
def optimize_rf(max_depth):
# 初始化随机森林模型
rf = XGBRegressor(
max_depth=int(max_depth)
)
# 训练模型
rf.fit(X_train, y_train)
# # 预测测试集
# y_pred = rf.predict(X_train)
y_end = rf.predict(X_test)
# 计算R2分数
r2_ = r2_score(y_ans, y_end)
return r2_
pbounds = {
'max_depth': (1, 10),
}
# 初始化贝叶斯优化器
optimizer = BayesianOptimization(
f=optimize_rf,
pbounds=pbounds,
verbose=2,
random_state=42
)
# 运行贝叶斯优化
optimizer.maximize(
init_points=15,
n_iter=30
)
# 输出结果
print("最优超参数组合:", optimizer.max['params'])
print("最大R2分数:", optimizer.max['target'])
best_params = optimizer.max['params']
rf_best = RandomForestClassifier(
max_depth=int(best_params['max_depth']),
random_state=42
)
rf_best.fit(X_train, y_train.values.flatten()) # 确保y_train是一维数组
# 对测试集进行预测
y_pred_test = rf_best.predict(X_test)
参考:
[1]:pandas - 数据离散化之 get_dummies - 知乎
[2]:Pandas如何检查DataFrame列是否为分类变量|极客笔
[3]:师兄的方法模板和教程