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

我想找到第二个元素最低的列表,如果第三个元素是 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]
4

2 回答 2

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)

usingstr并不是真正需要的,整个表达式过于冗长;您可以将其简化为:

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辅助函数

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)

然后使用以下方法将“最后一个元素为 1”标准移动到过滤器更容易itertools.ifilter

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))
于 2013-04-19T10:20:04.440 回答
3

对于一般的理解,lambda这里有几个很好的答案,我不希望像这样复制。

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

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在上面的代码中,拦截每个传递给的 3 元素列表min()并返回第二个值。这告诉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])

(对不起,我无法抗拒!)

于 2013-04-19T10:47:12.483 回答