匹配列表python中相反数字的出现

时间:2017-03-31 14:15:41

标签: python list numpy

我有一个列表

results = [100, 100, -100, 100, -100, -100]

我想弄清楚第一次出现相反的数字。所以前100个匹配第一个-100,第二个100匹配第二个-100。

我希望将输出定位为:

[0, 2], [1, 4], [3, 5]

即:[0,2]代表results[0]results[2],其中第一次出现的100与第一次出现的-100匹配

编辑:您可以假设总是会有相同数量的正面/负数,而且该列表只包含1个数字

任何帮助都会被批评

8 个答案:

答案 0 :(得分:3)

对于列表中只包含2个整数(x-x)的简单情况,您可以简单地zip()将索引放在一起:

indexes = [[],[]]
for i,x in enumerate(results):
    indexes[0].append(i) if x > 0 else indexes[1].append(i)
list(zip(*indexes))

示例:

>>> results = [100, 100, -100, 100, -100, -100]
>>> indexes = [[],[]]
>>> for i,x in enumerate(results): indexes[0].append(i) if x > 0 else indexes[1].append(i)
... 
>>> list(zip(*indexes))
[(0, 2), (1, 4), (3, 5)]

注意小输入2个单独的列表推导(例如[i for i,x in enumerate(results) if x > 0]可能比追加for循环更快。

答案 1 :(得分:2)

这应该有效:

results = [100, 100, -100, 100, -100, -100]

solution = []
for i, x in enumerate(results):
    if x > 0 and isinstance(x, int):
        y = results.index(-x)
        results[results.index(-x)] = 'found'
        solution.append([i,y])

print solution

答案 2 :(得分:2)

这对于出现不同数字的一般情况也适用:

solutions = []
for x in set(abs(x) for x in results):
    solutions += list(zip([i for i, x2 in enumerate(results) if x2 == x],
                          [i for i, x2 in enumerate(results) if x2 == x*-1]))

答案 3 :(得分:2)

我们可以分两个阶段有效地完成这项工作。在分析阶段中,我们过滤掉正数,对它们进行排序并按索引对它们进行分组,例如:

from itertools import groupby

subresult = dict(map(lambda x:(x[0],iter(tuple(x[1]))),
                     groupby(sorted(filter(lambda x:x[1] < 0,enumerate(results)),
                             key=lambda x:x[::-1]),lambda x:x[1])
            ))

或者我们可以逐步生成 ,例如:

subresult = filter(lambda x:x[1] < 0,enumerate(results)) # filter negative values
subresult = sorted(subresult,key=lambda x:x[::-1]) # sort them on value and then on index
subresult = groupby(subresult,lambda x:x[1]) # group them on the value
subresult = map(lambda x:(x[0],iter(tuple(x[1]))),subresult) # construct a sequence of tuples (value,list of indices)
subresult = dict(subresult) # make it a dictionary

这会生成一个字典:

{-100: <itertools._grouper object at 0x7fedfb523ef0>}

接下来在构建阶段中,我们迭代所有正整数,并始终从subresult字典中取出下一个相反的整数。像:

end_result = [[i,next(subresult[-v])[0]] for i,v in enumerate(results) if v > 0]

这会产生:

>>> subresult = dict(map(lambda x:(x[0],iter(tuple(x[1]))),groupby(sorted(filter(lambda x:x[1] < 0,enumerate(results)),key=lambda x:x[::-1]),lambda x:x[1])))
>>> [[i,next(subresult[-v])[0]] for i,v in enumerate(results) if v > 0]
[[0, 2], [1, 4], [3, 5]]

通常由于字典查找,并且因为我们使用迭代器(因此我们对哪个索引进行簿记),这将非常有效。

答案 4 :(得分:2)

IMO,最快的方法(对于大输入)应该是以下一种方法(但是,我的解决方案并不假设输入列表只包含一个值及其相反的值,所以如果这样做可以更快假设被添加):

x = [100, 300, -300, 100, -100, -100]

from collections import defaultdict, deque

unmatched_positives = defaultdict(deque)

solution=[]
for i, val  in enumerate(x):
    if val > 0:
        unmatched_positives[val].append(i)
    else:
        solution.append( (unmatched_positives[-val].popleft(), i) )

print('Unsorted solution:', solution)
# If you need the result to be sorted
print('Sorted solution:', sorted(solution))

输出:

Unsorted solution: [(1, 2), (0, 4), (3, 5)]
Sorted solution: [(0, 4), (1, 2), (3, 5)]

答案 5 :(得分:2)

这种基于观察的简单方法怎么样?使用列表推导将其拆分为两个列表,然后按照您希望的顺序将它们zip分开。

使用列表理解

In [18]: neg_list = [idx for idx, el in enumerate(results) if el < 0]
In [19]: pos_list = [idx for idx, el in enumerate(results) if el > 0]

In [20]: neg_list
Out[20]: [2, 4, 5]

In [21]: pos_list
Out[21]: [0, 1, 3]

In [22]: list(zip(pos_list, neg_list))
Out[22]: [(0, 2), (1, 4), (3, 5)]

您还可以从zip的订单中修改所需的索引。

NumPy版本:   对于较大的列表(或等效的数组),numpy版本应该更快。

In [30]: res = np.array(results)
In [38]: pos_idx = np.where(res > 0)[0]
In [39]: pos_idx
Out[39]: array([0, 1, 3])

In [40]: neg_idx = np.where(res < 0)[0]
In [42]: neg_idx
Out[42]: array([2, 4, 5])

In [44]: list(zip(pos_idx, neg_idx))
Out[44]: [(0, 2), (1, 4), (3, 5)]

# If you want to avoid using zip, then 
# just use np.vstack and transpose the result
In [59]: np.vstack((pos_idx, neg_idx)).T
Out[59]: 
array([[0, 2],
       [1, 4],
       [3, 5]])

P.S。:您也可以使用generator comprehension来获得相同的结果,但请注意,将生成器转换为列表一次后它将会耗尽。

使用生成器理解

In [24]: neg_gen = (idx for idx, el in enumerate(results) if el < 0)
In [25]: pos_gen = (idx for idx, el in enumerate(results) if el > 0)

In [27]: list(zip(pos_gen, neg_gen))
Out[27]: [(0, 2), (1, 4), (3, 5)]

# on 2nd run, there won't be any element in the generator.
In [28]: list(zip(pos_gen, neg_gen))
Out[28]: []

答案 6 :(得分:1)

pos = {}
for i,item in enumerate(results ):
    if item < 0: continue 
    if item not in pos:
       pos[item] = []
    pos[item].append(i)

[ [pos[-item].pop(0), i] for i,item in enumerate(results ) if item < 0]  

[[0, 2], [1, 4], [3, 5]]

答案 7 :(得分:1)

对于results仅包含两个不同整数的示例案例:

import numpy as np

results = np.array([100, 100, -100, 100, -100, -100])

output = list(zip(np.where(results > 0)[0], np.where(results < 0)[0]))

输出:

[(0, 2), (1, 4), (3, 5)]

~0.002的时间为results * 1000