← 返回博客

数据科学中的 JSON:Python 和 Pandas 指南

JSON 在数据科学工作流中的完整指南。学习用 Python、Pandas 处理 JSON 并集成到 ML 管道中。

Big JSON Team13 分钟阅读programming
B

Big JSON Team

Technical Writer

Expert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.

13 分钟阅读

# 数据科学中的 JSON:Python 和 Pandas 指南

JSON 在数据科学中无处不在。它用于 API 数据、NoSQL 数据库、配置文件和机器学习管道。本指南展示如何在数据科学工作流中有效地使用 JSON。

JSON 在数据科学中的角色

常见用途

  • API 数据 - 从 REST API 获取数据
  • 数据库 - MongoDB、Firestore 等使用 JSON
  • 配置 - 模型配置、参数文件
  • 日志 - 结构化日志记录(JSON Lines)
  • 交换格式 - 系统之间的数据交换
  • 为什么 JSON 很重要

    • 灵活 - 支持嵌套和不同类型
    • 标准 - 广泛支持的格式
    • 可读 - 人类可读的结构
    • 轻量 - 比 XML 更紧凑

    使用 Pandas 加载 JSON

    基本加载

    import pandas as pd
    
    

    # 从文件加载

    df = pd.read_json('data.json')

    # 从字符串加载

    df = pd.read_json(json_string)

    # 从 URL 加载

    df = pd.read_json('https://api.example.com/data')

    # 查看数据

    print(df.head())

    print(df.info())

    JSON 行格式

    JSON Lines(或 JSONL)是每行一个 JSON 对象的文本格式。非常适合流式处理。

    # JSON Lines 文件
    

    # {"name": "张三", "age": 30}

    # {"name": "李四", "age": 25}

    df = pd.read_json('data.jsonl', lines=True)

    orient 参数

    JSON 可以有不同的结构:

    # 记录导向(数组中的对象)
    

    # [{"name": "张三", "age": 30}, ...]

    df = pd.read_json(json_str, orient='records')

    # 列导向

    # {"name": ["张三", "李四"], "age": [30, 25]}

    df = pd.read_json(json_str, orient='columns') # 默认

    # 索引导向

    # {"0": {"name": "张三", "age": 30}, ...}

    df = pd.read_json(json_str, orient='index')

    # 分割导向

    # {"index": [0, 1], "columns": ["name", "age"], "data": [...]}

    df = pd.read_json(json_str, orient='split')

    处理嵌套 JSON

    大多数现实世界的 JSON 是嵌套的。Pandas 提供了处理嵌套结构的工具。

    json_normalize 用于简单嵌套

    import pandas as pd
    
    

    # 嵌套数据

    data = {

    "users": [

    {

    "name": "张三",

    "age": 30,

    "address": {

    "city": "北京",

    "zip": "100000"

    }

    },

    {

    "name": "李四",

    "age": 25,

    "address": {

    "city": "上海",

    "zip": "200000"

    }

    }

    ]

    }

    # 展平嵌套数据

    df = pd.json_normalize(data['users'])

    # 结果列:

    # name age address.city address.zip

    # 张三 30 北京 100000

    # 李四 25 上海 200000

    处理深度嵌套的数据

    对于深度嵌套的结构,使用递归函数:

    import pandas as pd
    

    import json

    def flatten_json(nested_json, sep='_'):

    """递归展平嵌套 JSON"""

    out = {}

    def flatten(x, name=''):

    if type(x) is dict:

    for a in x:

    flatten(x[a], name + a + sep)

    elif type(x) is list:

    i = 0

    for item in x:

    flatten(item, name + '[' + str(i) + ']' + sep)

    i += 1

    else:

    out[name[:-1]] = x

    flatten(nested_json)

    return out

    # 使用

    data = json.load(open('deeply_nested.json'))

    flat_data = flatten_json(data)

    df = pd.DataFrame([flat_data])

    从 API 加载数据

    简单 API 请求

    import pandas as pd
    

    import requests

    # 获取数据

    response = requests.get('https://api.example.com/users')

    data = response.json()

    # 转换为 DataFrame

    df = pd.DataFrame(data)

    # 如果响应是嵌套的

    if isinstance(data, dict):

    df = pd.json_normalize(data['results'])

    elif isinstance(data, list):

    df = pd.DataFrame(data)

    处理分页

    import pandas as pd
    

    import requests

    def load_paginated_api(url, page_param='page'):

    """从分页 API 加载所有数据"""

    all_data = []

    page = 1

    while True:

    response = requests.get(url, params={page_param: page})

    data = response.json()

    if not data or (isinstance(data, dict) and 'results' not in data):

    break

    # 处理不同的 API 格式

    results = data.get('results', data)

    all_data.extend(results if isinstance(results, list) else [results])

    page += 1

    return pd.DataFrame(all_data)

    # 使用

    df = load_paginated_api('https://api.example.com/users')

    API 认证

    import pandas as pd
    

    import requests

    # 使用 API 密钥

    headers = {

    'Authorization': f'Bearer {API_KEY}',

    'Content-Type': 'application/json'

    }

    response = requests.get('https://api.example.com/data', headers=headers)

    df = pd.DataFrame(response.json())

    处理缺失数据

    JSON 可能有不一致的结构或缺失字段。

    缺失字段

    import pandas as pd
    
    

    # 数据中缺失某些字段

    data = [

    {"name": "张三", "age": 30, "email": "zhang@example.com"},

    {"name": "李四", "age": 25}, # 缺少 email

    {"name": "王五", "email": "wang@example.com"} # 缺少 age

    ]

    df = pd.DataFrame(data)

    # 填充缺失值

    df['email'] = df['email'].fillna('unknown')

    df['age'] = df['age'].fillna(df['age'].mean())

    # 或删除缺失值

    df = df.dropna()

    不一致的嵌套

    import pandas as pd
    
    

    # 某些记录有嵌套的地址,有些没有

    data = [

    {"name": "张三", "address": {"city": "北京"}},

    {"name": "李四"} # 没有地址

    ]

    # 处理不一致性

    def extract_city(record):

    if isinstance(record.get('address'), dict):

    return record['address'].get('city')

    return None

    df = pd.DataFrame(data)

    df['city'] = df.apply(lambda row: extract_city(row), axis=1)

    大文件处理

    对于超过内存的大 JSON 文件,使用流式处理。

    JSON Lines 分块读取

    import pandas as pd
    
    

    # 分块读取大的 JSON Lines 文件

    chunks = pd.read_json('large.jsonl', lines=True, chunksize=10000)

    for chunk in chunks:

    # 处理每个块

    process_data(chunk)

    使用 ijson 库

    ijson 允许流式解析 JSON,无需将整个文件加载到内存中。

    pip install ijson
    import ijson
    
    

    def process_large_json(filename, item_path='items.item'):

    """流式处理大 JSON 文件"""

    with open(filename, 'rb') as f:

    for item in ijson.items(f, item_path):

    yield item

    # 使用

    for record in process_large_json('huge.json'):

    print(record)

    并行处理

    import pandas as pd
    

    from multiprocessing import Pool

    def process_chunk(chunk):

    # 对每个块执行分析

    return chunk.groupby('category').size()

    # 读取分块

    chunks = pd.read_json('data.jsonl', lines=True, chunksize=10000)

    # 并行处理

    with Pool(4) as pool:

    results = pool.map(process_chunk, chunks)

    # 合并结果

    final_result = pd.concat(results)

    数据类型处理

    日期解析

    import pandas as pd
    
    

    # 自动解析日期

    df = pd.read_json('data.json', parse_dates=['created_at', 'updated_at'])

    # 或手动指定

    df['created_at'] = pd.to_datetime(df['created_at'])

    分类数据

    import pandas as pd
    
    

    df = pd.read_json('data.json')

    # 将字符串列转换为分类

    df['category'] = df['category'].astype('category')

    df['status'] = df['status'].astype('category')

    # 内存节省

    print(df.memory_usage())

    将数据导出为 JSON

    导出到文件

    import pandas as pd
    
    

    df = pd.read_csv('data.csv')

    # 默认导出

    df.to_json('output.json')

    # 以记录格式导出

    df.to_json('output.json', orient='records')

    # 漂亮打印

    df.to_json('output.json', indent=2)

    # JSON Lines 格式

    df.to_json('output.jsonl', orient='records', lines=True)

    导出到字符串

    # 获取 JSON 字符串
    

    json_str = df.to_json(orient='records')

    # 发送到 API

    import requests

    response = requests.post('https://api.example.com/data', json=json_str)

    在机器学习管道中使用 JSON

    加载训练数据

    import pandas as pd
    

    from sklearn.model_selection import train_test_split

    # 从 JSON 加载数据

    df = pd.read_json('training_data.json')

    # 分离特征和目标

    X = df.drop('target', axis=1)

    y = df['target']

    # 分割数据

    X_train, X_test, y_train, y_test = train_test_split(X, y)

    保存模型配置

    import json
    

    from sklearn.ensemble import RandomForestClassifier

    # 模型参数

    config = {

    'model_type': 'RandomForest',

    'n_estimators': 100,

    'max_depth': 10,

    'random_state': 42

    }

    # 保存配置

    with open('model_config.json', 'w') as f:

    json.dump(config, f, indent=2)

    # 读取配置

    with open('model_config.json', 'r') as f:

    config = json.load(f)

    # 使用配置创建模型

    model = RandomForestClassifier(config)

    常见模式和用例

    从多个 API 聚合数据

    import pandas as pd
    

    import requests

    def aggregate_from_apis(api_list):

    """从多个 API 聚合数据"""

    all_data = []

    for api_url in api_list:

    try:

    response = requests.get(api_url)

    data = response.json()

    all_data.extend(data if isinstance(data, list) else [data])

    except Exception as e:

    print(f"Error fetching {api_url}: {e}")

    return pd.DataFrame(all_data)

    # 使用

    apis = [

    'https://api.example.com/users',

    'https://api.other.com/customers'

    ]

    df = aggregate_from_apis(apis)

    数据质量检查

    import pandas as pd
    
    

    def check_data_quality(df):

    """检查 DataFrame 的数据质量"""

    report = {

    'total_rows': len(df),

    'total_cols': len(df.columns),

    'memory_mb': df.memory_usage(deep=True).sum() / 10242,

    'missing_values': df.isnull().sum().to_dict(),

    'dtypes': df.dtypes.to_dict(),

    'duplicates': df.duplicated().sum()

    }

    return report

    # 使用

    df = pd.read_json('data.json')

    quality = check_data_quality(df)

    print(quality)

    最佳实践

    1. 验证数据加载

    # 总是检查数据
    

    df = pd.read_json('data.json')

    print(f"形状: {df.shape}")

    print(f"列: {df.columns.tolist()}")

    print(f"数据类型:

    {df.dtypes}")

    print(df.head())

    2. 处理编码问题

    # 指定编码
    

    df = pd.read_json('data.json', encoding='utf-8')

    3. 缓存数据

    对于频繁使用的数据,缓存加载:

    import pickle
    
    

    # 保存处理后的 DataFrame

    df.to_pickle('processed_data.pkl')

    # 快速加载

    df = pd.read_pickle('processed_data.pkl')

    4. 文档化 Schema

    为您的 JSON 数据记录预期的 Schema:

    # schema.json
    

    {

    "name": "string",

    "age": "integer",

    "email": "string",

    "created_at": "datetime",

    "tags": "array"

    }

    故障排除

    问题:无法解析日期

    # 指定日期列
    

    df = pd.read_json('data.json', parse_dates=['date_column'])

    # 或手动转换

    df['date'] = pd.to_datetime(df['date'])

    问题:列数据类型不正确

    # 指定 dtype
    

    df = pd.read_json('data.json', dtype={'id': int, 'code': str})

    问题:内存不足

    # 使用分块处理
    

    for chunk in pd.read_json('huge.json', lines=True, chunksize=5000):

    process_chunk(chunk)

    结论

    JSON 在数据科学中是不可或缺的。关键技能:

    • 加载 - 使用 Pandas 加载 JSON
    • 处理 - 展平嵌套结构
    • 验证 - 检查数据质量
    • 扩展 - 处理大文件
    • 集成 - 与 ML 管道集成

    掌握这些技能,您可以有效地处理任何 JSON 数据!

    Share:

    相关文章

    Read in English