2

私は、非常に非効率的であると思う古いbashスクリプトのいくつかを書き直して(エレガントではないことは言うまでもなく)、恐ろしい配管を使用しようとしています...おそらく、実際のP​​ythonスキルを持っている人が私にいくつかのポインターを与えることができます...

スクリプトは複数の一時ファイルを使用します...私が思うもう1つのことは悪いスタイルであり、おそらく回避することができます...

基本的INPUT-FILEに、最初に上から特定の数の行を切り取る(見出しを破棄する)ことによって操作します。
次に、列の1つを引き出し、次のようにします。

  • の数を計算しraws = Nます。
  • この単一の列ファイルからすべての重複エントリをスローします(私はを使用しますsort -u -n FILE > S-FILE)。

その後、1からNまでの順次整数インデックスを作成し、INPUT-FILEpasteコマンドを使用してこの新しいインデックス列を元のインデックス列に貼り付けます。
次に、bashスクリプトは、S-FILEに書き込んだ値のパーセンタイルランクを生成します。
Pythonはを活用していると思いますscipy.statsが、bashでは、S-FILEの一意のエントリごとに重複行(dupline)の数を決定し、計算しますper-rank=$((100*($counter+$dupline/2)/$length))。ここで、$ length = FILEの長さであり、S-FILEではありません。次に、結果を別の1列のファイルに出力します(そして、重複する回数だけ同じランクごとに繰り返します)。
次に、パーセンタイルランクのこの新しい列をINPUT-FILEに貼り付けます(パーセンタイルランクの計算に使用される列でINPUT-FILEを並べ替えるため、結果にはすべてが完全に一致します)。

この後、それは以下の醜さになります...

sort -o $INPUT-FILE $INPUT-FILE

awk 'int($4)>2000' $INPUT-FILE | awk -v seed=$RANDOM 'BEGIN{srand(seed);} {print rand()"\t"$0}' | sort -k1 -k2 -n | cut -f2- | head -n 500 > 2000-$INPUT-FILE

diff $INPUT-FILE 2000-$INPUT-FILE | sed '/^[0-9][0-9]*/d; s/^. //; /^---$/d' | awk 'int($4)>1000' | awk -v seed=$RANDOM 'BEGIN{srand(seed);} {print rand()"\t"$0}' | sort -k1 -k2 -n | cut -f2- | head -n 500 > 1000-$INPUT-FILE

cat 2000-$INPUT-FILE 1000-$INPUT-FILE | sort > merge-$INPUT-FILE

diff merge-$INPUT-FILE $INPUT-FILE | sed '/^[0-9][0-9]*/d; s/^. //; /^---$/d' | awk 'int($4)>500' | awk -v seed=$RANDOM 'BEGIN{srand(seed);} {print rand()"\t"$0}' | sort -k1 -k2 -n | cut -f2- | head -n 500 > 500-$INPUT-FILE

rm merge-$INPUT-FILE

基本的に、これは次のことを行うための非常にエレガントでないbashの方法です。

  1. $ INPUT-FILEから500行をランダムに選択し、列4の値が2000より大きい場合は、ファイル2000-$INPUT-FILEに書き込みます。
  2. $ INPUT-FILEの残りのすべての行について、列4の値が1000より大きい500行をランダムに選択し、ファイル1000-$INPUT-FILEに書き込みます。
  3. 1)および2)の後の$ INPUT-FILEの残りのすべての行について、列4の値が500より大きい500行をランダムに選択し、ファイル500-$INPUT-FILEに書き込みます。

繰り返しになりますが、誰かがこの醜い配管をPythonの美しさのものに作り直すのを手伝ってくれることを願っています!:) ありがとう!

4

2 に答える 2

1

コメントの2つの重要なポイント:

(A)ファイルは〜100文字の〜50k行です。最新のデスクトップ/サーバー/ラップトップシステムのメモリに快適に収まるほど小さい。

(B)作成者の主な質問は、すでに選択されている行を追跡し、再度選択しない方法についてです。

私は3つのステップを提案します。

(1)ファイルを調べて、各基準を満たす行番号の3つの別々のリスト(u、v、wと呼びます)を作成します。これらのリストには500行を超える可能性があり、重複が含まれている可能性がありますが、手順(2)でこれらの問題を取り除きます。

u = []
v = []
w = []

with open(filename, "r") as f:
    for linenum, line in enumerate(f):
        x = int(line.split()[3])
        if x > 2000:
            u.append(x)
        if x > 1000:
            v.append(x)
        if x > 500:
            w.append(x)

(2)行番号を選択します。組み込みのRandom.sample()を使用して、母集団からk個の要素のサンプルを選択できます。以前に選択された要素を削除したいので、そのような要素をセットで追跡します。(「選択された」コレクションはリストではなくセットです。「xが選択されていない場合」のテストはセットの場合はO(log(n))ですが、リストの場合はO(n)です。リストに変更してタイミングを正確に測定すると速度が低下しますが、「たった」50kデータポイント/500サンプル/3カテゴリのデータセットでは目立った遅延にはならない場合があります。)

import random
rand = random.Random()       # change to random.Random(1234) for repeatable results

chosen = set()
s0 = rand.sample(u, 500)
chosen.update(s0)
s1 = rand.sample([x for x in v if x not in chosen], 500)
chosen.update(s1)
s2 = rand.sample([x for x in w if x not in chosen], 500)
chosen.update(s2)

(3)入力ファイルをもう一度通過し、番号がs0の行を最初の出力ファイルに入れ、番号がs1の行を2番目の出力ファイルに入れ、番号がs2の行を3番目の出力ファイルに入れます。どの言語でも非常に簡単ですが、Pythonの「イディオム」を使用する実装は次のとおりです。

linenum2sample = dict([(x, 0) for x in s0]+[(x, 1) for x in s1]+[(x, 2) for x in s2])

outfile = [open("-".join(x, filename), "w") for x in ["2000", "1000", "500"]]

try:
    with open(filename, "r") as f:
        for linenum, line in enumerate(f):
            s = linenum2sample.get(linenum)
            if s is not None:
                outfile[s].write(line)
finally:
    for f in outfile:
        f.close()
于 2013-03-26T01:45:50.303 に答える
0

簡単な部分に分割します。

  1. csv.DictReaderを使用してファイルを読み取り、ヘッダーが使用できない場合はcsv.readerを使用します。行を反復処理しているときに、列4の値を確認し、辞書キーが「gt_2000」、「gt_1000」、「gt_500」のようなリストの辞書に行を挿入します。

  2. 辞書のキーを繰り返し処理し、それぞれについてファイルを作成して500のループを実行し、繰り返しごとにrandom.randint(0、len(the_list)-1)を使用してリストのランダムなインデックスを取得し、に書き込みます。ファイルを作成し、そのインデックスにあるアイテムをリストから削除します。バケット内のアイテムが500未満になる可能性がある場合は、もう少し多くのアイテムが必要になります。

于 2013-03-27T02:23:34.137 に答える