tags:

views:

76

answers:

3

Hi,

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

+1  A: 

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.

Bevan
How you do get the values of s back into the order of the input array?
aaronasterling
You don't put `s` back into order, you don't need to (and you couldn't, anyway). `a` is your original array, in original order. `s` is a *copy*.
Bevan
+2  A: 

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'];
}
pferate
+1  A: 

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!

Sebastián Grignoli