Access non-sequential multiple entries in a list of lists - python-3.x

I have a matrix of M vectors where each vector is of size N (NxM).
I also have a Boolean vector of size L>=M, with exactly M entries = True.
I want to create a list of lists and place the M vectors where the Boolean vector is True in same order as they are in the matrix, and the rest I want to be empty lists
Example: M = 3, N = 4, L = 5
mat = np.array([[1, 5, 9],
[2, 6, 10],
[3, 7, 11],
[4, 8, 12]])
mask = [True, False, True, True, False]
I want to create the following:
res = [ [1, 2, 3, 4], [], [5, 6, 7, 8], [9, 10, 11, 12], []]
Accessing it can be done using:
data = [res[idx] for idx in range(len(res)) if mask(idx)]
However, creating it is a bit problematic.
I tried creating a list of empty lists, but I can't access all relevant entries at once.
Is there an elegant way of doing it?

Here is how I would do it:
mi = iter(mat.T.tolist())
[(m or []) and next(mi) for m in mask]
# [[1, 2, 3, 4], [], [5, 6, 7, 8], [9, 10, 11, 12], []]

As you are already using a list comprehension to get the data back from res, I would do a similar thing to create res in the first place.
mask_cs = np.cumsum(mask) - 1 # array([0, 0, 1, 2, 2]) , gives the corresponding index in mat
res = [mat[:, mask_cs[idx]].tolist() if mask[idx] else [] for idx in range(L)]
As alternativ, which accesses all columns of mat at once, on can create an intermediate array with size [N, L]
import numpy as np
res = np.zeros((N, L)) # Create result array
res[:, mask] = mat # Copy the data at the right positions
res = res.T.tolist() # Transform the array to a list of lists
for idx in range(L): # Replace the columns with empty lists, if mask[idx] is False
if not mask[idx]:
res[idx] = []

We could make use of np.split for some elegance, like so -
In [162]: split_cols = np.split(mat.T,np.cumsum(mask)[:-1])
In [163]: split_cols
Out[163]:
[array([[1, 2, 3, 4]]),
array([], shape=(0, 4), dtype=int64),
array([[5, 6, 7, 8]]),
array([[ 9, 10, 11, 12]]),
array([], shape=(0, 4), dtype=int64)]
So, that gives us a list of 2D arrays. For the desired output of list of lists, we need to map them to such -
In [164]: list(map(list,(map(np.ravel,split_cols))))
Out[164]: [[1, 2, 3, 4], [], [5, 6, 7, 8], [9, 10, 11, 12], []]
Alternatively, we can use lambda if that looks more elegant to some -
In [165]: F = lambda a: np.ravel(a).tolist()
In [166]: list(map(F,split_cols))
Out[166]: [[1, 2, 3, 4], [], [5, 6, 7, 8], [9, 10, 11, 12], []]

Related

How to append item to match the length of two list in Python

