【问题标题】:Pick subset of items minimizing the count of the most frequent of the selected item's labels选择项目子集以最小化所选项目标签中最频繁的计数
【发布时间】:2022-08-14 02:08:41
【问题描述】:

问题

我想从项目列表中选择一个固定大小的子集,以使所选项目的标签最频繁出现的计数最小化。在英语中,我有一个包含 10000 个项目的列表的 DataFrame,生成如下。

import random
import pandas as pd
def RandLet():    
    alphabet = \"ABCDEFG\"
    return alphabet[random.randint(0, len(alphabet) - 1)]
items = pd.DataFrame([{\"ID\": i, \"Label1\": RandLet(), \"Label2\": RandLet(), \"Label3\": RandLet()} for i in range(0, 10000)])
items.head(3)

每个项目有 3 个标签。标签是 ABCDEFG 中的字母,标签的顺序无关紧要。同一个标签可以多次标记一个项目。
[前 3 行示例]

   ID Label1 Label2 Label3
0   0      G      B      D
1   1      C      B      C
2   2      C      A      B

从这个列表中,我想选择 1000 个项目,以尽量减少这些项目中出现频率最高的标签的出现次数。

例如,如果我的 DataFrame 只包含上述 3 个项目,而我只想选择 2 个项目,并且我选择了 ID #1 和 #2 的项目,则标签 \'C\' 出现了 3 次,\'B\ ' 出现 2 次,\'A\' 出现 1 次,所有其他标签出现 0 次 - 这些标签的最大值为 3。但是,我可以通过选择项目 #0 和 #2 做得更好,其中标签 \' B\' 出现频率最高,计数为 2。由于 2 小于 3,因此选择项目 #0 和 #2 比选择项目 #1 和 #2 更好。

在有多种方法可以选择 1000 个项目以使最大标签出现次数最小化的情况下,返回这些选择中的任何一个都可以。

我有什么

对我来说,这感觉类似于len(\"ABCDEFG\") = 7 维度中的背包问题。我想在背包里放 1000 件物品,每个物品在相关维度中的大小是该特定物品标签出现次数的总和。在这个程度上,我已经构建了这个函数来将我的项目列表转换为背包的大小列表。

def ReshapeItems(items):
    alphabet = \"ABCDEFG\"
    item_rebuilder = []
    for i, row in items.iterrows():
        letter_counter = {}
        for letter in alphabet:
            letter_count = sum(row[[c for c in items.columns if \"Label\" in c]].apply(lambda x: 1 if x == letter else 0))
            letter_counter[letter] = letter_count
        letter_counter[\"ID\"] = row[\"ID\"]
        item_rebuilder.append(letter_counter)
    items2 = pd.DataFrame(item_rebuilder)
    return items2

items2 = ReshapeItems(items)
items2.head(3)

[前 3 行 item2 示例]

     A  B  C  D  E  F  G   ID
0    0  1  0  1  0  0  1    0
1    0  1  2  0  0  0  0    1
2    1  1  1  0  0  0  0    2

不幸的是,在那一点上,我完全被困住了。我认为背包问题的重点是最大化某种价值,同时将所选物品大小的总和保持在某个限制之下 - 但是,这里我的问题是相反的,我想最小化所选大小的总和,使得我的价值至少是一些。

我在寻找什么

尽管接收itemsitems2 并返回满足我的规范的这些项目的子集的函数是理想的,但我很乐意接受任何足够详细的答案,为我指明正确的方向。

    标签: python pandas dynamic-programming knapsack-problem np-hard


    【解决方案1】:

    使用不同的方法,这是我对您有趣问题的看法。

    def get_best_subset(df, n_rows, key_cols, iterations=10_000):
        """Subset df in such a way that the frequency 
        of most frequent values in key columns is minimum.
    
        Args:
            df: input dataframe.
            n_rows: number of rows in subset.
            key_cols: columns to consider.
            iterations: max number of tries. Defaults to 10_000.
    
        Returns:
            Subset of n rows of input dataframe.
    
        """
        lowest_frequency = df.shape[0] * df.shape[1]
        best_df = pd.DataFrame([])
    
        # Iterate through all possible subsets
        i = 0
        while i < iterations:
            sample_df = df.sample(n=n_rows)
            # Count values in each column, concat and sum counts, get max count
            frequency = (
                pd.concat([sample_df[col].value_counts() for col in key_cols])
                .pipe(lambda df_: df_.groupby(df_.index).sum())
                .max()
            )
            if frequency < lowest_frequency:
                lowest_frequency = frequency
                best_df = sample_df
            if i == iterations:
                break
            i += 1
        return lowest_frequency, best_df.sort_values(by=["ID"]).reset_index(drop=True)
    

    因此,使用您提供的玩具数据框构造函数:

    lowest_frequency, best_df = get_best_subset(
        items, 1_000, ["Label1", "Label2", "Label3"]
    )
    
    print(lowest_frequency)
    # 433
    
    print(best_df)
    # Output
           ID Label1 Label2 Label3
    0       0      F      D      G
    1      17      D      G      B
    2      19      B      A      B
    3      34      A      F      A
    4      38      F      E      D
    ..    ...    ...    ...    ...
    995  9965      G      C      F
    996  9967      B      A      D
    997  9969      E      F      G
    998  9988      D      D      C
    999  9998      C      D      E
    
    [1000 rows x 4 columns]
    

    【讨论】:

      猜你喜欢
      • 2018-04-22
      • 2011-02-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-25
      • 1970-01-01
      相关资源
      最近更新 更多