Skip to content

Instantly share code, notes, and snippets.

@mbadolato
Last active May 4, 2023 04:14
Show Gist options
  • Save mbadolato/8253004 to your computer and use it in GitHub Desktop.
Save mbadolato/8253004 to your computer and use it in GitHub Desktop.
PHP translation of the Wilson ConfidenceInterval Calculator. Ported from Ruby and uses a hardcoded (pre-calculated) confidence (z value) instead of a dynamic calculation with a translation of Ruby's Statistics2.pnormaldist method. Since z doesn't change once it's computed, nor is the computation dependant on the passed-in values, calculating it …
<?php
/*
* (c) Mark Badolato <mbadolato@gmail.com>
*
* This content is released under the {@link http://www.opensource.org/licenses/MIT MIT License.}
*/
namespace Bado;
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
*/
private const CONFIDENCE = 2.241403;
public function getScore(int $positiveVotes, int $totalVotes, float $confidence = self::CONFIDENCE) : float
{
return (float) $totalVotes ? $this->lowerBound($positiveVotes, $totalVotes, $confidence) : 0;
}
private function lowerBound(int $positiveVotes, int $totalVotes, float $confidence) : float
{
$phat = 1.0 * $positiveVotes / $totalVotes;
$numerator = $this->calculationNumerator($totalVotes, $confidence, $phat);
$denominator = $this->calculationDenominator($totalVotes, $confidence);
return $numerator / $denominator;
}
private function calculationDenominator(int $total, float $z) : float
{
return 1 + $z * $z / $total;
}
private function calculationNumerator(int $total, float $z, float $phat) : float
{
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\WilsonConfidenceIntervalCalculator;
use PHPUnit\Framework\TestCase;
class WilsonConfidenceIntervalCalculatorTest extends TestCase
{
/**
* @test
* @dataProvider ratingsProvider
*
* @param int $positiveVotes
* @param int $totalVotes
* @param float $expectedScore
*/
public function it_can_calculate_scores_properly(int $positiveVotes, int $totalVotes, float $expectedScore) : void
{
$calculator = new WilsonConfidenceIntervalCalculator();
$calculatedScore = $calculator->getScore($positiveVotes, $totalVotes);
self::assertEquals($expectedScore, $calculatedScore, '', 0.000001);
}
public function ratingsProvider() : array
{
// Pre-calculated score results using the known Ruby implementation
// Array format is [$positiveVotes, $totalVotes, $expectedScore]
return [
[0, 0, 0],
[0, 10, 0],
[1, 2, 0.077136],
[10, 10, 0.665607],
[10, 20, 0.275967],
[52, 76, 0.556480],
];
}
}
@mbadolato
Copy link
Author

Updates to PHP 7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment