仅通过其中一个值

时间:2015-08-25 16:31:27

标签: python sorting python-3.x dictionary

我正在为邮局购买邮票写一点优化工具。

我正在使用字典的过程中,我根据我在其他人中学到的东西进行排序"着名的"题: Sort a Python dictionary by value

在我的情况下,我的字典稍微复杂一点:
  - 一个四项目元组来制作
  - 和另一个五项目元组来制作数据

这个字典的来源是一个迭代,每个成功的循环都添加一行:

MyDicco[A, B, C, D] = eval, post, number, types, over

这只是一个微不足道的小例子,尝试75美分:
   {
   (0,0,1,1):( 22 ,75,2,2,0)
   (0,0,0,3):( 31 ,75,3,1,0)
   (0,0,2,0):( 2521 ,100,2,1,25)
   (0,1,0,0):( 12511 ,200,1,1,125)
   (1,0,0,0):( 27511 ,350,1,1,275)
   }

到目前为止,我正在使用此代码进行排序(正在运行):

MyDiccoSorted = sorted(MyDicco.items(), key=operator.itemgetter(1))

我按照我的评分进行排序,因为排序是为了将最佳解决方案带到顶端。评分得分只是五项元组中的一个数据(在示例中,这些是评分 - 分数:22,31,2521,12511和27511)。

正如你在上面的例子中看到的那样,它是由第二个元组索引1进行排序(如我所愿)。但是我必须(grumpily)带来我的评估分数"到了我的第二个元组的前面。代码显然是使用整个二元组进行排序过程,这很重,不需要。

这是我的问题:我怎样才能更精确地排序。我不想按字典的第二个元组排序:我想准确地定位第一个项目    理想情况下,我想把这个值放回原来的位置,即成为第二个元组中的最后一个项目 - 并且仍然按它排序。

我已经阅读并试验了operator.itemgetter()的语法,但还没有成功地抓住"抓住#34; "我的第二个项目的第一项"。 https://docs.python.org/3/library/operator.html?highlight=operator.itemgetter#operator.itemgetter

