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

How to drop rows of Pandas DataFrame whose value in a certain column is NaN

提问人:bigbug 提问时间:11/16/2012 最后编辑:smcibigbug 更新时间:7/2/2022 访问量:2083657

问:

我有这个,只想要列不是的记录:DataFrameEPSNaN

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

...即,类似于获取此结果 DataFrame:df.drop(....)

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

我该怎么做?

python pandas 数据帧 nan

评论

29赞 Wouter Overmeire 11/16/2012
德罗普娜: pandas.pydata.org/pandas-docs/stable/generated/...
276赞 Sergey Orshanskiy 9/6/2014
df.dropna(subset = ['column1_name', 'column2_name', 'column3_name'])
7赞 Ka Wa Yip 12/29/2021
df.dropna(subset = ['EPS'])
3赞 dejjub-AIS 10/2/2022
另一种无情的方式,如果你非常讨厌 NaN,你发现任何地方都没有 NaNdf = df.dropna(subset=df.columns.values)

答:

1582赞 eumiro 11/16/2012 #1

不要丢弃,只需选择 EPS 不是 NA 的行:

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

评论

31赞 Robert Muil 7/31/2015
索引和复制比删除有什么优势吗?
7赞 stormfield 9/7/2017
在这种情况下,@wes-mckinney 能否让我知道 dropna () 是否比 pandas.notnull 更好?如果是这样,那为什么?
0赞 Cadoiz 6/8/2020
这不包括第 3 行,其中 EPS 为 4.3(有效),现金为 NaN。我希望 OP 也想放弃那个。
6赞 Mohith7548 1/22/2021
我们还可以使用df.dropna(subset=['EPS'])
2赞 Ka Wa Yip 12/29/2021
dropna如果有多列,实际上更快。
1205赞 Aman 11/18/2012 #2

这个问题已经解决了,但是......

...还要考虑 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赞 James Tobin 6/18/2014
您还可以使用 .希望这至少能为一个人节省额外的 5 秒钟“我做错了什么”。很好的答案,+1df.dropna(subset = ['column_name'])
13赞 Sergey Orshanskiy 9/6/2014
@JamesTobin,我只花了 20 分钟为此编写了一个函数!官方文档非常隐晦:“要考虑其他轴上的标签,例如,如果您要删除行,这些将是要包含的列列表”。我无法理解他们的意思......
0赞 amalik2205 12/9/2019
df.dropna(subset = ['column_name'])这正是我想要的!谢谢!
1赞 cs95 6/19/2020
这个答案非常有帮助,但如果有人不清楚哪些选项在哪些情况下有用,我在这里整理了一篇 dropna FAQ 帖子。希望这能帮助那些正在努力申请其特定需求的人。dropna
2赞 cookiemonster 7/3/2021
+1 这个答案似乎也有助于避免以后使用时SettingWithCopyWarningdf.dropna(subset = ['column_name'], inplace=True)
149赞 Kirk Hadley 4/23/2014 #3

我知道这已经得到了回答,但只是为了对这个特定问题有一个纯粹的熊猫解决方案,而不是安缦的一般描述(这很棒),以防其他人遇到这种情况:

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

评论

