Find percentile using an array in php - php

I have a array like this
array(
45=>5,
42=>4.9,
48=>5,
41=>4.8,
40=>4.9,
34=>4.9,
.....
)
Here index is userid and value is his score.
Now what i want is to achieve percentile for on user for example percentile of 45,48 would be 99 and 42,40,34 would be 97 and 41 would be 94.
How i can achieve this?

Sort the array based on the "score", ascending
Percentile = (Index of an element in the sorted array ) * 100 / (total elements in the array)
Example:
<?php
$array = array(
45=>5,
42=>4.9,
48=>5,
41=>4.8,
40=>4.9,
34=>4.9,
);
print("Unsorted array:<br/>");
print_r($array);
arsort($array);
print("<br/>");
print("Sorted array:<br/>");
print_r($array);
print("<br/>");
$i=0;
$total = count($array);
$percentiles = array();
$previousValue = -1;
$previousPercentile = -1;
foreach ($array as $key => $value) {
echo "\$array[$key] => $value";
if ($previousValue == $value) {
$percentile = $previousPercentile;
} else {
$percentile = 99 - $i*100/$total;
$previousPercentile = $percentile;
}
$percentiles[$key] = $percentile;
$previousValue = $value;
$i++;
}
print("Percentiles:<br/>");
print_r($percentiles);
print("<br/>");
?>

It can be done a lot easier
function procentile($arr, $percentile=0.95){
sort($arr);
return $arr[round($percentile * count($arr) - 1.0-$percentile)];
}

Related

How to add string into array in PHP?

I have these for loop to determine consecutive number. What I achieve so far is to print the output in string.
$arr = [1,2,3,6,11,5,4,8,9,3];
for($start=0; $start<=count($arr); $start++){
for($end=$start+1; $end<=count($arr); $end++){
$total = $arr[$end] - $arr[$start];
if($total == 1){
echo 'Number is '.$arr[$start].','.$arr[$end].'<br/>';
} else {
echo '';
}
$arr[$start++];
}
}
My goal is to add the output into array.
I tried to use multidimensional array but no output display.
$arr = [1,2,3,6,11,5,4,8,9,3];
$arr3 = [];
for($start=0; $start<=count($arr); $start++){
for($end=$start+1; $end<=count($arr); $end++){
$total = $arr[$end] - $arr[$start];
if($total == 1){
$arr2 = array();
$arr2[] = $arr[$start].','.$arr[$end].'';
$arr3[] = $arr2;
} else {
}
$arr[$start++];
}
}
echo '<pre>';
print_r($arr3);
echo '</pre>';
exit;
Appreciate if someone can help me. Thanks.
you can simply use array functions, if sorting is important to you as #nice_dev said, you must sort your array before.
$arr = [1,2,3,6,11,5,4,8,9,3];
$cons = [] ;
while (array_key_last($arr) != key($arr)) {
if ((current($arr)+1) == next($arr)) {
prev($arr);
$cons[] = current($arr) . ',' . next($arr);
}
}
print_r($cons);
the output will be :
Array
(
[0] => 1,2
[1] => 2,3
[2] => 8,9
)
You can better sort() the input array first. This way, collecting all consecutive elements would get much simpler. If value at any index isn't +1 of the previous one, we add the $temp in our $results array and start a new $temp from this index.
Snippet:
<?php
$arr = [1,2,3,6,11,5,4,8,9,3];
$result = [];
sort($arr);
$temp = [];
for($i = 0; $i < count($arr); ++$i){
if($i > 0 && $arr[ $i ] !== $arr[$i - 1] + 1){
$result[] = implode(",", $temp);
$temp = [];
}
$temp[] = $arr[$i];
if($i === count($arr) - 1) $result[] = implode(",", $temp);
}
print_r($result);
Online Demo

How to find the smallest and largest value from this array

