如何使用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 |
直观地检查相关性
我们将使用 绘制变量相关性seaborn heatmap
。它将以图形方式向我们展示哪些变量是正相关或负相关的。
年龄与里程的最高相关性 – 听起来不错 – 以及价格与马力 – 也不是什么大新闻。看看负相关,价格与年龄的关系——这似乎也很自然。
我们可以忽略手动和自动之间的关系,因为很明显你只会有一个或另一个 – 几乎没有半自动装置。
import seaborn as sns sns.heatmap(cars.corr(), annot=True, cmap='coolwarm') plt.show()
为了进行双重检查,我们将用价格绘制马力和里程变量。我们会用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()
价格预测
我们正在到达关键部分。我们将尝试三种不同的预测模型,看看哪一种表现更好。
我们需要两个变量,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 的回归器
接下来,我们将使用Regressor
from 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()
估计汽车价格
假设您想购买或出售您的汽车。您收集我们提供的功能(里程、年份等)。如何为此使用预测模型?
我们将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
使用所有可用数据训练模型。它可能需要一分钟,但它可以存储在文件中并在需要时立即加载。
这些是首次测试数据集时的日常任务。从那里开始,标准做法,例如生成假人或删除异常值。
然后是多汁的部分。在这种情况下,价格预测使用线性回归,但它可以是任何东西。