Степенное распределение в T-SQL
-
28-09-2019 - |
Вопрос
мне в принципе нужен ответ на этот вопрос SO, который обеспечивает степенное распределение, переведенный для меня на T-SQL.
Я хочу извлечь фамилию по одной из перепись предоставила таблицу имен.Я хочу получить примерно такое же распределение, как и в популяции.В таблице 88 799 имен, ранжированных по частоте.«Смит» занимает 1-й ранг с частотой 1,006%, «Альдеринк» — 88 799-го ранга с частотой 1,7 x 10^-6.«Сандерс» занимает 75 место с частотой 0,100%.
Кривая вообще не обязательно должна точно совпадать.Просто дайте мне примерно 1% "Смит" и примерно 1 на миллион "Алдеринк"
Вот что у меня есть на данный момент.
SELECT [LastName]
FROM [LastNames] as LN
WHERE LN.[Rank] = ROUND(88799 * RAND(), 0)
Но это, конечно, дает равномерное распределение.
Обещаю, что к тому времени, когда мне ответит более умный человек, я все еще буду пытаться разобраться в этом сам.
Решение
Зачем соглашаться на степенное распределение, если можно извлечь данные из фактического распределения?
Я предлагаю вам изменить таблицу LastNames, включив в нее числовой столбец, который будет содержать числовое значение, представляющее фактическое количество лиц с более распространенным именем.Вероятно, вам понадобится число в меньшем, но пропорциональном масштабе, скажем, 10 000 на каждый процент представительства.
Тогда список будет выглядеть примерно так:
(кроме трех имен, упомянутых в вопросе, я предполагаю, что это Уайт, Джонсон и др.)
Smith 0
White 10,060
Johnson 19,123
Williams 28,456
...
Sanders 200,987
..
Alderink 999,997
И выбор имени будет
SELECT TOP 1 [LastName]
FROM [LastNames] as LN
WHERE LN.[number_described_above] < ROUND(100000 * RAND(), 0)
ORDER BY [number_described_above] DESC
Это выбор первого имени, число которого не превышает случайное число [равномерное распределение].Обратите внимание, как запрос использует меньше, чем и заказ в описание- окончание заказа;это будет гарантией того, что будет выбрана самая первая запись (Смит).Альтернативой было бы начать серию со Смита с 10 060, а не с нуля, и отбросить случайные розыгрыши, меньшие этого значения.
Помимо упомянутого выше вопроса об управлении границами (начиная с нуля, а не с 10 060), это решение, наряду с двумя другими ответами на данный момент, такое же, как и решение, предложенное в Дмкиответ на вопрос, указанный в этом вопросе.По сути, идея состоит в том, чтобы использовать CDF (Кумулятивная функция распределения).
Редактировать:
Если вы настаиваете на использовании математическая функция, а не фактическое распределение, следующее должно предоставить степенную функцию, которая каким-то образом передавала бы форму «длинного хвоста» реального распределения.Возможно, вы захотите настроить значение @PwrCoef (которое, кстати, не обязательно должно быть целым числом), по существу, чем больше коэффициент, тем больше смещена к началу списка функция.
DECLARE @PwrCoef INT
SET @PwrCoef = 2
SELECT 88799 - ROUND(POWER(POWER(88799.0, @PwrCoef) * RAND(), 1.0/@PwrCoef), 0)
Примечания:
- дополнительные «.0» в приведенной выше функции важны для того, чтобы заставить SQL выполнять операции с плавающей точкой, а не с целыми числами.
- причина, по которой мы вычитаем вычисление мощности из 88799, заключается в том, что распределение вычислений таково: чем ближе число к концу нашей шкалы, тем больше вероятность, что оно будет нарисовано.Список фамилий сортируется в обратном порядке (скорее всего, сначала имена), нам нужно это вычитание.
Предположим, что степень равна, скажем, 3, тогда запрос будет выглядеть примерно так:
SELECT [LastName]
FROM [LastNames] as LN
WHERE LN.[Rank]
= 88799 - ROUND(POWER(POWER(88799.0, 3) * RAND(), 1.0/3), 0)
Какой запрос из вопроса, кроме последней строки.
Повторное редактирование:
Если посмотреть на фактическое распределение, как видно из данных переписи, кривая чрезвычайно крутая и потребует очень большой коэффициент мощности, что, в свою очередь, может привести к переполнению и/или экстремальным ошибкам округления. в наивной формуле, показанной выше.
Более разумным подходом может быть работа на нескольких уровнях, т.е.выполнить одинаковое количество розыгрышей в каждом из, скажем, трёх третей (или четырёх четвертей, или...) совокупного распределения;в каждом из этих списков частей мы будем рисовать, используя степенную функцию, возможно, с одним и тем же коэффициентом, но с разными диапазонами.
Например
Если предположить третьи, список делится следующим образом:
- Первая треть = 425 имен, от Смита до Альварадо.
- Вторая треть = 6277 имен, от до Гейнера
- Последняя треть = 82 097 имен, от Фрисби до конца.
Если бы нам потребовалось, скажем, 1000 имен, мы бы взяли 334 из верхней трети списка, 333 из второй трети и 333 из последней трети.
Для каждой трети мы бы использовали аналогичную формулу, возможно, с большим коэффициентом мощности для первой трети (мы действительно заинтересованы в том, чтобы отдать предпочтение более ранним именам в списке, и также там, где относительные частоты более статистически значимы).Три запроса выбора могут выглядеть следующим образом:
-- Random Drawing of a single Name in top third
-- Power Coef = 12
SELECT [LastName]
FROM [LastNames] as LN
WHERE LN.[Rank]
= 425 - ROUND(POWER(POWER(425.0, 12) * RAND(), 1.0/12), 0)
-- Second third; Power Coef = 7
...
WHERE LN.[Rank]
= (425 + 6277) - ROUND(POWER(POWER(6277.0, 7) * RAND(), 1.0/7), 0)
-- Bottom third; Power Coef = 4
...
WHERE LN.[Rank]
= (425 + 6277 + 82097) - ROUND(POWER(POWER(82097.0, 4) * RAND(), 1.0/4), 0)
Другие советы
Вместо сохранения PDF-файла в качестве ранга сохраните CDF (сумму всех частот до этого имени, начиная с Альдекирка).
Затем измените свой выбор, чтобы получить первый LN с рангом, превышающим результат вашей формулы.
Я прочитал вопрос так: «Мне нужно получить поток имен, который будет отражать частоту фамилий из переписи населения США 1990 года».
Возможно, я прочитал этот вопрос немного иначе, чем другие предложения, и хотя ответ был принят, и это очень подробный ответ, я поделюсь своим опытом работы с фамилиями переписи населения.
Я скачал те же данные из переписи 1990 года.Моя цель состояла в том, чтобы создать большое количество имен для отправки на поисковое тестирование во время тестирования производительности приложения для медицинских записей.Фамилии и процент частоты я вставил в таблицу.Я добавил столбец и заполнил его целым числом, которое было произведением «общее количество требуемых имен * частота».Данные о частоте переписи населения не давали в сумме ровно 100%, поэтому мое общее количество имен также было немного меньше требуемого.Мне удалось исправить число, выбирая случайные имена из списка и увеличивая их количество до тех пор, пока я не получил точно необходимое число, случайно добавленное количество никогда не превышало 0,05% от общего числа в 10 миллионов.
Я сгенерировал 10 миллионов случайных чисел в диапазоне от 1 до 88799.Для каждого случайного числа я выбирал это имя из списка и уменьшал счетчик этого имени.Мой подход заключался в том, чтобы имитировать раздачу колоды карт, за исключением того, что в моей колоде было гораздо больше разных карт и разное количество каждой карты.
Сохраняете ли вы реальные частоты вместе с рангами?
Преобразование алгебры из принятого ответа в MySQL не составит труда, если вы знаете, какие значения использовать для n
. y
будет то, что у вас есть сейчас ROUND(88799 * RAND(), 0)
и x0,x1 = 1,88799
Я так думаю, хотя могу неправильно это понять.Единственный нестандартный математический оператор, задействованный с точки зрения T-SQL, — это ^
что просто POWER(x,y) == x^y
.