我试图在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
图书馆..所以也许有一种方法可以实现我之后的目标?
答案 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')