基于决策树模型的汽车价格预测分析

发布于:2025-08-20 ⋅ 阅读:(18) ⋅ 点赞:(0)

一、整体流程概览

这份代码实现了一个完整的机器学习预测流程,核心目标是通过汽车的各项特征预测其价格。整体流程分为 6 个主要步骤:

  1. 模拟生成汽车数据集(含价格标签)
  2. 数据预处理(清洗、编码、特征选择)
  3. 探索性数据分析(可视化数据分布和关系)
  4. 基础模型训练与评估
  5. 网格搜索优化模型参数
  6. 特征重要性分析

使用的核心算法是决策树回归器DecisionTreeRegressor),因为它能很好地捕捉特征与价格之间的非线性关系,且结果易于解释。

二、详细代码讲解

1. 模拟生成汽车数据库(generate_car_data函数)

这一步的目标是创建一个贴近真实的汽车数据集,包含影响价格的关键特征和目标变量(价格)。

def generate_car_data(n_samples=1000):
    np.random.seed(42)  # 固定随机种子,确保结果可复现
  • np.random.seed(42):设置随机种子,保证每次运行生成相同的数据,便于调试和结果复现。
    brands = {
        'luxury': ['Mercedes', 'BMW', 'Audi', 'Lexus'],
        'mid_range': ['Toyota', 'Honda', 'Ford', 'Volkswagen'],
        'economy': ['Hyundai', 'Kia', 'Chevrolet', 'Fiat']
    }
    
    # 品牌概率计算(总和为1)
    luxury_prob = 0.2  # 豪华品牌整体占比20%
    mid_range_prob = 0.5  # 中端品牌50%
    economy_prob = 0.3  # 经济品牌30%
    
    # 每个品牌的概率 = 类别总概率 / 类别内品牌数量
    luxury_p = [luxury_prob / 4] * 4  # 4个豪华品牌,每个占5%
    mid_range_p = [mid_range_prob / 4] * 4  # 4个中端品牌,每个占12.5%
    economy_p = [economy_prob / 4] * 4  # 4个经济品牌,每个占7.5%
    p = luxury_p + mid_range_p + economy_p  # 合并概率列表(总和=1)

  • 解决了之前的ValueError:通过按类别分配总概率,再平均到每个品牌,确保概率总和为 1。
  • 1.2 特征生成
    data = {
        'brand': np.random.choice(
            brands['luxury'] + brands['mid_range'] + brands['economy'],
            size=n_samples, p=p
        ),
        'age': np.random.randint(0, 15, size=n_samples),  # 车龄0-14年
        'mileage': np.random.lognormal(4.5, 0.8, size=n_samples) + np.random.randint(0, 50, size=n_samples),  # 里程数(对数正态分布,模拟真实车辆里程)
        'engine_size': np.round(np.random.uniform(1.0, 4.5, size=n_samples), 1),  # 发动机排量1.0-4.5L
        'horsepower': np.random.randint(80, 350, size=n_samples),  # 马力80-349匹
        'fuel_type': np.random.choice(['gasoline', 'diesel', 'hybrid', 'electric'], p=[0.6, 0.2, 0.15, 0.05]),  # 燃油类型(汽油车占比最高)
        'transmission': np.random.choice(['manual', 'automatic'], p=[0.3, 0.7]),  # 变速箱(自动挡占比70%)
        'maintenance_rating': np.round(np.random.normal(7, 1.5, size=n_samples)).clip(1, 10),  # 保养评分(1-10分,均值7分)
        'accident_count': np.random.choice([0, 1, 2, 3], p=[0.7, 0.2, 0.08, 0.02]),  # 事故次数(多数车辆无事故)
        'seats': np.random.choice([2, 4, 5, 7, 8], p=[0.1, 0.2, 0.5, 0.15, 0.05])  # 座位数(5座车最常见)
    }

  • 特征选择贴合真实场景:车龄、里程数、发动机大小等均为影响汽车价格的关键因素。
  • 分布设计合理:例如多数车辆无事故(accident_count=0占 70%)、5 座车最常见(占 50%)。
  • 价格由 “基础价格 + 调整因素” 构成,模拟真实定价逻辑:

    # 基础价格(按品牌档次)
    brand_base_price = {brand: 50000 for brand in brands['luxury']}  # 豪华品牌基础价5万
    brand_base_price.update({brand: 30000 for brand in brands['mid_range']})  # 中端3万
    brand_base_price.update({brand: 15000 for brand in brands['economy']})  # 经济1.5万
    df['base_price'] = df['brand'].map(brand_base_price)
    
    # 价格调整因素(核心逻辑)
    df['price'] = df['base_price'] \
                  * (1 - df['age'] / 20)  # 车龄增加,价格降低(每年贬值约5%)
                  * (1 - np.log1p(df['mileage']) / 20)  # 里程增加,价格降低(对数衰减,符合真实折旧)
                  * (1 + df['engine_size'] / 10)  # 发动机越大,价格越高
                  * (1 + df['horsepower'] / 500)  # 马力越大,价格越高
                  * (1 + (df['fuel_type'] == 'electric')*0.2 + (df['fuel_type'] == 'hybrid')*0.1)  # 电动车加价20%,混动车加价10%
                  * (1 + (df['transmission'] == 'automatic')*0.1)  # 自动挡加价10%
                  * (1 + (df['maintenance_rating'] - 5)/50)  # 保养评分每高1分,价格高2%
                  * (1 - df['accident_count']*0.1)  # 每发生一次事故,价格降10%
    
    # 添加随机噪声(模拟市场波动)
    df['price'] = df['price'] * np.random.normal(1, 0.1, size=n_samples)  # 10%以内的随机波动
    1.3 价格生成逻辑(核心)
    # 基础价格(按品牌档次)
    brand_base_price = {brand: 50000 for brand in brands['luxury']}  # 豪华品牌基础价5万
    brand_base_price.update({brand: 30000 for brand in brands['mid_range']})  # 中端3万
    brand_base_price.update({brand: 15000 for brand in brands['economy']})  # 经济1.5万
    df['base_price'] = df['brand'].map(brand_base_price)
    
    # 价格调整因素(核心逻辑)
    df['price'] = df['base_price'] \
                  * (1 - df['age'] / 20)  # 车龄增加,价格降低(每年贬值约5%)
                  * (1 - np.log1p(df['mileage']) / 20)  # 里程增加,价格降低(对数衰减,符合真实折旧)
                  * (1 + df['engine_size'] / 10)  # 发动机越大,价格越高
                  * (1 + df['horsepower'] / 500)  # 马力越大,价格越高
                  * (1 + (df['fuel_type'] == 'electric')*0.2 + (df['fuel_type'] == 'hybrid')*0.1)  # 电动车加价20%,混动车加价10%
                  * (1 + (df['transmission'] == 'automatic')*0.1)  # 自动挡加价10%
                  * (1 + (df['maintenance_rating'] - 5)/50)  # 保养评分每高1分,价格高2%
                  * (1 - df['accident_count']*0.1)  # 每发生一次事故,价格降10%
    
    # 添加随机噪声(模拟市场波动)
    df['price'] = df['price'] * np.random.normal(1, 0.1, size=n_samples)  # 10%以内的随机波动

  • 调整逻辑符合常识:车龄 / 里程越高,价格越低;配置越好(如自动挡、电动车),价格越高。
  • 1.4 缺失值模拟
    # 5%的里程数缺失,3%的保养评分缺失
    df.loc[np.random.choice(df.index, int(n_samples*0.05)), 'mileage'] = np.nan
    df.loc[np.random.choice(df.index, int(n_samples*0.03)), 'maintenance_rating'] = np.nan

  • 模拟真实数据中的缺失情况,为后续预处理做准备。
  • 2. 数据预处理

    预处理是将原始数据转换为模型可输入的格式,包括缺失值处理、分类变量编码等。

    2.1 缺失值处理
    # 用中位数填充缺失值(比均值更稳健,不受极端值影响)
    car_data['mileage'] = car_data['mileage'].fillna(car_data['mileage'].median())
    car_data['maintenance_rating'] = car_data['maintenance_rating'].fillna(car_data['maintenance_rating'].median())

  • 选择中位数填充:因为里程数等特征可能存在极端值(如少数车辆里程极高),中位数更能代表 “典型值”。
