Ответы Doug stevenson помогли мне лучше:
TL; DR: Это предупреждающее сообщение. если вы не выполняете исходящие вызовы, вы в порядке
Это предупреждение появляется для всех функций при выполнении плана Spark. Это просто предупреждение - вы можете игнорировать его, если вы делаете какие-либо исходящие сети в своих собственных.
Мы изучаем, как это сообщение было изменено или удалено, так что оно менее запутанно.
[...]
blockquote>
Я думаю, что ваш пример ввода / вывода не соответствует типичным способам вычисления процентиля. Если вы вычисляете процентиль как «пропорцию точек данных, строго меньших этого значения», то верхнее значение должно быть 0,8 (поскольку 4 из 5 значений меньше самого большого). Если вы подсчитаете его как «процент точек данных, меньших или равных этому значению», то нижнее значение должно быть 0,2 (поскольку 1 из 5 значений равно наименьшему). Таким образом, процентили будут [0, 0.2, 0.4, 0.6, 0.8]
или [0.2, 0.4, 0.6, 0.8, 1]
. Ваше определение, по-видимому, представляет собой «количество точек данных, строго меньшее этого значения, которое рассматривается как доля от числа точек данных, не равных этому значению», но, по моему опыту, это не общее определение (см., Например, wikipedia ).
С типичными определениями процентилей процентиль точки данных равен ее рангу, деленному на количество точек данных. (См., Например, этот вопрос по статистике SE, в котором просят, как сделать то же самое в R.) Различия в том, как вычислить процентную долю в различиях в том, как вычислить ранг (например, как ранжировать привязанные значения). Функция scipy.stats.percentileofscore
предоставляет четыре способа вычисления процентилей:
>>> x = [1, 1, 2, 2, 17]
>>> [stats.percentileofscore(x, a, 'rank') for a in x]
[30.0, 30.0, 70.0, 70.0, 100.0]
>>> [stats.percentileofscore(x, a, 'weak') for a in x]
[40.0, 40.0, 80.0, 80.0, 100.0]
>>> [stats.percentileofscore(x, a, 'strict') for a in x]
[0.0, 0.0, 40.0, 40.0, 80.0]
>>> [stats.percentileofscore(x, a, 'mean') for a in x]
[20.0, 20.0, 60.0, 60.0, 90.0]
(я использовал набор данных, содержащий ссылки, чтобы проиллюстрировать, что происходит в таких случаях.)
Метод «ранга» присваивает связанным группам ранг, равный среднему числу рангов, которые они будут покрывать (т. е. трехсторонняя связь для 2-го места получает звание 3, потому что она «занимает» звания 2, 3 и 4). «Слабый» метод присваивает процентиль, исходя из доли точек данных, меньших или равных данной точке; «строгий» - это то же самое, но счет пропорции точек строго меньше данной точки. «Средний» метод является средним из последних двух.
Как заметил Кевин Х. Линь, вызов percentileofscore
в цикле неэффективен, поскольку он должен пересчитать ряды на каждом проходе. Однако эти процентильные вычисления могут быть легко реплицированы с использованием различных методов ранжирования, предоставляемых scipy.stats.rankdata
, позволяя вам сразу вычислить процентили:
>>> from scipy import stats
>>> stats.rankdata(x, "average")/len(x)
array([ 0.3, 0.3, 0.7, 0.7, 1. ])
>>> stats.rankdata(x, 'max')/len(x)
array([ 0.4, 0.4, 0.8, 0.8, 1. ])
>>> (stats.rankdata(x, 'min')-1)/len(x)
array([ 0. , 0. , 0.4, 0.4, 0.8])
. В последнем случае ранги скорректированы вниз на один, чтобы они начинались с 0 вместо 1. (я опустил «среднее», но его можно было легко получить, усреднив результаты последних двух методов.)
I сделал некоторые тайминги. С небольшими данными, такими как в вашем примере, использование rankdata
несколько медленнее, чем решение Кевина Х. Лина (предположительно из-за того, что накладные расходы приходится на преобразование вещей в массивы numpy под капотом), но быстрее, чем вызов percentileofscore
в как и в ответе рептилия:
In [11]: %timeit [stats.percentileofscore(x, i) for i in x]
1000 loops, best of 3: 414 µs per loop
In [12]: %timeit list_to_percentiles(x)
100000 loops, best of 3: 11.1 µs per loop
In [13]: %timeit stats.rankdata(x, "average")/len(x)
10000 loops, best of 3: 39.3 µs per loop
Однако при большом наборе данных преимущество работы numpy вступает в силу, а использование rankdata
в 10 раз быстрее, чем у Кевина list_to_percentiles
:
In [18]: x = np.random.randint(0, 10000, 1000)
In [19]: %timeit [stats.percentileofscore(x, i) for i in x]
1 loops, best of 3: 437 ms per loop
In [20]: %timeit list_to_percentiles(x)
100 loops, best of 3: 1.08 ms per loop
In [21]: %timeit stats.rankdata(x, "average")/len(x)
10000 loops, best of 3: 102 µs per loop
Это преимущество будет только более выраженным в больших и больших наборах данных.
Как сказал Кевин, оптимальное решение работает в O (n log (n)) времени. Вот быстрая версия его кода в numpy
, который работает почти в то же время, что и stats.rankdata
:
percentiles = numpy.argsort(numpy.argsort(array)) * 100. / (len(array) - 1)
PS. Это один из моих любимых трюков в numpy
.
это может показаться излишним, но как насчет этого:
def percentile(x):
pc = float(1)/(len(x)-1)
return ["%.2f"%(n*pc) for n, i in enumerate(x)]
EDIT:
def percentile(x):
unique = set(x)
mapping = {}
pc = float(1)/(len(unique)-1)
for n, i in enumerate(unique):
mapping[i] = "%.2f"%(n*pc)
return [mapping.get(el) for el in x]
С точки зрения сложности, я думаю, что ответ Рептилия не является оптимальным. Это время O (n ^ 2).
Вот решение, которое принимает время O (n log n).
def list_to_percentiles(numbers):
pairs = zip(numbers, range(len(numbers)))
pairs.sort(key=lambda p: p[0])
result = [0 for i in range(len(numbers))]
for rank in xrange(len(numbers)):
original_index = pairs[rank][1]
result[original_index] = rank * 100.0 / (len(numbers)-1)
return result
Я не уверен, но я думаю это оптимальная временная сложность, которую вы можете получить. Грубая причина, по которой я думаю, что она оптимальна, состоит в том, что информация обо всех процентилях по существу эквивалентна информации отсортированного списка, и вы не можете добиться большего, чем O (n log n) для сортировки.
EDIT: В зависимости от вашего определения «процентиль» это может не всегда давать правильный результат. См. Ответ BrenBarn для более подробного объяснения и лучшего решения, которое использует scipy / numpy.
scipy.stats.percentileofscore
, «не оптимален». Я приурочил оба подхода к timeit
, и ваша функция великолепна.
– Jubbles
12 February 2015 в 01:50
Если я правильно вас понимаю, все, что вы хотите сделать, это определить процентиль, который этот элемент представляет в массиве, насколько массив находится перед этим элементом. как в [1, 2, 3, 4, 5] должно быть [0.0, 0.25, 0.5, 0.75, 1.0]
Я считаю, что такого кода будет достаточно:
def percentileListEdited(List):
uniqueList = list(set(List))
increase = 1.0/(len(uniqueList)-1)
newList = {}
for index, value in enumerate(uniqueList):
newList[index] = 0.0 + increase * index
return [newList[val] for val in List]
percentileList([1,2,3,4,4,5,5])
, возвращается список [0.0, 0.17, 0.33, 0.5, 0.67, 0.83, 0.99]
, где я хотел бы вернуть [0.0, 0.17, 0.33, 0.50, 0.50, 1.00, 1.00]
.
– Jubbles
13 September 2012 в 21:35
0.0
и i>, причем наивысшее значение 100.0
кажется непоследовательным.
– senderle
13 September 2012 в 23:09
Эта версия позволяет также передавать точные значения процентилей, используемые для ранжирования:
def what_pctl_number_of(x, a, pctls=np.arange(1, 101)):
return np.argmax(np.sign(np.append(np.percentile(x, pctls), np.inf) - a))
Таким образом, можно узнать, какое значение имеет значение числа процентилей для предоставленных процентилей:
_x = np.random.randn(100, 1)
what_pctl_number_of(_x, 1.6, [25, 50, 75, 100])
Выход:
3
, поэтому он достигает диапазона 75 ~ 100
Я думаю, что вы хотите scipy.stats.percentileofscore
Пример:
percentileofscore([1, 2, 3, 4], 3)
75.0
percentiles = [percentileofscore(data, i) for i in data]
[percentileofscore(score) for score in original_list]
.
– Karl Knechtel
13 September 2012 в 21:55
[percentileofscore(data, i, 'weak') for i in data]
- это то, что я ищу. Очень Pythonic тоже.
– Jubbles
13 September 2012 в 22:08
Для меня лучшим решением является использование QuantileTransformer
в sklearn.preprocessing
.
from sklearn.preprocessing import QuantileTransformer
fn = lambda input_list : QuantileTransformer(100).fit_transform(np.array(input_list).reshape([-1,1])).ravel().tolist()
input_raw = [1, 2, 3, 4, 17]
output_perc = fn( input_raw )
print "Input=", input_raw
print "Output=", np.round(output_perc,2)
Вот результат
Input= [1, 2, 3, 4, 17]
Output= [ 0. 0.25 0.5 0.75 1. ]
Примечание: эта функция имеет две характерные особенности:
scipy.stats.rankdata
( github.com/scipy/scipy/blob/v0.16.0/scipy/stats/… ), вы увидите, что он используетargsort()
. Их алгоритм по существу тот же, что и мой, и разница полностью объясняется различием между списками Python и массивами numpy. – Kevin H. Lin 5 August 2015 в 21:46