使用Python和Pandas处理非结构化CSV数据:字段对齐与初步清洗指南

花韻仙語
发布: 2025-09-19 20:09:00
原创
496人浏览过

使用Python和Pandas处理非结构化CSV数据:字段对齐与初步清洗指南

本教程旨在解决CSV文件中字段长度不一致导致的数据错位问题。通过Python和Pandas库,我们将学习如何根据每行字段的数量对数据进行分组,并为每个分组创建独立的DataFrame。这种方法为后续的精细化数据清洗和分析奠定了基础,尤其适用于处理历史遗留或结构松散的数据集。

引言:CSV数据字段对齐的挑战

在处理历史遗留或由不同系统导出的csv数据时,经常会遇到数据结构不一致的问题。最常见的挑战之一是,同一文件中的不同行可能包含不同数量的字段,导致字段错位,使得数据难以直接用于分析。例如,某些行可能包含额外的描述性字段,而另一些行则没有,这使得传统的按列读取方式变得不可行。

考虑以下示例数据,其中不同行的字段数量明显不同:

30,1204,PO,71100,147130,I09,B10,OC,350,20105402
31,1221,PO,70400,147170,I09,B10,OC,500,20105402
32,1223,SI,70384,147122,I09,B10,OC,500,PN,3,BO,OI,20105402
38,1307,SI,70379,146041,I09,B10,OC,500,21,BH,1,BO,195,40,SW,20105402
49,1405,LD,2,70119,148280,I10,B10,OC,0000,20105403
登录后复制

从上述数据可以看出,第一行和第二行有10个字段,而第三行有14个字段,第四行有17个字段,第五行又有11个字段。这种差异使得我们无法简单地将整个文件作为一个统一的表格来处理。

核心策略:按行字段数量分组

解决此类问题的有效初步方法是根据每行所包含的字段数量进行分组。其基本思想是:如果两行具有相同数量的字段,那么它们很可能遵循相同的结构模式,或者至少在字段数量上是一致的。通过将具有相同字段数量的行聚合在一起,我们可以为每种结构模式创建一个独立的子数据集,从而简化后续的清洗和分析工作。

Python的Pandas库是处理此类结构化和半结构化数据的强大工具,它能够方便地将数据转换为DataFrame,并进行各种数据操作。

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

Python实现:数据分组与DataFrame构建

以下是使用Python和Pandas实现上述分组策略的步骤和示例代码:

即构数智人
即构数智人

即构数智人是由即构科技推出的AI虚拟数字人视频创作平台,支持数字人形象定制、短视频创作、数字人直播等。

即构数智人 36
查看详情 即构数智人
  1. 准备数据 我们首先将示例数据存储在一个多行字符串中,以便于演示。在实际应用中,您可以从CSV文件中读取数据。

  2. 按行解析并分组 遍历每一行数据,使用逗号作为分隔符将其拆分为字段列表。然后,根据每个列表的长度(即字段数量)将其存储在一个字典中。字典的键是字段数量,值是包含所有具有该字段数量的行的列表。

  3. 为每个分组创建DataFrame 遍历字典中的每个分组,使用Pandas的DataFrame构造函数将该分组的数据转换为一个独立的DataFrame。

from io import StringIO
import pandas as pd

# 示例数据,实际应用中可以从文件读取
data = """
30,1204,PO,71100,147130,I09,B10,OC,350,20105402
31,1221,PO,70400,147170,I09,B10,OC,500,20105402
32,1223,SI,70384,147122,I09,B10,OC,500,PN,3,BO,OI,20105402
33,1224,SI,70392,147032,I09,B10,OC,500,PN,1,BO,OI,20105402
34,1227,PO,70400,146430,I09,B10,PF,500,20105402
35,1241,PO,71100,146420,I09,B10,PF,500,20105402
36,1249,PO,71100,146000,I09,B10,SN,500,20105402
37,1305,PO,70400,146000,I09,B10,OC,500,20105402
38,1307,SI,70379,146041,I09,B10,OC,500,21,BH,1,BO,195,40,SW,20105402
39,1312,SD,70372,146062,I09,B10,OC,500,20105402
40,1332,SI,70334,146309,I09,B10,OC,500,PN,4,BO,OI,20105402
41,1332,SI,70334,146309,I09,B10,OC,500,PN,5,BO,OI,20105403
42,1333,SI,70333,146324,I09,B10,OC,500,PN,2,BO,OI,20105403
43,1334,SI,70328,146348,I09,B10,OC,500,PN,1,BO,OI,20105403
44,1335,SI,70326,146356,I09,B10,OC,500,PN,1,BO,OI,20105403
45,1336,SI,70310,146424,I09,B10,OC,500,PN,1,BO,OI,20105403
46,1338,SI,70302,146457,I10,B10,OC,500,PN,1,BO,OI,20105403
47,1338,SI,70301,146464,I10,B10,OC,500,PN,1,BO,OI,20105403
48,1340,SI,70295,146503,I10,B10,OC,500,PN,8,BO,OI,20105403
49,1405,LD,2,70119,148280,I10,B10,OC,0000,20105403
01,1024,LA,1R,70120,148280,B10,OC,0000,21105501
02,1039,PO,70340,149400,I10,B10,OC,500,21105501
03,1045,SI,70378,149025,I10,B07,PF,300,PN,17,BO,OI,21105501
"""

