2011-01-09 80 views
0

明顯的(但昂貴)的解決方案:數學!近似均值,而不存儲整個數據集

我想用來存儲軌道(1-10)的評價在這樣的表:

TrackID 
Vote 

然後簡單

SELECT AVERAGE(Vote) FROM `table` where `TrackID` = some_val 

計算平均值。

但是,我擔心可擴展性,尤其是每次需要重新計算時。

建議,但可能笨,解決方法:

TrackID 
Rating 
NumberOfVotes 

每當有人張選票,Rating

new_rating = ((old_rating * NumberOfVotes) + vote)/(NumberOfVotes + 1) 

更新,併爲TrackID的新Rating值存儲。現在每當需要Rating時,這是一個簡單的查找,而不是計算。

顯然,這並不計算平均值。我已經嘗試了一些小數據集,並且接近平均值。我相信隨着數據集的增加,它可能會收斂?但我擔心它可能會發生分歧!

你們認爲什麼?謝謝!

回答

8

假設您具有無限的數字精度,則該計算會正確更新均值。實際上,你可能使用整數類型,所以它不是確切的。

如何存儲累計投票數和投票數? (即,total=total+vote,numVotes=numVotes+1)。這樣,你可以通過一個除以另一個來得到確切的均值。

這種方法只會在您得到如此多的選票時溢出您所使用的數據類型的範圍。因此,使用大數據類型(32位應該足夠了,除非您預計會有〜40億票)!

+0

現在很明顯,您提到它了!感謝Oli :-) – 0atman 2011-01-10 10:55:36

2

您的解決方案是完全合法的。並且從完整源集合計算出的值的差別僅爲浮點精度的幾倍。

3

商店TrackId,RatingSum,NumberOfVotes在您的表中。

每當有人票,

  • NumberOfVotes = NumberOfVotes + 1
  • RatingsSum = RatingsSum + [由用戶提供的評價]

然後選擇時

SELECT TrackId, RatingsSum/NumberOfVotes FROM ... 
1

你的sol的小改進ution。你有表:

TrackID 
SumOfVotes 
NumberOfVotes 

當有人票,

NumberOfVotes = NumberOfVotes + 1 
SumOfVotes = SumOfVotes + ThisVote 

,並看到平均只有這樣,你做了分工:

SELECT TrackID, (SumOfVotes/NumberOfVotes) AS Rating FROM `table` 

我想補充的是,原來的(明顯和昂貴的)解決方案相比,在計算平均值時提供的解決方案相對昂貴。 投票被添加,刪除或更改時更便宜。 我想這原始表

TrackID 
Vote 
VoterID 

仍然需要在所提供的解決方案,可用於跟蹤每個選民的選票(等級)。因此,對於此表中的每項更改(插入,刪除或投票更新),都必須更新兩個表。

換句話說,最初的解決方案可能是最好的方法。

2

您當然可以計算跑步平均數和標準差,而不必掌握所有的關鍵點。你只需要累積總和,平方和和點數。

這不是一個近似值;平均值和標準差是確切的。

這是一個演示Java類。您可以根據需要適應您的SQL解決方案:

package statistics; 

public class StatsUtils 
{ 
    private double sum; 
    private double sumOfSquares; 
    private long numPoints; 

    public StatsUtils() 
    { 
     this.init(); 
    } 

    private void init() 
    { 
     this.sum = 0.0; 
     this.sumOfSquares = 0.0; 
     this.numPoints = 0L; 
    } 

    public void addValue(double value) 
    { 
     // Check for overflow in either number of points or sum of squares; reset if overflow is detected 
     if ((this.numPoints == Long.MAX_VALUE) || (this.sumOfSquares > (Double.MAX_VALUE-value*value))) 
     { 
      this.init(); 
     } 

     this.sum += value; 
     this.sumOfSquares += value*value; 
     ++this.numPoints; 
    } 

    public double getMean() 
    { 
     double mean = 0.0; 

     if (this.numPoints > 0) 
     { 
      mean = this.sum/this.numPoints; 
     } 

     return mean; 
    } 

    public double getStandardDeviation() 
    { 
     double standardDeviation = 0.0; 

     if (this.numPoints > 1) 
     { 
      standardDeviation = Math.sqrt((this.sumOfSquares - this.sum*this.sum/this.numPoints)/(this.numPoints-1L)); 
     } 

     return standardDeviation; 
    } 

    public long getNumPoints() { return this.numPoints; } 
}