【问题标题】:Replacing Pandas or Numpy Nan with a None to use with MysqlDB用 None 替换 Pandas 或 Numpy Nan 以与 MysqlDB 一起使用
【发布时间】:2012-12-19 05:18:15
【问题描述】:

我正在尝试使用 MysqlDB 将 Pandas 数据框(或可以使用 numpy 数组)写入 mysql 数据库。 MysqlDB 似乎不理解“nan”,我的数据库抛出一个错误,指出 nan 不在字段列表中。我需要找到一种将“nan”转换为 NoneType 的方法。

有什么想法吗?

【问题讨论】:

  • 在 Pandas 中是否没有可以更改的设置以使其返回 NoneNULL 而不是 nan

标签: python pandas numpy mysql-python


【解决方案1】:

@bogatron 说得对,你可以使用where,值得注意的是,你可以在 pandas 中原生地做到这一点:

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

注意:这会将所有列的数据类型更改为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,然后使用 DataFrame fillna 方法:

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

不幸的是,无论是这个还是使用replace,都不能与None 一起使用,请参阅this (closed) issue


顺便说一句,值得注意的是,对于大多数用例,您不需要将 NaN 替换为 None,请参阅这个关于 the difference between NaN and None in pandas 的问题。

但是,在这种特定情况下,您似乎这样做了(至少在本回答时)。

【讨论】:

  • FWIW..这也会将列的 dtype 更改为对象,但您可能并不关心
  • 一个重要的用例是转换为 JSON 时。并非所有语言都支持 JSON 中的 NaN(例如 PHP),因此需要将它们转换为 None。这是我作为数据科学家经常遇到的事情。
  • 这个方法还能用吗?目前只有@EliadL 下面的答案对我来说没有错误,至少在熊猫版本1.0.3 中。
  • 使用 df.where(pd.notnull(df), None) 不再适用于 1.3.0 - 相反,我发现来自 @EliadL 的下一个答案仍然可以正常工作:stackoverflow.com/a/54403705/2407819
【解决方案2】:
df = df.replace({np.nan: None})

注意:这会将所有受影响的数据类型更改为object

感谢this Github issue 上的这个人。

【讨论】:

  • 这是最好的答案,因为您可以使用 df.replace({np.nan: None}) 作为临时对象
  • 如果df 中的值已经是None 这个答案会将它们切换回np.nan
  • @MaxSegal 怎么样?我在replace() 文档中没有找到这个。你能告诉我文档中提到的地方吗?
  • 对于 pandas 版本 如果 df 中的值已经是 None 这个答案会将它们切换回 np.nan
  • 我遇到了将它们切换回版本 >1.3.0 的问题。我的专栏是分类的。当我将其切换为对象时,它再次起作用。也许这可能是原因。
【解决方案3】:

您可以在 numpy 数组中将 nan 替换为 None

>>> 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'>

【讨论】:

  • 唯一潜在的问题是dtype的变化,x.dtypedtype('float64'),而y.dtypedtype('object')
【解决方案4】:

在跌跌撞撞之后,这对我有用:

df = df.astype(object).where(pd.notnull(df),None)

【讨论】:

    【解决方案5】:

    另外一个补充:替换倍数并将列的类型从 object 转换回 float 时要小心。如果您想确定您的None 不会翻转回np.NaN,请应用@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
    

    【讨论】:

    • 感谢您添加此内容。再次查看文档,我仍然无法理解这种行为。无论如何,这可以通过链接另一个 .replace({np.nan: None}) 来解决
    • 是的,您可以通过添加另一个 replace({np.nan: None}) 来完成。添加了我的评论以指出替换 np.nan 时的潜在陷阱。以上肯定让我有点失望!
    【解决方案6】:

    只是对@Andy Hayden 回答的补充:

    由于DataFrame.maskDataFrame.where的相反双胞胎,它们的签名完全相同,但含义相反:

    • DataFrame.where 对于替换条件为 False 的值很有用。
    • DataFrame.mask 用于替换条件为 True 的值。

    所以在这个问题中,使用df.mask(df.isna(), other=None, inplace=True) 可能更直观。

    【讨论】:

      【解决方案7】:

      相当古老,但我偶然发现了同样的问题。 尝试这样做:

      df['col_replaced'] = df['col_with_npnans'].apply(lambda x: None if np.isnan(x) else x)
      

      【讨论】:

      • 如果列数据类型是数字则不起作用,因为 None 只是被转换回 nan (pandas 0.23)
      【解决方案8】:

      我相信最干净的方法是在 pandas.DataFrame.to_numpy() 方法 (docs) 中使用 na_value 参数:

      na_value:任意,可选

      用于缺失值的值。默认值取决于 dtype 和 DataFrame 列的 dtypes。

      1.1.0 版中的新功能。

      你可以例如转换为字典,将 NaN 替换为 None 使用

      columns = df.columns.tolist()
      dicts_with_nan_replaced = [
          dict(zip(columns, x))
          for x in df.to_numpy(na_value=None)
      ]
      

      【讨论】:

      • 您的代码将 NaN 保持为 NaN,但如果您还传递了 dtype=object,则可以修复它。
      【解决方案9】:

      你有一个代码块需要偶然检查吗?

      使用 .loc,pandas 可以根据逻辑条件(过滤)访问记录并对其进行操作(使用 = 时)。将 .loc 掩码设置为某个值将更改返回数组 inplace (所以在这里要小心;我建议在代码块中使用之前先在 df 副本上进行测试)。

      df.loc[df['SomeColumn'].isna(), 'SomeColumn'] = None
      

      外部函数是df.loc[row_label, column_label] = None。我们将使用 .isna() 方法为 row_label 使用布尔掩码,在我们的列 SomeColumn 中查找“NoneType”值。

      我们将使用 .isna() 方法在 SomeColumn 列中返回一个包含行/记录的布尔数组作为我们的 row_labeldf['SomeColumn'].isna()。它将隔离所有 SomeColumn 具有 Pandas 使用 .isna() 方法检查的任何“NoneType”项目的行。

      我们将在为 row_label 屏蔽数据框时使用 column_label,并为 .loc 掩码标识我们想要操作的列。

      最后,我们将 .loc 掩码设置为 None,因此返回的行/记录根据掩码更改为 None索引。

      以下是关于 .loc.isna() 的 pandas 文档的链接。

      参考:
      https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.loc.html https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.isna.html

      【讨论】:

        【解决方案10】:

        这对我有用:

        df = df.fillna(0)
        

        【讨论】:

          【解决方案11】:

          在用 where 语句替换之前将 numpy NaN 转换为 pandas NA:

          df = df.replace(np.NaN, pd.NA).where(df.notnull(), None)
          

          【讨论】:

            【解决方案12】:

            在将 Pandas 更新到 1.3.2 后发现推荐的答案和替代建议的答案都不适用于我的应用程序后,我采用蛮力方法解决了安全问题:

            buf = df.to_json(orient='records')
            recs = json.loads(buf)
            

            【讨论】:

              【解决方案13】:

              np.nan 替换为None在不同版本的pandas 中实现的方式不同

              if version.parse(pd.__version__) >= version.parse('1.3.0'):
                  df = df.replace({np.nan: None})
              else:
                  df = df.where(pd.notnull(df), None)
              

              这解决了pandas版本df中的值已经是None,那么df.replace({np.nan: None})会将它们切换回np.nan

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2011-10-05
                • 1970-01-01
                • 2017-08-01
                • 2023-03-04
                • 1970-01-01
                • 2021-10-28
                相关资源
                最近更新 更多