Панды находят все комбинации строк в рамках бюджета - PullRequest
0 голосов
/ 26 сентября 2019

Я пытаюсь найти способ определения всех возможных комбинаций строк в рамках 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

Данные

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

Я хочу найти каждую комбинацию, которую я мог бы купить за определенный бюджет, скажем, 35 ​​долларов за этот пример, при этом получая только одну из каждого типа.Я хотел бы получить новый кадр данных, состоящий из строк для каждой комбинации, который работает с каждым элементом в своем собственном столбце.

Я пытался сделать это с помощью itertools.product, но это может объединять и добавлять столбцы, но мне действительно нужно объединить и добавить определенный столбец на основе значений в другом столбце.Я сейчас немного в замешательстве.

Спасибо за помощь!

1 Ответ

3 голосов
/ 26 сентября 2019

Здесь можно использовать рецепт powerset из itertools с pd.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)])

Выводит один кадр данных с группами продуктов, которые удовлетворяют условию $ 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

Сколько способов было объединено, чтобы удовлетворить бюджет в 35 долларов?

df_groups['grp'].nunique()

Вывод:

258

Подробности:

Есть парахитрости / методы, которые используются здесь.Во-первых, мы используем индекс кадра данных для создания групп строк или элементов, используя powerset.Затем мы используем enumerate для идентификации каждой группы и с помощью assign создаем новый столбец в кадре данных с этим номером группы из перечисления.

Изменение для захвата не более одного из каждого типа:

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())])

Сколько групп?

df_groups['grp'].nunique()
62

Получите ровно по одному для каждого Тип:

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))])

Сколько групп?

df_groups['grp'].nunique()
21
...