varianza rango percentiles percentil moda mediana intervalos cuartiles confianza python numpy scipy median percentile

python - rango - Asigne cada valor de la lista a su percentil correspondiente



varianza en python (8)

Me gustaría crear una función que tome una lista (ordenada) como su argumento y muestre una lista que contenga el percentil correspondiente de cada elemento.

Por ejemplo, fn([1,2,3,4,17]) devuelve [0.0, 0.25, 0.50, 0.75, 1.00] .

¿Alguien puede por favor ya sea:

  1. Ayudame a corregir mi código a continuación? O
  2. ¿Ofrecer una mejor alternativa que mi código para asignar valores en una lista a sus percentiles correspondientes?

Mi código actual:

def median(mylist): length = len(mylist) if not length % 2: return (mylist[length / 2] + mylist[length / 2 - 1]) / 2.0 return mylist[length / 2] ############################################################################### # PERCENTILE FUNCTION ############################################################################### def percentile(x): """ Find the correspoding percentile of each value relative to a list of values. where x is the list of values Input list should already be sorted! """ # sort the input list # list_sorted = x.sort() # count the number of elements in the list list_elementCount = len(x) #obtain set of values from list listFromSetFromList = list(set(x)) # count the number of unique elements in the list list_uniqueElementCount = len(set(x)) # define extreme quantiles percentileZero = min(x) percentileHundred = max(x) # define median quantile mdn = median(x) # create empty list to hold percentiles x_percentile = [0.00] * list_elementCount # initialize unique count uCount = 0 for i in range(list_elementCount): if x[i] == percentileZero: x_percentile[i] = 0.00 elif x[i] == percentileHundred: x_percentile[i] = 1.00 elif x[i] == mdn: x_percentile[i] = 0.50 else: subList_elementCount = 0 for j in range(i): if x[j] < x[i]: subList_elementCount = subList_elementCount + 1 x_percentile[i] = float(subList_elementCount / list_elementCount) #x_percentile[i] = float(len(x[x > listFromSetFromList[uCount]]) / list_elementCount) if i == 0: continue else: if x[i] == x[i-1]: continue else: uCount = uCount + 1 return x_percentile

Actualmente, si envío el percentile([1,2,3,4,17]) , se devuelve la lista [0.0, 0.0, 0.5, 0.0, 1.0] .


Versión numpy pura de la solución de Kevin

Como dijo Kevin, la solución óptima funciona en el tiempo O (n log (n)). Aquí está la versión rápida de su código en numpy , que funciona casi al mismo tiempo que stats.rankdata :

percentiles = numpy.argsort(numpy.argsort(array)) * 100. / (len(array) - 1)

PD. Este es uno de mis trucos favoritos en numpy .


Creo que quieres scipy.stats.percentileofscore

Ejemplo:

percentileofscore([1, 2, 3, 4], 3) 75.0 percentiles = [percentileofscore(data, i) for i in data]


Creo que su entrada / salida de ejemplo no se corresponde con las formas típicas de calcular el percentil. Si calcula el percentil como "proporción de puntos de datos estrictamente menores que este valor", entonces el valor superior debería ser 0.8 (ya que 4 de 5 valores son menores que el más grande). Si lo calcula como "porcentaje de puntos de datos inferiores o iguales a este valor", entonces el valor inferior debería ser 0.2 (ya que 1 de 5 valores es igual al más pequeño). Por lo tanto, los percentiles serían [0, 0.2, 0.4, 0.6, 0.8] o [0.2, 0.4, 0.6, 0.8, 1] . Su definición parece ser "el número de puntos de datos estrictamente inferior a este valor, considerado como una proporción de la cantidad de puntos de datos no equivalentes a este valor", pero en mi experiencia esta no es una definición común (consulte, por ejemplo, wikipedia ) .

Con las definiciones percentiles típicas, el percentil de un punto de datos es igual a su rango dividido por el número de puntos de datos. (Véase, por ejemplo, esta pregunta en Stats SE preguntando cómo hacer lo mismo en R.) Diferencias en cómo calcular el monto percentil a las diferencias en cómo calcular el rango (por ejemplo, cómo clasificar los valores ligados). La función scipy.stats.percentileofscore proporciona cuatro formas de calcular percentiles:

