2012-12-11 10 views
15

में टाइमस्टैम्प पर आधारित मूविंग औसत मैं टाइमस्टैम्प के माध्यम से मूविंग औसत करना चाहता था। मेरे पास दो कॉलम हैं: तापमान और टाइमस्टैम्प (समय-तिथि) और मैं प्रत्येक 15 मिनट के लगातार तापमान अवलोकनों के आधार पर चलती औसत निष्पादित करना चाहता हूं। दूसरे शब्दों में, 15 मिनट के अंतराल के आधार पर औसत प्रदर्शन करने के लिए डेटा का चयन करना। इसके अलावा, विभिन्न समय अनुक्रमों के लिए विभिन्न प्रकार के अवलोकन होना संभव है। मेरा मतलब था कि सभी खिड़की के आकार बराबर (15 मिनट) हैं लेकिन प्रत्येक खिड़की में अलग-अलग अवलोकन होना संभव है। उदाहरण के लिए: पहली विंडो के लिए हमें एन अवलोकन के औसत की गणना करना है और दूसरी विंडो के लिए एन +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

मान लिया जाये कि आप प्रत्येक 15 मिनट के अंतराल के बाद रोलिंग औसत पुनः आरंभ करना चाहते हैं:

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-execution भाग पर केंद्रित है, और मेरे अपने अनुभव यह है कि plpgsql तेज़ नहीं है। यदि आप प्रत्येक अवलोकन के लिए पिछली 15 मिनट की पंक्तियों को ढूंढने के लिए आसानी से तालिका में लुकअप कर सकते हैं, तो एक स्व-जुड़ें (जैसा कि @danihp उत्तर में) अच्छा होगा। हालांकि, यह दृष्टिकोण कुछ और जटिल स्रोतों से आने वाले अवलोकनों से निपट सकता है, जहां वे लुकअप व्यावहारिक नहीं हैं। हमेशा के रूप में, परीक्षण और अपने सिस्टम पर तुलना करें।

-- 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;