Python: slice a matrix horizontally - python

Let's consider those two variables :
matrix = [[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]]
list_of_lengths = [2, 1, 1]
I am looking for a method to get this result :
result = [[[1, 2],
[5, 6],
[9, 10]], [[3],
[7],
[11]], [[4],
[8],
[12]]]
Indeed, I want three matrix as a result. The j-length of each one is determined by the variable list_of_lentghts.
I have already written a solution that works, but I would like to know if there is a solution more simple. Here is my solution:
def cut_matrix(matrix, list_of_lengths):
result = []
for a_len in list_of_lengths:
current_sub_matrix = []
for a_line in matrix:
current_new_line = []
for i in range(0, a_len):
current_new_line.append(a_line.pop(0))
current_sub_matrix.append(current_new_line)
result.append(current_sub_matrix)
return result

If you know the column offset i and the number of columns n, you can use slicing to obtain the columns:
[row[i:i+n] for row in matrix]
to get the slice. So you can simply use:
def cut_matrix(matrix, list_of_lengths):
result = []
col = 0
for a_len in list_of_lengths:
result.append([row[col:col+a_len] for row in matrix])
col += a_len
return result
It generates:
>>> cut_matrix(matrix,list_of_lengths)
[[[1, 2], [5, 6], [9, 10]], [[3], [7], [11]], [[4], [8], [12]]]
This will also work faster than using .pop(0) since popping from the front is done in O(n) (so popping all elements requires O(n2) whereas slicing all elements is done in O(n)). Finally it leaves the original matrix intact which is thus more declarative.

Related

How to efficiently find the common part between two lists

There are two lists, which respectively represent two results of the clustering algorithm, such as com1 = [[1,2,3,4], [5, 6, 7, 8], [9]], where com1 represents a clustering result, [1,2,3,4] represents nodes 1,2,3,4 belong to the same class. [5,6,7,8] indicates that nodes 5,6,7,8 belong to the same class, and 9 belongs to a separate class. com2 = [[1, 2, 4], [3], [5, 6, 7, 8], [9]]. Now, I need to find out the common parts between com1 and com2, such as [1,2,4],[5,6,7,8],[9].
Is there an efficient way to solve this problem?
Assuming that a given value can only occur in one sublist in com1 and in one sublist in com2, we can observe the following:
Two values will belong to the same sublist in the result when they belong to the same sublist in com1 and also in the same sublist in com2.
So we could collect for each value the two indices of the sublists they belong to: one index that identifies the sublist in com1, and another that identifies the sublist in com2.
We can use those pairs as keys that uniquely identify a target sublist, and populate those sublists accordinly:
from collections import defaultdict
def combine(com1, com2):
d = defaultdict(list)
for com in com1, com2:
for i, lst in enumerate(com):
for val in lst:
d[val].append(i)
res = defaultdict(list)
for val, key in d.items():
res[tuple(key)].append(val)
return list(res.values())
# Example 1
com1 = [[1,2,3,4], [5, 6, 7, 8], [9]]
com2 = [[1, 2, 4], [3], [5, 6, 7, 8], [9]]
print(combine(com1, com2)) # [[1, 2, 4], [3], [5, 6, 7, 8], [9]]
# Example 2
com1 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
com2 = [[1, 9], [2, 3, 4], [5, 6, 7, 8], [9]]
print(combine(com1, com2)) # [[1], [2, 3], [4], [5, 6], [7, 8], [9]]
If we assume an amortised constant time complexity for dictionary get/set actions, then this brings the total time complexity to O(𝑛) where 𝑛 represents the number of values in the list that gets partitioned.

python) how to divide a python 1D list to constant number of random size python lists(2D)

I have a 1D list and I want to divide it into 4 "chunks" with each "chunk" being a random size within a certain range.
example
a_list = [[2, 0], [7], [4, 3], [1, 5, 6]]
I want to change this 2D list into a 1D list and then regroup a random number of values(range 0 to length of 1D list) inside the 1D list into 4 groups.
To flatten the list you can use itertools.chain. Then you have to have to find some random indexes where you can split the list. You can use random.sample for that.
from itertools import chain
import random
a_list = [[2, 0], [7], [4, 3], [1, 5, 6]]
number_of_chunks = 4
# flatten the list
data = list(chain.from_iterable(a_list))
print(data)
# get indexes where to split the list
indexes = sorted(random.sample(list(range(1, len(data))), number_of chunks-1))
print(indexes)
# add start end end of the list indexes
indexes = [0] + indexes + [None]
print(indexes)
# with zip we get pairs of start and end points for the chunks
# and then create the chunks in a list comprehension
result = [data[s:e] for s, e in zip(indexes, indexes[1:])]
print(result)
Due to the random part you'll get answers like [[2, 0], [7], [4, 3, 1], [5, 6]], [[2, 0, 7], [4, 3], [1, 5], [6]] or [[2], [0, 7], [4, 3, 1, 5], [6]].
To answer the questions from the comment:
Yes, random.sample is uniform.
If you want to have possible duplicate split points - leading to an empty sublist - you have to use an other approach than using sample. Create your indexes with
indexes = sorted([random.randint(1, len(data) - 1) for _ in range(number_of_chunks - 1)])
And don't forget to add a 0 at the beginning and None at the end of this index list.
This could produce results like [[2, 0, 7, 4, 3, 1, 5], [], [], [6]]

