Skip to content

Instantly share code, notes, and snippets.

@julienbourdeau
Last active January 11, 2023 05:34
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save julienbourdeau/77eaca0fd1e4af3fde9fe018fdf13d7d to your computer and use it in GitHub Desktop.
Save julienbourdeau/77eaca0fd1e4af3fde9fe018fdf13d7d to your computer and use it in GitHub Desktop.
[PHP] 5 Star Rating - Lower bound of Wilson score confidence interval for a Bernoulli parameter
<?php
/*
|--------------------------------------------------------------------------
| 5 Star Rating
|--------------------------------------------------------------------------
|
| Lower bound of Wilson score confidence interval for a Bernoulli parameter (0.9604)
|
| See:
| * http://www.evanmiller.org/how-not-to-sort-by-average-rating.html
| * https://gist.github.com/richardkundl/2950196
| * https://onextrapixel.com/how-to-build-a-5-star-rating-system-with-wilson-interval-in-mysql/
|
*/
function score($positive, $negative) {
return ((($positive + 1.9208) / ($positive + $negative) - 1.96 * sqrt((($positive * $negative) / ($positive + $negative)) + 0.9604) / ($positive + $negative)) / (1 + 3.8416 / ($positive + $negative)));
}
function fiveStarRating($one, $two, $three, $four, $five) {
$positive = $two * 0.25 + $three * 0.5 + $four * 0.75 + $five;
$negative = $one + $two * 0.75 + $three * 0.5 + $four * 0.25;
return score($positive, $negative);
}
function fiveStarRatingAverage($avg, $total)
{
$positive = ($avg * $total - $total) / 4;
$negative = $total - $positive;
return score($positive, $negative);
}
// Examples
echo fiveStarRating(10, 1, 2, 6, 90); // 0.80390178246001
echo fiveStarRating(80, 1, 2, 6, 90); // 0.46188074417216
echo fiveStarRating( 0, 1, 2, 6, 0 ); // 0.33136280289755
echo fiveStarRating(10, 1, 2, 0, 2 ); // 0.079648861762752
echo fiveStarRatingAverage(4.8000001907349, 10); // 0.65545605272928
@git-webmaster
Copy link

Hi, thanks for the PHP implementation)
Explain, please, what is the meaning of parameters in this function:

fiveStarRating($one, $two, $three, $four, $five)

I'm trying to sort a group of objects rated from 1 to 5, each object can be rated more than once.

Also, what's the difference between fiveStarRating() and fiveStarRatingAverage()?
Thank you!

@julienbourdeau
Copy link
Author

In fiveStarRating, each arguments is the number of vote for the note.
If there was 3 votes for 1 star and 4 votes for 5 stars, you would use:

fiveStarRating(3, 0, 0, 0, 4);

The fiveStarRatingAverage version can be used when you don't know the distribution of votes and you only have the average rating and the number of votes.

@git-webmaster
Copy link

Got it. Thanks!

@justanotheranonymoususer

How do fiveStarRating and fiveStarRatingAverage compare in terms of the quality of the sorting? Do you have any data or examples?
Do you have a reference for the fiveStarRating algo, or is it rather arbitrary?
Thanks, and thank you for sharing this snippet.

@julienbourdeau
Copy link
Author

I don't know how different the result would be. I'd expect fiveStarRating to be "better" and I'd use fiveStarRatingAverage only if I lost the details and only stored avg and count but I don't really know.

My feeling is that this should only be one criteria and that it's hard to say that 0.46188074417216 is definitely better than 0.46188074417301`.

@justanotheranonymoususer

I'd expect fiveStarRating to be "better"

Actually I proved they're equal. Let's say that I want to implement fiveStarRatingAverage with fiveStarRating's parameters:

function fiveStarRatingAverage2($one, $two, $three, $four, $five)
{
    // Doesn't compile, $avg, $total not defined
    $positive = ($avg * $total - $total) / 4;
    $negative = $total - $positive;

    return score($positive, $negative);
}

Let's complete the implementation:

function fiveStarRatingAverage2($one, $two, $three, $four, $five)
{
    $total = $one + $two + $three + $four + $five;
    $avg = ($one * 1 + $two * 2 + $three * 3 + $four * 4 + $five * 5) / $total;
    $positive = ($avg * $total - $total) / 4;
    $negative = $total - $positive;

    return score($positive, $negative);
}

Now let's inline $total and $avg into $positive:

function fiveStarRatingAverage2($one, $two, $three, $four, $five)
{
    $total = $one + $two + $three + $four + $five;
    $positive = (($one * 1 + $two * 2 + $three * 3 + $four * 4 + $five * 5) - ($one + $two + $three + $four + $five)) / 4;
    $negative = $total - $positive;

    return score($positive, $negative);
}

And simplify:

function fiveStarRatingAverage2($one, $two, $three, $four, $five)
{
    $total = $one + $two + $three + $four + $five;
    $positive = $two * 0.25 + $three * 0.5 + $four * 0.75 + $five;
    $negative = $total - $positive;

    return score($positive, $negative);
}

Voila! We got the same calculation for $positive. It's not difficult to figure out that $negative is also equal for both functions.

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