如何使用Python学习预测汽车价格

上周,我们对汽车数据集进行了一些探索性数据分析。在使用数据集并收集了许多见解之后,我们今天将重点关注价格预测。

该数据集包括在德国销售的汽车,注册年份在 2011 年到 2021 年之间。因此我们可以假设它是当今市场价格的准确表示。

先决条件

要使代码正常工作,您需要安装 python3。有些系统已经预装了它。之后,通过运行安装所有必需的库pip install

# last weeks 
pip install pandas matplotlib squarify seaborn 
# new libs 
pip install scipy sklearn catboost statsmodels

数据清洗

假设我们要根据属性预测汽车价格。为此,我们将训练一个模型。让我们从用年龄替换注册年份并删除品牌和型号开始——我们不会将它们用于预测。

cars['age'] = datetime.now().year - cars['year'] 
cars = cars.drop('year', 1) 
cars = cars.drop('make', 1) 
cars = cars.drop('model', 1)

我们上周谈到了离群值,现在是时候删除它们了。为此,我们将删除价格、马力或里程的z 分数高于 3 的项目。简而言之,这意味着删除与平均值偏离超过三个标准差的值。

在此之前,dropna将删除所有具有空值或空值的行。

from scipy import stats 
 
cars = cars.dropna() 
cars = cars[stats.zscore(cars.price) < 3] 
cars = cars[stats.zscore(cars.hp) < 3] 
cars = cars[stats.zscore(cars.mileage) < 3]

接下来,我们将用布尔标记替换类别值(优惠类型和装备)。实际上,这意味着为每个类别类型创建新列(即,对于齿轮,它将是自动、手动和半自动)。

pandas 中有一个函数可以做到这一点:get_dummies.

offerTypeDummies = pd.get_dummies(cars.offerType) 
cars = cars.join(offerTypeDummies) 
cars = cars.drop('offerType', 1) 
 
gearDummies = pd.get_dummies(cars.gear) 
cars = cars.join(gearDummies) 
cars = cars.drop('gear', 1)
示范 员工用车 新的 预登记 用过的 自动的 手动的 半自动
0 0 0 1个 0 0 1个 0
0 0 1个 0 0 1个 0 0
[/su_table]

直观地检查相关性

我们将使用 绘制变量相关性seaborn heatmap。它将以图形方式向我们展示哪些变量是正相关或负相关的。

年龄与里程的最高相关性 – 听起来不错 – 以及价格与马力 – 也不是什么大新闻。看看负相关,价格与年龄的关系——这似乎也很自然。

我们可以忽略手动和自动之间的关系,因为很明显你只会有一个或另一个 – 几乎没有半自动装置。

import seaborn as sns 
 
sns.heatmap(cars.corr(), annot=True, cmap='coolwarm') 
plt.show()

car-correlation-heatmap

为了进行双重检查,我们将用价格绘制马力和里程变量。我们会用seaborn jointplot.

它将绘制所有条目和回归线。为简洁起见,只有一个代码片段。第二个是相同的,将hp替换为mileage

sns.set_theme(style="darkgrid") 
sns.jointplot(x="hp", y="price", data=cars, 
    kind="reg", color="m", line_kws={'color': 'red'}) 
plt.show()
car-price-hp-corrcar-price-mileage-corr

 

价格预测

我们正在到达关键部分。我们将尝试三种不同的预测模型,看看哪一种表现更好。

我们需要两个变量,Y 和 X,包含价格和所有剩余的列。我们也将为其他模型使用这些新变量。然后,我们将数据以 70%-30% 的分布进行训练和测试。

免责声明:我们对所有模型进行了多次测试,并为每个模型选择了最佳结果。不相同的变量适用,并且会出现一些“魔术”数字。我们主要通过反复试验来调整这些。

from sklearn.model_selection import train_test_split 
 
X = cars.drop('price', 1) 
Y = cars.price 
X_train, X_test, y_train, y_test = train_test_split( 
    X, Y, train_size=0.7, test_size=0.3, random_state=100)

来自 sklearn 的线性模型

为了训练第一个LinearRegression模型,我们将训练数据传递给fit方法,然后将测试数据传递给predict

为了检查结果,我们将对所有结果使用R 平方。在本例中,结果为0.81237

from sklearn import linear_model 
from sklearn.metrics import r2_score 
 
lm = linear_model.LinearRegression() 
lm.fit(X_train, y_train) 
y_pred = lm.predict(X_test) 
print(r2_score(y_true=y_test, y_pred=y_pred)) # 0.81237

来自 CatBoost 的回归器

接下来,我们将使用Regressorfrom CatBoost。该模型是使用我们通过测试调整的一些数字创建的。与上一个类似,用训练数据拟合模型并检查分数,结果为0.92416

有很大的不同,因为这种方法要慢得多,超过 20 秒。它可能是更接近的匹配,但如果它必须立即运行,则不是一个好的选择。

from catboost import CatBoostRegressor 
 
