python - from matrix to dictionary in single line - python

matrix
is a list of lists with the same length. I've to return a dictionary of the form
{i:(l1[i],l2[i],...,lm[i])}
Where the key i is matched with a tuple the i'th elements
from each list.
Say
matrix=[[1,2,3,4],[9,8,7,6],[4,8,2,6]]
so the line:
>>> dict([(i,tuple(matrix[k][i] for k in xrange(len(matrix)))) for i in xrange(len(matrix[0]))])
does the job pretty well and outputs:
{0: (1, 9, 4), 1: (2, 8, 8), 2: (3, 7, 2), 3: (4, 6, 6)}
but fails if the matrix is empty: matrix=[]. The output should be: {}
How can i deal with this?

How about this instead:
>>> matrix = [[1,2,3,4],[9,8,7,6],[4,8,2,6]]
>>> dict(enumerate(zip(*matrix)))
{0: (1, 5, 4), 3: (4, 8, 6), 2: (3, 7, 2), 1: (2, 6, 8)}
>>> matrix = []
>>> dict(enumerate(zip(*matrix)))
{}

try changing part "len(matrix[0])"
This will try look up an index that doesn't exist if the matrix is empty.
instead make it
len(matrix[0]) if matrix else 0

If you want a simple solution (instead of knowing what's wrong with your one), I suggest you use a list instead of a dict. Seeing as a matrix is fairly static in terms of its size and keys.
This can be done simply by zip(*matrix)
>>> matrix = [[1,2,3,4],[9,8,7,6],[4,8,2,6]]
>>> dict_ = dict(enumerate(zip(*matrix)))
>>> list_ = zip(*matrix)
>>> for key_ in dict_:
... print dict_[key_] == list_[key_]
...
True
True
True
True

Related

Finding a pair of elements in a list that adds up to a sum using a dictionary

Context of the problem:
Find Pair with given Sum in the Array.
Given an unsorted list of ints, find a pair with a given sum in it.
EXAMPLE:
list = [8, 7, 2, 5, 3, 1]
sum = 10
OUTPUT = index 0 & 2 (8, 2) or 1 & 4 (7, 3)
This is what I have so far:
def find_pair_dict(ints: [int], sum_: int):
dict_ = dict()
# {
# element: index
# 8: 0,
# 7: 1,
# ...
# }
output = list()
for i in range(len(ints)):
diff = sum_ - ints[i]
# print(diff)
if diff not in dict_.keys():
# int: index
dict_[ints[i]] = i
else:
output.append((dict_[ints[i]], dict_[diff]))
if not output:
return "No pairs were found"
return output
I am calling this function with find_pair_dict([8, 7, 2, 5, 3, 1], 10) and am getting an error that I do not understand.
The Error
Traceback (most recent call last):
File "find_pair_sum.py", line 62, in <module>
print(find_pair_dict([8, 7, 2, 5, 3, 1], 10))
File "find_pair_sum.py", line 53, in find_pair_dict
output.append((dict_[ints[i]], dict_[diff]))
KeyError: 2
This sounds like the element of 2 cannot be added?
Almost a one-liner:
def find_pairs(ints: [int], sum_: int):
return {
tuple(sorted((n, ints.index(sum_-i)))): (i, sum_-i)
for n, i in enumerate(ints) if sum_ - i in ints
}
print(find_pairs([8, 7, 2, 5, 3, 1], 10))
Result:
{(0, 2): (2, 8), (1, 4): (3, 7), (3, 3): (5, 5)}
Note: the key to the dictionary is a sorted tuple. A tuple because a list isn't hashable and sorted to avoid both (0,2) and (2,0) showing up as keys (for example).
The error in your solution occurs because the first time this line is executed:
output.append((dict_[ints[i]], dict_[diff]))
The value if dict_ is {8: 0, 7: 1} and ints[i] is 2. Since there is no entry for 2 in the dict, you get this error.
Actually I think there should be three outputs:
(2,0) = 2,8
(4,1) = 3,7
(3,3) = 5,5
The problem has already been succinctly described by #Selcuk. I am suggesting you how to solve that. Many ways. Easiest for me is to use defaultdict from collections.
Below is the code with minimal changes
from collections import defaultdict
def find_pair_dict(ints: [int], sum_: int):
dict_ = defaultdict(list)
output = list()
for i in range(len(ints)):
diff = sum_ - ints[i]
dict_[ints[i]] = i
output.append((dict_[ints[i]], dict_[diff]))
if not output:
return "No pairs were found"
return output
print(find_pair_dict([8, 7, 2, 5, 3, 1], 10))
This prints out the following. I have not filtered for empty matches.
[(0, []), (1, []), (2, 0), (3, 3), (4, 1), (5, [])]
Is this what you wanted?

Is There A Universal Selector Option For if...in Clauses?

I have a "large" list of tuples:
thelist=[(1,2),(1,3),(2,3)]
I want to check whether any tuple in the list starts with a 1, and if it does, print "aaa":
for i in thelist:
templist.append((i[0],i))
for i in templist:
if i[0]==1:
print("aaa")
break
Which is rather ardurous as I have to create the templist. Is there any way I can do this:
if (1,_) in thelist:
print("aaa")
Where _ is the universal selector. Note that the list would be very large and thus it is very costly to implement another list.
There isn't, although you can just use any
any(i[0] == 1 for i in thelist) --> Returns true if the first element is 1
If you don’t actually need the actual tuple, like you do in your example, then you can actually use tuple unpacking for exactly that purpose:
>>> the_list = [(1, 2), (1, 3), (2, 3)]
>>> for x, y in the_list:
if x == 1:
print('aaa')
break
aaa
If you add a * in front of the y, you can also unpack tuples of different sizes, collecting the remainder of the tuple:
>>> other_list = [(1, 2, 3, 4, 5), (1, 3), (2, 3)]
>>> for x, *y in other_list:
if x == 1:
print(y)
break
[2, 3, 4, 5]
Otherwise, if you just want to filter your list based on some premise and then do something on those filtered items, you can use filter with a custom function:
>>> def startsWithOne(x):
return x[0] == 1
>>> thelist = [(1, 2), (1, 3), (2, 3)]
>>> for x in filter(starts_with_one, the_list):
print(x)
(1, 2)
(1, 3)
This is probably the most flexible way which also avoids creating a separate list in memory, as the elements are filtered lazily when you interate the list with your loop.
Finally, if you just want to figure out if any of your items starts with a 1, like you do in your example code, then you could just do it like this:
>>> if any(filter(starts_with_one, the_list)):
print('aaa')
aaa
But I assume that this was just an oversimplified example.

Why am I getting a KeyError after reading values from .txt file?

So I'm trying to change an already-working program to read from a .txt file, but I only see a KeyError.
#V = ([1,2,3,4,5])
#E = ([(1,2),(1,3),(2,3),(3,5),(5,4)])
import ast
with open('v.txt') as V:
A = ast.literal_eval(V.read())
with open('e.txt') as E:
B = ast.literal_eval(E.read())
print(A)
print(B)
indegree = {}
outdegree = {}
for x in A:
indegree[x] = 0
outdegree[x] = 0
for x,y in B:
outdegree[x] += 1
indegree[y] += 1
for x in A:
print("Outdegree for vertex", x,"=", outdegree[x])
print("Indegree for vertex", x,"=", indegree[x])
The output is:
[(1, 2, 3, 4, 5)]
[(1, 2), (1, 3), (2, 3), (3, 5), (5, 4)]
line 21, in
outdegree[x] += 1
KeyError: 1
When I run it with values from #V and #E and the beginning of the code, it runs completely fine.
It seems like the commented 'V' above has the shape
([1,2,3,4,5])
This gets read in and interpreted to the inner list, when I run it on my console, becoming:
[1,2,3,4,5]
However, notice in your print statements that instead, A is written as a list of a single tuple. The nesting is different.
[(1, 2, 3, 4, 5)]
When you iterate over the first dimension of A, you'll only instantiate the hashmap as follows:
{(1, 2, 3, 4, 5): 0}
My guess is the intended behavior is {1: 0, 2:0, ...}. I'd suggest that you use standard data formats (like tsv/csv) to simplify loading the data. Second, if you switch the order of brackets, you should get your intended answer

Zipping a set and a list in python

I'm trying to find duplicates in a list. I want to preserve the values and insert them into a tuple with their number of occurrences.
For example:
list_of_n = [2, 3, 5, 5, 5, 6, 2]
occurance_of_n = zip(set(list_of_n), [list_of_n.count(n) for n in set(list_of_n)])
[(2, 2), (3, 1), (5, 3), (6, 1)]
This works fine with small sets. My question is: as list_of_n gets larger, will I have to worry about arg1 and arg2 in zip(arg1, arg2) not lining up correctly if they're the same set?
I.e. Is there a conceivable future where I call zip() and it accidentally aligns index [0] of list_of_n in arg1 with some other index of list_of_n in arg2?
(in case it's not clear, I'm converting the list to a set for purposes of speed in arg2, and under the pretense that zip will behave better if they're the same in arg1)
Since your sample output preserves the order of appearance, you might want to go with a collections.OrderedDict to gather the counts:
list_of_n = [2, 3, 5, 5, 5, 6, 2]
d = OrderedDict()
for x in list_of_n:
d[x] = d.get(x, 0) + 1
occurance_of_n = list(d.items())
# [(2, 2), (3, 1), (5, 3), (6, 1)]
If order does not matter, the appropriate approach is using a collections.Counter:
occurance_of_n = list(Counter(list_of_n).items())
Note that both approach require only one iteration of the list. Your version could be amended to sth like:
occurance_of_n = list(set((n, list_of_n.count(n)) for n in set(list_of_n)))
# [(6, 1), (3, 1), (5, 3), (2, 2)]
but the repeated calls to list.count make an entire iteration of the initial list for each (unique) element.

Using Python's list index() method on a list of tuples or objects?

Python's list type has an index() method that takes one parameter and returns the index of the first item in the list matching the parameter. For instance:
>>> some_list = ["apple", "pear", "banana", "grape"]
>>> some_list.index("pear")
1
>>> some_list.index("grape")
3
Is there a graceful (idiomatic) way to extend this to lists of complex objects, like tuples? Ideally, I'd like to be able to do something like this:
>>> tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11)]
>>> some_list.getIndexOfTuple(1, 7)
1
>>> some_list.getIndexOfTuple(0, "kumquat")
2
getIndexOfTuple() is just a hypothetical method that accepts a sub-index and a value, and then returns the index of the list item with the given value at that sub-index. I hope
Is there some way to achieve that general result, using list comprehensions or lambas or something "in-line" like that? I think I could write my own class and method, but I don't want to reinvent the wheel if Python already has a way to do it.
How about this?
>>> tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11)]
>>> [x for x, y in enumerate(tuple_list) if y[1] == 7]
[1]
>>> [x for x, y in enumerate(tuple_list) if y[0] == 'kumquat']
[2]
As pointed out in the comments, this would get all matches. To just get the first one, you can do:
>>> [y[0] for y in tuple_list].index('kumquat')
2
There is a good discussion in the comments as to the speed difference between all the solutions posted. I may be a little biased but I would personally stick to a one-liner as the speed we're talking about is pretty insignificant versus creating functions and importing modules for this problem, but if you are planning on doing this to a very large amount of elements you might want to look at the other answers provided, as they are faster than what I provided.
Those list comprehensions are messy after a while.
I like this Pythonic approach:
from operator import itemgetter
tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11)]
def collect(l, index):
return map(itemgetter(index), l)
# And now you can write this:
collect(tuple_list,0).index("cherry") # = 1
collect(tuple_list,1).index("3") # = 2
If you need your code to be all super performant:
# Stops iterating through the list as soon as it finds the value
def getIndexOfTuple(l, index, value):
for pos,t in enumerate(l):
if t[index] == value:
return pos
# Matches behavior of list.index
raise ValueError("list.index(x): x not in list")
getIndexOfTuple(tuple_list, 0, "cherry") # = 1
One possibility is to use the itemgetter function from the operator module:
import operator
f = operator.itemgetter(0)
print map(f, tuple_list).index("cherry") # yields 1
The call to itemgetter returns a function that will do the equivalent of foo[0] for anything passed to it. Using map, you then apply that function to each tuple, extracting the info into a new list, on which you then call index as normal.
map(f, tuple_list)
is equivalent to:
[f(tuple_list[0]), f(tuple_list[1]), ...etc]
which in turn is equivalent to:
[tuple_list[0][0], tuple_list[1][0], tuple_list[2][0]]
which gives:
["pineapple", "cherry", ...etc]
You can do this with a list comprehension and index()
tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11)]
[x[0] for x in tuple_list].index("kumquat")
2
[x[1] for x in tuple_list].index(7)
1
Inspired by this question, I found this quite elegant:
>>> tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11)]
>>> next(i for i, t in enumerate(tuple_list) if t[1] == 7)
1
>>> next(i for i, t in enumerate(tuple_list) if t[0] == "kumquat")
2
I would place this as a comment to Triptych, but I can't comment yet due to lack of rating:
Using the enumerator method to match on sub-indices in a list of tuples.
e.g.
li = [(1,2,3,4), (11,22,33,44), (111,222,333,444), ('a','b','c','d'),
('aa','bb','cc','dd'), ('aaa','bbb','ccc','ddd')]
# want pos of item having [22,44] in positions 1 and 3:
def getIndexOfTupleWithIndices(li, indices, vals):
# if index is a tuple of subindices to match against:
for pos,k in enumerate(li):
match = True
for i in indices:
if k[i] != vals[i]:
match = False
break;
if (match):
return pos
# Matches behavior of list.index
raise ValueError("list.index(x): x not in list")
idx = [1,3]
vals = [22,44]
print getIndexOfTupleWithIndices(li,idx,vals) # = 1
idx = [0,1]
vals = ['a','b']
print getIndexOfTupleWithIndices(li,idx,vals) # = 3
idx = [2,1]
vals = ['cc','bb']
print getIndexOfTupleWithIndices(li,idx,vals) # = 4
ok, it might be a mistake in vals(j), the correction is:
def getIndex(li,indices,vals):
for pos,k in enumerate(lista):
match = True
for i in indices:
if k[i] != vals[indices.index(i)]:
match = False
break
if(match):
return pos
z = list(zip(*tuple_list))
z[1][z[0].index('persimon')]
tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11)]
def eachtuple(tupple, pos1, val):
for e in tupple:
if e == val:
return True
for e in tuple_list:
if eachtuple(e, 1, 7) is True:
print tuple_list.index(e)
for e in tuple_list:
if eachtuple(e, 0, "kumquat") is True:
print tuple_list.index(e)
Python's list.index(x) returns index of the first occurrence of x in the list. So we can pass objects returned by list compression to get their index.
>>> tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11)]
>>> [tuple_list.index(t) for t in tuple_list if t[1] == 7]
[1]
>>> [tuple_list.index(t) for t in tuple_list if t[0] == 'kumquat']
[2]
With the same line, we can also get the list of index in case there are multiple matched elements.
>>> tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11), ("banana", 7)]
>>> [tuple_list.index(t) for t in tuple_list if t[1] == 7]
[1, 4]
I guess the following is not the best way to do it (speed and elegance concerns) but well, it could help :
from collections import OrderedDict as od
t = [('pineapple', 5), ('cherry', 7), ('kumquat', 3), ('plum', 11)]
list(od(t).keys()).index('kumquat')
2
list(od(t).values()).index(7)
7
# bonus :
od(t)['kumquat']
3
list of tuples with 2 members can be converted to ordered dict directly, data structures are actually the same, so we can use dict method on the fly.
This is also possible using Lambda expressions:
l = [('rana', 1, 1), ('pato', 1, 1), ('perro', 1, 1)]
map(lambda x:x[0], l).index("pato") # returns 1
Edit to add examples:
l=[['rana', 1, 1], ['pato', 2, 1], ['perro', 1, 1], ['pato', 2, 2], ['pato', 2, 2]]
extract all items by condition:
filter(lambda x:x[0]=="pato", l) #[['pato', 2, 1], ['pato', 2, 2], ['pato', 2, 2]]
extract all items by condition with index:
>>> filter(lambda x:x[1][0]=="pato", enumerate(l))
[(1, ['pato', 2, 1]), (3, ['pato', 2, 2]), (4, ['pato', 2, 2])]
>>> map(lambda x:x[1],_)
[['pato', 2, 1], ['pato', 2, 2], ['pato', 2, 2]]
Note: The _ variable only works in the interactive interpreter. More generally, one must explicitly assign _, i.e. _=filter(lambda x:x[1][0]=="pato", enumerate(l)).
I came up with a quick and dirty approach using max and lambda.
>>> tuple_list = [("pineapple", 5), ("cherry", 7), ("kumquat", 3), ("plum", 11)]
>>> target = 7
>>> max(range(len(tuple_list)), key=lambda i: tuple_list[i][1] == target)
1
There is a caveat though that if the list does not contain the target, the returned index will be 0, which could be misleading.
>>> target = -1
>>> max(range(len(tuple_list)), key=lambda i: tuple_list[i][1] == target)
0

Categories