如何在Python中进行如下有序子集元素测试?

How to perform the following ordered subset element test in Python?

我有一个字典(称为 dict),其键是表示特征名称的字符串,其值是表示每个特征计数的浮点数。

这是我的字典 (dict) 的示例:

{'11268-238-1028': 2.0, '1028': 10.0, '10295': 2.0, '1781': 2.0, '11268-238': 3.0, '6967-167': 1.0, '9742-232-788': 1.0, '8542': 4.0, '238-1028': 5.0, '1028-122': 1.0}

在此示例中,“10295”被视为一级特征,“6967-167”被视为二级特征,而“9742-232-788”被视为三级特征。如果我们有'x-x-x-x-x-x-x',那么它就是一个七度特征。换句话说,对于任何 n 度特征,该特征具有 (n-1) 个破折号 ('-')。

'11268-238-1028': 2.0表示3度特征'11268-238-1028'的计数为2.那么我们看到'11268-238':3.0,表示'11268' -238' 出现 3 次。然而,这是一些重复计算问题,因为在 3 次出现的“11268-238”中,有 2 次实际上是由于“11268-238-1028”的出现。因此,我们要将'11268-238'的计数改为它的实际计数,即3-2 = 1。

同样,'238-1028'的实际计数不是5,因为'238-1028'是'11268-238-1028'的一部分,而'11268-238-1028'的计数为2 . 所以,'238-1028'的真实计数应该是(5-2 = 3)。

另一个例子是特征'1028',它的实际计数不应该是10。'1028'是3度特征'11268-238-1028'的一部分,它的计数为2。' 1028'也是计数为5的2度特征'238-1028'的一部分。'1028'也是计数为1的2度特征'1028-122'的一部分。因此,实际计数1 度特征“1028”应该是 (10-2-5-1 = 2)。

我应该用什么样的算法来解决这个问题?

我考虑过将每个键转换为一组由破折号分割的 1 度特征,然后针对每个集合,对所有其他具有更高长度的集合进行子集成员资格测试。但是,set 存储无序元素,但我关心顺序。例如,特征 '11268-238-1028' 转换为集合将是 (['11268', '238', '1028']);转换为集合的另一个特征 '11268-1028' 是 (['11268', '1028'])。如果我对这两个特征集执行子集测试,我会得出结论,(['11268', '1028']) 是 (['11268', '238', '1028']) 的子集。但是,特征“11268-1028”不是特征“11268-238-1028”的子集,因为在“11268”和“1028”之间,还有一个东西“238”,即顺序应该很重要。

那我该如何解决这个问题呢?

非常感谢!

将您的问题分解成更小、更简单的问题

首先让我们编写一个辅助函数来实际调整我们的数据字典

# this assumes we have one big feature (ie 3) and several smaller features(ie 2&1)
def adjust_data(big_feature,smaller_features,data):
    for feature in smaller_features:
        if feature.count("-") == big_feature.count("-"):
           continue # skip any features that are the same size as our target
        #3 cases for a sub feature it starts with ends with or is contained
        # we use delimiters to eliminate partial matches
        does_start = big_feature.startswith(feature+"-") 
        does_end = bigfeature.endswith("-"+feature) 
        does_contain = "-"+feature+"-" in big_feature
        if does_start or does_end or does_contain :
            # one of our cases match so this is a sub feature of our big feature
            data[feature] -= data[big_feature]

现在,在使用它之前,我们需要组织我们的数据,以便对其进行适当的排序。

 sorted_keys = sorted(my_data_dict.keys(),
                      key=lambda key:key.count("-"), 
                      reversed=True) #we want bigger features on top

现在沿着我们排序的 data_list

  for i,key in enumerate(sorted_keys,1):
      adjust_data(key,sorted_keys[i:],my_data_dict)

这只是蛮力,所以它不会那么快,但它会完成工作

在第一次创建 dict 时防止重复计数比以后撤消要容易得多。

但是假设不能重新创建dict。这是一个解决方案。它不假设对于每个较高度数的特征,每个度数都保证有较低度数的对应物(即,对于特征 A1-A2-...-An,您可能会遗漏 A1、A1-A2 中的任何一个等,直至 A1-A2-...-An-1)。如果这个假设确实成立,一些 try-except 可以简化掉。

def undo_double_counting(d):
    sorted_features = sorted(d, key=lambda f: f.count('-'), reverse=True)
    for f in sorted_features:
        if '-' not in f:
            return d
        feature_below, _ = f.rsplit('-', 1)
        while True:
            try:
                d[feature_below] -= d[f]
            except KeyError:
                # if the feature one degree below isn't actually in d,
                # we keep trying lower degrees until we know that we
                # can't go lower any more (by hitting ValueError)
                try:
                    feature_below, _ = feature_below.rsplit('-', 1)
                except ValueError:
                    break
            else:
                break
    # if there are no degree-1 features in d, return here
    return d

在您的数据上尝试(顺便说一句,为什么是浮点数,而不是整数?):

{'1028': 9.0,
 '1028-122': 1.0,
 '10295': 2.0,
 '11268-238': 1.0,
 '11268-238-1028': 2.0,
 '1781': 2.0,
 '238-1028': 5.0,
 '6967-167': 1.0,
 '8542': 4.0,
 '9742-232-788': 1.0}