生成min组合多个列表,返回最低的未组合列表?

时间:2013-04-19 10:07:35

标签: python list

我有一个列表如下:

aList = [[10564, 15, 1], [10564, 13, 1], [10589, 18, 1], [10637, 39, 1], [10662, 38, 1], [10837, 45, 1], [3, 17, 13], [7, 21, 13], [46, 1, 13]]

我想找到具有最低第二个元素的列表,如果第三个元素是1,那么它上面是[10564, 13, 1]。我做了一些帮助(虽然我不完全理解key = lambda k:k [1],这是什么意思?):

i = min((x for x in aList if (str(x[2])=="1")), key=lambda k:k[1])

我自己理解的方式是:

target = min(x[1] for x in aList if (str(x[2])=="1"))
matches = [x for x in aList if (x[1] == target) and (str(x[2])=="1")]

但是我想现在改变它,我想比较所有相邻列表,将它们的第二个元素添加到一起,找到具有最小值的列表对,然后最终返回具有该对中最小第二个元素的一个列表如果第三个元素是1,这就是全部。你怎么做?

编辑:示例输入:

aList = [[10564, 15, 1], [10564, 13, 1], [10589, 18, 1], [10637, 39, 1], [10662, 38, 1], [10837, 45, 1], [3, 17, 13], [7, 21, 13], [46, 1, 13]]

示例输出第一阶段:

[10564, 15, 1], [10564, 13, 1]

这是最低的相邻对,因为15 + 13 = 28,并且没有其他对具有这么低的第二个元素的添加。

最终输出是这一对中最低的:

[10564, 13, 1]

2 个答案:

答案 0 :(得分:3)

key参数告诉min通过确定最小的内容。

如果没有key参数,min会将任何给定的整个元组与其他元组进行比较,然后首先比较元组中的第一个元素。为输入序列中的每个元素调用key函数,并且最小元素仅由该键的返回值确定。 lambda k: k[1]返回元组中的第二个元素。

比较以下两个结果:

>>> example = [(5, 1), (4, 2), (3, 3), (2, 4), (1, 5)]
>>> min(example)
(1, 5)
>>> min(example, key=lambda element: element[1])
(5, 1)

在第一个示例中,未提供key函数且min()按原样比较每个元组,在第二个示例中,min()仅查看key()的内容函数返回,因此选择不同的元素作为最小值。

你可以真正落伍于这个关键功能:

>>> min(example, key=lambda element: (element[0] / element[1]) + element[1])
(4, 2)

不需要使用str,整个表达式过于冗长;你可以简化为:

i = min((x for x in aList if x[2] == 1), key=lambda k: k[1])

或使用operator.itemgetter

from operater import itemgetter

i = min((x for x in aList if x[2] == 1), key=itemgetter(1))

要比较相邻对,您需要itertools helper function

from itertools import tee, izip

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

然后使用itertools.ifilter将“最后一个元素为1”条件移动到过滤器更容易:

from itertools import ifilter

last_is_one = ifilter(lambda x: x[2] == 1, aList)
paired = pairwise(last_is_one)

现在我们可以做真正的工作;对于每对相邻列表,找到第二个元素总和最低的对,然后从该对找到最低的第二个元素:

# find minimum pair by second elements summed
minpair = min(paired, key=lambda pair: pair[0][1] + pair[1][1])
minimum = min(minpair, key=itemgetter(1))

将所有这些放在一起,过滤的责任留给函数的调用者:

from operater import itemgetter
from itertools import tee, izip

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

def neighbouring_minimum(iterable):
    paired = pairwise(iterable)

    # find minimum pair by second elements summed
    minpair = min(paired, key=lambda pair: pair[0][1] + pair[1][1])
    return min(minpair, key=itemgetter(1))

为您的样本输入提供:

>>> from itertools import ifilter
>>> aList = [[10564, 15, 1], [10564, 13, 1], [10589, 18, 1], [10637, 39, 1], [10662, 38, 1], [10837, 45, 1], [3, 17, 13], [7, 21, 13], [46, 1, 13]]
>>> filtered = ifilter(lambda x: x[2] == 1, aList)
>>> neighbouring_minimum(filtered)
[10564, 13, 1]

您甚至可以将最小值的条件移动到单独的键参数:

def neighbouring_minimum(iterable, key=None):
    if key is None:
        # default to the element itself
        key = lambda x: x

    paired = pairwise(iterable)

    # find minimum pair by key summed
    minpair = min(paired, key=lambda pair: sum(map(key, pair)))
    return min(minpair, key=key)

neighbouring_minimum(ifilter(lambda x: x[2] == 1, aList), key=itemgetter(1))

答案 1 :(得分:3)

对于lambda的一般理解,这里有几个很好的答案,我不能希望重现like this one

在您的具体情况中:(我已经稍微清理过了)

i = min((x for x in aList if x[2]==1)), key=lambda k:k[1])

你应该把它读作:

some_generator = (x for x in aList if x[2]==1)) # a generator of list elements 
                                                # where the 3rd element == 1
i = min(some_generator, key=lambda k:k[1])      # minimum with respect to the
                                                # 2nd element
上面代码中的

lambda拦截传递给min()的每个3元素列表并返回第二个值。这告诉min()不要将第一个元素最小化,默认情况下会这样做,但是第二个元素。希望下面的简单案例清楚地表明这一点。

>>> min([3,0],[1,2],[2,1])
[1,2]
>>> min([3,0],[1,2],[2,1], key=lambda x:x[1])
[3,0]

现在针对你的第二个问题,我认为这将实现你想要的......

[编辑:我删除了环绕功能。见下面的评论。这也意味着模数是多余的,因此代码更清晰!]

# Accept only elements where 3rd value is a 1 
only_ones = [x for x in aList if x[2]==1]

# neighbours will hold pairs of entries for easy lambda minimization
neighbours = []
for i, element in enumerate(only_ones[:-1]) :
   neighbours.append([element, only_ones[i+1]])

# Get minimum pair with respect to the sum of the pairs' middle elements first
# then get minimum of the resulting pair with respect to to middle element
i = min(min(neighbours, key=lambda k: k[0][1] + k[1][1]),
        key=lambda k:k[1])

事后看来,neighbours可能更适合作为发电机

neighbours = ([x, only_ones[i+1]] for i, x in enumerate(only_ones[:-1])) 

最后,对于过多,不可读的列表/生成器理解的粉丝

i = min(min(([x, only_ones[i+1]] 
              for i, x in enumerate([x for x in aList if x[2]==1][:-1])) ,
                 key=lambda k: k[0][1] + k[1][1]),
                    key=lambda k: k[1])

(抱歉,我无法抗拒!)