2

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?

2
  • 3
    One thing to remember is that lists are mutable. For your usecase, is it OK if the list is literally [a, b, b, b]? (In that, if I were to do b[1][3] = "PASTA" later in the code, is it OK that that propagates itself into your resulting array? (eg array[2][1][3] would give "PASTA") Commented Sep 1, 2021 at 18:06
  • you probably want an numpy.array, not a native Python list! Commented Sep 1, 2021 at 18:16

4 Answers 4

3

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]]]
Sign up to request clarification or add additional context in comments.

5 Comments

looks cleaner and since you have to import a library anway e.g. copy, its the better solution. The only downside I see is that numpy is much heavier than copy, e. g. for the unlikely case you want to create an executable. Anyway, propably best solution!
Since np.array() makes a proper copy of its input, you could also shorten the code to np.array([a]+[b]*5).
Careful, your answer has shape (20,3) instead of (5,4,3) as I believe OP wants.
that's probably most clean as it took me a moment trying to figure out a comfortable way to get what they appear to want
@joanis another reshape will correct that!
2

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

1

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

this doesn't directly work because many of the members refer to the same member! try it yourself c = [a] + [b]*5 c[1][1][1] = "foo"
In order to protect the results from have shared references, I would use copy.deepcopy: import copy, c = copy.deepcopy([a] + [b]*5)
@ti7 and joanis updated the answer accordingly to your comments. Thank you very much for pointing that out!
it needs to be done for each copy (specifically the list multiplication is troublesome)
@joanis unfortunately this will not work, as ti7 pointed out you have to deep copy every list seperately, as shown in the updated answer.
|
0

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.