PostgreSQL中基于时间戳的移动平均

PostgreSQL中基于时间戳的移动平均,sql,postgresql,timestamp,moving-average,Sql,Postgresql,Timestamp,Moving Average,我想通过时间戳执行移动平均。 我有两列:温度和时间戳(时间日期),我想根据每15分钟连续的温度观测值执行移动平均。换句话说,根据15分钟的时间间隔选择要执行平均值的数据。此外,对于不同的时间序列,可能有不同数量的观测值。我的意思是所有的窗口大小都是相等的(15分钟),但每个窗口中可能有不同数量的观察。 例如: 对于第一个窗口,我们必须计算n个观测值的平均值,对于第二个窗口,我们必须计算n+5个观测值的平均值 数据样本: ID Timestamps Temperature 1

我想通过时间戳执行移动平均。 我有两列:温度和时间戳(时间日期),我想根据每15分钟连续的温度观测值执行移动平均。换句话说,根据15分钟的时间间隔选择要执行平均值的数据。此外,对于不同的时间序列,可能有不同数量的观测值。我的意思是所有的窗口大小都是相等的(15分钟),但每个窗口中可能有不同数量的观察。 例如: 对于第一个窗口,我们必须计算n个观测值的平均值,对于第二个窗口,我们必须计算n+5个观测值的平均值

数据样本:

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 ID时间戳温度 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分钟进行一次区分。

您可以将表格本身加入到表格中:

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
;

注意:只做“努力工作”。您应该将结果与原始表连接起来,或者将新列追加到查询中。我不知道你最后需要什么问题。调整此解决方案或寻求更多帮助。

以下是一种利用工具将聚合函数用作窗口函数的方法。聚合函数将过去15分钟的观察值与当前运行总数一起保存在一个数组中。状态转换功能将落在15分钟窗口后的元素移出阵列,并推进最新的观察。最后一个函数只是计算数组中的平均温度

现在,至于这是否是一个好处。。。视情况而定。它关注postgresql的plgpsql执行部分,而不是数据库访问部分,我自己的经验是plpgsql速度不快。如果您可以轻松地对表进行查找,为每个观察找到前15分钟的行,那么自连接(如@danihp answer)将很好。然而,这种方法可以处理来自更复杂来源的观察结果,而这些来源的观察结果并不实用。一如既往,在您自己的系统上进行试验和比较

-- 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;
——基于使用此表定义
创建表观察(id int主键,timestamp timestamp not null unique,
温度数值(5,2)不为空;
--注意,我在这里将表结构作为状态的一种类型重用
创建类型rollavg_状态为(内存观察[],总数值(5,2));
创建函数rollavg_func(state rollavg_state,next_in observation)返回rollavg_state不可变语言plpgsql作为$$
声明
截止时间戳;
i int;
更新的_记忆观察[];
开始
提升调试“rollavg_func:state=%,next_in=%”,state,next_in;
截止时间:=下一个_in.时间戳-‘15分钟’::间隔;
i:=array_lower(state.memory,1);
提升调试“截止为%”,截止;
而我呢
返回state.total/array_长度(state.memory,1);
其他的
返回null;
如果结束;
结束
$$;
创建聚合rollavg(观察)(sfunc=rollavg\u func,finalfunc=rollavg\u输出,stype=rollavg\u状态);
--仅引用表名意味着整个行的元组值,其类型为表类型
--聚合依赖于以升序时间戳顺序到达的输入
从观察中选择rollavg(观察)over(按时间戳排序);

假设您希望在每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;
它基于按“时间范围”分组:


下面是一个SQLFIDLE示例:

如果新的15分钟窗口启动,滚动平均值是否会“重新启动”?或者平均值应该计算“最后”15分钟吗?@a_horse_,没有名称,实际上,数据集包括4周的历史数据,我需要移动平均值结果作为一个新的数据集。这并不能回答我的问题。因为
id-row_number()
用作
group_nr
,我认为只有当
ID
s是连续的(即
ID
s中必须没有间隙)时,这才有效
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;