0

0

如何用 Python 将 CSV 中的“子行”结构(如订单明细)智能拆分为多列

聖光之護

聖光之護

发布时间:2026-02-03 17:37:01

|

977人浏览过

|

来源于php中文网

原创

如何用 Python 将 CSV 中的“子行”结构(如订单明细)智能拆分为多列

本文介绍一种健壮、可扩展的方法,使用原生 `csv` 模块解析具有嵌套结构的 csv(如订单主表+明细行),将分散在多行中的字段(如 artikelnummer、styckpris)按逻辑分组并展开为 `artikelnummer_1`, `artikelnummer_2` 等标准化列,完美解决顺序混乱、数量不一、空主键行等实际难题。

在处理某些导出型 CSV(如电商订单报表)时,常见一种“主-子”混合格式:首行为订单头(含 Ordernummer),后续空 Ordernummer 的行则属于该订单的明细项(如多个 Artikelnummer 和对应价格)。这种结构无法直接用 pandas.read_csv() 解析——因为 pandas 默认按行列对齐,而此处逻辑关系是跨行的。

原提问者尝试用 .split(';') 处理单列字符串,但数据实际是物理多行、逻辑一对多,强行切分会导致错位、丢失或崩溃。正确思路是:逐行扫描,识别主记录(非空 Ordernummer)与子记录(空 Ordernummer),动态构建每条订单的完整字段映射

以下为推荐实现方案,兼顾鲁棒性、可读性与可维护性:

Musico
Musico

Musico 是一个AI驱动的软件引擎,可以生成音乐。 它可以对手势、动作、代码或其他声音做出反应。

下载

✅ 核心策略

  • 使用 Python 内置 csv 模块逐行读取(避免 pandas 对不规则结构的误解析)
  • 维护一个 out_data 列表,每个元素为 dict,代表一条完整订单记录
  • 遇到非空 Ordernummer → 新建记录,拷贝当前行所有字段(主表信息)
  • 遇到空 Ordernummer → 向最后一条记录追加子字段(如 Artikelnummer_1, Artikelns styckpris inkl moms_1)
  • 自动推断最大子项数,确保输出 CSV 列头完整覆盖所有可能列(如 _1, _2, _3)

? 完整可运行代码

import csv
import pandas as pd

