9

私は、2 分の頻度で数週間の観測を含むデータセットを持っています。時間間隔を 2 分から 5 分に増やしたいです。問題は、観測の頻度が常に同じではないことです。つまり、理論的には、10 分ごとに 5 つの観測があるはずですが、通常はそうではありません。平均関数に基づいて、観測の日時に基づいて観測を集計する方法を教えてください。つまり、5 分ごとに集計されますが、観測数は 5 分の時間間隔ごとに同じではありません。さらに、タイムスタンプ形式の日付と時刻があります。

サンプルデータ:

1 2007-09-14 22:56:12 5.39
2 2007-09-14 22:58:12 5.34
3 2007-09-14 23:00:12 5.16
4 2007-09-14 23:02:12 5.54
5 2007-09-14 23:04:12 5.30
6 2007-09-14 23:06:12 5.20

予想された結果:

1 2007-09-14 23:00 5.29
2 2007-09-14 23:05 5.34
4

4 に答える 4

9

この質問への回答は、データを時間枠に効率的に集約する方法を示して、問題に対する適切な解決策を提供する可能性があります。

基本的に、avg集計を次のように使用します。

GROUP BY floor(extract(epoch from the_timestamp) / 60 / 5)
于 2012-10-22T11:16:26.330 に答える
3

編集: これについてもう少し考えてみたところ、2 分から 5 分に変更することはできないことに気付きました。それは合計しません。これについては後で説明しますが、集計する 1 分間のデータがあれば、次のコードは機能します。

--

データが「開始」形式の場合は、この関数内のコードを使用するか、アクセスを容易にするためにデータベースに関数を作成できます。

CREATE OR REPLACE FUNCTION dev.beginning_datetime_floor(timestamp without time zone,   
integer)  /* switch out 'dev' with your schema name */
RETURNS timestamp without time zone AS
$BODY$ 
SELECT
date_trunc('minute',timestamp with time zone 'epoch' + 
 floor(extract(epoch from $1)/($2*60))*$2*60
 * interval '1 second') at time zone 'CST6CDT' /* change this to your time zone */
$BODY$
LANGUAGE sql VOLATILE;

集計したい整数の分数 (1、2、3、4、5、6、10、12、15、20、または 30 を使用) を入力するだけで、いくつかの結果が得られます。

select dev.beginning_datetime_floor('2012-01-01 02:02:21',2)

= '2012-01-01 02:02:00'

select dev.beginning_datetime_floor('2012-01-01 02:02:21',5)

= '2012-01-01 02:00:00'

テストして、組み込みのタイムスタンプ関数を使用して開始タイムスタンプと終了タイムスタンプを処理する時間を加算または減算するだけです。

必要なタイムスタンプを取得したら、クレイグが言ったことを実行し、そのタイムスタンプで GROUP BY を、目的の集計関数(おそらく平均) と組み合わせて実行します。

次の方法でテスト/調整できます。

date_trunc('minute',timestamp with time zone 'epoch' + 
 floor(extract(epoch from your_datetime)/(interval_minutes*60))*interval_minutes*60
 * interval '1 second') at time zone 'CST6CDT' /* change this to your time zone */

タイムスタンプを平均化したいことが判明する場合があります-たとえば、間隔の持続時間が不安定な場合。このために、フロアを取る代わりにタイムスタンプを丸める同様の関数を作成できます。

于 2012-10-22T20:12:37.987 に答える
1

さて、これはこれを処理する1つの方法にすぎません。これにより、分析のニーズに合わせてデータを変換する方法について考えていただければ幸いです。

このコードをテストするための前提条件があります。可能なすべての1分のタイムスタンプを含むテーブルが必要です。これを行うには多くの方法があります。利用可能なものを使用します。1つのテーブルです。1分ごと(00:01:00)から(23:59:00)までのdim_timeと、すべての可能な別のテーブルです。日付(dim_date)。これらに参加すると(1 = 1)、すべての可能な日のすべての可能な分を取得します。

--first you need to create some functions I'll use later
--credit to this first function goes to David Walling
CREATE OR REPLACE FUNCTION dev.beginning_datetime_floor(timestamp without time zone, integer)
  RETURNS timestamp without time zone AS
$BODY$ 
SELECT
date_trunc('minute',timestamp with time zone 'epoch' + 
    floor(extract(epoch from $1)/($2*60))*$2*60
* interval '1 second') at time zone 'CST6CDT'
$BODY$
  LANGUAGE sql VOLATILE;

--the following function is what I described on my previous post  
CREATE OR REPLACE FUNCTION dev.round_minutes(timestamp without time zone, integer)
  RETURNS timestamp without time zone AS
$BODY$ 
  SELECT date_trunc('hour', $1) + cast(($2::varchar||' min') as interval) * round(date_part('minute',$1)::float / cast($2 as float)) 
$BODY$
  LANGUAGE sql VOLATILE;

