Smoothing Data Array PHP - php

I have an array:
Array
(
[1] => 25
[2] => 50
[3] => 25
)
I would like to make it into:
Array
(
[1] => 50
[2] => 50
)
To do this I split the middle value between 1 and 3. This is the simplest example, where the split is 50,50. I would like to be able to take a 15 element array down to 6 elements.
Any ideas?
Additional Examples
[10, 15, 20, 25] Reduced to two elements: 25(10 + 15),45(20 + 25)
[10, 10, 10, 10, 11] Reduced to two elements: 25(10 + 10 + (10/2)),26((10/2) + 10 + 11)

After doing additional tests on Peter's solution, I noticed it did not get me what I expected if the reduce to size is an odd number. Here is the function I came up with. It also inflates data sets that are smaller then the requested size.
<?php
function reduceto($data,$r) {
$c = count($data);
// just enough data
if ($c == $r) return $data;
// not enough data
if ($r > $c) {
$x = ceil($r/$c);
$temp = array();
foreach ($data as $v) for($i = 0; $i < $x; $i++) $temp[] = $v;
$data = $temp;
$c = count($data);
}
// more data then needed
if ($c > $r) {
$temp = array();
foreach ($data as $v) for($i = 0; $i < $r; $i++) $temp[] = $v;
$data = array_map('array_sum',array_chunk($temp,$c));
}
foreach ($data as $k => $v) $data[$k] = $v / $r;
return $data;
}
?>

You could sum the values using array_sum() and then, depending on the number of elements you want to have in your resulting array, divide that sum and fill every element you want to keep with the result of your division.
(Here I'm assuming you'll use a second array, but you could unset the unneeded if you prefer it that way).

Here's my stab at your issue
<pre>
<?php
class Thingy
{
protected $store;
protected $universe;
public function __construct( array $data )
{
$this->store = $data;
$this->universe = array_sum( $data );
}
public function reduceTo( $size )
{
// Guard condition incase reduction size is too big
$storeSize = count( $this->store );
if ( $size >= $storeSize )
{
return $this->store;
}
// Odd number of elements must be handled differently
if ( $storeSize & 1 )
{
$chunked = array_chunk( $this->store, ceil( $storeSize / 2 ) );
$middleValue = array_pop( $chunked[0] );
$chunked = array_chunk( array_merge( $chunked[0], $chunked[1] ), floor( $storeSize / $size ) );
// Distribute odd-man-out amonst other values
foreach ( $chunked as &$chunk )
{
$chunk[] = $middleValue / $size;
}
} else {
$chunked = array_chunk( $this->store, floor( $storeSize / $size ) );
}
return array_map( 'array_sum', $chunked );
}
}
$tests = array(
array( 2, array( 25, 50, 25 ) )
, array( 2, array( 10, 15, 20, 25 ) )
, array( 2, array( 10, 10, 10, 10, 11 ) )
, array( 6, array_fill( 0, 15, 1 ) )
);
foreach( $tests as $test )
{
$t = new Thingy( $test[1] );
print_r( $t->reduceTo( $test[0] ) );
}
?>
</pre>

Related

How do you find the closest value to a number in an associative array?