>>> 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]

(Utilicé un conjunto de datos que contenía vínculos para ilustrar lo que sucede en tales casos).

El método de "rango" asigna a los grupos empatados un rango igual al promedio de los rangos que cubrirían (es decir, un empate tripartito para el 2do lugar obtiene un rango de 3 porque "ocupa" los rangos 2, 3 y 4). El método "débil" asigna un percentil basado en la proporción de puntos de datos menores o iguales a un punto dado; "estricto" es el mismo pero cuenta la proporción de puntos estrictamente menor que el punto dado. El método "malo" es el promedio de los dos últimos.

Como señaló Kevin H. Lin, llamar percentileofscore en un ciclo es ineficiente ya que tiene que recalcular los rangos en cada pase. Sin embargo, estos cálculos de percentiles se pueden replicar fácilmente utilizando diferentes métodos de clasificación proporcionados por scipy.stats.rankdata , lo que le permite calcular todos los percentiles a la vez:

>>> 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])

En el último caso, los rangos se ajustan en uno para que comiencen desde 0 en lugar de 1. (He omitido "mean", pero podría obtenerse fácilmente promediando los resultados de los últimos dos métodos).

Hice algunos tiempos Con pequeños datos como el de su ejemplo, el uso de datos de rankdata es algo más lento que la solución de Kevin H. Lin (presumiblemente debido a las incrustaciones indirectas al convertir las cosas en matrices numpy bajo el capó) pero más rápido que llamar al percentileofscore de la percentileofscore en un bucle como en reptilicus responder:

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

Con un gran conjunto de datos, sin embargo, la ventaja de rendimiento de Numpy tiene efecto y el uso de datos de rankdata es 10 veces más rápido que la list_to_percentiles de 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

Esta ventaja solo se volverá más pronunciada en conjuntos de datos cada vez más grandes.


En términos de complejidad, creo que la respuesta de reptilicus no es óptima. Toma O (n ^ 2) tiempo.

Aquí hay una solución que toma el tiempo 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

No estoy seguro, pero creo que esta es la complejidad de tiempo óptima que puede obtener. La razón principal por la que creo que es óptima es porque la información de todos los percentiles es esencialmente equivalente a la información de la lista ordenada, y no se puede obtener más que O (n log n) para la clasificación.

EDITAR: Dependiendo de su definición de "percentil", esto no siempre da el resultado correcto. Vea la respuesta de BrenBarn para obtener más explicaciones y una mejor solución que utiliza scipy / numpy.


Esta versión también permite pasar los valores percentiles exactos utilizados para la clasificación:

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))

Por lo tanto, es posible averiguar cuál es el valor numérico percentil para los percentiles proporcionados:

_x = np.random.randn(100, 1) what_pctl_number_of(_x, 1.6, [25, 50, 75, 100])

Salida:

3

entonces llega al rango 75 ~ 100


Para mí, la mejor solución es usar sklearn.preprocessing en 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)

Aquí está la salida

Input= [1, 2, 3, 4, 17] Output= [ 0. 0.25 0.5 0.75 1. ]

Nota: esta función tiene dos características destacadas:

  1. los datos sin procesar de entrada NO están necesariamente ordenados.
  2. los datos sin procesar de entrada NO son necesariamente una sola columna.

Si te entiendo correctamente, todo lo que quieres hacer es definir el percentil que este elemento representa en la matriz, la cantidad de la matriz que está antes de ese elemento. como en [1, 2, 3, 4, 5] debería ser [0.0, 0.25, 0.5, 0.75, 1.0]

Creo que tal código será suficiente:

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]


esto podría parecer demasiado limitado, pero ¿qué pasa con esto?

def percentile(x): pc = float(1)/(len(x)-1) return ["%.2f"%(n*pc) for n, i in enumerate(x)]

EDITAR:

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]