Замена Pandas или Numpy Nan на None для использования с MysqlDB

128

Я пытаюсь записать фреймворк Pandas (или можно использовать массив numpy) в базу данных mysql с помощью MysqlDB. MysqlDB, похоже, не понимает "nan", и моя база данных выдает ошибку, говоря, что nan отсутствует в списке полей. Мне нужно найти способ преобразовать «nan» в NoneType.

Любые идеи?

Риши
источник
2
Нет ли настройки можно изменить в панд , чтобы сделать его вернуться Noneна NULLвместо nan?
Натан

Ответы:

196

@bogatron прав, вы можете использовать where, стоит отметить, что вы можете сделать это изначально в пандах:

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

Примечание: это изменяет dtype всех столбцов на object.

Пример:

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

Примечание: то, что вы не можете сделать, переделать DataFrames, dtypeчтобы разрешить все типы данных, используя astype, а затем fillnaметод DataFrame :

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

К сожалению , ни этого, ни с помощью replace, работа с Noneувидеть это (закрытый) вопрос .


В стороне, стоит отметить, что для большинства случаев использования вам не нужно заменять NaN на None, см. Этот вопрос о разнице между NaN и None в пандах .

Однако в этом конкретном случае кажется, что да (по крайней мере, на момент этого ответа).

Энди Хайден
источник
см. документы pandas.pydata.org/pandas-docs/stable/…
Джефф
1
FWIW .. это также изменит dtype столбцов на object, хотя вам, вероятно, все равно
Джефф
@Jeff Спасибо за ссылку, как ни странно, я не мог найти ее раньше! Я подумал, что нужно изменить dtype, чтобы разрешить None, о чем определенно стоит упомянуть!
Энди Хайден
полезно использовать перед вставкой с помощью Django, чтобы избежать np.nanпреобразования в строку"nan"
shadi
Полезный нюанс. Имеет смысл перебрать только те столбцы, которые уже dtypeот objectи делают это для тех , кто и обрабатывать другие типы по- разному , как это необходимо. В идеале fillna(None)было бы потрясающе.
Vishal
84
df = df.replace({np.nan: None})

Благодарим этого парня за этот вопрос с Github .

EliadL
источник
5
это лучший ответ, поскольку вы можете использовать его df.replace({np.nan: None})как временный объект
Мэтт
17

Вы можете заменить nanс Noneвашей 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
источник
2
Единственная потенциальная проблема - это изменение dtype, x.dtypeis dtype('float64'), while y.dtypeis dtype('object').
Jaime
10

После спотыкания это сработало для меня:

df = df.astype(object).where(pd.notnull(df),None)
Родни Кокс
источник
4

Просто дополнение к ответу @Andy Hayden:

Поскольку DataFrame.maskэто противоположный двойник DataFrame.where, у них точно такая же сигнатура, но с противоположным значением:

  • DataFrame.whereполезен для замены значений, когда условие ложно .
  • DataFrame.maskиспользуется для замены значений, если условие истинно .

Итак, в этом вопросе использование df.mask(df.isna(), other=None, inplace=True)может быть более интуитивным.

YaOzI
источник
2

Еще одно дополнение: будьте осторожны при замене кратных и преобразовании типа столбца обратно из объекта в число с плавающей запятой . Если вы хотите быть уверены, что ваш None's не вернется к предложению np.NaN' apply @andy-hayden 'с использованием pd.where. Иллюстрация того, как замена может пойти не так:

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
источник
Спасибо, что добавили это. Снова просматривая документацию, я все еще не могу понять этого поведения. В любом случае, это можно обойти, связав еще одну цепочку.replace({np.nan: None})
EliadL
1
Да, вы можете закончить, добавив еще один replace({np.nan: None}). Мой комментарий был добавлен, чтобы указать на потенциальную ловушку при замене np.nan's. Вышеупомянутое, безусловно, немного сбило меня с толку!
gaatjeniksaan
1

Довольно старый, но я наткнулся на ту же проблему. Попробуйте сделать это:

df['col_replaced'] = df['col_with_npnans'].apply(lambda x: None if np.isnan(x) else x)
Робин Немет
источник
не работает, если тип данных столбца числовой, потому что None просто конвертируется обратно в nan (pandas 0,23)
shadi