I have an associative array with 15 different companies and their stock prices, formatted as shown below:
$CloseStockPrice ('Business'=>50.5. 'Business two'=>100.5, .....)
I have found the average stock price:
$Average = (array_sum($CloseStockPrice)/count($CloseStockPrice));
The average ends up being 161.
But I now need to find the closest number (absolute terms) to that average value (161) within the associative array. I need to display the business and the stock value.
My most recent attempt:
function computeClosest(array $CloseStockPrice)
{
$closest = 161;
for ($i = 161; $i < count($CloseStockPrice) ; $i++)
{
if ($closest === 161)
{
$closest = $CloseStockPrice[$i];
} else if ($CloseStockPrice[$i] > 161 && $CloseStockPrice[$i] <= abs($closest))
{
$closest = $CloseStockPrice[$i];
} else if ($CloseStockPrice[$i] < 161 && -$CloseStockPrice[$i] < abs($closest))
{
$closest = $CloseStockPrice[$i];
return $closest;
}
}
}
Any suggestions?
While you loop through your array of business entries, cache the businesses with prices with the smallest absolute difference involving the average value.
While you might expect a single value in many cases, the fact that multiple qualifying businesses is possible means that you must keep an array of qualifying businesses for most accurate results.
A linear (single loop) process (O(n)) will outperform a sort algorithm (O(n log n)).https://stackoverflow.com/q/56506410/2943403
Code: (Demo)
$closeStockPrice = [
'A' => 50,
'B' => 155,
'C' => 75,
'D' => 245,
'E' => 300,
'F' => 100,
'G' => 153,
];
$average = array_sum($closeStockPrice) / count($closeStockPrice);
$bestDistances = [];
foreach ($closeStockPrice as $business => $price) {
$distance = abs($average - $price);
$current = current($bestDistances);
if (!$bestDistances || $current > $distance) {
$bestDistances = [$business => $distance]; // new best distance
} elseif ($current === $distance) {
$bestDistances[$business] = $distance; // push business with same distance
}
}
var_export([
'average' => $average,
'bestDistances' => $bestDistances,
'bestBusinessPrices' => array_intersect_key($closeStockPrice, $bestDistances)
]);
Output:
array (
'average' => 154,
'bestDistances' =>
array (
'B' => 1,
'G' => 1,
),
'bestBusinessPrices' =>
array (
'B' => 155,
'G' => 153,
),
)
I'm not here to agree with the stock market, but you may want to know what they're up to:
<?php
class AvgSorter{
public $sorted = [];
public function sort($array){
$s = &$this->sorted; array_splice($s, 0);
foreach($array as $k => $v){
$s[$k] = $v;
}
$avg = array_sum($s)/count($s);
uasort($s, function($a, $b) use($avg){
$a = abs($a-$avg); $b = abs($b-$avg);
if($a === $b){
return 0;
}
return $a > $b ? 1 : -1;
});
return $this;
}
public function firstKey(){
return array_keys($this->sorted)[0];
}
public function firstValue(){
return $this->sorted[$this->firstKey()];
}
public function lastKey(){
return array_keys($this->sorted)[count($this->sorted)-1];
}
public function lastValue(){
return $this->sorted[$this->lastKey()];
}
}
$array = ['business 1'=>1000.32, 'business 2'=>5.15, 'business 3'=>10.22, 'business 4'=>4.01, 'business 5'=>10.82, 'business 6'=>3.12, 'business 7'=>1.01];
$avgSorter = new AvgSorter; $firstKey = $avgSorter->sort($array)->firstKey();
$firstValue = $avgSorter->firstValue(); $lastKey = $avgSorter->lastKey();
$lastValue = $avgSorter->lastValue();
$test = 'first key:'.$firstKey.', first value:'.$firstValue.', last key:'.$lastKey.', last value:'.$lastValue;
?>

How can sort an array without using sort() function in php?

I'm trying to sort an array without using sort() function of php. I have tried so far and also google but unable to find result.
What I Need exactly
$arr = array(80, 90, 100, 10, 50, 3);
I want to sort this array in ascending. i can do it using sort() function but I want to do without using sort() function.
Are you looking for this:
<?php
$arr = array(80, 90, 100, 10, 50, 3);
for($a = 0; $a < count($arr); $a++) {
for($b = 0; $b < count($arr)-1; $b ++){
if($arr[$b] > $arr[$b+1]) {
$temp = $arr[$b+1];
$arr[$b+1]=$arr[$b];
$arr[$b]=$temp;
}
}
}
print_r($arr);
?>
Implementing QuickSort in PHP
or
Quicksort recursive.php
function quicksort( $array ) {
if( count( $array ) < 2 ) {
return $array;
}
$left = $right = array( );
reset( $array );
$pivot_key = key( $array );
$pivot = array_shift( $array );
foreach( $array as $k => $v ) {
if( $v < $pivot )
$left[$k] = $v;
else
$right[$k] = $v;
}
return array_merge(quicksort($left), array($pivot_key => $pivot), quicksort($right));
}
Usage :
$arr = array(80, 90, 100, 10, 50, 3);
$arr = quicksort($arr);
print_r($arr);
use this
$srtArray=array(80, 90, 100, 10, 50, 3);
for ($i=0; $i<count($srtArray); $i++) {
for ($j=0; $j<count($srtArray); $j++) {
// Compare two elements of array
if ($srtArray[$j] > $srtArray[$i]){
$tmp = $srtArray[$i];
$srtArray[$i] = $srtArray[$j];
$srtArray[$j] = $tmp;
}
}
}
//Print an array after sorting
for($i=0;$i<count($srtArray);$i++){
echo $srtArray[$i]."<br>\n";
}
You should use QuickSort because is practically the fastest way to sort an array of data. PHP's array sorting function sort() uses QuickSort. QuickSort is O(n log n).
<?php
$arr = array(80, 90, 100, 10, 50, 3);
function quicksort( $array ) {
if( count( $array ) < 2 ) {
return $array;
}
$left = $right = array( );
reset( $array );
$pivot_key = key( $array );
$pivot = array_shift( $array );
foreach( $array as $k => $v ) {
if( $v < $pivot )
$left[$k] = $v;
else
$right[$k] = $v;
}
return array_merge(quicksort($left), array($pivot_key => $pivot), quicksort($right));
}
$array = quicksort( $arr );
print_r($array);
Output:
Array
(
[0] => 3
[1] => 10
[2] => 50
[3] => 80
[4] => 90
[5] => 100
)
But if you can't use any built in function you can use this implementation of BubbleSort:
$arr = array(80, 90, 100, 10, 50, 3);
function bubbleSort ($items) {
$size = count($items);
for ($i=0; $i<$size; $i++) {
for ($j=0; $j<$size-1-$i; $j++) {
if ($items[$j+1] < $items[$j]) {
arraySwap($items, $j, $j+1);
}
}
}
return $items;
}
function arraySwap (&$arr, $index1, $index2) {
list($arr[$index1], $arr[$index2]) = array($arr[$index2], $arr[$index1]);
}
$array = bubbleSort( $arr );
print_r($array);
Reference:
http://pageconfig.com/post/implementing-quicksort-in-php
http://en.wikibooks.org/wiki/Algorithm_Implementation/Sorting/Quicksort#PHP
http://www.codecodex.com/wiki/Bubble_sort#PHP
Hope this will be the more optimized. Try this:
$arr = array(80, 90, 100, 10, 50, 3);
$count = count($arr);
for ($i = 0; $i < $count - 1; $i++ ) {
for ($j = $i+1; $j < $count; $j++ ) {
if ($arr[$i] > $arr[$j]) {
$temp = $arr[$i];
$arr[$i] = $arr[$j];
$arr[$j] = $temp;
}
}
}
print_r($arr);
$arr = array(80, 90, 100, 10, 50, 3,34,1,890);
for($i = 0; $i < count($arr); $i++) {
for($x = 0; $x < count($arr)-1; $x ++){
if($arr[$x] > $arr[$x+1]) {
$temp = $arr[$x+1];
$arr[$x+1]=$arr[$x];
$arr[$x]=$temp;
}
}
}
print_r($arr);

