明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺!

用 PHP 完成的容易線性回歸:(一)

[摘要]數(shù)據(jù)庫在 PHP 中的重要性PHP 領(lǐng)域中缺少了一個功能強大的工具:基于語言的數(shù)學(xué)庫。在這個由兩部分組成的系列文章中,Paul Meagher 希望通過提供一個如何開發(fā)分析模型庫的示例來啟發(fā) PHP 開發(fā)人員去開發(fā)和實現(xiàn)基于 PHP 的數(shù)學(xué)庫。在第 1 部分中,他演示了如何使用 PHP 作為實現(xiàn)語...
數(shù)據(jù)庫在 PHP 中的重要性


PHP 領(lǐng)域中缺少了一個功能強大的工具:基于語言的數(shù)學(xué)庫。在這個由兩部分組成的系列文章中,Paul Meagher 希望通過提供一個如何開發(fā)分析模型庫的示例來啟發(fā) PHP 開發(fā)人員去開發(fā)和實現(xiàn)基于 PHP 的數(shù)學(xué)庫。在第 1 部分中,他演示了如何使用 PHP 作為實現(xiàn)語言來開發(fā)和實現(xiàn)簡單線性回歸(Simple Linear Regression)算法包的核心部分。在第 2 部分中,作者在該包中添加了一些功能:針對中小規(guī)模數(shù)據(jù)集的有用的數(shù)據(jù)分析工具。

簡介
與其它開放源碼語言(比如 Perl 和 Python)相比,PHP 社區(qū)缺少強有力的工作來開發(fā)數(shù)學(xué)庫。

造成這種狀況的一個原因可能是由于已經(jīng)存在大量成熟的數(shù)學(xué)工具,這可能阻礙了社區(qū)自行開發(fā) PHP 工具的工作。例如,我曾研究過一個功能強大的工具 S System,它擁有一組令人印象深刻的統(tǒng)計庫,專門被設(shè)計成用來分析數(shù)據(jù)集,并且在 1998 年由于其語言設(shè)計而獲得了 ACM 獎。如果 S 或者其開放源碼同類 R 僅僅是一個 exec_shell 調(diào)用,那么為何還要麻煩用 PHP 實現(xiàn)相同的統(tǒng)計計算功能呢?有關(guān) S System、它的 ACM 獎或 R 的更多信息,請參閱參考資料。

難道這不是在浪費開發(fā)人員的精力嗎?如果開發(fā) PHP 數(shù)學(xué)庫的動機是出自節(jié)省開發(fā)人員的精力以及使用最好的工具來完成工作,那么 PHP 現(xiàn)在的課題是很有意義的。

另一方面,出于教學(xué)動機可能會鼓勵對 PHP 數(shù)學(xué)庫的開發(fā)。對于大約 10% 的人來說,數(shù)學(xué)是個值得探索的有趣課題。對于那些同時還熟練應(yīng)用 PHP 的人來說,PHP 數(shù)學(xué)庫的開發(fā)可以增強數(shù)學(xué)學(xué)習(xí)過程,換句話說,不要只閱讀有關(guān) T 測試的章節(jié),還要實現(xiàn)一個能計算相應(yīng)的中間值并用標(biāo)準(zhǔn)格式顯示它們的類。

通過指導(dǎo)和訓(xùn)練,我希望證明開發(fā) PHP 數(shù)學(xué)庫并不是一項很難的任務(wù),它可能代表一項有趣的技術(shù)和學(xué)習(xí)難題。在本文中,我將提供一個 PHP 數(shù)學(xué)庫示例,名為 SimpleLinearRegression,它演示了一個可以用來開發(fā) PHP 數(shù)學(xué)庫的通用方法。讓我們從討論一些通用的原則開始,這些原則指導(dǎo)我開發(fā)這個 SimpleLinearRegression 類。

指導(dǎo)原則
我使用了六個通用原則來指導(dǎo) SimpleLinearRegression 類的開發(fā)。

1.每個分析模型建立一個類。
2.使用逆向鏈接來開發(fā)類。
3.預(yù)計有大量的 getter。
4.存儲中間結(jié)果。
5.為詳細的 API 制定首選項。
6.盡善盡美并非目標(biāo)。
7.讓我們更詳細地逐條研究這些指導(dǎo)方針。

