Using Median Function with sort and Array - php

I am trying to calculate the Median of an array. I was able to get the MIN, MAX, AVG, But I am having difficulty with the Median and the way the display sorts the information.
My Question is How do I get the Median, using a function individually for each status. (Status being A, C, E, L, P, W)
My Code is:
<?php
echo "<p><b>A</b> = Active, <b>E</b> = Expired, <b>L</b> = Canceled, <b>P</b> = Pending, <b>W</b> = Withdrawn</p>\n";
// COUNT STATUS
include '_inc/include.php';
$query = "SELECT status, COUNT(status) FROM median GROUP BY status";
$result = mysql_query($query) or die(mysql_error());
// Print out result
while($row = mysql_fetch_array($result)){
echo "There are ". $row['COUNT(status)'] ." ". $row['status'] ." items.";
echo "<br />";
}
?>
The display is:
A = Active, C = Closed, E = Expired, L = Canceled, P = Pending, W = Withdrawn
There are 6 A items
There are 2 C items
There are 9 E items
There are 4 L items
There are 2 P items
My Code is:
$query = "SELECT listprice, status, MIN(listprice), COUNT(status) FROM median GROUP BY status";
$result = mysql_query($query) or die(mysql_error());
// Print out result
while($row = mysql_fetch_array($result)){
echo "The MIN list price for the ". $row['COUNT(status)'] ." ". $row['status'] ." is $" . $row['MIN(listprice)'];
echo "<br />";
}
?>
The display is:
The MIN list price for the 6 A is $139900.00
The MIN list price for the 2 C is $149900.00
The MIN list price for the 9 E is $138000.00
The MIN list price for the 4 L is $229000.00
The MIN list price for the 2 P is $179000.00
The MAX and AVG is similar to the above.
I tried to include a function to calculate the Median (MED)
<?php
function median($arr)
{
sort($arr);
$count = count($arr); //count the number of values in array
$middleval = floor(($count-1)/2); // find the middle value, or the lowest middle value
if ($count % 2) { // odd number, middle is the median
$median = $arr[$middleval];
} else { // even number, calculate avg of 2 medians
$low = $arr[$middleval];
$high = $arr[$middleval+1];
$median = (($low+$high)/2);
}
return $median;
}
echo median(array($lp)) . "\n"; // 153500
echo "<p> </p>";
echo median(array(4.1, 7.2, 1.7, 9.3, 4.4, 3.2)) . "\n"; // 4.25
?>
This works for a static array, but how do I calculate it to look like the above MIN, MAX?
<?php
$i=0;
while ($i < $num) {
$ld=mysql_result($result,$i,"listdate");
$cd=mysql_result($result,$i,"closedate");
$contractd=mysql_result($result,$i,"contractdate");
$s=mysql_result($result,$i,"status");
$scd=mysql_result($result,$i,"statuschangedate");
$wd=mysql_result($result,$i,"withdrawdate");
$cand=mysql_result($result,$i,"canceldate");
$lp=mysql_result($result,$i, "listprice");
$sp=mysql_result($result,$i,"soldprice");
$gla=mysql_result($result,$i,"gla");
$b=mysql_result($result,$i,"built");
$o=mysql_result($result,$i,"owner");
$fs=mysql_result($result,$i,"fore");
$ss=mysql_result($result,$i,"shortsale");
$f=mysql_result($result,$i,"features");
?>
I think maybe the reason I having so much trouble with this is that the data in the table is uploaded via file upload (csv). But I didn't think that really had anything to do with it because the data is already there.
I used $lp for listprice in place of the static array in the median function, that is were it goes blank.

...that is where it goes blank.
Blank pages are an indicator of an PHP error being thrown but having error display settings turned off. Make sure you turn on error reporting to see the issue.
One thing of importance, the deprecated mysql_result() function will retrieve the contents of one cell from a MySQL result set. If you were hoping to have multiple values by passing your median function array($lp) it's not going to happen that way. Additionally, if the mysql_result() function fails or doesn't find a result, it returns false mean the array you pass to your median function is now array(false).
Those are some things to keep in mind while troubleshooting. Turn on error reporting and the problem should be much easier to deduce.

There is no median function for most MySQL versions. However, MariaDB 10.3.3 just introduced a median function.

Related

Distributing x amount of gold coins to n number of thieves

Suppose if there are 10 thieves and 100 gold coins to be distributed and the distribution pattern goes like this:
Thief 1 gets 1 coin.
Thief 2 gets 2 coins.
Thief 3 gets 3 coins.... and so on upto 10.
When all the thief have received the coins then the sum of total coins will be (1+2+3+... = 55 coins). Now the coins left are 45. Now, how can I start redistribution from thief one but with the last incremented value instead of starting from 1 coin again? Like 1st thief in 2nd round should get 11 coins instead of 1 and this should continue until all the coins are distributed and coin left is 0. If the last thief has to get 7 coins but the coin available is 3 then he should get 3 coins and the distribution should be over.
I tried this...
$thieves = 10;
$goldCoins = 100;
$thiefArr = range(1, $thieves);
$assgnCoins = 0;
foreach($thiefArr as $key => $value){
for($i=1; $i<=$goldCoins; $i++){
$assgnCoins = $value; // This assigns first round of coins but how to redistribute it again I have no idea.
}
echo "Thief ".$value." will have ".$assgnCoins." gold coins. <br><br>";
}
I'm not really sure if I understood your problem correctly but here's my take, hope this helps:
<?php
$numt = 10; //Total number of thieves
$totalCoins = 100; //Total coins to redistribute
$data = []; //Data array (will contains the index, which is the "thief" number, and the value of a specific index is the total amount of coins for that specific thief
//Loop init: $coins is the amount of coins given to a thief in a specific distribution round
//Loop condition: We loop until there are still coins to redistribute
//Loop statement: We add 1 coin to each redistribution round
// (I start from 0 and use $coins + 1 because I do modulus on $numt ($coins % $numt) which will give me a number from 0 to $numt - 1 (Hint/fact: array indexes start from 0 and not 1)
for ($coins = 0; $totalCoins > 0; $coins++)
{
//This will always give me a number between 0 and $numt - 1 (so that we know which thief's turn is)
//(If this is not clear, you can print out $coins and $thiefIndex then you'll see/understand why I do this)
$thiefIndex = ($coins % $numt);
//Because we did not initialize $data values, we check if this specific thief's coins amount has been initialized
if (!isset($data[$thiefIndex]))
{
$data[$thiefIndex] = 0; //Every thief starts with 0 coins
}
//Because we started from 0, we need to add 1 (this is the amount of coins that this thief ($thiefIndex) will get in this redistribution round
$coinsToGive = ($coins + 1);
//If there's not enough coins left, we just give the total coins remaining
if ($totalCoins < $coinsToGive)
{
$coinsToGive = $totalCoins;
}
//Here we sum up all coins that a thief receives in a specific redistribution round
$data[$thiefIndex] += $coinsToGive;
//We need to subtract the given coins to the total conins that we redistribute (this makes the loop break when it reaches 0 (see the loop condition part)
$totalCoins -= $coinsToGive;
}
//Use data/print in your case
foreach ($data as $idx => $tot)
{
echo "Thief ".($idx + 1)." will have ".$tot." gold coins. <br />";
}
EDIT: I think you edited the question while I was writing my answer. Anyways, if you want to see how much coins every thief is getting on every redistribution round, you can add an echo in the first loop (at the end).
If you're new to programming, you may not know what += or -= means:
$foo += $bar;
Is the same as:
$foo = $foo + $bar;
This can be applied to every arithmetical operator like +, -, *, / (and even other signs like &= and |=, etc... but those will be bit-wise operations)
Information about operators can be found online on the PHP website.
<?php
$thieves = range(1, 10);
$availableCoins = 100;
$assignCoins = 0;
while($availableCoins > 0) {
foreach($thieves as $thief) {
// increase the coins to assign
$assignCoins++;
// check if assign coins are greater than the availableCoins
if ($assignCoins > $availableCoins) {
$assignCoins = $availableCoins;
}
echo "Thief ".$thief." will have ".$assignCoins." gold coins. <br><br>";
// substract assignedCoins from availableCoins
$availableCoins -= $assignCoins;
// break the loop if no coins left
if ($availableCoins <= 0) {
break;
}
}
}
I have now edited the answer of #julian S to give the result that the homework should give:
I also have added some more HTMl input to demonstrate the structure of the array the running operations and the resulting array in readable form.
In short:
The range was wrong here.
Because it will fill an array with 1,2,3,4,5,6,7,8,9, that already is a numerical array so member 1 has a value 1 ?? Useless. NO!
So we crate an array of the 10thives weach with teh initial value = - because teh thieves have = money initially.
We print this array and add pre so that it looks a little better :-)
Then we have two loops - the outer while loop that has the function to look if there is still money left. if there is still moey left then make another round.
That was the logic missing to the original poster as he did understand that he needed a loop for the 10 thieves.
Then there is the INNER LOOP
just count from 1 to the number of thieves. So each thief will get its share.
$a += $b is the short form of $a=$a+$b
Add the currentcoins to the current arrray member
Add a break to leave the for loop if the monex is gone before you are at the end of the 10 thieves.
At the end just print the array with the wealth of the thieves.
0-9 as arrays start with 0.
<?php
$thieves = 10;
$availableCoins = 100;
$assignCoins = 0;
$array_thieves= array_fill(0,10,0);
echo "<h2>Initial array with wealth of each thief</h2> ";
echo "<pre>";
print_r($array_thieves)."<br>";
echo"</pre>";
while($availableCoins > 0) { //inerate as long as there are coins - outer loop
for($i=0; $i<$thieves; $i++) { //execute loop once per thieve so 10 x per round -inner loop
// increase the coins to assign
$assignCoins++; //we have started with 0 and each time we add 1
// check if assign coins are greater than the availableCoins
//if the number of available coins is smaller then just assign all available coins
if ($assignCoins > $availableCoins) {
$assignCoins = $availableCoins;
}
$array_thieves[$i] += $assignCoins; //add the coins to the thied (= array member) with the current number $i [0-9]
echo "Thief ". $i ." gets now additional ".$assignCoins." gold coins. He has now a total of: ". $array_thieves[$i] ." <br>";
// substract assignedCoins from availableCoins
$availableCoins -= $assignCoins;
// break the FOR loop if no coins left - otherwise the inner loop will continue
if ($availableCoins <= 0) {
break;
}
}
}
echo"<h2>Resulting array with all thieves and their wealth</h2>";
echo"<pre>";
print_r($array_thieves);
echo"</pre>";

