I have an array that contains half-hourly time periods; this array is then sub-divided into dates going back 6 weeks of 'today', each date having the amount of transactions completed on that date, in that half hour period. e.g.
[07:30:00] => Array
(
[2015-05-18] => 10
[2015-05-25] => 17
[2015-06-01] => 11
[2015-06-08] => 20
[2015-06-15] => 16
[average] => 15
)
[08:00:00] => Array
(
[2015-05-18] => 12
[2015-05-25] => 10
[2015-06-01] => 14
[2015-06-08] => 19
[2015-06-15] => 18
[average] => 15
)
This goes on for the entire business day.
The average I'm currently calculating above is a simple mean of all those values, calculated thus:
foreach($average as $half_hour => $data) {
$average[$half_hour]['average'] = round(array_sum($data)/count($data), 0, PHP_ROUND_HALF_UP);
}
What I'd like to achieve is applying more weight to the more recent dates, and lesser weight to the older values. I started off defining an array of dates I'd encounter, and setting a weight for that particular date:
$date_weights = array(
date("Y-m-d", strtotime("6 weeks ago $day")) => 0.05,
date("Y-m-d", strtotime("5 weeks ago $day")) => 0.05,
date("Y-m-d", strtotime("4 weeks ago $day")) => 0.15,
date("Y-m-d", strtotime("3 weeks ago $day")) => 0.20,
date("Y-m-d", strtotime("2 weeks ago $day")) => 0.25,
date("Y-m-d", strtotime("1 weeks ago $day")) => 0.30
);
The weights all add up to 1, which from what I understand of weighted mean calculations, keeps things simple(r). Except, this is where my poor-math brain threw an exception and refused to play any more.
I tried the following code, but that appears not to do what I'm looking for:
foreach($average as $half_hour => $data) {
// apply the desired weighting to the values
foreach($data as $date => $transactions) {
$average[$half_hour][$date] = ($date_weights[$date]*$transactions);
}
}
foreach($average as $half_hour => $data) {
$average[$half_hour]['average'] = round(array_sum($data)/count($data), 0, PHP_ROUND_HALF_UP);
}
I think I'm on the right track here ? But I'm obviously missing a step. Can any kind slightly more math-savvy person point out where I'm going wrong and how best to achieve where I'm trying to get with this? Thank you !!
Simply remove the division by count($data) so that you get
$average[$half_hour]['average'] = round(array_sum($data), 0, PHP_ROUND_HALF_UP);
Or pick weights that have an average of (instead of a sum) of one, and keep the division.
Also, if you have 6 weight you need to have 6 values - otherwise the weights don't add up to one. Now you seem to have 6 weights (in $date_weights but only 5 values (in $average[$half_hour], not counting average).
EDIT: If you don't want to worry about the weights adding up to one, you can use this:
$average[$half_hour]['average'] = round(array_sum($data)/array_sum($date_weights), 0, PHP_ROUND_HALF_UP);
You still have to make sure all of the weights are used, though.
Related
I’m re-factoring an application, and one of the database tables stores days of the week in columns. Each day can either be “on” or “off”.
The notion of on and off makes me think this data could be more succinctly represented as bits. But I’ve not worked with raw bits as a data type before.
The application is written in PHP. I’ve seen bits used to store header values here. There are then “helper” constants (HEADER_X_FORWARDED_ALL and HEADER_X_FORWARDED_ALL that act as “pre-configurations” for all headers being used, or a sub-set being used for AWS ELB-like environments (where some headers are not sent, so are “off”).
So I have a couple of questions using this data type for representing days of the weeks:
How would I store the final bit value in MySQL? As in the column data type, length etc.
How would I check a day is “on”? If I have the value 0b0100000, how do I determine using bitwise operators that Tuesday is “on”?
How do I “set” days to persist the value in the database? So if I have checkboxes in the UI that submits the values as an array, how would I convert something like:
Array
(
[days] => Array
(
[Monday] => 0
[Tuesday] => 1
[Wednesday] => 0
[Thursday] => 0
[Friday] => 0
[Saturday] => 0
[Sunday] => 0
)
)
…to a bit value like 0b0100000, again using bitwise operators?
If someone could point me in the right direction of getting started with working with bit values in PHP, I’d be most grateful!
You could store the decimal values of the selected days. So as a really basic example:
/**
* Keys are ISO-8601 numeric representation
* of the days of the week
*/
$days = [
1 => 0b0000001, // Monday => 1
2 => 0b0000010, // Tuesday => 2
3 => 0b0000100, // Wednesday => 4
4 => 0b0001000, // Thursday => 8
5 => 0b0010000, // Friday => 16
6 => 0b0100000, // Saturday => 32
7 => 0b1000000, // Sunday => 64
];
You'd just loop and check if the day's bit is set in the value (bitwise and):
// The value you'd store in your database
$mondayToFriday = 31; // 1 + 2 + 4 + 8 + 16
foreach ($days as $n => $v) {
/**
* Get the day's name using strftime (has locale capability)
* strtotime("Sunday +n days") would work with ISO-8601 day
* numbers or PHP's date("w") (0 - 6 / Sunday - Saturday)
*/
$day = strftime("%A", strtotime("Sunday +{$n} days"));
echo "{$day}: " . (($v & $mondayToFriday) ? "Y" : "N") . PHP_EOL;
}
As for your checkboxes, you could just build the binary string from the state of each day (0 or 1), and convert it to decimal using bindec() (the order in which they appear in the array is important, you'd need to ensure that they're correctly mapped to their corresponding day's value).
Given your example input, and the days in this example, the following would work:
$dec = bindec(strrev(implode("", $input["days"])));
Hopefully that'll push you in the right direction.
Here's an example to play with
How can I get the time period (day, week, month) by a given timestamp? I do not want the date. I want the next time period based on the amount of seconds.
Is there a PHP native function for that?
Example:
$period = getTimeperiod( 15638400 );
My attempt: I could check and count the seconds:
if x <= 60 => min
if x <= 60*60 => hour
if x <= 60*60*24 => day
...
Edit:
Time period means either minute, hour, day, week, ... as stated above... ?! So all I want is the corresponding time period for a timestamp.
Example: (day = 86400 secs) then a timestamp with getTimeperiod( 85000 ) should be "day".
I think you're searching for something like the class DateInterval ...
It is part of PHP since 5.3.0 and has a static function called createFromDateString() where you can set up a DateInterval from a string like "3600 seconds". From this object you then can get the day, month, year and so on.
Take a look at this page:
http://www.php.net/manual/de/dateinterval.createfromdatestring.php
But is this on the right path returning an interval object (period)? Cred to #SimonSimCity for pointing out DateInterval. If you guide me I could improve the answer.
<?php
$timestamp = 15638400;
echo "The timestamp $timestamp is " . date("Y-m-d H:i:s", 15638400) . "<br \>";
echo "<pre>";
print_r (DateInterval::createFromDateString(date("Y \\y\\e\\a\\r\\s m \\m\\o\\n\\t\\h\\s d \\d\\a\\y\\s\\ H \\h\\o\\u\\r\\s i \\m\\i\\n\\u\\t\\e\\s s \\s\\e\\c\\o\\n\\d\\s", 15638400 ) ) );
echo "</pre>"
?>
Outputting
The timestamp 15638400 is 1970-07-01 00:00:00
DateInterval Object
(
[y] => 1970
[m] => 7
[d] => 1
[h] => 0
[i] => 0
[s] => 0
[invert] => 0
[days] => 0
)
I solved it that way:
/*
seconds 0
minutes 1
hours 2
days 3
week 4
month 5
year 6
decade 7
century 8
millenium 9
*/
$arTimes = array(
0 => 1,
1 => 60,
2 => 60*60,
3 => 60*60*24,
4 => 60*60*24*7,
5 => 60*60*24*7*4,
6 => 60*60*24*7*4*12,
7 => 60*60*24*7*4*12*10,
8 => 60*60*24*7*4*12*10*10,
9 => 60*60*24*7*4*12*10*10*10
);
$nDiff = strtotime( $nTo ) - strtotime( $nFrom );
switch( $nDiff )
{
// check difference and time period
case $nDiff <= $arTimes[ 1 ]:
$nGranularity = 0;
break;
...
}
I have a logical structure or idea of something i want to happen and i wanted it to be coded in PHP using array, here's the scenario:
10 Loops/occurrence of month
month => repetition
jan => 2
mar => 1
sep => 5
dec => 2
As shown above i have a loop that occur 10 times, within that count of loops 4 different month have been recorded, and every specific month there is a chance that is has been repeated multiple times like the month (sep) has been repeated 5times. This idea is same as example a record of a person of what are the months he/she traveled and how many times he traveled within that month from 10 recorded travels. Any idea how to build this in PHP using Arrays? Any suggestions or answers are greatly appreciated!
Ok here's a real deal what i wanted to happen, i pull the records of months from a database, then i wanted to display the months pulled from database and the how many times it has been repeated, example the display are like these:
Jan (2)
Sep (5)
I wanted it to be dynamic, no certain month will be displayed if it doesn't exist from the database record.
I think he's trying to perform rudimentary frequency analysis. Assuming you have dates fed into an array somehow, you can proceed by using strtotime(), date() functions to generate keys, and then parse them.
First, create some dummy data:
// dummy data; unsure of the format, so going with time()-like structure
$dates = array();
for( $i = 0; $i < 10; $i++ ) {
$dates[] = strtotime(sprintf("%s-%s-2012", rand(1,30), rand(1,12) ));
}
Now perform a frequency count, keyed by month written by date("M" ...):
$freq = array();
foreach( $dates as $timestamp ) {
// assuming it's all the same year, discover the month
$month = date("M", $timestamp );
if( !isset($freq[$month]) ) $freq[$month] = 0;
$freq[$month]++;
}
At this point, you'll have something resembling what you want:
print_r( $freq );
Array
(
[Sep] => 2
[Feb] => 2
[Nov] => 1
[Dec] => 1
[Jan] => 2
[Apr] => 1
[May] => 1
)
If the year varies, you may want to double-joint this somehow, or otherwise.
I am currently looking at creating a script for my site that will count down to sunday of that week, every week.
Example:
The user visits the site on a saturday at 11:30am, they will be greeted with:
"The next time this page will be updated is in 0 days, 12 hours and 30 minutes."
Any ideas?
You can use this little trick to get a timestamp for midnight next Sunday:
$sunday = strtotime('next Sunday');
See this answer for how to format it into something useful. Right now I get this:
print_r(dateDifference($sunday, time()));
Array
(
[years] => 0
[months_total] => 0
[months] => 0
[days_total] => 0
[days] => 0
[hours_total] => 4
[hours] => 4
[minutes_total] => 256
[minutes] => 16
[seconds_total] => 15387
[seconds] => 27
)
I am using similar to this solution in one of my pojects. You can use it like this:
ago(strtotime("next sunday")) but you need to change $difference = $now - $time; to $difference = $time - $now;
Here's one solution:
http://jsfiddle.net/foxbunny/xBE7L/
It also automatically updates every second.
Edit: I've included the offset parameter, and you use it to supply the difference between user's and server's time-zone if necessary.
Assume that selected date from Canlender is 02/09/2011. To store weekly date into array from 20/09/2011 is
for($i=0; $i<7; $i++)
{
$WeeklyDate[] = date("Y-m-d", strtotime(2011-09-02) - 86400*$i);
}
My question is how to store monthly date into array from the selected date.
Many thanks
---Update----------
The final result of monthlyDate should look like the following:
$monthlyDate= array{2011-08-03, 2011-08-04, 2011-08-05, 2011-08-06, 2011-08-07 ....2011-08-31, 2011-09-01, 2011-09-02}
First, calculate the number of days in a month using cal_days_in_month and then proceed as you are doing with weeks eg:
$days = cal_days_in_month(CAL_GREGORIAN, 9, 2011);
for($i = 0; $i <= $days; $i++)
{
$MonthlyDate[] = date("Y-m-d", strtotime(2011-09-02) - 86400*$i);
}
Notice that CAL_GREGORIAN is a built-in constant.
Working Example
Whenever programs are incrementing a date using 86400 there is a risk of unexpected output because of DST.
By using strtotime() with a unit larger than hours (like days, weeks, months, etc.) preventing any DST hiccups. Note: a DateTime object approach can be used but for this case, it is unnecessary overhead.
The following is an adjusted form of a one-liner date range function I developed.
Here is the online demo for this case.
function getDatesFromRange($a,$b,$x=0,$dates=[]){
while(end($dates)!=$b && $x=array_push($dates,date("Y-m-d",strtotime("$a +$x day"))));
return $dates;
}
$date='2011-09-02';
$monthlyDate=getDatesFromRange(date("Y-m-d",strtotime("$date -1 month +1 day")),$date);
var_export($monthlyDate);
output as desired/expected:
array (
0 => '2011-08-03',
1 => '2011-08-04',
2 => '2011-08-05',
3 => '2011-08-06',
4 => '2011-08-07',
5 => '2011-08-08',
6 => '2011-08-09',
7 => '2011-08-10',
8 => '2011-08-11',
9 => '2011-08-12',
10 => '2011-08-13',
11 => '2011-08-14',
12 => '2011-08-15',
13 => '2011-08-16',
14 => '2011-08-17',
15 => '2011-08-18',
16 => '2011-08-19',
17 => '2011-08-20',
18 => '2011-08-21',
19 => '2011-08-22',
20 => '2011-08-23',
21 => '2011-08-24',
22 => '2011-08-25',
23 => '2011-08-26',
24 => '2011-08-27',
25 => '2011-08-28',
26 => '2011-08-29',
27 => '2011-08-30',
28 => '2011-08-31',
29 => '2011-09-01',
30 => '2011-09-02',
)