Converter coluna Pandas contendo NaNs em dtype `int`

175

Eu li dados de um arquivo .csv em um dataframe do Pandas, como abaixo. Para uma das colunas, a saber id, desejo especificar o tipo de coluna como int. O problema é que a idsérie tem valores ausentes / vazios.

Quando tento converter a idcoluna em inteiro enquanto lê o arquivo .csv, recebo:

df= pd.read_csv("data.csv", dtype={'id': int}) 
error: Integer column has NA values

Como alternativa, tentei converter o tipo de coluna depois de ler como abaixo, mas desta vez recebo:

df= pd.read_csv("data.csv") 
df[['id']] = df[['id']].astype(int)
error: Cannot convert NA to integer

Como posso resolver isso?

Zhubarb
fonte
3
Eu acho que valores inteiros não podem ser convertidos ou armazenados em uma série / dataframe se houver valores ausentes / NaN. Isso eu acho que tem a ver com a compatibilidade numpy (eu estou supondo aqui), se você quiser faltando compatibilidade valor, então eu iria armazenar os valores como carros alegóricos
EdChum
1
veja aqui: pandas.pydata.org/pandas-docs/dev/… ; você deve ter um tipo de flutuação quando houver valores ausentes (ou tecnicamente objeto tipo, mas isso é ineficiente); qual é o seu objetivo de usar o tipo int?
Jeff
6
Acredito que esse seja um problema do NumPy, não específico do Pandas. É uma pena, pois há muitos casos em que um tipo int que permite a possibilidade de valores nulos é muito mais eficiente do que uma grande coluna de flutuadores.
Ely
1
Eu também tenho um problema com isso. Eu tenho vários quadros de dados que desejo mesclar com base em uma representação de seqüência de caracteres de várias colunas "inteiras". No entanto, quando uma dessas colunas inteiras tem um np.nan, a conversão de cadeia produz um ".0", o que desencadeia a mesclagem. Apenas torna as coisas um pouco mais complicadas, seria bom se houvesse uma solução simples.
Dermen
1
@Rhubarb, o Suporte Integral Anulável 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:

169

A falta de NaN rep em colunas inteiras é um panda "pegadinha" .

A solução usual é simplesmente usar carros alegóricos.

