Redondear al int más cercano con numpy.rint () no es consistente para .5

la ronda int de numpy no parece ser consistente con la forma en que se trata con xxx.5

In [2]: np.rint(1.5) Out[2]: 2.0 In [3]: np.rint(10.5) Out[3]: 10.0 

1.5 se redondea hacia arriba mientras que 10.5 se redondea hacia abajo. ¿Hay alguna razón para esto? ¿Es justo y artefacto de la inexactitud de las carrozas ?

Editar

¿Hay alguna forma de obtener la funcionalidad deseada donde n.5 se redondea, es decir, a n + 1 para n = par o impar?

Por lo tanto, este tipo de comportamiento (como se señala en los comentarios), es una forma muy tradicional de redondeo, visto en el método de la mitad redonda, incluso . También conocido (según David Heffernan) como redondeo de banqueros. La documentación sobre este comportamiento implica que están usando este tipo de redondeo, pero también implica que puede haber problemas con la forma en que interactúa el numpy en el formato de punto flotante IEEE. (mostrado a continuación)

 Notes ----- For values exactly halfway between rounded decimal values, Numpy rounds to the nearest even value. Thus 1.5 and 2.5 round to 2.0, -0.5 and 0.5 round to 0.0, etc. Results may also be surprising due to the inexact representation of decimal fractions in the IEEE floating point standard [1]_ and errors introduced when scaling by powers of ten. 

Sea o no ese el caso, honestamente no lo sé. Sé que grandes porciones del núcleo numpy todavía están escritas en FORTRAN 77, que es anterior al estándar IEEE (establecido en 1984), pero no conozco suficiente FORTRAN 77 para decir si hay algún problema con la interfaz aquí.

Si lo que busca es simplemente redondear, la función np.ceil (función de techo en general), lo hará. Si está buscando lo contrario (siempre redondeando hacia abajo), la función np.floor lo logrará.

El redondeo desordenado se redondea hacia el par, pero los otros modos de redondeo se pueden express utilizando una combinación de operaciones.

 >>> a=np.arange(-4,5)*0.5 >>> a array([-2. , -1.5, -1. , -0.5, 0. , 0.5, 1. , 1.5, 2. ]) >>> np.floor(a) # Towards -inf array([-2., -2., -1., -1., 0., 0., 1., 1., 2.]) >>> np.ceil(a) # Towards +inf array([-2., -1., -1., -0., 0., 1., 1., 2., 2.]) >>> np.trunc(a) # Towards 0 array([-2., -1., -1., -0., 0., 0., 1., 1., 2.]) >>> a+np.copysign(0.5,a) # Shift away from 0 array([-2.5, -2. , -1.5, -1. , 0.5, 1. , 1.5, 2. , 2.5]) >>> np.trunc(a+np.copysign(0.5,a)) # 0.5 towards higher magnitude round array([-2., -2., -1., -1., 0., 1., 1., 2., 2.]) 

En general, los números de la forma n.5 se pueden representar con precisión mediante un punto flotante binario (son m.1 en binario, como 0.5 = 2 ** – 1), pero los cálculos que se esperan que los scopen. Por ejemplo, los poderes negativos de diez no están representados exactamente:

 >>> (0.1).as_integer_ratio() (3602879701896397, 36028797018963968) >>> [10**n * 10**-n for n in range(20)] [1, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.9999999999999999, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] 

De hecho, este es exactamente el redondeo especificado por el estándar de punto flotante IEEE IEEE 754 (1985 y 2008). Se pretende hacer redondeado imparcial. En la teoría de probabilidad normal, un número aleatorio entre dos enteros tiene una probabilidad cero de ser exactamente N + 0.5, por lo que no debería importar cómo se redondea porque ese caso nunca ocurre. Pero en los progtwigs reales, los números no son aleatorios y N + 0.5 ocurre con bastante frecuencia. (De hecho, debe redondear 0.5 cada vez que un número de punto flotante pierde 1 bit de precisión). Si siempre redondea 0.5 hasta el siguiente número más grande, entonces es probable que el promedio de los números redondeados sea ligeramente mayor que el promedio de los números no redondeados: este sesgo o deriva puede tener efectos muy negativos en algunos algoritmos numéricos y hacerlos inexactos.

La razón para redondear a par es mejor que redondear a impar es que se garantiza que el último dígito sea cero, por lo que si tiene que dividir por 2 y redondear nuevamente, no perderá ninguna información.

En resumen, este tipo de redondeo es lo mejor que los matemáticos han podido idear, y debe QUERERLO en la mayoría de las circunstancias. Ahora todo lo que tenemos que hacer es conseguir que las escuelas comiencen a enseñarlo a los niños.

La función de redondeo incorporada parece hacer lo que quieres, aunque solo funciona en escalares:

 def correct_round(x): try: y = [ round(z) for z in x ] except: y = round(x) return y 

y luego para verificar:

 print correct_round([-2.5,-1.5,-0.5,0.5,1.5,2.5]) > [-3.0, -2.0, -1.0, 1.0, 2.0, 3.0] 

Una respuesta a tu edición:

 y = int(np.floor(n + 0.5))