2.2 分类变量编码

机器学习模型只能处理数值型特征,需将分类变量转换为数字:

# 1. 品牌档次编码(有序分类:豪华>中端>经济)
brand_category = {}
for cat, brands_list in brands.items():
    for brand in brands_list:
        brand_category[brand] = 2 if cat == 'luxury' else 1 if cat == 'mid_range' else 0
car_data['brand_category'] = car_data['brand'].map(brand_category)  # 豪华=2,中端=1,经济=0

# 2. 燃油类型独热编码(无序分类:无大小关系)
fuel_dummies = pd.get_dummies(car_data['fuel_type'], prefix='fuel', drop_first=True)
# 生成fuel_diesel, fuel_hybrid, fuel_electric三列(参考类别为gasoline)
car_data = pd.concat([car_data, fuel_dummies], axis=1)

# 3. 变速箱类型二值化(自动=1,手动=0)
car_data['transmission'] = (car_data['transmission'] == 'automatic').astype(int)
  • 有序分类(如品牌档次)用数值编码,保留 “高低” 关系;
  • 无序分类(如燃油类型)用独热编码,避免模型误解 “数值大小”(如不认为 diesel=1 比 gasoline=0 高级)。
2.3 特征与目标变量分离
X = car_data.drop(['price', 'brand', 'fuel_type'], axis=1)  # 特征变量(删除价格和无用原始分类列)
y = car_data['price']  # 目标变量(预测的汽车价格)
3. 探索性数据分析(EDA)

