我的问题看起来像是经典问题,但我无法在stackoverflow中找到完全相同的问题。我希望我的不是一个重复的问题。
我有一个大文件。该文件有许多行和固定列。我对所有列中的A列和B列感兴趣。目标是我想得到行,其中(1)行中的列A中的值也出现在其他行中,以及(2)有多个行具有相同的列A值,但是B列的不同值。
考虑下表。我对行1,3和5感兴趣,因为“a”出现在3行中,而列B中的值是不同的。相反,我对第2行和第4行不感兴趣,因为“b”出现两次,但其在B列中的对应值始终为“1”。同样,我对第6行不感兴趣,因为“c”只出现一次。
# A B C D ========= 1 a 0 x x 2 b 1 x x 3 a 2 x x 4 b 1 x x 5 a 3 x x 6 c 1 x x
要查找此类列,我会读取文件中的所有行,使用对象转换每一行,为对象创建列表,并使用以下算法查找有趣的列。该算法有效,但我的数据集需要时间。您有什么建议可以使算法有效吗?
def getDuplicateList(oldlist):
# find duplicate elements
duplicate = set()
a_to_b = {}
for elements in oldlist:
a = elements.getA()
b = elements.getB()
if a in a_to_b:
if b != a_to_b[a]:
duplicate.add(a)
a_to_b[a] = b
# get duplicate list
newlist = []
for elements in oldlist:
a = elements.getA()
if a in duplicate:
newlist.append(a)
return newlist
P.S。我添加了一些限制来澄清。
duplicate
有“一些”有趣的“a”。答案 0 :(得分:0)
原始订单是否需要维护?如果没有,它看起来与groupby非常相似,并且您可能会因使用内置方法而获得性能提升。
也许是这样的(未经测试!):
s = sorted(oldlist, key=lambda e: (e.getA(), e.getB()))
interesting = (g for k,g in itertools.groupby(s, lambda e: e.getA())
if len(g) > 1)
答案 1 :(得分:0)
你的复杂性已经非常好了。你只是想在这里寻找线性加速。
有没有理由你不能只返回duplicate
而不是做第二次循环?
如果您添加else
,则可以避免重新插入a_to_b[a] = b
。
此外,磁盘I / O速度很慢,并且在等待读取时CPU有很多时间用于其他事情。由于你有很多这样做,你可以通过让一个线程找到重复项而另一个线程正在读取下一个文件来获得显着的加速。
答案 2 :(得分:0)
以下内容非常简单。它产生有趣行的A值;修改它以产生行很简单:
def isInteresting(rows):
avals = {}
for row in rows:
bvals = avals.get(row.getA()) or set()
bvals.add(rowgetB())
avals[row.getA()] = bvals
return [ aval
for aval in avals.keys()
if avals[aval] and len(avals[aval]) > 1 ]
答案 3 :(得分:0)
嗯,oldlist
中元素的两次迭代可以用一次迭代代替。我相信在大多数情况下,这会提高算法的效率,特别是对于长列表。
如果newlist
的顺序与您无关,我建议使用与您的算法具有相同结果的单循环替换。我已经对随机生成的百万元素列表进行了测试,它总是在大约一半的时间内运行:
def new_getDuplicateList(oldlist):
# find duplicate elements
newlist = []
duplicate = set()
a_to_b = {}
for elements in oldlist:
a = elements[0]
b = elements[1]
if a in duplicate:
newlist.append(a)
else:
if a in a_to_b.keys():
if not b in a_to_b[a]:
a_to_b[a].append(b)
duplicate.add(a)
extension = [a for i in a_to_b[a]]
newlist.extend(extension)
else:
a_to_b[a].append(b)
else:
a_to_b[a] = [b]
return newlist
(可能会使条件变得更漂亮。)修改它以输出整行而不仅仅是a
值非常容易,只需将a
替换为(a, b)
必要时。另请注意,由于a_to_b dicts(现在包含列表),它比第一个算法消耗的内存更多。
答案 4 :(得分:0)
从列表中的不同项目创建对象可能会导致一些减速。在这里,我只是使用collections模块创建一个multiset,并让容器自己整理出不相关的项目。看看这对你有什么用。我假设你上面提供的确切文件格式。
import collections
def get_interesting_items(filename):
multiset = collections.defaultdict(set)
with open(filename) as f:
# skip header lines
f.readline()
f.readline()
# add all B items to Bset, indexed by A
for line in f:
_, a, b, _ = line.split(' ', 3)
multiset[a].add(int(b))
# generate all A, Bset pairs where Bset contains at least 2 items.
for a, bset in multiset.iteritems():
if len(bset) >= 2:
yield a, bset
def main():
for a, bset in get_interesting_items('myfile.txt'):
print a, bset