Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save aligundogdu/67f37e463ce09809a560 to your computer and use it in GitHub Desktop.
Save aligundogdu/67f37e463ce09809a560 to your computer and use it in GitHub Desktop.
<?php
/*
* (c) Mark Badolato <mbadolato@gmail.com>
*
* This content is released under the {@link http://www.opensource.org/licenses/MIT MIT License.}
*/
namespace Bado\ScoreCalculator;
/**
* Calculate a score based on a Wilson Confidence Interval
*
* Based on concepts discussed at @link http://www.evanmiller.org/how-not-to-sort-by-average-rating.html
*/
class WilsonConfidenceIntervalCalculator
{
/**
* Computed value for confidence (z)
*
* These values were computed using Ruby's Statistics2.pnormaldist function
* 1.959964 = 95.0% confidence
* 2.241403 = 97.5% confidence
*/
const CONFIDENCE = 2.241403;
public function getScore($positiveVotes, $totalVotes)
{
return $totalVotes ? $this->lowerBound($positiveVotes, $totalVotes) : 0;
}
private function lowerBound($positiveVotes, $totalVotes)
{
$phat = 1.0 * $positiveVotes / $totalVotes;
$numerator = $this->calculationNumerator($totalVotes, self::CONFIDENCE, $phat);
$denominator = $this->calculationDenominator($totalVotes, self::CONFIDENCE);
return $numerator / $denominator;
}
private function calculationDenominator($total, $z)
{
return 1 + $z * $z / $total;
}
private function calculationNumerator($total, $z, $phat)
{
return $phat + $z * $z / (2 * $total) - $z * sqrt(($phat * (1 - $phat) + $z * $z / (4 * $total)) / $total);
}
}
<?php
/*
* (c) Mark Badolato <mbadolato@gmail.com>
*
* This content is released under the {@link http://www.opensource.org/licenses/MIT MIT License.}
*/
namespace Bado\Tests\ScoreCalculator;
use Bado\ScoreCalculator\WilsonConfidenceIntervalCalculator;
class WilsonConfidenceIntervalCalculatorTest extends \PHPUnit_Framework_TestCase
{
/** @var WilsonConfidenceIntervalCalculator */
private $calculator;
/** @test */
public function calculateFiftyTwoPositiveOutOfSeventySixTotal()
{
$this->floatAssertion(0.556480, $this->getScore(52, 76));
}
/** @test */
public function calculateNoPositiveOutOfTenTotal()
{
$this->floatAssertion(0, $this->getScore(0, 10));
}
/** @test */
public function calculateNoVotes()
{
$this->floatAssertion(0, $this->getScore(0, 0));
}
/** @test */
public function calculateOneOutOfTwo()
{
$this->floatAssertion(0.077136, $this->getScore(1, 2));
}
/** @test */
public function calculateTenPositiveOutOfTenTotal()
{
$this->floatAssertion(0.665607, $this->getScore(10, 10));
}
/** @test */
public function calculateTenPositiveOutOfTwentyTotal()
{
$this->floatAssertion(0.275967, $this->getScore(10, 20));
}
protected function setUp()
{
$this->calculator = new WilsonConfidenceIntervalCalculator();
}
private function floatAssertion($expected, $result)
{
$this->assertEquals($expected, $result, '', 0.000001);
}
private function getScore($positiveVotes, $totalVotes)
{
return $this->calculator->getScore($positiveVotes, $totalVotes);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment