2012-12-11 16 views
15

私はタイムスタンプを通して移動平均を実行したかったのです。 私は2つの列を持っています:温度とタイムスタンプ(時間 - 日付)と、15分間の連続した温度観測に基づいて移動平均を実行したいと思います。言い換えれば、15分の時間間隔に基づいて平均を実行するためにデータを選択する。さらに、異なる時間シーケンスに対して異なる数の観測値を有することが可能である。私はすべてのウィンドウサイズが等しいことを意味しました(15分)が、各ウィンドウで異なる観測数を持つことは可能です。 例: 最初のウィンドウではn回の観測の平均を計算し、2回目のウィンドウではn + 5回の観測の平均を計算する必要があります。PostgreSQLのタイムスタンプに基づく移動平均

データサンプル:

 
ID Timestamps   Temperature 
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 
7 2007-09-14 23:10:12 5.39 
8 2007-09-14 23:12:12 5.34 
9 2007-09-14 23:20:12 5.16 
10 2007-09-14 23:24:12 5.54 
11 2007-09-14 23:30:12 5.30 
12 2007-09-14 23:33:12 5.20 
13 2007-09-14 23:40:12 5.39 
14 2007-09-14 23:42:12 5.34 
15 2007-09-14 23:44:12 5.16 
16 2007-09-14 23:50:12 5.54 
17 2007-09-14 23:52:12 5.30 
18 2007-09-14 23:57:12 5.20 

主な課題:異なるサンプリング周波数による15分の時間間隔が正確ではありませんしている間、私はすべての15分を識別するためのコードを学ぶことができますどのように

+0

15分の新しいウィンドウが開始されるとローリング平均が「再開」しますか?または、平均が「最後」の15分を計算する必要がありますか? –

+0

@a_horse_with_no_name、実際には、データセットには4週間の履歴データが含まれており、移動平均結果を新しいデータセットとして必要とします。 –

+0

それは私の質問に答えることはありません。 –

答えて

5

select id, 
     temp, 
     avg(temp) over (partition by group_nr order by time_read) as rolling_avg 
from (  
    select id, 
     temp, 
     time_read, 
     interval_group, 
     id - row_number() over (partition by interval_group order by time_read) as group_nr 
    from (
    select id, 
      time_read, 
      'epoch'::timestamp + '900 seconds'::interval * (extract(epoch from time_read)::int4/900) as interval_group, 
      temp 
    from readings 
) t1 
) t2 
order by time_read; 

それは、「時間範囲」によってグループにDepesz's solutionに基づいています。ここでは

はSQLFiddle例です。 :http://sqlfiddle.com/#!1/0f3f0/2

9

あなたは自分自身とあなたのテーブルに参加することができます。

select l1.id, avg(l2.Temperature) 
from l l1 
inner join l l2 
    on l2.id <= l1.id and 
     l2.Timestamps + interval '15 minutes' > l1.Timestamps 
group by l1.id 
order by id 
; 

Results

| ID |   AVG | 
----------------------- 
| 1 |   5.39 | 
| 2 |   5.365 | 
| 3 | 5.296666666667 | 
| 4 |   5.3575 | 
| 5 |   5.346 | 
| 6 | 5.321666666667 | 
| 7 | 5.331428571429 | 

お知らせ:のみ 'ハードワークが' 作られます。結果を元のテーブルに結合するか、クエリに新しい列を追加する必要があります。あなたの最終的な質問が必要なのか分かりません。この解決法を適応させるか、より多くの助けを求める。

3

ここでは、ファンクションを使用して集計関数をウィンドウ関数として使用するアプローチがあります。集計関数は、アレイ内の最後の15分間の観測値を、現在の実行中の合計とともに保持します。状態遷移関数は、15分のウィンドウの後ろにある配列から要素をシフトし、最新の観測をプッシュします。最終的な関数は単純に配列の平均温度を計算します。

これが利益であるかどうかに関しては、それは依存しています。これはデータベースアクセス部分ではなくpostgresqlのplgpsql実行部分に焦点を当てています。私の経験ではplpgsqlは高速ではありません。あなたが簡単にテーブルに戻って各観測の前の15分の行を見つけることができれば、自己結合(@danihp答えのように)がうまくいくでしょう。しかし、このアプローチは、よりルックアップが実用的でない、より複雑なソースからの観察に対処することができます。これまでどおり、自分のシステムで試用して比較します。

あなたは各15分間隔の後にローリング平均を再開したいと仮定すると、
-- based on using this table definition 
create table observation(id int primary key, timestamps timestamp not null unique, 
         temperature numeric(5,2) not null); 

-- note that I'm reusing the table structure as a type for the state here 
create type rollavg_state as (memory observation[], total numeric(5,2)); 

create function rollavg_func(state rollavg_state, next_in observation) returns rollavg_state immutable language plpgsql as $$ 
declare 
    cutoff timestamp; 
    i int; 
    updated_memory observation[]; 
begin 
    raise debug 'rollavg_func: state=%, next_in=%', state, next_in; 
    cutoff := next_in.timestamps - '15 minutes'::interval; 
    i := array_lower(state.memory, 1); 
    raise debug 'cutoff is %', cutoff; 
    while i <= array_upper(state.memory, 1) and state.memory[i].timestamps < cutoff loop 
    raise debug 'shifting %', state.memory[i].timestamps; 
    i := i + 1; 
    state.total := state.total - state.memory[i].temperature; 
    end loop; 
    state.memory := array_append(state.memory[i:array_upper(state.memory, 1)], next_in); 
    state.total := coalesce(state.total, 0) + next_in.temperature; 
    return state; 
end 
$$; 

create function rollavg_output(state rollavg_state) returns float8 immutable language plpgsql as $$ 
begin 
    raise debug 'rollavg_output: state=% len=%', state, array_length(state.memory, 1); 
    if array_length(state.memory, 1) > 0 then 
    return state.total/array_length(state.memory, 1); 
    else 
    return null; 
    end if; 
end 
$$; 

create aggregate rollavg(observation) (sfunc = rollavg_func, finalfunc = rollavg_output, stype = rollavg_state); 

-- referring to just a table name means a tuple value of the row as a whole, whose type is the table type 
-- the aggregate relies on inputs arriving in ascending timestamp order 
select rollavg(observation) over (order by timestamps) from observation; 
関連する問題