NumPy o Pandas: mantener el tipo de matriz como entero mientras tiene un valor NaN

160

¿Hay una forma preferida de mantener el tipo de datos de una numpymatriz fija como int( int64o lo que sea), mientras que todavía tiene un elemento dentro de la lista como numpy.NaN?

En particular, estoy convirtiendo una estructura de datos interna en un Pandas DataFrame. En nuestra estructura, tenemos columnas de tipo entero que todavía tienen NaN (pero el dtype de la columna es int). Parece relanzar todo como flotante si hacemos de esto un DataFrame, pero realmente nos gustaría serlo int.

Pensamientos?

Cosas intentadas:

Intenté usar la from_records()función en pandas.DataFrame, con coerce_float=Falsey esto no ayudó. También intenté usar matrices enmascaradas NumPy, con NaN fill_value, que tampoco funcionó. Todo esto provocó que el tipo de datos de la columna se convirtiera en flotante.

ely
fuente
¿Podría usar una matriz enmascarada numpy?
mgilson
Lo probaré. También probé la from_recordsfunción en pandas.DataFrame, con coerce_float=False, pero sin suerte ... todavía hace que los nuevos datos tengan tipo float64.
ely
1
Sí, no tuve suerte. Incluso con una matriz enmascarada, todavía se convierte en flotante. Parece que Pandas dice así: "¿Hay un NaN en alguna parte? ... Entonces todo es un flotador". Esperemos que haya una forma de evitar esto.
ely
1
El soporte opcional Nullable Integer ahora se agrega oficialmente en pandas 0.24.0 - finalmente :) - encuentre una respuesta actualizada a continuación. Notas de la versión de pandas 0.24.x
mork

Respuestas:

70

Esta capacidad se ha agregado a los pandas (a partir de la versión 0.24): https://pandas.pydata.org/pandas-docs/version/0.24/whatsnew/v0.24.0.html#optional-integer-na-support

En este punto, requiere el uso de la extensión dtype Int64 (en mayúscula), en lugar del dtype predeterminado int64 (en minúscula).

techvslife
fuente
1
Por ahora tienes que especificar un tipo especial como 'Int64'hacer que funcione. Será aún mejor cuando se habilitará de forma predeterminada.
Jean Paul
¡Esto es genial! Sin embargo, hay un pequeño problema que PyCharm no puede mostrar el marco de datos en la ventana de depuración si se usa de esta manera. Puede ver mi respuesta para otra pregunta sobre cómo forzar su visualización: stackoverflow.com/questions/38956660/… (el problema original es diferente, pero la solución para mostrar el marco de datos funciona)
Alaa M.
¿Tengo que usar 'Int64'o hay algo así 'Int8'? Utiliza una cantidad increíble de memoria en comparación con np.float.
Superdooperhero
'Int8'parece funcionar, pero np.floataún parece cargar mucho más rápido. El problema parece ser que no está liberando memoria en el medio. Suponga que el recolector de basura finalmente se ejecutará.
Superdooperhero
103

NaNno se puede almacenar en una matriz entera. Esta es una limitación conocida de los pandas en este momento; He estado esperando que se realicen progresos con los valores de NA en NumPy (similar a los NA en R), pero pasarán al menos 6 meses a un año antes de que NumPy obtenga estas características, parece:

http://pandas.pydata.org/pandas-docs/stable/gotchas.html#support-for-integer-na

