Tính toán điểm dữ liệu phù hợp từ dấu thời gian mờ trong Postgresql


7

Tôi có một bảng chứa nhiều chuỗi thời gian khác nhau. Dấu thời gian của các điểm gắn kết từ các chuỗi khác nhau không khớp chính xác (nghĩa là chênh lệch có thể lên tới một giờ).

Lược đồ

Dưới đây là lược đồ với hai loạt ví dụ:

CREATE TABLE series (id integer, series_type integer, charttime timestamp,
                     value integer, PRIMARY KEY (id));
INSERT INTO series VALUES (1, 1, '2018-03-01 12:10:00', 40),
    (2, 1, '2018-03-01 13:25:00', 30), (3, 1, '2018-03-01 14:10:00', 50);
INSERT INTO series VALUES (4, 2, '2018-03-01 11:20:00', 2), (5, 2, '2018-03-01 12:15:00', 6),
    (6, 2, '2018-03-01 13:00:00', 7), (7, 2, '2018-03-01 13:45:00', 1);

id |series_type |charttime           |value |
---|------------|--------------------|------|
1  |1           |2018-03-01 12:10:00 |40    |
2  |1           |2018-03-01 13:25:00 |30    |
3  |1           |2018-03-01 14:10:00 |50    |
4  |2           |2018-03-01 11:20:00 |2     |
5  |2           |2018-03-01 12:15:00 |6     |
7  |2           |2018-03-01 13:45:00 |1     |
6  |2           |2018-03-01 13:00:00 |7     |

Mục tiêu

Mục tiêu là chọn một chuỗi cùng với điểm dữ liệu gần nhất từ ​​chuỗi khác. Đối với tập dữ liệu mẫu, kết quả sẽ là:

charttime           |s1 |s2 |
--------------------|---|---|
2018-03-01 12:10:00 |40 |6  |
2018-03-01 13:25:00 |30 |1  |
2018-03-01 14:10:00 |50 |1  |

Phương pháp làm việc đầu tiên

Cách tiếp cận hiện tại của tôi là chọn điểm dữ liệu phù hợp nhất từ ​​chuỗi khác bằng một truy vấn con:

SELECT l.charttime, l.value AS s1,
    ( SELECT r.value
      FROM series r
      WHERE ABS( EXTRACT( EPOCH FROM l.charttime - r.charttime ) / 3600 ) < 1
            AND r.series_type = 2
      ORDER BY ABS( EXTRACT( EPOCH FROM l.charttime - r.charttime )) ASC LIMIT 1 
    ) AS s2
FROM series l
WHERE l.series_type = 1
ORDER BY l.charttime ASC

Đây dường như không phải là cách tiếp cận tốt nhất vì bộ dữ liệu khá lớn và do đó thực hiện nhiều truy vấn con làm chậm truy vấn.

Cách tiếp cận thứ hai

Một ý tưởng khác là tự tham gia bảng và bộ lọc cho dấu thời gian dữ liệu gần:

SELECT l.charttime, l.value AS s1, r.charttime, r.value AS s2
FROM series l, series r
WHERE abs(EXTRACT(EPOCH FROM l.charttime - r.charttime) / 3600) < 1
      AND l.series_type = 1 AND r.series_type = 2

charttime           |s1 |charttime           |s2 |
--------------------|---|--------------------|---|
2018-03-01 12:10:00 |40 |2018-03-01 11:20:00 |2  |
2018-03-01 12:10:00 |40 |2018-03-01 12:15:00 |6  |
2018-03-01 12:10:00 |40 |2018-03-01 13:00:00 |7  |
2018-03-01 13:25:00 |30 |2018-03-01 13:45:00 |1  |
2018-03-01 13:25:00 |30 |2018-03-01 13:00:00 |7  |
2018-03-01 14:10:00 |50 |2018-03-01 13:45:00 |1  |

Vấn đề sau đó là các điểm dữ liệu trùng lặp. Nhóm trong cột đầu tiên không hoạt động vì s2không thể chọn kết quả phù hợp nhất .

Có một cách tiếp cận tốt hơn?


Bạn có một số lượng thiết lập của loạt? Bạn nói Mục tiêu là chọn một chuỗi cùng với điểm dữ liệu gần nhất từ ​​chuỗi khác nhưng trong ví dụ của bạn 40,30,50 tất cả đều đến từ loạt_type = 1. Tôi cũng bối rối về cách bạn có hai bộ dữ liệu và ba điểm trong đầu ra mong muốn của bạn. Là một trong những đầu ra mong muốn? Bạn có thể hiển thị / xác nhận đầu ra mong muốn được cung cấp đầu vào để chúng tôi có trường hợp thử nghiệm không?
Evan Carroll

Trong các mẫu đầu ra được hiển thị, các giá trị trong cột s2đến từ series_type= 2.
RDFozz