def parse_nested_csv(
    input_file: str = 'orderexport_new.csv',
    output_file: str = 'ny_orderdata.csv',
    main_key_col: int = 0,  # Ordernummer 所在列索引(从0开始)
    sub_cols: list = [1, 3],  # 需要拆分为多列的子字段列索引(如 Artikelnummer, styckpris)
    sub_col_names: list = ['Artikelnummer', 'Artikelns styckpris inkl moms']
):
    """
    解析主-子混合CSV,将子行字段展开为带序号的列(如 Artikelnummer_1, Artikelnummer_2)

    Args:
        input_file: 输入CSV路径
        output_file: 输出CSV路径
        main_key_col: 主键列索引(Ordernummer所在列)
        sub_cols: 子字段列索引列表(需展开的列)
        sub_col_names: 对应子字段的基名列表(用于生成列名如 'Artikelnummer_1')
    """
    out_data = []
    max_sub_count = 0  # 记录全局最大子项数

    with open(input_file, encoding='utf-8') as f:
        reader = csv.reader(f)

        # 跳过两行表头(根据你的示例:第一行为字段名,第二行为单位/说明)
        try:
            headers1 = next(reader)  # 如 ['Ordernummer', 'Intakt inkl moms', ...]
            headers2 = next(reader)  # 如 ['', 'Artikelnummer', 'Antal', ...]
        except StopIteration:
            raise ValueError("CSV 文件至少需要两行表头")

        for row in reader:
            if len(row) <= main_key_col or not row[main_key_col].strip():
                # 子行:空 Ordernummer → 追加到上一条记录
                if not out_data:
                    continue  # 忽略开头无主键的孤立子行

                # 当前子项序号(从1开始)
                current_sub_idx = len([k for k in out_data[-1].keys() 
                                     if k.startswith(sub_col_names[0] + '_')]) + 1
                max_sub_count = max(max_sub_count, current_sub_idx)

                # 为每个指定子列生成带序号的新键值
                for i, col_idx in enumerate(sub_cols):
                    if col_idx < len(row):
                        key_name = f"{sub_col_names[i]}_{current_sub_idx}"
                        out_data[-1][key_name] = row[col_idx].strip()
            else:
                # 主行:新建记录,初始化主字段
                new_record = {headers1[i]: row[i].strip() if i < len(row) else "" 
                             for i in range(len(headers1))}
                out_data.append(new_record)

    # 构建最终列头:主字段 + 动态生成的子字段(最多 max_sub_count 个)
    main_headers = [h for h in headers1 if h.strip()]
    dynamic_headers = []
    for name in sub_col_names:
        for i in range(1, max_sub_count + 1):
            dynamic_headers.append(f"{name}_{i}")

    all_headers = main_headers + dynamic_headers

    # 写入结果CSV
    with open(output_file, 'w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=all_headers)
        writer.writeheader()
        for record in out_data:
            # 补全缺失的动态列(避免 KeyError)
            padded_record = {k: record.get(k, "") for k in all_headers}
            writer.writerow(padded_record)

    print(f"✅ 成功解析 {len(out_data)} 条订单,最大子项数:{max_sub_count}")
    print(f"? 输出列:{all_headers}")
    return pd.DataFrame(out_data, columns=all_headers)

# --- 使用示例 ---
if __name__ == "__main__":
    # 配置你的字段映射(关键!按实际CSV结构调整)
    df_result = parse_nested_csv(
        input_file='orderexport_new.csv',
        output_file='ny_orderdata.csv',
        main_key_col=0,  # Ordernummer 在第0列
        sub_cols=[1, 3],  # "Intakt inkl moms"列(索引1)和"Dynamisk valutajusterad"列(索引3)需展开
        sub_col_names=['Artikelnummer', 'Artikelns styckpris inkl moms']
    )
    print(df_result.head())

⚠️ 关键注意事项

  • 编码兼容性:示例中使用 utf-8,若原始文件为 latin1(如问题中所示),请将 encoding='utf-8' 改为 encoding='latin1'
  • 表头处理:代码默认跳过前两行(适配你提供的表格结构)。若你的 CSV 只有一行表头,请删除 headers2 = next(reader) 并调整 headers1 逻辑
  • 列索引校准:sub_cols 中的数字必须严格对应 CSV 的实际列索引(用 Excel 或文本编辑器确认)。例如 "Artikelnummer" 在第二行显示为第2列,但在数据行中可能是索引 1(0起始)
  • 空值安全:自动填充缺失子字段为空字符串,避免 pandas 后续处理报错
  • 扩展性:只需修改 sub_cols 和 sub_col_names 即可支持任意数量的子字段(如增加 Antal_1, Antal_2)

? 总结

此方法摒弃了对“单列内分号分割”的错误假设,直击问题本质——结构化解析跨行逻辑关系。它不依赖数据顺序,能自动适应不同订单的子项数量差异,并生成规整的宽表(wide format)供后续分析。对于类似 ERP/CRM 导出的不规则报表,这是最可靠、最易调试的解决方案。

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

热门AI工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

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

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

71

2025.12.04

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

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

2

2026.01.31

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

844

2023.07.31

python中的format是什么意思
python中的format是什么意思

python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

439

2024.06.27

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

361

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

213

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1505

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

626

2023.11.24

全国统一发票查询平台入口合集
全国统一发票查询平台入口合集

本专题整合了全国统一发票查询入口地址合集,阅读专题下面的文章了解更多详细入口。

12

2026.02.03

热门下载

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

精品课程

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

共162课时 | 15.3万人学习

成为PHP架构师-自制PHP框架
成为PHP架构师-自制PHP框架

共28课时 | 2.5万人学习

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

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