Is there an elegant way to count values of an array over a given interval/step to generate histogram data?

I want to get generate histogram data for the following array. array_count_values() would be great to use, except it just counts values for an exact value match. How can I elegantly do the same, but bundle values by a given step or interval?
$dataArray = array(385,515,975,1136,2394,2436,4051,4399,4484,4768,4768,4849,4856,4954,5020,5020,5020,5020,5020,5020,5020,5020,5020,5052,5163,5200,5271,5421,5421,5442,5746,5765,5903,5992,5992,6046,6122,6205,6208,6239,6310,6360,6416,6512,6536,6543,6581,6609,6696,6699,6752,6796,6806,6855,6859,6886,6906,6911,6923,6953,7016,7072,7086,7089,7110,7232,7278,7293,7304,7309,7348,7367,7378,7380,7419,7453,7454,7492,7506,7549,7563,7721,7723,7731,7745,7750,7751,7783,7791,7813,7813,7814,7818,7833,7863,7875,7886,7887,7902,7907,7935,7942,7942,7948,7973,7995,8002,8013,8013,8015,8024,8025,8030,8038,8041,8050,8056,8060,8064,8071,8081,8082,8085,8093,8124,8139,8142,8167,8179,8204,8214,8223,8225,8247,8248,8253,8258,8264,8265,8265,8269,8277,8278,8289,8300,8312,8314,8323,8328,8334,8363,8369,8390,8397,8399,8399,8401,8436,8442,8456,8457,8471,8474,8483,8503,8511,8516,8533,8560,8571,8575,8583,8592,8593,8626,8635,8635,8644,8659,8685,8695,8695,8702,8714,8715,8717,8729,8732,8740,8743,8750,8756,8772,8772,8778,8797,8828,8840,8840,8843,8856,8865,8874,8876,8878,8885,8887,8893,8896,8905,8910,8955,8970,8971,8991,8995,9014,9016,9042,9043,9063,9069,9104,9106,9107,9116,9131,9157,9227,9359,9471);
// if array_count_values accepted a step value I could do this:
print_r(array_count_values($dataArray,1000));
// expected result:
// array(1000 => 3, 2000 => 1, ... 10000 => 15);
// ^0-1000 ^[385,515,975]
What do you recommend?
In the event I have to loop through all values manually, is there an elegant way to round all values to a given interval?
$step = 1000;
$result = array_count_values(
array_map(
function ($value) use ($step) {
return (int) ceil($value / $step) * $step;
},
$dataArray
)
);
var_dump($result);
The rounding solution seems pretty straight forward:
$step_size = 10;
$data = array(10, 20, 24, 30, 35, 50);
foreach ($data as $index => $value) {
$data[$index] = round($value / $step_size) * $step_size;
}
// array(10, 20, 20, 30, 40, 50);
You can build the output directly to avoid mapping the entire data array just to make use of array_count_values(); below is a more generic implementation that allows the mapping to be done outside of the function itself:
function array_count_values_callback(array $data, callable $fn)
{
$freq = [];
foreach ($data as $item) {
$key = $fn($item);
$freq[$key] = isset($freq[$key]) ? $freq[$key] + 1 : 1;
}
return $freq;
}
print_r(array_count_values_callback($dataArray, function($item) {
return ceil($item / 1000) * 1000;
}));
Here is a simple solution where you loop through your $dataArray,
$step_size = 1000;
$histogramArray = array();
foreach ($dataArray as $v) {
$k = (int)ceil($v / $step_size) * $step_size;
if (!array_key_exists($k, $histogramArray)) $histogramArray[$k] = 0;
$histogramArray[$k]++;
}
And the output would be,
Array
(
[1000] => 3
[2000] => 1
[3000] => 2
[5000] => 8
[6000] => 21
[7000] => 25
[8000] => 46
[9000] => 110
[10000] => 15
)

