seg — тип данных для отрезков или интервалов с плавающей запятой

Этот модуль реализует тип данных seg для представления отрезков или интервалов с плавающей запятой. Тип seg может отображать неопределенность концов интервала, что делает его особенно полезным для представления лабораторных измерений.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.


Обоснование

Геометрия измерений обычно более сложна, чем точка в числовом континууме. Как правило, измерение представляет собой отрезок этого континуума с несколько размытыми границами. Эти измерения выводятся в виде отрезков вследствие неопределенности и случайности, а также из-за того, что измеряемое значение может по своей природе быть интервалом, указывающим на некое условие, например, диапазон температур стабильности белка.

Исходя только из здравого смысла, кажется более удобным хранить такие данные в виде интервалов, а не пары чисел. На практике это оказывается еще эффективнее в большинстве приложений.

Если и далее рассуждать здраво, размытость границ предполагает, что использование традиционных числовых типов данных приводит к определенной потере информации. Рассмотрим такой пример: ваш инструмент выдает 6.50, и вы вводите это значение в базу данных. Что вы получите, выбрав его из базы? Смотрите:

test=> SELECT 6.50 :: float8 AS "pH";
 pH
---
6.5
(1 row)

В мире измерений 6.50 — это не то же самое, что 6.5. Разница между этими значениями иногда может быть критической. Экспериментаторы обычно записывают (и публикуют) цифры, в которых уверены. Запись 6.50 на самом деле представляет собой нечеткий интервал, содержащийся внутри большего и еще более нечеткого интервала 6.5, и единственное, что у них может быть общего, — это их центральные точки. Мы определенно не хотим, чтобы такие разные элементы данных выглядели одинаково.

Вывод? Удобно иметь специальный тип данных, в котором можно сохранить границы интервала с произвольной переменной точностью. (Здесь под переменной точностью подразумевается, что для каждого элемента данных она записывается индивидуально.)

Проверьте это:

test=> SELECT '6.25 .. 6.50'::seg AS "pH";
          pH
------------
6.25 .. 6.50
(1 row)

Синтаксис

Внешнее представление интервала образуется одним или двумя числами с плавающей запятой, соединенными оператором диапазона (.. или ...). Как вариант, интервал можно задать центральной точкой плюс/минус отклонение. Также этот тип позволяет сохранить дополнительные индикаторы достоверности (<, > или ~). (Однако индикаторы достоверности игнорируются всеми встроенными операторами.) В Таблице 14 дается обзор допустимых представлений; в Таблице 15 приводятся некоторые примеры.

В Таблице 14 символы x, y и delta обозначают числа с плавающей запятой. Перед значениями x и y (но не delta) может быть добавлен индикатор достоверности.

Таблица 14. Внешние представления seg

ПредставлениеОписание
xОдно значение (интервал нулевой длины)
x .. yИнтервал от x до y
x (+-) deltaИнтервал от x - delta до x + delta
x ..Открытый интервал с нижней границей x
.. xОткрытый интервал с верхней границей x

Таблица 15. Примеры допустимых вводимых значений seg

ПримерЧто делает
5.0Создает сегмент нулевой длины (или точку, если хотите).
~5.0Создает сегмент нулевой длины и записывает ~ в данные. Знак ~ игнорируется при операциях с seg, но сохраняется как комментарий.
<5.0Создает точку с координатой 5.0. Знак < игнорируется, но сохраняется как комментарий.
>5.0Создает точку с координатой 5.0. Знак > игнорируется, но сохраняется как комментарий.
5(+-)0.3Создает интервал 4.7 .. 5.3. Обратите внимание, что запись (+-) не сохраняется.
50 ..Все, что больше или равно 50.
.. 0Все, что меньше или равно 0.
1.5e-2 .. 2E-2Создает интервал 0.015 .. 0.02.
1 ... 2То же, что и 1...2, или 1 .. 2, или 1..2 (пробелы вокруг оператора диапазона игнорируются).

Поскольку оператор ... широко используется в источниках данных, он принимается в качестве альтернативного написания оператора ... К сожалению, это порождает неоднозначность при разборе: неясно, какая верхняя граница подразумевается в записи 0...23 — 23 или 0.23. Для разрешения этой неоднозначности во входных числах seg перед десятичной точкой всегда должна быть как минимум одна цифра.

В целях контроля корректности seg не принимает интервалы с нижней границей, превышающей верхнюю, например, 5 .. 2.


Точность

Значения seg хранятся внутри как пары 32-битных чисел с плавающей запятой. Это значит, что числа с более чем 7 значащими цифрами будут усекаться.

Числа, содержащие 7 значащих цифр и меньше, сохраняют изначальную точность. То есть если запрос возвращает 0.00, вы можете быть уверены, что конечные нули не являются артефактами форматирования: они отражают точность исходных данных. Количество ведущих нулей не влияет на точность: значение 0.0067 считается имеющим только 2 значащие цифры.


Применение

Модуль seg включает класс операторов индекса GiST для значений seg. Операторы, поддерживаемые этим классом операторов, перечислены ниже.

seg << seg → boolean

Первый seg полностью находится левее второго? [a, b] << [c, d] — true, если b < c.

seg >> seg → boolean

Первый seg полностью находится правее второго? [a, b] >> [c, d] — true, если a > d.

seg &< seg → boolean

Первый seg не простирается правее второго? [a, b] &< [c, d] — true, если b <= d.

seg &> seg → boolean

Первый seg не простирается левее второго? [a, b] &> [c, d] — true, если a >= c.

seg = seg → boolean

Два интервала seg равны?

seg && seg → boolean

Два интервала seg пересекаются?

seg @> seg → boolean

Первый интервал seg содержит второй?

seg <@ seg → boolean

Первый интервал seg содержится во втором?

Помимо вышеописанных операторов для типа seg доступны обычные операторы сравнения, показанные в разделе Операторы сравнения. Эти операторы сначала сравнивают (a) и (c), и если они равны, сравнивают (b) и (d). В большинстве случаев это приводит к адекватному упорядочиванию, что полезно, если нужно применить ORDER BY с этим типом.


Примечания

Примеры использования можно увидеть в регрессионном тесте sql/seg.sql.

Механизм, преобразующий (+-) в обычные диапазоны, не вполне точно определяет число значащих цифр для границ. К примеру, он добавляет дополнительную цифру к нижней границе, если результирующий интервал включает степень десяти:

qhb=> SELECT '10(+-)1'::seg AS seg;
      seg
---------
9.0 .. 11             -- должно быть: 9 .. 11

Производительность индекса R-дерева может значительно зависеть от начального порядка вводимых значений. Может быть очень полезно отсортировать входную таблицу по столбцу seg; пример можно найти в скрипте sort-segments.pl.


Благодарности

Оригинальный автор: Джин Селков мл. (Gene Selkov, Jr.), selkovjr@mcs.anl.gov, Аргоннская национальная лаборатория, Отдел математики и компьютерных наук.

Я благодарен в первую очередь профессору Джо Геллерштейну (Joe Hellerstein) (https://dsf.berkeley.edu/jmh/) за пояснение сути GiST (http://gist.cs.berkeley.edu/). Я также признателен всем разработчикам Postgres в настоящем и прошлом за возможность создать мой собственный мир и спокойно жить в нем. Еще я хотел бы выразить признательность Аргоннской лаборатории и Министерству энергетики США за годы постоянной поддержки моих исследований в области баз данных.