【问题标题】:Importing big tecplot block files in python as fast as possible尽可能快地在 python 中导入大的 tecplot 块文件
【发布时间】:2013-10-06 18:37:07
【问题描述】:

我想在 python 中导入一些 ascii 文件(来自 tecplot,用于 cfd 后处理的软件)。 这些文件的规则是(至少对于我需要导入的那些):

  • 文件分为几个部分

每个部分都有两行作为标题,如:

VARIABLES = "x" "y" "z" "ro" "rovx" "rovy" "rovz" "roE" "M" "p" "Pi" "tsta" "tgen" 
ZONE T="Window(s) : E_W_Block0002_ALL",  I=29,  J=17,  K=25, F=BLOCK
  • 每个部分都有一组由第一行给出的变量。当一个部分结束时,一个新部分以两条相似的行开头。
  • 对于每个变量,都有 I*J*K 值。
  • 每个变量都是一个连续的值块。
  • 每行有固定数量的值 (6)。
  • 当一个变量结束时,下一个变量从新行开始。
  • 变量是“IJK 有序数据”。I-index 变化最快; J-index 次之; K 指数最慢。 I-index 应该是内部循环,K-index 应该是外部循环,而 J-index 应该是中间的循环。

这是一个数据示例:

VARIABLES = "x" "y" "z" "ro" "rovx" "rovy" "rovz" "roE" "M" "p" "Pi" "tsta" "tgen" 
ZONE T="Window(s) : E_W_Block0002_ALL",  I=29,  J=17,  K=25, F=BLOCK
-3.9999999E+00 -3.3327306E+00 -2.7760824E+00 -2.3117116E+00 -1.9243209E+00 -1.6011492E+00
[...]
0.0000000E+00 #fin first variable
-4.3532482E-02 -4.3584235E-02 -4.3627592E-02 -4.3663762E-02 -4.3693815E-02 -4.3718831E-02 #second variable, 'y'
[...]
1.0738781E-01 #end of second variable
[...]
[...]
VARIABLES = "x" "y" "z" "ro" "rovx" "rovy" "rovz" "roE" "M" "p" "Pi" "tsta" "tgen" #next zone
ZONE T="Window(s) : E_W_Block0003_ALL",  I=17,  J=17,  K=25, F=BLOCK

我是 python 的新手,我编写了一个代码来将数据导入字典,将变量写为 3D numpy.array 。这些文件可能非常大(高达 Gb)。我怎样才能使这段代码更快? (或者更一般地说,我怎样才能尽可能快地导入这些文件)?

import re
from numpy import zeros, array, prod
def vectorr(I,  J,  K):
    """function"""
    vect = []
    for k in range(0,  K):
        for j in range(0, J):
            for i in range(0, I):
                vect.append([i, j, k])
    return vect

a = open('E:\u.dat')

filelist = a.readlines()

NumberCol = 6
count = 0
data = dict()
leng = len(filelist)
countzone = 0
while count < leng:
    strVARIABLES = re.findall('VARIABLES', filelist[count])
    variables = re.findall(r'"(.*?)"',  filelist[count])
    countzone = countzone+1
    data[countzone] = {key:[] for key in variables}
    count = count+1
    strI = re.findall('I=....', filelist[count])
    strI = re.findall('\d+', strI[0]) 
    I = int(strI[0])
    ##
    strJ = re.findall('J=....', filelist[count])
    strJ = re.findall('\d+', strJ[0])
    J = int(strJ[0])
    ##
    strK = re.findall('K=....', filelist[count])
    strK = re.findall('\d+', strK[0])
    K = int(strK[0])
    data[countzone]['indmax'] = array([I, J, K])
    pr = prod(data[countzone]['indmax'])
    lin = pr // NumberCol
    if pr%NumberCol != 0:
        lin = lin+1
    vect = vectorr(I, J, K)
    for key in variables:
        init = zeros((I, J, K))
        for ii in range(0, lin):
            count = count+1
            temp = map(float, filelist[count].split())
            for iii in range(0, len(temp)):
                init.itemset(tuple(vect[ii*6+iii]), temp[iii])
        data[countzone][key] = init
    count = count+1

附言。在python中,没有cython或其他语言