I am working on a Python script which is connected to a server. Every x min, server returns two list but the length of these list is not same. For ex:
a = [8, 10, 1, 34]
b = [4, 6, 8]
As you can see above that a is of length 4 and b is of length 3. Similarly, sometimes it returns
a = [3, 6, 4, 5]
b = [8, 3, 5, 2, 9, 3]
I have to write a logic where I have to check if length of these two list is not same, then add the 0 at the end of the list which is smaller than other list. So for ex, if input is:
a = [3, 6, 4, 5]
b = [8, 3, 5, 2, 9, 3]
then output will be:
a = [3, 6, 4, 5, 0, 0]
b = [8, 3, 5, 2, 9, 3]
What can I try to achieve this?
def pad(list1, list2):
# make copies of the existing lists so that original lists remain intact
list1_copy = list1.copy()
list2_copy = list2.copy()
len_list1 = len(list1_copy)
len_list2 = len(list2_copy)
# find the difference in the element count between the two lists
diff = abs(len_list1 - len_list2)
# add `diff` number of elements to the end of the list
if len_list1 < len_list2:
list1_copy += [0] * diff
elif len_list1 > len_list2:
list2_copy += [0] * diff
return list1_copy, list2_copy
a = [3, 6, 4, 5]
b = [8, 3, 5, 2, 9, 3]
# prints: ([3, 6, 4, 5, 0, 0], [8, 3, 5, 2, 9, 3])
print(pad(a, b))
a = [8, 10, 1, 34]
b = [4, 6, 8]
# prints: ([8, 10, 1, 34], [4, 6, 8, 0])
print(pad(a, b))
For now, I can suggest this solution:
a = [3, 6, 4, 5]
b = [8, 3, 5, 2, 9, 3]
# Gets the size of a and b.
sizeA, sizeB = len(a), len(b)
# Constructs the zeros...
zeros = [0 for _ in range(abs(sizeA-sizeB))]
# Determines whether a or b needs to be appended with 0,0,0,0...
if sizeA < sizeB:
a += zeros
else:
b += zeros
print(a,b)
You should use extend instead of append. This is the way to add a list to another list in Python. The list here is the list of zeros.
a = [3, 6, 4, 5, 9, 3]
b = [8, 3, 5, 2]
lenA, lenB = len(a), len(b)
diff=abs(len(a)-len(b))
if lenA < lenB:
a.extend([0]*diff)
else:
b.extend([0]*diff)
print(a)
print(b)
You could also try to use more_itertools padded() method:
It's prob. more elegant and adaptable for future Use cases.
Notes: just need to do pip install more_itertools first.
# simple example to demo it:
from more_itertools import padded
print(list(padded([1, 2, 3], 0, 5))) # last num: 5 is the numbers of 0 to be padded to make the total length to be 5. (needs 2 zeros)
# [1, 2, 3, 0, 0]
# more examples:
>>> L = [1, 2, 3]
>>> K = [3, 4, 5, 6, 8, 9]
>>> gap = len(K) - len(L)
# 3
# shorter list is L
>>>list(padded(L, 0, len(L) + gap))
[1, 2, 3, 0, 0, 0]

Check if element exists in Nth position in list of lists

