Appending a list to a list in a loop (Python)

Viewed 133

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

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]]]

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.

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)]

[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]]]

Related