0

0

Python数据清洗:高效移除JSON文件中的NaN值

DDD

DDD

发布时间:2025-09-14 09:49:23

|

305人浏览过

|

来源于php中文网

原创

Python数据清洗:高效移除JSON文件中的NaN值

本教程旨在指导如何使用Python准确地从JSON数据中移除NaN(非数字)值。文章将详细阐述NaN与null(Python中的None)的区别,并提供一个基于math.isnan()的健壮解决方案,以实现选择性地过滤掉包含NaN的键值对,从而确保数据纯净性,同时保留合法的null值。

引言:理解JSON中的NaN与Null

在数据处理和交换中,json(javascript object notation)是一种广泛使用的轻量级数据格式。然而,在从数据库、科学计算或数据分析工具(如pandas)导出数据时,我们经常会遇到两种特殊的值:nan(not a number,非数字)和null。尽管它们都表示“缺失”或“无效”的概念,但在语义和处理方式上却有着本质的区别:

  • NaN: 通常来源于浮点数运算的无效结果(如0/0,sqrt(-1))或数据集中表示缺失的浮点数。在Python中,NaN由float('nan')表示,它是一个浮点数类型。一个关键特性是NaN不等于自身(NaN != NaN),这使得直接比较变得复杂。
  • Null: 在JSON中表示一个空值或缺失值,对应于Python中的None。它是一个独立的类型,与数字类型无关,并且可以被视为有效但为空的数据。

我们的目标是精确地移除JSON数据中所有值为NaN的键值对,同时保留值为null(Python中的None)的键值对。例如,{"height": null}应该被保留,而{"weight": NaN}则应该被移除。

核心挑战:识别NaN的特殊性

由于NaN != NaN的特性,我们不能简单地使用value == float('nan')来判断一个值是否为NaN。Python的math模块提供了一个专门用于此目的的函数:math.isnan()。

为了准确识别一个值是否为NaN,我们需要两个条件:

  1. 类型检查: 确保该值首先是一个浮点数类型。因为math.isnan()只适用于浮点数。
  2. NaN判断: 使用math.isnan()来确认这个浮点数是否确实是NaN。

因此,判断一个值value是否为NaN的可靠条件是 isinstance(value, float) and math.isnan(value)。

立即学习Python免费学习笔记(深入)”;

解决方案:构建NaN移除函数

我们将创建一个辅助函数remove_nans,它接收一个字典对象,并返回一个移除了所有NaN键值对的新字典。这个函数将利用字典推导式和上述的NaN识别逻辑。

首先,假设我们有一个包含多个JSON对象的列表,其中一些对象包含NaN和null值:

import math
import json

# 模拟输入JSON数据
# 注意:在实际的JSON文件中,NaN通常会被json.loads()转换为float('nan')
# 或者在序列化时被json.dumps()转换为null。
# 这里我们直接构造Python对象来模拟解析后的数据。
data = [
    {
        "name": "John Doe",
        "age": 30,
        "height": None,  # JSON null
        "weight": float('nan'), # JSON NaN
        "city": "New York"
    },
    {
        "name": "Jim Hanks",
        "age": float('nan'),
        "height": float('nan'),
        "weight": float('nan'),
        "occupation": None
    },
    {
        "id": 101,
        "value": 123.45,
        "status": "active"
    }
]

print("原始数据示例:")
for item in data:
    print(item)
print("-" * 30)

# 定义移除NaN的函数
def remove_nans(obj):
    """
    从字典对象中移除所有值为NaN的键值对。
    保留None(JSON null)值。
    """
    # 使用字典推导式遍历所有键值对
    # 条件:如果值不是浮点数NaN,则保留该键值对
    return {
        key: value
        for key, value in obj.items()
        if not (isinstance(value, float) and math.isnan(value))
    }

# 应用函数到数据列表中的每个字典
processed_data = [remove_nans(row) for row in data]

print("处理后的数据示例:")
for item in processed_data:
    print(item)