【问题讨论】:

  • 首先将 a = open('E:\8-Documenti\onera stage\u.dat') 更改为 with open('E:\8-Documenti\onera stage\u.dat') as a。其次,您的代码似乎还可以,看不到任何引人注目的东西会使它变得非常慢。 PS: tu fais un stage dans onera? ;)
  • 使用 RunSnakeRun 来分析你的代码,以便知道时间花在了哪里。我认为大文件上的正则表达式不是一个好主意。尝试改用 PEG?还是一些自定义解析?
  • 嗯,我不知道你在说什么:-)。自定义解析?
  • 你的电脑有多少内存?如果事情变得非常缓慢,您可能会耗尽所有内存(第一个 f.readlines() 调用会将所有内容读取到内存中,并且您的 Numpy 数据结构可能会占用同样多的内存)。从文件格式的外观来看,您可以按顺序读取它,而无需将所有内容复制到 RAM。就像 mguijarr 建议的那样,分析您的代码以找出导致它变慢的原因。
  • 为什么你有几个 Gb 大小的 ascii 文件?如果它们以二进制格式(.fits 或类似格式)存储,它们会运行得更快

标签: python python-2.7 numpy


【解决方案1】:

将一大堆字符串转换为数字总是会有点慢,但假设三重嵌套的 for 循环是这里的瓶颈,也许将其更改为以下内容可以为您提供足够的加速:

# add this line to your imports
from numpy import fromstring

# replace the nested for-loop with:
count += 1
for key in variables:
    str_vector = ' '.join(filelist[count:count+lin])
    ar = fromstring(str_vector, sep=' ')
    ar = ar.reshape((I, J, K), order='F')

    data[countzone][key] = ar 
    count += lin

不幸的是,目前我只能使用我的智能手机(没有电脑),所以我无法测试它有多快,或者它是否正常工作或根本无法测试!


更新

最后我开始做一些测试:

  • 我的代码包含一个小错误,但它现在似乎可以正常工作。
  • 建议更改的代码运行速度比原始代码快 4 倍
  • 您的代码大部分时间都花在ndarray.itemset 上,并且可能循环开销和浮点转换。不幸的是,cProfile 没有详细说明这一点..
  • 改进后的代码在numpy.fromstring 中花费了大约 70% 的时间,在我看来,这表明这种方法对于您可以使用 Python / NumPy 实现的目标来说相当快。

更新 2

当然更好的是迭代文件而不是一次加载所有内容。在这种情况下,这会稍微快一些(我试过了),并且显着减少了内存使用。您也可以尝试使用多个 CPU 内核来进行加载和转换为浮点数,但是很难将所有数据放在一个变量下。最后一句警告:我使用的fromstring 方法随着字符串的长度而变差。例如。从某个字符串长度开始,使用np.fromiter(itertools.imap(float, str_vector.split()), dtype=float) 之类的内容会变得更有效。

【讨论】:

    【解决方案2】:

    如果你在这里使用正则表达式,我会改变两件事:

    • 编译更经常使用的 RE(我猜这适用于您示例中的所有 RE)。对它们执行regex=re.compile("&lt;pattern&gt;"),并将生成的对象与match=regex.match() 一起使用,如the Python documentation 中所述。

    • 对于 I、J、K RE,考虑将两个 RE 减少为一个,使用分组功能(也如上所述),通过搜索“I=(\d+)”形式的模式,并抓取部分使用regex.group(1) 在括号内匹配。更进一步,您可以定义一个正则表达式来一步捕获所有三个变量。

    至少对于开始部分来说,RE 似乎有点过头了:您需要寻找的字符串没有变化,string.find() 就足够了,而且在这种情况下可能更快。

    编辑:我刚刚看到您已经对变量使用了分组...

    【讨论】:

    • 这些只是用于解析每个部分的标题。与实际读取 1 GB 的数字相比,它们不应该花费任何大量时间。
    猜你喜欢
    • 1970-01-01
    • 2012-03-26
    • 1970-01-01
    • 1970-01-01
    • 2019-03-24
    • 2014-07-18
    • 1970-01-01
    • 2013-04-02
    • 2011-04-19
    相关资源
    最近更新 更多