2013-03-25 142 views
2

我正在嘗試重寫一些我認爲效率很低(更不用說不雅)的舊bash腳本,並使用一些可怕的管道......也許某些有真正Python技能的人可以給我一些指點...python文件操作(bash腳本移植)

這個腳本的多個臨時文件的用途......另一件事我認爲這是一個糟糕的風格,也許可以避免......

它基本上是由第一切割出一定的操縱INPUT-FILE頂部的行數(丟棄標題)。
然後翻出的一列和:

  • raws = N計算數目;
  • 從此單個列文件中拋出所有重複條目(我使用sort -u -n FILE > S-FILE)。

之後,我創建一個從1到N的順序整數索引,並使用paste命令將此新索引列粘貼到原始的INPUT-FILE中。
我的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 500線-FILE其中第4列中的值大於2000並將其寫入文件2000- $ INPUT-FILE
  2. 對於$ INPUT-FILE中的所有REMAINING行,隨機選擇500行,其中列4中的值大於1000,並且把它寫出來到文件1000- $ I NPUT-FILE
  3. 對於1後在$ INPUT-FILE所有剩餘的線)和2)中,隨機選擇500行,其中在第4列值大於500並寫入到文件500- $ INPUT-FILE

同樣,我希望有人能夠幫助我將這個醜陋的管道變成一件蟒蛇之美! :) 謝謝!評價

+0

你可以從文件中添加一個示例行,以及從命令行調用此腳本的示例嗎? – 2013-03-25 22:14:20

+0

請在發佈之前查看帖子預覽,並確保避免文字牆。它只會讓你的問題被忽略,因爲它很難破譯所說的話。 – Serdalis 2013-03-25 22:14:27

+0

一些圖書館看:'tempfile','difflib''隨機' – monkut 2013-03-25 22:16:31

回答

1

兩個關鍵點:

(A)的文件是〜的〜100個字符50K行。小到足以適應現代桌面/服務器/筆記本電腦系統的內存。 (B)作者的主要問題是關於如何跟蹤已經選擇的線條,並且不要再選擇它們。

我建議三個步驟。(1)通過文件,製作三個單獨的列表 - 將它們稱爲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中的行放入第二個輸出文件,將行數爲s2的行放入第三個輸出文件。這在任何語言中都很平凡,但是這裏有一個使用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() 
+0

感謝您的詳細解答!幾個註釋...... 1)在第三個代碼塊中,join()似乎只在我將它放入join([x,filename])時才起作用。2)我已將樣本大小從500更改爲100以進行測試而且我得到的結果是三個文件似乎在大小上波動,即第一次運行在一個超過76k行的大文件上時,恭敬地生成了98,100和100行的三個文件。第二次運行在SAME文件上產生了三個文件,每個文件分別有100,98和94行。第三次運行出現了99,98和99行的文件。我不太關注爲什麼...任何想法? – 2013-03-27 03:50:27

+0

顯然,在s0,s1和s2中出現重複的可能性......並且dict()不會創建重複的條目 - 即輸出文件中的行數減少了發現的重複次數每個集... – 2013-03-27 08:40:26

+0

好吧,我繞過這個問題,通過使用random.sample(set(u),500)構造等。謝謝!看起來這些解決方案支持小改正! :) – 2013-03-27 08:50:16

0

將它分解爲簡單的部分。

  1. 如果標頭不可用,請使用csv.DictReader或csv.reader讀取文件。在遍歷行時,請檢查第4列的值,並將行插入字典鍵列表的字典中,如'gt_2000','gt_1000','gt_500'。

  2. 迭代你的字典鍵和每一個,創建一個文件,並做一個500循環,併爲每個迭代,使用random.randint(0,len(the_list)-1)獲得列表的隨機索引,將其寫入文件,然後從列表中刪除該索引處的項目。如果任何一個桶裏的物品數量少於500個,那麼這需要多一點。