2010-10-15 51 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