每個分析模型建立一個類
每種主要的分析測試或過程應(yīng)當(dāng)有一個名稱與測試或過程名相同的 PHP 類,這個類包含了輸入函數(shù)、計算中間值和匯總值的函數(shù)和輸出函數(shù)(將中間值和匯總值用文本或圖形格式全部顯示在屏幕上)。

使用逆向鏈接來開發(fā)類
在數(shù)學(xué)編程中,編碼的目標(biāo)通常是分析過程(比如 MultipleRegression、TimeSeries 或 ChiSquared)所希望生成的標(biāo)準(zhǔn)輸出值。從解決問題的角度出發(fā),這意味著您可以使用逆向鏈接來開發(fā)數(shù)學(xué)類的方法。

例如,匯總輸出屏幕顯示了一個或多個匯總統(tǒng)計結(jié)果。這些匯總統(tǒng)計結(jié)果依賴于中間統(tǒng)計結(jié)果的計算,這些中間統(tǒng)計結(jié)果又可能會涉及到更深一層的中間統(tǒng)計結(jié)果,以此類推。這個基于逆向鏈接的開發(fā)方法導(dǎo)出了下一個原則。

預(yù)計有大量的 getter
數(shù)學(xué)類的大部分類開發(fā)工作都涉及到計算中間值和匯總值。實際上,這意味著,如果您的類包含許多計算中間值和匯總值的 getter 方法,您不應(yīng)當(dāng)感到驚訝。

存儲中間結(jié)果
將中間計算結(jié)果存儲在結(jié)果對象內(nèi),這樣您就可以將中間結(jié)果用作后續(xù)計算的輸入。在 S 語言設(shè)計中實施了這一原則。在當(dāng)前環(huán)境下,通過選擇實例變量來表示計算得到的中間值和匯總結(jié)果,從而實施了該原則。

為詳細的 API 制定首選項
當(dāng)為 SimpleLinearRegression 類中的成員函數(shù)和實例變量制定命名方案時,我發(fā)現(xiàn):如果我使用較長的名稱(類似于 getSumSquaredError 這樣的名稱,而不是 getYY2)來描述成員函數(shù)和實例變量,那么就更容易了解函數(shù)的操作內(nèi)容和變量所代表的意義。

我沒有完全放棄簡寫名稱;但是,當(dāng)我用簡寫形式的名稱時,我得設(shè)法提供注釋以完整闡述該名稱的含義。我的看法是:高度簡寫的命名方案在數(shù)學(xué)編程中很常見,但它們使得理解和證明某個數(shù)學(xué)例程是否按部就班更為困難,而原本不必造成此種困難。

盡善盡美并非目標(biāo)
這個編碼練習(xí)的目標(biāo)不是一定要為 PHP 開發(fā)高度優(yōu)化和嚴格的數(shù)學(xué)引擎。在早期階段,應(yīng)當(dāng)強調(diào)學(xué)習(xí)實現(xiàn)意義重大的分析測試,以及解決這方面的難題。


實例變量
當(dāng)對統(tǒng)計測試或過程進行建模時,您需要指出聲明哪些實例變量。

實例變量的選擇可以通過說明由分析過程生成的中間值和匯總值來確定。每個中間值和匯總值都可以有一個相應(yīng)的實例變量,將變量的值作為對象屬性。

我采用這樣的分析來確定為清單 1 中的 SimpleLinearRegression 類聲明哪些變量?梢詫 MultipleRegression、ANOVA 或 TimeSeries 過程執(zhí)行類似的分析。

清單 1. SimpleLinearRegression 類的實例變量
<?php

// Copyright 2003, Paul Meagher
// Distributed under GPL

