# How to create arrays of lists#

```import awkward as ak
import numpy as np
```

## From Python lists#

If you have a collection of Python lists, the easiest way to turn them into an Awkward Array is to pass them to the `ak.Array` constructor, which recognizes a non-dict, non-NumPy iterable and calls `ak.from_iter()`.

```python_lists = [[1, 2, 3], [], [4, 5], , [7, 8, 9, 10]]
python_lists
```
```[[1, 2, 3], [], [4, 5], , [7, 8, 9, 10]]
```
```awkward_array = ak.Array(python_lists)
awkward_array
```
```[[1, 2, 3],
[],
[4, 5],
,
[7, 8, 9, 10]]
---------------------
type: 5 * var * int64```

The lists of lists can be arbitrarily deep.

```python_lists = [[[[], [1, 2, 3]]], [[[4, 5]]], []]
python_lists
```
```[[[[], [1, 2, 3]]], [[[4, 5]]], []]
```
```awkward_array = ak.Array(python_lists)
awkward_array
```
```[[[[], [1, 2, 3]]],
[[[4, 5]]],
[]]
---------------------------------
type: 3 * var * var * var * int64```

The “`var *`” in the type string indicates nested levels of variable-length lists. This is an array of lists of lists of lists of integers.

The advantage of the Awkward Array is that the numerical data are now all in one array buffer and calculations are vectorized across the array, such as NumPy universal functions.

```np.sqrt(awkward_array)
```
```[[[[], [1, 1.41, 1.73]]],
[[[2, 2.24]]],
[]]
-----------------------------------
type: 3 * var * var * var * float64```

Unlike Python lists, arrays consist of a homogeneous type. A Python list wouldn’t notice if numerical data were given at two different levels of nesting, but that’s a big difference to an Awkward Array.

```union_array = ak.Array([[[[], [1, 2, 3]]], [[4, 5]], []])
union_array
```
```[[[[], [1, 2, 3]]],
[[4, 5]],
[]]
----------------------------
type: 3 * var * var * union[
var * int64,
int64
]```

In this example, the data type is a “union” of two levels deep and three levels deep.

```union_array.type
```
```ArrayType(ListType(ListType(UnionType([ListType(NumpyType('int64')), NumpyType('int64')]))), 3)
```

Some operations are possible with union arrays, but not all. (Iteration in Numba is one such example.)

## From NumPy arrays#

The `ak.Array` constructor loads NumPy arrays differently from Python lists. The inner dimensions of a NumPy array are guaranteed to have the same lengths, so they are interpreted as a fixed-length list type.

```numpy_array = np.arange(2 * 3 * 5).reshape(2, 3, 5)
numpy_array
```
```array([[[ 0,  1,  2,  3,  4],
[ 5,  6,  7,  8,  9],
[10, 11, 12, 13, 14]],

[[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24],
[25, 26, 27, 28, 29]]])
```
```regular_array = ak.Array(numpy_array)
regular_array
```
```[[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14]],
[[15, 16, 17, 18, 19], [20, 21, 22, 23, 24], [25, 26, 27, 28, 29]]]
--------------------------------------------------------------------
type: 2 * 3 * 5 * int64```

The type in this case has no “`var *`” in it, only “`2 *`”, “`3 *`”, and “`5 *`”. It’s a length-2 array of length-3 lists containing length-5 lists of integers.

Furthermore, if NumPy arrays are nested within Python lists (or other iterables), they’ll be treated as variable-length (”`var *`”) because there’s no guarantee at the start of a sequence that all NumPy arrays in the sequence will have the same shape.

```numpy_arrays = [
np.arange(3 * 5).reshape(3, 5),
np.arange(3 * 5, 2 * 3 * 5).reshape(3, 5),
]
numpy_arrays
```
```[array([[ 0,  1,  2,  3,  4],
[ 5,  6,  7,  8,  9],
[10, 11, 12, 13, 14]]),
array([[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24],
[25, 26, 27, 28, 29]])]
```
```irregular_array = ak.Array(numpy_arrays)
irregular_array
```
```[[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14]],
[[15, 16, 17, 18, 19], [20, 21, 22, 23, 24], [25, 26, 27, 28, 29]]]
--------------------------------------------------------------------
type: 2 * var * var * int64```

Both `regular_array` and `irregular_array` have the same data values:

```regular_array.to_list() == irregular_array.to_list()
```
```True
```