14赞 joris 4/23/2014
其实具体的答案是:(根据安缦的一般描述,这当然也行得通)df.dropna(subset=['EPS'])
2赞 fantabolous 7/9/2014
notnull也是韦斯(《熊猫》的作者)在对另一个答案的评论中提出的建议。
0赞 Aakash Gupta 3/4/2016
这可能是一个菜鸟问题。但是当我执行 df[pd.notnull(...) 或 df.dropna 时,索引会被删除。因此,如果长度为 200 的 df 中的行索引 10 中有一个 null 值。运行 drop 函数后的数据帧的索引值从 1 到 9,然后是 11 到 200。无论如何要“重新索引”它
0赞 ocean800 11/1/2019
如果您不知道名称,您也可以执行编号列的位置df[pd.notnull(df[df.columns[INDEX]])]INDEX
0赞 Mian Ahmad 6/24/2020
出于某种原因,这个答案对我有用,但对我没有。df.dropna(subset=['column name']
26赞 Anton Protopopov 12/4/2015 #4

您可以使用 dataframe 方法 notnullisnull 的 inverse,或 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
3赞 David 3/15/2016 #5

可以添加“&”可用于添加附加条件,例如

df = df[(df.EPS > 2.0) & (df.EPS <4.0)]

请注意,在评估语句时,pandas 需要括号。

评论

2赞 jezrael 3/16/2016
对不起,但 OP 想要别的东西。顺便说一句,你的代码是错误的,返回.您需要添加括号 - ,但它也不是这个问题的答案。ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().df = df[(df.EPS > 2.0) & (df.EPS <4.0)]
15赞 MaxU - stand with Ukraine 4/21/2017 #6

另一个解决方案使用以下事实: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
156赞 Joe 8/3/2017 #7

您可以使用以下功能:

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

评论

42赞 Anton Protopopov 1/16/2018
how='all'这里是多余的,因为您只用一个字段子集 DataFrame,所以两者都具有相同的效果。'all''any'
0赞 Enrique Ortiz Casillas 10/21/2022
@AntonProtopopov 重要提示:不是多余的。定义一个简单的数据帧:执行操作会使数据帧保持不变(因为没有两列所在的行,而删除该参数将返回一个空的数据帧。how='all'df = pd.DataFrame({"a": [10, None], "b": [None, 10]})df.dropna(subset=['a', 'b'], how='all')Nan
1赞 Anton Protopopov 11/25/2022
@EnriqueOrtizCasillas我们谈论的是那个具体案例。在评论中,我提到它只涉及一个领域。为此,并且是一样的。一般来说,这取决于你的最终目标是什么。在您的示例中,您按两列进行选择 - 这是不同的情况。'all''any'
43赞 Gil Baggio 11/23/2017 #8

最简单的解决方案:

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

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

32赞 Noordeen 1/22/2019 #9

简单易行的方法

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

来源:https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.dropna.html

评论

0赞 AMC 2/16/2020
inplace=True是一个奇怪的话题,对.请参见:github.com/pandas-dev/pandas/issues/16529DataFrame.dropna()
3赞 misantroop 3/28/2020
这个答案与@Joe的答案有何不同?此外,inplace is 最终将被弃用,最好不要使用它。
2赞 Pradeep Singh 12/14/2019 #10

在具有大量列的数据集中,最好查看有多少列包含空值,有多少列不包含空值。

print("No. of columns containing null values")
print(len(df.columns[df.isna().any()]))

print("No. of columns not containing null values")
print(len(df.columns[df.notna().all()]))

print("Total no. of columns in the dataframe")
print(len(df.columns))

例如,在我的数据帧中,它包含 82 列,其中 19 列至少包含一个 null 值。

此外,您还可以根据哪个具有更多 null 值
自动删除列和行 下面是智能执行此操作的代码:

df = df.drop(df.columns[df.isna().sum()>len(df.columns)],axis = 1)
df = df.dropna(axis = 0).reset_index(drop=True)

注意:上面的代码删除了所有 null 值。如果需要 null 值,请先处理它们。

评论

0赞 Pradeep Singh 12/14/2019
还有另一个问题链接
0赞 Moaaz Siddiqui 9/28/2021
这个问题真的被挤出了质疑,明白了吗?:)
4赞 keramat 2/8/2020 #11

另一个版本:

df[~df['EPS'].isna()]

评论

1赞 AMC 2/16/2020
为什么要用这个?Series.notna()
67赞 cs95 6/18/2020 #12

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

这是一个被打死的老问题,但我确实相信在这个线程上有一些更有用的信息。如果您正在寻找以下任何问题的答案,请继续阅读:

  • 如果行的任何值有 NaN,我可以删除行吗?如果它们都是 NaN 呢?
  • 删除行时,我可以只查看特定列中的 NaN 吗?
  • 是否可以删除具有特定 NaN 值计数的行?
  • 如何删除列而不是行?
  • 我尝试了上面的所有选项,但我的 DataFrame 无法更新!

DataFrame.dropna:用法和示例

已经有人说这是从 DataFrame 中删除 NaN 的规范方法,但在此过程中,没有什么比一些视觉提示更能提供帮助了。df.dropna

# 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

注意
:如果您只想查看哪些行为 null (IOW,如果您想要 行的布尔掩码),使用 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=[...]

指定一个列列表(或带有 的索引),以告诉 pandas 在删除行(或带有 .axis=1axis=1axis=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 值计数的行?

这是参数的用例。将 NON-NULL 值的最小数目指定为整数。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

这里需要注意的是,您需要指定要保留的 NON-NULL 值数,而不是要删除的 NULL 值数。这是新用户的痛点。

幸运的是,修复很简单:如果您有 NULL 值的计数,只需从列大小中减去它即可获得函数的正确 thresh 参数。

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=0axis=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)

enter image description here

4赞 Taie 12/8/2021 #13

以下方法对我有用。如果上述方法都不起作用,那将会有所帮助:

df[df['colum_name'].str.len() >= 1]

基本思想是,仅当长度强度大于 1 时,您才拾取记录。如果您正在处理字符串数据,这将特别有用

最好!

评论

1赞 rubengavidia0x 2/8/2022
这仅适用于对象列:AttributeError:只能使用带有字符串值的 .str 访问器!如果列是 float 或 int
-4赞 Simon 2/22/2022 #14

您可以尝试:

df['EPS'].dropna()
3赞 rachwa 7/2/2022 #15

您还可以在查询中使用 notna

In [4]: df.query('EPS.notna().values')
Out[4]: 
                 STK_ID.1  EPS  cash
STK_ID RPT_Date                     
600016 20111231    600016  4.3   NaN
601939 20111231    601939  2.5   NaN
0赞 cottontail 11/29/2023 #16

dropnaVS 布尔索引

如果我们看一下源代码,在引擎盖下,正是 + 布尔索引。取决于传递给 或被调用的内容,将掩码减少为一个系列。dropna()notna()how=all()any()notna

主要区别在于,使用 ,您可以指定要删除的行,而使用布尔索引,您看起来可以指定要保留的行,这在逻辑上是相反的问题。因此,根据用例的不同,从保留非 NaN 行或删除 NaN 行的角度处理删除具有 NaN 值的行的问题可能更直观。dropna()

总而言之,对于任何数据帧,以下情况都是 True:df

df = pd.DataFrame({"A": [1, 2, pd.NA], "B": [pd.NA, 'a', 'b'], "C": [pd.NA, 10, 20]})

cols = ['A', 'B']
x1 = df.dropna(subset=cols, how='any')      # specify which rows to drop
y1 = df[df[cols].notna().all(axis=1)]       # specify which rows to keep
assert x1.equals(y1)

x2 = df.dropna(subset=cols, how='all')
y2 = df[df[cols].notna().any(axis=1)]
assert x2.equals(y2)

此外,参数等同于检查每行中非 NaN 值的数量是否不小于 value;换言之,以下情况为 True:thresh=thresh

thresh = 2
x3 = df[df[cols].count(axis=1) >= thresh]
y3 = df.dropna(subset=cols, thresh=thresh)
assert x3.equals(y3)

现在,如果任务是简单地删除带有 NaN 值的行,那么这是最直观的,应该使用。但是,由于掩码 + 布尔索引更通用,因此您可以定义更复杂的掩码并使用它来筛选。dropna()

例如,假设您要删除列值为 NaN 或 NaN 值超过 1 个的行。这需要使用 2 次函数调用。但是,使用布尔索引,可以使用单个掩码进行筛选。Adropna

msk = (df.isna().sum(axis=1) > 1) | df['A'].isna()
df = df[~msk]

顺便说一句,如果你在修改通过布尔索引构造的数据帧时得到,请考虑将写入时复制模式设置为 True(在此处阅读有关它的更多信息)。SettingWithCopyWarning

pd.set_option('mode.copy_on_write', True)   # turn on copy-on-write

msk = (df.isna().sum(axis=1) > 1) | df['A'].isna()
df1 = df[~msk]
df1['new_col'] = 1                          # <--- no SettingWithCopyWarning