model = CatBoostRegressor(iterations=6542, learning_rate=0.03) 
model.fit( 
    X_train, y_train, 
    eval_set=(X_test, y_test), 
) 
print(model.score(X, Y)) # 0.92416

来自 statsmodels 的 OLS

对于statsmodels,我们将更改 X 的值并仅取 mileage、hp 和 age。差异比以前的值好近 10%。

R 平方为0.91823,它在两秒内运行 – 计算数据负载。

import statsmodels.api as sm 
 
X = cars[['mileage', 'hp', 'age']] 
 
model = sm.OLS(Y, X).fit() 
predictions = model.predict(X) 
print(model.rsquared) # 0.91823

额外球:最佳预测

如果我们不删除品牌和型号会怎样?其中两个模型的性能会更差,但不会CatBoost。这将花费更长的时间并占用更多空间。我们将拥有 700 多个特征列。但是,如果您追求准确性,那是值得的。

为简洁起见,我们不会重现我们之前所做的所有操作。不要删除品牌和型号,而是为它们创建虚拟对象,然后像以前一样继续。

makeDummies = pd.get_dummies(cars.make) 
cars = cars.join(makeDummies) 
cars = cars.drop('make', 1) 
 
modelDummies = pd.get_dummies(cars.model) 
cars = cars.join(modelDummies) 
cars = cars.drop('model', 1) 
 
# the rest of the features, just as before 
# split train and test data 
 
model = CatBoostRegressor(iterations=6542, learning_rate=0.03) 
model.fit( 
    X_train, y_train, 
    eval_set=(X_test, y_test), 
) 
print(model.score(X, Y)) # 0.9664

该模型公开了一种获取特征重要性的方法。我们可以使用带有条形图的数据来检查哪些特征对预测的影响最大。我们将它们限制为 20 个,但正如您将看到的,其中两个 – 不包括价格本身 – 承担了所有的重量。

年龄不大乍一看可能看起来很可疑。但这是有道理的。正如我们在相关图中看到的那样,年龄和里程数是齐头并进的。所以没有必要让他们两个人扛起所有的重量。

sorted_feature_importance = model.get_feature_importance().argsort( 
    )[-20:] 
plt.barh( 
    cars.columns[sorted_feature_importance], 
    model.feature_importances_[sorted_feature_importance] 
) 
plt.xlabel("Feature Importance") 
plt.show()

cars-feature-importance

估计汽车价格

假设您想购买或出售您的汽车。您收集我们提供的功能(里程、年份等)。如何为此使用预测模型?

我们将CatBoost再次选择并使用他们的predict方法。我们将需要通过使用虚拟对象转换所有数据来再次进行所有操作,因此我们将进行总结。对于真实世界应用程序中的训练、测试或实际数据,将同样提取和执行此过程。

我们还需要添加模型支持的所有空特征(即所有其他特征)。

在这里,我们展示了一个出售三辆汽车的示例。我们手动输入了所有初始特征(包括价格),因此我们可以比较输出。正如您将看到的,预测与实际价格相差不远。

realData = pd.DataFrame.from_records([ 
    {'mileage': 87000, 'make': 'Volkswagen', 'model': 'Gold', 
        'fuel': 'Gasoline', 'gear': 'Manual', 'offerType': 'Used', 
        'price': 12990, 'hp': 125, 'year': 2015}, 
    {'mileage': 230000, 'make': 'Opel', 'model': 'Zafira Tourer', 
        'fuel': 'CNG', 'gear': 'Manual', 'offerType': 'Used', 
        'price': 5200, 'hp': 150, 'year': 2012}, 
    {'mileage': 5, 'make': 'Mazda', 'model': '3', 'hp': 122, 
        'gear': 'Manual', 'offerType': 'Employee's car', 
        'fuel': 'Gasoline', 'price': 20900, 'year': 2020} 
]) 
 
realData = realData.drop('price', 1) 
realData['age'] = datetime.now().year - realData['year'] 
realData = realData.drop('year', 1) 
 
# all the other transformations and dummies go here 
 
fitModel = pd.DataFrame(columns=cars.columns) 
fitModel = fitModel.append(realData, ignore_index=True) 
fitModel = fitModel.fillna(0) 
 
preds = model.predict(fitModel) 
print(preds) # [12213.35324984 5213.058479 20674.08838559]

结论

作为对这三种模型的快速说明,sklearn性能稍差。另外两个在结果上非常相似——如果不包括品牌和型号——但在训练时间上则不同。因此,在它们之间进行选择时,这可能是一个需要考虑的关键方面。

如果您追求高精度,请CatBoost使用所有可用数据训练模型。它可能需要一分钟,但它可以存储在文件中并在需要时立即加载。

这些是首次测试数据集时的日常任务。从那里开始,标准做法,例如生成假人或删除异常值。
然后是多汁的部分。在这种情况下,价格预测使用线性回归,但它可以是任何东西。

类似文章