Substituindo Pandas ou Numpy Nan por Nenhum para usar com MysqlDB

127

Eu estou tentando escrever um quadro de dados do Pandas (ou pode usar uma matriz numpy) em um banco de dados mysql usando o MysqlDB. O MysqlDB não parece entender 'nan' e meu banco de dados gera um erro dizendo que nan não está na lista de campos. Preciso encontrar uma maneira de converter o 'nan' em um NoneType.

Alguma ideia?

Rishi
fonte
2
Não existe uma configuração que você possa alterar no Pandas para retornar Noneem NULLvez de nan?
21717 Nathan Hinchey

Respostas:

194

O @bogatron está certo, você pode usar where, vale a pena notar que você pode fazer isso nativamente nos pandas:

df1 = df.where(pd.notnull(df), None)

Nota: isso altera o tipo de todas as colunas para object.

Exemplo:

In [1]: df = pd.DataFrame([1, np.nan])

In [2]: df
Out[2]: 
    0
0   1
1 NaN

In [3]: df1 = df.where(pd.notnull(df), None)

In [4]: df1
Out[4]: 
      0
0     1
1  None

Nota: o que você não pode fazer reformula os DataFrames dtypepara permitir todos os tipos de tipos de dados, usando astypee, em seguida, o fillnamétodo DataFrame :

df1 = df.astype(object).replace(np.nan, 'None')

Infelizmente nem este, nem usar replace, funciona com Nonever esta questão (fechado) .


Como um aparte, vale a pena notar que, para a maioria dos casos de uso, você não precisa substituir o NaN por Nenhum, consulte esta pergunta sobre a diferença entre NaN e Nenhum nos pandas .

No entanto, neste caso específico, parece que você faz (pelo menos no momento desta resposta).

Andy Hayden
fonte
1
FWIW .. isso também mudará o tipo de coluna para objeto, mas você provavelmente não se importa
Jeff Jeff
@ Jeff Obrigado pelo link, estranhamente não consegui encontrá-lo antes! Eu achei que tinha que mudar o tipo para permitir None, definitivamente vale a pena mencionar!
Andy Hayden
útil para uso antes de inserir com Django para evitar a np.nanser convertido para string"nan"
shadi
Advertência útil. Faz sentido para percorrer apenas aquelas colunas que já estão dtypede objecte fazê-lo para aqueles e lidar com outros tipos de forma diferente, conforme necessário. Idealmente, fillna(None)seria fantástico.
Vishal
83
df = df.replace({np.nan: None})

O crédito é para esse cara aqui nesta questão do Github .

EliadL
fonte
3
esta é a melhor resposta, como você pode usar df.replace({np.nan: None})como um objeto temporário
Matt
17

Você pode substituir nanpor Noneem sua matriz numpy:

>>> x = np.array([1, np.nan, 3])
>>> y = np.where(np.isnan(x), None, x)
>>> print y
[1.0 None 3.0]
>>> print type(y[1])
<type 'NoneType'>
Bogatron
fonte
2
A única preocupação potencial é a mudança de dtype, x.dtypeé dtype('float64'), enquanto y.dtypeé dtype('object').
Jaime
10

Depois de tropeçar, isso funcionou para mim:

df = df.astype(object).where(pd.notnull(df),None)
rodney cox
fonte
4

Apenas uma adição à resposta de @Andy Hayden:

Como DataFrame.maské o gêmeo oposto de DataFrame.where, eles têm exatamente a mesma assinatura, mas com significado oposto:

  • DataFrame.whereé útil para substituir valores em que a condição é falsa .
  • DataFrame.maské usado para Substituir valores em que a condição é True .

Portanto, nesta questão, o uso df.mask(df.isna(), other=None, inplace=True)pode ser mais intuitivo.

YaOzI
fonte
2

Outra adição: tenha cuidado ao substituir múltiplos e converter o tipo da coluna de volta ao objeto para flutuar . Se você quiser ter certeza de que o seu Nonenão voltará para np.NaNa sugestão de @ andy-hayden de usar pd.where. Ilustração de como a substituição ainda pode dar errado:

In [1]: import pandas as pd

In [2]: import numpy as np

In [3]: df = pd.DataFrame({"a": [1, np.NAN, np.inf]})

In [4]: df
Out[4]:
     a
0  1.0
1  NaN
2  inf

In [5]: df.replace({np.NAN: None})
Out[5]:
      a
0     1
1  None
2   inf

In [6]: df.replace({np.NAN: None, np.inf: None})
Out[6]:
     a
0  1.0
1  NaN
2  NaN

In [7]: df.where((pd.notnull(df)), None).replace({np.inf: None})
Out[7]:
     a
0  1.0
1  NaN
2  NaN
gaatjeniksaan
fonte
Obrigado por adicionar isso. Revendo a documentação novamente, ainda não consigo entender esse comportamento. Enfim, isso pode ser contornado encadeando outro.replace({np.nan: None})
EliadL 2/19/19
1
Sim, você pode terminar adicionando outro replace({np.nan: None}). Meu comentário foi adicionado para apontar a possível armadilha ao substituir np.nan's. O exposto certamente me tropeçou um pouco!
gaatjeniksaan
1

Muito velho, mas me deparei com o mesmo problema. Tente fazer isso:

df['col_replaced'] = df['col_with_npnans'].apply(lambda x: None if np.isnan(x) else x)
Robin Nemeth
fonte
não funciona se o tipo de dados da coluna é numérica porque nenhum apenas recebe de volta convertido em nan (pandas 0,23)
shadi