【问题标题】:How to create list of all unique combinations while keeping a total column for each combination?如何创建所有唯一组合的列表,同时为每个组合保留一个总列?
【发布时间】:2020-08-13 02:54:00
【问题描述】:

我正在尝试寻找所有可能的独特组合来制作三明治。我还想知道每个三明治要多少钱。我有 3 组两个列表,一个列表是商品,第二个是商品价格。

bread = ['italian', 'wheat', 'honey oat']
bprice = [1, 2, 3]
meat = ['roastbeef', 'ham', 'turkey', 'steak']
mprice = [3, 1, 2, 4]
vegetable = ['lettuce', 'onions', 'tomatoes', 'pickles']
vprice = [1, 4, 2, 3]

我正在尝试找到所有可能的唯一组合,但我也想要每个组合的总数。该组合应包含一种面包、一种肉和两种不同的蔬菜。例如:

输出:

          Combinations                Total
italian, ham, onions, pickles           9
italian, turkey, onions, pickles       ...
wheat, ham, onions, pickles            ...

我完全不知道该怎么做。我研究了 itertools.product,但这似乎只是得到组合而不是总数。我也想过索引来获取商品的价格,但又不知道如何将它与组合列表以及字典结合起来,但我也不知道如何将它与获取所有组合的列表结合起来。有什么想法可以让我这样做吗?

【问题讨论】:

  • 第一个问题,你真的需要这些清单吗?或者只是总数就足够了?其次,顺序重要吗?这很重要,因为通常在组合顺序中并不重要,我只是想确保您不想要顺序重要的排列。
  • 你打算用它们做什么?

标签: python python-3.x unique combinations


【解决方案1】:

我认为你可以这样做:

import pandas as pd #To display the results
from itertools import combinations, product

# Create bread dictionary 
bread = ['italian', 'wheat', 'honey oat']
bprice = [1, 2, 3]
breaddict = dict(zip(bread, bprice))

# Create meat dictionary
meat = ['roastbeef', 'ham', 'turkey', 'steak']
mprice = [3, 1, 2, 4]
meatdict= dict(zip(meat,mprice))

# Create veggie dictionary
vegetable = ['lettuce', 'onions', 'tomatoes', 'pickles']
vprice = [1, 4, 2, 3]
vegdict=dict(zip(vegetable, vprice))

# The real work is done here
# Create combinations of two veggies
# Then use that combination with bread and meat to calculate a product of sandwiches
sandwiches = product(bread,meat,combinations(vegetable,2))

# Create empty dataframe for storage and display
df=pd.DataFrame()

# Iterate through sandwiches 
# and use unpacking tuple and map with dictionary to populate dataframe
for b, m, v in sandwiches:
    df=df.append(pd.concat([pd.Series(', '.join([b, m, *v])).rename('Combinations'),
                            pd.Series(sum([breaddict[b], 
                                           meatdict[m], 
                                           *map(vegdict.get, v)])).rename('Total')], 
                           axis=1))
print(df)

输出:

                             Combinations  Total
0     italian, roastbeef, lettuce, onions      9
1   italian, roastbeef, lettuce, tomatoes      7
2    italian, roastbeef, lettuce, pickles      8
3    italian, roastbeef, onions, tomatoes     10
4     italian, roastbeef, onions, pickles     11
..                                    ...    ...
67    honey oat, steak, lettuce, tomatoes     10
68     honey oat, steak, lettuce, pickles     11
69     honey oat, steak, onions, tomatoes     13
70      honey oat, steak, onions, pickles     14
71    honey oat, steak, tomatoes, pickles     12

[72 rows x 2 columns]

没有 pandas,但仍然使用 itertools.combinations 和 itertools.product

from itertools import combinations, product

bread = ['italian', 'wheat', 'honey oat']
bprice = [1, 2, 3]
breaddict = dict(zip(bread, bprice))

# Create meat dictionary
meat = ['roastbeef', 'ham', 'turkey', 'steak']
mprice = [3, 1, 2, 4]
meatdict= dict(zip(meat,mprice))

# Create veggie dictionary
vegetable = ['lettuce', 'onions', 'tomatoes', 'pickles']
vprice = [1, 4, 2, 3]
vegdict=dict(zip(vegetable, vprice))

# The real work is done here
# Create combinations of two veggies
# Then use that combination with bread and meat to calculate a product of sandwiches
sandwiches = product(bread,meat,combinations(vegetable,2))

sandwiches_with_price = [[b,m,*v], 
                          sum([breaddict[b], meatdict[m], *map(vegdict.get, v)])) for b, m, v in sandwiches]
sandwiches_with_price

输出:

