如何删除Pandas DataFrame中某一列值为NaN的行

1422

我有一个DataFrame,只想要EPS列不是NaN的记录:

>>> df
                 STK_ID  EPS  cash
STK_ID RPT_Date                   
601166 20111231  601166  NaN   NaN
600036 20111231  600036  NaN    12
600016 20111231  600016  4.3   NaN
601009 20111231  601009  NaN   NaN
601939 20111231  601939  2.5   NaN
000001 20111231  000001  NaN   NaN

...即使用df.drop(....)之类的语句可以得到如下结果数据框:

                  STK_ID  EPS  cash
STK_ID RPT_Date                   
600016 20111231  600016  4.3   NaN
601939 20111231  601939  2.5   NaN

我该怎么做?


29
dropna:http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.dropna.htmldropna()是一个Pandas DataFrame对象的方法,用于删除具有NaN或null值的行或列。默认情况下,它将删除包含任何NaN值的行。您可以使用不同的参数来定制这个方法的行为,例如通过设置“axis”参数来指定要删除的轴。此方法返回一个新的DataFrame对象,不会更改原始对象。 - Wouter Overmeire
276
df.dropna(subset = ['column1_name', 'column2_name', 'column3_name']) - Sergey Orshanskiy
7
df.dropna(subset = ['EPS']) - Ka Wa Yip
3
如果你非常讨厌NaN,可以使用另一种无情的方法df = df.dropna(subset=df.columns.values),这会删除所有包含NaN的行。如果没有任何NaN存在,则不会进行任何更改。 - dejjub-AIS
16个回答

1580

不要删除,只需选择EPS不为NA的行:

df = df[df['EPS'].notna()]

499
我建议使用pandas.notnull而不是np.isfinite - Wes McKinney
31
索引和复制与删除相比是否有优势? - Robert Muil
10
创建错误: TypeError:ufunc“isfinite”不支持输入类型,根据强制转换规则“safe”,无法将输入安全地强制转换为任何受支持的类型。 - Philipp Schwarz
7
请问 @wes-mckinney 能否告诉我在这种情况下,dropna() 是否比pandas.notnull更好?如果是的话,为什么? - stormfield
7
如果列(例如示例中的 EPS 列)包含无法被 np.isfinite() 处理的字符串或其他类型,则会出现此错误。建议使用 pandas.notnull(),它可以更慷慨地处理这种情况。 - normanius
显示剩余3条评论

1205

这个问题已经解决,但是请考虑一下Wouter在他的原始评论中提出的解决方案。pandas 明确地构建了处理缺失数据的能力,包括dropna()。除了潜在的手动操作改进性能之外,这些函数还有许多可能有用的选项。

In [24]: df = pd.DataFrame(np.random.randn(10,3))

In [25]: df.iloc[::2,0] = np.nan; df.iloc[::4,1] = np.nan; df.iloc[::3,2] = np.nan;

In [26]: df
Out[26]:
          0         1         2
0       NaN       NaN       NaN
1  2.677677 -1.466923 -0.750366
2       NaN  0.798002 -0.906038
3  0.672201  0.964789       NaN
4       NaN       NaN  0.050742
5 -1.250970  0.030561 -2.678622
6       NaN  1.036043       NaN
7  0.049896 -0.308003  0.823295
8       NaN       NaN  0.637482
9 -0.310130  0.078891       NaN

In [27]: df.dropna()     #drop all rows that have any NaN values
Out[27]:
          0         1         2
1  2.677677 -1.466923 -0.750366
5 -1.250970  0.030561 -2.678622
7  0.049896 -0.308003  0.823295

In [28]: df.dropna(how='all')     #drop only if ALL columns are NaN
Out[28]:
          0         1         2
1  2.677677 -1.466923 -0.750366
2       NaN  0.798002 -0.906038
3  0.672201  0.964789       NaN
4       NaN       NaN  0.050742
5 -1.250970  0.030561 -2.678622
6       NaN  1.036043       NaN
7  0.049896 -0.308003  0.823295
8       NaN       NaN  0.637482
9 -0.310130  0.078891       NaN

In [29]: df.dropna(thresh=2)   #Drop row if it does not have at least two values that are **not** NaN
Out[29]:
          0         1         2
1  2.677677 -1.466923 -0.750366
2       NaN  0.798002 -0.906038
3  0.672201  0.964789       NaN
5 -1.250970  0.030561 -2.678622
7  0.049896 -0.308003  0.823295
9 -0.310130  0.078891       NaN

In [30]: df.dropna(subset=[1])   #Drop only if NaN in specific column (as asked in the question)
Out[30]:
          0         1         2
1  2.677677 -1.466923 -0.750366
2       NaN  0.798002 -0.906038
3  0.672201  0.964789       NaN
5 -1.250970  0.030561 -2.678622
6       NaN  1.036043       NaN
7  0.049896 -0.308003  0.823295
9 -0.310130  0.078891       NaN

还有其他选择(请参见文档:http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.dropna.html),包括删除列而不是行。

非常方便!


