本文目录导读:

在PHP项目中实现多维缩放(Multidimensional Scaling, MDS)通常需要借助数值计算库或自己实现算法,以下是几种常见方法:
使用PHP科学计算库
使用 math-php 库
use MathPHP\LinearAlgebra\Matrix;
use MathPHP\Statistics\Multivariate\MDS;
// 安装: composer require markrogoyski/math-php
// 准备距离矩阵 (欧几里得距离示例)
$distances = [
[0, 2, 3, 4],
[2, 0, 5, 6],
[3, 5, 0, 7],
[4, 6, 7, 0]
];
// 创建距离矩阵
$distanceMatrix = new Matrix($distances);
// 执行经典MDS
$mds = new MDS($distanceMatrix);
$coordinates = $mds->classical(); // 返回降维后的坐标
// 输出结果 (通常2D或3D坐标系)
foreach ($coordinates as $point) {
echo "X: " . $point[0] . ", Y: " . $point[1] . "\n";
}
手动实现经典MDS算法
class ClassicMDS {
/**
* 执行经典多维缩放
* @param array $distanceMatrix 距离矩阵 (对称)
* @param int $dimensions 目标维度
* @return array 降维后的坐标
*/
public static function compute(array $distanceMatrix, int $dimensions = 2): array {
$n = count($distanceMatrix);
// 1. 距离平方矩阵
$squared = [];
for ($i = 0; $i < $n; $i++) {
for ($j = 0; $j < $n; $j++) {
$squared[$i][$j] = $distanceMatrix[$i][$j] ** 2;
}
}
// 2. 双中心化
$rowMeans = array_map(function($row) {
return array_sum($row) / count($row);
}, $squared);
$colMeans = [];
for ($j = 0; $j < $n; $j++) {
$colMeans[$j] = array_sum(array_column($squared, $j)) / $n;
}
$totalMean = array_sum($rowMeans) / $n;
// 计算内积矩阵 B
$B = [];
for ($i = 0; $i < $n; $i++) {
for ($j = 0; $j < $n; $j++) {
$B[$i][$j] = -0.5 * ($squared[$i][$j] - $rowMeans[$i] - $colMeans[$j] + $totalMean);
}
}
// 3. 特征值分解 (使用幂迭代法近似)
$eigenvalues = [];
$eigenvectors = [];
self::powerIteration($B, $dimensions, $eigenvalues, $eigenvectors);
// 4. 计算坐标
$coordinates = [];
for ($i = 0; $i < $n; $i++) {
$point = [];
for ($k = 0; $k < $dimensions; $k++) {
$point[] = sqrt(max(0, $eigenvalues[$k])) * $eigenvectors[$k][$i];
}
$coordinates[] = $point;
}
return $coordinates;
}
private static function powerIteration(array $matrix, int $k, array &$eigenvalues, array &$eigenvectors): void {
$n = count($matrix);
for ($iter = 0; $iter < $k; $iter++) {
$vector = array_fill(0, $n, 1.0);
// 幂迭代
for ($power = 0; $power < 100; $power++) {
$newVector = array_fill(0, $n, 0);
for ($i = 0; $i < $n; $i++) {
for ($j = 0; $j < $n; $j++) {
$newVector[$i] += $matrix[$i][$j] * $vector[$j];
}
}
// 归一化
$norm = sqrt(array_sum(array_map(function($x) { return $x ** 2; }, $newVector)));
$vector = array_map(function($x) use ($norm) {
return $norm > 0 ? $x / $norm : 0;
}, $newVector);
}
$eigenvectors[$iter] = $vector;
// 计算特征值
$eigenvalues[$iter] = 0;
for ($i = 0; $i < $n; $i++) {
$sum = 0;
for ($j = 0; $j < $n; $j++) {
$sum += $matrix[$i][$j] * $vector[$j];
}
$eigenvalues[$iter] += $sum * $vector[$i];
}
// 去相关 (从矩阵中移除该特征向量)
for ($i = 0; $i < $n; $i++) {
for ($j = 0; $j < $n; $j++) {
$matrix[$i][$j] -= $eigenvalues[$iter] * $vector[$i] * $vector[$j];
}
}
}
}
}
// 使用示例
$distances = [
[0, 2.5, 3.7],
[2.5, 0, 4.2],
[3.7, 4.2, 0]
];
$coordinates = ClassicMDS::compute($distances, 2);
print_r($coordinates);
通过外部工具集成
使用Python脚本 (推荐)
// Python MDS脚本
exec("python3 mds_script.py " . escapeshellarg(json_encode($distanceMatrix)), $output);
$result = json_decode($output[0], true);
// mds_script.py 内容:
// import sys, json
// from sklearn.manifold import MDS
// distances = np.array(json.loads(sys.argv[1]))
// mds = MDS(n_components=2, dissimilarity="precomputed")
// coords = mds.fit_transform(distances)
// print(json.dumps(coords.tolist()))
使用R语言
// 同样可以通过system()调用R脚本 $command = "Rscript mds_script.R " . escapeshellarg(json_encode($distanceMatrix)); exec($command, $output);
性能优化建议
// 1. 对大矩阵使用缓存
$cacheKey = md5(serialize($distanceMatrix));
if ($cached = apcu_fetch($cacheKey)) {
return $cached;
}
// 2. 使用SVD分解替代特征值分解(更稳定)
// 3. 对大型数据集使用批处理
function batchMDS(array $data, int $batchSize = 1000): array {
$result = [];
foreach (array_chunk($data, $batchSize) as $batch) {
$result = array_merge($result, processBatch($batch));
}
return $result;
}
// 4. 使用内存高效的数据结构
$matrix = new SplFixedArray($n);
for ($i = 0; $i < $n; $i++) {
$matrix[$i] = new SplFixedArray($n);
}
完整示例:客户相似度分析
class CustomerMDS {
private $customers = [];
private $distanceMatrix = [];
private $coordinates = [];
public function addCustomers(array $customers): void {
$this->customers = $customers;
}
public function computeSimilarity(array $metrics = ['purchase_amount', 'visit_frequency', 'category_preference']): void {
$n = count($this->customers);
// 标准化数据
$normalized = $this->normalize($metrics);
// 计算欧几里得距离
for ($i = 0; $i < $n; $i++) {
for ($j = $i; $j < $n; $j++) {
$distance = 0;
foreach ($metrics as $metric) {
$diff = $normalized[$i][$metric] - $normalized[$j][$metric];
$distance += $diff * $diff;
}
$this->distanceMatrix[$i][$j] = sqrt($distance);
$this->distanceMatrix[$j][$i] = $this->distanceMatrix[$i][$j];
}
}
// 执行MDS
$this->coordinates = ClassicMDS::compute($this->distanceMatrix, 2);
}
public function getCustomerPosition(int $customerId): array {
$idx = array_search($customerId, array_column($this->customers, 'id'));
return $this->coordinates[$idx] ?? [];
}
public function findNearestCustomers(int $customerId, int $k = 5): array {
$pos = $this->getCustomerPosition($customerId);
$distances = [];
foreach ($this->coordinates as $idx => $coord) {
if ($this->customers[$idx]['id'] !== $customerId) {
$dist = sqrt(($pos[0] - $coord[0])**2 + ($pos[1] - $coord[1])**2);
$distances[$idx] = $dist;
}
}
asort($distances);
return array_slice(array_keys($distances), 0, $k);
}
private function normalize(array $metrics): array {
$min = [];
$max = [];
foreach ($metrics as $metric) {
$values = array_column($this->customers, $metric);
$min[$metric] = min($values);
$max[$metric] = max($values);
}
$normalized = $this->customers;
foreach ($normalized as &$customer) {
foreach ($metrics as $metric) {
$range = $max[$metric] - $min[$metric];
$customer[$metric] = $range > 0 ? ($customer[$metric] - $min[$metric]) / $range : 0;
}
}
return $normalized;
}
}
注意事项
- 数据预处理:确保距离矩阵满足三角不等式
- 维度选择:使用肘部法则确定最佳维度
- 数值稳定性:对负特征值进行处理
- 大规模数据:考虑使用随机MDS或Landmark MDS
对于生产环境,建议使用Python或R等成熟的科学计算工具配合PHP调用,以获得更好的性能和精度。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播