581

ファイル全体の各行を繰り返し処理したいと思います。これを行う1つの方法は、ファイル全体を読み取り、それをリストに保存してから、目的の行を調べることです。この方法は大量のメモリを使用するため、別の方法を探しています。

これまでの私のコード:

for each_line in fileinput.input(input_file):
    do_something(each_line)

    for each_line_again in fileinput.input(input_file):
        do_something(each_line_again)

このコードを実行すると、エラーメッセージが表示されますdevice active

助言がありますか?

目的は、ペアごとの文字列の類似性を計算することです。つまり、ファイル内の各行について、1行おきのレーベンシュタイン距離を計算したいと思います。

4

11 に答える 11

1344

ファイルを読み取る正しい完全なPythonicの方法は、次のとおりです。

with open(...) as f:
    for line in f:
        # Do something with 'line'

このwithステートメントは、内部ブロックで例外が発生した場合を含め、ファイルのオープンとクローズを処理します。for line in fはファイルオブジェクトを反復可能として扱います。fこれはバッファリングされたI/Oとメモリ管理を自動的に使用するため、大きなファイルについて心配する必要はありません。

それを行うための明白な方法は1つ、できれば1つだけである必要があります。

于 2011-11-04T13:46:44.787 に答える
144

ランク付けされた順序での2つのメモリ効率の高い方法(最初が最適)-

  1. の使用with-Python2.5以降でサポート
  2. yieldあなたが本当に読む量を制御したい場合の使用

1.の使用with

with大きなファイルを読み取るための優れた効率的なpythonic方法です。利点-1)ファイルオブジェクトは、with実行ブロックを終了した後に自動的に閉じられます。with2)ブロック内の例外処理。3)メモリforループは、fファイルオブジェクトを1行ずつ繰り返します。内部的には、バッファリングされたIO(コストのかかるIO操作を最適化するため)とメモリ管理を行います。

with open("x.txt") as f:
    for line in f:
        do something with data

2.の使用yield

場合によっては、各反復で読み取る量をよりきめ細かく制御したいことがあります。その場合、iteryieldを使用します。この方法では、最後にファイルを明示的に閉じる必要があることに注意してください。

def readInChunks(fileObj, chunkSize=2048):
    """
    Lazy function to read a file piece by piece.
    Default chunk size: 2kB.

    """
    while True:
        data = fileObj.read(chunkSize)
        if not data:
            break
        yield data

f = open('bigFile')
for chunk in readInChunks(f):
    do_something(chunk)
f.close()

落とし穴と完全を期すために-以下の方法は、大きなファイルを読むのにそれほど良くないか、エレガントではありませんが、丸みを帯びた理解を得るために読んでください。

Pythonでは、ファイルから行を読み取る最も一般的な方法は、次のことを行うことです。

for line in open('myfile','r').readlines():
    do_something(line)

ただし、これが行われると、readlines()関数(関数にも同じことが当てはまりread()ます)はファイル全体をメモリにロードしてから、それを繰り返し処理します。大きなファイルの場合、少し良いアプローチ(上記の最初の2つの方法が最適です)は、fileinput次のようにモジュールを使用することです。

import fileinput

for line in fileinput.input(['myfile']):
    do_something(line)

呼び出しは行を順番に読み取りますが、Pythonでは反復可能であるfileinput.input()ため、読み取られた後、または単にこれを行った後でも、行をメモリに保持しません。file

参考文献

  1. ステートメント付きのPython
于 2011-11-04T13:31:42.240 に答える
38

改行を削除するには:

with open(file_path, 'rU') as f:
    for line_terminated in f:
        line = line_terminated.rstrip('\n')
        ...

ユニバーサル改行サポートを使用'\n'すると、ファイル内のターミネータが何であれ、すべてのテキストファイル行は、で終了しているように'\r'見え'\n'ます'\r\n'

