0

I have two arrays, percentiles and percentile_bounds and another int value total_score. Given the total score, I want to return the percentile in which this value is. Ie. I want to return a value from percentiles which corresponds to placing the total_score within the percentile_bounds. In python, I would do:

import numpy as np

percentiles = np.array([ 0,  5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95])
percentile_bounds = np.array([ 84, 104, 109, 115, 120, 123, 125, 127, 129, 132, 135, 136, 137, 139, 141, 145, 148, 151, 155, 159])

# example 1
total_score = 130
print(percentiles[np.where(percentile_bounds==(percentile_bounds[percentile_bounds<total_score][-1]))])
# 40

# example 2
total_score = 153
print(percentiles[np.where(percentile_bounds==(percentile_bounds[percentile_bounds<total_score][-1]))])
# 85

# example 3
total_score = 100
print(percentiles[np.where(percentile_bounds==(percentile_bounds[percentile_bounds<total_score][-1]))])
# 0

and I found a way in PHP (function sources: 1, 2) but it is very clumsy and long:

<?php
// Example 4
$total_score = 120;

$percentile_bounds = [84, 104, 109, 115, 120, 123, 125, 127, 129, 132, 135, 136, 137,139, 141, 145, 148, 151, 155, 159];
       
$percentiles = [0,  5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95];

// get the last element from percentile_bounds which is less than total_score
$value = end(array_filter($percentile_bounds, function ($x) use ($total_score) { return $x < $total_score; }));

// find its index
$key = array_search($value, $percentile_bounds);

// find the corresponding value in percentiles
echo "percentile: $percentiles[$key]";
// 15

Is there a better way of selecting the element from one array (percentiles) based on a position of an element (total_score) in the other array (percentile_bounds)?

Thanks

Note: it is not a duplicate of this.

Clarification: For total_score<=84 the result should be 0 (based on how percentiles work) but it also should not happen. In the code above I do not deal with this condition so it's ok not to but if the solution does, the better :). Thanks @jspit for pointing it out!

4
  • 1
    Why is this two arrays to begin with, why not one array that uses 0, 5, 10 etc. as keys? Commented Oct 28, 2021 at 11:20
  • I guess that's just because I think in python and not PHP :D. I have data, then from them, I get the percentiles and the bounds which are two different arrays, then I go back to PHP (I do not have the data within PHP). I didn't think of it as a dictionary. Commented Oct 28, 2021 at 11:22
  • no result is defined for a total_score <= 84 Commented Oct 28, 2021 at 12:23
  • @jspit, thanks, good point, in that case, it should be 0 (based on how percentiles work) but it also should not happen. Commented Oct 28, 2021 at 12:28

2 Answers 2

1

You can do it with this line where array_keys gives us an array of keys of our filtered array. end and max do the same job in this case because we have a sorted array and want to return its last index. So finally $percentiles[$lastIndexKeyOfOurFilteredArray] is what we print out:

echo $percentiles[max(array_keys(array_filter($percentile_bounds,function ($x) use ($total_score){return $x<$total_score; })))]
Sign up to request clarification or add additional context in comments.

5 Comments

While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. You can find more information on how to write good answers in the help center: stackoverflow.com/help/how-to-answer . Good luck 🙂
what @nima writes is exactly what I would like to ask you. Could you please provide some explanations to your code? How it works, what it does etc., exactly as nima writes. Thanks
Sure, array_keys gives us array of keys of our filtered array. end and max do the same job in this case because we have sorted array and want to return its last index. So finally $percentiles[$lastIndexKeyOfOurFilteredArray] is what we print out.
One more issue with this: if array_keys is empty (the value is less than any in thepercentile_bounds), the max function fails. How can it be fixed? The expected behaviour is to return 0.
PHP max function will throw an E_WARNING error and return a boolean false because of empty array input. PHP internal type-casting conversion will convert the boolean false to integer 0 so you already have your expected 0. You can skip the warning with an "@" {echo @percentiles[max(arr...}. Obviously its just a trick for preserving single-line style of the code and its better to use if/else statement for validating data before passing to function to avoid errors.
1

foreach would be more readable and performant approach compared to array_filter

$total_score = 153;

$percentile_bounds = [84, 104, 109, 115, 120, 123, 125, 127, 129, 132, 135, 136, 137, 139, 141, 145, 148, 151, 155, 159];

$percentiles = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95];

foreach ($percentile_bounds as $key => $bound) {
    if ($bound >= $total_score) {
        echo 'percentile: ' . $percentiles[max(0, $key - 1)];
        break;
    }
}

4 Comments

echo "percentile: ".($key > 0 ? $percentiles[$key-1] : "??");
@jspit Good catch. thanks, buddy. we can use max() instead of ternary operator to resolve negative index
And I believe it's missing an equal sign: if ($bound >= $total_score) otherwise, the borders fall into the wrong interval. Also, would you have an explanation or reference for why it has better performance? Thanks
One more thing, it fails for $total_score>=160

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.