Laravel 按几天内的奇数小时数对数据进行分组
Laravel group data by odd amount of hours throughout days
我正在尝试按照与规范略有不同的日期格式对我的 Laravel 项目中的一些数据进行分组。我有一个数据库和一个查询,可以根据用户想要查看的时间段获取用户网站的“正常运行时间检查”,然后我需要将其作为某种时间线显示给用户。
为了减少数据中的“噪音”(在给定时间段内可能没有足够的正常运行时间检查)我想将我的所有结果分组假设一天中有 3 个小时,那么我将获得以下所有数据:
- 2021-05-0203:00:00
- 2021-05-0206:00:00
- 2021-05-0209:00:00
等等,现在我按小时带回数据,但不确定如何修改它以达到预期的结果
// get the uptime checks for past X hours
$uptimeData = UptimeChecks::where('user_id', 1)
->where('monitor_id', 1)
->where('checked_at', '>=', '2021-05-02 13:00:00')
->where('checked_at', '<=', '2021-05-03 13:00:00')
->orderBy('checked_at', 'asc')
->select('event', 'checked_at')
->get();
$uptimeDataTimeline = $uptimeData->groupBy(function ($item, $key) {
$date = Carbon::parse($item->checked_at);
// group by hour, how can I get say every 3 hours worth of data?
return $date->format('Y-m-d H:00:00');
});
$uptimeDataTimeline = $uptimeDataTimeline->map(function ($checksInPeriod, $key) {
$down = 0;
$up = 0;
$total = 0;
$uptime = 0;
$fill = '#1fc777'; // green
// $checksInPeriod is all of the data for a given hour at the moment
// I need to group by a bigger period, say, every 3 hours
// add our events
foreach ($checksInPeriod as $key => $value) {
$total++;
if (strtolower($value['event']) == 'down') $down++;
if (strtolower($value['event']) == 'up') $up++;
}
// calculate uptime
$uptime = floatval(number_format(round($up / $total, 5) * 100, 2, '.', ','));
// fill colours
if ($uptime < 100) $fill = '#9deab8'; // lighter green
if ($uptime < 99) $fill = '#fbaa49'; // amber
if ($uptime < 98) $fill = '#e0465e'; // red
return [
'total_events' => $total,
'down_events' => $down,
'up_events' => $up,
'uptime' => $uptime,
'fill' => $fill
];
});
不确定如何修改 returns 格式的 groupBy
函数,因为我的理解是不可能这样做?顺便说一下,我正在使用 Carbon。
更新
我一直在挖掘,并且遇到了 CarbonInterval
功能,它允许我生成一些间隔,并且我已经尝试实现它,我似乎得到了一个等间隔的时间段,但是我的数据已用完并且不包含两个间隔之间的所有数据(见附图)
$intervals = CarbonInterval::hours(2)->toPeriod($from, $to);
$uptimeDataTimeline = $uptimeData->groupBy(function ($item, $key) use ($intervals) {
$date = Carbon::parse($item->checked_at);
foreach ($intervals as $key => $interval) {
if ($date->hour == Carbon::parse($interval)->addHours(1)->hour) {
$actualHour1 = Carbon::parse($interval)->hour;
if (strlen($actualHour1) == 1) $actualHour1 = "0$actualHour1";
return $date->format("Y-m-d $actualHour1:00:00");
} else if ($date->hour == Carbon::parse($interval)->addHours(2)->hour) {
$actualHour2 = Carbon::parse($interval)->subHours(2)->hour;
if (strlen($actualHour2) == 1) $actualHour2 = "0$actualHour2";
return $date->format("Y-m-d $actualHour2:00:00");
}
}
return $date->format('Y-m-d H:00:00');
});
例如,我应该在 07
键中看到第 7 小时和第 8 小时的所有检查,但我只看到一个小时(11
小时)的数据?
当您需要时间片时,最好使用 DateInterval 或更好的 CarbonInterval。他们给你的是循环遍历这些切片并对样本数据进行 equality/unequlity 操作的能力,这样你就可以通过这些时间片轻松地将数据组织到它们各自的“槽”
这里是关于如何
的一般思路
$intervals = \Carbon\CarbonInterval::hours(3)->toPeriod('2021-05-02 13:00:00', '2021-05-03 13:00:00');
//we get time slots of 3 hours between provided datetimes
foreach ($intervals as $date) {
$dtArr[] = strtotime($date->format('Y-m-d H:i:s')); //we collect those "time markers"
}
$result = [
'first'=> 0,
'second'=>0.
'third'=>0,
'forth'=>0,
'fifth'=>0,
'sixth'=>0,
'seventh'=>0,
'eighth'=>0
]; //array to accumulate your aggregations to correct time slot
foreach ($uptimeData as $sample) {
//loop over sample set
$ordinality = getSlotNo($sample->checked_at); //eg. third
//read the accumulated total in $result and add this too
$result[$ordinality] += 1;
}
function getSlotNo($dt){
$ts = strtotime($dt);
//eg. say greater than or equal to "13:00" but smaller than "16:00" -> You go in first slot
if($ts>=$dtArr[0] && $ts<$dtArr[1]){
//first slot
return 'first';
}
elseif($ts>=$dtArr[1] && $ts<$dtArr[2]){
//eg. say greater than or equal to "16:00" but smaller than "19:00" -> You go in second slot
//second slot
return 'second';
}
elseif($ts>=$dtArr[2] && $ts<$dtArr[3]){
//third slot
return 'third';
}
// and so on
}
更新
可能会尝试这样的事情,将插槽 getter 修改为“向前看”并决定结果
$i=0;
foreach ($intervals as $date) {
$dtArr[] = strtotime($date->format('Y-m-d H:i:s')); //we collect those "time markers"
$result['int_'.$i] = 0;
$i++;
}
//fake data
$uptimeData=collect([
(object)['checked_at'=>'2021-05-03 10:10:00'],
(object)['checked_at'=>'2021-05-03 11:20:00'],
(object)['checked_at'=>'2021-05-03 12:20:00'],
(object)['checked_at'=>'2021-05-03 13:20:00'],
(object)['checked_at'=>'2021-05-03 14:20:00'],
]);
foreach ($uptimeData as $sample) {
//loop over sample set
$ordinalInfo = getSlotNo($sample->checked_at, $dtArr); //eg. third
//read the accumulated total in $result and add this too
if($ordinalInfo['match']){
$result['int_'.$ordinalInfo['index']] += 1;
}
}
/**
* @param $dt
* @return int index in $dtArr this value belongs to
*/
function getSlotNo($dt, $dtArr){
$ts = strtotime($dt);
$info = [];
for($i =0; $i<count($dtArr); $i++){
if(!empty($dtArr[$i+1])){ // if not reached the last item ie. still there's a next
if($ts>=$dtArr[$i] && $ts<$dtArr[$i+1]){
//i'th slot
$info=['match'=>true,'index'=>$i];
break;
}
}else{
// at last item ie. ( $i == count($dtArr)-1 )
if($ts<=$dtArr[$i])
$info=['match'=>true,'index'=>$i];
else
$info=['match'=>false,'index'=>NULL];
}
}
return $info;
}
我正在尝试按照与规范略有不同的日期格式对我的 Laravel 项目中的一些数据进行分组。我有一个数据库和一个查询,可以根据用户想要查看的时间段获取用户网站的“正常运行时间检查”,然后我需要将其作为某种时间线显示给用户。
为了减少数据中的“噪音”(在给定时间段内可能没有足够的正常运行时间检查)我想将我的所有结果分组假设一天中有 3 个小时,那么我将获得以下所有数据:
- 2021-05-0203:00:00
- 2021-05-0206:00:00
- 2021-05-0209:00:00
等等,现在我按小时带回数据,但不确定如何修改它以达到预期的结果
// get the uptime checks for past X hours
$uptimeData = UptimeChecks::where('user_id', 1)
->where('monitor_id', 1)
->where('checked_at', '>=', '2021-05-02 13:00:00')
->where('checked_at', '<=', '2021-05-03 13:00:00')
->orderBy('checked_at', 'asc')
->select('event', 'checked_at')
->get();
$uptimeDataTimeline = $uptimeData->groupBy(function ($item, $key) {
$date = Carbon::parse($item->checked_at);
// group by hour, how can I get say every 3 hours worth of data?
return $date->format('Y-m-d H:00:00');
});
$uptimeDataTimeline = $uptimeDataTimeline->map(function ($checksInPeriod, $key) {
$down = 0;
$up = 0;
$total = 0;
$uptime = 0;
$fill = '#1fc777'; // green
// $checksInPeriod is all of the data for a given hour at the moment
// I need to group by a bigger period, say, every 3 hours
// add our events
foreach ($checksInPeriod as $key => $value) {
$total++;
if (strtolower($value['event']) == 'down') $down++;
if (strtolower($value['event']) == 'up') $up++;
}
// calculate uptime
$uptime = floatval(number_format(round($up / $total, 5) * 100, 2, '.', ','));
// fill colours
if ($uptime < 100) $fill = '#9deab8'; // lighter green
if ($uptime < 99) $fill = '#fbaa49'; // amber
if ($uptime < 98) $fill = '#e0465e'; // red
return [
'total_events' => $total,
'down_events' => $down,
'up_events' => $up,
'uptime' => $uptime,
'fill' => $fill
];
});
不确定如何修改 returns 格式的 groupBy
函数,因为我的理解是不可能这样做?顺便说一下,我正在使用 Carbon。
更新
我一直在挖掘,并且遇到了 CarbonInterval
功能,它允许我生成一些间隔,并且我已经尝试实现它,我似乎得到了一个等间隔的时间段,但是我的数据已用完并且不包含两个间隔之间的所有数据(见附图)
$intervals = CarbonInterval::hours(2)->toPeriod($from, $to);
$uptimeDataTimeline = $uptimeData->groupBy(function ($item, $key) use ($intervals) {
$date = Carbon::parse($item->checked_at);
foreach ($intervals as $key => $interval) {
if ($date->hour == Carbon::parse($interval)->addHours(1)->hour) {
$actualHour1 = Carbon::parse($interval)->hour;
if (strlen($actualHour1) == 1) $actualHour1 = "0$actualHour1";
return $date->format("Y-m-d $actualHour1:00:00");
} else if ($date->hour == Carbon::parse($interval)->addHours(2)->hour) {
$actualHour2 = Carbon::parse($interval)->subHours(2)->hour;
if (strlen($actualHour2) == 1) $actualHour2 = "0$actualHour2";
return $date->format("Y-m-d $actualHour2:00:00");
}
}
return $date->format('Y-m-d H:00:00');
});
例如,我应该在 07
键中看到第 7 小时和第 8 小时的所有检查,但我只看到一个小时(11
小时)的数据?
当您需要时间片时,最好使用 DateInterval 或更好的 CarbonInterval。他们给你的是循环遍历这些切片并对样本数据进行 equality/unequlity 操作的能力,这样你就可以通过这些时间片轻松地将数据组织到它们各自的“槽”
这里是关于如何
的一般思路$intervals = \Carbon\CarbonInterval::hours(3)->toPeriod('2021-05-02 13:00:00', '2021-05-03 13:00:00');
//we get time slots of 3 hours between provided datetimes
foreach ($intervals as $date) {
$dtArr[] = strtotime($date->format('Y-m-d H:i:s')); //we collect those "time markers"
}
$result = [
'first'=> 0,
'second'=>0.
'third'=>0,
'forth'=>0,
'fifth'=>0,
'sixth'=>0,
'seventh'=>0,
'eighth'=>0
]; //array to accumulate your aggregations to correct time slot
foreach ($uptimeData as $sample) {
//loop over sample set
$ordinality = getSlotNo($sample->checked_at); //eg. third
//read the accumulated total in $result and add this too
$result[$ordinality] += 1;
}
function getSlotNo($dt){
$ts = strtotime($dt);
//eg. say greater than or equal to "13:00" but smaller than "16:00" -> You go in first slot
if($ts>=$dtArr[0] && $ts<$dtArr[1]){
//first slot
return 'first';
}
elseif($ts>=$dtArr[1] && $ts<$dtArr[2]){
//eg. say greater than or equal to "16:00" but smaller than "19:00" -> You go in second slot
//second slot
return 'second';
}
elseif($ts>=$dtArr[2] && $ts<$dtArr[3]){
//third slot
return 'third';
}
// and so on
}
更新
可能会尝试这样的事情,将插槽 getter 修改为“向前看”并决定结果
$i=0;
foreach ($intervals as $date) {
$dtArr[] = strtotime($date->format('Y-m-d H:i:s')); //we collect those "time markers"
$result['int_'.$i] = 0;
$i++;
}
//fake data
$uptimeData=collect([
(object)['checked_at'=>'2021-05-03 10:10:00'],
(object)['checked_at'=>'2021-05-03 11:20:00'],
(object)['checked_at'=>'2021-05-03 12:20:00'],
(object)['checked_at'=>'2021-05-03 13:20:00'],
(object)['checked_at'=>'2021-05-03 14:20:00'],
]);
foreach ($uptimeData as $sample) {
//loop over sample set
$ordinalInfo = getSlotNo($sample->checked_at, $dtArr); //eg. third
//read the accumulated total in $result and add this too
if($ordinalInfo['match']){
$result['int_'.$ordinalInfo['index']] += 1;
}
}
/**
* @param $dt
* @return int index in $dtArr this value belongs to
*/
function getSlotNo($dt, $dtArr){
$ts = strtotime($dt);
$info = [];
for($i =0; $i<count($dtArr); $i++){
if(!empty($dtArr[$i+1])){ // if not reached the last item ie. still there's a next
if($ts>=$dtArr[$i] && $ts<$dtArr[$i+1]){
//i'th slot
$info=['match'=>true,'index'=>$i];
break;
}
}else{
// at last item ie. ( $i == count($dtArr)-1 )
if($ts<=$dtArr[$i])
$info=['match'=>true,'index'=>$i];
else
$info=['match'=>false,'index'=>NULL];
}
}
return $info;
}