2009-06-17 61 views
2

假设我有一组列定义:如何建立此表中所有可能元组的列表?

Col1: value11 value12 value13 
Col2: value21 value22 
Col3: value31 value32 value33 
... 

鉴于列的某个子集 - 2个或更多 - 我想找到这些列所有可能的值。假如我选择1和2列,上面:

(value11 value21) 
(value11 value22) 
(value12 value21) 
(value12 value22) 
(value13 value21) 
(value13 value22) 

如果我选择2:3:

(value21 value31) 
(value21 value32) 
(value21 value33) 
(value22 value31) 
(value22 value32) 
(value22 value33) 

如果我选择这三个:

(value11 value21 value31) 
(value11 value21 value32) 
... 

我在Python中实现这一点,我想要一个快速算法来做到这一点。我的输入是元组列表:(columnName,columnValueList)

有什么建议吗?

回答

15

得到这个最好的方法是使用itertools.product()。例如:

import itertools 

group1 = ['a', 'b'] 
group2 = ['c', 'd'] 

print list(itertools.product(group1, group2)) 

#==> [('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd')] 

该函数接受多个参数(即多列)。请参阅this

0

看起来像itertools.combination可以帮助你。

>>> from itertools import combinations 
>>> list(combinations(xrange(5), 2)) 
[(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)] 
>>> list(combinations(xrange(5), 3)) 
[(0, 1, 2), (0, 1, 3), (0, 1, 4), (0, 2, 3), (0, 2, 4), (0, 3, 4), (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)] 
+0

itertools。组合会从单个迭代中得到子序列 - 提问者从两个迭代中寻找可能的对 – mdirolf 2009-06-17 15:45:26

1

除了使用itertools.product()为杰里米建议,你可能要考虑你的元组的列表转换为一个字典,使查找的COLUMNNAME快:

dict(tupleList)

2

更普遍的解决方案,杰里米的是:

import itertools 
full = [[value11, value12, value13], 
     [value21, value22], 
     [value31, value32, value33]] 
ids = 0, 2 

,或者如果它是一个字典(因为它应该是):

full = {'col1': [value11, value12, value13], 
     'col2': [value21, value22], 
     'col3': [value31, value32, value33]} 
ids = 'col1', 'col3' 
selected = (full[i] for i in ids) 
list(itertools.product(*selected)) 
1

我敢打赌itertools为基础的解决方案将会更快,但如果他们需要避免(例如,在没有itertools.product的情况下停留在Python 2.5上),当然必须在“基本Python”中完全编码。

试图“使出浑身解数”的速度,也许是这样的:(!需要与现实的样本数据彻底剖析)

def odo(*names_and_valuelists): 
    aux = [[vl, 0] for n, vl in names_and_valuelists] 
    if any(len(vl)==0 for vl, _ in aux): 
     return 
    while True: 
     yield tuple(vl[i] for vl, i in aux) 
     for vlandi in reversed(aux): 
      if vlandi[1] == len(vlandi[0])-1: 
      vlandi[1] = 0 
      else: 
      vlandi[1] += 1 
      break 
     else: 
      return 

虽然小的调整可能仍然加速它。

这是你使用的例子:

def main(): 
    data = [ 
     ('Col1', 'value11 value12 value13'.split()), 
     ('Col2', 'value21 value22'.split()), 
     ('Col3', 'value31 value32 value33'.split()), 
    ] 
    for tup in odo(data[0], data[1]): print tup 
    print 
    for tup in odo(data[1], data[2]): print tup 
    print 
    for i, tup in enumerate(odo(*data)): 
     print tup 
     if i>5: break 

if __name__ == '__main__': 
    main() 

发射结果:

('value11', 'value21') 
('value11', 'value22') 
('value12', 'value21') 
('value12', 'value22') 
('value13', 'value21') 
('value13', 'value22') 

('value21', 'value31') 
('value21', 'value32') 
('value21', 'value33') 
('value22', 'value31') 
('value22', 'value32') 
('value22', 'value33') 

('value11', 'value21', 'value31') 
('value11', 'value21', 'value32') 
('value11', 'value21', 'value33') 
('value11', 'value22', 'value31') 
('value11', 'value22', 'value32') 
('value11', 'value22', 'value33') 
('value12', 'value21', 'value31') 
相关问题