Я новичок в современном SQL и почти уверен, что что-то слишком усложнил. Я пытаюсь разбить значения на процентили как по самим значениям, так и по их частоте. Так что, если у меня есть 1000 записей с 100 различными числами, есть диапазон значений, а также диапазон частот, с которыми эти значения встречаются. То, что я хочу получить для каждого значения:
- Само значение.
- Его процентиль в диапазоне значений
- Это количество (частота)
- процентиль частоты
Я использую игрушечные таблицы, чтобы поэкспериментировать с 1000 записями, заполненными случайными значениями из www.mockaroo.com. Мои настоящие таблицы имеют сотни тысяч или миллионы строк. Смысл всего этого заключается в том, чтобы прикрепить процентили и т. Д. К концу каждой строки в представлении для подачи платформы визуализации данных, которая не очень хороша для процентилей. Чтобы было понятно, если я начинаю с 1К записей в моей таблице, запросы должны заканчиваться 1К строк.
CREATE TABLE IF NOT EXISTS mock (
n integer
);
Учитывая мое крошечное количество строк в игрушечных таблицах, я использую децили, а не процентили, но это не должно ничего менять в структуре поиска. Вот как я получаю то, что мне нужно, из таблицы в один столбец:
-- Get the value, count, and value percentile.
with
value_counts as (
select n as value,
count(*) as frequency_count,
ntile(10) over (order by n) as value_decile
from mock
group by n
),
-- Now add the frequency percentile.
frequency_analysis as (
select value,
ntile(10) over (order by frequency_count) as frequency_decile
from value_counts
),
-- Don't need this CTE, just making things readable.
value_information as (
select value_counts.value,
value_counts.value_decile,
value_counts.frequency_count,
frequency_analysis.frequency_decile
from value_counts
join frequency_analysis on (frequency_analysis.value= value_counts.value)
)
select * from value_information;
Итак, я думаю, что это работает .... но у меня нет одного столбца для проверки, у меня есть лотов столбцов для генерации счетчика частоты, процентиля частоты и процентиля значения на. Кажется, это должен быть обычный статистический запрос для выполнения, но мне трудно понять, как это сделать аккуратно в Postgres. Пара моментов, прежде чем попасть в таблицу с двумя столбцами:
Я использую CTE для разборчивости, для создания агрегатов, которые затем можно будет агрегировать в более поздних CTE, и для создания небольшого продукта, к которому можно присоединиться. Все быстро с записями в 1 КБ, но, возможно, не так много с записями в 20 миллионов.
Я на Postgres 11 и не перейду на PG 12 до тех пор, пока он не появится. Таким образом, нет никакого беспокойства о том, как осуществляются CTE. Поведение PG 11 - это именно то, чего я хочу.
Я использую ntile (), потому что на самом деле очень просто динамически регулировать количество бинов, поэтому 10 вместо 100. Я не уверен, стоит ли использовать вместо этого width_bucket, процентный_конт / процентный_диск. Я только что нашел width_bucket этим утром, так что, возможно, есть еще один встроенный способ сделать процентили. (Помимо ручного кодирования.)
Я полностью готов выбросить все это и заняться чем-то другим, если есть лучший способ. Вот почему я экспериментирую с игрушечными данными для начала.
Хорошо, теперь пример более реалистичной таблицы с более значимыми именами полей:
CREATE TABLE IF NOT EXISTS ascendco.mock2 (
num_inst integer,
points integer
);
Опять 1000 записей с разными значениями в двух столбцах. Эти два столбца не имеют отношения в вычислениях, они просто находятся в одной строке, и я хочу, чтобы агрегаты были добавлены в конец. Все это очень похоже на операцию с оконной функцией, но я не хочу повторять работу над 10M строками. Итак, как это сделать для двух + столбцов? У меня сегодня 2-5 в большинстве моих столов, и это будет расти. Я сталкиваюсь с тем, что GROUP BY предназначен для обеспечения одного уровня агрегации, мне нужно отдельное агрегирование для каждого столбца. Есть ли способ сделать это, кроме длинной формы, которую я опробовал ниже?
-- Get the num_inst count and the decile for the value.
with
num_inst_distinct_counts as (
select num_inst,
count(*) as num_inst_frequency,
ntile(10) over (order by num_inst) as value_decile
from mock2
group by num_inst),
-- Extend the previous CTE with the decile for the value's frequency.
num_inst_information as (
select *,
ntile(10) over (order by num_inst_frequency) as frequency_decile
from num_inst_distinct_counts
),
-- Get the points count and the decile for the value.
points_distinct_counts as (
select points,
count(*) as points_frequency,
ntile(10) over (order by points) as value_decile
from mock2
group by points),
-- Extend the previous CTE with the decile for the value's frequency.
points_information as (
select *,
ntile(10) over (order by points_frequency) as frequency_decile
from points_distinct_counts
)
-- Put it all togehter. I could have used more general names in the CTEs, but this makes the output clearer
select mock2.num_inst,
num_inst_information.value_decile as num_inst_value_decile,
num_inst_information.frequency_decile as num_inst_frequency_decile,
mock2.points,
points_information.value_decile as points_value_decile,
points_information.frequency_decile as points_frequency_decile
from mock2
join num_inst_information on (num_inst_information.num_inst = mock2.num_inst)
join points_information on (points_information.points = mock2.points)
order by 1;
Это просто поражает меня как сумасшедшее долго и вовлечено. Я предполагаю, что есть некоторый аккуратный способ использовать массив и LATERAL соединение, чтобы выполнить работу.
Спасибо за любую помощь!
Проснулся и дал ему еще один ход. Ну, я не понял что-то о count (*) и прочем. Вы можете вкладывать агрегаты, хотя бы немного. Пересмотренные программы, приведенные ниже, дают те же результаты, что и оригиналы, так что это немного лучше. Я до сих пор интуитивно понимаю, что мне не хватает чего-то очевидного, что могло бы сделать все это значительно проще. Есть идеи? Для записи вот новые версии запросов с одним и двумя столбцами. Эти длинные операторы ORDER BY в нижней части каждого есть только для того, чтобы я мог легко получать и анализировать результаты исходного и пересмотренного запросов.
Вот новый запрос с одним столбцом, намного короче:
select distinct n as value,
ntile(10) over (order by n) as value_decile,
count(*) as frequency_count,
ntile(10) over (order by count(*)) as frequency_decile
from mock
group by n
order by 1,2,3,4
Версия с двумя столбцами использует один CTE на столбец, а затем объединяет все вместе с главной таблицей.
- Получить данные для столбца num_inst.
with
num_inst_information as (
select distinct num_inst,
ntile(10) over (order by num_inst) as value_decile,
count(*) as frequency_count,
ntile(10) over (order by count(num_inst)) as frequency_decile
from mock2
group by num_inst
),
-- Get the details for the points column.
points_information as (
select distinct points,
ntile(10) over (order by points) as value_decile,
count(*) as frequency_count,
ntile(10) over (order by count(points)) as frequency_decile
from mock2
group by points
)
-- Get every row in the base table and use the CTEs above for lookups (joins) with the extra data.
select mock2.num_inst,
num_inst_information.value_decile as num_inst_value_decile,
num_inst_information.frequency_decile num_inst_frequency_decile,
mock2.points,
points_information.value_decile as points_value_decile,
points_information.frequency_decile as points_frequency_decile
from mock2
left join num_inst_information on (num_inst_information.num_inst = mock2.num_inst)
left join points_information on (points_information.points = mock2.points)
order by 1,2,3,4,5,6
Новые версии немного быстрее, что тоже неплохо.
S-Man запросил пример данных и вывод. Справедливо! И спасибо за чтение и хочу помочь. Я создал учетную запись Pastebin с образцами из 1 столбца
https://pastebin.com/embed_js/eUZkBqhA
и данные с 2 столбцами:
https://pastebin.com/embed_js/7J4vx850
Но, если честно, это просто случайные числа. Цель моего вопроса - найти краткий и эффективный способ добавления производных общих данных в каждую строку:
Получить исходное значение из каждой записи в выводе. 1M строк в таблице, 1M строк в результате.
Для каждой строки добавьте три новых столбца к выводу для каждого «реального» столбца данных:
1) процентиль значения.
2) частота значения (кол.)
3) процентиль частоты.
Так, например,
num_inst
Реальные данные из базовой таблицы.
num_inst_value_percentile
Процентиль (использованные выше децили, отмечено) для этого num_inst среди всех num_inst в таблице.
num_inst_frequency
Как часто это значение появляется во всей таблице? Итак, граф.
num_inst_frequency_percentile
Процентиль (использованные выше децили, отмечено) для этого num_inst_frequency среди всех num_inst_frequencies в таблице.
... и то же самое для поля базовых точек и многих, многих других в нескольких таблицах.
На случай, если кому-то интересно, наши данные довольно длинны и их трудно составить. Объединение данных в процентили облегчает вычисление формы данных / распределений. После того, как я закончу с этим, следующим шагом будет выяснить, как использовать оконные функции (я полагаю), чтобы получить размеры диапазона для каждого процентиля.
Надеюсь, это понятнее!