python文件操作(bash脚本移植)

时间:2013-03-25 22:05:39

标签: python parsing text

我正在尝试重写我认为非常低效的一些旧bash脚本(更不用说不优雅)并使用一些可怕的管道......也许有真正Python技能的人可以给我一些指示......

该脚本使用了多个临时文件......我认为另一件事是风格不好,可能可以避免......

它实际上是通过从顶部切掉一定数量的线(丢弃标题)来操纵INPUT-FILE
然后它拉出其中一列并且:

  • 计算raws = N的数量;
  • 抛出此单个列文件中的所有重复条目(我使用sort -u -n FILE > S-FILE)。

之后,我创建一个从1到N的连续整数索引,并使用粘贴命令将此新索引列粘贴到原始INPUT-FILE中。
然后我的bash脚本为我们写入S-FILE的值生成Percentile Ranks 我相信Python利用scipy.stats,而在bash中我确定S-FILE中每个唯一条目的重复行数(dupline),然后计算per-rank=$((100*($counter+$dupline/2)/$length)),其中$ length = FILE的长度而不是S -文件。然后,我将结果打印到一个单独的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中的所有REMAINING行,随机选择500行,其中第4列中的值大于1000并将其写入文件1000- $ INPUT-FILE
  3. 对于1)和2)之后$ INPUT-FILE中的所有REMAINING行,随机选择500行,其中第4列中的值大于500,并将其写入文件500- $ INPUT-FILE
  4. 再一次,我希望有人可以帮我把这个丑陋的管道事物改造成蟒蛇之美! :)谢谢!

2 个答案:

答案 0 :(得分:1)

评论中的两个关键点:

(A)文件大约50k行~100个字符。小到足以在现代桌面/服务器/笔记本电脑系统中舒适地适应内存。

(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个元素的样本。我们想要删除之前选择的元素,因此请在集合中跟踪这些元素。 (“selected”集合是一个集合而不是列表,因为测试“如果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()

答案 1 :(得分:0)

将其分解成简单的部分。

  1. 如果标头不可用,请使用csv.DictReader或csv.reader读取文件。当您遍历这些行时,检查第4列的值并将这些行插入列表字典中,其中字典键类似于“gt_2000”,“gt_1000”,“gt_500”。

  2. 迭代你的字典键,为每一个创建一个文件并进行500循环,每次迭代,使用random.randint(0,len(the_list)-1)得到一个随机索引列表,将其写入文件,然后从列表中删除该索引处的项目。如果任何桶中的物品数量少于500件,则需要更多的物品。