【问题标题】:Finding non-numeric rows in dataframe in pandas?在熊猫的数据框中查找非数字行?
【发布时间】:2014-03-13 07:53:06
【问题描述】:

我在 pandas 中有一个大数据框,除了用作索引的列应该只有数值:

df = pd.DataFrame({'a': [1, 2, 3, 'bad', 5],
                   'b': [0.1, 0.2, 0.3, 0.4, 0.5],
                   'item': ['a', 'b', 'c', 'd', 'e']})
df = df.set_index('item')

如何找到数据框df 中包含非数字值的行?

在此示例中,它是数据框中的第四行,在 a 列中包含字符串 'bad'。如何以编程方式找到该行?

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    抱歉,这应该是正确的方法。您是否只想捕获'bad',而不是'good' 之类的东西;或者只是任何非数值?

    In[15]:
    np.where(np.any(np.isnan(df.convert_objects(convert_numeric=True)), axis=1))
    Out[15]:
    (array([3]),)
    

    【讨论】:

    • 这会选择'bad',但不会选择流氓字符串号码,因为转换会成功..
    • 或者遇到表示有效数字的编码字符串的问题,例如'over_the_chart',表示该变量的最大允许值。可能不是一个安全的方法。我宁愿从在现实世界中构建dataframe 的方式来解决问题。
    • 这取决于你是否认为“4”不好,我的直觉是肯定的:S
    • convert_objects 已弃用
    【解决方案2】:

    您可以使用np.isreal 来检查每个元素的类型(applymap 将函数应用于 DataFrame 中的每个元素):

    In [11]: df.applymap(np.isreal)
    Out[11]:
              a     b
    item
    a      True  True
    b      True  True
    c      True  True
    d     False  True
    e      True  True
    

    如果行中的所有内容都是 True,那么它们都是数字:

    In [12]: df.applymap(np.isreal).all(1)
    Out[12]:
    item
    a        True
    b        True
    c        True
    d       False
    e        True
    dtype: bool
    

    所以要获取 rouges 的 subDataFrame,(注意:上面的否定,~,找到至少有一个 rogue non-numeric 的那些):

    In [13]: df[~df.applymap(np.isreal).all(1)]
    Out[13]:
            a    b
    item
    d     bad  0.4
    

    您还可以找到第一个罪犯的位置,您可以使用argmin

    In [14]: np.argmin(df.applymap(np.isreal).all(1))
    Out[14]: 'd'
    

    正如@CTZhu 指出的那样,check whether it's an instance of 无论是 int 还是 float 都可能稍微快一些(np.isreal 有一些额外的开销):

    df.applymap(lambda x: isinstance(x, (int, float)))
    

    【讨论】:

    • 太棒了!只有df.applymap(lambda x: isinstance(x, (int, float))) 会快 18% 左右。将lambda 与内置方法一起使用有时相当快。
    • @CTZhu 好点。我想这并不令人惊讶(因为 np.isreal 也做了其他一些事情,例如处理数组而不仅仅是单个值)。
    • 值得注意的是np.isreal(None) is not True
    • 如何只检查一列中的非数字值?
    • @Superdooperhero 在列上使用 .apply 而不是在 DataFrame 上使用 .applymap,即 df["a"].apply(np.isreal)df[~df["a"].apply(np.isreal)]
    【解决方案3】:

    这个问题已经有了一些很好的答案,但是这里有一个很好的 sn-p,如果它们在某些列上有非数字值,我会经常使用它来删除行:

    # Eliminate invalid data from dataframe (see Example below for more context)
    
    num_df = (df.drop(data_columns, axis=1)
             .join(df[data_columns].apply(pd.to_numeric, errors='coerce')))
    
    num_df = num_df[num_df[data_columns].notnull().all(axis=1)]
    

    这个工作的方式是我们首先drop来自df的所有data_columns,然后在将它们通过pd.to_numeric后使用join将它们放回原处(带有选项'coerce',这样所有非数字条目都将转换为NaN)。结果保存到num_df

    在第二行,我们使用了一个过滤器,它只保留所有值都不为空的行。

    请注意,pd.to_numeric 将所有无法转换为数值的内容强制转换为 NaN,因此不会删除表示数值的字符串。例如'1.25' 将被识别为数值1.25

    免责声明:pd.to_numeric 在 pandas 版本中引入 0.17.0

    示例:

    In [1]: import pandas as pd
    
    In [2]: df = pd.DataFrame({"item": ["a", "b", "c", "d", "e"],
       ...:                    "a": [1,2,3,"bad",5],
       ...:                    "b":[0.1,0.2,0.3,0.4,0.5]})
    
    In [3]: df
    Out[3]: 
         a    b item
    0    1  0.1    a
    1    2  0.2    b
    2    3  0.3    c
    3  bad  0.4    d
    4    5  0.5    e
    
    In [4]: data_columns = ['a', 'b']
    
    In [5]: num_df = (df
       ...:           .drop(data_columns, axis=1)
       ...:           .join(df[data_columns].apply(pd.to_numeric, errors='coerce')))
    
    In [6]: num_df
    Out[6]: 
      item   a    b
    0    a   1  0.1
    1    b   2  0.2
    2    c   3  0.3
    3    d NaN  0.4
    4    e   5  0.5
    
    In [7]: num_df[num_df[data_columns].notnull().all(axis=1)]
    Out[7]: 
      item  a    b
    0    a  1  0.1
    1    b  2  0.2
    2    c  3  0.3
    4    e  5  0.5
    

    【讨论】:

    • 这是我使用的,由于某种原因,最佳答案并不总是对我有用,convert_object 已被弃用
    • 我做了类似的事情,但是像这样 float_cols=['a','b','c'] for cols in float_cols: df[cols]=df[cols].apply(pd. to_numeric, errors='coerce').fillna(0) 这特别适用于我希望 nan 为零而不是 nan 的情况。您也可以省略 fillna(0) 位。
    • 这个 drop/join 逻辑是否扩展到数百万行?
    【解决方案4】:

    如果您正在处理具有字符串值的列,您可以使用 非常有用的函数 series.str.isnumeric() 像:

    a = pd.Series(['hi','hola','2.31','288','312','1312', '0,21', '0.23'])
    

    我所做的是将该列复制到新列,然后执行 str.replace('.','') 和 str.replace(',','') 然后我选择数值。 和:

    a = a.str.replace('.','')
    a = a.str.replace(',','') 
    a.str.isnumeric()
    

    输出[15]: 0 错误 1 错误 2 真 3 真 4 真 5 真 6 真 7 真 数据类型:布尔

    祝大家好运!

    【讨论】:

    • 为什么2.31 不是数字?
    • 我认为是因为它有一个点。我所做的是将该列复制到新列,然后执行 str.replace('.','') 然后选择数值。我最近遇到了这个问题。
    • @toto_tico 这是因为一个点。根据pandas.pydata.org/pandas-docs/stable/generated/…它“检查系列/索引中每个字符串中的所有字符是否都是数字”
    • @kekert,谢谢,我忘记了,实际上 str 中没有isreal(或类似的)...@fra44,我认为您应该直接在回答,因为它可能会误导......
    【解决方案5】:
    # Original code
    df = pd.DataFrame({'a': [1, 2, 3, 'bad', 5],
                       'b': [0.1, 0.2, 0.3, 0.4, 0.5],
                       'item': ['a', 'b', 'c', 'd', 'e']})
    df = df.set_index('item')
    

    Convert to numeric 使用 'coerce' 用 'nan' 填充错误值

    a = pd.to_numeric(df.a, errors='coerce')
    

    使用isna 返回一个布尔索引:

    idx = a.isna()
    

    将该索引应用于数据框:

    df[idx]
    

    输出

    返回包含错误数据的行:

            a    b
    item          
    d     bad  0.4
    

    【讨论】:

      【解决方案6】:

      我正在考虑类似的事情,只是给出一个想法,将列转换为字符串,并且使用字符串更容易。但是,这不适用于包含数字的字符串,例如bad123。和~ 正在接受选择的补充。

      df['a'] = df['a'].astype(str)
      df[~df['a'].str.contains('0|1|2|3|4|5|6|7|8|9')]
      df['a'] = df['a'].astype(object)
      

      并使用'|'.join([str(i) for i in range(10)]) 生成'0|1|...|8|9'

      或使用np.isreal() 函数,就像投票最多的答案一样

      df[~df['a'].apply(lambda x: np.isreal(x))]
      

      【讨论】:

      • 请解释您的解决方案
      • @BookOfZeus 只需将其转换为字符串,然后使用它。之后将其转换回来。
      猜你喜欢
      • 1970-01-01
      • 2018-04-21
      • 2019-01-26
      • 1970-01-01
      • 2019-04-17
      • 1970-01-01
      • 2022-08-22
      • 2019-07-21
      • 1970-01-01
      相关资源
      最近更新 更多