在范围字典中查找值 - python

时间:2013-03-13 17:44:04

标签: python dictionary range

我正在将2个文件与初始标识符列,起始值和结束值进行比较。第二个文件包含相应的标识符和另一个值列。

实施例。

文件1:

A     200     900
A     1000    1200
B     100     700
B     900     1000

文件2:

A     103
A     200
A     250
B     50
B     100
B     150

我想找到第二个文件中包含在第一个文件中找到的范围内的所有值,以便我的输出看起来像:

A     200
A     250
B     100
B     150

现在我已经从第一个文件创建了一个包含范围列表的字典: 实施例

if Identifier in Dictionary:
    Dictionary[Identifier].extend(range(Start, (End+1)))
else:
    Dictionary[Identifier] = range(Start, (End+1))

然后我浏览第二个文件并搜索范围字典中的值: 实施例

if Identifier in Dictionary:
    if Value in Dictionary[Identifier]:
    OutFile.write(Line + "\n")

虽然不是最优的,但这适用于相对较小的文件,但是我有几个大文件,这个程序证明非常低效。我需要优化我的程序,以便它运行得更快。

5 个答案:

答案 0 :(得分:2)

from collections import defaultdict
ident_ranges = defaultdict(list)
with open('file1.txt', 'r') as f1
    for row in f1:
        ident, start, end = row.split()
        start, end = int(start), int(end)
        ident_ranges[ident].append((start, end))
with open('file2.txt', 'r') as f2, open('out.txt', 'w') as output:  
    for line in f2:
        ident, value = line.split()
        value = int(value)
        if any(start <= value <= end for start, end in ident_ranges[ident]):
            output.write(line)

备注:使用defaultdict可以在不首先检查密钥是否存在的情况下为字典添加范围。使用any可以缩短范围检查。使用链式比较是一个很好的Python语法快捷方式(start <= value <= end)。

答案 1 :(得分:0)

由于你有很大的范围而你的问题基本上只是一堆比较,所以存储开始/结束元组几乎肯定比整个范围更快(特别是因为你现在拥有的将会复制大部分的如果两个碰巧重叠,则范围内的数字。)

# Building the dict
if not ident in d:
    d[ident] = (lo, hi)
else:
    old_lo, old_hi = d[ident]
    d[ident] = (min(lo, old_lo), max(hi, old_hi))

然后你的比较看起来像:

# comparing...
if ident in d:
    if d[ident][0] <= val <= d[ident][1]:
        outfile.write(line+'\n')

如果您不对if ident in d进行单独检查,则此部分的两个部分都会更快。 Python字典很好而且速度很快,所以只需要调用它就可以了。您可以为字典提供默认值,因此请使用它。我没有对这个或任何事情进行基准测试,看看加速是什么,但你肯定会得到一些,它肯定有效:

# These both make use of the following somewhat silly hack:
# In Python, None is treated as less than everything (even -float('inf))
# and empty containers (e.g. (), [], {}) are treated as greater than everything.
# So we use the tuple ((), None) as if it was (float('inf'), float('-inf))

for line in file1:
    ident, lo, hi = line.split()
    lo = int(lo)
    hi = int(hi)
    old_lo, old_hi = d.get(ident, ((), None))
    d[ident] = (min(lo, old_lo), max(hi, old_hi))

# comparing:
for line in file2:
    ident, val = line.split()
    val = int(val)
    lo, hi = d.get(ident, ((), None))
    if lo <= val <= hi:
        outfile.write(line) # unless you stripped it off, this still has a \n

以上代码是我用来测试的;它会在几秒钟内在file2百万行上运行。

答案 2 :(得分:0)

你需要构建range(START, END)吗?当你可以这样做时,这似乎很浪费:

if START <= x <= END:
    # process

检查值是否在范围内是因为a)您必须构建列表并且b)对列表执行线性搜索以找到它。

答案 3 :(得分:0)

您可以尝试这样的事情:

In [27]: ranges=defaultdict(list)

In [28]: with open("file1") as f:
    for line in f:
        name,st,end=line.split()
        st,end=int(st),int(end)
        ranges[name].append([st,end])
   ....:         

In [30]: ranges
Out[30]: defaultdict(<type 'list'>, {'A': [[200, 900], [1000, 1200]], 'B': [[100, 700], [900, 1000]]})

In [29]: with open("file2") as f:
    for line in f:
        name,val=line.split()
        val=int(val)
        if any(y[0]<=val<=y[1] for y in ranges[name]):
            print name,val
   ....:             
A 200
A 250
B 100
B 150

答案 4 :(得分:0)

巧妙的技巧:Python允许您与in个对象进行xrange比较,这比使用in进行range快得多,并且内存效率更高。< / p>

所以,你可以做到

from collections import defaultdict

rangedict = defaultdict(list)
...
rangedict[ident].append(xrange(start, end+1))
...

for i in rangedict:
    for r in rangedict[i]:
        if v in r:
            print >>outfile, line