Using .index() with duplicate values that have separate identifiers - python

In the code below, I create identifiers a and b that point to two separate lists with the same value, verified by their unique id values.
Then I insert both into a list, and try to find the index for b, but instead it finds the index for a.
In [21]: a = [3,2]
In [22]: b = [3,2]
In [23]: id(a)
Out[23]: 4368404136
In [24]: id(b)
Out[24]: 4368429352
In [25]: c = [[4,3], a, [5,7], b, [6,3]]
In [26]: c.index(a)
Out[26]: 1
In [27]: c.index(b)
Out[27]: 1
How can I return 3? A while loop would work but it seems like there should be a function for this.
i = 0
match = False
while (not match) and (i < len(c)):
if id(c[i]) == id(b):
print i
match = True
i += 1

list.index() matches values by equality, not identity.
It is easy enough to write a helper function that uses a loop, testing with the is operator and the enumerate() function:
def index_by_identity(lst, target):
for i, obj in enumerate(lst):
if obj is target:
return i
# no object matches
raise IndexError(target)
or, alternatively with the next() function and a generator expression:
def index_by_identity(lst, target):
try:
return next(i for i, obj in enumerate(lst) if obj is target)
except StopIteration:
# no object matches
raise IndexError(target)
Demo:
>>> a = [3, 2]
>>> b = [3, 2]
>>> a is b
False
>>> c = [[4, 3], a, [5, 7], b, [6, 3]]
>>> def index_by_identity(lst, target):
... for i, obj in enumerate(lst):
... if obj is target:
... return i
... # no object matches
... raise IndexError(target)
...
>>> index_by_identity(c, b)
3
>>> def index_by_identity(lst, target):
... try:
... return next(i for i, obj in enumerate(lst) if obj is target)
... except StopIteration:
... # no object matches
... raise IndexError(target)
...
>>> index_by_identity(c, b)
3

Related

lambdify sympy with Variable-Length Arguments

I have a list of expr with a different number of variables as follows:
G = [[-x_1 + y_1], [-x_2 + y_1 + y_2], [y_1 + y_2] , [-y_3] , ....]
I want to evaluate these expressions using sympy and lambdify. For this purpose, I have written the following instructions:
def f(item):
return lamndify(list(tem.free_symbols), item)
for item in G:
f_ = f(item)
...
Now, what should I do to call f_ because there are different variables in each term?
Get the pertinent free_symbols, too. Perhaps make your function return those for you or else recapture them:
def f(item):
return lambdify(list(item.free_symbols), item)
def f2(item):
args = list(item.free_symbols)
return args, lambdify(args, item)
for [gi] in G:
args = list(gi.free_symbols)
f_ = f(gi)
a, f2_ = f2(gi)
assert f_(*args) == f2_(*a)
If you have a set of values you want to use like vals = {x_1:1,x_2:2,y_1:3,y_2:4,y_3:5} then something like this could be used:
>>> for [gi] in G:
... a, f2_ = f2(gi)
... v = [vals[i] for i in a]
... print(v, f2_(*v))
...
[3, 1] 2
[3, 2, 4] 5
[3, 4] 7
[5] -5

iterating over list with different elements