@EvanCarroll Kết quả ví dụ là từ loạt ví dụ. Có ba điểm dữ liệu trong series_type = 1. Như đã chỉ ra, s2là những điểm phù hợp từ series_type=2.
stsc

Câu trả lời:


4

Trong cách tiếp cận thứ hai của bạn, với việc tự tham gia, bạn có thể xóa các bản sao bằng cách sử dụng row_number(),

Phân vùng theo l.charttime, sắp xếp theo chênh lệch thời gian và bộ lọc cho row_number = 1.

Tôi nghĩ rằng hiệu suất sẽ là khủng khiếp, tuy nhiên. Bởi vì tham gia cartesian, đây sẽ là một hoạt động O (kích thước (loạt 1) x size (loạt 2)).

Có cả chức năng l.charttime và r.charttime bên trong cũng có thể gây rắc rối. Hãy thử tái cấu trúc thành (bằng mã giả)

    r.charttime < l.charttime + 3600
and r.charttime > l.charttime - 3600

.. và xem kế hoạch truy vấn trông như thế nào. Tôi đoán có một chỉ số về biểu đồ. Không có một cách tiếp cận sẽ được nhanh chóng. Thật vậy, hai chỉ mục một phần , một trên Series 1 và một trên Series 2 có thể còn tốt hơn nữa.


Ý tưởng của bạn với parition bởi hoạt động khá sẽ, nó giảm thời gian truy vấn từ khoảng 20 giây xuống còn 1 giây. Tôi đã đăng một câu trả lời với mã mà tôi đã kết thúc.
stsc

1

Tôi đang đọc quy tắc kinh doanh của bạn là "lấy từng hàng từ loạt 1 và hàng gần nhất từ ​​loạt 2, nếu có, phải nằm trong 3600 giá trị của sê-ri 1". Tôi tự hỏi nếu một giải pháp tốt cho điều này sẽ không phải là một con trỏ. Vâng, hai con trỏ.

Thuật toán cơ bản là tìm hai giá trị sê-ri 2 nối tiếp từng giá trị sê-ri 1, tức là giá trị ngay trước và giá trị ngay sau chuỗi thời gian. Sau đó sử dụng bất cứ trong số này là gần hơn. Nó sẽ trông giống như thế này:

declare two variables: Smaller(datetime, value) and Larger(datetime, value).
initialize the variables to their domain minimum value e.g. (1900-01-01 00:00:00, 0).

open a cursor on series 1, in time order
open a cursor on series 2, in time order

while rows remain in Series1

    while Larger.datetime < Series1.datetime
        Read next Series2
        set Smaller = Larger
        set Larger = Series2
        // Add logic for when Series2 is exhausted
    end

    // We know Smaller is less than Series1.datetime and Larger is greater than or equal,
    // or there's a case not documented in the question.
    // Check for Smaller, Larger within the 3600 window to be added.
    if (Series1.datetime - Smaller.datetime) < (Larger.datetime - Series1.datetime)
        use Smaller.value
    else
        use Larger.value
    end

    read next Series1
end

Rõ ràng là tôi đã bỏ qua rất nhiều thứ tốt đẹp. Sẽ có một số trường hợp về việc có nhiều giá trị sê-ri 1 trước giá trị sê-ri 2 đầu tiên hoặc sau giá trị sê-ri 2 cuối cùng. Ngoài ra nếu không có kết quả khớp trong chuỗi 2 cho một giá trị loạt 1 đã cho. Mô tả của bạn không đề cập đến các quy tắc cho những điều này nhưng tôi chắc chắn bạn sẽ có thể làm việc với chúng.

Điều này đòi hỏi cả hai giá trị của chuỗi phải theo trình tự thời gian. Các loại có thể đắt tiền. Tuy nhiên, sẽ phải có một chỉ mục trên cột này để mọi giải pháp đều khả thi. Vì vậy, truy vấn sẽ có đường dẫn truy cập theo thứ tự thời gian vào dữ liệu và có khả năng sẽ không có sắp xếp thực tế khi chạy.

Độ phức tạp thời gian của điều này là O (kích thước (loạt 1) + kích thước (loạt 2)) tức là O (N) chứ không phải O (N ^ 2) của tự tham gia chéo.


1

Dựa trên ý tưởng từ Michael Green, tôi đã kết thúc như sau:

WITH c AS (
SELECT
    l.charttime,
    l.value AS s1,
    r.value AS s2,
    rank() OVER (PARTITION BY l.charttime ORDER BY abs(EXTRACT(EPOCH FROM l.charttime - r.charttime) / 3600) ASC) AS rnk
FROM
    series l, series r
WHERE
    abs(EXTRACT(EPOCH FROM l.charttime - r.charttime) / 3600) < 1
    AND l.series_type = 1
    AND r.series_type = 2
)
SELECT charttime, s1, s2 FROM c WHERE rnk = 1 ORDER BY charttime

Thời gian truy vấn là khoảng 1 giây so với 20 giây trong cách tiếp cận ban đầu của tôi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.