[(['italian', 'roastbeef', 'lettuce', 'onions'], 9),
 (['italian', 'roastbeef', 'lettuce', 'tomatoes'], 7),
 (['italian', 'roastbeef', 'lettuce', 'pickles'], 8),
 (['italian', 'roastbeef', 'onions', 'tomatoes'], 10),
 (['italian', 'roastbeef', 'onions', 'pickles'], 11),
 (['italian', 'roastbeef', 'tomatoes', 'pickles'], 9),
 (['italian', 'ham', 'lettuce', 'onions'], 7),
 (['italian', 'ham', 'lettuce', 'tomatoes'], 5),
 (['italian', 'ham', 'lettuce', 'pickles'], 6),
 (['italian', 'ham', 'onions', 'tomatoes'], 8),
 (['italian', 'ham', 'onions', 'pickles'], 9),
 (['italian', 'ham', 'tomatoes', 'pickles'], 7),
 (['italian', 'turkey', 'lettuce', 'onions'], 8),
 (['italian', 'turkey', 'lettuce', 'tomatoes'], 6),
 (['italian', 'turkey', 'lettuce', 'pickles'], 7),
 (['italian', 'turkey', 'onions', 'tomatoes'], 9),
 (['italian', 'turkey', 'onions', 'pickles'], 10),
 (['italian', 'turkey', 'tomatoes', 'pickles'], 8),
 (['italian', 'steak', 'lettuce', 'onions'], 10),
 (['italian', 'steak', 'lettuce', 'tomatoes'], 8),
 (['italian', 'steak', 'lettuce', 'pickles'], 9),
 (['italian', 'steak', 'onions', 'tomatoes'], 11),
 (['italian', 'steak', 'onions', 'pickles'], 12),
 (['italian', 'steak', 'tomatoes', 'pickles'], 10),
 (['wheat', 'roastbeef', 'lettuce', 'onions'], 10),
 (['wheat', 'roastbeef', 'lettuce', 'tomatoes'], 8),
 (['wheat', 'roastbeef', 'lettuce', 'pickles'], 9),
 (['wheat', 'roastbeef', 'onions', 'tomatoes'], 11),
 (['wheat', 'roastbeef', 'onions', 'pickles'], 12),
 (['wheat', 'roastbeef', 'tomatoes', 'pickles'], 10),
 (['wheat', 'ham', 'lettuce', 'onions'], 8),
 (['wheat', 'ham', 'lettuce', 'tomatoes'], 6),
 (['wheat', 'ham', 'lettuce', 'pickles'], 7),
 (['wheat', 'ham', 'onions', 'tomatoes'], 9),
 (['wheat', 'ham', 'onions', 'pickles'], 10),
 (['wheat', 'ham', 'tomatoes', 'pickles'], 8),
 (['wheat', 'turkey', 'lettuce', 'onions'], 9),
 (['wheat', 'turkey', 'lettuce', 'tomatoes'], 7),
 (['wheat', 'turkey', 'lettuce', 'pickles'], 8),
 (['wheat', 'turkey', 'onions', 'tomatoes'], 10),
 (['wheat', 'turkey', 'onions', 'pickles'], 11),
 (['wheat', 'turkey', 'tomatoes', 'pickles'], 9),
 (['wheat', 'steak', 'lettuce', 'onions'], 11),
 (['wheat', 'steak', 'lettuce', 'tomatoes'], 9),
 (['wheat', 'steak', 'lettuce', 'pickles'], 10),
 (['wheat', 'steak', 'onions', 'tomatoes'], 12),
 (['wheat', 'steak', 'onions', 'pickles'], 13),
 (['wheat', 'steak', 'tomatoes', 'pickles'], 11),
 (['honey oat', 'roastbeef', 'lettuce', 'onions'], 11),
 (['honey oat', 'roastbeef', 'lettuce', 'tomatoes'], 9),
 (['honey oat', 'roastbeef', 'lettuce', 'pickles'], 10),
 (['honey oat', 'roastbeef', 'onions', 'tomatoes'], 12),
 (['honey oat', 'roastbeef', 'onions', 'pickles'], 13),
 (['honey oat', 'roastbeef', 'tomatoes', 'pickles'], 11),
 (['honey oat', 'ham', 'lettuce', 'onions'], 9),
 (['honey oat', 'ham', 'lettuce', 'tomatoes'], 7),
 (['honey oat', 'ham', 'lettuce', 'pickles'], 8),
 (['honey oat', 'ham', 'onions', 'tomatoes'], 10),
 (['honey oat', 'ham', 'onions', 'pickles'], 11),
 (['honey oat', 'ham', 'tomatoes', 'pickles'], 9),
 (['honey oat', 'turkey', 'lettuce', 'onions'], 10),
 (['honey oat', 'turkey', 'lettuce', 'tomatoes'], 8),
 (['honey oat', 'turkey', 'lettuce', 'pickles'], 9),
 (['honey oat', 'turkey', 'onions', 'tomatoes'], 11),
 (['honey oat', 'turkey', 'onions', 'pickles'], 12),
 (['honey oat', 'turkey', 'tomatoes', 'pickles'], 10),
 (['honey oat', 'steak', 'lettuce', 'onions'], 12),
 (['honey oat', 'steak', 'lettuce', 'tomatoes'], 10),
 (['honey oat', 'steak', 'lettuce', 'pickles'], 11),
 (['honey oat', 'steak', 'onions', 'tomatoes'], 13),
 (['honey oat', 'steak', 'onions', 'pickles'], 14),
 (['honey oat', 'steak', 'tomatoes', 'pickles'], 12)]