I have this array which stores the values in the array in this format
Array
(
[0] => 0,20
[1] => 21,50
[2] => 201,300
[3] => 301,400
)
now how will I find the smallest and the largest numeric value from it ?
I think you need minimum and maximum range.
For minimum and maximum range, first walk through array and replace
, by . so that they become numbers (comparable).
Them find out min() and max() of the resulting array.
Find out the key where the elements sit.
Now, access the elements of the original array with these keys.
<?php
$org = $result = Array(
'0,20',
'21,50',
'201,300',
'301,400',
);
$result = array_map(function($el) {
return str_replace(',','.',$el); }, $result
);
echo '<pre>';
$minKey = array_search(min($result), $result);
$maxKey = array_search(max($result), $result);
$min2 = $org[$minKey]; // Returns 0,20
$temp = explode(',', $min2);
$min = $temp[0];
echo $min; // Prints 0
echo "<br/>";
$max2 = $org[$maxKey]; // Returns 301,400
$temp = explode(',', $max2);
$max = $temp[1];
echo $max; // Prints 400
?>
Try as below :
$final_array = array();
foreach($a as $val)
{
$exploded_val = explode(",",$val);
$final_array[] = $exploded_val[0];
$final_array[] = $exploded_val[1];
}
sort($final_array);
$min = $final_array[0];
$max = $final_array[count($final_array)-1];
echo $min.'>>'.$max;
Why not using min and max functions?
You should watch out how you store your data... If I understood well, you just need to explode the array values, to mount a proper array and then use max and min functions.
Something like this:
$ar = array("0,20","21,50","201,300","301,400");
foreach ($ar as $el) {
$el = explode(",", $el);
$result[] = $el[0];
$result[] = $el[1];
}
echo "min: ".min($result)."\n";
echo "max: ".max($result);

Expand array of numbers and hyphenated number ranges to array of integers [duplicate]

This question already has answers here:
Populate array of integers from a comma-separated string of numbers and hyphenated number ranges
(8 answers)
Closed 6 months ago.
I'm trying to normalize/expand/hydrate/translate a string of numbers as well as hyphen-separated numbers (as range expressions) so that it becomes an array of integer values.
Sample input:
$array = ["1","2","5-10","15-20"];
should become :
$array = [1,2,5,6,7,8,9,10,15,16,17,18,19,20];
The algorithm I'm working on is:
//get the array values with a range in it :
$rangeArray = preg_grep('[-]',$array);
This will contain ["5-10", "16-20"]; Then :
foreach($rangeArray as $index=>$value){
$rangeVal = explode('-',$value);
$convertedArray = range($rangeVal[0],$rangeVal[1]);
}
The converted array will now contain ["5","6","7","8","9","10"];
The problem I now face is that, how do I pop out the value "5-10" in the original array, and insert the values in the $convertedArray, so that I will have the value:
$array = ["1","2",**"5","6","7","8","9","10"**,"16-20"];
How do I insert one or more values in place of the range string? Or is there a cleaner way to convert an array of both numbers and range values to array of properly sequenced numbers?
Here you go.
I tried to minimize the code as much as i can.
Consider the initial array below,
$array = ["1","2","5-10","15-20"];
If you want to create a new array out of it instead $array, change below the first occurance of $array to any name you want,
$array = call_user_func_array('array_merge', array_map(function($value) {
if(1 == count($explode = explode('-', $value, 2))) {
return [(int)$value];
}
return range((int)$explode[0], (int)$explode[1]);
}, $array));
Now, the $array becomes,
$array = [1,2,5,6,7,8,9,10,15,16,17,18,19,20];
Notes:
Casts every transformed member to integer
If 15-20-25 is provided, takes 15-20 into consideration and ignores the rest
If 15a-20b is provided, treated as 15-20, this is result of casing to integer after exploded with -, 15a becomes 15
Casts the array keys to numeric ascending order starting from 0
New array is only sorted if given array is in ascending order of single members and range members combined
Try this:
<?php
$array = ["1","2","5-10","15-20"];
$newdata = array();
foreach($array as $data){
if(strpos($data,'-')){
$range = explode('-', $data);
for($i=$range[0];$i<=$range[1];$i++){
array_push($newdata, $i);
}
}
else{
array_push($newdata, (int)$data);
}
}
echo "<pre>";
print_r($array);
echo "</pre>";
echo "<pre>";
print_r($newdata);
echo "</pre>";
Result:
Array
(
[0] => 1
[1] => 2
[2] => 5-10
[3] => 15-20
)
Array
(
[0] => 1
[1] => 2
[2] => 5
[3] => 6
[4] => 7
[5] => 8
[6] => 9
[7] => 10
[8] => 15
[9] => 16
[10] => 17
[11] => 18
[12] => 19
[13] => 20
)
Problem solved!
Using range and array_merge to handle the non-numeric values:
$array = ["1","2","5-10","15-20"];
$newArray = [];
array_walk(
$array,
function($value) use (&$newArray) {
if (is_numeric($value)) {
$newArray[] = intval($value);
} else {
$newArray = array_merge(
$newArray,
call_user_func_array('range', explode('-', $value))
);
}
}
);
var_dump($newArray);
It's easier to find out the minimum and maximum value and create the array with them. Here's an example:
$in = ["1","2","5-10","15-20"];
$out = normalizeArray($in);
var_dump($out);
function normalizeArray($in)
{
if(is_array($in) && sizeof($in) != 0)
{
$min = null;
$max = null;
foreach($in as $k => $elem)
{
$vals = explode('-', $elem);
foreach($vals as $i => $val)
{
$val = intval($val);
if($min == null || $val < $min)
{
$min = $val;
}
if($max == null || $val > $max)
{
$max = $val;
}
}
}
$out = array();
for($i = $min; $i <= $max; $i++)
{
$out[] = $i;
}
return $out;
}
else
{
return array();
}
}
here you go mate.
<?php
$array = ["1","2","5-10","15-20"];
$newArr = array();
foreach($array as $item){
if(strpos($item, "-")){
$temp = explode("-", $item);
$first = (int) $temp[0];
$last = (int) $temp[1];
for($i = $first; $i<=$last; $i++){
array_push($newArr, $i);
}
}
else
array_push($newArr, $item);
}
print_r($newArr);
?>
Simpler and shorter answer.
See in Ideone
$new_array = array();
foreach($array as $number){
if(strpos($number,'-')){
$range = explode('-', $number);
$new_array = array_merge($new_array, range($range[0],$range[1]));
}
else{
$new_array[] = (int) $number;
}
}
var_dump($new_array);
try this:
$array = ["1","2","5-10","15-20"];
$result = [];
foreach ($array as $a) {
if (strpos($a,"-")!== false){
$tmp = explode("-",$a);
for ($i = $tmp[0]; $i<= $tmp[1]; $i++) $result[] = $i;
} else {
$result[] = $a;
}
}
var_dump($result);
you did not finish a little
$array = ["1","2","5-10","15-20"];
// need to reverse order else index will be incorrect after inserting
$rangeArray = array_reverse( preg_grep('[-]',$array), true);
$convertedArray = $array;
foreach($rangeArray as $index=>$value) {
$rangeVal = explode('-',$value);
array_splice($convertedArray, $index, 1, range($rangeVal[0],$rangeVal[1]));
}
print_r($convertedArray);

