首页 > 解决方案 > 使用 Python 处理巨大的文本文件(或准 csv) - 从文本数据创建矩阵

问题描述

当我对如何解决我的问题没有正确的想法时,我已经到了这样的地步。我正在处理文本文件(*.inp - abaqus 作业文件),我想从中提取一些基本信息。到目前为止,我发现了两个主要问题:

  1. 这样的文件非常大,即 500 000 行。
  2. 它们的结构并不总是类似于 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 行中的第一个事物)。

现在我陈述问题:):

  1. 如何正确导入位于 *ELEMENT, ELSET=name1 和 *ELEMENT, ELSET=name2 之间的数据,当我的目标是让矩阵中的每个元素仅使用 1 行,总共 11 列(1st - element_id, 2- 11 - 节点x_id)。
  2. 到目前为止,我将这个 *.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

标签: pythonpandascsvtextabaqus

解决方案


有趣的挑战!

只要您需要处理的文件大致遵循该结构,这样的东西就可能适合您。请参阅下面的输出。

  • 文件数据内联在 aio.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
------------------------------

推荐阅读