2010-10-15 88 views
4

您好我有多個CSV文件一噸的數據,並用grep過濾掉數據集:排序在bash CSV列,讀的bash輸出到蟒蛇變量

[email protected]:~/$ cat data.csv | grep -a "63[789]\...;" 
637.05;1450.2 
637.32;1448.7 
637.60;1447.7 
637.87;1451.5 
638.14;1454.2 
638.41;1448.6 
638.69;1445.8 
638.96;1440.0 
639.23;1431.9 
639.50;1428.8 
639.77;1427.3 

我想弄清楚的數據集其中計數最高的是列右邊的;然後知道相應的值(左邊的)。在這種情況下,我在尋找一套是638.14; 1454.2

我嘗試不同的事情,結束了使用bash和Python,它的工作原理的組合,但不是很漂亮:

os.system('ls | grep csv > filelist') 
files = open("filelist") 
files = files.read() 
files = files.split("\n") 

for filename in files[0:-1]: 
    os.system('cat ' + filename + ' | grep -a "63[6789]\...;" > filtered.csv') 
    filtered = csv.reader(open('filtered.csv'), delimiter=';') 
    sortedlist = sorted(filtered_file, key=operator.itemgetter(1), reverse=True) 
    dataset = sortedlist[0][0] + ';' + sortedlist[0][1] + '\n' 

我很想有一個bash唯一的解決方案(cut,awk,arrays?!?),但無法弄清楚。另外,我不喜歡將bash命令寫入文件然後將它們讀入python變量的工作。我可以直接將它們讀入變量,還是有更好的解決方案來解決這個問題? (可能是perl等......但我真的對bash解決方案感興趣..)

非常感謝!

回答

3

如果您要使用Python,請使用Python。你爲什麼將bash命令混合在一起?它使你的代碼不可移植/依賴於bash環境。

import os 
import glob 
import operator 
os.chdir("/mypath") 
for file in glob.glob("*.csv"): 
    data=open(file).readlines() 
    data=[i.strip().split(";") for i in data if i[:3] in ["637","638","639"]] 
    # data=[i.strip().split(";") for i in data if i[:3] in ["637","638","639"] and isinstance(float(i[:6]),float) ] 
    sortedlist = sorted(data, key=operator.itemgetter(1), reverse=True) 
    print "Highest for file %s: %s" % (file,sortedlist[0]) 

,或者,如果你更感興趣的是bash +工具的解決方案

find . -type f -name '*.csv' |while read -r FILE 
do 
grep -a "63[789]\...;" "$FILE" | sort -n -r -t ';' -k 2 | head -1 >> output.txt 
done 
+0

感謝這是一個非常好的腳本,但637,638和639的過濾器不檢查正則表達式\ ...;用python很容易嗎?我剛纔注意到的是在data = open line中的「」周圍的文件..再次感謝我真的很喜歡這個片段 – gletscher 2010-10-16 00:30:19

+0

如果你真的想使用正則表達式檢查,你可以使用're'模塊。否則,你可以簡單地檢查它是否是一個浮點數。看我的編輯。 – ghostdog74 2010-10-16 00:54:19

+0

+1,如果你認爲你需要frankenscripts,你可能不知道任何環境(bash或python)。我經常犯這個。 – Thomas 2010-10-16 14:42:33

6

一個快速班輪將是:

grep -a "63[789]\...;" data.csv | sort -n -r -t ';' -k 2 | head --lines=1 

這只是數字排序基於第二列中的文件,然後打印出的第一行。希望有所幫助。

+0

'-r'&'head'與刪除'-r'&使用'tail'相比有更多優點嗎? – Wrikken 2010-10-16 00:06:23

+0

如果在這兩個命令之間有大量數據傳輸,從頭部讀取尾部會更快。 – 2010-10-16 00:14:40

1
$ cat data.csv | grep -a "63[789]\...;" | awk 'BEGIN {FS=";"} $2>max{max=$2; val=$1} END {print "max " max " at " val}' 

max 1454.2 at 638.14 
+0

謝謝你,這條線與上面顯示的數據一起工作,但我仍然需要使用grep「63 [89] \ ...;」來過濾數據集,我嘗試將它輸入,但它不起作用。這行不會在csv上找到一個數字,因爲它包含一個標題等... – gletscher 2010-10-16 00:23:38

+0

@gletscher:看起來像你找到了解決方案,但我已經更新了答案,以顯示如何將它管到awk(哪些工作在我的結尾)。 – ars 2010-10-16 00:33:25

+0

非常感謝您的更新,它找到了正確的位置,但沒有正確顯示結果,輸出是:在638.14 也我試圖循環目錄中的所有文件並收集所有過濾的數據集 – gletscher 2010-10-16 00:36:32

0

好的,非常感謝,Hakop Palyan!

現在有一個關於如何從所有csv文件中獲取該數據集並將其作爲新文件收集到某處的技巧?像

