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 в настоящем и прошлом за возможность создать мой собственный мир и спокойно жить в нем. Еще я хотел бы выразить признательность Аргоннской лаборатории и Министерству энергетики США за годы постоянной поддержки моих исследований в области баз данных.