如何从大文件中删除行

时间:2013-07-23 15:45:35

标签: python algorithm

我的表格的每一行都有一个大文件

a b c

我想删除所有不存在其他行的行,例如

b d e

d a e

abs(c - e) < 10

abcde都是整数。

例如,如果输入为:

0 1 10 
1 2 20
2 3 25
0 1 15 
1 4 40

然后输出应该是

1 2 20
2 3 25
0 1 15

是否有可能在线性时间之外做这件事?

一个想法是创建两个排序列表的词典。一个用于与第一列值关联的第三列值。另一个用于与第二列值关联的第三列值。然后当你看到一个b c时,在第二个字典中使用键a获得的排序列表中查找c,然后使用第一个字典中的键b获得排序列表中的c。

3 个答案:

答案 0 :(得分:3)

我不知道这是否可以在线性时间内完成。如果输入中有n个三元组,则在O(n·log n)时间内直接进行。以下是一种方法的草图,以一种不一定是优选的实现形式:

  1. 制作一系列标记M,最初全部清除。

  2. 创建一个数组并复制输入,首先在中间元素上排序,然后在中间元素相等时由第三个元素排序。 (到目前为止,时间是O(n·log n)。)

  3. 对于每个不同的中间值,使用key = third元素创建BST(二叉搜索树)。 (时间再次为O(n·log n)。)

  4. 使用中间值键入哈希表,数据指向适当的BST。也就是说,给定中间值y和第三个元素z,在时间O(1)中,我们可以得到中间值为y的三元组的BST;从那时起,O(log n)可以找到最接近z的第三元素值的三元组。

  5. 对于每个三元组t =(x,y,z)依次,如果尚未设置标记,则使用哈希表查找对应于x的BST(如果有)。在那个BST中,找到最接近z的第三个元素的三元组u。如果差值小于10,则设置t和u的标记。 (时间再次为O(n·log n)。)

  6. 重复步骤2-5,但BST基于第一个元素值而不是中间值,并在步骤5中基于y而不是x进行查找。 (虽然匹配关系是对称的,因此我们可以在步骤5中的每个周期设置两个标记,但是一些符合条件的三元组可能最终没有标记;即,它们处于公差范围内,但比找到的最近匹配范围更远。可以在步骤5中标记所有符合条件的三元组,但这会将最坏情况时间从O(n·log n)增加到O(n²·log n)。)

  7. 对于设置的每个标记,输出相应的三元组。

  8. 总时间:O(n·log n)。同时可以在不构建BST的情况下实现,而是在排序数组的子范围内使用二进制搜索。

    编辑:在python中,可以使用 bisect 构建可用的结构,如下面ipython解释器会话的摘录所示。 (可能有更有效的方法来执行这些步骤。)字典h中的每个数据项都是适合使用bisect进行搜索的数组。

    In [1]: from itertools import groupby
    
    In [2]: a=[(0,1,10), (1,2,20), (2,3,25), (0,1,15), (1,4,40), (1,4,33), (3,3,17), (2,1,19)]
    
    In [3]: b=sorted((e[1],e[2],i) for i,e in enumerate(a)); print b
    [(1, 10, 0), (1, 15, 3), (1, 19, 7), (2, 20, 1), (3, 17, 6), (3, 25, 2), (4, 33, 5), (4, 40, 4)]
    
    In [4]: h={k:list(g) for k,g in groupby(b,lambda x: x[0])}; h
    Out[4]: 
    {1: [(1, 10, 0), (1, 15, 3), (1, 19, 7)],
     2: [(2, 20, 1)],
     3: [(3, 17, 6), (3, 25, 2)],
     4: [(4, 33, 5), (4, 40, 4)]}
    

答案 1 :(得分:1)

像其他人所说的那样,线性时间可能是不可能的。这是一个简单的O(n ^ 2)实现。如果对字典中的列表进行排序,则应该能够改进运行时。

lines = """0 1 10 
1 2 20
2 3 25
0 1 15 
1 4 40"""
Adata = {}
Bdata = {}
for line in lines.split('\n'):
    a,b,c = line.split(' ')[:3]
    vals = map(int,[a,b,c])
    if b in Adata:
        Adata[b].append(vals)
    else:
        Adata[b] = [vals]
    if a in Bdata:
        Bdata[a].append(vals)
    else:
        Bdata[a] = [vals]

def case1(a,b,c):
    if a in Adata:
        for val in Adata[a]:
            if abs(int(c)-val[2]) < 10:
                return True
    return False

def case2(a,b,c):
    if b in Bdata:
        for val in Bdata[b]:
            if abs(int(c)-val[2]) < 10:
                return True
    return False


out = []
for line in lines.split('\n'):
    a,b,c = line.split(' ')[:3]
    if case1(a,b,c) or case2(a,b,c):
        out.append(line)

for line in out:
    print line

答案 2 :(得分:0)

我认为您正在寻找的是

set lines
for line in infile:
  if line not in lines:
    lines.add(line)
    outfile.write(line)