class SimpleLinearRegression {

var $n;
var $X = array();
var $Y = array();
var $ConfInt;
var $Alpha;
var $XMean;
var $YMean;
var $SumXX;
var $SumXY;
var $SumYY;
var $Slope;
var $YInt;
var $PredictedY = array();
var $Error = array();
var $SquaredError = array();
var $TotalError;
var $SumError;
var $SumSquaredError;
var $ErrorVariance;
var $StdErr;
var $SlopeStdErr;
var $SlopeVal; // T value of Slope
var $YIntStdErr;
var $YIntTVal; // T value for Y Intercept
var $R;
var $RSquared;
var $DF; // Degrees of Freedom
var $SlopeProb; // Probability of Slope Estimate
var $YIntProb; // Probability of Y Intercept Estimate
var $AlphaTVal; // T Value for given alpha setting
var $ConfIntOfSlope;

var $RPath = "/usr/local/bin/R"; // Your path here

var $format = "%01.2f"; // Used for formatting output

}
?>


構(gòu)造函數(shù)
SimpleLinearRegression 類的構(gòu)造函數(shù)方法接受一個 X 和一個 Y 向量,每個向量都有相同數(shù)量的值。您還可以為您預(yù)計的 Y 值設(shè)置一個缺省為 95% 的置信區(qū)間(confidence interval)。

構(gòu)造函數(shù)方法從驗證數(shù)據(jù)形式是否適合于處理開始。一旦輸入向量通過了“大小相等”和“值大于 1”測試,就執(zhí)行算法的核心部分。

執(zhí)行這項任務(wù)涉及到通過一系列 getter 方法計算統(tǒng)計過程的中間值和匯總值。將每個方法調(diào)用的返回值賦給該類的一個實例變量。用這種方法存儲計算結(jié)果確保了前后鏈接的計算中的調(diào)用例程可以使用中間值和匯總值。還可以通過調(diào)用該類的輸出方法來顯示這些結(jié)果,如清單 2 所描述的那樣。

清單 2. 調(diào)用類輸出方法
<?php

// Copyright 2003, Paul Meagher
// Distributed under GPL

function SimpleLinearRegression($X, $Y, $ConfidenceInterval="95") {

$numX = count($X);
$numY = count($Y);

if ($numX != $numY) {
die("Error: Size of X and Y vectors must be the same.");

}
if ($numX <= 1) {
die("Error: Size of input array must be at least 2.");
}

$this->n = $numX;
$this->X = $X;
$this->Y = $Y;

$this->ConfInt = $ConfidenceInterval;
$this->Alpha = (1 + ($this->ConfInt / 100) ) / 2;

$this->XMean = $this->getMean($this->X);
$this->YMean = $this->getMean($this->Y);
$this->SumXX = $this->getSumXX();
$this->SumYY = $this->getSumYY();
$this->SumXY = $this->getSumXY();
$this->Slope = $this->getSlope();
$this->YInt = $this->getYInt();
$this->PredictedY = $this->getPredictedY();
$this->Error = $this->getError();
$this->SquaredError = $this->getSquaredError();
$this->SumError = $this->getSumError();
$this->TotalError = $this->getTotalError();
$this->SumSquaredError = $this->getSumSquaredError();
$this->ErrorVariance = $this->getErrorVariance();
$this->StdErr = $this->getStdErr();
$this->SlopeStdErr = $this->getSlopeStdErr();
$this->YIntStdErr = $this->getYIntStdErr();
$this->SlopeTVal = $this->getSlopeTVal();
$this->YIntTVal = $this->getYIntTVal();
$this->R = $this->getR();
$this->RSquared = $this->getRSquared();
$this->DF = $this->getDF();
$this->SlopeProb = $this->getStudentProb($this->SlopeTVal, $this->DF);
$this->YIntProb = $this->getStudentProb($this->YIntTVal, $this->DF);
$this->AlphaTVal = $this->getInverseStudentProb($this->Alpha, $this->DF);
$this->ConfIntOfSlope = $this->getConfIntOfSlope();

return true;
}

?>


方法名及其序列是通過結(jié)合逆向鏈接和參考大學(xué)本科學(xué)生使用的統(tǒng)計學(xué)教科書推導(dǎo)得出的,該教科書一步一步地說明了如何計算中間值。我需要計算的中間值的名稱帶有“get”前綴,從而推導(dǎo)出方法名。

使模型與數(shù)據(jù)相吻合
SimpleLinearRegression 過程用于產(chǎn)生與數(shù)據(jù)相吻合的直線,其中直線具有以下標(biāo)準(zhǔn)方程:

y = b + mx

該方程的 PHP 格式看起來類似于清單 3:

清單 3. 使模型與數(shù)據(jù)相吻合的 PHP 方程
$PredictedY[$i] = $YIntercept + $Slope * $X[$i]


SimpleLinearRegression 類使用最小二乘法準(zhǔn)則推導(dǎo)出 Y 軸截距(Y Intercept)和斜率(Slope)參數(shù)的估計值。這些估計的參數(shù)用來構(gòu)造線性方程(請參閱清單 3),該方程對 X 和 Y 值之間的關(guān)系進行建模。

使用推導(dǎo)出的線性方程,您就可以得到每個 X 值對應(yīng)的預(yù)測 Y 值。如果線性方程與數(shù)據(jù)非常吻合,那么 Y 的觀測值與預(yù)測值趨近于一致。

如何確定是否非常吻合
SimpleLinearRegression 類生成了相當(dāng)多的匯總值。一個重要的匯總值是 T 統(tǒng)計值,它可以用來衡量一個線性方程與數(shù)據(jù)的吻合程度。如果非常吻合,那么 T 統(tǒng)計值往往很大。如果 T 統(tǒng)計值很小,那么應(yīng)當(dāng)用一個模型替換該線性方程,該模型假設(shè) Y 值的均值是最佳預(yù)測值(也就是說,一組值的均值通常是下一個觀測值有用的預(yù)測值,使之成為缺省模型)。

要測試 T 統(tǒng)計值是否大得足以不把 Y 值的均值作為最佳預(yù)測值,您需要計算獲取 T 統(tǒng)計值的隨機概率。如果獲取 T 統(tǒng)計值的概率很低,那么您可以否定均值是最佳預(yù)測值這個無效假設(shè),與此相對應(yīng),也就確信簡單線性模型與數(shù)據(jù)非常吻合。

那么,如何計算 T 統(tǒng)計值的概率呢?

計算 T 統(tǒng)計值概率
由于 PHP 缺少計算 T 統(tǒng)計值概率的數(shù)學(xué)例程,因此我決定將此任務(wù)交給統(tǒng)計計算包 R(請參閱參考資料中的 www.r-project.org)來獲得必要的值。我還想提醒大家注意該包,因為:

1. R 提供了許多想法,PHP 開發(fā)人員可能會在 PHP 數(shù)學(xué)庫中模擬這些想法。
2. 有了 R,可以確定從 PHP 數(shù)學(xué)庫獲得的值與那些從成熟的免費可用的開放源碼統(tǒng)計包中獲得的值是否一致。
清單 4 中的代碼演示了交給 R 來處理以獲取一個值是多么容易。

清單 4. 交給 R 統(tǒng)計計算包來處理以獲取一個值
<?php

// Copyright 2003, Paul Meagher
// Distributed under GPL

class SimpleLinearRegression {

var $RPath = "/usr/local/bin/R"; // Your path here

function getStudentProb($T, $df) {
$Probability = 0.0;
$cmd = "echo 'dt($T, $df)' $this->RPath --slave";
$result = shell_exec($cmd);
list($LineNumber, $Probability) = explode(" ", trim($result));
return $Probability;
}

function getInverseStudentProb($alpha, $df) {
$InverseProbability = 0.0;
$cmd = "echo 'qt($alpha, $df)' $this->RPath --slave";
$result = shell_exec($cmd);
list($LineNumber, $InverseProbability) = explode(" ", trim($result));
return $InverseProbability;
}

}

?>


請注意,這里已經(jīng)設(shè)置了到 R 可執(zhí)行文件的路徑,并在兩個函數(shù)中使用了該路徑。第一個函數(shù)根據(jù)學(xué)生的 T 分布返回了與 T 統(tǒng)計值相關(guān)的概率值,而第二個反函數(shù)計算了與給定的 alpha 設(shè)置相對應(yīng)的 T 統(tǒng)計值。getStudentProb 方法用來評估線性模型的吻合程度;getInverseStudentProb 方法返回一個中間值,它用來計算每個預(yù)測的 Y 值的置信區(qū)間。

