Appending correctly with ranges python - python

This is part of a code for transposing matrixes, the function takes one argument, a list of lists. Let's suppose if input is:
[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
Basically, the output should return
[[1, 4, 7],
[2, 5, 8],
[3, 6, 9]]
And so on for any matrix.
This following code returns
def matrix(data)
column = len(data)
transposed[0].append(data[0][0])
transposed[0].append(data[1][0])
transposed[0].append(data[2][0])
outputs [1,4,7]
However, when I try to make it follow colum length it returns instead
transposed[0].append(data[0:column][0])
outputs [1,2,3]
What is wrong with my code?

You can transpose a matrix using the built-in function zip:
def transpose(m):
return zip(*m)
From the docs:
This function returns a list of tuples, where the i-th tuple contains
the i-th element from each of the argument sequences or iterables. The
returned list is truncated in length to the length of the shortest
argument sequence. When there are multiple arguments which are all of
the same length, zip() is similar to map() with an initial argument of
None. With a single sequence argument, it returns a list of 1-tuples.
With no arguments, it returns an empty list.
The left-to-right evaluation order of the iterables is guaranteed.
This makes possible an idiom for clustering a data series into
n-length groups using zip(*[iter(s)]*n).
To make this return a list of lists instead of a list of tuples, return the following list comprehension:
[list(r) for r in zip(*m)]
Here's how to do it using append:
def transpose(m):
transposed = [[] for _ in range(len(m[0]))]
for i in range(len(m)):
for j in range(len(m[0])):
transposed[j].append(m[i][j])
return transposed
As you can see, using zip is much easier. :)

Related

Given multiple lists, how to find the values between neighboring lists?

I have multiple lists of increasing numbers. The elements in each list are strictly greater than those in its previous neighbor. i.e. [1,2,3], [6,7,8], [10,11,12].
How to find the numbers between neighboring lists? In this case, the results would be [4,5], [9].
If there are only two lists, I can use something like
a = [1,2,3]
b = [4,5,6]
result = list(range(a[-1]+1,b[0]))
but I can't think of a simple and fast way to construct a loop to do this if I have more than two lists.
Assuming that the lists are sorted and that the elements in each list are strictly increasing you can use a loop to iterate over the indices of the lists and uses the range() function to generate a list of numbers between the last element of the current list and the first element of the next list. The results are appended to the results array:
def find_gaps(lists):
gaps = []
for i in range(len(lists) - 1):
gap = list(range(lists[i][-1] + 1, lists[i + 1][0]))
gaps.append(gap)
return gaps
lists = [[1, 2, 3], [6, 7, 8], [10, 11, 12]]
print(find_gaps(lists))
The resulting list of arrays will contain the numbers between neighboring lists: [[4, 5], [9]]. The complexity of the find_gaps() function is O(n), where n is the number of lists.
You can try the following:
a,b,c=[1,2,3], [6,7,8], [10,11,12]
c=[a,b,c]
result=[]
for i in range(len(c)-1):
result.append(list(range(c[i][-1]+1,c[i+1][0])))
print(result)

How to change the index in a list of lists

I would like to change the way a list of lists in indexed.
Suppose my initial list is two lists of one list and two lists of three elements. For example:
L = [[[1, 2, 3]], [[4, 5, 6], [7, 8, 9]]]
Then let say I want to take '4' in L, I must do
L[1][0][0].
Now I'd like to create a new list such that the last indexing become the first one
Lnew = [[[1], [4, 7]], [[2], [5, 8]], [[3], [6, 9]]]
And then for taking '4' I have to do:
Lnew[0][1][0]
In a more general case, I'd like to create the list Lnew defined by:
Lnew[i][k][l] = L[k][l][i]
Is there a way to do this kind of permutation of the index without doing the following loops:
Lnew = []
for i in range(len(Payment_dates)):
L1 = []
for k in range(N+1):
L2 = []
for l in range(k+1):
L2.append(L[k][l][i])
L1.append(L2)
Lnew.append(L1)
Which is not very optimal in term of complexity.
Thanks
What you'd like to achieve, presupposes that all sublists have the same length.
If they have differing lengths, you may wish to append zeros to all sublists until they have the length of the longest sublist or (which is easier) until infinity.
The same behaviour can be achieved by using a function to access the elements of the list. You can call this function during runtime every time you need an element of the list:
def getElement(myList, i, k, l):
if k < myList.length and l < myList[k].length and i < myList[k][l].length:
return myList[k][l][i]
else:
return None # or zero, or whatever you prefer
Depending on your code structure, you might not need this as a function and you can just put the conditions inside of your code.
You can also nest the if-conditions and throw different errors or return different values depending on what level the element does not exist.
If we neglect the time complexity of outputting a multidimensional list's element, this approach should decrease your time complexity from O(n^3) to O(1).