# 用于存储按字段数量分组的数据
all_data_by_length = {}

# 逐行处理数据
for line in map(str.strip, data.splitlines()): # 移除每行首尾空白字符
    if not line: # 跳过空行
        continue
    fields = line.split(",") # 按逗号分割字段
    field_count = len(fields)

    # 将行添加到对应字段数量的分组中
    all_data_by_length.setdefault(field_count, []).append(fields)

# 为每个分组创建并打印DataFrame
print("--- 分组后的DataFrames ---")
for count, lines_in_group in all_data_by_length.items():
    print(f"数据行数: {len(lines_in_group)}, 字段数量: {count}")
    df = pd.DataFrame(lines_in_group)
    print(df)
    print("-" * 80)
登录后复制

代码解析:

  • from io import StringIO: StringIO 模块允许我们将字符串当作文件来处理,方便 pd.read_csv 等函数使用,尽管在这个例子中我们直接处理字符串。
  • map(str.strip, data.splitlines()): data.splitlines() 将多行字符串分割成一个列表,其中每个元素是一行。map(str.strip, ...) 则对列表中的每一行应用 str.strip() 方法,移除行首尾的空白字符,包括换行符。
  • if not line: continue: 过滤掉可能存在的空行,避免处理空字符串。
  • line.split(","): 使用逗号作为分隔符将每行字符串拆分成字段列表。
  • all_data_by_length.setdefault(field_count, []).append(fields): 这是分组的核心逻辑。setdefault(key, default_value) 方法会检查字典中是否存在 key。如果存在,则返回其对应的值;如果不存在,则将 key 添加到字典中,并将其值设置为 default_value(在这里是一个空列表 []),然后返回这个 default_value。这样,我们就可以直接向返回的列表中添加当前行的字段列表 fields。
  • pd.DataFrame(lines_in_group): 将同一分组内的所有行列表转换为一个Pandas DataFrame。由于此时我们没有列名信息,Pandas会默认使用整数作为列索引(0, 1, 2...)。

输出示例:

运行上述代码,您将看到根据字段数量生成了多个独立的DataFrame,每个DataFrame都包含字段数量相同的行。例如,一个DataFrame可能包含所有10个字段的行,另一个包含所有14个字段的行,以此类推。

--- 分组后的DataFrames ---
数据行数: 9, 字段数量: 10
    0     1   2      3       4       5    6   7     8         9
0  30  1204  PO  71100  147130     I09  B10  OC   350  20105402
1  31  1221  PO  70400  147170     I09  B10  OC   500  20105402
2  34  1227  PO  70400  146430     I09  B10  PF   500  20105402
3  35  1241  PO  71100  146420     I09  B10  PF   500  20105402
4  36  1249  PO  71100  146000     I09  B10  SN   500  20105402
5  37  1305  PO  70400  146000     I09  B10  OC   500  20105402
6  39  1312  SD  70372  146062     I09  B10  OC   500  20105402
7  01  1024  LA     1R   70120  148280  B10  OC  0000  21105501
8  02  1039  PO  70340  149400     I10  B10  OC   500  21105501
--------------------------------------------------------------------------------
数据行数: 12, 字段数量: 14
    0     1   2      3       4    5    6   7    8   9   10  11  12        13
