This question is an extension to this question.
I'm representing a two-dimensional array using list of lists, L, say:
[ [1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4] ]
For a given sub-list, say [9, 99], I want to replace a specific sub-list in the "2-D" list by this sublist using something intuitive like:
L[1][0:2] = sublist
# which updates `L` to:
[ [1, 2, 3, 4],
[1, 9, 99, 4],
[1, 2, 3, 4],
[1, 2, 3, 4] ] # not in this format, but written like this for clarity
This works for horizontal replacements, but not for vertical replacements since, as we can't slice to separate lists like this: L[0:2][0]. If I had to use this slicing system, I could transpose L (Transpose list of lists), then use this slicing method, then transpose it back. But that's not efficient, even for the sake of simplicity.
What would be an efficient way to replicate L[0:2][0] and get this output?
[ [1, 2, 3, 4],
[1, 9, 3, 4],
[1, 99, 3, 4],
[1, 2, 3, 4] ]
Note: Assume len(sublist) <= len(L), for vertical replacements (which is the focus of this question).
Looping approach:
def replaceVert(al : list, repl:list, oIdx:int, iIdx:int):
for pos in range(len(repl)):
al[oIdx+pos][iIdx] = repl[pos]
a = [ [1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16] ]
print(a) # [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
replaceVert(a,['ä','ü'],2,2) # this is a one liner ;)
print(a) # [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 'ä', 12], [13, 14, 'ü', 16]]
Transpose/slice/transpose approach:
I overread the mentioning of "no transposing". This is using transpose, change, transpose method with slicing which is not wanted by the Q. It is a answer for the title of this question, so I decided to leave it in for future people search SO and stumble over this Q:
a = [ [1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16] ]
b = list(map(list,zip(*a))) # will make [ [1,5,9,13], ... ,[4,8,12,16]]
b[1][0:2]=['a','b'] # replaces what you want here (using a and b for clarity)
c = list(map(list,zip(*b))) # inverts b back to a's form
print(a)
print(b)
print(c)
Output:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]] # a
[[1, 5, 9, 13], ['a', 'b', 10, 14], [3, 7, 11, 15], [4, 8, 12, 16]] # b replaced
[[1, 'a', 3, 4], [5, 'b', 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]] # c
Timing 4x4 list, 2 replaces:
setuptxt = """
def replaceVert(al : list, repl:list, oIdx:int, iIdx:int):
for pos in range(len(repl)):
al[oIdx+pos][iIdx] = repl[pos]
a = [ [1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16] ]
"""
zipp = """b = list(map(list,zip(*a)))
b[1][0:2]=['a','b']
c = list(map(list,zip(*b)))
"""
import timeit
print(timeit.timeit("replaceVert(a,['ä','ü'],2,2)",setup = setuptxt))
print(timeit.timeit(stmt=zipp, setup=setuptxt))
Output:
looping: 12.450226907037592
zipping: 7.50479947070815
The method wit ZIPPing (transpose/slice/transpose) needs roughly 60% of the time for 4x4 lists.
Bigger list 1000x1000 and ~70 elements replaced:
setuptxt = """
def replaceVert(al : list, repl:list, oIdx:int, iIdx:int):
for pos in range(len(repl)):
al[oIdx+pos][iIdx] = repl[pos]
a = [ [kk for kk in range(1+pp,1000+pp)] for pp in range(1,1000)]
repl = [chr(mm) for mm in range(32,100)]
"""
import timeit
print(timeit.timeit("replaceVert(a,repl,20,5)",number=500, setup = setuptxt))
zipp = """b = list(map(list,zip(*a)))
b[20][5:5+len(repl)]=repl
c = list(map(list,zip(*b)))
"""
print(timeit.timeit(stmt=zipp, setup=setuptxt,number=500))
Output:
looping: 0.07702917579216137
zipping: 69.4807168493871
Looping wins. Thanks #Sphinx for his comment
Related
I have simple tensor
a = tf.constant([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]])
and want to slice it, but I need to do it differently for each of the rows. This slice operation is described by another tensor
b = tf.constant([[0, 1], [2, 4], [2, 5]])
It means that from the first row of tensor a I need elements from 0 to 1, from the second row from 2 to 4 and so on. So the final final result will be
[
[1],
[8, 9],
[13, 14, 15]
]
My first idea was to fill ranges between begin and end of a slice, but unfortunately, doing it with map_fn is not possible because result's rows have different lengths.
Does anyone know how to do such operation?
Basically we have two arrays to iterate. One with actual data, other with range to return.
Therefore, zip function can help iterate over elements from multiple arrays one by one.
import tensorflow as tf
a = tf.constant([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]])
b = tf.constant([[0, 1], [2, 4], [2, 5]])
# As you iterate, provided a and b have same length
# [1, 2, 3, 4, 5] sliced as [0:1]
# [6, 7, 8, 9, 10] sliced as [2:4]
# [11, 12, 13, 14, 15] sliced as [2:5]
[data.numpy().tolist()[start:end] for data, (start, end) in zip(a,b)]
Output:
[[1], [8, 9], [13, 14, 15]]
If the size of b is known at graph compile time, then you can slice each row separately.
import tensorflow as tf
a = tf.constant([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]])
b = tf.constant([[0, 1], [2, 4], [2, 5]])
r = []
for i in range(3):
bi = b[i]
r.append(a[i][bi[0]: bi[1]])
print(r)
I am trying to get indexes of a list store into a new list.
for example,
A = ['A', 'B', 'C',....,'Z']
and B list will select random no of indexes of A list like.
B = [[2,None,None], [1,None,None], [3,None,None],.....,[0, None,None]]
where list limit is, suppose, 10 and None will be replaced with the random no between 0 to 20 and finally the list of resultant look like,
result = [[2, 2, 3], [0, 4, 5], [8, 2, 4], [3, 8, 9]]
the first element in a sublist refers to the list A and the 2nd and third elements refer to the random selection of numbers between 0 to 10
Using random.sample.
import random
result = [random.sample(range(len(A)), 1) + random.sample(range(10), 2) for _ in range(10) ]
If you don't mind possible replication of values in the elements you can use a list comprehension, using random.randrange to generate the numbers:
result = [[random.randrange(26), random.randrange(10), random.randrange(10)] for _ in range(10)]
print(result)
Sample output:
[[18, 8, 1], [24, 1, 4], [24, 6, 5], [1, 4, 4], [7, 0, 9], [10, 7, 7], [0, 6, 9], [0, 9, 4], [6, 4, 4], [4, 2, 7]]
If you want to ensure no replication in each of elements of the list, you can use zip and random.sample to put together 3 lists of unique values and select values from those:
result = [[a, b, c] for a, b, c in zip(random.sample(range(26), 10), random.sample(range(10), 10), random.sample(range(10), 10))]
print(result)
Sample output:
[[2, 0, 1], [21, 4, 0], [11, 1, 4], [10, 7, 5], [15, 3, 3], [23, 6, 8], [25, 5, 2], [1, 9, 7], [24, 8, 9], [6, 2, 6]]
Think this basis for you
A = ['A', 'B', 'C','Z']
B = [[2,None,None], [1,None,None], [3,None,None],[0, None,None]]
for newb in B:
if newb[1] is None:
newb[1] = random.randrange(0,10)
if newb[2] is None:
newb[2] = random.randrange(0,10)
print(B)
it do like
[[2, 2, 2], [1, 6, 9], [3, 5, 7], [0, 6, 2]]
I'm struggling to get each of the four numbers of a python array contained within a 2x2 into their separate arrays much like a Sudoku grid. Order doesn't matter. I would have tried writing a code or something but my mind has gone blank.
example grid
[
[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]
]
I want to be able to get it in the form
[
[ 1, 2, 5, 6],
[ 3, 4, 7, 8],
[ 9, 10, 13, 14],
[11, 12, 15, 16]
]
Here is a pure Python solution. If you are after something more readable consider using NumPy (see below).
>>> from itertools import count, chain
>>>
# create 2x2 blocks of 2x2
>>> c = count(1)
>>> L4D = [[[[next(c) for i in range(2)] for j in range(2)] for k in range(2)] for l in range(2)]
>>> L4D
[[[[1, 2], [3, 4]], [[5, 6], [7, 8]]], [[[9, 10], [11, 12]], [[13, 14], [15, 16]]]]
# swap middle dimensions
>>> L4D = [zip(*i) for i in L4D]
# next line is not necessary, only here so we can see what's going on
>>> L4D = [list(i) for i in L4D]
>>> L4D
[[([1, 2], [5, 6]), ([3, 4], [7, 8])], [([9, 10], [13, 14]), ([11, 12], [15, 16])]]
# join first two and last two dimensions
>>> result = [list(chain.from_iterable(j)) for j in chain.from_iterable(L4D)]
>>> result
[[1, 2, 5, 6], [3, 4, 7, 8], [9, 10, 13, 14], [11, 12, 15, 16]]
If using NumPy is an option this can be simplified. Here are three different possibilities. The first is a direct translation of the pure Python solution:
>>> import numpy as np
>>>
>>> np.arange(1, 17).reshape(2, 2, 2, 2).swapaxes(1, 2).reshape(4, 4)
array([[ 1, 2, 5, 6],
[ 3, 4, 7, 8],
[ 9, 10, 13, 14],
[11, 12, 15, 16]])
>>> np.block(list(map(list, np.arange(1, 17).reshape(2, 2, 2, 2))))
array([[ 1, 2, 5, 6],
[ 3, 4, 7, 8],
[ 9, 10, 13, 14],
[11, 12, 15, 16]])
>>> a = np.arange(4).reshape(2, 2)
>>> b = np.ones((2, 2), dtype = int)
>>> 4 * np.kron(a, b) + np.kron(b, a) + 1
array([[ 1, 2, 5, 6],
[ 3, 4, 7, 8],
[ 9, 10, 13, 14],
[11, 12, 15, 16]])
I finally found a solution to my question with a little modification to this answer
cells = [[] * 4 for x in range(4)]
for row_index, row in enumerate(grid):
for col_index, col in enumerate(row):
i = col_index//2 + 2*(row_index//2)
cells[i].append(col)
I am reading Data from CSV file which comes similar to the below matrix/array
b = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
I would like to change the index of every element greater than 1 to a new row in the arraylist
this will make the above array as below
b = [[1,2],[5,6],[9,10],[3,4],[7,8][11,12]]
what i have done in python (but couldn't get the answer)
b = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
c = b
rows = len(b)
columns = len(b[0])
c[4].append(1)
count = 3
for i in range(rows):
for j in range(columns):
if i > 1:
for k in columns
list1 =
c.insert(count,list1)
count = count + 1
You can use numpy. Perform indexing and concatenate at the end:
import numpy as np
b = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
print(np.concatenate((b[:,:2], b[:,2:])))
# [[ 1 2]
# [ 5 6]
# [ 9 10]
# [ 3 4]
# [ 7 8]
# [11 12]]
data =[]
data.append(['a',1])
data.append(['b',2])
data.append(['c',3])
data.append(['d',4])
print(data)
output
[['a', 1], ['b', 2], ['c', 3], ['d', 4]]
One line solution np.array(b).reshape(-1,2):
import numpy as np
b = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
np.array(b).reshape(-1,2)
array([[ 1, 2],
[ 3, 4],
[ 5, 6],
[ 7, 8],
[ 9, 10],
[11, 12]])
Why not just split it into two lists, and then recombine them?
new_elements = []
for i in range(len(b)):
if len(b[i]) > 2:
new_elements.append(b[i][2:])
b[i] = b[i][:2]
b.extend(new_elements)
You might want to use numpy arrays and the concatenate function.
import numpy as np
b = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) # or b = np.array(b)
c = np.concatenate((b[:, :2], b[:, 2:]),0)
If you prefer working with python arrays, you can use list interpretation:
c = [row[:2] for row in b]
c.extend([row[2:] for row in b])
which returns
[[1, 2], [5, 6], [9, 10], [3, 4], [7, 8], [11, 12]]
Using a list comprehension.
Ex:
b = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
n = 2
b = [j[i:i+n] for j in b for i in range(0, len(j), n)]
b = b[0::2] + b[1::2]
print(b)
Output:
[[1, 2], [5, 6], [9, 10], [3, 4], [7, 8], [11, 12]]
Another approach would be this:
b = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
step = 2
length = len(b[0])
b = [elem[i:i+step] for i in range(0,length,step) for elem in b]
print(b)
Output:
[[1, 2], [5, 6], [9, 10], [3, 4], [7, 8], [11, 12]]
So, I am writing a code to shift the elements of a particular list within a list of lists towards right.
def right(state,index):
r_state=state
new_state = []
for j in range(1,len(r_state[index])):
new_state.append(r_state[index][j-1])
new_state.insert(0, r_state[index][-1])
r_state[index]=new_state
return r_state
#case1
for i in range(2):
print(right([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]], i))
#case2
def printer(node):
for i in range(2):
print(right(node, i))
printer([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])
case 1 gives me the output that I want(only one sublist corresponding to the index changed):
[[4, 1, 2, 3], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
[[1, 2, 3, 4], [8, 5, 6, 7], [9, 10, 11, 12], [13, 14, 15, 16]]
But case 2 ends up updating my list of lists
[[4, 1, 2, 3], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
[[4, 1, 2, 3], [8, 5, 6, 7], [9, 10, 11, 12], [13, 14, 15, 16]]
Why is the list being updated? Also, how can I modify case2 to get the same output as case 1?
The problem is that assignment does not make a copy in Python. You have to explicitly copy. In your own code, to copy the list, change
r_state = state
To
r_state = state[:]
Also, you will see r_state = list(state) used as well. Python 3 has the even more explicit:
r_state = state.copy()
You can also use a list-comprehension to make a new list. Here is a quick-and-dirty approach using modular arithmetic to shift the elements of your sequence:
>>> def shift_right(lst, shift):
... modulus = len(lst)
... return [lst[(i + shift)%modulus] for i in range(len(lst))]
...
>>> def right(state, index):
... return [shift_right(sub, 1) if i == index else sub for i, sub in enumerate(state)]
...
>>> test = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]
>>> shift_right(test[0], 1)
[2, 3, 4, 1]
>>> shift_right(test[0], 2)
[3, 4, 1, 2]
>>> shift_right(test[0], 3)
[4, 1, 2, 3]
>>> shift_right(test[0], 4)
[1, 2, 3, 4]
>>> shift_right(test[0], 5)
[2, 3, 4, 1]
>>> right(test, 0)
[[2, 3, 4, 1], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
>>> right(test, 1)
[[1, 2, 3, 4], [6, 7, 8, 5], [9, 10, 11, 12], [13, 14, 15, 16]]
That last comprehension is perhaps a bit too dense, and could be written like this instead:
>>> def right(state, index):
... result = []
... for i, sub in enumerate(state):
... if i == index:
... result.append(shift_right(sub, 1))
... else:
... result.append(sub)
... return result
...
>>> right(test, 0)
[[2, 3, 4, 1], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
>>> right(test, 1)
[[1, 2, 3, 4], [6, 7, 8, 5], [9, 10, 11, 12], [13, 14, 15, 16]]
>>> right(test, 2)
[[1, 2, 3, 4], [5, 6, 7, 8], [10, 11, 12, 9], [13, 14, 15, 16]]
>>> right(test, 3)
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [14, 15, 16, 13]]
>>>