Метод Ruby on Rails для расчета процентилей - можно ли его реорганизовать? - PullRequest
2 голосов
/ 09 июля 2009

Я написал метод для вычисления данного процентиля для набора чисел для использования в приложении, которое я создаю. Обычно пользователю необходимо знать 25-й процентиль данного набора чисел и 75-й процентиль.

Мой метод следующий:

def calculate_percentile(array,percentile)
 #get number of items in array
 return nil if array.empty?

 #sort the array
 array.sort!

 #get the array length
 arr_length = array.length

 #multiply items in the array by the required percentile (e.g. 0.75 for 75th percentile)
 #round the result up to the next whole number
 #then subtract one to get the array item we need to return
 arr_item = ((array.length * percentile).ceil)-1

 #return the matching number from the array
 return array[arr_item]

end

Это похоже на результаты, которые я ожидал, но может ли кто-нибудь рефакторинг этого или предложить улучшенный метод для возврата определенных процентилей для набора чисел?

Ответы [ 4 ]

12 голосов
/ 09 июля 2009

Некоторые замечания:

  • Если определенного индекса Array не существует, [] вернет nil, поэтому ваша первоначальная проверка пустого Array не требуется.
  • Вы не должны sort! аргумент Array, потому что вы влияете на порядок элементов в Array в коде, который вызвал ваш метод . Вместо этого используйте sort (без !).
  • Вы фактически не используете arr_length после назначения.
  • Инструкция return в последней строке не нужна в Ruby.
  • Не существует стандартного определения для функции процентили (может быть много тонкостей с округлением), поэтому я просто предположу, что то, как вы это реализовали, соответствует вашему поведению. Поэтому я не могу комментировать логику.

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

def calculate_percentile(array, percentile)
  array.sort[(percentile * array.length).ceil - 1]
end
1 голос
/ 09 июля 2009

Вот тот же рефакторинг в один лайнер. Вам не нужен явный return в качестве последней строки в Ruby. Возвращаемое значение последнего оператора метода - это то, что возвращается.

def calculate_percentile(array=[],percentile=0.0)
  # multiply items in the array by the required percentile 
  # (e.g. 0.75 for 75th percentile)
  # round the result up to the next whole number
  # then subtract one to get the array item we need to return
  array ? array.sort[((array.length * percentile).ceil)-1] : nil
end
0 голосов
/ 02 октября 2016

Не уверен, стоит ли оно того, но вот как я это сделал для квартилей:

def median(list)
  (list[(list.size - 1) / 2] + list[list.size / 2]) / 2
end

numbers = [1, 2, 3, 4, 5, 6]

if numbers.size % 2 == 0
  puts median(numbers[0...(numbers.size / 2)])
  puts median(numbers)
  puts median(numbers[(numbers.size / 2)..-1])
else
  median_index = numbers.index(median(numbers))
  puts median(numbers[0..(median_index - 1)])
  puts median(numbers)
  puts median(numbers[(median_index + 1)..-1])
end
0 голосов
/ 09 июля 2009

Если вы вычисляете оба квартиля, вы можете переместить «сортировку» за пределы функции, так что это нужно сделать только один раз. Это также означает, что вы не изменяете данные вызывающего абонента (сортируете!) И не делаете копию каждый раз, когда вызывается функция (сортировка).

Я знаю, преждевременная оптимизация и все такое. И немного неудобно говорить функции: «массив должен быть отсортирован перед вызовом этой функции». Поэтому разумно оставить все как есть.

Но сортировка уже отсортированных данных займет значительно больше времени, чем все остальные функции, вместе взятые (*). Он также имеет более высокую алгоритмическую сложность: в лучшем случае O (N), когда функция может быть O (1) для второго квартиля (хотя O (N log N) для первого, конечно, если данные еще не отсортированы) , Поэтому стоит избегать, если производительность может быть проблемой для этой функции.

Существуют несколько более быстрые способы поиска двух квартилей, чем полная сортировка (см. «Алгоритмы выбора»). Например, если вы знакомы с тем, как qsort использует опорные точки, обратите внимание, что если вам нужно знать 25-й и 75-й элементы из 100, и ваш опорный пункт на каком-то этапе заканчивается в позиции 80, то нет абсолютно никакого смысла возвращаться в блок выше оси. Вам действительно все равно, в каком порядке находятся эти элементы, просто они находятся в верхнем квартиле. Но это значительно увеличит сложность кода по сравнению с простым вызовом библиотеки для сортировки для вас. Если вам не нужно небольшое повышение производительности, я думаю, что вы хороши как есть.

(*) Если у ruby-массивов нет флага, чтобы помнить, что они уже отсортированы и с тех пор не были изменены. Я не знаю, делают ли они это, но если это так, то используйте сортировку! второй раз, конечно, бесплатно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...