but they have different types:

```regular_array.type, irregular_array.type
```
```(ArrayType(RegularType(RegularType(NumpyType('int64'), 5), 3), 2),
ArrayType(ListType(ListType(NumpyType('int64'))), 2))
```

This can make a difference in some operations, such as broadcasting.

If you want more control over this, use the explicit `ak.from_iter()` and `ak.from_numpy()` functions instead of the general-purpose `ak.Array` constructor.

## Unflattening#

Another difference between `ak.from_iter()` and `ak.from_numpy()` is that iteration over Python lists is slow and necessarily copies the data, whereas ingesting a NumPy array is zero-copy. (You can see that it’s zero copy by changing the data in-place.)

In some cases, list-making can be vectorized. If you have a flat NumPy array of data and an array of “counts” that add up to the length of the data, then you can `ak.unflatten()` it.

```data = np.array([1, 2, 3, 4, 5, 6, 7, 8])
counts = np.array([3, 0, 1, 4])

unflattened = ak.unflatten(data, counts)
unflattened
```
```[[1, 2, 3],
[],
,
[5, 6, 7, 8]]
---------------------
type: 4 * var * int64```

The first list has length `3`, the second has length `0`, the third has length `1`, and the last has length `4`. This is close to Awkward Array’s internal representation of variable-length lists, so it can be performed quickly.

This function is named `ak.unflatten()` because it has the opposite effect as `ak.flatten()` and `ak.num()`:

```ak.flatten(unflattened)
```
```[1,
2,
3,
4,
5,
6,
7,
8]
---------------
type: 8 * int64```
```ak.num(unflattened)
```
```[3,
0,
1,
4]
---------------
type: 4 * int64```

## With ArrayBuilder#

`ak.ArrayBuilder` is described in more detail in this tutorial, but you can also construct arrays of lists using the `begin_list`/`end_list` methods or the `list` context manager.

(This is what `ak.from_iter()` uses internally to accumulate lists.)

```builder = ak.ArrayBuilder()

builder.begin_list()
builder.append(1)
builder.append(2)
builder.append(3)
builder.end_list()

builder.begin_list()
builder.end_list()

builder.begin_list()
builder.append(4)
builder.append(5)
builder.end_list()

array = builder.snapshot()
array
```
```[[1, 2, 3],
[],
[4, 5]]
---------------------
type: 3 * var * int64```
```builder = ak.ArrayBuilder()

with builder.list():
builder.append(1)
builder.append(2)
builder.append(3)

with builder.list():
pass

with builder.list():
builder.append(4)
builder.append(5)

array = builder.snapshot()
array
```
```[[1, 2, 3],
[],
[4, 5]]
---------------------
type: 3 * var * int64```

## In Numba#

Functions that Numba Just-In-Time (JIT) compiles can use `ak.ArrayBuilder` or construct flat data and “counts” arrays for `ak.unflatten()`.

(At this time, Numba can’t use context managers, the `with` statement, in fully compiled code. `ak.ArrayBuilder` can’t be constructed or converted to an array using `snapshot` inside a JIT-compiled function, but can be outside the compiled context. Similarly, `ak.*` functions like `ak.unflatten()` can’t be called inside a JIT-compiled function, but can be outside.)

```import numba as nb
```
```@nb.jit
def append_list(builder, start, stop):
builder.begin_list()
for x in range(start, stop):
builder.append(x)
builder.end_list()

@nb.jit
def example(builder):
append_list(builder, 1, 4)
append_list(builder, 999, 999)
append_list(builder, 4, 6)
return builder

builder = example(ak.ArrayBuilder())

array = builder.snapshot()
array
```
```[[1, 2, 3],
[],
[4, 5]]
---------------------
type: 3 * var * int64```
```@nb.jit
def append_list(i, data, j, counts, start, stop):
for x in range(start, stop):
data[i] = x
i += 1
counts[j] = stop - start
j += 1
return i, j

@nb.jit
def example():
data = np.empty(5, np.int64)
counts = np.empty(3, np.int64)
i, j = 0, 0
i, j = append_list(i, data, j, counts, 1, 4)
i, j = append_list(i, data, j, counts, 999, 999)
i, j = append_list(i, data, j, counts, 4, 6)
return data, counts

data, counts = example()

array = ak.unflatten(data, counts)
array
```
```[[1, 2, 3],
[],
[4, 5]]
---------------------
type: 3 * var * int64```