代码解析:

  • import math 和 import json: 导入必要的模块。math用于isnan函数,json虽然在此示例中直接构造了Python对象,但在实际应用中会用于加载和保存JSON文件。
  • data: 这是一个列表,其中包含了多个字典,每个字典代表一个JSON对象。我们手动将NaN表示为float('nan'),将null表示为None,以模拟JSON解析后的Python对象。
  • remove_nans(obj) 函数:
    • 它接收一个字典obj作为输入。
    • {key: value for key, value in obj.items() if ...} 是一个字典推导式,它遍历输入字典的所有键值对。
    • if not (isinstance(value, float) and math.isnan(value)) 是核心过滤条件。
      • isinstance(value, float) 检查value是否为浮点数类型。
      • math.isnan(value) 检查该浮点数是否为NaN。
      • not (...) 表示只有当value不是一个NaN浮点数时,才将该键值对保留在新字典中。
      • 例如,如果value是None,isinstance(None, float)为False,整个条件not (False and ...)为True,所以None会被保留。
      • 如果value是float('nan'),isinstance(float('nan'), float)为True,math.isnan(float('nan'))为True,整个条件not (True and True)为False,所以float('nan')会被移除。
  • processed_data = [remove_nans(row) for row in data]: 使用列表推导式,将remove_nans函数应用于data列表中的每个字典,生成一个全新的、经过清洗的字典列表。

完整示例代码

为了展示一个更完整的流程,包括从JSON字符串加载数据和最终输出,我们可以结合json模块:

Insou AI
Insou AI

Insou AI 是一款强大的人工智能助手,旨在帮助你轻松创建引人入胜的内容和令人印象深刻的演示。

下载
import math
import json

# 模拟原始JSON字符串数据
# 注意:在JSON标准中,NaN不是一个合法的字面量。
# 通常,float('nan')在json.dumps时会被转换为null。
# 但如果JSON文件是从某些非标准源生成,可能包含字符串"NaN"。
# 本教程假设我们处理的是解析后Python对象中的float('nan')。
json_string = """
[
  {
    "name": "John Doe",
    "age": 30,
    "height": null,
    "weight": NaN,
    "city": "New York"
  },
  {
    "name": "Jim Hanks",
    "age": NaN,
    "height": NaN,
    "weight": NaN,
    "occupation": null
  },
  {
    "id": 101,
    "value": 123.45,
    "status": "active"
  }
]
"""

# 为了让json.loads能够处理非标准的"NaN"字符串,需要自定义parse_constant
# 否则,如果json_string中直接是"NaN",json.loads会报错。
# 如果实际JSON文件中的NaN是"null",则不需要这一步。
# 这里我们假设数据源已经正确地将NaN转换为Python的float('nan')。
# 如果json_string中直接是NaN,需要这样处理:
# import re
# json_string_parsed = re.sub(r'NaN', 'null', json_string) # 或者其他处理
# data_from_json = json.loads(json_string_parsed)

# 更直接模拟问题中的情况,假设json.loads能够处理或我们直接构造了包含float('nan')的Python对象
# 实际的json.loads()默认会将NaN转换为null,除非自定义parser。
# 因此,为了匹配问题中“weight: NaN”在Python中被识别为float('nan')的场景,
# 我们直接使用前面构造的Python对象来演示。
data_from_json = [
    {
        "name": "John Doe",
        "age": 30,
        "height": None,
        "weight": float('nan'),
        "city": "New York"
    },
    {
        "name": "Jim Hanks",
        "age": float('nan'),
        "height": float('nan'),
        "weight": float('nan'),
        "occupation": None
    },
    {
        "id": 101,
        "value": 123.45,
        "status": "active"
    }
]


print("--- 原始数据(Python对象形式)---")
print(json.dumps(data_from_json, indent=2, default=lambda x: str(x) if math.isnan(x) else x)) # 打印时将NaN转换为字符串显示

# 定义移除NaN的函数
def remove_nans(obj):
    """
    从字典对象中移除所有值为NaN的键值对。
    保留None(JSON null)值。
    """
    return {
        key: value
        for key, value in obj.items()
        if not (isinstance(value, float) and math.isnan(value))
    }

# 应用函数到数据列表中的每个字典
processed_data = [remove_nans(row) for row in data_from_json]

print("\n--- 处理后的数据(Python对象形式)---")
print(json.dumps(processed_data, indent=2))

# 期望的JSON输出格式:
# { "name": "John Doe", "age": 30, "height": null, "city": "New York" }
# { "name": "Jim Hanks", "occupation": null }
# { "id": 101, "value": 123.45, "status": "active" }

运行上述代码,你会看到weight、age和height中所有float('nan')值对应的键值对都被成功移除,而null(None)值则被保留。