編集-ユニバーサル改行サポートを指定するには:

  • Unix上のPython2---open(file_path, mode='rU')必須[ @Daveに感謝]
  • Windows上のPython2---open(file_path, mode='rU')オプション
  • Python3---open(file_path, newline=None)オプション

このnewlineパラメーターはPython3でのみサポートされ、デフォルトはNoneです。パラメータのmodeデフォルト'r'はすべての場合です。これUはPython3では非推奨です。WindowsのPython2では、他のメカニズムがに変換\r\nされるよう\nです。

ドキュメント:

ネイティブラインターミネータを保持するには:

with open(file_path, 'rb') as f:
    with line_native_terminated in f:
        ...

バイナリモードでも、ファイルを。で行に解析できますin。各行には、ファイルに含まれるターミネータが含まれます。

@katrielalexの回答、Pythonのopen()ドキュメント、およびiPythonの実験に感謝します。

于 2015-09-15T15:07:52.737 に答える
17

これは、Pythonでファイルを読み取るための可能な方法です。

f = open(input_file)
for line in f:
    do_stuff(line)
f.close()

完全なリストは割り当てられません。それは線を繰り返します。

于 2011-11-04T13:33:37.617 に答える
13

私がどこから来たのかについてのいくつかの文脈。コードスニペットは最後にあります。

可能であれば、H2Oなどのオープンソースツールを使用して超高性能の並列CSVファイル読み取りを行うことを好みますが、このツールの機能セットには制限があります。教師あり学習を適切に行うためにH2Oクラスターにフィードする前に、データサイエンスパイプラインを作成するための多くのコードを作成することになります。

マルチプロセッシングライブラリのプールオブジェクトとマップ関数で多くの並列処理を追加することにより、UCIリポジトリから8GBHIGGSデータセットやデータサイエンス目的の40GBCSVファイルなどのファイルを大幅に高速に読み取っています。たとえば、最近傍探索を使用したクラスタリング、およびDBSCANとマルコフのクラスタリングアルゴリズムでは、深刻な問題を抱えるメモリと実時間の問題を回避するために、並列プログラミングの精巧さが必要です。

私は通常、最初にgnuツールを使用してファイルを行ごとに分割し、次にそれらすべてをglob-filemaskして、Pythonプログラムでそれらを並行して検索して読み取るのが好きです。私は一般的に1000以上の部分ファイルのようなものを使用します。これらのトリックを実行すると、処理速度とメモリ制限に非常に役立ちます。

pandas dataframe.read_csvはシングルスレッドであるため、並列実行のためにmap()を実行することで、これらのトリックを実行してパンダを非常に高速にすることができます。htopを使用すると、プレーンな古いシーケンシャルパンダdataframe.read_csvで、1つのコアの100%CPUがpd.read_csvの実際のボトルネックであり、ディスクではないことがわかります。

SATA6バスで回転するHDではなく、高速ビデオカードバスでSSDを使用し、さらに16個のCPUコアを使用していることを追加する必要があります。

また、一部のアプリケーションでうまく機能することを発見した別の手法は、1つの大きなファイルを多くのパーツファイルに事前に分割するのではなく、並列CSVファイルが1つの巨大なファイル内ですべてを読み取り、各ワーカーをファイルの異なるオフセットで開始することです。各並列ワーカーでpythonのファイルseek()とtell()を使用して、ビッグファイル内の異なるバイトオフセットの開始バイトと終了バイトの位置で、同時にビッグテキストファイルをストリップで読み取ります。バイトに対して正規表現のfindallを実行し、改行の数を返すことができます。これは部分的な合計です。最後に、部分的な合計を合計して、ワーカーの終了後にmap関数が戻ったときにグローバルな合計を取得します。

以下は、並列バイトオフセットトリックを使用したベンチマークの例です。

