I have a Python list with values, say:
a = [[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]]
I want to append in a loop a new list 'b' to the list 'a'.
b = [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]
The result should look like (when adding 'b' once to 'a'):
[[[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]], [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]]
Now, I want to append the list b N times to the list a.
Both a and b have shape (4,3)
The result should then have shape: (N+1,4,3)
How do I do this?
4 Answers 4
Native Python Lists will not behave the way you expect here as described in a few comments, so if you can use a 3rd party library, consider NumPy, which will behave more like a matrix of values as you expect and can then be converted back into a Python List
Setup
>>> a = [[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]]
>>> b = [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]
Replicate b vertically
- the second argument to
np.tile()describes the replications in each dimension .reshape()to prepare it as a 3-dimensional array
>>> import numpy as np
>>> b_tiled = np.tile(np.array(b), (4,1)).reshape(4,4,3)
>>> b_tiled
array([[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]]])
Collect a and b_tiled into the same array
NOTE a should be reshaped or [a] to match the shape of b_tiled
>>> np.vstack((np.array([a]), b_tiled))
array([[[0, 0, 0],
[1, 0, 1],
[1, 1, 0],
[0, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]]])
.tolist()
You can use .tolist() to make a native Python list again, though it may be more convenient to you as a numpy array
>>> np.vstack((np.array([a]), b_tiled)).tolist()
[[[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]], [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]], [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]], [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]], [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]]
5 Comments
np.array() makes a proper copy of its input, you could also shorten the code to np.array([a]+[b]*5).Wrapping up my contributions to this discussion into an actual answer, summarizing what I think are the two best solutions:
1 Using NumPy arrays
Start from @Andreas' solution, but follow @ti7's lead and wrap the results into a numpy array, which manages the memory correctly:
result = np.array([a] + [b] * 5)
This solution brings the results into much more usable and versatile NumPy arrays.
2 Using deepcopy
Start from @Andreas' solution, and add a deep copy so the result does not alias parts of the array together:
import copy
result = [a] + [copy.deepcopy(b) for _ in range(5)]
This solution keeps the results as standard Python lists of lists.
Kuddos to @ti7 for noticing that the deep copy had to be done by instance of b rather than over the results, since deepcopy does not break aliasing that is internal to its input; and to @Andreas for assembling this line of code from the comments.
Caveat: since a is not deep copied here, result[0] is an alias for a, and changes to either will change both. Deep copy a too to avoid this.
Comments
add the lists and multiply the second list:
l = [a] + [b]*5
[[[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]],
[[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]],
[[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]],
[[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]],
[[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]],
[[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]]
In case you want to modify the list values later on, be aware that you actually have 5x the SAME list referenced (as mentioned by @ti7), this means if you change one value in list b you change all, like this:
l[1][1][1] = "foo"
[[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]]
[[2, 1, 0], [3, 'foo', 1], [4, 1, 0], [2, 1, 1]]
[[2, 1, 0], [3, 'foo', 1], [4, 1, 0], [2, 1, 1]]
[[2, 1, 0], [3, 'foo', 1], [4, 1, 0], [2, 1, 1]]
[[2, 1, 0], [3, 'foo', 1], [4, 1, 0], [2, 1, 1]]
[[2, 1, 0], [3, 'foo', 1], [4, 1, 0], [2, 1, 1]]
to avoid that use (as mentioned by @joanis):
l = [a] + [copy.deepcopy(b) for _ in range(5)]
6 Comments
c = [a] + [b]*5 c[1][1][1] = "foo"copy.deepcopy: import copy, c = copy.deepcopy([a] + [b]*5)[a+b*5] this will create
[[[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1], [2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1], [2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1], [2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1], [2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1], [2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]]
[a, b, b, b]? (In that, if I were to dob[1][3] = "PASTA"later in the code, is it OK that that propagates itself into your resulting array? (egarray[2][1][3]would give"PASTA")