How to reverse the elements in a sublist?

I'm trying to create a function that reverses the order of the elements in a list, and also reverses the elements in a sublist. for example:
For example, if L = [[1, 2], [3, 4], [5, 6, 7]] then deep_reverse(L) mutates L to be [[7, 6, 5], [4, 3], [2, 1]]
I figured out how to reverse the order of one list, but I am having troubles with reversing the order of elements in a sublist. This is what I have so far:
def deep_reverse(L)
"""
assumes L is a list of lists whose elements are ints
Mutates L such that it reverses its elements and also
reverses the order of the int elements in every element of L.
It does not return anything.
"""
for i in reversed(L):
print(i)
In the example above, my code would just print [5,6,7], [3,4], [1,2], which is not what i'm trying to accomplish. It's just reversing the order of the lists, the not actual elements in the lists.
What should I add to the code so that it also reverses the order of the elements in a sublist?
[EDIT: my code needs to mutate the list; I don't want it just to print it, it actually needs to change the list.]
[sublist[::-1] for sublist in to_reverse[::-1]]
List comprehension works here. [::-1] is basically the same as reversed, but does not modify the list.
EDIT:
As pointed out below, reversed doesn't modify the list. It returns a listreverseiterator object
More Edit:
If you want a solution for lists of arbitrary depth, try:
def deep_reverse(to_reverse):
if isinstance(to_reverse, list):
return list(map(deep_reverse, to_reverse[::-1]))
else:
return to_reverse
Even more Edit:
To mutate a list in a function:
L[:] = new_list
Will modify the list in place.
I'm trying to create a function that reverses the order of the elements in a list, and also reverses the elements in a sublist.
Then do exactly those two things:
L.reverse()
for sublist in L:
sublist.reverse()
Full demo because you seem to be confused about what your function is supposed to do and how to test it:
>>> def deep_reverse(L):
"""
assumes L is a list of lists whose elements are ints
Mutates L such that it reverses its elements and also
reverses the order of the int elements in every element of L.
It does not return anything.
"""
L.reverse()
for sublist in L:
sublist.reverse()
>>> L = [[1, 2], [3, 4], [5, 6, 7]]
>>> deep_reverse(L)
>>> print(L)
[[7, 6, 5], [4, 3], [2, 1]]
Alternatively you use map() to achieve this as:
>>> map(lambda x: x[::-1], L[::-1]) # In Python 2.x
[[7, 6, 5], [4, 3], [2, 1]]
>>> list(map(lambda x: x[::-1], L[::-1])) # In Python 3.x
[[7, 6, 5], [4, 3], [2, 1]]
Check Blog on Lambda, filter, reduce and map to know how lambda functions and map() works in Python.
This should do the trick.
L = [[1, 2], [3, 4], [5, 6, 7]]
def deep_reverse(L):
for i in range(len(L)):
L[i]=L[i][::-1]
L=L[::-1]
return L
This looks very familiar :). I'm not going to give the whole working solution but here are some tips:
As you know, there are two steps, reverse each sub-list and then reverse the outer list (in place, without making a new list, so it will mutate the global L).
So you can loop through the outer list, and mutate each sub-list:
for i in range(len(L)):
# if L[i] is a list:
# reverse with [::-1] and update L[i] to the reversed version
# reverse the outer list L, list.reverse() will operate in-place on L
Now remember, if you loop through the list like this:
for item in list:
item = 'xxx'
You can't change item with the above code. item is a placeholder value, so changing it doesn't actually modify the list.
You instead need to index the item in L, and enumerate can help with this, or you can use the less-preffered range(len()) as above.
for i, item in enumerate(L):
# do something with L[i]
L[i] = 'something'
Edit: since there is so much confusion over this, I'll go ahead and post a working solution based on Stefan Pochmann's very elegant answer:
def deep_reverse(L):
L.reverse()
for sublist in L:
sublist.reverse()
Notice there is no return statement, and no print statement. This will correctly modify L in place. You cannot reassign L inside the function because then it will just create a new local version of L, and it will not modify the global L. You can use list.reverse() to modify L in place which is necessary based on the specifications.
with functional paradigm and defensive programming:
def deep_reverse(L):
""" assumes L is a list of lists whose elements are ints
Mutates L such that it reverses its elements and also
reverses the order of the int elements in every element of L.
It does not return anything.
"""
# Your code here
for i in L:
try:
deep_reverse(i)
except:
pass
L.reverse()
You could make this recursive, so it will work for arbitrarily deep nests.
something like (UNTESTED):
def deep_reverse(L)
"""
assumes L is a list of lists whose elements are ints
Mutates L such that it reverses its elements and also
reverses the order of the int elements in every element of L.
It does not return anything.
"""
for i in reversed(L):
if len(i) > 1:
deep_reverse(i)
else:
print(i)