(Esta característica se ha agregado a partir de la versión 0.24 de pandas, pero tenga en cuenta que requiere el uso de la extensión dtype Int64 (en mayúscula), en lugar del dtype predeterminado int64 (minúscula): https://pandas.pydata.org/pandas- docs / version / 0.24 / whatsnew / v0.24.0.html # optional-integer-na-support )

Wes McKinney
fuente
77
Hola Wes, ¿hay alguna actualización sobre esto? Nos encontramos con problemas que unen columnas se convierten en ints o flotantes, en función de la existencia de un valor de NA en la lista original. (Creando problemas más adelante al intentar fusionar estos marcos de datos)
Carst
8

Si el rendimiento no es el problema principal, puede almacenar cadenas en su lugar.

df.col = df.col.dropna().apply(lambda x: str(int(x)) )

Luego puedes mezclar NaNtodo lo que quieras. Si realmente desea tener enteros, dependiendo de su aplicación, puede usar -1, o 0, o 1234567890, o algún otro valor dedicado para representar NaN.

También puede duplicar temporalmente las columnas: una como tiene, con flotantes; el otro experimental, con ints o strings. Luego inserta assertsen cada lugar razonable comprobando que los dos están sincronizados. Después de suficientes pruebas, puede soltar los flotadores.

osa
fuente
5

Esta no es una solución para todos los casos, pero la mía (coordenadas genómicas) he recurrido al uso de 0 como NaN

a3['MapInfo'] = a3['MapInfo'].fillna(0).astype(int)

Esto al menos permite utilizar el tipo de columna 'nativo' adecuado, operaciones como la resta, la comparación, etc. funcionan como se espera

pez globo
fuente
5

Pandas v0.24 +

La funcionalidad para admitir NaNen series enteras estará disponible en v0.24 hacia arriba. Hay información sobre esto en la sección "Novedades" v0.24, y más detalles en Tipo de datos enteros anulables .

Pandas v0.23 y anteriores

En general, es mejor trabajar con floatseries siempre que sea posible, incluso cuando la serie está desviada de inta floatdebido a la inclusión de NaNvalores. Esto permite cálculos vectorizados basados ​​en NumPy donde, de lo contrario, se procesarían bucles de nivel de Python.

Los documentos sugieren : "Una posibilidad es utilizar dtype=objectmatrices en su lugar". Por ejemplo:

s = pd.Series([1, 2, 3, np.nan])

print(s.astype(object))

0      1
1      2
2      3
3    NaN
dtype: object

Por razones estéticas, por ejemplo, salida a un archivo, esto puede ser preferible.

Pandas v0.23 y anterior: antecedentes

NaNse considera afloat . Los documentos actualmente (a partir de v0.23) especifican la razón por la cual las series de enteros se convierten en float:

En ausencia de un soporte de NA de alto rendimiento integrado en NumPy desde cero, la principal víctima es la capacidad de representar NA en matrices de enteros.

Esta compensación se realiza principalmente por razones de memoria y rendimiento, y también para que la Serie resultante continúe siendo "numérica".

Los documentos también proporcionan reglas para la transmisión debido a la NaNinclusión:

Typeclass   Promotion dtype for storing NAs
floating    no change
object      no change
integer     cast to float64
boolean     cast to object
jpp
fuente
1

Solo quería agregar que en caso de que esté tratando de convertir un vector flotante (1.143) a entero (1) que tenga NA convirtiéndose al nuevo tipo 'Int64' le dará un error. Para resolver esto, debe redondear los números y luego hacer ".astype ('Int64')"

s1 = pd.Series([1.434, 2.343, np.nan])
#without round() the next line returns an error 
s1.astype('Int64')
#cannot safely cast non-equivalent float64 to int64
##with round() it works
s1.round().astype('Int64')
0      1
1      2
2    NaN
dtype: Int64

Mi caso de uso es que tengo una serie flotante que quiero redondear a int, pero cuando haces .round () queda un '* .0' al final del número, por lo que puedes dejar ese 0 desde el final por convirtiendo a int.

Pedro Moisés Camacho Ureña
fuente
0

Si hay espacios en blanco en los datos de texto, las columnas que normalmente serían números enteros se convertirán en flotantes como float64 dtype porque int64 dtype no puede manejar valores nulos. Esto puede causar un esquema inconsistente si está cargando varios archivos, algunos con espacios en blanco (que terminarán como float64 y otros sin los cuales terminarán como int64

Este código intentará convertir cualquier columna de tipo de número a Int64 (a diferencia de int64) ya que Int64 puede manejar nulos

import pandas as pd
import numpy as np

#show datatypes before transformation
mydf.dtypes

for c in mydf.select_dtypes(np.number).columns:
    try:
        mydf[c] = mydf[c].astype('Int64')
        print('casted {} as Int64'.format(c))
    except:
        print('could not cast {} to Int64'.format(c))

#show datatypes after transformation
mydf.dtypes
Kynrek
fuente