# Array Concatenation, Splitting and Iteration Techniques in Numpy

## Array Concatenation

Array concatenation involves combining multiple arrays along a specified axis to create a larger array. This operation allows you to merge arrays either horizontally (along columns) or vertically (along rows).

Let’s explore three key functions for array concatenation in NumPy:

### np.concatenate()

The `np.concatenate()`

is the main function for joining arrays in NumPy. It takes a sequence of arrays as input and concatenates them along the specified axis. For example, you can stack arrays vertically (like stacking blocks on top of each other) or horizontally (like placing blocks side by side). You can specify the axis using `axis`

attribute.

#### Example:

import numpy as np arr1=np.array([[3,8,4,6],[8,7,6,2]]) arr2=np.array([[4,2,2,1],[6,4,3,1]]) print("Join two arrays along rows :") joint_arr1=np.concatenate((arr1,arr2), axis=0) print(joint_arr1) print("-----------------------") print("Join two arrays along columns :") joint_arr2=np.concatenate((arr1,arr2), axis=1) print(joint_arr2)

#### Output :

```
Join two arrays along columns :
[[3 8 4 6]
[8 7 6 2]
[4 2 2 1]
[6 4 3 1]]
-----------------------
Join two arrays along rows :
[[3 8 4 6 4 2 2 1]
[8 7 6 2 6 4 3 1]]
```

### np.vstack() & np.hstack()

If you only want to stack arrays vertically, `np.vstack()`

is your go-to function. It’s like stacking plates one on top of the other. You don’t need to worry about the axis; it always stacks along rows. It is equivalent to `np.concatenate()`

with `axis=0`

.

When you need to stack arrays horizontally, use `np.hstack()`

. It’s like placing plates side by side on a table. It is equivalent to `np.concatenate()`

with `axis=1`

.

**Example: Stacking Arrays Horizontally**

import numpy as np # Two arrays with the same number of rows array1 = np.array([[1, 2], [3, 4]]) array2 = np.array([[5, 6], [7, 8]]) # Horizontally concatenate them concatenated_horizontal = np.hstack((array1, array2)) print("Array 1:") print(array1) print("\nArray 2:") print(array2) print("\nConcatenated Horizontally:") print(concatenated_horizontal)

#### Output :

```
Array 1:
[[1 2]
[3 4]]
Array 2:
[[5 6]
[7 8]]
Concatenated Horizontally:
[[1 2 5 6]
[3 4 7 8]]
```

**Example: Stacking Arrays Vertically**

import numpy as np # Two arrays with the same number of columns array3 = np.array([[7], [8]]) array4 = np.array([[9], [10]]) # Vertically concatenate them concatenated_vertical = np.vstack((array3, array4)) print("Array 3:") print(array3) print("\nArray 4:") print(array4) print("\nConcatenated Vertically:") print(concatenated_vertical)

#### Output :

```
Array 3:
[[7]
[8]]
Array 4:
[[ 9]
[10]]
Concatenated Vertically:
[[ 7]
[ 8]
[ 9]
[10]]
```

**Array Splitting**

Array splitting in NumPy is a crucial operation for segmenting and organizing array data into manageable chunks. NumPy provides several functions for array splitting, let’s explore the 3 most used functions.

### np.split()

The `np.split()`

function divides an array into multiple sub-arrays along a specified axis. It takes three arguments: the array to split, the number of sections to split it into, and the axis along which to split. Here’s an example:

**Example:**

import numpy as np # Create an array arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9]) # Split the array into three equal parts along axis 0 split_arr = np.split(arr, 3) print(split_arr)

#### Output:

`[array([1, 2, 3]), array([4, 5, 6]), array([7, 8, 9])]`

### np.hsplit()

If you need to split an array horizontally, you can use `np.hsplit()`

. It divides a 2D array into smaller arrays along columns.

**Example:**

import numpy as np # Create a 3x4 array matrix = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) # Splitting the array horizontally into two equal subarrays subarrays = np.hsplit(matrix, 2) print("Original Array:") print(matrix) print("\nSplit Arrays Horizontally:") for subarray in subarrays: print(subarray)

#### Output :

```
Original Array:
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
Split Arrays Horizontally:
[[ 1 2]
[ 5 6]
[ 9 10]]
[[ 3 4]
[ 7 8]
[11 12]]
```

### np.vsplit()

When you want to split an array vertically, you use `np.vsplit()`

. It breaks a 2D array into smaller arrays along rows.

#### Example:

import numpy as np # Create a 2D array matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # Splitting the array vertically into three subarrays subarrays = np.vsplit(matrix, 3) print("Original Array:") print(matrix) print("\nSplit Arrays Vertically:") for subarray in subarrays: print(subarray)

#### Output :

```
Original Array:
[[1 2 3]
[4 5 6]
[7 8 9]]
Split Arrays Vertically:
[[1 2 3]]
[[4 5 6]]
[[7 8 9]]
```

## Looping Through NumPy Arrays

Looping through arrays is a common task for accessing and working with the elements inside. This process is essential for performing calculations, applying functions, or extracting specific data points from arrays.

If you have a one-dimensional array, you can simply use a `for`

loop to go through each element one by one. In addition, you can also use the list comprehension.

**Example: Iterating through a 1D Array**

import numpy as np # Create a 1D array array_1d = np.array([1, 2, 3, 4, 5]) # Using a for loop for element in array_1d: print(element) # Using a list comprehension squared_elements = [x ** 2 for x in array_1d] print("Squared Elements:", squared_elements)