find . -name '*.csv' -print0 | xargs -0 grep -a "63[789]\...;" | sort -n -r -t ';' -k 2 | head --lines=1 

這一個只打印第一行,我需要遍歷各個文件和收集數據集...

+0

您應該要求這是一個單獨的問題或更新您的原始問題。 – istruble 2010-10-16 05:52:28

1

如果你有大量的數據,那麼你不要」 t想要存儲所有的數據到內存然後排序它獲得最大值。關於計算時間複雜度和內存,這種方法是效率低下

您可以簡單地解析文件並實時計算所需的值。一個快速純Python的方法來處理你的問題:

import os, re 
os.chdir('/path/to/csvdir') 
for f in os.listdir('.'): 
    dataset, count = 0.0, 0.0 
    for line in open(f): 
     if re.search(r'63[6789]\...', line): 
      d, c = map(float, line.strip().split(';')) 
      if count < c: 
       dataset, count = d, c 
    print f, dataset 

這種方法也可用於通過修改相應的顯示最大值的列表(如果可以有多個數據集最高計數)行:

dataset, count = [], 0.0 
... 
     if count < c: 
      dataset, count = [d], c 
     elif count == c: 
      dataset.append(d) 

編輯:腳本假定您csvdir只與包含解析格式的文件填充。如果你想通過名稱對其進行過濾,您可以使用水珠(在名稱過濾有限的正則表達式功能):

for f in glob.glob('*.csv'): 

或過濾器適用於os.listdir

for f in filter(lambda f: re.match('.*\.csv', f), os.listdir('.')): 
+0

嘿,這是一個超級漂亮的腳本,儘管如果其他文件存在於文件夾中,腳本會出錯,所以使用ghostdogs74 glob.glob(「*。csv」):而不是os.listdir(「。」)似乎可以工作在這種情況下更好,非常感謝 – gletscher 2010-10-16 00:54:46

+0

謝謝你的正則表達式過濾器 – gletscher 2010-10-17 06:33:01

1

這裏是代碼我寫的使用python對csv文件進行排序。它允許您指定多個列,並使用減號按相反順序排序。

#!/usr/bin/env python 
# Usage: 
# (1) sort ctb_consolidated_test_id.csv by Academic Year, Test ID, Period, and Test Name, with Test ID in descending order 
# sort_csv.py -c "Academic Year" -c "-Test ID" -c "Period" -c "Test Name" ctb_consolidated_test_id.csv 
from __future__ import with_statement 
from __future__ import print_function 

import sys 

def multikeysort(items, columns): 
    from operator import itemgetter 
    import re 
    num_re = re.compile(r'^\d+$') 
    comparers = [ 
     ((itemgetter(col[1:].strip()), -1) if col.startswith('-') else (itemgetter(col.strip()), 1)) 
     for col in columns 
    ] 
    def number_comparable(val1, val2): 
     return len(val1) != len(val2) and num_re.match(val1) and num_re.match(val2) 
    def column_comparer(left, right): 
     for fn, mult in comparers: 
      val1, val2 = fn(left), fn(right) 
      if number_comparable(val1, val2): 
       val1, val2 = int(val1), int(val2) 
      result = cmp(val1, val2) 
      if result: 
       return mult * result 
     return 0 
    return sorted(items, cmp=column_comparer) 

def sort_csv(filename, columns): 
    import csv 
    with open(filename, "r") as f: 
     reader = csv.DictReader(f) 
     writer = csv.DictWriter(sys.stdout, reader.fieldnames) 
     writer.writerow(dict(zip(reader.fieldnames, reader.fieldnames))) 
     writer.writerows(multikeysort(reader, columns)) 

if __name__ == '__main__': 
    from glob import glob 
    from optparse import OptionParser, make_option 
    option_list = [ 
     make_option('-c', '--column', dest='columns', action='append', metavar='COLUMN NAME'), 
    ] 
    parser = OptionParser(option_list=option_list) 
    (options, args) = parser.parse_args() 
    filenames = (filename for arg in args for filename in glob(arg)) 
    for filename in filenames: 
     sort_csv(filename, options.columns) 
0

我知道你正在尋找一個基於bash的解決方案,但我還是忍不住用csv模塊提供的東西。

import os 
import csv 
import re 

target_re = re.compile(r'^63[789]\.\d\d$') 
csv_filenames = [f for f in os.listdir('.') if f.endwith('.csv')] 
largest_in_each_file = [] 

for f in csv_filenames: 
    largest = (None, 0) 
    for a,b in csv.reader(open(f, 'rb'), delimiter=';'): 
     if target_re.match(a) and b > largest[1]: 
      largest = (a, b) 
    largest_in_each_file.append(largest) 


largest_overall = largest_in_each_file[0] 
for largest in largest_in_each_file: 
    print "%s;%s in %s" % largest 
    if largest[1] > largest_overall[1]: 
     largest_overall = largest 

print "-" * 10 
print "%s;%s in %s is the largest record in all files" % largest_overall