¿Cómo eliminar el signo menos extra al redondear números negativos hacia cero en números?

Tengo una pregunta simple acerca de las funciones de fix y floor en numpy . Al redondear los números negativos que son más grandes que -1 hacia cero, los numpy redondean correctamente a cero, sin embargo, deja un signo negativo. Este signo negativo interfiere con mi disfraz de función unique_rows ya que usa la ascontiguousarray para comparar elementos de la matriz y este signo perturba la singularidad. Tanto round como fix se comportan igual en este sentido.

 >>> np.fix(-1e-6) Out[1]: array(-0.0) >>> np.round(-1e-6) Out[2]: -0.0 

¿Alguna idea sobre cómo deshacerse de la señal? Pensé en usar la función np.sign pero viene con un costo computacional adicional.

Gracias por adelantado.

El problema que tienes entre -0. y +0. es parte de la especificación de cómo se supone que se comportan los flotadores (IEEE754). En alguna circunstancia, uno necesita esta distinción. Ver, por ejemplo, los documentos que están vinculados en los documentos para around .

También vale la pena señalar que los dos ceros deben compararse con iguales, por lo que

 np.array(-0.)==np.array(+0.) # True 

Es decir, creo que el problema es más probable con su comparación de singularidad . Por ejemplo:

 a = np.array([-1., -0., 0., 1.]) np.unique(a) # array([-1., -0., 1.]) 

Si desea mantener los números como punto flotante pero tener todos los ceros iguales, podría usar:

 x = np.linspace(-2, 2, 6) # array([-2. , -1.2, -0.4, 0.4, 1.2, 2. ]) y = x.round() # array([-2., -1., -0., 0., 1., 2.]) y[y==0.] = 0. # array([-2., -1., 0., 0., 1., 2.]) # or y += 0. # array([-2., -1., 0., 0., 1., 2.]) 

Sin embargo, tenga en cuenta que tiene que hacer este poco de trabajo adicional, ya que está tratando de evitar la especificación de punto flotante.

Tenga en cuenta también que esto no se debe a un error de redondeo. Por ejemplo,

 np.fix(np.array(-.4)).tostring().encode('hex') # '0000000000000080' np.fix(np.array(-0.)).tostring().encode('hex') # '0000000000000080' 

Es decir, los números resultantes son exactamente iguales, pero

 np.fix(np.array(0.)).tostring().encode('hex') # '0000000000000000' 

es diferente. Es por esto que su método no funciona, ya que compara la representación binaria de los números, que es diferente para los dos ceros. Por lo tanto, creo que el problema es más el método de comparación que la idea general de comparar los números de punto flotante para la singularidad.

Una rápida prueba de tiempo para los diferentes enfoques:

 data0 = np.fix(4*np.random.rand(1000000,)-2) # [ 1. -0. 1. -0. -0. 1. 1. 0. -0. -0. .... ] N = 100 data = np.array(data0) print timeit.timeit("data += 0.", setup="from __main__ import np, data", number=N) # 0.171831846237 data = np.array(data0) print timeit.timeit("data[data==0.] = 0.", setup="from __main__ import np, data", number=N) # 0.83500289917 data = np.array(data0) print timeit.timeit("data.astype(np.int).astype(np.float)", setup="from __main__ import np, data", number=N) # 0.843791007996 

Estoy de acuerdo con el punto de @ senderle de que si quiere comparaciones simples y exactas y puede hacerlo con intenciones, las intenciones generalmente serán más fáciles. Pero si quieres flotadores únicos, deberías poder hacer esto también, aunque necesitas hacerlo un poco más cuidadosamente. El principal problema con los flotadores es que puede tener pequeñas diferencias que pueden introducirse a partir de los cálculos y no aparecen en una print normal, pero esto no es una barrera enorme y, especialmente, no después de una round, fix, rint a un rango razonable de flotadores.

Creo que el problema fundamental es que estás usando operaciones similares a conjuntos en números de punto flotante, lo cual es algo que debes evitar como regla general , a menos que tengas una buena razón y una comprensión profunda de los números de punto flotante.

La razón obvia para seguir esta regla es que incluso una diferencia muy pequeña entre dos flotadores se registra como una diferencia absoluta, por lo que un error numérico puede hacer que las operaciones de tipo conjunto produzcan resultados inesperados. Ahora, en su caso de uso, inicialmente puede parecer que ha evitado ese problema al redondear primero, lo que limita el rango de valores posibles. Pero resulta que todavía son posibles resultados inesperados, como lo muestra este caso de esquina. Los números de punto flotante son difíciles de razonar.

Creo que la solución correcta es redondear y luego convertir a int usando astype .

 >>> a array([-0.5, 2. , 0.2, -3. , -0.2]) >>> numpy.fix(a) array([-0., 2., 0., -3., -0.]) >>> numpy.fix(a).astype(int) # could also use 'i8', etc... array([ 0, 2, 0, -3, 0]) 

Ya que ya está redondeando, esto no debería desechar ninguna información, y será más estable y predecible para las operaciones similares a conjuntos más tarde. ¡Este es uno de esos casos donde es mejor usar la abstracción correcta!

Si necesitas flotadores, siempre puedes volver a convertir. El único problema con esto es que crea otra copia; pero la mayoría de las veces eso no es realmente un problema. numpy es lo suficientemente rápido como para que la sobrecarga de la copia sea bastante pequeña!

Agregaré que si su caso realmente exige el uso de flotadores, entonces la respuesta de tom10 es buena. Pero creo que la cantidad de casos en los que tanto las operaciones de flotación como las operaciones de set-set son realmente necesarias es muy pequeña.