注意事项与最佳实践

  1. 输入数据格式的假设: 本教程的核心是处理Python对象中float('nan')形式的NaN。

    • 如果JSON文件中的NaN是字符串"NaN": json.loads()在默认情况下会报错,因为"NaN"不是一个合法的JSON值。你需要预处理JSON字符串(例如使用str.replace('"NaN"', 'null'))或提供自定义的parse_constant函数给json.loads()。
    • json.dumps()的默认行为: 当将包含float('nan')的Python对象序列化为JSON字符串时,json.dumps()通常会将float('nan')转换为null。如果你的目标是输出一个不含null的JSON,那么在序列化之前移除NaN是必要的。
  2. 处理嵌套结构: 上述remove_nans函数仅适用于单层字典。如果你的JSON数据包含嵌套的字典或列表,你需要一个递归函数来遍历所有层级。

    def remove_nans_recursive(obj):
        if isinstance(obj, dict):
            return {
                key: remove_nans_recursive(value)
                for key, value in obj.items()
                if not (isinstance(value, float) and math.isnan(value))
            }
        elif isinstance(obj, list):
            return [remove_nans_recursive(elem) for elem in obj]
        else:
            # 对于非字典、非列表的叶子节点,直接返回其值
            # 确保NaN浮点数不被保留
            if isinstance(obj, float) and math.isnan(obj):
                return None # 或者其他处理,这里为了兼容可以返回None,但通常应该在父级被过滤
            return obj

    请注意,在递归版本中,如果一个叶子节点是NaN,它最终会被父级字典的过滤条件移除。如果递归到它本身,它会被if isinstance(obj, float) and math.isnan(obj): return None处理。更严谨的做法是让父级负责过滤,因此叶子节点可以直接返回obj。

    # 改进后的递归版本,确保过滤逻辑在字典层级生效
    def remove_nans_recursive_v2(obj):
        if isinstance(obj, dict):
            cleaned_dict = {}
            for key, value in obj.items():
                if not (isinstance(value, float) and math.isnan(value)):
                    cleaned_dict[key] = remove_nans_recursive_v2(value)
            return cleaned_dict
        elif isinstance(obj, list):
            return [remove_nans_recursive_v2(elem) for elem in obj]
        else:
            return obj # 非字典、非列表的叶子节点直接返回
  3. 性能考量: 对于非常大的JSON文件或数据流,一次性加载到内存并处理可能效率低下。在这种情况下,可以考虑使用迭代器或流式解析库(如ijson)来逐块处理数据。

  4. None与NaN的区分: 再次强调,本教程的解决方案精确地区分了None和NaN。如果你也想移除null值,只需修改过滤条件,例如 if value is not None and not (isinstance(value, float) and math.isnan(value))。

总结

通过本教程,我们学习了如何在Python中利用math.isnan()函数,结合类型检查,高效且精确地从JSON数据中移除NaN值。这种方法不仅能够处理常见的NaN场景,还能确保null值得到正确保留,从而满足严格的数据清洗要求。掌握这一技巧对于任何处理外部数据源并需要维护数据质量的Python开发者都至关重要。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

457

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

549

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

337

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

82

2025.09.10

Python 时间序列分析与预测
Python 时间序列分析与预测

本专题专注讲解 Python 在时间序列数据处理与预测建模中的实战技巧,涵盖时间索引处理、周期性与趋势分解、平稳性检测、ARIMA/SARIMA 模型构建、预测误差评估,以及基于实际业务场景的时间序列项目实操,帮助学习者掌握从数据预处理到模型预测的完整时序分析能力。

81

2025.12.04

Python 数据清洗与预处理实战
Python 数据清洗与预处理实战

本专题系统讲解 Python 在数据清洗与预处理中的核心技术,包括使用 Pandas 进行缺失值处理、异常值检测、数据格式化、特征工程与数据转换,结合 NumPy 高效处理大规模数据。通过实战案例,帮助学习者掌握 如何处理混乱、不完整数据,为后续数据分析与机器学习模型训练打下坚实基础。

34

2026.01.31

css中float用法
css中float用法

css中float属性允许元素脱离文档流并沿其父元素边缘排列,用于创建并排列、对齐文本图像、浮动菜单边栏和重叠元素。想了解更多float的相关内容,可以阅读本专题下面的文章。

595

2024.04.28

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

108

2025.10.23

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

49

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 6.1万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 3.5万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号