How to display the serial number of the table rows in descending order in php?

I am looking way to sort the serial number of the table in descending order. I am using a here simple while loop, with a counter variable inside it.
Code sample:
$i= 0;
while(condition)
{
$i++;
echo "<td>".$i."</td>";
}
Output:
I am not using an autoincrement column here. I just want it with a simple counter, but in descending order.
example:
#
10
9
8
7
6
5
4
3
2
1
Any help is highly appreciated.
If you already have loop outputting the 1-10 version, you could simple change the output to show 11 minus the current count...
echo "<td>".(11-$i)."</td>";
Or to change the whole code, you could start at 11 and decrement the counter each time and output it that way
$i= 11;
while($i>0)
{
$i--;
echo "<td>".$i."</td>";
}
count first and after do a loop in reverse order
$i= 0;
while(condition)
{
$i++;
}
for ( cnt= $i, $i>= 0, $i--){
echo "<td>".$cnt."</td>";
}
If you are fetching from MYSQL Database and you're using PDO to connect to Database
//COUNT THE TOTAL NUMBER OF ROWS RETURNED
$sql = "SELECT COUNT(*) FROM all_tnxs ORDER BY id DESC";
$stmt = $con->query($sql);
$stmt_num_rows = $stmt->fetch();
$tot_rows = array_shift($stmt_num_rows);
$sn = $tot_rows
while(){
$sn--;
echo '<td>' . $sn . '</td>';
}
So whatever the total number of rows you have - fetching from the database it'll countdown to '0'using the while loop.
I have been looking for this for long but later figured it out myself so I decided to drop it here for anyone it might be helpful to...