【讨论】:

  • 添加一个没有 pandas 的普通 python 解决方案会有所帮助
  • @Pynchia pandas 只是为了对齐两个列表。如何,否则你会在发布的问题中显示“输出”吗?在我看来,这个问题确实可以用“产品和组合”系列来回答。其他一切都只是装扮。
  • 感谢 Scott 的帮助和解释,使用不带 pandas 的版本,这是我可以从最小到最大排序的一种方式吗?
  • 试试这个:sorted(sandwiches_with_price, key=lambda x: x[1], reverse=True)
【解决方案2】:

我假设每个三明治有 2 种蔬菜、1 种肉和 1 种面包。

bread = ['italian', 'wheat', 'honey oat']
bprice = [1, 2, 3]
meat = ['roastbeef', 'ham', 'turkey', 'steak']
mprice = [3, 1, 2, 4]
vegetable = ['lettuce', 'onions', 'tomatoes', 'pickles']
vprice = [1, 4, 2, 3]

# prices are easier to manage in a dict, let's do that.
prices = {x:y for x,y in zip(bread, bprice)}
prices.update({x:y for x,y in zip(meat, mprice)})
prices.update({x:y for x,y in zip(vegetable, vprice)})


#now let's make all the vegetable combinations:
combveg = [ sorted((x ,y)) for x in vegetable for y in vegetable if len(set([x,y])) == 2 ]

# remove duplicates
combveg = list(set([tuple(x) for x in combveg]))

# now calculate all the sandwich possibilities
sandwiches = [[b, m] + list(vgs) for b in bread for m in meat for vgs in combveg]

# just have to build their prices now
sandwiches_with_price = [(sandwich, sum([prices[item] for item in sandwich])) for sandwich in sandwiches]

大多数列表操作可以链接起来,或者使用一些生成器表达式进行优化,但目标是解释每个步骤。

【讨论】:

    【解决方案3】:

    另一个纯 Python 解决方案。

    from operator import itemgetter
    from itertools import product, combinations
    
    bread = ['italian', 'wheat', 'honey oat']
    bprice = [1, 2, 3]
    meat = ['roastbeef', 'ham', 'turkey', 'steak']
    mprice = [3, 1, 2, 4]
    vegetable = ['lettuce', 'onions', 'tomatoes', 'pickles']
    vprice = [1, 4, 2, 3]
    
    d = dict()
    d.update(zip(bread, bprice))
    d.update(zip(meat, mprice))
    d.update(zip(vegetable, vprice))
    
    for p in product(bread, meat, combinations(vegetable, 2)):
        # make 1 tuple from list and tuple (from combinations)
        p = p[0:2] + p[2]
        print('{:<40} = {}'.format(', '.join(p), sum(itemgetter(*p)(d))))
    

    它使用项目的产品(由Scott Boston使用)

    另外,它还有一个字典slice

    itemgetter(*p)(d))
    

    这会从字典 d 中获取(面包、肉类、蔬菜产品 p 的价格)

    以下代码行:

    p = p[0:2] + p[2]
    

    这很 hacky,但我不知道另一种方法可以让 combinations(vegetable, 2) 生成的元组为单个项目而不是它生成的元组。

    打印:

    italian, roastbeef, lettuce, onions      = 9
    italian, roastbeef, lettuce, tomatoes    = 7
    italian, roastbeef, lettuce, pickles     = 8
    italian, roastbeef, onions, tomatoes     = 10
    italian, roastbeef, onions, pickles      = 11
    italian, roastbeef, tomatoes, pickles    = 9
    italian, ham, lettuce, onions            = 7
    . . .
    honey oat, steak, lettuce, tomatoes      = 10
    honey oat, steak, lettuce, pickles       = 11
    honey oat, steak, onions, tomatoes       = 13
    honey oat, steak, onions, pickles        = 14
    honey oat, steak, tomatoes, pickles      = 12
    

    【讨论】:

      猜你喜欢
      • 2018-03-12
      • 1970-01-01
      • 2018-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-28
      • 2022-01-18
      • 1970-01-01
      相关资源
      最近更新 更多