由于篇幅有限,我不可能逐個詳細說明這個類中的所有函數(shù),因此如果您想搞清楚簡單線性回歸分析中所涉及的術(shù)語和步驟,我鼓勵您參考大學(xué)本科學(xué)生使用的統(tǒng)計學(xué)教科書。

燃耗研究
要演示如何使用該類,我可以使用來自公共事業(yè)中燃耗(burnout)研究中的數(shù)據(jù)。Michael Leiter 和 Kimberly Ann Meechan 研究了稱為消耗指數(shù)(Exhaustion Index)的燃耗度量單位和稱之為集中度(Concentration)的獨立變量之間的關(guān)系。集中度是指人們的社交接觸中來自其工作環(huán)境的那部分比例。

要研究他們樣本中個人的消耗指數(shù)值與集中度值之間的關(guān)系,請將這些值裝入適當(dāng)命名的數(shù)組中,并用這些數(shù)組值對該類進行實例化。對類進行實例化后,顯示該類所生成的某些匯總值以評估線性模型與數(shù)據(jù)的吻合程度。

清單 5 顯示了裝入數(shù)據(jù)和顯示匯總值的腳本:

清單 5. 用于裝入數(shù)據(jù)并顯示匯總值的腳本
<?php

// BurnoutStudy.php

// Copyright 2003, Paul Meagher
// Distributed under GPL

include "SimpleLinearRegression.php";

// Load data from burnout study

$Concentration = array(20,60,38,88,79,87,
68,12,35,70,80,92,
77,86,83,79,75,81,
75,77,77,77,17,85,96);

$ExhaustionIndex = array(100,525,300,980,310,900,
410,296,120,501,920,810,
506,493,892,527,600,855,
709,791,718,684,141,400,970);

$slr = new SimpleLinearRegression($Concentration, $ExhaustionIndex);

$YInt = sprintf($slr->format, $slr->YInt);
$Slope = sprintf($slr->format, $slr->Slope);
$SlopeTVal = sprintf($slr->format, $slr->SlopeTVal);
$SlopeProb = sprintf("%01.6f", $slr->SlopeProb);

?>

<table border='1' cellpadding='5'>
<tr>
<th align='right'>Equation:</th>
<td></td>
</tr>
<tr>
<th align='right'>T:</th>
<td></td>
</tr>
<tr>
<th align='right'>Prob > T:</th>
<td><td>
</tr>



通過 Web 瀏覽器運行該腳本,產(chǎn)生以下輸出:

Equation: Exhaustion = -29.50 + (8.87 * Concentration)
T: 6.03
Prob > T: 0.000005


這張表的最后一行指出獲取這樣大 T 值的隨機概率非常低?梢缘贸鲞@樣的結(jié)論:與僅僅使用消耗值的均值相比,簡單線性模型的預(yù)測能力更好。

知道了某個人的工作場所聯(lián)系的集中度,就可以用來預(yù)測他們可能正在消耗的燃耗程度。這個方程告訴我們:集中度值每增加 1 個單位,社會服務(wù)領(lǐng)域中一個人的消耗值就會增加 8 個單位。這進一步證明了:要減少潛在的燃耗,社會服務(wù)領(lǐng)域中的個人應(yīng)當(dāng)考慮在其工作場所之外結(jié)交朋友。

這只是粗略地描述了這些結(jié)果可能表示的含義。為全面研究這個數(shù)據(jù)集的含義,您可能想更詳細地研究這個數(shù)據(jù)以確信這是正確的解釋。在下一篇文章中我將討論應(yīng)當(dāng)執(zhí)行其它哪些分析。

您學(xué)到了什么?
其一,要開發(fā)意義重大的基于 PHP 的數(shù)學(xué)包,您不必是一名火箭科學(xué)家。堅持標(biāo)準(zhǔn)的面向?qū)ο蠹夹g(shù),以及明確地采用逆向鏈接問題解決方法,就可以相對方便地使用 PHP 實現(xiàn)某些較為基本的統(tǒng)計過程。

從教學(xué)的觀點出發(fā),我認為:如果只是因為要求您在較高和較低的抽象層次思考統(tǒng)計測試或例程,那么這個練習(xí)是非常有用的。換句話說,補充您的統(tǒng)計測試或過程學(xué)習(xí)的一個好辦法就是將這個過程作為算法實現(xiàn)。

