Solved

Convert a 1D array of timestamps into a 2D array (time-group-mid-point, frequency)

Posted on 2016-08-08
10
88 Views
Last Modified: 2016-08-11
Say I have an array of unix timestamps, and I want to draw a graph showing time across the X axis, and frequency on the Y axis, I need to convert that 1D array into a 2D array (time-group-mid-point, frequency)

[for ease of reading, I've used standard time, but in reality, this will be unix timestamps)

so for example:
11:25:00
12:20:00
12:29:00
12:45:00
13:00:00
13:15:00

and if I want the groups to be 60 minutes, but offset by 30 minutes (halfthe interval, each way)
would become:
10:30 to 11:30 (11:00) = 1
11:30 to 12:30  (12:00) = 2
12:30 to 13:30 (13:00) = 3
13:30 to 14:30 (14:00) = 0

Can anyone suggest a function to do this, or even the correct mathematical terminology so I can research it further.

Thanks.
0
Comment
Question by:stuartlandreth
  • 6
  • 4
10 Comments
 
LVL 109

Expert Comment

by:Ray Paseur
ID: 41747585
Here's some background information on how to handle Date/Time values in PHP.

Procedural:
https://www.experts-exchange.com/articles/201/Handling-Date-and-Time-in-PHP-and-MySQL-Procedural-Version.html

Object-oriented:
https://www.experts-exchange.com/articles/20920/Handling-Time-and-Date-in-PHP-and-MySQL-OOP-Version.html

Have a look at that, and I'll try to put together an example for you in a moment.
0
 
LVL 109

Accepted Solution

by:
Ray Paseur earned 500 total points
ID: 41747628
This should help you get started.  The idea here is to create an array of objects.  Each object is aware of one time interval.  When we inject the test data times into the objects, the objects that have matching (bounding) time intervals will keep the injected time.  Then we can get a count of the injected times to see how many occurred in each time interval.
https://iconoun.com/demo/temp_stuart.php
<?php // demo/temp_stuart.php
/**
 * https://www.experts-exchange.com/questions/28962158/Convert-a-1D-array-of-timestamps-into-a-2D-array-time-group-mid-point-frequency.html
 */
error_reporting(E_ALL);
echo '<pre>';

/* OBJECTIVE FROM THE POST AT E-E
10:30 to 11:30 (11:00) = 1
11:30 to 12:30 (12:00) = 2
12:30 to 13:30 (13:00) = 3
13:30 to 14:30 (14:00) = 0
*/

// TEST DATA FROM THE POST AT E-E
$times = array
( '11:25:00'
, '12:20:00'
, '12:29:00'
, '12:45:00'
, '13:00:00'
, '13:15:00'
)
;

// A THING TO COUNT INSTANCES OF MATCHING TIMES
Class TimeThing
{
    public function __construct($name, $alpha, $omega)
    {
        $this->times = [];
        $this->name  = $name;
        $this->alpha = strtotime($alpha);
        $this->omega = strtotime($omega);
    }
    public function inject($time)
    {
        $time = strtotime($time);
        if ($time >= $this->alpha)
        {
            if ($time <= $this->omega)
            {
                $this->times[] = $time;
            }
        }
    }
    public function getCount()
    {
        return count($this->times);
    }
}

// AN ARRAY OF COUNTERS
$timethings = array
( new TimeThing('10:30 to 11:30 (11:00)', 'TODAY 10:30:00', 'TODAY 11:29:59')
, new TimeThing('11:30 to 12:30 (12:00)', 'TODAY 11:30:00', 'TODAY 12:29:59')
, new TimeThing('12:30 to 13:30 (13:00)', 'TODAY 12:30:00', 'TODAY 13:29:59')
, new TimeThing('13:30 to 14:30 (14:00)', 'TODAY 13:30:00', 'TODAY 14:29:59')
)
;

// DISTRIBUTE EACH OF THE TEST DATA TIMES INTO THE ARRAY OF COUNTERS
foreach ($times as $t)
{
    $t = 'TODAY ' . $t;
    foreach ($timethings as $e)
    {
        $e->inject($t);
    }
}

// SHOW THE WORK PRODUCT
foreach ($timethings as $e)
{
    echo PHP_EOL . "$e->name => ";
    echo $e->getCount();
}

Open in new window

Outputs:
10:30 to 11:30 (11:00) => 1
11:30 to 12:30 (12:00) => 2
12:30 to 13:30 (13:00) => 3
13:30 to 14:30 (14:00) => 0

Open in new window

0
 
LVL 109

Expert Comment

by:Ray Paseur
ID: 41747630
Afterthought... Time intervals like 10:30 to 11:30, followed by 11:30 to 12:30 contain overlapping values.  That's why I made the intervals look like 10:30:00 to 11:29:59 (instead of 11:30:00).
0
Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

 

Author Comment

by:stuartlandreth
ID: 41747651
OK.

Thanks for that. I'll need to consider lines 54-59 as those boundaries need to be dynamically generated based on other factors. i.e. different ranges (1 hour blocks, 6 hours, 12 hours, 48 hours, etc. then 7 days, 14 days, 28 days, etc.)

And then see what happens when we get to a few thousand data points over 100 or so ranges.
0
 
LVL 109

Expert Comment

by:Ray Paseur
ID: 41747668
boundaries need to be dynamically generated...
I think this design will still work based on other time boundaries and ranges.  The reason I used "TODAY" with the times is to accommodate the PHP way of handling date/time values.  If you have explicit dates and times, you probably want to express them in the ISO-8601 date/time standard notation.  So an object definition to capture all the entries for today might look like this:
new TimeThing('Fred', '2016-08-08 00:00:00', '2016-08-08 23:59:59')

Open in new window

Thousands of data points and/or thousands of ranges should be no problem.  Millions of ranges might add up to a memory hog, but there would be essentially no practical limit on the number of data points.
0
 

Author Comment

by:stuartlandreth
ID: 41747700
Yes it does. I'm working in UNIX timestamps anyway, so managed to abstract a bit to this:

<?php // demo/temp_stuart.php
/**
 * https://www.experts-exchange.com/questions/28962158/Convert-a-1D-array-of-timestamps-into-a-2D-array-time-group-mid-point-frequency.html
 */
error_reporting(E_ALL);
echo '<pre>';


// BOUNDARIES AS TIMESTAMPS
$lb = strtotime("TODAY 00:00:00");
$ub = strtotime("TODAY 23:59:59");

// 10,000 RANDOM TIMES BETWEEN BOUNDARIES
$times = array();
for ($count=1; $count<=10000; $count++)
{
    $times[]=rand($lb, $ub);
}

// A THING TO COUNT INSTANCES OF MATCHING TIMES
Class TimeThing
{
    public function __construct($name, $alpha, $omega)
    {
        $this->times = [];
        $this->name  = $name;
        $this->alpha = $alpha;
        $this->omega = $omega;
    }
    public function inject($time)
    {
        //$time = strtotime($time);
        if ($time >= $this->alpha)
        {
            if ($time <= $this->omega)
            {
                $this->times[] = $time;
            }
        }
    }
    public function getCount()
    {
        return count($this->times);
    }
}

// GENERATE TIME RANGES
$timethings = array();
$interval = 60*60; // 1 hour;
for ($timestamp = $lb; $timestamp<=$ub; $timestamp=$timestamp+$interval)
{

    $label = date('d/m/Y H:i:s', $timestamp);
    $lower = $timestamp-($interval/2);
    $upper= $timestamp+($interval/2);
    $timethings[]=new TimeThing($label, $lower, $upper);
}


// DISTRIBUTE EACH OF THE TEST DATA TIMES INTO THE ARRAY OF COUNTERS
foreach ($times as $t)
{
    foreach ($timethings as $e)
    {
        $e->inject($t);
    }
}

// SHOW THE WORK PRODUCT
foreach ($timethings as $e)
{
    echo PHP_EOL . "$e->name => ";
    echo $e->getCount();
}

Open in new window

0
 

Author Comment

by:stuartlandreth
ID: 41747708
    $upper= $timestamp+($interval/2)-1; 

Open in new window

on line 55 to prevent overlap.
0
 
LVL 109

Expert Comment

by:Ray Paseur
ID: 41748571
A (small) note that will matter if you find your code crossing timezones or daylight savings time.  The commonly understood definition of an hour == 60 minutes == 60*60 seconds is not always valid.  It's better to use the PHP functions to manipulate the date/time values.  These will always come out with the right answers because oddities like leap year and daylight savings time are accounted for in the language calculations.  One particularly useful thing is mktime() which will allow correct date calculations with out-of-range values.
0
 
LVL 109

Expert Comment

by:Ray Paseur
ID: 41752703
Just for proof-of-concept I did the same exercise in object-oriented notation using DateTime objects and type-hinting.  As I look at the two solutions side-by-side I don't think the OOP version adds much value to the process.
<?php // demo/temp_stuart.php
/**
 * https://www.experts-exchange.com/questions/28962158/Convert-a-1D-array-of-timestamps-into-a-2D-array-time-group-mid-point-frequency.html
 *
 * Modified for PHP Object-Oriented DateTime
 * https://www.experts-exchange.com/articles/20920/Handling-Time-and-Date-in-PHP-and-MySQL-OOP-Version.html
 */
error_reporting(E_ALL);
echo '<pre>';

/* OBJECTIVE FROM THE POST AT E-E
10:30 to 11:30 (11:00) = 1
11:30 to 12:30 (12:00) = 2
12:30 to 13:30 (13:00) = 3
13:30 to 14:30 (14:00) = 0
*/

// TEST DATA, MODIFIED FROM THE POST AT E-E TO USE DATETIME OBJECTS
$times = array
( new DateTime('TODAY 11:25:00')
, new DateTime('TODAY 12:20:00')
, new DateTime('TODAY 12:29:00')
, new DateTime('TODAY 12:45:00')
, new DateTime('TODAY 13:00:00')
, new DateTime('TODAY 13:15:00')
)
;

// A THING TO COUNT INSTANCES OF MATCHING TIMES USES TYPE-HINTS FOR ITS ARGUMENTS
Class TimeThing
{
    public function __construct($name, DateTime $alpha, DateTime $omega)
    {
        $this->times = [];
        $this->name  = $name;
        $this->alpha = $alpha;
        $this->omega = $omega;
    }
    public function inject(DateTime $time)
    {
        if ($time >= $this->alpha)
        {
            if ($time <= $this->omega)
            {
                $this->times[] = $time;
            }
        }
    }
    public function getTimes()
    {
        return $this->times;
    }
    public function getCount()
    {
        return count($this->times);
    }
}

// AN ARRAY OF COUNTERS
$timethings = array
//...............Name........Alpha...........................Omega.....................
( new TimeThing('11:00', new DateTime('TODAY 10:30:00'), new DateTime('TODAY 11:29:59') )
, new TimeThing('12:00', new DateTime('TODAY 11:30:00'), new DateTime('TODAY 12:29:59') )
, new TimeThing('13:00', new DateTime('TODAY 12:30:00'), new DateTime('TODAY 13:29:59') )
, new TimeThing('14:00', new DateTime('TODAY 13:30:00'), new DateTime('TODAY 14:29:59') )
)
;

// DISTRIBUTE EACH OF THE TEST DATA TIMES INTO THE ARRAY OF COUNTER OBJECTS
foreach ($times as $dto)
{
    foreach ($timethings as $tt)
    {
        $tt->inject($dto);
    }
}

// SHOW THE WORK PRODUCT
foreach ($timethings as $tt)
{
    echo PHP_EOL . "$tt->name = ";
    echo $tt->getCount();
//    echo PHP_EOL;
//    print_r($tt->getTimes());
//    echo PHP_EOL;
}

Open in new window

0
 

Author Closing Comment

by:stuartlandreth
ID: 41753157
Code solved the problem.
0

Featured Post

Free Tool: Postgres Monitoring System

A PHP and Perl based system to collect and display usage statistics from PostgreSQL databases.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Lithium-ion batteries area cornerstone of today's portable electronic devices, and even though they are relied upon heavily, their chemistry and origin are not of common knowledge. This article is about a device on which every smartphone, laptop, an…
Password hashing is better than message digests or encryption, and you should be using it instead of message digests or encryption.  Find out why and how in this article, which supplements the original article on PHP Client Registration, Login, Logo…
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.

839 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question