使用php-ml线性回归预测Airbnb价格时遇TypeError错误求助
问题背景
使用php-ml的最小二乘法线性回归预测2019年纽约Airbnb数据集的价格,已清理空值,剩余列顺序为index、room_type、price、minimum_nights、number_of_reviews、neighbourhood group,目标变量为price,选取minimum_nights和number_of_reviews作为特征时,执行代码触发TypeError。
错误信息
Fatal error: Uncaught TypeError:
Phpml\CrossValidation\Split::__construct(): Argument #1 ($dataset)
must be of type Phpml\Dataset\Dataset, array given, called in
C:\xampp\htdocs\490\testing2.php on line 23 and defined in
C:\xampp\htdocs\490\vendor\php-ai\php-ml\src\CrossValidation\Split.php:32
Stack trace: #0 C:\xampp\htdocs\490\testing2.php(23):
Phpml\CrossValidation\Split->__construct(Array, Array, 0.3) #1 {main}
thrown in
C:\xampp\htdocs\490\vendor\php-ai\php-ml\src\CrossValidation\Split.php
on line 32
错误触发在代码第23行:$randomSplit = new \Phpml\CrossValidation\RandomSplit($samples, $targets, 0.3);
问题代码
<?php require 'vendor/autoload.php'; use Phpml\Regression\LeastSquares; use Phpml\Metric\Regression; use Phpml\CrossValidation\RandomSplit; use Phpml\Dataset\CsvDataset; // Load the data $dataset = new \Phpml\Dataset\CsvDataset(filepath: "./data/NYC2019datareduced.csv", features: 5, headingRow: true); $samples = []; $targets = []; foreach ($dataset->getSamples() as $sample) { // Selecting the features (excluding the target variable 'price') $samples[] = [$sample[3], $sample[4]]; $targets[] = $sample[2]; } // Split the data into training and testing sets $randomSplit = new \Phpml\CrossValidation\RandomSplit($samples, $targets, 0.3); // Train the model $regression = new \Phpml\Regression\LeastSquares(); $regression->train($randomSplit->getTrainSamples(), $randomSplit->getTrainLabels()); // Test the model $predicted = $regression->predict($randomSplit->getTestSamples()); $r2 = \Phpml\Metric\Regression::r2Score($randomSplit->getTestLabels(), $predicted); echo "R2 score: $r2\n"; // Predict the price for a new listing $newListing = [1,45]; // Replace with your own data $predictedPrice = $regression->predict([$newListing]); echo "Predicted price for the new listing: $predictedPrice[0]\n";
解决方案
错误原因
php-ml的RandomSplit类构造函数要求第一个参数必须是实现了Dataset接口的对象(如ArrayDataset、CsvDataset),而不是单独的样本数组和目标数组。旧版本可能支持传入数组,但新版本已调整参数要求。
修复步骤
- 引入
ArrayDataset类,用于将手动提取的样本和目标变量包装成符合要求的Dataset对象。 - 用
ArrayDataset包装$samples和$targets,再传入RandomSplit。
修改后的完整代码
<?php require 'vendor/autoload.php'; use Phpml\Regression\LeastSquares; use Phpml\Metric\Regression; use Phpml\CrossValidation\RandomSplit; use Phpml\Dataset\CsvDataset; use Phpml\Dataset\ArrayDataset; // 新增引入 // Load the data $dataset = new \Phpml\Dataset\CsvDataset(filepath: "./data/NYC2019datareduced.csv", features: 5, headingRow: true); $samples = []; $targets = []; foreach ($dataset->getSamples() as $sample) { // 选取特征:minimum_nights(索引3)、number_of_reviews(索引4) $samples[] = [$sample[3], $sample[4]]; // 目标变量:price(索引2) $targets[] = $sample[2]; } // 将样本和目标变量包装为ArrayDataset对象 $arrayDataset = new ArrayDataset($samples, $targets); // 初始化RandomSplit,传入Dataset对象和测试集占比0.3 $randomSplit = new \Phpml\CrossValidation\RandomSplit($arrayDataset, 0.3); // 训练模型 $regression = new \Phpml\Regression\LeastSquares(); $regression->train($randomSplit->getTrainSamples(), $randomSplit->getTrainLabels()); // 测试模型并计算R2分数 $predicted = $regression->predict($randomSplit->getTestSamples()); $r2 = \Phpml\Metric\Regression::r2Score($randomSplit->getTestLabels(), $predicted); echo "R2 score: $r2\n"; // 预测新房源价格 $newListing = [1,45]; // minimum_nights=1,number_of_reviews=45 $predictedPrice = $regression->predict([$newListing]); echo "Predicted price for the new listing: $predictedPrice[0]\n";
另一种简化方式(直接处理CsvDataset)
如果不想手动循环提取样本,可以直接基于原始CsvDataset创建包含所需特征的子数据集,再传入RandomSplit,但需要确保目标变量的位置正确。不过手动提取样本的方式更直观,适合自定义特征选择场景。
内容的提问来源于stack exchange,提问作者Arshado

