查找标称属性的所有二进制拆分

时间:2016-10-20 15:10:13

标签: python split combinations permutation decision-tree

问题

我试图在Python中基于仅具有名义属性的数据集在Python中构建二进制决策树分类器。

我坚持的一步是找到计算名义属性的二进制分割的所有可能方法。例如,对于具有可能值[a,b,c,d]的属性,我正在寻找一种方法将它们分成两个数组,以便我们获得:

  left     right
  ----     -----
  a        bcd
  b        acd
  c        abd
  d        abc
  ab       cd
  ac       bd
  ad       bc

没有重复拆分(例如我们不需要" bc"在left和" ad"在right中,因为这会产生相同的二进制拆分为"广告"在left和" bc"在right中)。每次拆分中的订单也无关紧要(例如" ad"与" da"在拆分的一侧相同)。

当前尝试

确切的术语正在逃避我,但我认为这是某种形式的组合/置换问题。我知道它不是一个我以后的动力。我能找到的与我类似的最接近的问题是here

到目前为止,我已经开始了一个迭代过程:

for i in range(1, array.length / 2):
  for j in range(1, i):
     # stuck here

仅在属性的可能值(array)的一半长度上进行循环的原因是因为如果我们在array.length / 2中存储最多left个值,右边有1 - (array.length / 2)个值,涵盖所有可能的分割。

另外,我已经听说过itertools图书馆..所以也许有一种方法可以实现我之后的目标?

2 个答案:

答案 0 :(得分:2)

仅供参考,您的二进制分割也称为partitions,只有2个部分。每个2分区完全由子集确定(分区的另一半​​是子集的补码),因此与组合的关系。

事实上,如果您generate the powerset中的字符串shortlex order,您实际上可以将powerset折叠成两半以生成所需的分区。

import itertools

def bsplit(chars):
    "Returns a list of all unique 2-partitions."
    assert len(chars) >= 2

    # first, we generate the powerset in shortlex order,
    # minus the empty set and its complement
    subsets = (itertools.combinations(chars, k) for k in range(1, len(chars)))
    subsets = itertools.chain.from_iterable(subsets)
    subsets = [''.join(sub) for sub in subsets]

    # then, we "fold" the set in half--pairing each subset 
    # in the first half with its complement from the second half
    half = len(subsets) // 2
    return list(zip(subsets[:half], reversed(subsets[half:])))

def test(*strings):
    for string in strings:
        for left, right in bsplit(string):
            print(left, right)
        print()

test('ab', 'abc', 'abcd', 'abcde')

这也表明,对于一组大小为n,有(2^n - 2) / 2) = 2^(n - 1) - 1)个大小为2的分区。

显然,你不能将它用于大型序列,因为它需要同时实现(几乎)整个powerset。虽然,它确实提出了避免生成重复项的有效解决方案:枚举有序powerset的第一个2^(n - 1) - 1)非空元素,并将每个子集映射到其对应的分区。

答案 1 :(得分:1)

我会使用itertools.product编写一个函数,将一个序列分成两半的所有可能的分区。我将遍历并使用集合删除重复项。

import itertools

def binary_splits(seq):
    for result_indices in itertools.product((0,1), repeat=len(seq)):
        result = ([], [])
        for seq_index, result_index in enumerate(result_indices):
            result[result_index].append(seq[seq_index])
        #skip results where one of the sides is empty
        if not result[0] or not result[1]: continue
        #convert from list to tuple so we can hash it later
        yield map(tuple, result)

def binary_splits_no_dupes(seq):
    seen = set()
    for item in binary_splits(seq):
        key = tuple(sorted(item))
        if key in seen: continue
        yield key
        seen.add(key)

for left, right in binary_splits_no_dupes("abcd"):
    print left, right

结果:

('a', 'b', 'c') ('d',)
('a', 'b', 'd') ('c',)
('a', 'b') ('c', 'd')
('a', 'c', 'd') ('b',)
('a', 'c') ('b', 'd')
('a', 'd') ('b', 'c')
('a',) ('b', 'c', 'd')