Степенное распределение в T-SQL - PullRequest
2 голосов
/ 24 ноября 2010

Мне в основном нужен ответ на этот вопрос SO, который обеспечивает степенное распределение , переведенное для меня в T-SQL.

Я хочу получить фамилию, одну ввремя из предоставленной переписи таблицы имен .Я хочу получить примерно такое же распределение, как в популяции.Таблица насчитывает 88 799 имен, ранжированных по частоте.«Смит» - это ранг 1 с частотой 1,006%, «Алдеринк» - это ранг 88,799 с частотой 1,7 х 10 ^ -6.«Сандерс» занимает 75-е место с частотой 0,100%.

Кривая совсем не обязательно должна соответствовать.Просто дайте мне около 1% «Смита» и около 1 на миллион «Alderink»

Вот что у меня есть.

SELECT [LastName]
FROM [LastNames] as LN
WHERE LN.[Rank] = ROUND(88799 * RAND(), 0)

Но это, конечно, дает равномерное распределение.

Обещаю, я все еще буду пытаться понять это к тому времени, когда умный человек ответит.

Ответы [ 4 ]

3 голосов
/ 24 ноября 2010

Зачем соглашаться на степенное распределение, когда вы можете извлечь из фактического распределения?

Я предлагаю вам изменить таблицу LastNames, включив в нее числовой столбец, который будет содержать числовое значение, представляющее фактическое число индивидов с более распространенным именем. Вы, вероятно, захотите число в меньшей, но пропорциональной шкале, скажем, возможно, 10000 для каждого процента представительства.

Список будет выглядеть примерно так:
(кроме трех имен, упомянутых в вопросе, я предполагаю, что Уайт, Джонсон и др.)

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

Это выбор первого имени, число которого не превышает случайное число [равномерное распределение]. Обратите внимание, как в запросе используется меньше и порядок в desc -конечный порядок; это гарантирует, что будет выбрана самая первая запись (Смит). Альтернативой было бы начать серию со Смита с 10 060, а не с нуля, и отбросить случайные ничьи, меньшие, чем это значение.

Помимо упомянутого выше вопроса об управлении границами (начиная с нуля, а не 10 060), это решение, наряду с двумя другими ответами на данный момент, совпадает с предложенным в dmckee . ответ на вопрос, указанный в этом вопросе. По сути, идея заключается в использовании 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)

Это запрос из вопроса, за исключением последней строки.

Re-Edit
При рассмотрении фактического распределения, как видно из данных переписи, кривая является чрезвычайно крутой и потребует очень большой коэффициент мощности, что, в свою очередь, приведет к переполнению и / или крайним ошибкам округления в наивной формуле показано выше.
Более разумный подход может состоять в том, чтобы работать на нескольких уровнях, то есть выполнять одинаковое количество розыгрышей в каждой из, скажем, трех третей (или четырех четвертей или ...) совокупного распределения; в каждом из этих списков частей мы будем рисовать с использованием степенной функции, возможно, с тем же коэффициентом, но с разными диапазонами.
Например
Если взять трети, список делится следующим образом:

  • Первая треть = 425 имен, от Смита до Альварадо
  • Второй третий = 6 277 имен, от Гейнера
  • Последняя треть = 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)
2 голосов
/ 24 ноября 2010

Вместо того, чтобы сохранять pdf в качестве ранга, сохраните CDF (сумму всех частот до этого имени, начиная с Aldekirk).

Затем измените ваш выбор, чтобы получить первый LN с рангом больше, чем ваша формуларезультат.

1 голос
/ 25 ноября 2010

Я читаю вопрос как «Мне нужно получить поток имен, который будет отражать частоту фамилий из переписи населения США 1990 года»

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

Я скачал те же данные из переписи 1990 года. Моя цель состояла в том, чтобы создать большое количество имен, которые будут представлены для поискового тестирования во время тестирования производительности приложения медицинской карты. Я вставил фамилии и процент частоты в таблицу. Я добавил столбец и заполнил его целым числом, которое было произведено как «общее количество имен, требуемых * частота». Данные о частоте переписи не составляли ровно 100%, поэтому мое общее количество имен также не соответствовало требованиям. Я смог исправить число, выбрав случайные имена из списка и увеличивая их количество до тех пор, пока у меня не будет точно необходимого числа. Случайно добавленное число никогда не превышало 0,05% от общего числа 10 миллионов.

Я сгенерировал 10 миллионов случайных чисел в диапазоне от 1 до 88799. С каждым случайным числом я выбирал это имя из списка и уменьшал счетчик для этого имени. Мой подход состоял в том, чтобы смоделировать раздачу колоды карт, за исключением того, что в моей колоде было гораздо больше разных карт и разное количество каждой карты.

0 голосов
/ 24 ноября 2010

Сохраняете ли вы фактические частоты с рангами?

Преобразование алгебры из этого принятого ответа в MySQL не составляет никакого труда, если вы знаете, какие значения использовать для n.y было бы тем, что у вас сейчас есть ROUND(88799 * RAND(), 0) и x0,x1 = 1,88799 Я думаю, хотя я мог бы неправильно это понять.Единственный нестандартный математический оператор, задействованный с точки зрения T-SQL, - это ^, то есть просто POWER(x,y) == x^y.

...