python - 使用 Python 处理巨大的文本文件(或准 csv) - 从文本数据创建矩阵
问题描述
当我对如何解决我的问题没有正确的想法时,我已经到了这样的地步。我正在处理文本文件(*.inp - abaqus 作业文件),我想从中提取一些基本信息。到目前为止,我发现了两个主要问题:
- 这样的文件非常大,即 500 000 行。
- 它们的结构并不总是类似于 csv
广告1。由于数据量巨大,我想包含 pandas 库以加快操作(将在优化循环中重复)
广告 2。具有“奇怪”结构的示例 *.inp 文件(请注意,“节点”和“元素”是代码中使用的实际名称,每个元素由多个节点组成,如 cube=element,每个立方体顶点=节点]:
*NODE
1, 0.0, 0.0, 3.0
2, -17.0, 5.5, 2.3
3, 51.0, 0.0, 639.8
5, 0.0, 5.5 , 31.0
...
145000, 31.3, 21.5, 99.8
*ELEMENT, ELSET=Name1, TYPE=Type1
1527450, 265156, 273237, 265019, 265021, 275728, 273221, 265599,
265146, 273583, 265020
1527449, 269279, 272869, 269277, 269479, 273130, 272862, 269278,
269489, 275729, 269627
1527448, 272250, 272858, 275350, 273327, 272851, 275730, 275731,
273346, 275732, 275733
...
1126546, 265180, 275352, 273263, 273237, 275736, 275737, 275738,
275739, 275740, 273246
*ELEMENT, ELSET=Name2, Type2
...
*SURFACE, NAME=Surf1
12345, S5
34567, S3
...
*STEP
*STATIC
1.0,,,1.0
*BOUNDARY
bc_1,1,3,0.0
bc_2,6,6,0.0
...
...
“*NODE”关键字下列出的值具有以下序列:node_id、coord_x、coord_y、coord_z
这是模型中最大的数据集,这就是我想使用 pandas 的原因(像 csv 一样读取它)。对于这一部分,我没有发现重大问题。
*ELEMENT" 关键字下列出的值有点复杂:
第 n 行:elementn_id、node1_id、node2_id、node3_id、node4_id、node5_id、node6_id、node7_id
第 n+1 行:node8_id、node9_id、node10_id
在这种情况下,pandas 将这部分代码作为两行(显然)导入,在 n+1 行的最后 7 列中包含 N/A 项。我使用 pd.read_csv 。请注意,从 1 到 10 的节点 id 一起形成一个元素(id 指定为第 n 行中的第一个事物)。
现在我陈述问题:):
- 如何正确导入位于 *ELEMENT, ELSET=name1 和 *ELEMENT, ELSET=name2 之间的数据,当我的目标是让矩阵中的每个元素仅使用 1 行,总共 11 列(1st - element_id, 2- 11 - 节点x_id)。
- 到目前为止,我将这个 *.inp 文件分成单独的文件以便能够对它们进行处理......现在我想在一个脚本中完成所有操作,即创建矩阵 A = [(node_id, coord_x, coord_y, coord_z),。 ..] 和矩阵 B = [(element_id, node1_id, node2_id, ... , node10_id),...] 一次。如果在这种情况下简单的 pd.read_csv 不能正常执行,该怎么做?有许多严格的字符串行不应该被导入或被排除以加速脚本。
我的想法是将 *.inp 文件作为“打开”函数导入 python,然后添加某种标签/触发器以匹配应进一步使用的代码行(并且可能使用 pandas 处理),但在这种情况下,我不要使用 pandas 作为导入选项...
我相信我的问题对你们大多数人来说都很无聊,但我并不是严格意义上的开发人员 :) 我不希望得到直接的、现成的解决方案,而是希望得到你关于在哪里寻找潜在答案或工具的建议。
提前谢谢大家,祝你有个美好的一天,prz
解决方案
有趣的挑战!
只要您需要处理的文件大致遵循该结构,这样的东西就可能适合您。请参阅下面的输出。
- 文件数据内联在 a
io.StringIO()
中以使其自给自足,但它也可以是open("data.inp")
文件流。 - 如果您对 Python 不是很精通,那么具有
yield
魔力的生成器函数可能看起来有点神秘,对此感到抱歉。:) - 它确实需要“重构”内存中的 CSV 文件以供 Pandas 读取,如果内存不足,这可能是一个瓶颈,但试一试就会知道......
- 请注意“Surf1”组是如何被故意跳过的,因此您知道如何忽略某些部分。
import io
import itertools
import pandas as pd
def split_abq(inp_file):
"""
Split an ABQ file into tuples of "group" header and lines in that group.
"""
current_header = None
for line in inp_file:
line = line.strip() # remove whitespace
if not line: # skip empty lines
continue
if line.startswith("*"): # note headers
current_header = line
continue
yield (current_header, line) # generate lines
def group_split_abq(split_generator):
"""
"De-repeat" the output of `split_abq` into a generator (group name, generator-of-lines)
"""
for group, entries in itertools.groupby(split_abq(input_file), lambda pair: pair[0]):
line_generator = (line for group, line in entries) # A generator expression; this is evaluated lazily
yield (group, line_generator)
def lines_to_df(line_generator, **read_csv_kwargs):
"""
Convert an iterable of CSV-ish lines into a Pandas dataframe
"""
# "Write" an in-memory file for Pandas to parse
csv_io = io.StringIO()
for line in line_generator:
print(line, file=csv_io)
csv_io.seek(0) # Seek back to the start
return pd.read_csv(csv_io, **read_csv_kwargs)
input_file = io.StringIO(
"""
*NODE
1, 0.0, 0.0, 3.0
2, -17.0, 5.5, 2.3
3, 51.0, 0.0, 639.8
5, 0.0, 5.5 , 31.0
145000, 31.3, 21.5, 99.8
*ELEMENT, ELSET=Name1, TYPE=Type1
1527450, 265156, 273237, 265019, 265021, 275728, 273221, 265599, 265146, 273583, 265020
1527449, 269279, 272869, 269277, 269479, 273130, 272862, 269278, 269489, 275729, 269627
1527448, 272250, 272858, 275350, 273327, 272851, 275730, 275731, 273346, 275732, 275733
1126546, 265180, 275352, 273263, 273237, 275736, 275737, 275738, 275739, 275740, 273246
*ELEMENT, ELSET=Name2, Type2
1527450, 265156, 273237, 265019, 265021, 275728, 273221, 265599, 265146, 273583, 265020
*SURFACE, NAME=Surf1
12345, S5
34567, S3
*STEP
*STATIC
1.0,,,1.0
*BOUNDARY
bc_1,1,3,0.0
bc_2,6,6,0.0
"""
)
for group, lines in group_split_abq(split_abq(input_file)):
print("=================================")
print("Group: ", group)
if "Surf1" in group: # You can use this opportunity to ignore some groups
print("-> Skipping Surf1")
continue
df = lines_to_df(lines, header=None) # `header` should probably be decided by the group type
print(df.head())
print("------------------------------\n")
# You could store the various `df`s generated in a dict here
输出是
=================================
Group: *NODE
0 1 2 3
0 1 0.0 0.0 3.0
1 2 -17.0 5.5 2.3
2 3 51.0 0.0 639.8
3 5 0.0 5.5 31.0
4 145000 31.3 21.5 99.8
------------------------------
=================================
Group: *ELEMENT, ELSET=Name1, TYPE=Type1
0 1 2 3 4 5 6 7 8 9 10
0 1527450 265156 273237 265019 265021 275728 273221 265599 265146 273583 265020
1 1527449 269279 272869 269277 269479 273130 272862 269278 269489 275729 269627
2 1527448 272250 272858 275350 273327 272851 275730 275731 273346 275732 275733
3 1126546 265180 275352 273263 273237 275736 275737 275738 275739 275740 273246
------------------------------
=================================
Group: *ELEMENT, ELSET=Name2, Type2
0 1 2 3 4 5 6 7 8 9 10
0 1527450 265156 273237 265019 265021 275728 273221 265599 265146 273583 265020
------------------------------
=================================
Group: *SURFACE, NAME=Surf1
-> Skipping Surf1
=================================
Group: *STATIC
0 1 2 3
0 1.0 NaN NaN 1.0
------------------------------
=================================
Group: *BOUNDARY
0 1 2 3
0 bc_1 1 3 0.0
1 bc_2 6 6 0.0
------------------------------
推荐阅读
- javascript - jquery .load 在 .remove 之后不断触发 javascript
- android - 为什么 ViewModel 和 Fragment 中的相同变量不同?
- slack - 如何添加仅适用于某些用户或某些频道的斜杠命令?
- windows - 尝试在 Windows 中保存文件时如何修复“无法保存文件”
- android - 如何在 Flutter 应用中发现本地网络上的 Firetv、Airplay、Chromecast、Roku 等设备?
- php - Imagecopy合并问题
- laravel - 雄辩的 with() 在多个 where() clouse
- sql - 显示无效语法
- c# - 低优先级线程阻止其他具有正常优先级的线程执行
- qt - 如何使用 QMenuBar 在小部件之间切换?