#### Output :

```
1
2
3
4
5
Squared Elements: [1, 4, 9, 16, 25]
```

## Iterate Multi-dimensional Arrays

When you’re dealing with arrays that have more than one dimension, like 2D arrays, you’ll use nested `for`

loops. Each loop handles one dimension of the array. NumPy follows the row-major order for iteration, meaning it moves through the elements row by row. In a 2D array, it iterates through rows first and then columns.

**Example 1: Iterating through a 2D Array**

# Create a 2D array matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # Using nested loops for row in matrix: for element in row: print(element) # Using a list comprehension (flattening the array) flattened_elements = [element for row in matrix for element in row] print("Flattened Elements:", flattened_elements)

#### Output :

```
1
2
3
4
5
6
7
8
9
Flattened Elements: [1, 2, 3, 4, 5, 6, 7, 8, 9]
```

**Example 2: Iterating through a 3D Array**

# Create a 3D array cube = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) # Using nested loops for a 3D array for layer in cube: for row in layer: for element in row: print(element) # Using a list comprehension (flattening the 3D array) flattened_elements = [element for layer in cube for row in layer for element in row] print("Flattened Elements:", flattened_elements)

#### Output :

```
1
2
3
4
5
6
7
8
Flattened Elements: [1, 2, 3, 4, 5, 6, 7, 8]
```

## Using np.nditer() for Efficient Iteration

NumPy’s `np.nditer()`

is a handy tool that helps you loop through arrays efficiently. Traditional loops in Python, such as `for`

or `while`

loops can be slow when applied to large arrays due to Python’s interpreted nature. In contrast, `np.nditer()`

leverages optimized C code under the hood, resulting in significantly faster execution times, especially for large datasets.

With `np.nditer()`

, you can perform complex operations on arrays with concise and readable code. It means you don’t have to write nested loops for multi-dimensional arrays.

**Example:**

import numpy as np # Create a 2D array matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # Using numpy.nditer() with options with np.nditer(matrix, flags=['multi_index'], op_flags=['readwrite'], order="F") as it: for x in it: print(f"Element at index {it.multi_index}: {x}") # Performing operations during iteration with np.nditer(matrix, op_flags=['readwrite']) as it: for x in it: # Square elements in the iterator it[0] = x ** 2 print("Squared Matrix:") print(matrix)

In this example, we use `np.nditer()`

to iterate through a 2D array, accessing elements and performing operations efficiently.

#### Output :

```
Element at index (0, 0): 1
Element at index (1, 0): 4
Element at index (2, 0): 7
Element at index (0, 1): 2
Element at index (1, 1): 5
Element at index (2, 1): 8
Element at index (0, 2): 3
Element at index (1, 2): 6
Element at index (2, 2): 9
Squared Matrix:
[[ 1 4 9]
[16 25 36]
[49 64 81]]
```

## Understanding Key Parameters of np.nditer()

When using `numpy.nditer()`

, you have several parameters to customize how the iteration process behaves. Let’s explore some of the key parameters available with `np.nditer()`

:

**1. The **`op`

** Option:** This option tells `np.nditer()`

which arrays to work with. You can specify one array or multiple arrays to iterate over.

**2. Operand Data Types (**`op_dtypes`

**):** This option lets you specify the data types of the arrays you’re working with. You can choose one type or a sequence of types.

**3. Order:** Finally, the order option decides how the output array is laid out in memory:

`"C"`

: The array is stored in C-contiguous order, meaning it’s laid out row by row.`"F"`

: The array is stored in Fortran-contiguous order, meaning it’s laid out column by column.`"A"`

: It can be in either C or Fortran order.`"K"`

: It keeps the existing order of the input array.

### Flags Parameters

**1. **`flags`

**:** Flags are like special instructions for the iteration process. Some mostly used flags are:

`"c_index"`

: It gives you the index of the current element, following C-order (like reading left to right, top to bottom).`"f_index"`

: This one gives you the index of the current element, but in Fortran-order (like reading top to bottom, left to right).`"external_loop"`

: Normally,`np.nditer()`

returns one item at a time. This flag tells it to give you whole rows or columns instead.`"multi_index"`

: Instead of just one index, this flag gives you a tuple of indices for the current element.`"ranged"`

: You can limit the iteration to a specific range of elements.

**2. Operand Flags** **(**`op_flags`

**): **These flags control how you can interact with the arrays you’re iterating over:

`"readwrite"`

: You can read from and write to the array.`"copy"`

: It makes a copy of the array before iterating over it.`"no_broadcast"`

: Prevents broadcasting, which is a way of stretching arrays to match each other’s shapes.`"readonly"`

: You can only read from the array.`"writeonly"`

: You can only write to the array.

## Using np.ndenumerate()

The `np.ndenumerate()`

is a function in NumPy that iterates over elements of an array, providing both the index of each element and its corresponding value. This is particularly useful when you need to access both the value and its position within the array during iteration.

Using `np.ndenumerate()`

is straightforward. Here’s a basic example demonstrating its usage:

**Example:**

import numpy as np # Create a 2D array matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # Using numpy.ndenumerate() for index, value in np.ndenumerate(matrix): print(f"Index {index}: Value {value}")

#### Output:

```
Index (0, 0): Value 1
Index (0, 1): Value 2
Index (0, 2): Value 3
Index (1, 0): Value 4
Index (1, 1): Value 5
Index (1, 2): Value 6
Index (2, 0): Value 7
Index (2, 1): Value 8
Index (2, 2): Value 9
```