449
你也可以使用 df.dropna(subset = ['column_name'])。希望这能至少为一个人节省额外的5秒钟“我做错了什么”的时间。很棒的答案,点个赞。 - James Tobin
13
@JamesTobin,我刚刚花了20分钟写了一个函数来处理这个问题!官方文档写得很难懂:“考虑其他轴上的标签,例如,如果你要删除行,那么这将是一个包含列名的列表。”我无法理解他们的意思…… - Sergey Orshanskiy
df.dropna(subset = ['column_name']) 就是我正在寻找的!谢谢! - amalik2205
1
这个答案非常有帮助,但是如果有人不清楚哪些选项在哪些情况下有用,我已经在这里发布了一个关于dropna的常见问题解答帖子链接。希望这能帮助那些在应用dropna时遇到困难的人们。 - cs95
2
+1 这个答案似乎也有助于避免在使用 df.dropna(subset = ['column_name'], inplace=True) 时后面出现 SettingWithCopyWarning 的情况。 - cookiemonster
@阿曼,你能看一下这个问题吗? https://dev59.com/kcPra4cB1Zd3GeqPios5 - Aaditya Ura

155

您可以使用这个:

df.dropna(subset=['EPS'], how='all', inplace=True)

42
how='all'在这里是多余的,因为您仅使用一个字段对数据框进行子集化,因此无论使用'all'还是'any'都会产生相同的效果。 - Anton Protopopov
@AntonProtopopov 重要提示: how='all' 不是多余的。定义一个简单的数据框:df = pd.DataFrame({"a": [10, None], "b": [None, 10]})执行 df.dropna(subset=['a', 'b'], how='all') 会保持数据框不变(因为没有行中两列都是 Nan),而省略该参数则返回一个空数据框。 - Enrique Ortiz Casillas
1
@EnriqueOrtizCasillas 我们正在谈论那个特定的情况。在评论中,我提到这只涉及一个字段。对于这种情况,'all''any'是相同的。一般情况下,这取决于您的最终目标。在您的示例中,您正在选择两列 - 这是不同的情况。 - Anton Protopopov

149

我知道这个问题已经得到回答,但为了提供一个纯粹的pandas解决方案,针对这个具体问题,而不是像Aman那样的一般描述(非常好),以防其他人也遇到这个问题:

import pandas as pd
df = df[pd.notnull(df['EPS'])]