Itertools Chain on Nested List

I have two lists combined sequentially to create a nested list with python's map and zip funcionality; however, I wish to recreate this with itertools.
Furthermore, I am trying to understand why itertools.chain is returning a flattened list when I insert two lists, but when I add a nested list it simply returns the nested list.
Any help on these two issues would be greatly appreciated.
from itertools import chain
a = [0,1,2,3]
b = [4,5,6,7]
#how can I produce this with itertools?
c = list(map(list, zip(a,b)))
print(c) #[[0, 4], [1, 5], [2, 6], [3, 7]]
d = list(chain(c))
print(d) #[[0, 4], [1, 5], [2, 6], [3, 7]]
d = list(chain(a,b))
print(d) #[0, 1, 2, 3, 4, 5, 6, 7]
I'll try to answer your questions as best I can.
First off, itertools.chain doesn't work the way you think it does. chain takes x number of iterables and iterates over them in sequence. When you call chain, it essentially (internally) packs the objects into a list:
chain("ABC", "DEF") # Internally creates ["ABC", "DEF"]
Inside the method, it accesses each of these items one at a time, and iterates through them:
for iter_item in arguments:
for item in iter_item:
yield item
So when you call chain([[a,b],[c,d,e],[f,g]]), it creates a list with one iterable object: the list you passed as an argument. So now it looks like this:
[ #outer
[ #inner
[a,b],
[c,d,e],
[f,g]
]
]
chain as such iterates over the inner list, and returns three elements: [a,b], [c,d,e], and [f,g] in order. Then they get repacked by list, giving you what you had in the first place.
Incidentally, there is a way to do what you want to: chain.from_iterable. This is an alternate constructor for chain which accepts a single iterable, such as your list, and pulls the elements out to iterate over. So instead of this:
# chain(l)
[ #outer
[ #inner
[a,b],
[c,d,e],
[f,g]
]
]
You get this:
# chain.from_iterable(l)
[
[a,b],
[c,d,e],
[f,g]
]
This will iterate through the three sub-lists, and return them in one sequence, so list(chain.from_iterable(l)) will return [a,b,c,d,e,f,g].
As for your second question: While I don't know why itertools is a necessity to this process, you can do this in Python 2.x:
list(itertools.izip(x,y))
However, in 3.x, the izip function has been removed. There is still zip_longest, which will match up as many pairs as it can, and accept a filler value for extra pairs: list(zip_longest([a,b,c],[d,e,f,g,h],fillvalue="N")) returns [(a,d),(b,e),(c,f),(N,g),(N,h)] since the second list is longer than the first. Normal zip will take the shortest iterable and cut off the rest.
In other words, unless you want zip_longest instead of zip, itertools does not have a built-in method for zipping.
You can also run itertools.chain(*your_list_of_lists). For example:
for p in itertools.chain(*[[1,2],[3,4]]):
print(p)
1
2
3
4

Creating a tuple with n entries for n lists

If I have a function that creates a random number of lists and I want the final result to return a tuple containing those lists - is it at all possible to do this?
Since tuples are immutable and the number of elements in a tuple cannot be changed once it has been created, then how can one be automatically generated which contains n entries for n lists?
Unlike lists, expanding the entries of a tuple can't simply be changed with a command like .append().
EDIT: It just dawned on me that I could create a tuple with a really large number of entries e.g. tuple = (a,b,c,d,e,f,g,h, ... to infinity) to cover n lists and then pass on the values of the tuple, which contain a list, to a new tuple with the exact number of n entries but this seems like a really inefficient method.
Create a list of lists, then pass that list to the built in tuple() function:
my_lists = [[1, 2], [5, 7], [8, 9]]
my_tuple = tuple(my_lists)
print(my_tuple)
Output
([1, 2], [5, 7], [8, 9])

Categories