2013-03-25 145 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个,那么这需要多一点。