14
实际上,具体的答案是:df.dropna(subset=['EPS'])(基于Aman的一般描述,当然这也适用)。 - joris
2
“notnull”也是Wes(Pandas的作者)在另一个答案的评论中提到的建议。 - fantabolous
这可能是一个初学者的问题。但是当我执行df[pd.notnull(...)或df.dropna时,索引会被删除。因此,如果在长度为200的df中的行索引10中有一个空值,则运行drop函数后的数据框具有从1到9,然后从11到200的索引值。是否有任何方法可以“重新索引”它? - Aakash Gupta
你也可以使用 df[pd.notnull(df[df.columns[INDEX]])] 这个语句,其中的 INDEX 是列的编号,如果你不知道列名的话。 - ocean800
由于某种原因,这个答案对我起作用了,而 df.dropna(subset=['column name'] 没有。 - Dr. Mian

67

如何删除Pandas DataFrame中某一列值为NaN的行

这是一个被反复提问的老问题,但我相信仍有一些更有用的信息可以在这个帖子上浮出水面。如果你正在寻找以下任一问题的答案,请继续阅读:

  • 是否可以删除具有NaN值的任何值的行?如果全部都是NaN呢?
  • 是否可以在删除行时只查看特定列中的NaN值?
  • 是否可以删除具有指定数量NaN值的行?
  • 如何删除列而不是行?
  • 我尝试了上述所有选项,但我的DataFrame仍然无法更新!

DataFrame.dropna:用法和示例

已经说过df.dropna是从DataFrame删除NaN的规范方法,但没有什么比一些视觉线索更能帮助你了解。

# Setup
df = pd.DataFrame({
    'A': [np.nan, 2, 3, 4],  
    'B': [np.nan, np.nan, 2, 3], 
    'C': [np.nan]*3 + [3]}) 

df                      
     A    B    C
0  NaN  NaN  NaN
1  2.0  NaN  NaN
2  3.0  2.0  NaN
3  4.0  3.0  3.0

以下是最重要的参数细节及其工作原理,以FAQ格式排列。


如果任何值为NaN,我可以删除行吗? 如果全部为NaN呢?

这就是how=...参数派上用场的地方。它可以是以下之一:

  • 'any'(默认)-如果至少有一列具有NaN,则删除行
  • 'all' - 仅在所有列都有NaN时才删除行

<!_ ->

# Removes all but the last row since there are no NaNs 
df.dropna()

     A    B    C
3  4.0  3.0  3.0

# Removes the first row only
df.dropna(how='all')

     A    B    C
1  2.0  NaN  NaN
2  3.0  2.0  NaN
3  4.0  3.0  3.0

注意
如果您只想查看哪些行为空(也就是说,如果您想要一系列布尔掩码的行),请使用 isna函数:

df.isna()

       A      B      C
0   True   True   True
1  False   True   True
2  False  False   True
3  False  False  False

df.isna().any(axis=1)

0     True
1     True
2     True
3    False
dtype: bool
要获取此结果的反转,请使用notna。这是在删除行时仅查看特定列中的NaN值的用例,您可以使用subset=[...]参数指定要查看的列(或具有axis = 1的索引,以便在删除行或列时只查看这些列(或行)。)。
# Drop all rows with NaNs in A
df.dropna(subset=['A'])

     A    B    C
1  2.0  NaN  NaN
2  3.0  2.0  NaN
3  4.0  3.0  3.0

# Drop all rows with NaNs in A OR B
df.dropna(subset=['A', 'B'])

     A    B    C
2  3.0  2.0  NaN
3  4.0  3.0  3.0

我可以删除特定数量的NaN值吗?

这是使用thresh = ...参数的情况。将最小数量的非空值指定为整数。

df.dropna(thresh=1)  

     A    B    C
1  2.0  NaN  NaN
2  3.0  2.0  NaN
3  4.0  3.0  3.0

df.dropna(thresh=2)

     A    B    C
2  3.0  2.0  NaN
3  4.0  3.0  3.0

df.dropna(thresh=3)

     A    B    C
3  4.0  3.0  3.0

需要注意的是,你需要指定要保留多少个非空值,而不是要删除多少个空值。这对新用户来说是一个痛点。

幸运的是,修复很容易:如果你有一个空值计数,只需从列大小中减去它,就可以得到该函数的正确阈值参数。

required_min_null_values_to_drop = 2 # drop rows with at least 2 NaN
df.dropna(thresh=df.shape[1] - required_min_null_values_to_drop + 1)

     A    B    C
2  3.0  2.0  NaN
3  4.0  3.0  3.0

如何删除列而不是行?

使用axis=...参数,它可以是axis=0axis=1

告诉函数您想要删除的是行(axis=0)还是列(axis=1)。

df.dropna()

     A    B    C
3  4.0  3.0  3.0

# All columns have rows, so the result is empty.
df.dropna(axis=1)

Empty DataFrame
Columns: []
Index: [0, 1, 2, 3]

# Here's a different example requiring the column to have all NaN rows
# to be dropped. In this case no columns satisfy the condition.
df.dropna(axis=1, how='all')

     A    B    C
0  NaN  NaN  NaN
1  2.0  NaN  NaN
2  3.0  2.0  NaN
3  4.0  3.0  3.0

# Here's a different example requiring a column to have at least 2 NON-NULL
# values. Column C has less than 2 NON-NULL values, so it should be dropped.
df.dropna(axis=1, thresh=2)

     A    B
0  NaN  NaN
1  2.0  NaN
2  3.0  2.0
3  4.0  3.0

我尝试了上面所有的选项,但我的DataFrame就是不更新!

dropna,和pandas API中的大多数其他函数一样,返回的是一个新的DataFrame(原始数据的副本,带有更改后的结果),因此如果您想看到更改,应将其重新赋值。

df.dropna(...) # wrong
df.dropna(..., inplace=True) # right, but not recommended
df = df.dropna(...) # right

参考资料

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.dropna.html

DataFrame.dropna(
    self, axis=0, how='any', thresh=None, subset=None, inplace=False)

这里输入图片描述


43

最简单的解决方案:

filtered_df = df[df['EPS'].notnull()]

上述解决方案比使用np.isfinite()要好得多。


32

inplace=True 是一个奇怪的话题,对 DataFrame.dropna() 没有影响。请参见:https://github.com/pandas-dev/pandas/issues/16529 - AMC
3
这个答案与@Joe的答案有何不同?此外,inplace将最终被弃用,最好根本不要使用它。 - misantroop

26

你可以使用数据框架方法notnullisnull的反义词,或numpy.isnan

In [332]: df[df.EPS.notnull()]
Out[332]:
   STK_ID  RPT_Date  STK_ID.1  EPS  cash
2  600016  20111231    600016  4.3   NaN
4  601939  20111231    601939  2.5   NaN


In [334]: df[~df.EPS.isnull()]
Out[334]:
   STK_ID  RPT_Date  STK_ID.1  EPS  cash
2  600016  20111231    600016  4.3   NaN
4  601939  20111231    601939  2.5   NaN


In [347]: df[~np.isnan(df.EPS)]
Out[347]:
   STK_ID  RPT_Date  STK_ID.1  EPS  cash
2  600016  20111231    600016  4.3   NaN
4  601939  20111231    601939  2.5   NaN

15

另一种解决方案利用了这样一个事实:np.nan != np.nan

In [149]: df.query("EPS == EPS")
Out[149]:
                 STK_ID  EPS  cash
STK_ID RPT_Date
600016 20111231  600016  4.3   NaN
601939 20111231  601939  2.5   NaN

4
以下方法对我有用。如果以上方法都无效,这可以帮助您:
df[df['colum_name'].str.len() >= 1]

基本思路是仅在长度大于1时选择记录,如果您正在处理字符串数据,则特别有用。

最好!


1
这仅适用于对象列:AttributeError:如果您的列是浮点数或整数,则只能使用.str访问器与字符串值! - rubengavidia0x

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接