(注意:允许使用元组作为键和值,根据:
https://docs.python.org/3/tutorial/datastructures.html?highlight=dictionary 那些对我的项目来说很好;这个问题只是关于更好的排序)

对于那些喜欢一点背景的人(你会对我大喊我应该使用其他方法,但我现在正在学习字典(这是这个项目的目的之一) ):

这种优化适用于发展中国家,在这些国家,通常没有某些邮票价值,或者在任何特定邮局的库存都有限。它稍后将在Android手机上运行。

我们正在定期发邮件(是的,信件)。如果您考虑六种不同的基于目的地的邮资和数百封邮件信件,那么使用可用值确定每个目的地的确切邮资并找到具有特定值的低库存的解决方案是一个不平凡的过程。

还有其他模块可以帮助将理论上的最佳解决方案转化为可以在任何一天实际购买的东西,通过战略对话指导...

关于此问题中的词典: 我迭代所有合理的(足够高以制作所需的邮资,并且只支付高达一个邮票的一小部分)邮票价值的组合。

然后我计算一个"成功"值,基于所需的邮票数量(优先级),所需类型的数量(较低的优先级)(因为购买不同的邮票需要额外的时间在柜台)和非常高的罚款罚款。所以最低价值意味着最高的成功。

我会收集所有合理的解决方案"在字典中,需要标记的元组作为键,而另一些结果的元组 - 数据构成了值。它略微过度定义,因为人类需要在项目的这个阶段读取它(用于调试)。

如果你很好奇,想要阅读这个例子(第一行):
colums是:

  • 350美分的邮票数
  • 200美分的邮票数
  • 50美分的邮票数量
  • 25美分的邮票数
  • 评价得分
  • 计算应用邮资
  • 申请的邮票总数
  • 邮票类型总数
  • 以美分支付超额费用

或言语:(假设邮政服务提供350,200,50和25美分的现有邮票),我可以使用1x 50美分和1x 25美分来申请75美分的邮资。这给了我22的成功率(这个列表中最好的),邮资是75美分,需要两个不同价值的邮票和0美分多付。

7 个答案:

答案 0 :(得分:4)

我发现使用lambda expressions比记住各种operator函数更容易。

假设您的eval分数是您的价值元组的第3项(即(post, number, eval, types, over):

MyDiccoSorted = sorted(MyDicco.items(), key=lamba x:x[1][2])

或者,您可以创建一个命名函数来完成工作:

def myKey(x): return x[1][2]
MyDiccoSorted = sorted(MyDicco.items(), key=myKey)

答案 1 :(得分:4)

你可以使用双索引,这样的东西应该可以工作:

MyDiccoSorted = sorted(MyDicco.items(), key=lambda s: s[1][2])

只需将2设置为元组中ID的索引。

答案 2 :(得分:3)

您可以使用lambda表达式而不是operator.itemgetter()来获取要排序的精确元素。假设您的evalvalues元组中的第一项,否则请使用x[1][0]中所需的精确元素的索引。示例 -

MyDiccoSorted = sorted(MyDicco.items(), key=lambda x: x[1][0])

这是如何运作的 -

dict.items()返回类似于元组列表的内容(尽管不完全是Python 3.x中的内容),示例 -

>>> d = {1:2,3:4}
>>> d.items()
dict_items([(1, 2), (3, 4)])

现在,在sorted()函数中,key参数接受一个函数对象(可以是lambda,或者operator.itemgetter()也返回一个函数,或者任何简单的函数),函数您传递给key的人应该接受一个参数,该参数将是要排序的列表的元素。

然后使用每个元素调用key函数,并且您应该返回正确的值以对列表进行排序。一个帮助您理解这一点的例子 -

>>> def foo(x):
...     print('x =',x)
...     return x[1]
...
>>> sorted(d.items(),key=foo)
x = (1, 2)
x = (3, 4)
[(1, 2), (3, 4)]

答案 3 :(得分:1)

这样做你需要的吗?

sorted(MyDicco.items(), key=lambda x: x[1][0])

答案 4 :(得分:1)

index_of_evaluation_score = 0
MyDiccoSorted = sorted(MyDicco.items(), key=lambda key_value: key_value[1][index_of_evaluation_score])

答案 5 :(得分:1)

将评估分数放回到您想要的最后,您可以使用以下内容:

MyDicco = {
    (0, 0, 1, 1): (75, 2, 2, 0, 22),
    (0, 0, 0, 3): (75, 3, 1, 0, 31),
    (0, 0, 2, 0): (100, 2, 1, 25, 2521),
    (0, 1, 0, 0): (200, 1, 1, 125, 12511),
    (1, 0, 0, 0): (350, 1, 1, 275, 27511)} 

MyDiccoSorted = sorted(MyDicco.items(), key=lambda x: x[1][4])

print MyDiccoSorted

,并提供:

[((0, 0, 1, 1), (75, 2, 2, 0, 22)), ((0, 0, 0, 3), (75, 3, 1, 0, 31)), ((0, 0, 2, 0), (100, 2, 1, 25, 2521)), ((0, 1, 0, 0), (200, 1, 1, 125, 12511)), ((1, 0, 0, 0), (350, 1, 1, 275, 27511))]

答案 6 :(得分:1)

我认为您可能要寻找的一种稳定的方法。

Python中的排序函数通常是“稳定”的排序。例如,如果您排序:

 1 4 6
 2 8 1
 1 2 3
 2 1 8

在其第一列中,您将获得:

 1 4 6
 1 2 3
 2 8 1
 2 1 8

在列1中共享相同值的行的顺序不变。 1 4 61 2 3之前排序,因为那是这些行在列1排序之前的原始顺序。自Python 2.2版以来,排序一直是“稳定的”。更多详细信息here

另一方面,我对您需要解释代码的数量感兴趣。这表明该代码将从重构中受益,以使其用途更加清楚。

命名元组可用于删除您在此处许多答案中看到的难以阅读的元组索引,例如key=lambda x: x[1][0]-这实际上是什么意思?在做什么?

这是一个使用命名元组的版本,可以帮助读者(最重要的是,您!)了解您的代码正在尝试执行的操作。请注意,lambda现在如何更好地解释自己。

from collections import namedtuple

StampMix = namedtuple('StampMix', ['c350', 'c200', 'c50', 'c25'])
Stats = namedtuple('Stats', ['score', 'postage', 'stamps', 'types', 'overpayment'])

data = {
    (0, 0, 1, 1): (22, 75, 2, 2, 0),
    (0, 0, 0, 3): (31, 75, 3, 1, 0),
    (0, 0, 2, 0): (2521, 100, 2, 1, 25),
    (0, 1, 0, 0): (12511, 200, 1, 1, 125),
    (1, 0, 0, 0): (27511, 350, 1, 1, 275)
}

candidates = {}
for stampmix, stats in data.items():
    candidates[StampMix(*stampmix)] = Stats(*stats)

print(sorted(candidates.items(), key=lambda candidate: candidate[1].score))

您会在输出中看到此方法的好处:

>>> python namedtuple.py
(prettied-up output follows...)
[
    (StampMix(c350=0, c200=0, c50=1, c25=1), Stats(score=22, postage=75, stamps=2, types=2, overpayment=0)),
    (StampMix(c350=0, c200=0, c50=0, c25=3), Stats(score=31, postage=75, stamps=3, types=1, overpayment=0)),
    (StampMix(c350=0, c200=0, c50=2, c25=0), Stats(score=2521, postage=100, stamps=2, types=1, overpayment=25)),
    (StampMix(c350=0, c200=1, c50=0, c25=0), Stats(score=12511, postage=200, stamps=1, types=1, overpayment=125)),
    (StampMix(c350=1, c200=0, c50=0, c25=0), Stats(score=27511, postage=350, stamps=1, types=1, overpayment=275))
]

,它也会对您的算法有所帮助。例如:

def score(stats):
    return stats.postage * stats.stamps * stats.types + 1000 * stats.overpayment