要實現(xiàn)統(tǒng)計測試通常需要超出所給定的信息范圍并創(chuàng)造性地解決和發(fā)現(xiàn)問題。對于發(fā)現(xiàn)對某個學(xué)科認識的不足而言,它也是一個好辦法。

不利的一面,您發(fā)現(xiàn) PHP 對于取樣分布缺乏內(nèi)在手段,而這是實現(xiàn)大多數(shù)統(tǒng)計測試所必需的。您需要交給 R 來處理以獲取這些值,但是我擔(dān)心您會沒時間或沒興趣安裝 R。某些常見概率函數(shù)的本機 PHP 實現(xiàn)可以解決這個問題。

另一個問題是:該類生成許多中間值和匯總值,但是匯總輸出實際上沒有利用這一點。我提供了一些難處理的輸出,但是這既不夠充分也沒進行很好的組織,以致您無法充分地解釋分析結(jié)果。實際上,我完全不知道如何可以將輸出方法集成到該類中。這需要得到解決。

最后,要弄明白數(shù)據(jù),不僅僅是察看匯總值就可以了。您還需要明白各個數(shù)據(jù)點是如何分布的。最好的辦法之一是將您的數(shù)據(jù)繪制成圖表。再次聲明,我對這方面不太了解,但是如果要用這個類來分析實際數(shù)據(jù)的話就需要解決這個問題。

在本系列文章的下一篇文章中,我將使用本機 PHP 代碼實現(xiàn)一些概率函數(shù),用幾個輸出方法擴展 SimpleLinearRegression 類,并生成一個報告:用表和圖形格式表示中間值和匯總值,這樣更容易從數(shù)據(jù)中得出結(jié)論。且待下回分解!


參考資料

1.請參考由 James T. McClave 和 Terry Sincich 編著的廣受歡迎的大學(xué)教科書 Statistics,第 9 版(Prentice-Hall,在線),本文中所使用的算法步驟和“燃耗研究”示例參考了該書。
2.請查閱 PEAR 資源庫,它目前包含了少量低級別的 PHP 數(shù)學(xué)類。最終,應(yīng)該會很高興地看到 PEAR 包含實現(xiàn)標(biāo)準(zhǔn)的較高級別的數(shù)值方法(比如 SimpleLinearRegression、MultipleRegression、TimeSeries、ANOVA、FactorAnalysis、FourierAnalysis 及其它)的包。
3.查看作者的 SimpleLinearRegression 類的所有源代碼。
4.了解一下Numerical Python 項目,它用非?茖W(xué)的數(shù)組語言以及成熟的建立下標(biāo)方法擴展了 Python。有了該擴展,數(shù)學(xué)操作就非常接近人們期望從編譯語言所獲得的功能。
5.研究可用于 Perl 的許多數(shù)學(xué)參考資料,包括 CPAN 數(shù)學(xué)模塊的索引和 CPAN 中算法部分的模塊,以及 Perl 數(shù)據(jù)語言(Perl Data Language),它旨在為 Perl 提供壓縮存儲以及快速操作大型 N 維數(shù)據(jù)數(shù)組的能力。
6.有關(guān) John Chambers 的 S 編程語言的更多信息,請查閱關(guān)于他的出版物以及他在貝爾實驗室的各項研究項目的鏈接。還可以了解在 1998 年因語言設(shè)計而獲得的 ACM 獎。
7.R 是用于統(tǒng)計計算和圖形的語言和環(huán)境,類似于獲獎的 S System,R 提供了諸如線性和非線性建模、統(tǒng)計測試、時間序列分析、分類、群集之類的統(tǒng)計和圖形技術(shù)。請在 R Project 主頁上了解 R。
8.如果您剛接觸 PHP,那么請閱讀 Amol Hatwar 的 developerWorks 系列文章:“用 PHP 開發(fā)健壯的代碼:”“第 1 部分: 高屋建瓴的介紹 ”(2002 年 8 月)、“第 2 部分: 有效地使用變量”(2002 年 9 月)和“第 3 部分: 編寫可重用函數(shù)”(2002 年 11 月)。