
在处理历史遗留或由不同系统导出的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和Pandas实现上述分组策略的步骤和示例代码:
准备数据 我们首先将示例数据存储在一个多行字符串中,以便于演示。在实际应用中,您可以从CSV文件中读取数据。
按行解析并分组 遍历每一行数据,使用逗号作为分隔符将其拆分为字段列表。然后,根据每个列表的长度(即字段数量)将其存储在一个字典中。字典的键是字段数量,值是包含所有具有该字段数量的行的列表。
为每个分组创建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)代码解析:
输出示例:
运行上述代码,您将看到根据字段数量生成了多个独立的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。接下来的工作需要结合对数据内容的理解(领域知识)来进一步处理:
通过将CSV数据按行字段数量进行分组,我们能够有效地将非结构化、字段错位的数据分解为多个结构相对一致的子数据集。这种初步的清洗步骤为后续的精细化处理奠定了坚实的基础,使得利用Pandas进行进一步的数据类型转换、缺失值处理和业务逻辑分析成为可能。请记住,数据清洗是一个结合技术工具和领域知识的艺术,持续的探索和迭代是成功的关键。
以上就是使用Python和Pandas处理非结构化CSV数据:字段对齐与初步清洗指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号