Python Pandas 异常数据处理:NaN 检查、填充与范围裁剪方法

本文介绍了在 pandas 中处理异常数据的常用方法,涵盖了 NaN 值的查找、移除、填充以及超出合理范围值的裁剪。通过 isna()dropna()fillna() 等方法,可以有效处理缺失数据。文章展示了如何按列均值或按规律填充缺失值,以及使用 clip() 函数限定数值范围,以避免异常值对分析结果的干扰。配合代码和图表示例,本文为数据清洗提供了实用的 pandas 操作技巧,使数据分析更加精确和直观。

引入第三方库

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

一 主要功能

找到NaN数据移除NaN填充NaN处理不符合范围的值
pd.isna()df.dropna()df.fillna()df.clip()
pd.notna()

二 查找NaN数据

    # 创建一个包含 NaN 的 DataFrame
    df = pd.DataFrame([[1, None], [np.nan, 4]])
    print(df)
    print(df.isna())  # 查找 NaN 值
    print(~df.isna())  # 反转 NaN 查找结果,得到非 NaN 值的位置
    print(df.notna())  # 另一种查找非 NaN 值的方法

三 pandas 中NaN值

		# 创建包含 NaN 值的 DataFrame
    df = pd.DataFrame({
        "a": [1, None, 1],
        "b": [np.nan, 4, 3]
    })
    print(df)
    print("skipped NaN:\n", df.mean(axis=0))  # 默认跳过 NaN 计算均值
    print("\n\nnot skipped:\n", df.mean(axis=0, skipna=False))  # 不跳过 NaN 计算均值
1 df.mean(axis=0)

df.mean(axis=0) 默认参数 skipna=True,会跳过缺失值 NaN。计算结果如下:

  • "a":有效值为 1.01.0,平均值为 (1 + 1) / 2 = 1.0
  • "b":有效值为 4.03.0,平均值为 (4 + 3) / 2 = 3.5
2 skipna=False

使用 skipna=False 计算平均值时,不会跳过 NaN 值。如果列中包含任何 NaN 值,则该列的平均值结果为 NaN

  • "a":包含 NaN,结果为 NaN
  • "b":包含 NaN,结果也为 NaN
划重点

pandas 中,计算均值时默认跳过 NaN 值,不会将 NaN 计入运算。如果 NaN 被计入,那么均值的计算就会将 NaN 视为一个数据项,从而影响数据的数量。例如,在包含 [1, NaN, 1] 的数据中,如果考虑 NaN,均值会是 (1 + 1) / 3 而不是 (1 + 1) / 2

四 移除NaN

   # 创建包含 NaN 值的 DataFrame
   df = pd.DataFrame({
        "a": [1, None, 3],
        "b": [4, 5, 6]
    })
    print(df)
    print(df.dropna(axis=0))  # 删除包含 NaN 值的行
    print(df.dropna(axis=1))  # 删除包含 NaN 值的列
  • df.dropna(axis=0) 删除包含 NaN 值的行。
  • df.dropna(axis=1) 删除包含 NaN 值的列。

五 填充NaN

1 常用的均值填充
		# 使用均值填充缺失值
    df = pd.DataFrame({
        "a": [1, None, 3],
        "b": [4, 5, 6]
    })
    print(df)
    a_mean = df["a"].mean()  # 计算列 "a" 的平均值
    new_col = df["a"].fillna(a_mean)  # 用平均值填充缺失值
    df["a"] = new_col  # 更新 DataFrame 中的 "a" 列
    print(df)

计算列的平均值并用其填充缺失值,更新 DataFrame 中的 NaN。

2 按数据的规律填充
1)使用 df.fillna 填充
		# 根据列 "b" 中的规律填充列 "a" 中的 NaN 值
    df = pd.DataFrame({
        "a": [1, None, 3, None],
        "b": [4, 8, 12, 12]
    })
    a_nan = df["a"].isna()  # 查找列 "a" 中的 NaN
    a_new_value = df["b"][a_nan] / 4  # 计算填充值
    new_col = df["a"].fillna(a_new_value)  # 用计算的值填充缺失值
    df["a"] = new_col  # 更新 DataFrame 中的 "a" 列
    print(df)

用列 "b" 中对应的值除以 4 的结果填充列 "a" 中的缺失值。

2)使用 df.loc 填充
		# 使用 df.loc 方法按规律填充缺失值
    df = pd.DataFrame({
        "a": [1, None, 3, None],
        "b": [4, 8, 12, 12]
    })
    a_nan = df["a"].isna()  # 查找列 "a" 中的 NaN
    df.loc[a_nan, "a"] = df["b"][a_nan] / 4  # 按规律填充缺失值
    print(df)

六 不符合范围的值

 	  # 创建包含异常值的 DataFrame
    df = pd.DataFrame({
        "a": [1, 1, 2, 1, 2, 40, 1, 2, 1],
    })
    df.plot()  # 初始数据的图表
    df["a"] = df["a"].clip(lower=0, upper=3)  # 截取值,将所有值限制在 0 到 3 之间
    df.plot()  # 截取后的图表
    plt.show()