I have three types of element: a dict, a list with one dict, and a list with multiple dicts.
a = {"foo":1}
b = [{"foo":2}]
c = [{"foo":3}, {"foo":4}]
list = [a, b, c]
I want to be print all four values. The only ways I came up with were either checking type() of each element of list, or using try and except, like so:
for i in list:
try: # i is a dict
print(i["foo"])
except: # i a list
for e in i:
print(e["foo"])
Is there any better way to do it?
You can use isinstance
Ex:
a = {"foo":1}
b = [{"foo":2}]
c = [{"foo":3}, {"foo":4}]
l = [a, b, c]
for i in l:
if isinstance(i, dict): #Check if dict object
print(i["foo"])
elif isinstance(i, list): #Check if list object
for j in i:
print(j["foo"])
Output:
1
2
3
4
You can check the datatype explicitly:
mylist = [a, b, c]
for i in mylist:
if isinstance(i,dict):
print(i["foo"])
elif isinstance(i,list):
for e in i:
print(e["foo"])
But this won't work if you call your variable list. This snippet illustrates why you should not do that.
If you want to do it for more nested elements like a list in a dict in a list you can create a function for every possible datatype.
class Printer():
def print_element(self, element):
if isinstance(element, dict):
self._print_dict(element)
elif isinstance(element, list):
self._print_list(element)
else:
print(element)
def _print_dict(self, element):
for key,value in element.items():
self.print_element(value)
def _print_list(self, element):
for value in element:
self.print_element(value)
a = {"foo":1}
b = [{"foo":2}]
c = [{"foo":3}, {"foo":4}, [1, {"foo":[9,8,7,6]}]]
Printer().print_element(c)
Output:
3
4
1
9
8
7
6
You can use a recursive generator with your try / except idea:
a = {"foo":1}
b = [{"foo":2}]
c = [{"foo":3}, {"foo":4}]
L = [a, b, c]
def get_values(x):
for i in x:
try:
yield i['foo']
except TypeError:
yield from get_values(i)
res = list(get_values(L)) # [1, 2, 3, 4]
Alternatively, using `isinstance:
def get_values(x):
for i in x:
if isinstance(i, dict):
yield i['foo']
else:
yield from get_values(i)
Note you should never name variables after built-ins, e.g. use list_ or L instead of list.

search an array element in other 2d list and count sublists in python

I'm new in python,
I have a list like : A=['a','b','c']
and a list like B=[['a','c'],['a','c'],['b','b']]
i want to have a list like C=[2,1,2]
C stores occurrence of sublists that each element of A comes in B
that means 'a' is in 2 sublists
'b' is in 1 sublist
and 'c' is in 2 sublists,
how can I achieve this?
thanks
You can use sum:
a=['a','b','c']
b=[['a','c'],['a','c'],['b','b']]
final_list = [sum(i in c for c in b) for i in a]
Output:
[2, 1, 2]
You can loop over b and update a collections.Counter for each sublist, using set to remove duplicates:
from collections import Counter
a = ['a','b','c']
b = [['a','c'],['a','c'],['b','b']]
counter = Counter()
for sublist in b:
counter.update(set(sublist))
c = [counter[x] for x in a]
# result: [2, 1, 2]
You can loop and compare in both lists
a=['a','b','c']
b=[['a','c'],['a','c'],['b','b']]
result = []
for letter in a:
count = 0
for l in b:
if letter in l:
count += 1
result.append(count)
You can try dict approach :
A=['a','b','c']
B=[['a','c'],['a','c'],['b','b']]
d={}
for i in A:
for j in B:
if i in j:
if i not in d:
d[i]=1
else:
d[i]+=1
print(d)
output:
{'c': 2, 'b': 1, 'a': 2}
You can use a list comprehension with sum to construct C.
C = [sum(elem in sub for sub in B) for elem in A]
This has the same effect as using nested for loops:
C = []
for elem in A:
sum = 0
for sub in B:
sum += elem in sub
C.append(sum)
Here is a solution with collections.defaultdict.
from collections import defaultdict
a = ['a','b','c']
b = [['a','c'],['a','c'],['b','b']]
# initialise defaultdict
d = defaultdict(int)
# convert to sets for performance
a_set = set(a)
b_sets = list(map(set, b))
# loop through list of sets
for item in b_sets:
for i in item & a_set:
d[i] += 1
# retrieve counts in correct order
res = list(map(d.get, a))
print(res)
# [2, 1, 2]
Performance note
This may not matter, but the performance differential is interesting as it shows clearly the Counter overhead (4x slower).
from collections import defaultdict, Counter
a = ['a','b','c']
b = [['a','c'],['a','c'],['b','b']]
b = b*100000
def dd(a, b):
d = defaultdict(int)
a_set = set(a)
b_sets = list(map(set, b))
for item in b_sets:
for i in item & a_set:
d[i] += 1
return list(map(d.get, a))
def counter(a, b):
counter = Counter()
for sublist in b:
counter.update(set(sublist))
return [counter[x] for x in a]
assert dd(a, b) == counter(a, b)
%timeit dd(a, b) # 414 ms
%timeit counter(a, b) # 1.65 s

Max index of repeating elements divisible by 'n' in a List in Python

I have a sorted list of numbers like:
a = [77,98,99,100,101,102,198,199,200,200,278,299,300,300,300]
I need to find the max index of each values which is divisible by 100.
Output should be like: 4,10,15
My Code:
a = [77,98,99,100,101,102,198,199,200,200,278,299,300,300,300]
idx = 1
for i in (a):
if i%100 == 0:
print idx
idx = idx+1
Output of above code:
4
9
10
13
14
15
In case people are curious, I benchmarked the dict comprehension technique against the backward iteration technique. Dict comprehension is about twice the speed. Changing to OrderedDict resulted in MASSIVE slowdown. About 15x slower than the dict comprehension.
def test1():
a = [77,98,99,100,101,102,198,199,200,200,278,299,300,300,300]
max_index = {}
for i, item in enumerate(a[::-1]):
if item not in max_index:
max_index[item] = len(a) - (i + 1)
return max_index
def test2():
a = [77,98,99,100,101,102,198,199,200,200,278,299,300,300,300]
return {item: index for index, item in enumerate(a, 1)}
def test3():
a = [77,98,99,100,101,102,198,199,200,200,278,299,300,300,300]
OrderedDict((item, index) for index, item in enumerate(a, 1))
if __name__ == "__main__":
import timeit
print(timeit.timeit("test1()", setup="from __main__ import test1"))
print(timeit.timeit("test2()", setup="from __main__ import test2"))
print(timeit.timeit("test3()", setup="from __main__ import test3; from collections import OrderedDict"))
3.40622282028
1.97545695305
26.347012043
Use a simple dict-comprehension or OrderedDict with divisible items as the keys, old values will be replaced by newest values automatically.
>>> {item: index for index, item in enumerate(lst, 1) if not item % 100}.values()
dict_values([4, 10, 15])
# if order matters
>>> from collections import OrderedDict
>>> OrderedDict((item, index) for index, item in enumerate(lst, 1) if not item % 100).values()
odict_values([4, 10, 15])
Another way will be to loop over reversed list and use a set to keep track of items seen so far(lst[::-1] may be slightly faster than reversed(lst) for tiny lists).
>>> seen = set()
>>> [len(lst) - index for index, item in enumerate(reversed(lst))
if not item % 100 and item not in seen and not seen.add(item)][::-1]
[4, 10, 15]
You can see the sort-of equivalent code of the above here.
You could use itertools.groupby since your data is sorted:
>>> a = [77,98,99,100,101,102,198,199,200,200,278,299,300,300,300]
>>> from itertools import groupby
>>> [list(g)[-1][0] for k,g in groupby(enumerate(a), lambda t: (t[1] % 100, t[1])) if k[0] == 0]
[3, 9, 14]
Although this is a little cryptic.
Here's a complicated approach using only a list-iterator and accumulating into a list:
>>> run, prev, idx = False, None, []
>>> for i, e in enumerate(a):
... if not (e % 100 == 0):
... if not run:
... prev = e
... continue
... idx.append(i - 1)
... run = False
... else:
... if prev != e and run:
... idx.append(i - 1)
... run = True
... prev = e
...
>>> if run:
... idx.append(i)
...
>>> idx
[3, 9, 14]
I think this is best dealt with a dictionary approach like #AshwiniChaudhary It is more straightforward, and much faster:
>>> timeit.timeit("{item: index for index, item in enumerate(a, 1)}", "from __main__ import a")
1.842843743012054
>>> timeit.timeit("[list(g)[-1][0] for k,g in groupby(enumerate(a), lambda t: (t[1] % 100, t[1])) if k[0] == 0]", "from __main__ import a, groupby")
8.479677081981208
The groupby approach is pretty slow, note, the complicated approach is faster, and not far-off form the dict-comprehension approach:
>>> def complicated(a):
... run, prev, idx = False, None, []
... for i, e in enumerate(a):
... if not (e % 100 == 0):
... if not run:
... prev = e
... continue
... idx.append(i - 1)
... run = False
... else:
... if prev != e and run:
... idx.append(i - 1)
... run = True
... prev = e
... if run:
... idx.append(i)
... return idx
...
>>> timeit.timeit("complicated(a)", "from __main__ import a, complicated")
2.6667005629860796
Edit Note, the performance difference narrows if we call list on the dict-comprehension .values():
>>> timeit.timeit("list({item: index for index, item in enumerate(a, 1)}.values())", "from __main__ import a")
2.3839886570058297
>>> timeit.timeit("complicated(a)", "from __main__ import a, complicated")
2.708565960987471
it seemed like a good idea at the start, got a bit twisty, had to patch a couple of cases...
a = [0,77,98,99,100,101,102,198,199,200,200,278,299,300,300,300, 459, 700,700]
bz = [*zip(*((i, d//100) for i, d in enumerate(a) if d%100 == 0 and d != 0))]
[a for a, b, c in zip(*bz, bz[1][1:]) if c-b != 0] + [bz[0][-1]]
Out[78]: [4, 10, 15, 18]
enumerate, zip to create bz which mates 100's numerator(s) with indices
bz = [*zip(*((i, d//100) for i, d in enumerate(a) if d%100 == 0 and d != 0))]
print(*bz, sep='\n')
(4, 9, 10, 13, 14, 15, 17, 18)
(1, 2, 2, 3, 3, 3, 7, 7)
then zip again, zip(*bz, bz[1][1:]) lagging the numerator tuple to allow the lagged difference to give a selection logic if c-b != 0for the last index of each run but the last
add the last 100's match because its always the end of the last run + [bz[0][-1]]

How to find the number of lists in an input? (python)

def lists(A: list) -> int:
'''Return the total number of lists in A (including A itself).
Each element of A and any nested lists are either ints or other lists.
Example:
>>> lists([1, 2, 3])
1
>>> lists([[1], [2], [3]])
4
>>> lists([[[1, 2], [], 3]])
4
'''
Does anyone know how to do this?
All I have is
for i in range(0, len(A)):
if (isinstance(A[i], list)):
count=count+1
return(lists(A[i]))
else:
B=A[i:]
return(count)
Here is a 'dirty' but easy way to do it
def lists(l):
'''
Return the total number of lists in A (including A itself).
Each element of A and any nested lists are either ints or other lists.
'''
# convert the list to string and count the ['s
# each [ is the start of a list, so the number of ['s equals
# the number of lists
nr_of_lists = str(l).count('[')
# return the number of sublists
return nr_of_lists
No recursion required
Here's one way to write it:
def numlists(lst, num = 1):
for item in lst:
if isinstance(item, list):
num += numlists(item)
return num
Sample outputs:
print(numlists([1, 2, 3])) # 1
print(numlists([[1], [2], [3]])) # 4
print(numlists([[[1, 2], [], 3]])) # 4
print(numlists([[1,[2,3,[4,5,[6]]]],7,8,[9]])) # 6
You should do this with recursion:
def count_list(a):
result = 0
if isinstance(a, list):
result += 1
try:
for b in a:
result += count_list(b)
except:
pass
return result
def lists(a):
if not isinstance(a, list):
return 0
s = 1
for x in a:
s += lists(x)
return s
print lists([])
print lists([1,2,3])
print lists([[1], [2], [3]])
print lists([[[1, 2], [], 3]])
def lists(A):
return 1 + sum(lists(e) if isinstance(e, list) else 0 for e in A)

Categories