私は2つのファイルを使用しています:HIGGS.csvは8GBです。これは、UCI機械学習リポジトリからのものです。all_bin.csvは40.4GBで、現在のプロジェクトのものです。私は2つのプログラムを使用しています。Linuxに付属しているGNUwcプログラムと、私が開発した純粋なpythonfastread.pyプログラムです。

HP-Z820:/mnt/fastssd/fast_file_reader$ ls -l /mnt/fastssd/nzv/HIGGS.csv
-rw-rw-r-- 1 8035497980 Jan 24 16:00 /mnt/fastssd/nzv/HIGGS.csv

HP-Z820:/mnt/fastssd$ ls -l all_bin.csv
-rw-rw-r-- 1 40412077758 Feb  2 09:00 all_bin.csv

ga@ga-HP-Z820:/mnt/fastssd$ time python fastread.py --fileName="all_bin.csv" --numProcesses=32 --balanceFactor=2
2367496

real    0m8.920s
user    1m30.056s
sys 2m38.744s

In [1]: 40412077758. / 8.92
Out[1]: 4530501990.807175

これは、約4.5 GB / s、つまり45 Gb/sのファイルスラップ速度です。それは回転するハードディスクではありません、私の友人。それは実際にはSamsungPro950SSDです。

以下は、純粋なCコンパイル済みプログラムであるgnuwcによって行カウントされている同じファイルの速度ベンチマークです。

この場合、私の純粋なpythonプログラムがgnuwcでコンパイルされたCプログラムの速度と本質的に一致していることがわかります。Pythonは解釈されますが、Cはコンパイルされているので、これは非常に興味深い速度の偉業です。あなたも同意すると思います。もちろん、wcは本当に並列プログラムに変更する必要があります。そうすれば、それは私のpythonプログラムの靴下を本当に打ち負かすでしょう。しかし、現在のところ、gnuwcは単なるシーケンシャルプログラムです。あなたはできることをします、そしてpythonは今日並行して行うことができます。Cythonのコンパイルは私を助けることができるかもしれません(しばらくの間)。また、メモリマップトファイルはまだ調査されていません。

HP-Z820:/mnt/fastssd$ time wc -l all_bin.csv
2367496 all_bin.csv

real    0m8.807s
user    0m1.168s
sys 0m7.636s


HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2
11000000

real    0m2.257s
user    0m12.088s
sys 0m20.512s

HP-Z820:/mnt/fastssd/fast_file_reader$ time wc -l HIGGS.csv
11000000 HIGGS.csv

real    0m1.820s
user    0m0.364s
sys 0m1.456s

結論:Cプログラムと比較して、純粋なPythonプログラムの速度は良好です。ただし、少なくとも行数を数える目的で、Cプログラムよりも純粋なPythonプログラムを使用するだけでは十分ではありません。通常、この手法は他のファイル処理にも使用できるため、このPythonコードは引き続き優れています。

質問:正規表現を一度だけコンパイルしてすべてのワーカーに渡すと、速度が向上しますか?回答:正規表現の事前コンパイルは、このアプリケーションでは役に立ちません。その理由は、すべてのワーカーのプロセスのシリアル化と作成のオーバーヘッドが支配的であるためだと思います。

もう一つ。並列CSVファイルの読み取りも役立ちますか?ディスクがボトルネックですか、それともCPUですか?stackoverflowに関するいわゆる一流の回答の多くには、ファイルを読み取るのに1つのスレッドしか必要ないという一般的な開発の知恵が含まれています。でも、確かですか?

確認してみましょう:

HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2
11000000

real    0m2.256s
user    0m10.696s
sys 0m19.952s

HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=1 --balanceFactor=1
11000000

real    0m17.380s
user    0m11.124s
sys 0m6.272s

そうそう、そうです。並列ファイル読み取りは非常にうまく機能します。さて、あなたは行き​​ます!

追伸 知りたい人がいる場合、単一のワーカープロセスを使用しているときにbalanceFactorが2だったとしたらどうでしょうか。まあ、それは恐ろしいです:

HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=1 --balanceFactor=2
11000000

real    1m37.077s
user    0m12.432s
sys 1m24.700s

fastread.py pythonプログラムの重要な部分:

fileBytes = stat(fileName).st_size  # Read quickly from OS how many bytes are in a text file
startByte, endByte = PartitionDataToWorkers(workers=numProcesses, items=fileBytes, balanceFactor=balanceFactor)
p = Pool(numProcesses)
partialSum = p.starmap(ReadFileSegment, zip(startByte, endByte, repeat(fileName))) # startByte is already a list. fileName is made into a same-length list of duplicates values.
globalSum = sum(partialSum)
print(globalSum)


def ReadFileSegment(startByte, endByte, fileName, searchChar='\n'):  # counts number of searchChar appearing in the byte range
    with open(fileName, 'r') as f:
        f.seek(startByte-1)  # seek is initially at byte 0 and then moves forward the specified amount, so seek(5) points at the 6th byte.
        bytes = f.read(endByte - startByte + 1)
        cnt = len(re.findall(searchChar, bytes)) # findall with implicit compiling runs just as fast here as re.compile once + re.finditer many times.
    return cnt

PartitionDataToWorkersのdefは、通常のシーケンシャルコードです。他の誰かが並列プログラミングとは何かについて練習したい場合に備えて、私はそれを省略しました。私はあなたの学習の利益のために、より難しい部分を無料で配りました:テストされて動作する並列コード。

おかげで:オープンソースのH2Oプロジェクト、ArnoとCliff、およびH2Oスタッフによる、優れたソフトウェアと教育ビデオのおかげで、上記のような純粋なpython高性能パラレルバイトオフセットリーダーのインスピレーションを得ることができました。H2Oは、Javaを使用して並列ファイル読み取りを行い、PythonおよびRプログラムで呼び出すことができ、大きなCSVファイルを読み取る際に地球上で最も高速です。

于 2017-02-02T16:48:32.087 に答える
5

Katrielalexは、1つのファイルを開いて読み取る方法を提供しました。

ただし、アルゴリズムの実行方法では、ファイルの各行についてファイル全体が読み取られます。つまり、Nがファイル内の行数である場合、ファイルの読み取りの全体量(およびレーベンシュタイン距離の計算)はN*Nで実行されます。あなたはファイルサイズを心配していて、それをメモリに保持したくないので、私は結果として生じる二次ランタイムについて心配しています。あなたのアルゴリズムはO(n ^ 2)クラスのアルゴリズムであり、多くの場合、特殊化によって改善することができます。

ここでは、メモリとランタイムのトレードオフをすでに知っていると思いますが、複数のレーベンシュタイン距離を並列に計算する効率的な方法があるかどうかを調査することをお勧めします。もしそうなら、ここであなたの解決策を共有することは興味深いでしょう。

ファイルには何行あり、どの種類のマシン(mem&cpu power)でアルゴリズムを実行する必要がありますか?また、許容されるランタイムはどれくらいですか?

コードは次のようになります。

with f_outer as open(input_file, 'r'):
    for line_outer in f_outer:
        with f_inner as open(input_file, 'r'):
            for line_inner in f_inner:
                compute_distance(line_outer, line_inner)

しかし、問題は、距離(行列)をどのように保存するかであり、たとえば、処理のためにouter_lineを準備したり、再利用のためにいくつかの中間結果をキャッシュしたりする利点を得ることができます。

于 2011-11-04T14:09:14.623 に答える
2

fileinput .input()のPythonドキュメントから:

これは、にリストされているすべてのファイルの行を繰り返しsys.argv[1:]、デフォルトsys.stdinでリストが空の場合になります

さらに、関数の定義は次のとおりです。

fileinput.FileInput([files[, inplace[, backup[, mode[, openhook]]]]])