PHP: Optimizing array iteration

i am working on an algorithm for sorting teams based on highest number of score. Teams are to be generated from a list of players. The conditions for creating a team is
It should have 6 players.
The collective salary for 6 players must be less than or equal to 50K.
Teams are to be generated based on highest collective projection.
What i did to get this result is generate all possibilities of team then run checks on them to exclude those teams that have more than 50K salary and then sort the remainder based on projection. But generating all the possibilities takes a lot of time and sometimes it consume all the memory. For a list of 160 players it takes around 90 seconds. Here is the code
$base_array = array();
$query1 = mysqli_query($conn, "SELECT * FROM temp_players ORDER BY projection DESC");
while($row1 = mysqli_fetch_array($query1))
{
$player = array();
$mma_id = $row1['mma_player_id'];
$salary = $row1['salary'];
$projection = $row1['projection'];
$wclass = $row1['wclass'];
array_push($player, $mma_id);
array_push($player, $salary);
array_push($player, $projection);
array_push($player, $wclass);
array_push($base_array, $player);
}
$result_base_array = array();
$totalsalary = 0;
for($i=0; $i<count($base_array)-5; $i++)
{
for($j=$i+1; $j<count($base_array)-4; $j++)
{
for($k=$j+1; $k<count($base_array)-3; $k++)
{
for($l=$k+1; $l<count($base_array)-2; $l++)
{
for($m=$l+1; $m<count($base_array)-1; $m++)
{
for($n=$m+1; $n<count($base_array)-0; $n++)
{
$totalsalary = $base_array[$i][1]+$base_array[$j][1]+$base_array[$k][1]+$base_array[$l][1]+$base_array[$m][1]+$base_array[$n][1];
$totalprojection = $base_array[$i][2]+$base_array[$j][2]+$base_array[$k][2]+$base_array[$l][2]+$base_array[$m][2]+$base_array[$n][2];
if($totalsalary <= 50000)
{
array_push($result_base_array,
array($base_array[$i], $base_array[$j], $base_array[$k], $base_array[$l], $base_array[$m], $base_array[$n],
$totalprojection, $totalsalary)
);
}
}
}
}
}
}
}
usort($result_base_array, "cmp");
And the cmp function
function cmp($a, $b) {
if ($a[6] == $b[6]) {
return 0;
}
return ($a[6] < $b[6]) ? 1 : -1;
}
Is there anyway to reduce the time it takes to do this task, or any other workaround for getting the desired number of teams
Regards
Because number of elements in array can be very big (for example 100 players can generate 1.2*10^9 teams), you can't hold it in memory. Try to save resulting array to file by parts (truncate array after each save). Then use external file sorting.
It will be slow, but at least it will not fall because of memory.
If you need top n teams (like 10 teams with highest projection) then you should convert code that generates result_base_array to Generator, so it will yield next team instead of pushing it into array. Then iterate over this generator. On each iteration add new item to sorted resulted array and cut redundant elements.
Depending on whether the salaries are often the cause of exclusion, you could perform tests on this in the other loops as well. If after 4 player selections their summed salaries are already above 50K, there is no use to select the remaining 2 players. This could save you some iterations.
This can be further improved by remembering the lowest 6 salaries in the pack, and then check if after selecting 4 members you would still stay under 50K if you would add the 2 lowest existing salaries. If this is not possible, then again it is of no use to try to add the two remaining players. Of course, this can be done at each stage of the selection (after selecting 1 player, 2 players, ...)
Another related improvement comes into play when you sort your data by ascending salary. If after selecting the 4th player, the above logic brings you to conclude you cannot stay under 50K by adding 2 more players, then there is no use to replace the 4th player with the next one in the data series either: that player would have a greater salary, so it would also yield to a total above 50K. So that means you can backtrack immediately and work on the 3rd player selection.
As others pointed out, the number of potential solutions is enormous. For 160 teams and a team size of 6 members, the number of combinations is:
160 . 159 . 158 . 157 . 156 . 155
--------------------------------- = 21 193 254 160
6 . 5 . 4 . 3 . 2
21 billion entries is a stretch for memory, and probably not useful to you either: will you really be interested in the team at the 4 432 456 911th place?
You'll probably be interested in something like the top-10 of those teams (in terms of projection). This you can achieve by keeping a list of 10 best teams, and then, when you get a new team with an acceptable salary, you add it to that list, keeping it sorted (via a binary search), and ejecting the entry with the lowest projection from that top-10.
Here is the code you could use:
$base_array = array();
// Order by salary, ascending, and only select what you need
$query1 = mysqli_query($conn, "
SELECT mma_player_id, salary, projection, wclass
FROM temp_players
ORDER BY salary ASC");
// Specify with option argument that you only need the associative keys:
while($row1 = mysqli_fetch_array($query1, MYSQLI_ASSOC)) {
// Keep the named keys, it makes interpreting the data easier:
$base_array[] = $row1;
}
function combinations($base_array, $salary_limit, $team_size) {
// Get lowest salaries, so we know the least value that still needs to
// be added when composing a team. This will allow an early exit when
// the cumulative salary is already too great to stay under the limit.
$remaining_salary = [];
foreach ($base_array as $i => $row) {
if ($i == $team_size) break;
array_unshift($remaining_salary, $salary_limit);
$salary_limit -= $row['salary'];
}
$result = [];
$stack = [0];
$sum_salary = [0];
$sum_projection = [0];
$index = 0;
while (true) {
$player = $base_array[$stack[$index]];
if ($sum_salary[$index] + $player['salary'] <= $remaining_salary[$index]) {
$result[$index] = $player;
if ($index == $team_size - 1) {
// Use yield so we don't need to build an enormous result array:
yield [
"total_salary" => $sum_salary[$index] + $player['salary'],
"total_projection" => $sum_projection[$index] + $player['projection'],
"members" => $result
];
} else {
$index++;
$sum_salary[$index] = $sum_salary[$index-1] + $player['salary'];
$sum_projection[$index] = $sum_projection[$index-1] + $player['projection'];
$stack[$index] = $stack[$index-1];
}
} else {
$index--;
}
while (true) {
if ($index < 0) {
return; // all done
}
$stack[$index]++;
if ($stack[$index] <= count($base_array) - $team_size + $index) break;
$index--;
}
}
}
// Helper function to quickly find where to insert a value in an ordered list
function binary_search($needle, $haystack) {
$high = count($haystack)-1;
$low = 0;
while ($high >= $low) {
$mid = (int)floor(($high + $low) / 2);
$val = $haystack[$mid];
if ($needle < $val) {
$high = $mid - 1;
} elseif ($needle > $val) {
$low = $mid + 1;
} else {
return $mid;
}
}
return $low;
}
$top_team_count = 10; // set this to the desired size of the output
$top_teams = []; // this will be the output
$top_projections = [];
foreach(combinations($base_array, 50000, 6) as $team) {
$j = binary_search($team['total_projection'], $top_projections);
array_splice($top_teams, $j, 0, [$team]);
array_splice($top_projections, $j, 0, [$team['total_projection']]);
if (count($top_teams) > $top_team_count) {
// forget about lowest projection, to keep memory usage low
array_shift($top_teams);
array_shift($top_projections);
}
}
$top_teams = array_reverse($top_teams); // Put highest projection first
print_r($top_teams);
Have a look at the demo on eval.in, which just generates 12 players with random salary and projection data.
Final remarks
Even with the above mentioned optimisations, doing this for 160 teams might still require a lot of iterations. The more often the salaries amount to more than 50K, the better the performance will be. If this never happens, the algorithm cannot escape from having to look at each of the 21 billion combinations. If you would know beforehand that the 50K limit would not play any role, you would of course order the data by descending projection, like you originally did.
Another optimisation could be if you would feed back into the combination function the 10th highest team projection you have so far. The function could then eliminate combinations that would lead to a lower total projection. You could first take the 6 highest player projection values and use this to determine how high a partial team projection can still grow by adding the missing players. It might turn out that this becomes impossible after having selected a few players, and then you can skip some iterations, much like done on the basis of salaries.

Calculating average from a percent

I am using DW to create record set that calculates the percentage from a table grouped by hospital name. I need to get the average of the percentage (by summation of the percentage divided by the number of hospital). I have only one table.
mysql_select_db($database_localapconnect, $localapconnect);
$query_Recordset1 = "SELECT COUNT(IF(ap_intervalname = 'Less than or equal to 12 hours', ap_intervalname, NULL))/ COUNT(ap_intervalname) *100 AS 'percent' FROM maintble GROUP BY `hospitalname`";
$Recordset1 = mysql_query($query_Recordset1, $localapconnect) or die(mysql_error());
$totalRows_Recordset1 = mysql_num_rows($Recordset1);
while($row_Recordset1 = mysql_fetch_array($Recordset1)) {
print_r( $row_Recordset1['percent'].'<br>');
}
print_r ($totalRows_Recordset1).'<br>' ;
echo sum($row_Recordset1['percent'])/$totalRows_Recordset1;
result:
83.0189
98.0000
86.2745
68.0365
94.9686
78.4314
6
I get the following error:
Fatal error: Call to undefined function sum() in C:\wamp\www\ap_database\Untitled-1.php on line 49
The function sum doesn't exist in PHP. If you want the sum of the elements of an array, you should use array_sum
But, in this specific case, I think it's better sum inside the loop, because you are already accessing all the elements anyway...
For example, like this:
$totalRows_Recordset1 = mysql_num_rows($Recordset1);
$sum = 0.0;
while($row_Recordset1 = mysql_fetch_array($Recordset1)) {
print_r( $row_Recordset1['percent'].'<br>');
$sum += $row_Recordset1['percent'];
}
print_r ($totalRows_Recordset1).'<br>' ;
echo $sum/$totalRows_Recordset1;
The easiest way is to use a subquery:
SELECT avg(percent)
FROM (SELECT AVG(CASE WHEN ap_intervalname = 'Less than or equal to 12 hours' THEN 100.0
ELSE 0 END) AS percent
FROM maintble
GROUP BY `hospitalname`
) m;
You're calling sum() in this line of php code.
sum($row_Recordset1['percent'])/$totalRows_Recordset1; /* mistake */
But that doesn't make sense. For one thing, $row_Recordset1['percent'] is just a plain old number. For another thing, php doesn't have a built in sum() function.
If you don't want to use SQL to do that computation, you need to add up the values in your recordset processing loop.
$sum = 0.0;
$count = 0;
while($row_Recordset1 = mysql_fetch_array($Recordset1)) {
$count ++;
$sum += $row_Recordset1['percent'];
print_r( $row_Recordset1['percent'].'<br>');
}
echo $sum / $count;
Notice I counted the rows explicitly rather than relying on num_rows(). That's for a good reason; under some circumstances num_rows() is not reliable before you read the recordset.

adding/subtract random number to variable

I want to create some random sql data with a php script
currently I get the last record, generate a random number between 1 and 2 ... if its a 1 I will add a random number to the record else if its a 2 i will subtract a random number from
the problem is it keeps spitting out minus random numbers! I only want positive random numbers.
$result = mysql_query("SELECT * FROM Data ORDER BY id DESC")
or die(mysql_error());
while($row = mysql_fetch_array( $result )) {
// Print out the contents of each row into a table
$temp=$row['temp'];
$light=$row['light'];
};
$name="Herbi";
$date=Date("o:m:d");
$time=Date("H:i:s");
$rand =rand(1,2);
$randnu =rand(1,10);
echo " rand:".$rand;
switch($rand){
case 1:
$temprand=$temp+$randnu;
$lightrand=$light+$randnu;
break;
case 2:
$temprand=$temp-$randnu;
$lightrand=$light-$randnu;
break;
};
echo"";
echo"randnu";
echo $randnu;
echo " ";
echo"lightrand";
echo $lightrand;
Based on your code this is valid, if your $temp=1 and $rand=2 then $temprand will be -1.
You can add a check that your $temp and $light should always be greater than or equal the max random numbers so when you minus the (max of) random number from $temp or $light you will end up with 0.
if($row['temp']>=2){
$temp=$row['temp'];
}else{
$temp=2; //Max of $rand =rand(1,2);
}
or shorthand
$temp=($row['temp'] >= 2? $row['temp'] : 2);
$light=($row['light'] >= 10? $row['light'] : 10);

Categories