在更改值时使用python拆分排序文件

时间:2013-08-28 18:49:07

标签: python shell awk filesplitting

我是python的新手。我的要求,如果我必须使用awk这么简单,如下所示,

下面提到的文件(test.txt)是制表符分隔的,

1 a b c
1 a d e
1 b d e
2 a b c
2 a d e
3 x y z

我想要的输出

文件1.txt应具有以下值

a b c
a d e
b d e

文件2.txt应具有以下值

a b c
a d e

文件3.txt应具有以下值

x y z

原始文件在第一列上排序。我不知道我必须拆分的行号。它必须是价值的变化。使用awk,我会像

一样编写它
awk -F"\t" 'BEGIN {OFS="\t";} {print $2","$3","$4 > $1}' test.txt 

(性能明智,python会更好吗?)

4 个答案:

答案 0 :(得分:1)

Awk是完美的,应该快得多。速度真的是一个问题,你的输入有多大?

$ awk '{print $2,$3,$4 > ("file"$1)}' OFS='\t' file

<强>演示:

$ ls
file

$ cat file
1 a b c
1 a d e
1 b d e
2 a b c
2 a d e
3 x y z

$ awk '{print $2,$3,$4 > ("file"$1)}' OFS='\t' file

$ ls
file  file1  file2  file3

$ cat file1
a b c
a d e
b d e

$ cat file2 
a b c
a d e

$ cat file3
x y z

答案 1 :(得分:0)

这样的事情应该做你想做的事。

import itertools as it

with open('test.txt') as in_file:
    splitted_lines = (line.split(None, 1) for line in in_file)
    for num, group in it.groupby(splitted_lines, key=lambda x: x[0]):
        with open(num + '.txt', 'w') as out_file:
            out_file.writelines(line for _, line in group)
  • with语句允许安全地使用资源。在这种情况下,它们会自动关闭文件。
  • splitted_lines = (...)行在占据每一行的字段上创建一个可迭代,并产生第一个元素对,其余的行。
  • itertools.groupby函数是执行大部分工作的函数。它遍历文件的行并根据第一个元素对它们进行分组。
  • (line for _, line in group)遍历“分割线”。它只删除第一个元素并只写入其余的行。 (_只是一个标识符。我可以使用xfirst,但我_通常用于表示您拥有的内容分配,但你不使用)

我们可以简化代码。例如,最外面的with不太可能有用,因为我们只是在读取模式下打开文件,而不是修改它。 删除它我们可以取消缩进:

import itertools as it

splitted_lines = (line.split(None, 1) for line in open('test.txt'))
for num, group in it.groupby(splitted_lines, key=lambda x: x[0]):
    with open(num + '.txt', 'w') as out_file:
        out_file.writelines(line for _, line in group)

我做了一个非常简单的基准来测试python解决方案与awk解决方案。 性能大致相同,python稍微更快使用一个文件,其中每行有10个字段,100个“行组”,每个随机大小在2到30个元素之间。

python代码的时间:

In [22]: from random import randint
    ...: 
    ...: with open('test.txt', 'w') as f:
    ...:     for count in range(1, 101):
    ...:         num_nums = randint(2, 30)
    ...:         for time in range(num_nums):
    ...:             numbers = (str(randint(-1000, 1000)) for _ in range(10))
    ...:             f.write('{}\t{}\n'.format(count, '\t'.join(numbers)))
    ...:             

In [23]: %%timeit
    ...: splitted_lines = (line.split(None, 1) for line in open('test.txt'))
    ...: for num, group in it.groupby(splitted_lines, key=lambda x: x[0]):
    ...:     with open(num + '.txt', 'w') as out_file:
    ...:         out_file.writelines(line for _, line in group)
    ...: 
10 loops, best of 3: 11.3 ms per loop

Awk时间:

$time awk '{print $2,$3,$4 > ("test"$1)}' OFS='\t' test.txt

real    0m0.014s
user    0m0.004s
sys     0m0.008s

请注意0.014s约为14 ms

无论如何,根据操作系统的负载,时间可能会有所不同,并且有效地同步很快。在实践中,几乎所有的时间都是从文件读取/写入文件,这是由python和awk高效完成的。我相信使用C你不会看到巨大的速度增益。

答案 2 :(得分:0)

我的版本:

for line in open('text.txt', 'r'):
    line = line.split(' ')
    doc_name = line[0]
    content = ' '.join(line[1:]) 

    f = open('file' + doc_name, 'a+')
    f.write(content)

答案 3 :(得分:0)

如果你有一个非常大的文件,awk会打开并关闭每一行上的文件来做追加,不是吗?如果这是一个问题,那么C ++有速度和容器类来很好地处理任意数量的打开输出文件,这样每个文件只能打开和关闭一次。这是标记的Python,它几乎一样快,假设I / O时间占主导地位。

避免Python中额外开/关开销的版本:

# iosplit.py

def iosplit(ifile, ifname="", prefix=""):
    ofiles = {}
    try:
        for iline in ifile:
            tokens = [s.strip() for s in iline.split('\t')]
            if tokens and tokens[0]:
                ofname = prefix + str(tokens[0]) + ".txt"
                if ofname in ofiles:
                    ofile = ofiles[ofname]
                else:
                    ofile = open(ofname, "w+")
                    ofiles[ofname] = ofile
                ofile.write( '\t'.join(tokens[1:]) + '\n')
    finally:
        for ofname in ofiles:
            ofiles[ofname].close()

if __name__ == "__main__":
    import sys
    ifname = (sys.argv + ["test.txt"])[1]
    prefix = (sys.argv + ["", ""])[2]
    iosplit(open(ifname), ifname, prefix)

命令行用法是python iosplit.py

默认为空,将作为每个输出文件名的前缀。调用程序提供了一个文件(或类文件对象),因此您可以使用StringIO对象甚至列表/元组字符串来驱动它。

警告:这将删除行中制表符前面或后面的任何空格。内部空间不会被触及。所以当写入1.txt时,“1 \ ta b \ t c \ t d”将被转换为“a b \ tc \ td”。