39

この非常に大きな CSV ファイル (15 Gb) があり、そこから約 100 万行のランダムな行を読み取る必要があります。私が見て実装できる限り、Python の CSV ユーティリティでは、ファイル内で順次反復することしかできません。

すべてのファイルをメモリに読み取ってランダムな選択を使用するのは非常にメモリを消費し、すべてのファイルを調べていくつかの値を破棄して他の値を選択するには非常に時間がかかるため、CSV ファイルからランダムな行を選択する方法はありますか?その行だけを読む?

私は成功せずに試しました:

import csv

with open('linear_e_LAN2A_F_0_435keV.csv') as file:
    reader = csv.reader(file)
    print reader[someRandomInteger]

CSV ファイルのサンプル:

331.093,329.735
251.188,249.994
374.468,373.782
295.643,295.159
83.9058,0
380.709,116.221
352.238,351.891
183.809,182.615
257.277,201.302
61.4598,40.7106
4

13 に答える 13

32
import random

filesize = 1500                 #size of the really big file
offset = random.randrange(filesize)

f = open('really_big_file')
f.seek(offset)                  #go to random position
f.readline()                    # discard - bound to be partial line
random_line = f.readline()      # bingo!

# extra to handle last/first line edge cases
if len(random_line) == 0:       # we have hit the end
    f.seek(0)
    random_line = f.readline()  # so we'll grab the first line instead

@AndreBoos が指摘したように、このアプローチは偏った選択につながります。行の最小長と最大長がわかっている場合は、次のようにしてこの偏りを取り除くことができます。

(この場合) min=3 と max=15 があると仮定しましょう

1) 前の行の長さ (Lp) を見つけます。

次に、Lp = 3 の場合、線は最も偏っています。Lp = 15 の場合、線は最も偏っています。選択される可能性が 5 倍高いため、20% の確率で使用する必要があります。

これを達成するには、次の X% の時間、行をランダムに維持します。

X = 分 / Lp

ラインを維持しない場合は、サイコロが出るまで別のランダムピックを行います. :-)

于 2012-05-30T16:03:29.243 に答える
10

この非常に大きな CSV ファイル (15 Gb) があり、そこから約 100 万行のランダムな行を読み取る必要があります。

正確に100 万行は必要なく、CSV ファイルの行数が事前にわかっていると仮定すると、リザーバー サンプリングを使用してランダムなサブセットを取得できます。データを繰り返し処理し、行ごとにその行が選択される可能性を判断します。そうすれば、必要なデータのパスは 1 つだけです。

これは、ランダム サンプルを頻繁に抽出する必要があるが、実際のデータセットが頻繁に変更されない場合にうまく機能します (データセットが変更されるたびにエントリの数を追跡するだけでよいため)。

chances_selected = desired_num_results / total_entries
for line in csv.reader(file):
   if random() < chances_selected:
        result.append(line)
于 2012-05-30T16:17:13.537 に答える
4

ランダムな行を何度も取得したい場合 (機械学習用のミニバッチなど)、巨大なファイルを 1 回 (メモリにロードせずに) スキャンしてもかまわない場合は、行インデックスのリストを作成して、 seek を使用して、行をすばやく取得します (Maria Zverina の回答に基づいています)。

# Overhead:
# Read the line locations into memory once.  (If the lines are long,
# this should take substantially less memory than the file itself.)
fname = 'big_file'
s = [0]
linelocs = [s.append(s[0]+len(n)) or s.pop(0) for n in open(fname)]
f = open(fname) # Reopen the file.

# Each subsequent iteration uses only the code below:
# Grab a 1,000,000 line sample
# I sorted these because I assume the seeks are faster that way.
chosen = sorted(random.sample(linelocs, 1000000))
sampleLines = []
for offset in chosen:
  f.seek(offset)
  sampleLines.append(f.readline())
# Now we can randomize if need be.
random.shuffle(sampleLines)
于 2016-03-03T23:36:26.113 に答える
2

random.sample(xrange(n), 1000000)行の総数がわかっている場合は、別の解決策が可能です。セットとしての行の総数まで100万個の乱数()を生成し、次を使用します。

for i, line in enumerate(csvfile):
    if i in lines_to_grab:
        yield line

これにより、偏りのない方法で正確に100万行が得られますが、事前に行数を用意しておく必要があります。

于 2012-05-30T16:41:02.393 に答える
2

行が本当に .csv 形式で固定フィールドでない場合は、そうではありません。ファイルを一度クロールして、各行のバイト オフセットのインデックスを作成し、後で必要になったときにインデックス セットのみを使用できますが、任意の csv ファイルの行末 \n 文字の正確な位置を先験的に予測する方法はありません。

于 2012-05-30T16:05:44.733 に答える
1

このデータを sqlite3 データベースに配置できる場合、いくつかのランダムな行を選択するのは簡単です。ファイル内の行を事前に読み取ったり、埋めたりする必要はありません。sqlite データ ファイルはバイナリであるため、データ ファイルは CSV テキストよりも 1/3 から 1/2 小さくなります。

THISのようなスクリプトを使用して CSV ファイルをインポートするか、最初にデータをデータベース テーブルに書き込むだけでよいでしょう。SQLITE3は Python ディストリビューションの一部です。

次に、これらのステートメントを使用して、1,000,000 のランダムな行を取得します。

mydb='csv.db'
con=sqlite3.connect(mydb)

with con:
    cur=con.cursor()
    cur.execute("SELECT * FROM csv ORDER BY RANDOM() LIMIT 1000000;")

    for row in cur.fetchall():
        # now you have random rows...
于 2012-05-30T22:18:00.733 に答える
0

この方法では、要素数が読み取る行数に等しい乱数セットを生成し、その範囲はデータに存在する行数です。次に、小さいものから大きいものへとソートされて保管されます。

次に、csv ファイルが行ごとに読み取られline_counter、行番号を示すために a が配置されます。次に、これline_counterはソートされた乱数リストの最初の要素でチェックされ、それらが同じ場合、その特定の行が新しいcsvファイルに書き込まれ、最初の要素がリストから削除され、以前の2番目の要素が最初の要素に置き換わりますそしてそのサイクルは続きます。

import random
k=random.sample(xrange(No_of_rows_in_data),No_of_lines_to_be_read)
Num=sorted(k)    
line_counter = 0

with open(input_file,'rb') as file_handle:
    reader = csv.reader(file_handle)
    with open(output_file,'wb') as outfile:
            a=csv.writer(outfile)
            for line in reader:
                line_counter += 1
                if line_counter == Num[0]:
                a.writerow(line)
                Num.remove(Num[0])
                if len(Num)==0:
                break    
于 2015-09-14T12:05:58.283 に答える
0

固定長レコードでファイルを書き換えて、後で中間ファイルにランダム アクセスを実行できます。

ifile = file.open("inputfile.csv")
ofile = file.open("intermediatefile.csv",'w')
for line in ifile:
    ofile.write(line.rstrip('\n').ljust(15)+'\n')

次に、次のことができます。

import random
ifile = file.open("intermediatefile.csv")
lines = []
samples = random.sample(range(nlines))
for sample in samples:
    ifile.seek(sample)
    lines.append(ifile.readline())

より多くのディスク容量が必要で、最初のプログラムの実行には時間がかかる場合がありますが、2 番目のプログラムのレコードへの無制限のランダム アクセスが許可されます。

于 2012-05-30T16:45:52.643 に答える