Consider having this list:
ini_list = [[1, 2, 5, 10, 7],
[4, 3, 4, 3, 21],
[45, 65, 8, 8, 9]]
I need to be able to find if 4 exists in any of the 3rd elements of the list of lists. So in this example, I see it does exist on the 2nd list [4, 3, 4, 3, 21], on positions 1 and 3 so it will return true.
I can do this:
elem_to_find = 4
res1 = any(elem_to_find in sublist for sublist in ini_list)
But by doing it this way I'm searching through all the list of list elements not only the 3rd of each list as expected. What would be the right approach?
You can just do this:
res1 = any(elem_to_find == sublist[2] for sublist in ini_list if len(sublist) > 2)
This compares elem_to_find to sublist[2] instead of just sublist, i.e. the 3rd element instead of the whole list. It also makes sure the length of the sublist is greater than 2 (otherwise the 3rd element wouldn't exist).
Below is the entire code with 2 examples:
ini_list = [[1, 2, 5, 10, 7],
[4, 3, 4, 3, 21],
[45, 65, 8, 8, 9]]
elem_to_find = 4
res1 = any(elem_to_find == sublist[2] for sublist in ini_list if len(sublist) > 2)
print(res1)
# Prints True
ini_list = [[1, 2, 5, 10, 7],
[4, 3, 3, 3, 21], # No longer has 4 in 3rd position
[45, 65, 8, 8, 9]]
res1 = any(elem_to_find == sublist[2] for sublist in ini_list if len(sublist) > 2)
print(res1)
# Prints False

print values from a list in vertical order for many lists

I'm having trouble printing data from a list vertically , the list is as shown below
[[1, 4, 5], [4, 6, 8], [8, 3, 10]]
I want to print the data into a new list as follows:
[[1, 4, 8], [4, 6, 3], [5, 8, 10]]
I'm having trouble doing it when the lists get longer, as it is a nested list
If the data is only numbers / integers, then you might want to use numpy for this. It will be faster too.
import numpy
givenList = [[1, 4, 5], [4, 6, 8], [8, 3, 10]]
toNumpy = numpy.array(givenList) #convert to numpy array
toNumpy = toNumpy.T #transpose
toList = toNumpy.tolist() #convert back to list
print(toList)
# output : [[1, 4, 8], [4, 6, 3], [5, 8, 10]]
I think you are looking for zip.
l = [[1, 4, 5], [4, 6, 8], [8, 3, 10]]
z = zip(*l)
print('\n'.join(map(str, z)))
# Output is:
# (1, 4, 8)
# (4, 6, 3)
# (5, 8, 10)
It does produce tuples instead of lists, but that is usually easily dealt with, and if you are just iterating over them, then it probably doesn't matter.
l = [[1, 4, 5], [4, 6, 8], [8, 3, 10]]
z = map(list, zip(*l))
print('\n'.join(map(str, z)))
Will give you the same result, but will print them out as lists.

What does a list next to a list mean?

I am confused about what does lists[outer_index][inner_index] do? I thought that when two lists are next to each other, it means the first list is the selected list and the second list indicates the index of the first list. However, that doesn't seem to be the case here.
def flatten(lists):
results = []
for outer_index in range(len(lists)): # outer index = 0, 1
for inner_index in range(len(lists[outer_index])): # inner_index = [0, 1, 2, 0, 1, 2, 3, 4, 5]
results.append(lists[outer_index][inner_index])
return results
n = [[1, 2, 3], [4, 5, 6, 7, 8, 9]]
print(flatten(n))
You are creating an list of lists (basically a table).
n = [[1, 2, 3],
[4, 5, 6, 7, 8, 9]]
If I do n[0][1] I am saying go to row 0 and grab the element in column 1.
Its better to think of it this way.
n = [[1, 2, 3], [4, 5, 6, 7, 8, 9]]
s = n[0] # Now s = [1,2,3], the first element in n
s[1] = 2 # Because I just grabbed the second element in [1,2,3]
# This is the same as
n[0][1]

Python: Bug in writing a transpose function using lists

I have an assignment to do, which is:
Write a function transpose which takes in a matrix and transposes it. Basically, this converts a m x n matrix into a n x m matrix.
I wrote a code which seems sensible, but it doesnt get me the result I want. Can anyone point out what is wrong with my code?
def transpose(matrix):
new_matrix=[[]]*len(matrix[0])
for row in matrix:
i=0
for j in row:
new_matrix[i]+=[j]
i+=1
return new_matrix
Test case:
print(transpose([[ 1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]))
If you use the * to multiply some values in a list-initialisation, be careful. You might end up with references that point multiple times to the same value:
l = [ [] ]*3
print(l)
l[1].append(34) # change only "the first" list by appending smth
print(l)
Output:
[[], [], []]
[[34], [34], [34]] # they are all "the same data" reference
There is an built-in zip() that does exactly your transposing:
l = [[ 1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
l_t = [ list(x) for x in zip(*l)] # one-line solutions for transposing ;)
print(l)
print(l_t) # transposed
Zip has the limitation that it only works to the length of the smallest sublists - yours are all equal so all is fine.
Output:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
If you ever need a zip that takes the longest list, itertools.zip_longest(..) can be used, it takes a default param that is substituted for any shorter list-items that are not there.
Btw. just list(zip(l)) looks like this: [(1,5,9),(2,6,10),(3,7,11),(4,8,12)] - it create tuples over the same indexes of the parts of the iterable you put into it.
By hand:
l = [[ 1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
l2 = []
for colIdx in range(len(l[0])): # 0-3 iterate over the inner indexes first
newRow = []
for rowIdx in range(len(l)): # 0-2 then over the outer ones
newRow.append(l[rowIdx][colIdx])
l2.append(newRow)
print(l2) # [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
What i meant was something like this:
def t(array): #The original array has size mxn
duplicate = [[0 for x in range(len(array))] for y in range(len(array[1]))] #You create an array of size nxm, which is filled with zeros
for i in range(len(array)): #Loop over the rows
for j in range(len(array[i])): #Then loop over the columns
duplicate[j][i] = array[i][j] #Replace j,i or duplicate with i,j th element of original
return duplicate
Now,
>>> t([[ 1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

Resources