由于存在异常值 40,初始图表可能会拉高纵轴范围,使得其他数据点在视觉上变得不明显。使用 clip(lower=0, upper=3) 对列 "a" 的值进行截取,将所有低于 0 的值替换为 0,所有高于 3 的值替换为 3。这样一来,异常值 40 会被替换为 3,确保数据在指定的范围内。

在这里插入图片描述

截取前的图表展示了原始数据的异常值。截取后,所有数据点都被限制在 [0, 3] 范围内,消除了异常值的影响,使图表更直观。

在这里插入图片描述

七 完整代码示例

# This is a sample Python script.

# Press ⌃R to execute it or replace it with your code.
# Press Double ⇧ to search everywhere for classes, files, tool windows, actions, and settings.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt


def print_hi(name):
    # Use a breakpoint in the code line below to debug your script.
    print(f'Hi, {name}')  # Press ⌘F8 to toggle the breakpoint.
    # 找到NaN数据
    # pd.isna(), pd.notna()
    # NaN的影响
    # 移除NaN
    # df.dropna()
    # 填充NaN
    # df.fillna()
    # 不符合范围的值
    # df.clip()

    # 找到NaN数据
    df = pd.DataFrame([[1, None], [np.nan, 4]])
    print(df)
    print(df.isna())
    print(~df.isna())
    print(df.notna())

    # NaN的影响
    # pandas 是不考虑 NaN 值的,也就是说不会把 NaN 带入运算,如果考虑 NaN 的话, mean 就会是 (1+1)/3 和 (4+4)/3 因为有三个数据。
    df = pd.DataFrame({
        "a": [1, None, 1],
        "b": [np.nan, 4, 3]
    })
    print(df)
    print("skipped NaN:\n", df.mean(axis=0))
    print("\n\nnot skipped:\n", df.mean(axis=0, skipna=False))

    # 移除NaN
    df = pd.DataFrame({
        "a": [1, None, 3],
        "b": [4, 5, 6]
    })
    print(df)
    print(df.dropna(axis=0))
    print(df.dropna(axis=1))
    print()
    # 填充NaN
    df = pd.DataFrame({
        "a": [1, None, 3],
        "b": [4, 5, 6]
    })
    print(df)
    a_mean = df["a"].mean()
    print(a_mean)
    new_col = df["a"].fillna(a_mean)
    print(new_col)
    df["a"] = new_col
    print(df)
    print()
    # 有规律的数据填充
    # 使用fillna填充
    df = pd.DataFrame({
        "a": [1, None, 3, None],
        "b": [4, 8, 12, 12]
    })
    print(df)
    a_nan = df["a"].isna()
    print(a_nan)
    a_new_value = df["b"][a_nan] / 4
    print(a_new_value)
    new_col = df["a"].fillna(a_new_value)
    print(new_col)
    df["a"] = new_col
    print(df)
    print()
    # 使用 loc 填充
    df = pd.DataFrame({
        "a": [1, None, 3, None],
        "b": [4, 8, 12, 12]
    })
    a_nan = df["a"].isna()
    print(a_nan)
    print(df.loc[a_nan, "a"])
    print(df["b"][a_nan] / 4)
    df.loc[a_nan, "a"] = df["b"][a_nan] / 4
    print(df)

    # 不符合范围的值
    df = pd.DataFrame({
        "a": [1, 1, 2, 1, 2, 40, 1, 2, 1],
    })
    df.plot()
    df["a"] = df["a"].clip(lower=0, upper=3)
    df.plot()
    plt.show()


# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    print_hi('异常数据处理')

# See PyCharm help at https://www.jetbrains.com/help/pycharm/

复制粘贴并覆盖到你的 main.py 中运行,运行结果如下。

Hi, 异常数据处理
     0    1
0  1.0  NaN
1  NaN  4.0
       0      1
0  False   True
1   True  False
       0      1
0   True  False
1  False   True
       0      1
0   True  False
1  False   True
     a    b
0  1.0  NaN
1  NaN  4.0
2  1.0  3.0
skipped NaN:
 a    1.0
b    3.5
dtype: float64


not skipped:
 a   NaN
b   NaN
dtype: float64
     a  b
0  1.0  4
1  NaN  5
2  3.0  6
     a  b
0  1.0  4
2  3.0  6
   b
0  4
1  5
2  6

     a  b
0  1.0  4
1  NaN  5
2  3.0  6
2.0
0    1.0
1    2.0
2    3.0
Name: a, dtype: float64
     a  b
0  1.0  4
1  2.0  5
2  3.0  6

     a   b
0  1.0   4
1  NaN   8
2  3.0  12
3  NaN  12
0    False
1     True
2    False
3     True
Name: a, dtype: bool
1    2.0
3    3.0
Name: b, dtype: float64
0    1.0
1    2.0
2    3.0
3    3.0
Name: a, dtype: float64
     a   b
0  1.0   4
1  2.0   8
2  3.0  12
3  3.0  12

0    False
1     True
2    False
3     True
Name: a, dtype: bool
1   NaN
3   NaN
Name: a, dtype: float64
1    2.0
3    3.0
Name: b, dtype: float64
     a   b
0  1.0   4
1  2.0   8
2  3.0  12
3  3.0  12

八 源码地址

代码地址:

国内看 Giteepandas/异常数据处理.py

国外看 GitHubpandas/异常数据处理.py

引用 莫烦 Python

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