2012-01-15 37 views
15

Tôi đang cố gắng để thực hiện một đường trung bình di chuyển theo cấp số nhân (EMA) trên postgres, nhưng khi tôi kiểm tra tài liệu và suy nghĩ về nó, tôi càng cố gắng bối rối hơn.Làm cách nào để tính toán mức trung bình di động theo số mũ trên postgres?

Công thức EMA(x) là:

EMA(x1) = x1 
EMA(xn) = α * xn + (1 - α) * EMA(xn-1) 

Nó có vẻ là hoàn hảo cho một aggregator, giữ kết quả của các yếu tố tính toán cuối cùng là chính xác những gì đã được thực hiện ở đây. Tuy nhiên một aggregator tạo ra một kết quả duy nhất (như giảm, hoặc gấp) và ở đây chúng ta cần một danh sách (một cột) kết quả (như bản đồ). Tôi đã kiểm tra các thủ tục và chức năng hoạt động như thế nào, nhưng AFAIK chúng tạo ra một đầu ra đơn lẻ, chứ không phải một cột. Tôi đã nhìn thấy rất nhiều thủ tục và chức năng, nhưng tôi không thể thực sự tìm ra cách điều này tương tác với đại số quan hệ, đặc biệt là khi làm một cái gì đó như thế này, một EMA.

Tôi không có may mắn tìm kiếm Internets từ trước tới nay. Nhưng định nghĩa cho một EMA khá đơn giản, tôi hy vọng có thể dịch định nghĩa này thành một cái gì đó hoạt động trong postgres và đơn giản và hiệu quả, vì việc chuyển sang NoSQL sẽ là quá nhiều trong bối cảnh của tôi.

Cảm ơn bạn.

PD: ở đây bạn có thể thấy một ví dụ:
https://docs.google.com/spreadsheet/ccc?key=0AvfclSzBscS6dDJCNWlrT3NYdDJxbkh3cGJ2S2V0cVE

+0

Đăng một nguồn bảng mẫu một kết quả mong đợi. Điều này có thể giúp. – danihp

Trả lời

14

Bạn có thể xác định chức năng tổng hợp của riêng bạn và sau đó sử dụng nó với một đặc tả cửa sổ để có được kết quả tổng hợp ở mỗi giai đoạn chứ không phải là một giá trị duy nhất.

Vì vậy, tổng hợp là một phần của nhà nước, và một hàm chuyển đổi để sửa đổi trạng thái đó cho mỗi hàng, và tùy chọn một hàm hoàn thiện để chuyển đổi trạng thái thành giá trị đầu ra. Đối với một trường hợp đơn giản như thế này, chỉ cần một hàm biến đổi là đủ.

create function ema_func(numeric, numeric) returns numeric 
    language plpgsql as $$ 
declare 
    alpha numeric := 0.5; 
begin 
    -- uncomment the following line to see what the parameters mean 
    -- raise info 'ema_func: % %', $1, $2; 
    return case 
       when $1 is null then $2 
       else alpha * $2 + (1 - alpha) * $1 
     end; 
end 
$$; 
create aggregate ema(basetype = numeric, sfunc = ema_func, stype = numeric); 

mà mang lại cho tôi:

[email protected]@[local] =# select x, ema(x, 0.1) over(w), ema(x, 0.2) over(w) from data window w as (order by n asc) limit 5; 
    x  |  ema  |  ema  
-----------+---------------+--------------- 
44.988564 |  44.988564 |  44.988564 
    39.5634 | 44.4460476 | 43.9035312 
38.605724 | 43.86201524 | 42.84396976 
38.209646 | 43.296778316 | 41.917105008 
44.541264 | 43.4212268844 | 42.4419368064 

Những con số này dường như phù hợp vào bảng tính bạn đã thêm vào câu hỏi.

Ngoài ra, bạn có thể xác định các chức năng để vượt qua alpha như một tham số từ báo cáo kết quả:

create or replace function ema_func(state numeric, inval numeric, alpha numeric) 
    returns numeric 
    language plpgsql as $$ 