how to get top 3 values in php array and their index

I want to get the highest value, the second highest value and the third highest value
For example, I have an array like:
$n = array(100,90,150,200,199,155,15,186);
I know the method to get the max value and its index:
echo max($n); //200
$maxs = array_keys($n, max($n));
echo $maxs[0]; //3
I want to get the top 3 values and their index like : value: 200, 199, 186 index:3,4,7
How can i get them?
Try this:
$n = array(100,90,150,200,199,155,15,186);
rsort($n);
$top3 = array_slice($n, 0, 3);
echo 'Values: ';
foreach ($top3 as $key => $val) {
echo "$val\n";
}
echo '<br>';
echo 'Keys: ';
foreach ($top3 as $key => $val) {
echo "$key\n";
}
Output:
Values: 200 199 186
Keys: 0 1 2
This should do the trick:
function maxNitems($array, $n = 3){
asort($array);
return array_slice(array_reverse($array, true),0,$n, true);
}
Use like:
maxNitems(array(100,90,150,200,199,155,15,186));
You can achieve it by using arsort() and array_keys() functions:
arsort() sorts an array in reverse order and maintains index association
array_keys() returns all the keys or a subset of the keys of an array
Process array:
$n = array(100,90,150,200,199,155,15,186);
arsort($n);
$keys = array_keys($n);
Get top 3 values:
echo $n[$keys[0]];
echo $n[$keys[1]];
echo $n[$keys[2]];
$n = array(100,90,150,200,199,155,15,186);
arsort($n);
$x = 0;
while (++$x <= 3)
{
$key = key($n);
$value = current($n);
next($n);
echo "Key : " . $key . " Value : " . $value . '<br>' ;
}
Easier I would think:
arsort($n);
$three = array_chunk($n, 3, true)[0];
//or
$three = array_slice($n, 0, 3, true);
try this:
public function getTopSortedThree(array $data, $asc = true)
{
if ($asc) {
uasort($data, function ($a, $b) { return $a>$b;});
} else {
uasort($data, function ($a, $b) { return $a<$b;});
}
$count = 0;
$result = [];
foreach ($data as $key => $value) {
$result[] = $data[$key];
$count++;
if ($count >= 3){
break;
}
}
return $result;
}
send false for desc order and nothing for asc order
This functionality doesn't losing keys.

