熊猫按行三列将列分配为不相交的组

时间:2020-08-16 07:32:55

标签: python pandas

给出一个带有布尔值的数据框,其中每行至少有三个True,每列至少有一个True,如果可能,我需要将这些列分配给N个不相交的组,每组中至少有三列。

例如N = 2

数据框:

        a      b     c     d      e      f
0     False  True  True  True   False  False
1     True   False True  True   True   True
2     False  False True  False  True   True

唯一可能的结果:b,c,d和a,e,f

在较大的数据帧中可能有多个解决方案,我需要任何一种解决方案。如果解决方案需要,可以跳过列,但最好使用尽可能多的列。生成的组可以是任意大小,但不得少于三个。例如,对于N = 3和具有30列的数据帧,三个大小分别为9、3、11的组是有效结果。

无法分发的数据帧示例(在上面的示例中重置e1)

        a      b     c     d      e      f
0     False  True  True  True   False  False
1     True   False True  True   False  True
2     False  False True  False  True   True

1 个答案:

答案 0 :(得分:0)

我使用了RichieV的建议,并通过dancing links解决了该问题。

import pandas as pd
from typing import List, Tuple, Set
from itertools import combinations


def distribute_columns_by_triplets(df: pd.DataFrame, group_num: int,
        result: List[Tuple[str]], tested: Set[Tuple[str]] = None) -> bool:
    if not group_num:
        return True

    if not df.shape[0]:
        return False

    if df.shape[1] < 3 * group_num:
        return False

    if tested is None:
        tested = set()

    for index, row in df.iterrows():
        positives = row[row].index
        for i in range(positives.size, 2, -1):
            current = combinations(positives, i)
            for combination in current:
                if combination in tested:
                    continue

                tested.add(combination)

                if distribute_columns_by_triplets(df.drop(columns=list(combination)),
                        group_num - 1, result, tested):
                    result.append(combination)
                    return True

        df.drop(index, inplace=True)

    return False


d = {
    'a': [False, True, False],
    'b': [True, False, False],
    'c': [True, True, True],
    'd': [True, True, False],
    'e': [False, True, True],
    'f': [False, True, True],
}

df = pd.DataFrame(d)

df['device_number'] = df.sum(axis=1)
df.sort_values(by='device_number', inplace=True)
df.drop('device_number', axis=1, inplace=True)

group_num = 2

result = []
if distribute_columns_by_triplets(df.copy(), group_num, result):
    print(result)
else:
    print('Failed')