Python Pandas - 根据值删除行

Posted

技术标签:

【中文标题】Python Pandas - 根据值删除行【英文标题】:Python Pandas - Drop row based on value 【发布时间】:2017-07-07 11:37:00 【问题描述】:

我有一个带有 A 列和 B 列的 Pandas 数据框

import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.randint(0,100,size=(10, 2)), columns=list('AB'))

我创建列 C,如果 A > B 则为 NULL

df['C'] = np.select([ df.A > df.B ], [df.A], default=np.NaN)

这给了:

    A   B     C
0  95  19  95.0
1  46  11  46.0
2  96  86  96.0
3  22  61   NaN
4  69   1  69.0
5  78  91   NaN
6  42   7  42.0
7  24  28   NaN
8  55  92   NaN
9  92  16  92.0

然后,我使用以下几种方法之一删除将 df.C 作为 NaN 的行:

df = df.dropna(subset=['C'], how='any')

df = df.drop(df[pd.isnull(df.C)].index)

df = df.drop(df[(pd.isnull(df.C))].index)

所有 3 种方法都给了我大致的行数。在这种情况下:

    A   B     C
0  95  19  95.0
1  46  11  46.0
2  96  86  96.0
4  69   1  69.0
6  42   7  42.0
9  92  16  92.0

但是当我不使用数字时,例如字符串:

df['C'] = np.select([ df.A > df.B ], ['yes'], default=np.NaN)

然后,删除 df.C 为 NaN 的行的这 3 种方法不会被过滤。例如,当 df.A > df.B 将 C 列设置为 yes 时,我会得到这样的结果:

    A   B    C
0   6  70  nan
1  85  46  yes
2  76  87  nan
3  77  36  yes
4  73  18  yes
5   1  41  nan
6  19  69  nan
7  62  89  nan
8   6   7  nan
9  35  75  nan

我可以通过将 pd.NaN 替换为“IGNORE”之类的字符串,然后过滤“IGNORE”来解决此问题,但我发现此结果出乎意料。

df['C'] = np.select([ df.A > df.B ], ['yes'], default='IGNORE')
df = df.drop(df[(df.C == 'IGNORE')].index)

这是怎么回事? (当 df.C 是字符串时,我的 pd.NaN 是否正在转换为字符串?)


我在 Windows 10 上使用 64 位 Python 2.7.13、Pandas 0.19.2 和 Numpy 1.11.3。

【问题讨论】:

@Psidom 是的,是的。似乎 NaN 字面意思是“不是数字”,并且正在转换为字符串“nan”。 @Psidom 如果您将评论写成答案,我很乐意接受。它并没有真正解释为什么,但它确实解决了问题 【参考方案1】:

不要丢弃,只取有限值。

df = df[np.isfinite(df['C'])]

编辑:

根据您的评论,nan 属于 string 类型,因此,根据值删除行:

df = df[df.C != "nan"] 可以工作

df[df.C.notnull()]
    A   B    C
0  67  23  yes
1  91  61  yes
2  30  92  nan
3  53  97  nan
4  81  11  yes
5  23   7  yes
6  47  39  yes
7  11  27  nan
8  46  55  nan
9  31  82  nan
df = df[df.C != "nan"]


    A   B    C
0  67  23  yes
1  91  61  yes
4  81  11  yes
5  23   7  yes
6  47  39  yes 

【讨论】:

我收到 TypeError ufunc 'isfinite' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe'' 我试图模拟你的问题并想出了这个解决方案。将 numpy 导入为 np 将 pandas 导入为 pd df = pd.DataFrame(np.random.randint(0,100,size=(10, 2)), columns=list('AB')) df['C'] = np.select ([ df.A > df.B ], [df.A], 默认=np.NaN) 打印 df ABC 0 81 17 81.0 1 14 67 NaN 2 16 9 16.0 3 25 31 NaN 4 35 36 NaN 5 56 5 56.0 6 18 20 NaN 7 32 4 32.0 8 46 51 NaN 9 53 34 53.0 df = df[np.isfinite(df['C'])] 打印 df ABC 0 81 17 81.0 2 16 9 16.0 5 56 5 56.0 7 32 4 32.0 9 53 34 53.0 好吧,不同的是在我的实际代码中(而不是我在这里发布的示例代码),请试试这个:df['C'] = np.select([ df.A > df.B ], [u'yes'], default=np.NaN)【参考方案2】:

你的情况和这个类似:

np.array([1,2,'3',np.nan])
# array(['1', '2', '3', 'nan'], 
#       dtype='<U21')

因为np.select也返回一个数组,如果你进一步检查

type(np.nan)
# float

str(np.nan)
# 'nan'

所以np.nan是一个float,但是numpy数组除了structured array外更喜欢单一数据类型,所以当数组中有字符串元素时,所有元素都转换为字符串。


对于您的情况,如果您有字符串列,则可以默认使用None 代替np.nan,这将创建一个可以通过isnull() 检查并与dropna() 一起使用的缺失值:

import pandas as pd
import numpy as np
​
df = pd.DataFrame(np.random.randint(0,100,size=(10, 2)), columns=list('AB'))
df['C'] = np.select([ df.A > df.B ], ['yes'], default=None)

df.dropna()

#    A  B     C
#0  82  1   yes
#3  84  8   yes
#6  52  30  yes
#7  68  61  yes
#9  91  87  yes

【讨论】:

以上是关于Python Pandas - 根据值删除行的主要内容,如果未能解决你的问题,请参考以下文章

根据列值删除Python Pandas中的DataFrame行[重复]

Pandas - Python,根据日期列删除行

python--pandas删除

根据另一列中的值删除一列的重复项,Python,Pandas

根据条件 pandas 删除 DataFrame 中的重复行

使用 pandas 和 Python 删除重复项