Andy Hayden
fonte
13
Existem outras soluções alternativas além de tratá-las como carros alegóricos?
NumenorForLife 14/05
3
@ jsc123 você pode usar o objeto dtype. Isso vem com um pequeno aviso de saúde, mas na maioria das vezes funciona bem.
Andy Hayden
1
Você pode fornecer um exemplo de como usar o tipo de objeto? Eu estive pesquisando os documentos dos pandas e pesquisando no Google, e li que é o método recomendado. Mas não encontrei um exemplo de como usar o objeto dtype.
precisa saber é o seguinte
28
Na v0.24, agora você pode fazer df = df.astype(pd.Int32Dtype())(para converter todo o dataFrame ou) df['col'] = df['col'].astype(pd.Int32Dtype()). Outros tipos de número nulo aceitos são pd.Int16Dtypee pd.Int64Dtype. Escolha o seu veneno.
cs95
1
É o valor NaN, mas a verificação isnan não funciona :(
Winston
116

Na versão 0.24. + Os pandas ganharam a capacidade de armazenar tipos inteiros com valores ausentes.

Tipo de Dados Inteiro Anulável .

Pandas podem representar dados inteiros com possivelmente valores ausentes usando arrays.IntegerArray. Este é um tipo de extensão implementado no pandas. Não é o tipo padrão para números inteiros e não será inferido; você deve passar explicitamente o dtype para array()ou Series:

arr = pd.array([1, 2, np.nan], dtype=pd.Int64Dtype())
pd.Series(arr)

0      1
1      2
2    NaN
dtype: Int64

Para converter a coluna em números inteiros anuláveis, use:

df['myCol'] = df['myCol'].astype('Int64')
jezrael
fonte
4
Eu gosto desta resposta.
cs95
7
Note-se que dtipo deve ser "Int64"e não "int64"(primeiro 'i' deve ser capitalizado)
Viacheslav Z
2
df.myCol = df.myCol.astype('Int64')oudf['myCol'] = df['myCol'].astype('Int64')
LoMaPh 4/11/19
43

Meu caso de uso está filtrando dados antes do carregamento em uma tabela do banco de dados:

df[col] = df[col].fillna(-1)
df[col] = df[col].astype(int)
df[col] = df[col].astype(str)
df[col] = df[col].replace('-1', np.nan)

Remova NaNs, converta para int, converta para str e, em seguida, reinsira NANs.

Não é bonito, mas faz o trabalho!

hibernado
fonte
1
Eu tenho puxado meu cabelo tentando carregar números de série onde alguns são nulos e o resto são carros alegóricos, isso me salvou.
Chris Decker
1
O OP quer uma coluna de números inteiros. Convertê-lo em string não atende à condição.
Rishab Gupta
1
Funciona apenas se col ainda não tiver -1. Caso contrário, ele vai mexer com os dados
Sharvari Gc
então como voltar ao int .. ??
abdoulsn 23/01
5

Agora é possível criar uma coluna de pandas contendo NaNs como dtype int, já que agora é adicionada oficialmente ao pandas 0.24.0

Notas da versão do pandas 0.24.x Quote: "O Pandas ganhou a capacidade de armazenar tipos inteiros com valores ausentes

mork
fonte
4

Se você absolutamente deseja combinar números inteiros e NaNs em uma coluna, pode usar o tipo de dados 'objeto':

df['col'] = (
    df['col'].fillna(0)
    .astype(int)
    .astype(object)
    .where(df['col'].notnull())
)

Isso substituirá os NaNs por um número inteiro (não importa qual), converter para int, converter para objeto e finalmente reinserir NaNs.

jmenglund
fonte
3

Se você pode modificar os dados armazenados, use um valor sentinela como ausente id. Um caso de uso comum, inferido pelo nome da coluna, sendo idum número inteiro, estritamente maior que zero, você pode usar 0como um valor sentinela para poder escrever

if row['id']:
   regular_process(row)
else:
   special_process(row)
gboffi
fonte
3

Você pode usar .dropna()se estiver OK descartar as linhas com os valores de NaN.

df = df.dropna(subset=['id'])

Como alternativa, use .fillna()e .astype()para substituir o NaN por valores e convertê-los em int.

Corri para esse problema ao processar um arquivo CSV com números inteiros grandes, enquanto alguns deles estavam ausentes (NaN). Usar float como o tipo não era uma opção, porque eu poderia perder a precisão.

Minha solução foi usar str como o tipo intermediário . Em seguida, você pode converter a string para int como desejar posteriormente no código. Substituí NaN por 0, mas você pode escolher qualquer valor.

df = pd.read_csv(filename, dtype={'id':str})
df["id"] = df["id"].fillna("0").astype(int)

Para a ilustração, aqui está um exemplo de como os flutuadores podem perder a precisão:

s = "12345678901234567890"
f = float(s)
i = int(f)
i2 = int(s)
print (f, i, i2)

E a saída é:

1.2345678901234567e+19 12345678901234567168 12345678901234567890
elomage
fonte
2

A maioria das soluções aqui mostra como usar um número inteiro de espaço reservado para representar nulos. Essa abordagem não é útil se você não tiver certeza de que o número inteiro não será exibido nos dados de origem. Meu método com irá formatar carros alegóricos sem seus valores decimais e converter nulos em nenhum. O resultado é um tipo de dados do objeto que se parecerá com um campo inteiro com valores nulos quando carregado em um CSV.

keep_df[col] = keep_df[col].apply(lambda x: None if pandas.isnull(x) else '{0:.0f}'.format(pandas.to_numeric(x)))
Corbin
fonte
1

Encontrei este problema ao trabalhar com o pyspark. Como este é um front-end python para código em execução em uma jvm, requer segurança de tipo e o uso de float em vez de int não é uma opção. Eu resolvi o problema envolvendo os pandas pd.read_csvem uma função que preencherá as colunas definidas pelo usuário com valores de preenchimento definidos pelo usuário antes de convertê-los no tipo necessário. Aqui está o que eu acabei usando:

def custom_read_csv(file_path, custom_dtype = None, fill_values = None, **kwargs):
    if custom_dtype is None:
        return pd.read_csv(file_path, **kwargs)
    else:
        assert 'dtype' not in kwargs.keys()
        df = pd.read_csv(file_path, dtype = {}, **kwargs)
        for col, typ in custom_dtype.items():
            if fill_values is None or col not in fill_values.keys():
                fill_val = -1
            else:
                fill_val = fill_values[col]
            df[col] = df[col].fillna(fill_val).astype(typ)
    return df
Neuneck
fonte
1
import pandas as pd

df= pd.read_csv("data.csv")
df['id'] = pd.to_numeric(df['id'])
Monaheng Ramochele
fonte
4
Existe uma razão para você preferir essa formulação à proposta na resposta aceita? Nesse caso, seria útil editar sua resposta para fornecer essa explicação - e principalmente porque existem dez respostas adicionais que estão competindo por atenção.
Jeremy Caney
Embora esse código possa resolver o problema do OP, é melhor incluir uma explicação sobre como / por que seu código o trata. Dessa forma, futuros visitantes podem aprender com sua postagem e aplicá-la ao próprio código. O SO não é um serviço de codificação, mas um recurso para o conhecimento. Além disso, respostas completas de alta qualidade têm mais probabilidade de serem votadas. Esses recursos, juntamente com o requisito de que todas as postagens sejam independentes, são alguns dos pontos fortes do SO, pois uma plataforma o diferencia dos fóruns. Você pode editadicionar informações adicionais e / ou complementar suas explicações com a documentação de origem.
SherylHohman 6/06
0

Primeiro remova as linhas que contêm NaN. Em seguida, faça a conversão de número inteiro nas linhas restantes. Por último, insira as linhas removidas novamente. Espero que funcione

kamran kausar
fonte
-1

Supondo que o DateColumn formatado 3312018.0 deve ser convertido para 31/03/2018 como uma sequência. E, alguns registros estão ausentes ou 0.

df['DateColumn'] = df['DateColumn'].astype(int)
df['DateColumn'] = df['DateColumn'].astype(str)
df['DateColumn'] = df['DateColumn'].apply(lambda x: x.zfill(8))
df.loc[df['DateColumn'] == '00000000','DateColumn'] = '01011980'
df['DateColumn'] = pd.to_datetime(df['DateColumn'], format="%m%d%Y")
df['DateColumn'] = df['DateColumn'].apply(lambda x: x.strftime('%m/%d/%Y'))
Justin Malinchak
fonte