Slicing a matrix by an index matrix in TensorFlow

I have an n-by-m matrix X and an n-by-r index matrix I. I am wondering what are the relevant TensorFlow operators that allow me to get an n-by-r matrix R such that R[i,j] = X[i,I[i,j]]. As an example, let's say
X = tf.constant([[1,2,3],
[4,5,6],
[7,8,9]])
I = tf.constant([[1,2],
[1,0],
[0,2]])
The desired result would be a tensor
R = [[2, 3],
[5, 4],
[7, 9]]
I tried to use each column of the matrix I as the index and do tf.diag_part(tf.gather(X', index)), which seems to give me one column of R if I has the same number of row as X. For example,
idx = tf.transpose(I)[0] #[1,1,0]
res = tf.diag_part(tf.gather(tf.transpose(X), idx))
# res will be [2,5,7], i,e, first colum of R
Another attempt:
res = tf.transpose(tf.gather(tf.transpose(X), I),[0,2,1])
print(res.eval())
array([[[2, 3],
[5, 6],
[8, 9]],
[[2, 1],
[5, 4],
[8, 7]],
[[3, 1],
[6, 4],
[7, 9]]], dtype=int32)
From here i just need to be able to select the "diagonal entries" res[0,0], res[1,1] and res[2,2] to get R. I get stuck here though...
Use tf.gather with batch_dims argument:
res = tf.gather(X, I, batch_dims=1)

Using index from a list to get another value/element in another list

I have project wherein I have to get the index of certain element in a list, then use that index to get another value in another list.
For example,
j_set = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
on_going = [1]
e_list = [[], [1], [1], [2], [3], [3], [5], [4, 7], [6], [8, 9], [10]]
So far, the code looks like this:
if isinstance(on_going, int):
on_going = [on_going]
idx = [y for y, x in enumerate(e_list) if x in on_going] # code to get index in e_list
print(idx)
for i in idx:
q_active = j_set.append(i)
print(q_active)
The objective is to get the corresponding index of value/element in on_going from e_list. Then, use that index to get corresponding activity from j_set and store in q_active.
Expected output:
q_active = [2, 3]
The problem is, with the code above, I am getting an output for storing values in q_active as:
[1, 2] #idx output
None
None
Any help would be appreciated! Thanks!
Perhaps use a list_comprehension:
print([j_set[item] for i in on_going for item in range(len(e_list)) if i in e_list[item]])
#[2, 3]
You can use enumerate to get index and data from list:
e_list = [[], [1], [1], [2], [3], [3], [5], [4, 7], [6], [8, 9], [10]]
for data_index, data_val in enumerate(e_list):
print(data_index, data_val)
# write business logic here

Entering value in sublist if empty

So I have a larger for loop which produces nested lists of various of sizes: e.g.
[[6], [3], [5], [3, 2, 5, 3], [5], [6, 5, 4], [5, 3, 2]]
[[5], [], [], [4], [3]]
[[5], [2]]
[[], [4], [3, 2, 4]
In short, I would like it so that each array that has an empty sublist to simply be the value 0. So like :
If the list generated is :
[[6], [3], [5], [3, 2, 5, 3], [5], [6, 5, 4], [5, 3, 2]]
Then I would keep it that way.
But if the list generated is:[[5], [], [], [4], [3]]
Then I would like it to be:
[[5], [0], [0], [4], [3]]
Likewise, for the final row above, I would want it to be:[[0], [4], [3, 2, 4]
I have been trying something along the lines of :
for k in range(0,len(TempAisle)):
if TempAisle[k][0] == None:
TempAisle[k][0]= 0
k+=1
But I am getting an list index out of range error.
It seems to be a rather simple problem and have seen others who have asked ways to check if there is a sublist that is empty but I am having trouble replacing it with another value (0 in my case). Any help and explanation will be appreciated.
You can iterate on the items directly - the more Pythonic way - no need to use range. Test for the truthiness of each of the items (empty lists are falsy), and append a zero only if the sublist is empty:
for x in TempAisle:
if not x:
x.append(0)
For a one-line solution:
x = [elem or [0] for elem in TempAisle]
An EDIT based on the comment of #Moses Koledoye, to avoid creating a new list, but to keep it as a one-line solution:
[x.append(0) for x in TempAisle if not x]

Categories