【问题标题】:How to use dropna on dataframes with dtype=str?如何在 dtype=str 的数据帧上使用 dropna?
【发布时间】:2018-01-30 17:14:50
【问题描述】:

当我有这样的数据框时:

import pandas as pd
import numpy as np    

df = pd.DataFrame(np.nan, index=list('abc'), columns=list('DEF'), dtype=float)
df.set_value('a', 'D', 4.0)
df.set_value('b', 'E', 10.0)

     D     E   F
a  4.0   NaN NaN
b  NaN  10.0 NaN
c  NaN   NaN NaN

我可以通过调用轻松删除仅包含 NaNs 的行:

df = df.dropna(how='all')

产生

     D     E   F
a  4.0   NaN NaN
b  NaN  10.0 NaN

如何在使用dtype=str 初始化的数据帧上做同样的事情?以下方法不起作用:

df2 = pd.DataFrame(np.nan, index=list('abc'), columns=list('DEF'), dtype='str')
df2.set_value('a', 'D', 'foo')
df2.set_value('b', 'E', 'bar')

     D    E  F
a  foo    n  n
b    n  bar  n
c    n    n  n

然后是命令

df2 = df2.dropna(how='all')

返回未修改的数据帧。

【问题讨论】:

  • 它们不再是NaN,而是字符'n',所以你可以过滤掉它们,但这里真正的问题是什么,NaN 是一个数字,str 不能代表它,所以他们在技术上不是NaN

标签: python pandas dataframe nan missing-data


【解决方案1】:

先拨打df.replace,再拨打df.dropna

In [1576]: df2.replace('n', np.nan).dropna(how='all')
Out[1576]: 
     D    E   F
a  foo  NaN NaN
b  NaN  bar NaN

这似乎是最直接的选择。据我所知,一旦您使用dtype=str 初始化数据框,您就丢失了NaNs,因此这更像是一个最佳猜测替换(您可能有合法的非NaN 条目,即n被标记为误报并被删除)。


这是与 John Galt 类似的解决方案,但保留 NaNs:

In [1584]: df2[~df2.eq('n')].dropna(how='all')
Out[1584]: 
     D    E    F
a  foo  NaN  NaN
b  NaN  bar  NaN

扩展 Andrew L 的评论,您无需转换为 dtype=str 即可设置值。您可以改用基于.loc 的索引:

In [1586]: df2 = pd.DataFrame(np.nan, index=list('abc'), columns=list('DEF'))
      ...: df2.loc['a', 'D'] = 'foo'
      ...: df2.loc['b', 'E'] = 'bar'
      ...: 

In [1587]: df2
Out[1587]: 
     D    E   F
a  foo  NaN NaN
b  NaN  bar NaN
c  NaN  NaN NaN

现在,

In [1588]: df2.dropna(how='all')
Out[1588]: 
     D    E   F
a  foo  NaN NaN
b  NaN  bar NaN

【讨论】:

  • 好的,这很令人不快,但可以解决(赞成)。当我没有指定dtype 时,我不能将set_value 与字符串一起使用,很遗憾...
  • @Cleb 为什么不使用loc 设置值? df2.loc['a', 'D'] = 'foo'
  • @AndrewL:loc 确实可以使用,但set_value 应该更快。
  • @Cleb 是的,但是将ns 转换为NaNs 的惩罚将是你受苦的地方。重新考虑。我添加了一个 MVCE 来支持这个概念。
  • 谢谢,是的,我可能会选择loc;只是想知道是否有更好的解决方案,感谢您的详细回答。
【解决方案2】:

他们不再是NaN。你可以像这样过滤它们

In [503]: df2[~df2.eq('n').all(1)]
Out[503]:
     D    E  F
a  foo    n  n
b    n  bar  n

【讨论】:

  • 可靠的解决方法(赞成);不理想,因为实际数据可能等于n
  • @Cleb 恐怕你必须采取任何解决方案。
【解决方案3】:

由于您已将np.nan 转换为n <class 'str'>,您还可以执行以下操作:

df2[~(df2 == 'n').all(axis=1)]
     D    E  F
a  foo    n  n
b    n  bar  n

如果您可能有包含“n”的真实数据,这显然是不安全的。

【讨论】:

    【解决方案4】:

    您可以用真实的numpy.nan 值替换您的字符串:

    df2.replace('n',np.nan).dropna(how = 'all')
    

    这将起作用,但也会更改您可能希望保留的数据框中的 'n' 值。在这种情况下,删除仅包含值 'n' 的行:

    df2[(df2.T != 'n').any()]
    

    此外,第二种解决方案的计算效率更高:

    %timeit df2.replace('n',np.nan).dropna(how = 'all')
    985 µs ± 8.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    
    %timeit df2[(df2.T != 'n').any()]
    449 µs ± 1.33 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    【讨论】:

    • 与另一个答案相同,但仍支持时间比较。
    • 我最初对您的答案投了反对票,因为这是我的同上。现在我看到你添加了更多内容,我已经删除了它,但你应该知道我知道是你报复投了我的 3 个答案。
    • 这怎么可能是报复?我不知道你是反对我的人。即使我这样做了,我也不在乎。我对您的一些答案投了反对票,因为它们要么效率低下(例如使用基本上用于循环的 lambda 函数),要么有副作用(转换不需要的数据类型)。对不起,如果这伤害了你的感情。
    • 我不明白为什么这意味着我的更多答案会被否决。无论如何,这又不是出于报复,只是您的解决方案不是解决这些问题的最佳选择。
    猜你喜欢
    • 2020-11-15
    • 1970-01-01
    • 1970-01-01
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    • 2019-11-07
    • 2017-01-17
    • 1970-01-01
    相关资源
    最近更新 更多