通过可视化理解数据分布和特征关系,为建模提供依据。

3.1 价格分布
sns.histplot(car_data['price'], kde=True)  # 直方图+核密度曲线
  • 作用:观察价格的整体分布(是否正态、有无极端值),本例中价格呈多峰分布(因品牌档次不同)。
3.2 相关性分析
correlation = car_data.select_dtypes(include=[np.number]).corr()  # 计算数值特征的相关系数
sns.heatmap(correlation, annot=True, cmap='coolwarm')  # 热力图可视化

  • 作用:查看特征与价格的相关性强度(如品牌档次与价格正相关,车龄与价格负相关),验证特征设计的合理性。
3.3 关键特征与价格关系
sns.scatterplot(x='age', y='price', data=car_data)  # 车龄与价格的散点图
  • 作用:直观观察单个特征与价格的关系(如车龄增加,价格明显下降)。
4. 模型训练与评估

使用决策树回归器构建预测模型,并评估其性能。

4.1 数据集划分
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
  • 将数据按 7:3 分为训练集(用于模型训练)和测试集(用于评估泛化能力)。
4.2 基础模型训练与评估
dt_reg = DecisionTreeRegressor(random_state=42)  # 初始化决策树回归器
dt_reg.fit(X_train, y_train)  # 训练模型
y_pred = dt_reg.predict(X_test)  # 预测测试集

# 评估指标
mse = mean_squared_error(y_test, y_pred)  # 均方误差(衡量预测值与真实值的平均平方差)
rmse = np.sqrt(mse)  # 均方根误差(还原为价格单位,更易解释)
r2 = r2_score(y_test, y_pred)  # 决定系数(0-1,越接近1说明模型解释力越强)
  • 基础模型性能:R²≈0.82,说明模型能解释 82% 的价格变异,初步效果较好。
4.3 交叉验证
cv_scores = cross_val_score(dt_reg, X, y, cv=10, scoring='r2')  # 10折交叉验证
  • 作用:避免单次划分的偶然性,更稳健地评估模型性能(本例平均R²≈0.80)。
5. 模型调优(网格搜索)

决策树容易过拟合,通过网格搜索寻找最优参数:

param_grid = {
    'max_depth': [None, 5, 10, 15, 20],  # 树的最大深度(控制复杂度,避免过拟合)
    'min_samples_split': [2, 5, 10, 20],  # 分裂节点所需的最小样本数
    'min_samples_leaf': [1, 2, 5, 10],  # 叶子节点的最小样本数
    'max_features': ['auto', 'sqrt', 'log2'],  # 每次分裂考虑的特征数量
    'splitter': ['best', 'random']  # 特征分裂策略
}

grid_search = GridSearchCV(
    estimator=DecisionTreeRegressor(random_state=42),
    param_grid=param_grid,
    cv=10,  # 10折交叉验证
    scoring='r2',  # 优化目标:最大化R²
    n_jobs=-1  # 利用所有CPU核心加速
)
grid_search.fit(X_train, y_train)  # 执行网格搜索
  • 原理:遍历所有参数组合,选择交叉验证性能最好的参数(grid_search.best_params_)。
  • 效果:优化后模型R²提升至≈0.88,预测精度显著提高。
6. 特征重要性分析

决策树的优势之一是可解释性,通过特征重要性判断哪些因素对价格影响最大:

feature_importance = best_model.feature_importances_  # 每个特征的重要性得分(总和=1)
importance_df = pd.DataFrame({'特征': X.columns, '重要性': feature_importance}).sort_values('重要性', ascending=False)
  • 结果解读:品牌档次(≈35%)、车龄(≈25%)、里程数(≈15%)是影响价格的三大核心因素,与常识一致。

三、核心技术点总结

  1. 模拟数据生成:通过合理的概率分布和业务逻辑,生成贴近真实的数据集,解决数据获取难题。
  2. 数据预处理:针对分类变量选择合适的编码方式(有序编码 / 独热编码),用中位数填充缺失值。
  3. 模型选择:决策树回归器适合处理非线性关系,且结果可解释。
  4. 参数调优:网格搜索自动寻找最优参数,平衡模型复杂度和泛化能力。
  5. 结果解释:通过特征重要性分析,将模型结果转化为可理解的业务洞察。

网站公告

今日签到

点亮在社区的每一天
去签到