--let's load the data into a temp table, I added some data points. note: i got rid of the partial seconds
SELECT cast(timestamp_original as timestamp) as timestamp_original, datapoint INTO TEMPORARY TABLE timestamps_second2
FROM
(
SELECT '2007-09-14 22:56:12' as timestamp_original, 0 as datapoint
UNION
SELECT '2007-09-14 22:58:12' as timestamp_original, 1 as datapoint
UNION
SELECT '2007-09-14 23:00:12' as timestamp_original, 10 as datapoint 
UNION
SELECT '2007-09-14 23:02:12' as timestamp_original, 100 as datapoint
UNION
SELECT '2007-09-14 23:04:12' as timestamp_original, 1000 as datapoint
UNION
SELECT '2007-09-14 23:06:12' as timestamp_original, 10000 as datapoint
) as data

--this is the bit of code you'll have to replace with your implementation of getting all possible minutes
--you could make some sequence of timestamps in R, or simply make the timestamps in Excel to test out the rest of the code
--the result of the query is simply '2007-09-14 00:00:00' through '2007-09-14 23:59:00'
SELECT * INTO TEMPORARY TABLE possible_timestamps
FROM
(
select the_date + beginning_minute as minute_timestamp
FROM datawarehouse.dim_date as dim_date
JOIN datawarehouse.dim_time as dim_time
ON 1=1
where dim_date.the_date = '2007-09-14'
group by the_date, beginning_minute
order by the_date, beginning_minute
) as data

--round to nearest minute (be sure to think about how this might change your results
SELECT * INTO TEMPORARY TABLE rounded_timestamps2
FROM
(
SELECT dev.round_minutes(timestamp_original,1) as minute_timestamp_rounded, datapoint
from timestamps_second2
) as data

--let's join what minutes we have data for versus the possible minutes
--I used some subqueries so when you select all from the table you'll see the important part (not needed)
SELECT * INTO TEMPORARY TABLE joined_with_possibles
FROM
(
SELECT *
FROM
(
SELECT *, (MIN(minute_timestamp_rounded) OVER ()) as min_time, (MAX(minute_timestamp_rounded) OVER ()) as max_time
FROM possible_timestamps as t1
LEFT JOIN rounded_timestamps2 as t2
ON t1.minute_timestamp = t2.minute_timestamp_rounded
ORDER BY t1.minute_timestamp asc
) as inner_query
WHERE minute_timestamp >= min_time
AND minute_timestamp <= max_time
) as data

--here's the tricky part that might not suit your needs, but it's one method
--if it's missing a value it grabs the previous value
--if it's missing the prior value it grabs the one before that, otherwise it's null
--best practice would be run another case statement with 0,1,2 specifying which point was pulled, then you can count those when you aggregate
SELECT * INTO TEMPORARY TABLE shifted_values
FROM
(
SELECT 
*,
case 
when datapoint is not null then datapoint
when datapoint is null and (lag(datapoint,1) over (order by minute_timestamp asc)) is not null
  then lag(datapoint,1) over (order by minute_timestamp asc)
when datapoint is null and (lag(datapoint,1) over (order by minute_timestamp asc)) is null and (lag(datapoint,2) over (order by minute_timestamp asc)) is not null
  then lag(datapoint,2) over (order by minute_timestamp asc)
else null end as last_good_value
from joined_with_possibles
ORDER BY minute_timestamp asc
) as data

--now we use the function from my previous post to make the timestamps to aggregate on
SELECT * INTO TEMPORARY TABLE shifted_values_with_five_minute
FROM
(
SELECT *, dev.beginning_datetime_floor(minute_timestamp,5) as five_minute_timestamp
FROM shifted_values
) as data

--finally we aggregate
SELECT
AVG(datapoint) as avg_datapoint, five_minute_timestamp
FROM shifted_values_with_five_minute
GROUP BY five_minute_timestamp
于 2012-10-22T21:27:46.937 に答える
1

最も簡単なオプションは、参照テーブルを作成することです。そのテーブルには、関心のある間隔を保存します。

(これを独自の RDBMS の日付表記に合わせてください。)

CREATE TABLE interval (
  start_time    DATETIME,
  cease_time    DATETIME
);
INSERT INTO interval SELECT '2012-10-22 12:00', '2012-10-22 12:05';
INSERT INTO interval SELECT '2012-10-22 12:05', '2012-10-22 12:10';
INSERT INTO interval SELECT '2012-10-22 12:10', '2012-10-22 12:15';
INSERT INTO interval SELECT '2012-10-22 12:15', '2012-10-22 12:20';
INSERT INTO interval SELECT '2012-10-22 12:20', '2012-10-22 12:25';
INSERT INTO interval SELECT '2012-10-22 12:25', '2012-10-22 12:30';
INSERT INTO interval SELECT '2012-10-22 12:30', '2012-10-22 12:35';
INSERT INTO interval SELECT '2012-10-22 12:35', '2012-10-22 12:40';

次に、結合して集約するだけです...

SELECT
  interval.start_time,
  AVG(observation.value)
FROM
  interval
LEFT JOIN
  observation
    ON  observation.timestamp >= interval.start_time
    AND observation.timestamp <  interval.cease_time
GROUP BY
  interval.start_time

注:間隔テーブルを作成して入力する必要があるのは 1 回だけで、その後は何度でも再利用できます。

于 2012-10-22T09:51:22.513 に答える