简体   繁体   中英

what is most pythonic way to find a element in a list that is different with other elements?

Suppose we have a list with unknown size and there is an element in the list that is different with other elements but we don't know the index of the element. the list only contains numerics and is fetched from a remote server and the length of the list and the index of the different element is changed every time. what is the most pythonic way to find that different element? I tried this but I'm not sure if it's the best solution.

a = 1
different_element = None
my_list = fetch_list()

b = my_list[0] - a

for elem in my_list[1::]:
    if elem - a != b:
        different_element = elem

print(different_element)

Would this work for you?

In [6]: my_list = [1,1,1,2,1,1,1]
In [7]: different = [ii for ii in set(my_list) if my_list.count(ii) == 1]
In [8]: different
Out[8]: [2]

You can use Counter from collections package

from collections import Counter

a = [1,2,3,4,3,4,1]
b = Counter(a)  # Counter({1: 2, 2: 1, 3: 2, 4: 2})
elem = list(b.keys())[list(b.values()).index(1)]  # getting elem which is key with value that equals 1
print(a.index(elem))

Another possible solution that just differently compute elem

a = [1,2,3,4,3,4,1]
b = Counter(a)  # Counter({1: 2, 2: 1, 3: 2, 4: 2})
elem = (k for k, v in b.items() if v == 1)
print(a.index(next(elem)))

UPDATE

Time consumption:

As @Jblasco mentioned, Jblasco's method is not really efficient one, and i was curious to measure it.

So the initial data is array with 200-400 elements, with only one unique value. The code that generate that array is. At the end of snipped there is 100 first elements that prove that it has one unique

import random
from itertools import chain
f = lambda x: [x]*random.randint(2,4)
a=list(chain.from_iterable(f(random.randint(0,100)) for _ in range(100)))
a[random.randint(1, 100)] = 101
print(a[:100])
# [5, 5, 5, 84, 84, 84, 46, 46, 46, 46, 6, 6, 6, 68, 68, 68, 68, 38,
# 38, 38, 44, 44, 61, 61, 15, 15, 15, 15, 36, 36, 36, 36, 73, 73, 73, 
# 28, 28, 28, 28, 6, 6, 93, 93, 74, 74, 74, 74, 12, 12, 72, 72, 22, 
# 22, 22, 22, 78, 78, 17, 17, 17, 93, 93, 93, 12, 12, 12, 23, 23, 23, 
# 23, 52, 52, 88, 88, 79, 79, 42, 42, 34, 34, 47, 47, 1, 1, 1, 1, 71,
# 71, 1, 1, 45, 45, 101, 45, 39, 39, 50, 50, 50, 50]

That's the code that show us results, i choose to execute 3 times with 10000 executions:

from timeit import repeat


s = """\
import random
from itertools import chain
f = lambda x: [x]*random.randint(2,4)
a=list(chain.from_iterable(f(random.randint(0,100)) for _ in range(100)))
a[random.randint(1, 100)] = 101
"""

print('my 1st method:', repeat(stmt="""from collections import Counter
b=Counter(a)
elem = (k for k, v in b.items() if v == 1)
a.index(next(elem))""",
             setup=s, number=10000, repeat=3)

print('my 2nd method:', repeat(stmt="""from collections import Counter
b = Counter(a)
elem = list(b.keys())[list(b.values()).index(1)]
a.index(elem)""",
             setup=s, number=10000, repeat=3))

print('@Jblasco method:', repeat(stmt="""different = [ii for ii in set(a) if a.count(ii) == 1]
different""", setup=s, number=10000, repeat=3))

# my 1st method: [0.303596693000145, 0.27322746600111714, 0.2701447969993751]
# my 2nd method: [0.2715420649983571, 0.28590541199810104, 0.2821485950007627]
# @Jblasco method: [3.2133491599997797, 3.488262927003234, 2.884892332000163]

This is a great use for numpy

Given some random uniform list with a single uniquely different number in it:

>>> li=[1]*100+[200]+[1]*250

If the uniform value is known (in this case 1 and the unknown value is 200) you can use np.where on an array to get that value:

>>> import numpy as np
>>> a=np.array(li)
>>> a[a!=1]
array([200])

If the uniform values are not known, you can use np.uniques to get the counts of uniques:

>>> np.unique(a, return_counts=True)
(array([  1, 200]), array([350,   1]))

For a pure Python solution, use a generator with next to get the first value that is different than all the others:

>>> next(e for i, e in enumerate(li) if li[i]!=1)
200

Or, you can use dropwhile from itertools:

>>> from itertools import dropwhile
>>> next(dropwhile(lambda e: e==1, li))
200

If you do not know what the uniform value is, use a Counter on a slice big enough to get it:

>>> uniform=Counter(li[0:3]).most_common()[0][0]
>>> uniform
1
>>> next(e for i, e in enumerate(li) if li[i]!=uniform)
200

In these cases, next will short-circuit at the first value that satisfies the condition.

I would try maybe something like this:

newList = list(set(my_list))
print newList.pop()

Assuming there's only 1 different value and the rest are all the same. There's a little bit of ambiguity in your question which makes it difficult to answer but that's all I could think of optimally.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM