PostgreSQL объединяет LAG и LEAD для запроса n предыдущих и последующих строк - PullRequest
0 голосов
/ 01 ноября 2018

У меня есть таблица PostgreSQL, назовем ее токены , содержащие грамматические аннотации на токен в строках текста, в основном так:

idx | line | tno | token   | annotation      | lemma
----+------+-----+---------+-----------------+---------
  1 | I.01 | 1   | This    | DEM.PROX        | this
  2 | I.01 | 2   | is      | VB.COP.3SG.PRES | be
  3 | I.01 | 3   | an      | ART.INDEF       | a
  4 | I.01 | 4   | example | NN.INAN         | example

Я хочу сделать запрос, который позволяет мне искать грамматические контексты, в данном случае запрос, который проверяет, присутствует ли определенная аннотация в окне размером n до и после текущей строки , Из того, что я прочитал об этом, оконные функции PostgreSQL LEAD и LAG подходят для этого. Сначала я написал следующий запрос на основе документации, которую смог найти об этих функциях:

SELECT *
FROM (
    SELECT token, annotation, lemma,
        -- LAG(annotation) OVER prev_rows AS prev_anno, -- ?????
        LEAD(annotation) OVER next_rows AS next_anno
    FROM tokens
    WINDOW next_rows AS (
        ORDER BY line, tno ASC
        ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING
    )
    ORDER BY line, tno ASC
) AS "window"
WHERE
    lemma LIKE '...'
    AND "window".next_anno LIKE '...'
;

Однако, это только поиск в 2 следующих строках. У меня вопрос, как я могу перефразировать запрос, чтобы окно включало в таблицу как предыдущие, так и последующие строки? Очевидно, я не могу иметь 2 WINDOW заявлений или делать что-то вроде

ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
AND ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING

Ответы [ 2 ]

0 голосов
/ 01 ноября 2018

Альтернативный метод состоит в том, чтобы вычислить относительную позицию каждого токена в предложении и выполнить самостоятельное соединение токенов <-> токенов (это позволит вам выбрать скип-грамм на основе расстояние):


WITH www AS (   -- enumerate word posision with sentences
    SELECT line, tno    -- candidate key
        , row_number() OVER sentence AS rn
    FROM tokens
    WINDOW sentence AS ( ORDER BY line ASC, tno ASC)
        )
SELECT t0.line AS line
        , t0.token AS this
        , t1.tno AS tno
        , w1.rn - w0.rn AS rel  -- relative position
        , t1.token AS that
        , t1.annotation AS anno
FROM tokens t0
JOIN tokens t1 ON t1.line = t0.line     -- same sentence
JOIN www w0 ON t0.line = w0.line AND t0.tno= w0.tno -- PK1
JOIN www w1 ON t1.line = w1.line AND t1.tno= w1.tno -- PK2
WHERE 1=1
AND t0.lemma LIKE 'be'
    -- AND t1.annotation LIKE '.PROX' AND w1.rn - w0.rn  = -1
        ;

-- But, if you rno is consecutive(gapless) within lines,
-- you can omit the enumeration step, and do a plain self-join:

SELECT t0.line AS line
        , t0.token AS this
        , t1.tno AS tno
        , t1.tno - t0.tno AS rel        -- relative position
        , t1.token AS that
        , t1.annotation AS anno
FROM tokens t0
JOIN tokens t1 ON t1.line = t0.line     -- same sentence
WHERE 1=1
AND t0.lemma LIKE 'be'
    -- AND t1.annotation LIKE '.PROX' AND w1.rn - w0.rn  = -1
        ;
0 голосов
/ 01 ноября 2018

Я не уверен, правильно ли я понял ваш вариант использования: вы хотите проверить, находится ли одна данная аннотация в одной из 5 строк (2 предшествующих, текущие, 2 следующих). Правильно?


  1. Можно определить окно как BETWEEN 2 PRECEDING AND 2 FOLLOWING
  2. LEAD или LAG дают только одно значение, в этом случае одно значение после или до текущей строки - если окно поддерживает это; независимо от того, сколько строк содержит ваше окно. Но вы хотите проверить любой из этих пяти рядов.

Один из способов достижения этого:


демо: db <> скрипка
SELECT *
FROM (
    SELECT token, annotation, lemma,
        unnest(array_agg(annotation) OVER w) as surrounded_annos      -- 2
    FROM tokens
    WINDOW w AS (                                                     -- 1
        ORDER BY line, tno ASC
        ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING
    )
    ORDER BY line, tno ASC
) AS "window"
WHERE
    lemma LIKE '...'
    AND "window".surrounded_annos LIKE '...'
;
  1. определение окна, как описано выше
    1. агрегирует все аннотации в этих пяти строках (если возможно) с array_agg, что дает массив
    2. unnest расширяет этот массив в одну строку для каждого элемента, так как ИМХО нет способа искать элементы массива с помощью LIKE. Это дает вам этот результат (который можно отфильтровать на следующем шаге):

Результат подзапроса:

token     annotation        lemma     surrounded_annos
This      DEM.PROX          this      DEM.PROX
This      DEM.PROX          this      VB.COP.3SG.PRES
This      DEM.PROX          this      ART.INDEF
is        VB.COP.3SG.PRES   be        DEM.PROX
is        VB.COP.3SG.PRES   be        VB.COP.3SG.PRES
is        VB.COP.3SG.PRES   be        ART.INDEF
is        VB.COP.3SG.PRES   be        NN.INAN
an        ART.INDEF         a         DEM.PROX
an        ART.INDEF         a         VB.COP.3SG.PRES
an        ART.INDEF         a         ART.INDEF
an        ART.INDEF         a         NN.INAN
example   NN.INAN           example   VB.COP.3SG.PRES
example   NN.INAN           example   ART.INDEF
example   NN.INAN           example   NN.
...