How to convert array key values into percentages

So i've got an array that looks something like of:
Array ( [Safari] => 13 [Firefox] => 5 )
How do i a make a new array that looks like :
Array ( [Safari] => 72.2% [Firefox] => 27.7% )
using a neat php function?
thanks in advance.
You can use array_sum() to get the total, then iterate over the values returning the new value.
$total = array_sum($share);
foreach($share as &$hits) {
$hits = round($hits / $total * 100, 1) . '%';
}
CodePad.
If you have >= PHP 5.3 it can be a tad more elegant with an anonymous function.
$total = array_sum($share);
$share = array_map(function($hits) use ($total) {
return round($hits / $total * 100, 1) . '%';
}, $share);
CodePad.
$array=array ( 'Safari' => 13 ,'Firefox' => 5 );
$tot=array_sum($array);
foreach($array as $key=>$value){
$result[$key]=number_format(($value/$total)*100,1).'%';
}
print_r($result); //Array ( [Safari] => 72.2% [Firefox] => 27.7% )
Try this:
$array = Array ( 'Safari' => 13, 'Firefox' => 5 );
$total = array_sum($array); # total sum of all elements
$onePercent = $total / 100; # we want to know what value represents 1 percent
array_walk($array, create_function('&$v', '$v /= '.$onePercent.'; $v .= " %";')); # we walk through the array changing numbers to percents
var_export($array);
If you want to have your result in second array leaving $array not touched, you can use array_map instead of array_walk
You also might want to use sprintf to set precision of float values that represent percent, because my code would output:
array (
'Safari' => '72.2222222222 %',
'Firefox' => '27.7777777778 %',
)
It's not hard to figure out how to do it?
For PHP 5.3, you can do it like this:
$browsers = array('Safari' => 13, 'Firefox' => 5);
$browsers_proportioned = proportionalize($browsers);
function proportionalize ($arr) {
$total = array_sum($arr);
$names = array_map(
function($number) use ($total) { return number_format($number / $total * 100, 1) .'%'; },
$arr
);
return $names;
}
I personnally like when numbers add up to exactly 100 (not almost 100 like 99.99). So with the solution below, the last item uses the rest instead of being calculated like other items:
public static function getPercentagesByKeyValue(array $arr)
{
$ret = [];
$nbItems = count($arr);
$i = 1;
$sum = array_sum($arr);
foreach ($arr as $key => $number) {
$percent = round($number / $sum * 100, 2);
if ($i === $nbItems) {
$percent = 100 - (array_sum($ret));
}
$ret[$key] = $percent;
$i++;
}
return $ret;
}

Sum all values in a 2d array

How can I sum all of the values in an array of arrays?
Sample array:
[
[0],
[0],
[1, 6, 0]
]
Desired output: 7
echo array_sum(
call_user_func_array('array_merge', $array)
);
//or
echo array_sum(
array_map('array_sum', $array)
);
Use recursion. This will sum an array to any depth (ignoring php imposed call stack depth limit):
function sum_all($arr)
{
$sum = 0;
foreach ($arr as $val)
{
if (is_array($val))
$sum += sum_all($val);
else
$sum += $val;
}
return $sum;
}
I guess those three arrays are actually all part of a top-level array, like this:
$array = array( array( 0 ), array( 1, 2, 3, 4 ), array( 5, 6 ) );
$sum = 0;
for ( $i = 0; $i < count( $array ); $i++ )
{
$sum += array_sum( $array[$i] );
}

Categories