NumPy ou Pandas: Mantendo o tipo de matriz como número inteiro enquanto possui um valor NaN

160

Existe uma maneira preferida de manter o tipo de dados de uma numpymatriz fixo como int( int64ou qualquer outro), enquanto ainda tem um elemento listado como numpy.NaN?

Em particular, estou convertendo uma estrutura de dados interna em um DataFrame do Pandas. Em nossa estrutura, temos colunas do tipo inteiro que ainda possuem NaNs (mas o tipo de coluna é int). Parece reformular tudo como um float se fizermos deste um DataFrame, mas realmente gostaríamos de ser int.

Pensamentos?

As coisas tentaram:

Eu tentei usar a from_records()função em pandas.DataFrame, com coerce_float=Falsee isso não ajudou. Também tentei usar matrizes mascaradas NumPy, com NaN fill_value, que também não funcionou. Tudo isso fez com que o tipo de dados da coluna se tornasse flutuante.

ely
fonte
Você poderia usar uma matriz mascarada numpy?
mgilson
Vou dar uma chance. Eu também tentei a from_recordsfunção em pandas.DataFrame, com coerce_float=False, mas sem sorte ... ainda faz com que os novos dados tenham tipo float64.
ely
1
Sim, sem sorte. Mesmo com uma matriz mascarada, ela ainda é convertida em flutuante. Parece que o Pandas é assim: "Existe um NaN em algum lugar? ... Então tudo é uma bóia". Espero que haja uma maneira de contornar isso.
Ely
1
O Suporte Integral Nulo Opcional agora foi adicionado oficialmente ao pandas 0.24.0 - finalmente :) -, encontre uma resposta atualizada abaixo. notas de lançamento do pandas 0.24.x
mork

Respostas:

70

Este recurso foi adicionado aos pandas (começando na versão 0.24): https://pandas.pydata.org/pandas-docs/version/0.24/whatsnew/v0.24.0.html#optional-integer-na-support

Neste ponto, requer o uso da extensão dtype Int64 (maiúscula), em vez do padrão dtype int64 (minúscula).

techvslife
fonte
1
Por enquanto, você precisa especificar um tipo especial 'Int64'para fazê-lo funcionar. Será ainda melhor quando será ativado por padrão.
Jean Paul
Isso é ótimo! Há um pequeno problema, porém, de que o PyCharm falha ao exibir o quadro de dados na janela de depuração, se usado dessa maneira. Você pode ver a minha resposta para outra pergunta para como forçar exibi-lo: stackoverflow.com/questions/38956660/... (o problema original não é diferente, mas a solução para a exibição de obras trama de dados)
Alaa M.
Eu tenho que usar 'Int64'ou existe algo parecido 'Int8'? Ele usa uma quantidade insana de memória em comparação com np.float.
Superdooperhero
'Int8'parece funcionar, mas np.floatainda parece carregar muito mais rápido. Parece que o problema não está liberando memória no meio. Suponha que o coletor de lixo acabe sendo executado.
Superdooperhero
103

NaNnão pode ser armazenado em uma matriz inteira. Essa é uma limitação conhecida dos pandas no momento; Eu estava esperando o progresso com os valores de NA no NumPy (semelhante aos NAs no R), mas levará pelo menos seis meses a um ano até o NumPy obter esses recursos, parece:

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

(Esse recurso foi adicionado a partir da versão 0.24 do pandas, mas observe que ele requer o uso da extensão dtype Int64 (maiúscula), em vez do padrão dtype int64 (minúscula): https://pandas.pydata.org/pandas- docs / version / 0.24 / whatsnew / v0.24.0.html # opcional-inteiro-na-suporte )

Wes McKinney
fonte
7
Oi Wes, há alguma atualização sobre isso? Temos problemas que juntam colunas são convertidos em entradas ou flutuantes, com base na existência de um valor de NA na lista original. (Criação de problemas mais tarde ao tentar mesclar essas dataframes)
Carst
8

Se o desempenho não for o problema principal, você poderá armazenar as strings.

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

Então você pode misturar com o NaNquanto quiser. Se você realmente deseja ter números inteiros, dependendo do seu aplicativo, pode usar -1, ou 0, ou 1234567890, ou algum outro valor dedicado para representar NaN.

Você também pode duplicar temporariamente as colunas: uma como você, com flutuadores; o outro experimental, com ints ou strings. Em seguida, insere assertsem todos os locais razoáveis, verificando se os dois estão sincronizados. Após testes suficientes, você pode soltar os flutuadores.

osa
fonte
5

Esta não é uma solução para todos os casos, mas as minhas (coordenadas genômicas) recorri ao uso de 0 como NaN

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

Isso pelo menos permite que o tipo de coluna 'nativo' apropriado seja usado, operações como subtração, comparação etc. funcionam como esperado

baiacu
fonte
5

Pandas v0.24 +

A funcionalidade para suportar NaNséries inteiras estará disponível na v0.24 para cima. Há informações sobre isso na seção "Novidades" da v0.24 e mais detalhes em Tipo de dados inteiro nulo .

Pandas v0.23 e anterior

Em geral, é melhor trabalhar com floatséries onde for possível, mesmo quando a série é upcast intda floatdevido à inclusão deNaN valores. Isso permite cálculos vetorizados baseados em NumPy onde, caso contrário, os loops no nível do Python seriam processados.

Os documentos sugerem : "Uma possibilidade é usar dtype=objectmatrizes." Por exemplo:

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

print(s.astype(object))

0      1
1      2
2      3
3    NaN
dtype: object

Por razões cosméticas, por exemplo, saída para um arquivo, isso pode ser preferível.

Pandas v0.23 e anterior: plano de fundo

NaNé considerado afloat . Os documentos atualmente (a partir da v0.23) especificam o motivo pelo qual a série inteira é convertida para float:

Na ausência de suporte de NA de alto desempenho que está sendo incorporado ao NumPy desde o início, a principal vítima é a capacidade de representar NAs em matrizes inteiras.

Essa troca é feita principalmente por motivos de memória e desempenho, e também para que a Série resultante continue sendo “numérica”.

Os documentos também fornecem regras para upcasting devido à NaNinclusão:

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

Isso agora é possível, pois o pandas v 0.24.0

notas de versão do pandas 0.24.x Citação: "O Pandas ganhou a capacidade de armazenar tipos inteiros com valores ausentes.

mork
fonte
1

Só queria acrescentar que, caso você esteja tentando converter um vetor float (1.143) em número inteiro (1) que tenha NA convertendo para o novo dtype 'Int64', ocorrerá um erro. Para resolver isso, você deve arredondar os números e depois fazer ".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

Meu caso de uso é que tenho uma série flutuante que desejo arredondar para int, mas quando você faz .round () um '* .0' no final do número permanece, então você pode eliminar esse 0 do final até convertendo para int.

Pedro Moisés Camacho Ureña
fonte
0

Se houver espaços em branco nos dados de texto, as colunas que normalmente seriam números inteiros serão convertidas em flutuantes como float64 dtype porque int64 dtype não pode manipular nulos. Isso pode causar um esquema inconsistente se você estiver carregando vários arquivos, alguns com espaços em branco (que acabarão como float64 e outros sem que acabarão como int64

Esse código tentará converter qualquer coluna de tipo numérico para Int64 (em oposição a int64), pois o Int64 pode manipular 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
fonte