How to assign a rank number to an array when ties exist

I am struggling to know where to start when trying to assign ranks to the numeric values in an array when there are ties. So, for example, I need to turn an array like the following:
myarray = (4,76,34,13,34)
into another array like:
myarray2 = (1,5,3.5,2,3.5)
Basically, when the same number occurs more than once in the array, the assigned rank to those numbers is the average of the ranks. So, instead of the two 34s being ranked 3 and 4 they both get assigned 3.5. Similarly, if there were 3 copies of 34 then the 3 assigned ranks would be divided by 3. Any help would be much appreciated!
Many thanks,
Adam
I had fun with this one!
function rank($input)
{
$output = array();
$ranking = $input; sort($ranking); $ranking = array_flip($ranking);
$last_val = -1;
foreach($ranking as $key => $val){
$repetitions = ($val-$last_val-1);
$last_val = $val;
if($repetitions) {
$ranking[$key] = (($val*($repetitions+1))-($repetitions+1)*(($repetitions)/2))/($repetitions+1)+1 ;
} else {
$ranking[$key] = $val+1;
}
}
foreach($input as $key => $val){
$output[$key] = $ranking[$val];
}
return $output;
}
Use it like this:
$a = array(4,76,34,13,34);
$c = rank($a);
print_r($c);
will output:
Array
(
[0] => 1
[1] => 5
[2] => 3.5
[3] => 2
[4] => 3.5
)
wich is the same as:
Array(1, 5, 3.5, 2, 3.5)
as expected!
Here is one way to do it.
<?php
$myarray = array(4,76,34,13,34);
$sorted_array = $myarray;
$grouped_array = array();
sort($sorted_array);
foreach ($sorted_array as $rank => $entry) {
// Initialize the entry if it doesn't already exist
if (empty($grouped_array[$entry])) {
$grouped_array[$entry]['count'] = 1.0;
$grouped_array[$entry]['total'] = $rank + 1; // Account for 0-based array
} else {
$grouped_array[$entry]['count'] += 1.0;
$grouped_array[$entry]['total'] += $rank + 1; // Account for 0-based array
}
}
$myarray2 = array();
foreach ($myarray as $entry) {
// Get the average
$myarray2[] = $grouped_array[$entry]['total'] / $grouped_array[$entry]['count'];
}
I assume you also need to handle the cases where there are three or four or n values tied at the same rank.
I'm no PHP guru, but here's an approach (pseudo code) to defining a rank function:
define a = original array
define s = a.Sorted
define rank(n) = (s.FirstIndexOf(n) + s.LastIndexOf(n)) / 2
You may need to work a few examples on paper to convince yourself that this works even for triples and higher; it's reliant on s being sorted so that duplicates are adjacent.
The accepted solution (and others too) seem to be way more complicated than they need to be:
function Rank($data) {
$count = 0;
$unique = $data; sort($unique);
$unique = array_count_values($unique);
foreach ($unique as $key => $frequency) {
foreach (range(1, $frequency) as $i) {
$unique[$key] += $count++;
}
$unique[$key] /= $frequency;
}
foreach ($data as $key => $value) {
$data[$key] = $unique[$value];
}
return $data;
}
Example (demo):
print_r(Rank(array(4, 76, 34, 13, 34))); // 1; 5; 3.5; 2; 3.5
print_r(Rank(array(4, 76, 34, 13, 34, 34))); // 1; 6; 4; 2; 4; 4

Categories