# Nakafa Learning Content

> For AI agents: use [llms.txt](https://nakafa.com/llms.txt) for the site index. Markdown versions are available by appending `.md` to content URLs or sending `Accept: text/markdown`.

URL: https://nakafa.com/id/materi/ai-ds/pemrograman-ai/indexing-dan-slicing-dengan-numpy
Source: https://raw.githubusercontent.com/nakafaai/nakafa.com/refs/heads/main/packages/contents/material/lesson/ai-ds/ai-programming/indexing-slicing-numpy/id.mdx

Indexing dan slicing NumPy untuk array 1D, array 2D, boolean mask, dan advanced indexing dengan contoh kode.

---

## Konsep Dasar Indexing dan Slicing

Indexing dan slicing adalah alat untuk mengekstrak data spesifik dari array NumPy. Bayangkan indexing sebagai penunjuk ke lokasi tepat, sedangkan slicing mengambil seluruh bagian sekaligus. Berbeda dengan list Python yang membuat copy, slicing NumPy membuat view yang berbagi memori dengan array asli.

Perilaku berbagi memori ini membuat NumPy efisien tetapi memerlukan perhatian khusus untuk menghindari modifikasi yang tidak diinginkan. [Dokumentasi indexing NumPy](https://numpy.org/doc/stable/user/basics.indexing.html) menyediakan contoh ekstensif pola indexing lanjutan ketika kamu butuh seleksi data yang lebih rinci.

## Operasi Array Satu Dimensi

Array satu dimensi menggunakan sintaks yang sama dengan list Python. Slicing mengembalikan view dari data, bukan copy, sehingga perubahan pada view akan mempengaruhi array asli.

File: indexing_1d.py
```python
import numpy as np

# Membuat array 1D
a = np.linspace(0, 7, 8)
print("Array original:", a)  # Output: Array original: [0. 1. 2. 3. 4. 5. 6. 7.]

# Indexing elemen tunggal
print("Elemen ke-3:", a[3])  # Output: Elemen ke-3: 3.0
print("Elemen terakhir:", a[-1])  # Output: Elemen terakhir: 7.0

# Slicing dasar
print("Slice [2:6]:", a[2:6])  # Output: Slice [2:6]: [2. 3. 4. 5.]
print("Slice [3:-2]:", a[3:-2])  # Output: Slice [3:-2]: [3. 4. 5.]

# Modifikasi melalui slicing (mengubah array asli)
a[:3] = 0
print("Setelah a[:3] = 0:", a)  # Output: Setelah a[:3] = 0: [0. 0. 0. 3. 4. 5. 6. 7.]
```

Konsep penting dalam slicing NumPy adalah perbedaan antara view dan copy. View berbagi memori dengan array asli, sedangkan copy adalah duplikat independen.

File: view_vs_copy.py
```python
import numpy as np

# Array original
original = np.array([1, 2, 3, 4, 5])
print("Original:", original)  # Output: Original: [1 2 3 4 5]

# Membuat view melalui slicing
view = original[1:4]
print("View:", view)  # Output: View: [2 3 4]

# Mengubah view (mempengaruhi original)
view[0] = 999
print("Setelah mengubah view:")
print("Original:", original)  # Output: Original: [  1 999   3   4   5]
print("View:", view)  # Output: View: [999   3   4]

# Membuat copy eksplisit
copy_array = original[1:4].copy()
copy_array[0] = 777
print("Setelah mengubah copy:")
print("Original:", original)  # Output: Original: [  1 999   3   4   5]
print("Copy:", copy_array)  # Output: Copy: [777   3   4]
```

## Operasi Array Multidimensi

Array multidimensi menggunakan tuple integer untuk indexing. Setiap dimensi dipisahkan dengan koma dalam tanda kurung siku. Assignment dan slicing dapat dikombinasikan untuk operasi yang kompleks.

File: indexing_2d.py
```python
import numpy as np

# Membuat array 2D
a = np.array([[1., 2., 3.],
            [4., 5., 6.],
            [7., 8., 9.],
            [10., 11., 12.]])
print("Array 2D:")
print(a)
# Output:
# [[ 1.  2.  3.]
#  [ 4.  5.  6.]
#  [ 7.  8.  9.]
#  [10. 11. 12.]]

# Indexing elemen spesifik
print("a[2,1]:", a[2,1])  # Output: a[2,1]: 8.0

# Slicing baris
print("a[1,:]:", a[1,:])  # Output: a[1,:]: [4. 5. 6.]

# Slicing kolom
print("a[:,2]:", a[:,2])  # Output: a[:,2]: [ 3.  6.  9. 12.]
```

Slicing lanjutan memungkinkan pengambilan subarray dengan pola yang kompleks:

File: advanced_slicing_2d.py
```python
import numpy as np

# Array 2D untuk demonstrasi
a = np.array([[1., 2., 3.],
            [4., 5., 6.],
            [7., 8., 9.],
            [10., 11., 12.]])

# Slicing subarray
print("a[1:3, 0:2]:")
print(a[1:3, 0:2])
# Output:
# [[4. 5.]
#  [7. 8.]]

# Slicing dengan step
print("a[::2, :]:")
print(a[::2, :])
# Output:
# [[ 1.  2.  3.]
#  [ 7.  8.  9.]]

# Slicing kolom dengan step
print("a[:, ::2]:")
print(a[:, ::2])
# Output:
# [[ 1.  3.]
#  [ 4.  6.]
#  [ 7.  9.]
#  [10. 12.]]
```

### Ringkasan Operasi Dua Dimensi

| Operasi | Deskripsi | Contoh Hasil |
|---------|-----------|--------------|
| `a[2,1]` | Elemen baris $$2$$, kolom $$1$$ | Satu nilai: `8.0` |
| `a[1,:]` | Seluruh baris $$1$$ | Array satu dimensi: `[4. 5. 6.]` |
| `a[:,2]` | Seluruh kolom $$2$$ | Array satu dimensi: `[3. 6. 9. 12.]` |
| `a[1:3, 0:2]` | Subarray baris $$1\text{-}2$$, kolom $$0\text{-}1$$ | Array dua dimensi: `[[4. 5.], [7. 8.]]` |
| `a[::2, :]` | Setiap baris kedua | Array dua dimensi dengan baris $$0, 2$$ |
| `a[:, ::2]` | Setiap kolom kedua | Array dua dimensi dengan kolom $$0, 2$$ |

Visible text: | Operasi | Deskripsi | Contoh Hasil |
|---------|-----------|--------------|
| `a[2,1]` | Elemen baris , kolom | Satu nilai: `8.0` |
| `a[1,:]` | Seluruh baris | Array satu dimensi: `[4. 5. 6.]` |
| `a[:,2]` | Seluruh kolom | Array satu dimensi: `[3. 6. 9. 12.]` |
| `a[1:3, 0:2]` | Subarray baris , kolom | Array dua dimensi: `[[4. 5.], [7. 8.]]` |
| `a[::2, :]` | Setiap baris kedua | Array dua dimensi dengan baris |
| `a[:, ::2]` | Setiap kolom kedua | Array dua dimensi dengan kolom |

## Advanced Indexing

Advanced indexing memungkinkan penggunaan array integer atau boolean untuk mengakses elemen dengan pola yang kompleks. Indexing dengan array integer membuat copy baru daripada view. Copy memiliki shape yang sama dengan index array.

File: integer_array_indexing.py
```python
import numpy as np

# Array untuk demonstrasi
a = np.array([0, 1, 2, 3, 4])
print("Array original:", a)  # Output: Array original: [0 1 2 3 4]

# Indexing dengan list indeks
i = [1, 3, 2, 1, 4]
print("Index array:", i)  # Output: Index array: [1, 3, 2, 1, 4]
print("a[i]:", a[i])  # Output: a[i]: [1 3 2 1 4]

# Indexing dan reshaping dengan array 2D indeks
i = np.array([[1, 2], [3, 4]])
print("Index 2D array:")
print(i)
# Output:
# [[1 2]
#  [3 4]]
print("a[i]:")
print(a[i])
# Output:
# [[1 2]
#  [3 4]]
```

Boolean mask adalah teknik yang berguna untuk filtering data berdasarkan kondisi tertentu. Indexing dengan array boolean mengambil elemen di mana nilai True ditemukan.

File: boolean_mask_indexing.py
```python
import numpy as np

# Array untuk demonstrasi
a = np.linspace(0, 5, 6)
print("Array:", a)  # Output: Array: [0. 1. 2. 3. 4. 5.]

# Membuat boolean mask
mask = np.array([True, False, True, False, True, False])
print("Boolean mask:", mask)  # Output: Boolean mask: [ True False  True False  True False]
print("a[mask]:", a[mask])  # Output: a[mask]: [0. 2. 4.]

# Boolean mask dari kondisi
condition_mask = a % 2 == 0
print("Condition mask (a % 2 == 0):", condition_mask)  # Output: Condition mask (a % 2 == 0): [ True False  True False  True False]
print("a[condition_mask]:", a[condition_mask])  # Output: a[condition_mask]: [0. 2. 4.]

# Boolean mask dengan kondisi kompleks
complex_mask = (a > 1) & (a < 4)
print("Complex mask (a > 1) & (a < 4):", complex_mask)  # Output: Complex mask (a > 1) & (a < 4): [False False  True  True False False]
print("a[complex_mask]:", a[complex_mask])  # Output: a[complex_mask]: [2. 3.]
```

Advanced indexing dapat dikombinasikan dengan slicing regular untuk operasi yang sangat fleksibel:

File: combined_indexing.py
```python
import numpy as np

# Array 2D untuk demonstrasi
a = np.arange(24).reshape(4, 6)
print("Array 2D:")
print(a)
# Output:
# [[ 0  1  2  3  4  5]
#  [ 6  7  8  9 10 11]
#  [12 13 14 15 16 17]
#  [18 19 20 21 22 23]]

# Kombinasi slicing dan indexing
print("a[1:3, [0, 2, 5]]:")
print(a[1:3, [0, 2, 5]])
# Output:
# [[ 6  8 11]
#  [12 14 17]]

# Boolean indexing pada baris, slicing pada kolom
row_mask = np.array([True, False, True, False])
print("a[row_mask, 2:5]:")
print(a[row_mask, 2:5])
# Output:
# [[ 2  3  4]
#  [14 15 16]]
```

## Teknik Lanjutan

NumPy menyediakan berbagai teknik slicing yang memungkinkan manipulasi data yang efisien dan fleksibel. Step parameter memungkinkan kamu mengambil elemen dengan interval tertentu, seperti mengambil setiap elemen kedua atau ketiga.

File: step_slicing.py
```python
import numpy as np

# Array untuk demonstrasi
a = np.arange(20)
print("Array:", a)  # Output: Array: [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]

# Slicing dengan step
print("a[::2] (setiap elemen kedua):", a[::2])  # Output: a[::2] (setiap elemen kedua): [ 0  2  4  6  8 10 12 14 16 18]
print("a[1::3] (mulai indeks 1, setiap ketiga):", a[1::3])  # Output: a[1::3] (mulai indeks 1, setiap ketiga): [ 1  4  7 10 13 16 19]
print("a[::-1] (membalik array):", a[::-1])  # Output: a[::-1] (membalik array): [19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0]

# Step pada array 2D
b = np.arange(12).reshape(3, 4)
print("Array 2D:")
print(b)
# Output:
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

print("b[::2, ::2] (setiap baris dan kolom kedua):")
print(b[::2, ::2])
# Output:
# [[ 0  2]
#  [ 8 10]]
```

Ellipsis (`...`) adalah shorthand untuk slice penuh pada dimensi yang tidak disebutkan. Sangat berguna untuk array dengan dimensi tinggi.

File: ellipsis_slicing.py
```python
import numpy as np

# Array 3D untuk demonstrasi
a = np.arange(24).reshape(2, 3, 4)
print("Array 3D shape:", a.shape)  # Output: Array 3D shape: (2, 3, 4)

# Menggunakan ellipsis
print("a[0, ...] (sama dengan a[0, :, :]):")
print(a[0, ...])
# Output:
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

print("a[..., 2] (sama dengan a[:, :, 2]):")
print(a[..., 2])
# Output:
# [[ 2  6 10]
#  [14 18 22]]

print("a[1, ..., ::2] (sama dengan a[1, :, ::2]):")
print(a[1, ..., ::2])
# Output:
# [[12 14]
#  [16 18]
#  [20 22]]
```

## Boolean Indexing

Boolean indexing menggunakan array boolean untuk memfilter elemen berdasarkan kondisi tertentu. True indexes mengambil elemen di target array, sedangkan False mengabaikannya.

File: boolean_operations.py
```python
import numpy as np

# Array untuk demonstrasi
a = np.linspace(0, 5, 6)
print("Array:", a)  # Output: Array: [0. 1. 2. 3. 4. 5.]

# Boolean mask manual
mask = np.array([True, False, True, False, True, False], dtype=bool)
print("Manual mask:", mask)  # Output: Manual mask: [ True False  True False  True False]
print("a[mask]:", a[mask])  # Output: a[mask]: [0. 2. 4.]

# Boolean mask dari perbandingan
greater_than_2 = a > 2
print("a > 2:", greater_than_2)  # Output: a > 2: [False False False  True  True  True]
print("a[a > 2]:", a[a > 2])  # Output: a[a > 2]: [3. 4. 5.]

# Boolean mask dengan kondisi ganda
even_and_greater_than_1 = (a % 2 == 0) & (a > 1)
print("(a % 2 == 0) & (a > 1):", even_and_greater_than_1)  # Output: (a % 2 == 0) & (a > 1): [False False  True False  True False]
print("a[even_and_greater_than_1]:", a[even_and_greater_than_1])  # Output: a[even_and_greater_than_1]: [2. 4.]
```

Boolean indexing pada array dua dimensi dapat diterapkan untuk filtering elemen berdasarkan kondisi:

File: boolean_2d.py
```python
import numpy as np

# Array 2D untuk demonstrasi
a = np.arange(12).reshape(3, 4)
print("Array 2D:")
print(a)
# Output:
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

# Boolean mask untuk elemen tertentu
mask = a > 5
print("Mask a > 5:")
print(mask)
# Output:
# [[False False False False]
#  [False False  True  True]
#  [ True  True  True  True]]

print("a[mask]:", a[mask])  # Output: a[mask]: [ 6  7  8  9 10 11]

# Boolean indexing dengan assignment
a[a < 5] = 0
print("Setelah a[a < 5] = 0:")
print(a)
# Output:
# [[ 0  0  0  0]
#  [ 0  0  6  7]
#  [ 8  9 10 11]]
```

## Fancy Indexing

Fancy indexing menggunakan array integer untuk mengakses elemen dengan urutan atau pola tertentu. Teknik ini sangat berguna untuk data sampling dan reorganisasi.

File: fancy_indexing_1d.py
```python
import numpy as np

# Array untuk demonstrasi
a = np.array([10, 20, 30, 40, 50, 60])
print("Array:", a)  # Output: Array: [10 20 30 40 50 60]

# Fancy indexing dengan list indeks
indices = [0, 2, 4, 1]
print("Indices:", indices)  # Output: Indices: [0, 2, 4, 1]
print("a[indices]:", a[indices])  # Output: a[indices]: [10 30 50 20]

# Fancy indexing dengan array NumPy
np_indices = np.array([5, 1, 3, 1, 0])
print("NumPy indices:", np_indices)  # Output: NumPy indices: [5 1 3 1 0]
print("a[np_indices]:", a[np_indices])  # Output: a[np_indices]: [60 20 40 20 10]

# Fancy indexing dengan array 2D indeks
indices_2d = np.array([[0, 1], [2, 3]])
print("Indices 2D:")
print(indices_2d)
# Output:
# [[0 1]
#  [2 3]]
print("a[indices_2d]:")
print(a[indices_2d])
# Output:
# [[10 20]
#  [30 40]]
```

Fancy indexing pada array dua dimensi memungkinkan seleksi baris dan kolom dengan pola yang kompleks:

File: fancy_indexing_2d.py
```python
import numpy as np

# Array 2D untuk demonstrasi
a = np.arange(24).reshape(4, 6)
print("Array 2D:")
print(a)
# Output:
# [[ 0  1  2  3  4  5]
#  [ 6  7  8  9 10 11]
#  [12 13 14 15 16 17]
#  [18 19 20 21 22 23]]

# Fancy indexing untuk baris tertentu
row_indices = [0, 2, 3]
print("a[row_indices, :]:")
print(a[row_indices, :])
# Output:
# [[ 0  1  2  3  4  5]
#  [12 13 14 15 16 17]
#  [18 19 20 21 22 23]]

# Fancy indexing untuk elemen spesifik
row_idx = [0, 1, 2, 3]
col_idx = [1, 2, 3, 4]
print("a[row_idx, col_idx]:", a[row_idx, col_idx])  # Output: a[row_idx, col_idx]: [ 1  8 15 22]

# Kombinasi fancy indexing dengan slicing
print("a[[0, 2], 1:4]:")
print(a[[0, 2], 1:4])
# Output:
# [[ 1  2  3]
#  [13 14 15]]
```

## Aplikasi Praktis

Indexing dan slicing memiliki banyak aplikasi praktis dalam analisis data dan machine learning.

Teknik filtering sangat berguna untuk preprocessing data dalam machine learning dan analisis statistik:

File: data_filtering.py
```python
import numpy as np

# Simulasi data sensor suhu
temperatures = np.array([22.5, 25.1, 19.8, 30.2, 18.5, 27.3, 31.1, 24.8])
print("Data suhu:", temperatures)  # Output: Data suhu: [22.5 25.1 19.8 30.2 18.5 27.3 31.1 24.8]

# Filter suhu normal (20-28 derajat)
normal_temp_mask = (temperatures >= 20) & (temperatures <= 28)
normal_temps = temperatures[normal_temp_mask]
print("Suhu normal:", normal_temps)  # Output: Suhu normal: [22.5 25.1 27.3 24.8]

# Filter suhu ekstrem
extreme_temp_mask = (temperatures < 20) | (temperatures > 30)
extreme_temps = temperatures[extreme_temp_mask]
print("Suhu ekstrem:", extreme_temps)  # Output: Suhu ekstrem: [19.8 30.2 18.5 31.1]

# Mengganti nilai ekstrem dengan nilai rata-rata
mean_temp = temperatures[normal_temp_mask].mean()
temperatures_cleaned = temperatures.copy()
temperatures_cleaned[extreme_temp_mask] = mean_temp
print("Data setelah cleaning:", temperatures_cleaned)  # Output: Data setelah cleaning: [22.5   25.1   24.925 24.925 24.925 27.3   24.925 24.8  ]
```

Sampling dan reorganisasi data adalah teknik penting untuk machine learning dan analisis dataset besar:

File: data_sampling.py
```python
import numpy as np

# Dataset simulasi
data = np.arange(100).reshape(10, 10)
print("Dataset shape:", data.shape)  # Output: Dataset shape: (10, 10)

# Random sampling baris
np.random.seed(42)
sample_indices = np.random.choice(10, size=5, replace=False)
sample_indices.sort()
print("Sample indices:", sample_indices)  # Output: Sample indices: [0 1 5 7 8]

sampled_data = data[sample_indices, :]
print("Sampled data shape:", sampled_data.shape)  # Output: Sampled data shape: (5, 10)
print("First 3 columns of sampled data:")
print(sampled_data[:, :3])
# Output:
# [[ 0  1  2]
#  [10 11 12]
#  [50 51 52]
#  [70 71 72]
#  [80 81 82]]

# Reorganisasi kolom
column_order = [9, 0, 5, 2, 7, 1, 8, 3, 6, 4]
reorganized_data = data[:, column_order]
print("Reorganized first row:", reorganized_data[0, :])  # Output: Reorganized first row: [9 0 5 2 7 1 8 3 6 4]
```