begin 
    return case 
     when state is null then inval 
     else alpha * inval + (1-alpha) * state 
     end; 
end 
$$; 

create aggregate ema(numeric, numeric) (sfunc = ema_func, stype = numeric); 

select x, ema(x, 0.5 /* alpha */) over (order by n asc) from data 

Ngoài ra, chức năng này thực sự là đơn giản như vậy mà nó không cần phải được trong plpgsql ở tất cả, nhưng có thể chỉ là một chức năng sql, mặc dù bạn không có thể tham khảo các thông số theo tên một trong những:

create or replace function ema_func(state numeric, inval numeric, alpha numeric) 
    returns numeric 
    language sql as $$ 
select case 
     when $1 is null then $2 
     else $3 * $2 + (1-$3) * $1 
     end 
$$; 
+0

+1 Tôi đã có một cái gì đó như thế trong tâm trí, mặc dù không chi tiết. –

+0

Điều này có tính toán tập hợp tạo ra kết quả trong mỗi hàng cho mỗi danh sách con của dữ liệu đầu vào không? Bởi vì có vẻ như nó đang sử dụng trình tổng hợp lên đến hàng n, trả về kết quả và sau đó chuyển đến hàng 0 để tính tổng hợp lên đến hàng n + 1 một lần nữa. Có cách nào để sử dụng tích lũy hoặc một số biến tĩnh (như trong C) sao cho điều này phải được tính một lần? Cảm ơn bạn. – Trylks

+0

Không, nó đang sử dụng giá trị tích lũy. Nếu bạn chạy truy vấn với lệnh "nâng cao thông tin" không được chú ý, bạn sẽ có thể thấy rằng hàm chỉ được gọi một lần cho mỗi đầu ra hàng. Postgresql xuất ra giá trị trạng thái trên mỗi hàng (nếu có một finalfunc được định nghĩa, nó sẽ được gọi để biến trạng thái thành một giá trị đầu ra). – araqnid

1

Đây là loại truy vấn có thể được giải quyết với một CTE đệ quy - thử:

with recursive cte as (
select n, x ema from my_table where n = 1 
union all 
select m.n, alpha * m.x + (1 - alpha) * cte.ema 
from cte 
join my_table m on cte.n = m.n - 1 
cross join (select ? alpha) a) 
select * from cte; 
+1

Tôi đã tự do áp dụng một số sửa lỗi nhỏ. Loại bỏ hàng đầu ';' - được yêu cầu trong tSQL nhưng không phải trong PostgreSQL. Mã được định dạng. Cải thiện điều kiện JOIN. Gia tăng một giá trị từ CTE nhanh hơn việc giảm tất cả các giá trị từ bảng. (Tôi thực sự chạy một thử nghiệm nhanh để xác minh trên pg 9.0.) –

+0

Bất kỳ giải pháp nào dựa trên kết hợp chéo sẽ quá chậm đối với các tập dữ liệu lớn hơn nhỏ. Sử dụng chức năng cửa sổ để thay thế. –

+0

@PavelStehule: Bảng được nối chéo sẽ chỉ có một bản ghi (ảo) trong đó - mục đích duy nhất của nó là chấp nhận alpha làm tham số. –

0
--$1 Stock code 
--$2 exponential; 
create or replace function fn_ema(text,numeric) 
    returns numeric as 
    $body$ 
    declare 
     alpha numeric := 0.5; 
     var_r record; 
     result numeric:=0; 
     n int; 
     p1 numeric; 
    begin 
     alpha=2/(1+$2); 
     n=0; 
     for var_r in(select * 
     from stock_old_invest 
     where code=$1 order by stock_time desc) 
     loop 
      if n>0 then 
       result=result+(1-alpha)^n*var_r.price_now; 
      else 
       p1=var_r.price_now; 
      end if; 
      n=n+1; 
     end loop; 
     result=alpha*(result+p1); 
     return result; 
    end 
    $body$ 
    language plpgsql volatile 
    cost 100; 
    alter function fn_ema(text,numeric) 
    owner to postgres; 
Các vấn đề liên quan