0   32  1223  SI  70384  147122  I09  B10  OC  500  PN   3  BO  OI  20105402
1   33  1224  SI  70392  147032  I09  B10  OC  500  PN   1  BO  OI  20105402
2   40  1332  SI  70334  146309  I09  B10  OC  500  PN   4  BO  OI  20105402
3   41  1332  SI  70334  146309  I09  B10  OC  500  PN   5  BO  OI  20105403
4   42  1333  SI  70333  146324  I09  B10  OC  500  PN   2  BO  OI  20105403
5   43  1334  SI  70328  146348  I09  B10  OC  500  PN   1  BO  OI  20105403
6   44  1335  SI  70326  146356  I09  B10  OC  500  PN   1  BO  OI  20105403
7   45  1336  SI  70310  146424  I09  B10  OC  500  PN   1  BO  OI  20105403
8   46  1338  SI  70302  146457  I10  B10  OC  500  PN   1  BO  OI  20105403
9   47  1338  SI  70301  146464  I10  B10  OC  500  PN   1  BO  OI  20105403
10  48  1340  SI  70295  146503  I10  B10  OC  500  PN   8  BO  OI  20105403
11  03  1045  SI  70378  149025  I10  B07  PF  300  PN  17  BO  OI  21105501
--------------------------------------------------------------------------------
数据行数: 1, 字段数量: 17
    0     1   2      3       4    5    6   7    8   9   10 11  12   13  14  15        16
0  38  1307  SI  70379  146041  I09  B10  OC  500  21  BH  1  BO  195  40  SW  20105402
--------------------------------------------------------------------------------
数据行数: 1, 字段数量: 11
    0     1   2  3      4       5    6    7   8     9         10
0  49  1405  LD  2  70119  148280  I10  B10  OC  0000  20105403
--------------------------------------------------------------------------------
登录后复制

后续数据清洗与分析建议

将数据按字段数量分组只是数据清洗的第一步。完成此步骤后,您将拥有多个结构相对一致的DataFrame。接下来的工作需要结合对数据内容的理解(领域知识)来进一步处理:

  1. 确定列名: 对于每个DataFrame,根据其字段数量和内容,赋予有意义的列名。这通常需要手动检查数据样本,识别每个位置的字段含义。例如,一个10字段的DataFrame可能代表一种订单类型,而14字段的DataFrame可能代表另一种包含更多详情的订单类型。
  2. 数据类型转换: 将字符串类型的字段转换为正确的数值、日期或布尔类型。
  3. 处理缺失值: 识别并处理每个DataFrame中的缺失值。不同的数据模式可能需要不同的缺失值处理策略(例如,填充平均值、中位数,或删除行)。
  4. 合并与对齐: 如果不同的DataFrame代表同一实体(例如,都是订单数据但结构不同),您可能需要根据它们的共同字段(如订单ID)进行合并。这可能涉及到识别不同DataFrame中相同意义的字段,然后进行重命名和合并操作,必要时填充缺失的字段。
  5. 正则匹配与模式识别: 对于那些在同一列中包含多种信息或格式不规则的字段,可以使用正则表达式来提取、清洗或标准化数据。
  6. 异常值检测: 在每个结构一致的DataFrame中,更容易发现并处理异常值。

注意事项与最佳实践

  • 领域知识至关重要: 没有任何自动化工具可以完全替代对数据含义的理解。在清洗非结构化数据时,了解每个字段的预期内容和业务逻辑是成功的关键。
  • 迭代式清洗: 数据清洗通常是一个迭代过程。您可能需要多次运行代码,根据中间结果调整清洗策略。
  • 处理大规模数据: 对于非常大的CSV文件,一次性将所有数据加载到内存中可能不可行。在这种情况下,可以考虑使用Pandas的read_csv函数的chunksize参数分块读取,或者使用Dask等工具进行并行处理。
  • 文档记录: 记录您的清洗步骤和决策,这对于未来的维护和团队协作至关重要。

总结

通过将CSV数据按行字段数量进行分组,我们能够有效地将非结构化、字段错位的数据分解为多个结构相对一致的子数据集。这种初步的清洗步骤为后续的精细化处理奠定了坚实的基础,使得利用Pandas进行进一步的数据类型转换、缺失值处理和业务逻辑分析成为可能。请记住,数据清洗是一个结合技术工具和领域知识的艺术,持续的探索和迭代是成功的关键。

以上就是使用Python和Pandas处理非结构化CSV数据:字段对齐与初步清洗指南的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

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