9

PythonでいくつかのASCIIファイルをインポートしたい(tecplot、cfd後処理用のソフトウェアから)。これらのファイルのルールは次のとおりです (少なくとも、インポートする必要があるファイルについては):

  • ファイルはいくつかのセクションに分かれています

各セクションには、ヘッダーとして次のような 2 行があります。

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
  • 各セクションには、最初の行で指定された一連の変数があります。セクションが終了すると、新しいセクションが 2 つの同様の行で始まります。
  • 各変数には I*J*K 値があります。
  • 各変数は、連続した値のブロックです。
  • 行ごとに一定数の値があります (6)。
  • 変数が終了すると、次の変数が新しい行で始まります。
  • 変数は「IJK 順序付けデータ」です。I-index は最も速く変化します。J指数は次に速い。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

Ps。Pythonでは、cythonや他の言語はありません

4

2 に答える 2

2

大量の文字列を数値に変換するのは常に少し遅くなりますが、ここで 3 重にネストされた 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

残念ながら、現時点ではスマートフォン (PC なし) にしかアクセスできないため、これがどれほど高速であるか、または正しく動作するかどうかをテストすることはできません。


アップデート

最後に、いくつかのテストを行うことにしました。

  • 私のコードには小さなエラーが含まれていましたが、現在は正しく動作しているようです。
  • 提案された変更を加えたコードは、元のコードよりも約 4 倍速く実行されます
  • コードはほとんどの時間をndarray.itemsetループ オーバーヘッドと float 変換に費やしています。残念ながら、cProfile はこれを詳細に示していません。
  • 改善されたコードは約 70% の時間を に費やしていnumpy.fromstringます。これは、私の見解では、この方法が Python/NumPy で達成できることに対してかなり高速であることを示しています。

更新 2

もちろん、すべてを一度にロードするのではなく、ファイルを反復処理する方がさらに良いでしょう。この場合、これはわずかに高速で (試してみました)、メモリ使用量を大幅に削減します。複数の CPU コアを使用して float への読み込みと変換を試みることもできますが、すべてのデータを 1 つの変数の下に置くことが難しくなります。最後に警告の言葉:fromstring私が使用した方法は、文字列の長さのスケールがかなり悪いです。たとえば、特定の文字列の長さから、 のようなものを使用する方が効率的になりますnp.fromiter(itertools.imap(float, str_vector.split()), dtype=float)

于 2014-01-12T19:24:27.623 に答える
0

ここで正規表現を使用する場合、変更する点が 2 つあります。

  • より頻繁に使用されるREをコンパイルします(これは、例のすべてのREに適用されると思います)。Python ドキュメントで説明されているようにregex=re.compile("<pattern>")、それらを実行し、結果のオブジェクトを で使用します。match=regex.match()

  • I、J、K RE については、グループ化機能 (上記でも説明) を使用して、"I=(\d+)" の形式のパターンを検索し、2 つの RE を 1 つに減らすことを検討してください。を使用した括弧regex.group(1)。これをさらに進めて、1 つのステップで 3 つの変数すべてを取得する単一の正規表現を定義できます。

少なくともセクションを開始するには、正規表現は少し過剰に思えます。検索する必要がある文字列にはバリエーションがなくstring.find()、その場合は十分であり、おそらく高速です。

編集:変数に対して既にグループ化を使用しているのを見ました...

于 2014-01-10T16:08:25.073 に答える