行間を読んで、これはfilesあなたが次のようなものを持つことができるようにリストになることができることを私に教えてくれます:

for each_line in fileinput.input([input_file, input_file]):
  do_something(each_line)

詳細については、こちらをご覧ください

于 2011-11-04T13:32:05.290 に答える
2

デフォルトのファイルの読み込みは非常に遅いため、使用しないことを強くお勧めします。numpy関数とIOpro関数(例:numpy.loadtxt())を調べる必要があります。

http://docs.scipy.org/doc/numpy/user/basics.io.genfromtxt.html

https://store.continuum.io/cshop/iopro/

次に、ペアワイズ操作をチャンクに分割できます。

import numpy as np
import math

lines_total = n    
similarity = np.zeros(n,n)
lines_per_chunk = m
n_chunks = math.ceil(float(n)/m)
for i in xrange(n_chunks):
    for j in xrange(n_chunks):
        chunk_i = (function of your choice to read lines i*lines_per_chunk to (i+1)*lines_per_chunk)
        chunk_j = (function of your choice to read lines j*lines_per_chunk to (j+1)*lines_per_chunk)
        similarity[i*lines_per_chunk:(i+1)*lines_per_chunk,
                   j*lines_per_chunk:(j+1)*lines_per_chunk] = fast_operation(chunk_i, chunk_j) 

ほとんどの場合、データをチャンクでロードしてから、要素ごとに実行するよりも、データに対して行列演算を実行する方がはるかに高速です。

于 2014-10-17T19:39:11.767 に答える
2
#Using a text file for the example
with open("yourFile.txt","r") as f:
    text = f.readlines()
for line in text:
    print line
  • 読むためにファイルを開く(r)
  • ファイル全体を読み取り、各行をリスト(テキスト)に保存します
  • 各行を印刷するリストをループします。

たとえば、特定の行の長さが10を超えているかどうかを確認する場合は、すでに使用可能なものを使用します。

for line in text:
    if len(line) > 10:
        print line
于 2016-07-30T02:01:01.027 に答える
2

最後の位置の読み取りから大きなファイルを頻繁に読み取る必要がありますか?

Apacheaccess.logファイルを1日に数回カットするために使用するスクリプトを作成しました。そのため、最後の実行中に解析された最後の行に位置カーソルを設定する必要がありました。この目的のために、私はカーソルをファイルに保存できるメソッドを使用しましたfile.seek()file.seek()

私のコード:

ENCODING = "utf8"
CURRENT_FILE_DIR = os.path.dirname(os.path.abspath(__file__))

# This file is used to store the last cursor position
cursor_position = os.path.join(CURRENT_FILE_DIR, "access_cursor_position.log")

# Log file with new lines
log_file_to_cut = os.path.join(CURRENT_FILE_DIR, "access.log")
cut_file = os.path.join(CURRENT_FILE_DIR, "cut_access", "cut.log")

# Set in from_line 
from_position = 0
try:
    with open(cursor_position, "r", encoding=ENCODING) as f:
        from_position = int(f.read())
except Exception as e:
    pass

# We read log_file_to_cut to put new lines in cut_file
with open(log_file_to_cut, "r", encoding=ENCODING) as f:
    with open(cut_file, "w", encoding=ENCODING) as fw:
        # We set cursor to the last position used (during last run of script)
        f.seek(from_position)
        for line in f:
            fw.write("%s" % (line))

    # We save the last position of cursor for next usage
    with open(cursor_position, "w", encoding=ENCODING) as fw:
        fw.write(str(f.tell()))
于 2020-01-07T13:18:47.143 に答える
-3

大きなファイルを1行ずつ読み取る最良の方法は、pythonenumerate関数を使用することです

with open(file_name, "rU") as read_file:
    for i, row in enumerate(read_file, 1):
        #do something
        #i in line of that line
        #row containts all data of that line
于 2017-08-24T07:02:13.733 に答える