在数据清洗,特别是处理从 Excel 或数据库导入的数据时,经常会遇到各种“空值”。Python 中的空值类型不止一种,判断和转换方法也各不相同。下面系统梳理常见空值的判断,以及在不同场景下的转换方式。
一、Python 中常见的空值类型
| 类型 | 来源 | 典型值 | 特点 |
|---|---|---|---|
None | Python 内置 | None | 单例对象,表示“无”,常用于对象列或数据库 NULL |
float('nan') | Python 内置 | float('nan') | 浮点数,不等于自身(nan != nan) |
numpy.nan (np.nan) | NumPy | np.nan | 同上,但类型为 np.float64 |
pandas.NaT | Pandas | pd.NaT | 表示缺失的时间,兼容 datetime64 / timedelta64 |
pandas.NA | Pandas (≥1.0) | pd.NA | 实验性标量,可用于可空整数、字符串、布尔类型,避免类型提升 |
空字符串 '' | Python 内置 | '' | 长度为 0 的字符串,不是严格意义上的缺失值,但在业务中常当作空值处理 |
空白字符串 ' ' | Python 内置 | ' ' | 全是空白符,也常被当作空值 |
空容器 [], {} | Python 内置 | 空列表、字典等 | |
| 缺失值占位符 | 业务数据 | 如 'NA', 'NULL', 'N/A', '-' | 需要识别并转换为真正的缺失值 |
二、单值判断:如何识别一个变量是否为空
1. None 的判断
x = None
x is None # True- 注意:
x == None虽然可行,但不符合 PEP8,且对numpy数组会返回逐元素结果,推荐用is。
2. float('nan') / np.nan 的判断
import math
import numpy as np
x = float('nan')
# 不能用 ==,因为 nan != nan
math.isnan(x) # True
np.isnan(x) # True (如果是 np.nan 或 float nan)
# 对于非浮点类型会报错,如 np.isnan(None) 会引发 TypeErrornp.nan和float('nan')在math.isnan和np.isnan下等价,但np.isnan可接受数组。
3. pd.NaT 的判断
import pandas as pd
x = pd.NaT
pd.isna(x) # True
pd.isnull(x) # True (isnull 是 isna 的别名)
# x is pd.NaT # 也成立,但 pd.NaT 是单例,推荐使用 pd.isna4. pd.NA 的判断
x = pd.NA
pd.isna(x) # True5. 空字符串 / 空白字符串 / 占位符的判断
s = ''
if s == '':
# 空字符串
if s.strip() == '':
# 空或只有空白- 对于
None或np.nan,直接.strip()会报错,需先判断。
通用判断函数对比
| 方法 | 识别 None | 识别 np.nan | 识别 pd.NaT | 识别 pd.NA | 识别 ” | 备注 |
|---|---|---|---|---|---|---|
x is None | ✔️ | ❌ | ❌ | ❌ | ❌ | 仅 None |
math.isnan(x) | ❌ (报错) | ✔️ | ❌ (报错) | ❌ (报错) | ❌ | 仅 float nan |
np.isnan(x) | ❌ (报错) | ✔️ | ❌ (报错) | ❌ (报错) | ❌ | 仅浮点 nan,可处理数组 |
pd.isna(x) | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | 推荐,覆盖最广 |
pd.isnull(x) | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | 与 isna 相同 |
# 4. 综合判断:None / 空字符串 / NaN / 空列表等
def is_empty(v):
if v is None:
return True
if isinstance(v, float) and math.isnan(v):
return True
if isinstance(v, str) and v.strip() == "":
return True
if isinstance(v, (list, tuple, dict, set)) and len(v) == 0:
return True
return False最佳实践:在数据清洗时,统一使用
pd.isna()/pd.isnull()来判断各种缺失值标量。
三、批量判断:Pandas DataFrame / Series 中的空值
import pandas as pd
import numpy as np
df = pd.DataFrame({
'A': [1, None, 3],
'B': [np.nan, 'text', ''],
'C': [pd.NaT, pd.Timestamp('2020-01-01'), pd.NaT],
'D': [pd.NA, 2, pd.NA],
})1. 检测所有缺失值
df.isna() # 返回布尔 DataFrame,对 None/np.nan/NaT/NA 均为 True,对 '' 为 False
df.isnull() # 完全等价2. 检测非缺失值
df.notna()3. 统计缺失值
df.isna().sum() # 每列缺失值个数
df.isna().sum().sum() # 总缺失值个数4. 将空字符串也视为缺失值
# 替换所有空字符串为 NaN
df.replace('', np.nan, inplace=True)
# 或
df['B'] = df['B'].replace('', np.nan)
# 空白字符串同理
df.replace(r'^\s*$', np.nan, regex=True, inplace=True)5. 判断某列是否存在缺失值
df['A'].hasnans # True 如果列中包含 NaN 或 NaT (仅对数值/日期列有效)
# 通用
df['A'].isna().any()四、空值的转换与清洗
1. 统一缺失值表示:将各种“空”变成标准的 NaN / None
一个常见场景:从 Excel 或 CSV 读入后,缺失值可能有 None、np.nan、'NA'、'NULL'、''、'-' 等。
# 定义需要替换的占位符列表
na_values = ['NA', 'NULL', 'N/A', '-', '']
# 读取时直接处理(以 read_csv 为例)
df = pd.read_csv('file.csv', na_values=na_values, keep_default_na=True)
# 或读取后替换
df.replace(na_values, np.nan, inplace=True)
# 同时去除字符串首尾空白后视为缺失
df = df.applymap(lambda x: np.nan if isinstance(x, str) and x.strip() == '' else x)2. 填充缺失值 (fillna)
# 用常数填充
df['A'].fillna(0, inplace=True)
# 用均值/中位数填充
df['A'].fillna(df['A'].mean(), inplace=True)
# 用前一个值填充(向前填充)
df['B'].ffill()
# 用字典对不同列指定不同填充值
df.fillna({'A': 0, 'B': 'unknown', 'C': pd.Timestamp.now()}, inplace=True)
# 对于 pd.NA 兼容的可空类型,可直接填充 None 或 pd.NA
df['D'].fillna(0, inplace=True) # pd.NA 也可以接受数值填充3. 删除缺失值 (dropna)
# 删除任何包含缺失值的行
df.dropna(inplace=True)
# 仅当某列缺失才删除该行
df.dropna(subset=['A', 'C'], inplace=True)
# 删除全为空的行或列
df.dropna(how='all', inplace=True)
df.dropna(axis=1, how='all', inplace=True)
# 保留至少有 n 个非空值的行
df.dropna(thresh=2, inplace=True)4. 类型兼容:让整型列支持缺失值
普通的 int64 列无法容纳 NaN,一旦出现缺失值就会被自动提升为 float64。解决方法:
# 使用 Pandas 的可空整数类型
df['A'] = df['A'].astype('Int64') # 注意 I 大写,这是 pd.Int64Dtype()
# 现在缺失值显示为 <NA>,列为 Int64 类型,不再是 float对于布尔类型、字符串类型也有类似的扩展类型:
boolean(可空布尔)string(可空字符串,与 object 不同,存pd.NA而非NaN)
df['B'] = df['B'].astype('string')5. 写出数据时的空值转换
写入 Excel
# pandas 默认会将 NaN/NaT 写为空单元格,将 None 写为空单元格
df.to_excel('output.xlsx', na_rep='') # 可以用 na_rep 指定替代文本
# 对于 Int64 等可空类型,<NA> 也会写为空单元格写入数据库 (MySQL)
# 用 to_sql 时,NaN/NaT 自动映射为 SQL NULL
from sqlalchemy import create_engine
engine = create_engine('mysql+pymysql://...')
df.to_sql('table_name', engine, if_exists='replace', index=False)
# 如果自己组装 SQL,需要将 None 作为 NULL 插入
import pymysql
conn = pymysql.connect(...)
cursor = conn.cursor()
# 将 np.nan / pd.NaT / pd.NA 转换为 None
value = None if pd.isna(value) else value
cursor.execute("INSERT INTO table (col) VALUES (%s)", (value,))通用转换函数示例:
def to_sql_value(val):
"""将 Python 空值统一转换为 None,用于插入数据库"""
if val is None:
return None
if isinstance(val, float) and math.isnan(val):
return None
if isinstance(val, pd.Timestamp) and pd.isna(val):
return None
if hasattr(val, '__module__') and 'pandas' in val.__module__:
if pd.isna(val):
return None
return val6. 其他转换技巧
- 用
numpy.where或Series.where:
df['A'] = df['A'].where(df['A'].notna(), other=0) # 缺失值替换为0- 布尔值列,将 NaN 填充为 False:
df['flag'] = df['flag'].fillna(False).astype(bool)- 使用
coalesce逻辑选择第一个非空值:
# 相当于 SQL 的 COALESCE(A, B)
df['A'] = df['A'].combine_first(df['B'])
# 或
df['A'] = df['A'].fillna(df['B'])五、注意事项总结
np.nan具有传染性:任何与np.nan的算术运算结果都是np.nan。- 整型列缺失值会自动提升为
float64,需要可空整数类型用'Int64'。 None在某些情况下自动转为np.nan:当None被放入float64或datetime64数组时。pd.isna('')返回False,需要显式处理空字符串。- 判断不要用
== np.nan,始终用math.isnan、np.isnan或pd.isna。 - 输出到数据库时,记得将
np.nan、pd.NaT等转为None,否则可能报错或写入非预期的值。
掌握这些判断和转换方法后,处理 Excel、数据库导入的数据中的空值就会非常得心应手。