Pandas 查找预算下的所有行组合

Ema*_*mac 2 python python-itertools dataframe pandas

我试图找出一种方法来确定 DataFrame 中低于预算的所有可能的行组合,所以假设我有一个这样的数据帧:

data = [['Bread', 9, 'Food'], ['Shoes', 20, 'Clothes'], ['Shirt', 15, 'Clothes'], ['Milk', 5, 'Drink'], ['Cereal', 8, 'Food'], ['Chips', 10, 'Food'], ['Beer', 15, 'Drink'], ['Popcorn', 3, 'Food'], ['Ice Cream', 6, 'Food'], ['Soda', 4, 'Drink']]
df = pd.DataFrame(data, columns = ['Item', 'Price', 'Type'])
df
Run Code Online (Sandbox Code Playgroud)

数据

Item       Price  Type
Bread      9      Food
Shoes      20     Clothes
Shirt      15     Clothes
Milk       5      Drink
Cereal     8      Food
Chips      10     Food
Beer       15     Drink
Popcorn    3      Food
Ice Cream  6      Food
Soda       4      Drink
Run Code Online (Sandbox Code Playgroud)

我想找到在特定预算下我可以购买的每一种组合,假设这个例子是 35 美元,而每种类型只得到一种。我想获得一个由每个组合的行组成的新数据框,这些组合适用于其自己的列中的每个项目。

我试图使用 itertools.product 来做到这一点,但这可以组合和添加列,但我真正需要做的是根据另一列中的值组合和添加特定列。我现在有点难住了。

谢谢你的帮助!

Sco*_*ton 7

这里有一种使用方法 powerset食谱从itertoolspd.concat

from itertools import chain, combinations

def powerset(iterable):
    "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

df_groups = pd.concat([df.reindex(l).assign(grp=n) for n, l in 
                       enumerate(powerset(df.index)) 
                       if (df.loc[l, 'Price'].sum() <= 35)])
Run Code Online (Sandbox Code Playgroud)

输出包含满足 $35 条件的产品组的单个数据框:

          Item  Price     Type  grp
0       Bread      9     Food    1
1       Shoes     20  Clothes    2
2       Shirt     15  Clothes    3
3        Milk      5    Drink    4
4      Cereal      8     Food    5
..        ...    ...      ...  ...
3        Milk      5    Drink  752
4      Cereal      8     Food  752
7     Popcorn      3     Food  752
8   Ice Cream      6     Food  752
9        Soda      4    Drink  752
Run Code Online (Sandbox Code Playgroud)

有多少种方法可以满足 35 美元的预算?

df_groups['grp'].nunique()
Run Code Online (Sandbox Code Playgroud)

输出:

258
Run Code Online (Sandbox Code Playgroud)

细节:

这里使用了一些技巧/方法。首先,我们使用数据框的索引来创建行组或项目组powerset。接下来,我们使用enumerate来标识每个组,并使用assignenumerate 中的组号在数据框中创建一个新列。

修改以捕获每种类型中的不超过一种:

df_groups = pd.concat([df.reindex(l).assign(grp=n) for n, l in 
                       enumerate(powerset(df.index)) 
                       if ((df.loc[l, 'Price'].sum() <= 35) & 
                           (df.loc[l, 'Type'].value_counts()==1).all())])
Run Code Online (Sandbox Code Playgroud)

几组?

df_groups['grp'].nunique()
62
Run Code Online (Sandbox Code Playgroud)

为每种类型获取一个:

df_groups = pd.concat([df.reindex(l).assign(grp=n) for n, l in 
                       enumerate(powerset(df.index)) 
                       if ((df.loc[l, 'Price'].sum() <= 35) & 
                           (df.loc[l, 'Type'].value_counts()==1).all()&
                           (len(df.loc[l, 'Type']) == 3))])
Run Code Online (Sandbox Code Playgroud)

几组?

df_groups['grp'].nunique()
21
Run Code Online (Sandbox Code Playgroud)

  • 美丽的!谢谢楼主,你这么快就明白了!做得好。 (2认同)