K-Means Clustering#

K-Means clustering merupakan salah satu metode dalam unsupervised learning yang digunakan untuk mengelompokkan data ke dalam beberapa kelompok (cluster) berdasarkan kesamaan karakteristik atau fitur. Tujuan utamanya adalah untuk membagi data ke dalam K kelompok yang saling terpisah, dimana dalam kelompok data bersifat homogen dan antar kelompok data bersifat heterogen.

Langkah-langkah algoritma K-Means :#

  1. Menentukan jumlah kluster sebanyak \(K\)

  • Artinya menentukan berapa banyak kelompok/kluster yang ingin dibuat dari data. Misalnya \(K\) = 3 maka membagi data menjadi 3 kluster.

  • Pemilihan nilai \(K\) dapat menggunakan metode seperti Elbow Method untuk membantu menentukan \(K\) yang optimal.

  1. Memilih K centroid awal secara acak

  • Artinya menentukan titik awal (centroid) sebagai pusat dari masing-masing kluster.

  • Centroid bisa dipilih secara acak dari data yang ada atau menggunakan teknik seperti K-Means++ dimana dijalankan secara otomatis pada scikit-learn untuk hasil centroid yang lebih baik.

  1. Menghitung jarak setiap data ke tiap centroid

  • Artinya menentukan seberapa dekat setiap data ke setiap centroid.

  • Umumnya menggunakan Euclidian distance.

  1. Mengelompokkan data berdasarkan centroid terdekat

  • Artinya mengklasifikasikan setiap data ke dalam salah satu dari \(K\) kluster.

  • Setiap data ditempatkan ke kluster yang pusatnya (centroid-nya) paling dekat dengannya. Jadi, tiap data hanya masuk ke satu kluster.

  1. Menghitung rata-rata dari anggota kluster, untuk menemukan centroid baru

  • Menentukan pusat kluster baru berdasarkan data yang saat ini berada dalam kluster tersebut.

  • Centroid baru adalah rata-rata dari semua data di dalam suatu kluster.

  1. Mengulang langkah nomor 3-5 sampai data pada setiap kluster tidak berubah signifikan.

  • Iterasi dilakukan sampai tidak ada lagi data yang berpindah kluster, atau perubahan posisi centroid sangat kecil.

Evaluasi Hasil K-Means#

  1. Inertia (Sum Square Error/SSE)
    Inertia mengukur sejauh mana titik data dalam suatu cluster tersebar dari pusat cluster tersebut. Semakin kecil nilai inertia, klaster semakin baik.

\[Inertia= \sum_{i=1}^{n} \left\| x_i - \mu_{c_i} \right\|^2\]

Keterangan :
\(x_i\) : Titik data ke-\(i\)
\(\mu_{c_i}\) : Centroid dari cluster tempat \(x_i\) berada

  • Nilai inertia (sangat kecil/mendekati 0) : Klaster sangat kompak dan semua titik dekat dengan centroidnya (sangat baik).

  • Nilai inertia (kecil-sedang) : Klaster cukup baik dan dapat diterima dalam banyak kasus.

  • Nilai inertia (besar) : Klaster tidak rapat, data menyebar jauh dari centroid → bisa jadi jumlah klaster (k) kurang tepat atau K-Means tidak cocok digunakan.

  1. Silhouette Method : Sebuah metode yang mengukur tingkat kemiripan suatu point dengan clusternya dibandingkan dengan kluster lain.

  • untuk setiap titik \(i\), silhouette (\(i\)) dihitung sebagai :

\[s(i) = \frac{b(i) - a(i)}{\max\{a(i), b(i)\}}\]

Keterangan :
\(a(i)\) : rata-rata jarak titik \(i\) ke semua titik lain dalam klaster yang sama.
\(b(i)\) : rata-rata jarak titik \(i\) ke semua titik dalam klaster terdekat (tetangga).

  • Nilai silhouette mendekati angka 1 artinya suatu data berada pada cluster yang benar dengan jarak jauh dari cluster lain.

  • Nilai silhouette negatif artinya data tersebut lebih baik berada di kluster lain daripada kluster saat ini.

  • Nilai silhouette = 0, artinya data tersebut di perbatasan antara dua cluster.

Komputasi K-Means Data IRIS#

2 Klaster#

from sklearn.cluster import KMeans
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import silhouette_score, accuracy_score, confusion_matrix
import pandas as pd

# Baca data fitur dan label
df_features = pd.read_excel("data_iris.xlsx")
df_class = pd.read_excel("class.xlsx")  # kolom 'class'

df = df_features.copy()
df['class'] = df_class['class']

# Ambil fitur untuk clustering
features = df[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']]

# Normalisasi fitur(MinMax)
scaler = MinMaxScaler()
scaled_features = scaler.fit_transform(features)

# KMeans clustering
K = 2
kmeans = KMeans(n_clusters=K, max_iter=300, tol=0.0001, random_state=42, n_init='auto')
kmeans.fit(scaled_features)

df['cluster'] = kmeans.labels_

print(f"Jumlah iterasi sampai konvergen: {kmeans.n_iter_}")
print(f"Inertia (SSE): {kmeans.inertia_:.4f}")
sil_score = silhouette_score(scaled_features, kmeans.labels_)
print(f"Silhouette Score: {sil_score:.4f}")

# Mapping cluster ke class mayoritas
mapping = (
    df.groupby('cluster')['class']
    .agg(lambda x: x.mode()[0])
    .to_dict()
)
df['predicted_class'] = df['cluster'].map(mapping)

# Evaluasi
y_true = df['class']
y_pred = df['predicted_class']

acc = accuracy_score(y_true, y_pred)
print(f"\nAkurasi keseluruhan clustering terhadap label asli: {acc:.4%}")

# Distribusi cluster per kelas (untuk insight)
dist = pd.crosstab(df['class'], df['cluster'], rownames=['Class'], colnames=['Cluster'])
print("\nDistribusi cluster per kelas:")
print(dist)

# Simpan hasil ke Excel
df.to_excel("hasil2.xlsx", index=False)

# Kalau ingin tampil semua baris (hati-hati jika data sangat banyak)
pd.set_option('display.max_rows', None)
print(df[['class', 'cluster', 'predicted_class']])
Jumlah iterasi sampai konvergen: 4
Inertia (SSE): 12.1437
Silhouette Score: 0.6295

Akurasi keseluruhan clustering terhadap label asli: 66.6667%

Distribusi cluster per kelas:
Cluster           0   1
Class                  
Iris-setosa       0  50
Iris-versicolor  50   0
Iris-virginica   50   0
               class  cluster  predicted_class
0        Iris-setosa        1      Iris-setosa
1        Iris-setosa        1      Iris-setosa
2        Iris-setosa        1      Iris-setosa
3        Iris-setosa        1      Iris-setosa
4        Iris-setosa        1      Iris-setosa
5        Iris-setosa        1      Iris-setosa
6        Iris-setosa        1      Iris-setosa
7        Iris-setosa        1      Iris-setosa
8        Iris-setosa        1      Iris-setosa
9        Iris-setosa        1      Iris-setosa
10       Iris-setosa        1      Iris-setosa
11       Iris-setosa        1      Iris-setosa
12       Iris-setosa        1      Iris-setosa
13       Iris-setosa        1      Iris-setosa
14       Iris-setosa        1      Iris-setosa
15       Iris-setosa        1      Iris-setosa
16       Iris-setosa        1      Iris-setosa
17       Iris-setosa        1      Iris-setosa
18       Iris-setosa        1      Iris-setosa
19       Iris-setosa        1      Iris-setosa
20       Iris-setosa        1      Iris-setosa
21       Iris-setosa        1      Iris-setosa
22       Iris-setosa        1      Iris-setosa
23       Iris-setosa        1      Iris-setosa
24       Iris-setosa        1      Iris-setosa
25       Iris-setosa        1      Iris-setosa
26       Iris-setosa        1      Iris-setosa
27       Iris-setosa        1      Iris-setosa
28       Iris-setosa        1      Iris-setosa
29       Iris-setosa        1      Iris-setosa
30       Iris-setosa        1      Iris-setosa
31       Iris-setosa        1      Iris-setosa
32       Iris-setosa        1      Iris-setosa
33       Iris-setosa        1      Iris-setosa
34       Iris-setosa        1      Iris-setosa
35       Iris-setosa        1      Iris-setosa
36       Iris-setosa        1      Iris-setosa
37       Iris-setosa        1      Iris-setosa
38       Iris-setosa        1      Iris-setosa
39       Iris-setosa        1      Iris-setosa
40       Iris-setosa        1      Iris-setosa
41       Iris-setosa        1      Iris-setosa
42       Iris-setosa        1      Iris-setosa
43       Iris-setosa        1      Iris-setosa
44       Iris-setosa        1      Iris-setosa
45       Iris-setosa        1      Iris-setosa
46       Iris-setosa        1      Iris-setosa
47       Iris-setosa        1      Iris-setosa
48       Iris-setosa        1      Iris-setosa
49       Iris-setosa        1      Iris-setosa
50   Iris-versicolor        0  Iris-versicolor
51   Iris-versicolor        0  Iris-versicolor
52   Iris-versicolor        0  Iris-versicolor
53   Iris-versicolor        0  Iris-versicolor
54   Iris-versicolor        0  Iris-versicolor
55   Iris-versicolor        0  Iris-versicolor
56   Iris-versicolor        0  Iris-versicolor
57   Iris-versicolor        0  Iris-versicolor
58   Iris-versicolor        0  Iris-versicolor
59   Iris-versicolor        0  Iris-versicolor
60   Iris-versicolor        0  Iris-versicolor
61   Iris-versicolor        0  Iris-versicolor
62   Iris-versicolor        0  Iris-versicolor
63   Iris-versicolor        0  Iris-versicolor
64   Iris-versicolor        0  Iris-versicolor
65   Iris-versicolor        0  Iris-versicolor
66   Iris-versicolor        0  Iris-versicolor
67   Iris-versicolor        0  Iris-versicolor
68   Iris-versicolor        0  Iris-versicolor
69   Iris-versicolor        0  Iris-versicolor
70   Iris-versicolor        0  Iris-versicolor
71   Iris-versicolor        0  Iris-versicolor
72   Iris-versicolor        0  Iris-versicolor
73   Iris-versicolor        0  Iris-versicolor
74   Iris-versicolor        0  Iris-versicolor
75   Iris-versicolor        0  Iris-versicolor
76   Iris-versicolor        0  Iris-versicolor
77   Iris-versicolor        0  Iris-versicolor
78   Iris-versicolor        0  Iris-versicolor
79   Iris-versicolor        0  Iris-versicolor
80   Iris-versicolor        0  Iris-versicolor
81   Iris-versicolor        0  Iris-versicolor
82   Iris-versicolor        0  Iris-versicolor
83   Iris-versicolor        0  Iris-versicolor
84   Iris-versicolor        0  Iris-versicolor
85   Iris-versicolor        0  Iris-versicolor
86   Iris-versicolor        0  Iris-versicolor
87   Iris-versicolor        0  Iris-versicolor
88   Iris-versicolor        0  Iris-versicolor
89   Iris-versicolor        0  Iris-versicolor
90   Iris-versicolor        0  Iris-versicolor
91   Iris-versicolor        0  Iris-versicolor
92   Iris-versicolor        0  Iris-versicolor
93   Iris-versicolor        0  Iris-versicolor
94   Iris-versicolor        0  Iris-versicolor
95   Iris-versicolor        0  Iris-versicolor
96   Iris-versicolor        0  Iris-versicolor
97   Iris-versicolor        0  Iris-versicolor
98   Iris-versicolor        0  Iris-versicolor
99   Iris-versicolor        0  Iris-versicolor
100   Iris-virginica        0  Iris-versicolor
101   Iris-virginica        0  Iris-versicolor
102   Iris-virginica        0  Iris-versicolor
103   Iris-virginica        0  Iris-versicolor
104   Iris-virginica        0  Iris-versicolor
105   Iris-virginica        0  Iris-versicolor
106   Iris-virginica        0  Iris-versicolor
107   Iris-virginica        0  Iris-versicolor
108   Iris-virginica        0  Iris-versicolor
109   Iris-virginica        0  Iris-versicolor
110   Iris-virginica        0  Iris-versicolor
111   Iris-virginica        0  Iris-versicolor
112   Iris-virginica        0  Iris-versicolor
113   Iris-virginica        0  Iris-versicolor
114   Iris-virginica        0  Iris-versicolor
115   Iris-virginica        0  Iris-versicolor
116   Iris-virginica        0  Iris-versicolor
117   Iris-virginica        0  Iris-versicolor
118   Iris-virginica        0  Iris-versicolor
119   Iris-virginica        0  Iris-versicolor
120   Iris-virginica        0  Iris-versicolor
121   Iris-virginica        0  Iris-versicolor
122   Iris-virginica        0  Iris-versicolor
123   Iris-virginica        0  Iris-versicolor
124   Iris-virginica        0  Iris-versicolor
125   Iris-virginica        0  Iris-versicolor
126   Iris-virginica        0  Iris-versicolor
127   Iris-virginica        0  Iris-versicolor
128   Iris-virginica        0  Iris-versicolor
129   Iris-virginica        0  Iris-versicolor
130   Iris-virginica        0  Iris-versicolor
131   Iris-virginica        0  Iris-versicolor
132   Iris-virginica        0  Iris-versicolor
133   Iris-virginica        0  Iris-versicolor
134   Iris-virginica        0  Iris-versicolor
135   Iris-virginica        0  Iris-versicolor
136   Iris-virginica        0  Iris-versicolor
137   Iris-virginica        0  Iris-versicolor
138   Iris-virginica        0  Iris-versicolor
139   Iris-virginica        0  Iris-versicolor
140   Iris-virginica        0  Iris-versicolor
141   Iris-virginica        0  Iris-versicolor
142   Iris-virginica        0  Iris-versicolor
143   Iris-virginica        0  Iris-versicolor
144   Iris-virginica        0  Iris-versicolor
145   Iris-virginica        0  Iris-versicolor
146   Iris-virginica        0  Iris-versicolor
147   Iris-virginica        0  Iris-versicolor
148   Iris-virginica        0  Iris-versicolor
149   Iris-virginica        0  Iris-versicolor

3 Klaster#

from sklearn.cluster import KMeans
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import silhouette_score, accuracy_score, confusion_matrix
import pandas as pd

# Baca data fitur dan label
df_features = pd.read_excel("data_iris.xlsx")
df_class    = pd.read_excel("class.xlsx")  # kolom 'class'

df = df_features.copy()
df['class'] = df_class['class']

# Ambil fitur untuk clustering
features = df[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']]

# Normalisasi fitur (MinMax)
scaler = MinMaxScaler()
scaled_features = scaler.fit_transform(features)

# KMeans clustering
K = 3
kmeans = KMeans(
    n_clusters=K,
    max_iter=300,
    tol=0.0001,
    random_state=42,
    n_init='auto'  # ganti ke 10 jika sklearn <1.4
)
kmeans.fit(scaled_features)
df['cluster'] = kmeans.labels_

# Cetak metrik clustering
print(f"Jumlah iterasi sampai konvergen: {kmeans.n_iter_}")
print(f"Inertia (SSE): {kmeans.inertia_:.4f}")
sil_score = silhouette_score(scaled_features, kmeans.labels_)
print(f"Silhouette Score: {sil_score:.4f}")

# Mapping cluster ke class mayoritas
mapping = (
    df.groupby('cluster')['class']
      .agg(lambda x: x.mode()[0])
      .to_dict()
)
df['predicted_class'] = df['cluster'].map(mapping)

# Siapkan untuk evaluasi
y_true   = df['class']
y_pred   = df['predicted_class']
classes  = sorted(df['class'].unique())
cm       = confusion_matrix(y_true, y_pred, labels=classes)

# Akurasi keseluruhan
acc = accuracy_score(y_true, y_pred)
print(f"\nAkurasi keseluruhan clustering terhadap label asli: {acc:.4%}")

# Persentase error per kelas
error_per_class = {}
for i, c in enumerate(classes):
    total   = cm[i].sum()
    correct = cm[i, i]
    error   = 1 - correct/total if total > 0 else 0
    error_per_class[c] = error

print("\nPersentase kesalahan per kelas:")
for c, e in error_per_class.items():
    print(f" - {c}: {e:.2%}")

# Distribusi cluster per kelas (insight)
dist = pd.crosstab(df['class'], df['cluster'], 
                   rownames=['Class'], colnames=['Cluster'])
print("\nDistribusi cluster per kelas:")
print(dist)

# Simpan hasil lengkap ke Excel
df.to_excel("hasil_clustering_dengan_class_lengkap.xlsx", index=False)

# Tampilkan perbandingan class vs cluster vs predicted_class
pd.set_option('display.max_rows', None)
print("\nPerbandingan lengkap:")
print(df[['class', 'cluster', 'predicted_class']])
Jumlah iterasi sampai konvergen: 3
Inertia (SSE): 7.1386
Silhouette Score: 0.4825

Akurasi keseluruhan clustering terhadap label asli: 88.0000%

Persentase kesalahan per kelas:
 - Iris-setosa: 0.00%
 - Iris-versicolor: 20.00%
 - Iris-virginica: 16.00%

Distribusi cluster per kelas:
Cluster           0   1   2
Class                      
Iris-setosa       0  50   0
Iris-versicolor  10   0  40
Iris-virginica   42   0   8

Perbandingan lengkap:
               class  cluster  predicted_class
0        Iris-setosa        1      Iris-setosa
1        Iris-setosa        1      Iris-setosa
2        Iris-setosa        1      Iris-setosa
3        Iris-setosa        1      Iris-setosa
4        Iris-setosa        1      Iris-setosa
5        Iris-setosa        1      Iris-setosa
6        Iris-setosa        1      Iris-setosa
7        Iris-setosa        1      Iris-setosa
8        Iris-setosa        1      Iris-setosa
9        Iris-setosa        1      Iris-setosa
10       Iris-setosa        1      Iris-setosa
11       Iris-setosa        1      Iris-setosa
12       Iris-setosa        1      Iris-setosa
13       Iris-setosa        1      Iris-setosa
14       Iris-setosa        1      Iris-setosa
15       Iris-setosa        1      Iris-setosa
16       Iris-setosa        1      Iris-setosa
17       Iris-setosa        1      Iris-setosa
18       Iris-setosa        1      Iris-setosa
19       Iris-setosa        1      Iris-setosa
20       Iris-setosa        1      Iris-setosa
21       Iris-setosa        1      Iris-setosa
22       Iris-setosa        1      Iris-setosa
23       Iris-setosa        1      Iris-setosa
24       Iris-setosa        1      Iris-setosa
25       Iris-setosa        1      Iris-setosa
26       Iris-setosa        1      Iris-setosa
27       Iris-setosa        1      Iris-setosa
28       Iris-setosa        1      Iris-setosa
29       Iris-setosa        1      Iris-setosa
30       Iris-setosa        1      Iris-setosa
31       Iris-setosa        1      Iris-setosa
32       Iris-setosa        1      Iris-setosa
33       Iris-setosa        1      Iris-setosa
34       Iris-setosa        1      Iris-setosa
35       Iris-setosa        1      Iris-setosa
36       Iris-setosa        1      Iris-setosa
37       Iris-setosa        1      Iris-setosa
38       Iris-setosa        1      Iris-setosa
39       Iris-setosa        1      Iris-setosa
40       Iris-setosa        1      Iris-setosa
41       Iris-setosa        1      Iris-setosa
42       Iris-setosa        1      Iris-setosa
43       Iris-setosa        1      Iris-setosa
44       Iris-setosa        1      Iris-setosa
45       Iris-setosa        1      Iris-setosa
46       Iris-setosa        1      Iris-setosa
47       Iris-setosa        1      Iris-setosa
48       Iris-setosa        1      Iris-setosa
49       Iris-setosa        1      Iris-setosa
50   Iris-versicolor        0   Iris-virginica
51   Iris-versicolor        0   Iris-virginica
52   Iris-versicolor        0   Iris-virginica
53   Iris-versicolor        2  Iris-versicolor
54   Iris-versicolor        2  Iris-versicolor
55   Iris-versicolor        2  Iris-versicolor
56   Iris-versicolor        0   Iris-virginica
57   Iris-versicolor        2  Iris-versicolor
58   Iris-versicolor        2  Iris-versicolor
59   Iris-versicolor        2  Iris-versicolor
60   Iris-versicolor        2  Iris-versicolor
61   Iris-versicolor        2  Iris-versicolor
62   Iris-versicolor        2  Iris-versicolor
63   Iris-versicolor        2  Iris-versicolor
64   Iris-versicolor        2  Iris-versicolor
65   Iris-versicolor        0   Iris-virginica
66   Iris-versicolor        2  Iris-versicolor
67   Iris-versicolor        2  Iris-versicolor
68   Iris-versicolor        2  Iris-versicolor
69   Iris-versicolor        2  Iris-versicolor
70   Iris-versicolor        0   Iris-virginica
71   Iris-versicolor        2  Iris-versicolor
72   Iris-versicolor        2  Iris-versicolor
73   Iris-versicolor        2  Iris-versicolor
74   Iris-versicolor        2  Iris-versicolor
75   Iris-versicolor        2  Iris-versicolor
76   Iris-versicolor        0   Iris-virginica
77   Iris-versicolor        0   Iris-virginica
78   Iris-versicolor        2  Iris-versicolor
79   Iris-versicolor        2  Iris-versicolor
80   Iris-versicolor        2  Iris-versicolor
81   Iris-versicolor        2  Iris-versicolor
82   Iris-versicolor        2  Iris-versicolor
83   Iris-versicolor        2  Iris-versicolor
84   Iris-versicolor        2  Iris-versicolor
85   Iris-versicolor        0   Iris-virginica
86   Iris-versicolor        0   Iris-virginica
87   Iris-versicolor        2  Iris-versicolor
88   Iris-versicolor        2  Iris-versicolor
89   Iris-versicolor        2  Iris-versicolor
90   Iris-versicolor        2  Iris-versicolor
91   Iris-versicolor        2  Iris-versicolor
92   Iris-versicolor        2  Iris-versicolor
93   Iris-versicolor        2  Iris-versicolor
94   Iris-versicolor        2  Iris-versicolor
95   Iris-versicolor        2  Iris-versicolor
96   Iris-versicolor        2  Iris-versicolor
97   Iris-versicolor        2  Iris-versicolor
98   Iris-versicolor        2  Iris-versicolor
99   Iris-versicolor        2  Iris-versicolor
100   Iris-virginica        0   Iris-virginica
101   Iris-virginica        2  Iris-versicolor
102   Iris-virginica        0   Iris-virginica
103   Iris-virginica        0   Iris-virginica
104   Iris-virginica        0   Iris-virginica
105   Iris-virginica        0   Iris-virginica
106   Iris-virginica        2  Iris-versicolor
107   Iris-virginica        0   Iris-virginica
108   Iris-virginica        0   Iris-virginica
109   Iris-virginica        0   Iris-virginica
110   Iris-virginica        0   Iris-virginica
111   Iris-virginica        0   Iris-virginica
112   Iris-virginica        0   Iris-virginica
113   Iris-virginica        2  Iris-versicolor
114   Iris-virginica        0   Iris-virginica
115   Iris-virginica        0   Iris-virginica
116   Iris-virginica        0   Iris-virginica
117   Iris-virginica        0   Iris-virginica
118   Iris-virginica        0   Iris-virginica
119   Iris-virginica        2  Iris-versicolor
120   Iris-virginica        0   Iris-virginica
121   Iris-virginica        2  Iris-versicolor
122   Iris-virginica        0   Iris-virginica
123   Iris-virginica        0   Iris-virginica
124   Iris-virginica        0   Iris-virginica
125   Iris-virginica        0   Iris-virginica
126   Iris-virginica        0   Iris-virginica
127   Iris-virginica        0   Iris-virginica
128   Iris-virginica        0   Iris-virginica
129   Iris-virginica        0   Iris-virginica
130   Iris-virginica        0   Iris-virginica
131   Iris-virginica        0   Iris-virginica
132   Iris-virginica        0   Iris-virginica
133   Iris-virginica        2  Iris-versicolor
134   Iris-virginica        2  Iris-versicolor
135   Iris-virginica        0   Iris-virginica
136   Iris-virginica        0   Iris-virginica
137   Iris-virginica        0   Iris-virginica
138   Iris-virginica        0   Iris-virginica
139   Iris-virginica        0   Iris-virginica
140   Iris-virginica        0   Iris-virginica
141   Iris-virginica        0   Iris-virginica
142   Iris-virginica        2  Iris-versicolor
143   Iris-virginica        0   Iris-virginica
144   Iris-virginica        0   Iris-virginica
145   Iris-virginica        0   Iris-virginica
146   Iris-virginica        0   Iris-virginica
147   Iris-virginica        0   Iris-virginica
148   Iris-virginica        0   Iris-virginica
149   Iris-virginica        0   Iris-virginica

4 Klaster#

from sklearn.cluster import KMeans
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import silhouette_score, accuracy_score, confusion_matrix
import pandas as pd

# Baca data fitur dan label
df_features = pd.read_excel("data_iris.xlsx")
df_class = pd.read_excel("class.xlsx")  # kolom 'class'

df = df_features.copy()
df['class'] = df_class['class']

# Ambil fitur untuk clustering
features = df[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']]

# Normalisasi fitur(MinMax)
scaler = MinMaxScaler()
scaled_features = scaler.fit_transform(features)

# KMeans clustering
K = 4
kmeans = KMeans(n_clusters=K, max_iter=300, tol=0.0001, random_state=42, n_init='auto')
kmeans.fit(scaled_features)

df['cluster'] = kmeans.labels_

print(f"Jumlah iterasi sampai konvergen: {kmeans.n_iter_}")
print(f"Inertia (SSE): {kmeans.inertia_:.4f}")
sil_score = silhouette_score(scaled_features, kmeans.labels_)
print(f"Silhouette Score: {sil_score:.4f}")

# Mapping cluster ke class mayoritas
mapping = (
    df.groupby('cluster')['class']
    .agg(lambda x: x.mode()[0])
    .to_dict()
)
df['predicted_class'] = df['cluster'].map(mapping)

# Evaluasi
y_true = df['class']
y_pred = df['predicted_class']

acc = accuracy_score(y_true, y_pred)
print(f"\nAkurasi keseluruhan clustering terhadap label asli: {acc:.4%}")

# Distribusi cluster per kelas (untuk insight)
dist = pd.crosstab(df['class'], df['cluster'], rownames=['Class'], colnames=['Cluster'])
print("\nDistribusi cluster per kelas:")
print(dist)

# Simpan hasil ke Excel
df.to_excel("hasil4.xlsx", index=False)

# Kalau ingin tampil semua baris (hati-hati jika data sangat banyak)
pd.set_option('display.max_rows', None)
print(df[['class', 'cluster', 'predicted_class']])
Jumlah iterasi sampai konvergen: 6
Inertia (SSE): 5.5417
Silhouette Score: 0.4435

Akurasi keseluruhan clustering terhadap label asli: 84.6667%

Distribusi cluster per kelas:
Cluster           0   1   2   3
Class                          
Iris-setosa       0  50   0   0
Iris-versicolor  21   0  29   0
Iris-virginica   21   0   2  27
               class  cluster  predicted_class
0        Iris-setosa        1      Iris-setosa
1        Iris-setosa        1      Iris-setosa
2        Iris-setosa        1      Iris-setosa
3        Iris-setosa        1      Iris-setosa
4        Iris-setosa        1      Iris-setosa
5        Iris-setosa        1      Iris-setosa
6        Iris-setosa        1      Iris-setosa
7        Iris-setosa        1      Iris-setosa
8        Iris-setosa        1      Iris-setosa
9        Iris-setosa        1      Iris-setosa
10       Iris-setosa        1      Iris-setosa
11       Iris-setosa        1      Iris-setosa
12       Iris-setosa        1      Iris-setosa
13       Iris-setosa        1      Iris-setosa
14       Iris-setosa        1      Iris-setosa
15       Iris-setosa        1      Iris-setosa
16       Iris-setosa        1      Iris-setosa
17       Iris-setosa        1      Iris-setosa
18       Iris-setosa        1      Iris-setosa
19       Iris-setosa        1      Iris-setosa
20       Iris-setosa        1      Iris-setosa
21       Iris-setosa        1      Iris-setosa
22       Iris-setosa        1      Iris-setosa
23       Iris-setosa        1      Iris-setosa
24       Iris-setosa        1      Iris-setosa
25       Iris-setosa        1      Iris-setosa
26       Iris-setosa        1      Iris-setosa
27       Iris-setosa        1      Iris-setosa
28       Iris-setosa        1      Iris-setosa
29       Iris-setosa        1      Iris-setosa
30       Iris-setosa        1      Iris-setosa
31       Iris-setosa        1      Iris-setosa
32       Iris-setosa        1      Iris-setosa
33       Iris-setosa        1      Iris-setosa
34       Iris-setosa        1      Iris-setosa
35       Iris-setosa        1      Iris-setosa
36       Iris-setosa        1      Iris-setosa
37       Iris-setosa        1      Iris-setosa
38       Iris-setosa        1      Iris-setosa
39       Iris-setosa        1      Iris-setosa
40       Iris-setosa        1      Iris-setosa
41       Iris-setosa        1      Iris-setosa
42       Iris-setosa        1      Iris-setosa
43       Iris-setosa        1      Iris-setosa
44       Iris-setosa        1      Iris-setosa
45       Iris-setosa        1      Iris-setosa
46       Iris-setosa        1      Iris-setosa
47       Iris-setosa        1      Iris-setosa
48       Iris-setosa        1      Iris-setosa
49       Iris-setosa        1      Iris-setosa
50   Iris-versicolor        0  Iris-versicolor
51   Iris-versicolor        0  Iris-versicolor
52   Iris-versicolor        0  Iris-versicolor
53   Iris-versicolor        2  Iris-versicolor
54   Iris-versicolor        0  Iris-versicolor
55   Iris-versicolor        2  Iris-versicolor
56   Iris-versicolor        0  Iris-versicolor
57   Iris-versicolor        2  Iris-versicolor
58   Iris-versicolor        0  Iris-versicolor
59   Iris-versicolor        2  Iris-versicolor
60   Iris-versicolor        2  Iris-versicolor
61   Iris-versicolor        0  Iris-versicolor
62   Iris-versicolor        2  Iris-versicolor
63   Iris-versicolor        0  Iris-versicolor
64   Iris-versicolor        2  Iris-versicolor
65   Iris-versicolor        0  Iris-versicolor
66   Iris-versicolor        2  Iris-versicolor
67   Iris-versicolor        2  Iris-versicolor
68   Iris-versicolor        2  Iris-versicolor
69   Iris-versicolor        2  Iris-versicolor
70   Iris-versicolor        0  Iris-versicolor
71   Iris-versicolor        2  Iris-versicolor
72   Iris-versicolor        0  Iris-versicolor
73   Iris-versicolor        2  Iris-versicolor
74   Iris-versicolor        0  Iris-versicolor
75   Iris-versicolor        0  Iris-versicolor
76   Iris-versicolor        0  Iris-versicolor
77   Iris-versicolor        0  Iris-versicolor
78   Iris-versicolor        0  Iris-versicolor
79   Iris-versicolor        2  Iris-versicolor
80   Iris-versicolor        2  Iris-versicolor
81   Iris-versicolor        2  Iris-versicolor
82   Iris-versicolor        2  Iris-versicolor
83   Iris-versicolor        0  Iris-versicolor
84   Iris-versicolor        2  Iris-versicolor
85   Iris-versicolor        0  Iris-versicolor
86   Iris-versicolor        0  Iris-versicolor
87   Iris-versicolor        2  Iris-versicolor
88   Iris-versicolor        2  Iris-versicolor
89   Iris-versicolor        2  Iris-versicolor
90   Iris-versicolor        2  Iris-versicolor
91   Iris-versicolor        0  Iris-versicolor
92   Iris-versicolor        2  Iris-versicolor
93   Iris-versicolor        2  Iris-versicolor
94   Iris-versicolor        2  Iris-versicolor
95   Iris-versicolor        2  Iris-versicolor
96   Iris-versicolor        2  Iris-versicolor
97   Iris-versicolor        0  Iris-versicolor
98   Iris-versicolor        2  Iris-versicolor
99   Iris-versicolor        2  Iris-versicolor
100   Iris-virginica        3   Iris-virginica
101   Iris-virginica        0  Iris-versicolor
102   Iris-virginica        3   Iris-virginica
103   Iris-virginica        0  Iris-versicolor
104   Iris-virginica        3   Iris-virginica
105   Iris-virginica        3   Iris-virginica
106   Iris-virginica        2  Iris-versicolor
107   Iris-virginica        3   Iris-virginica
108   Iris-virginica        0  Iris-versicolor
109   Iris-virginica        3   Iris-virginica
110   Iris-virginica        0  Iris-versicolor
111   Iris-virginica        0  Iris-versicolor
112   Iris-virginica        3   Iris-virginica
113   Iris-virginica        0  Iris-versicolor
114   Iris-virginica        0  Iris-versicolor
115   Iris-virginica        3   Iris-virginica
116   Iris-virginica        0  Iris-versicolor
117   Iris-virginica        3   Iris-virginica
118   Iris-virginica        3   Iris-virginica
119   Iris-virginica        2  Iris-versicolor
120   Iris-virginica        3   Iris-virginica
121   Iris-virginica        0  Iris-versicolor
122   Iris-virginica        3   Iris-virginica
123   Iris-virginica        0  Iris-versicolor
124   Iris-virginica        3   Iris-virginica
125   Iris-virginica        3   Iris-virginica
126   Iris-virginica        0  Iris-versicolor
127   Iris-virginica        0  Iris-versicolor
128   Iris-virginica        0  Iris-versicolor
129   Iris-virginica        3   Iris-virginica
130   Iris-virginica        3   Iris-virginica
131   Iris-virginica        3   Iris-virginica
132   Iris-virginica        3   Iris-virginica
133   Iris-virginica        0  Iris-versicolor
134   Iris-virginica        0  Iris-versicolor
135   Iris-virginica        3   Iris-virginica
136   Iris-virginica        3   Iris-virginica
137   Iris-virginica        0  Iris-versicolor
138   Iris-virginica        0  Iris-versicolor
139   Iris-virginica        3   Iris-virginica
140   Iris-virginica        3   Iris-virginica
141   Iris-virginica        3   Iris-virginica
142   Iris-virginica        0  Iris-versicolor
143   Iris-virginica        3   Iris-virginica
144   Iris-virginica        3   Iris-virginica
145   Iris-virginica        3   Iris-virginica
146   Iris-virginica        0  Iris-versicolor
147   Iris-virginica        0  Iris-versicolor
148   Iris-virginica        3   Iris-virginica
149   Iris-virginica        0  Iris-versicolor

Kesimpulan :

  1. K = 2
    Inertia (SSE): 12.1437
    Silhouette Score: 0.6295

  • Nilai skor inertia lebih besar dari yang lain dan nilai skor silhouette yang diperoleh adalah yang tertinggi dibandingkan dengan K lainnya (lebih mendekati 1).

  1. K = 3
    Inertia (SSE): 7.1386
    Silhouette Score: 0.4825

  • Nilai skor inertia turun dan nilai skor silhouette juga mengalami penurunan dibandingkan dengan k=2 (menjauh dari 1).

  1. K =4
    Inertia (SSE): 5.5417
    Silhouette Score: 0.4435

  • Nilai skor inertia dan nilai skor silhouette sama-sama mengalami penurunan (semakin jauh dari 1).

Maka berdasarkan hasil evaluasi yang telah dilakukan, nilai K = 2 merupakan jumlah klaster yang paling optimal. Hal ini ditunjukkan oleh nilai silhouette score paling mendekati 1 (0.6295).

Metode Elbow#

Metode Elbow untuk menentukan jumlah klaster 𝑘 yang optimal dengan memantau penurunan nilai inertia seiring bertambahnya 𝑘. Titik sebelum grafik melandai adalah titik siku (elbow point) → yaitu jumlah klaster optimal.

Contoh Elbow Method :

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.preprocessing import MinMaxScaler

iris = pd.read_excel('data_iris.xlsx')
iris_x = iris.iloc[:, 1:5]
iris_x.head()

x_iris = np.array(iris_x)
scaler = MinMaxScaler()
x_scaled = scaler.fit_transform(x_iris)
x_scaled

wcss=[]
for i in range(1,11):
    km = KMeans(i)
    km.fit(iris_x)
    wcss.append(km.inertia_)
np.array(wcss)

fig, ax = plt.subplots(figsize=(15,7))
ax = plt.plot(range(1,11),wcss, linewidth=2, color="red", marker ="8")
plt.axvline(x=3, ls='--')
plt.ylabel('WCSS')
plt.xlabel('No. of Clusters (k)')
plt.title('The Elbow Method', fontsize = 20)
plt.show()
_images/3a7043c39ef19240c8be32b053b50c59ccb56e6c67a3ec6cbb74647a8f4119a7.png
  • Berdasarkan metode Elbow, jumlah klaster optimal adalah K = 3, karena pada titik ini terjadi penurunan WCSS (Within-Cluster Sum of Squares) yang signifikan sebelum grafik mulai melandai.

Fuzzy C-mean#

Konsep Fuzzy C-mean#

Fuzzy C-Means merupakan algoritma yang termasuk dalam kategori soft clustering, di mana setiap data dapat menjadi anggota dari lebih dari satu klaster secara bersamaan. Berbeda dengan hard clustering yang hanya menetapkan satu klaster untuk setiap data, Fuzzy C-Means menghitung derajat keanggotaan (membership value) dari setiap data terhadap seluruh klaster, dengan nilai antara 0 hingga 1. Penentuan klaster utama untuk suatu data dilakukan berdasarkan nilai derajat keanggotaan tertinggi. Pada tahap inisialisasi, jumlah total derajat keanggotaan dari satu data terhadap seluruh klaster adalah 1, yang mencerminkan distribusi keanggotaannya di semua klaster.

Langkah-langkah#

  1. Tentukan jumlah klaster \(C\), nilai fuzzifier \(m\), dan inisialisasi nilai derajat keanggotaan awal secara acak dimana jumlah semua nilai keanggotaan untuk satu baris data terhadap seluruh klaster harus sama dengan 1.

  2. Hitung pusat setiap klaster

  • Rumus :
    $\(v_i = \frac{\sum_{k=1}^{n} u_{ik}^m x_k}{\sum_{k=1}^{n} u_{ik}^m}\)$

  • Penjelasan :
    \(v_{i}\) : pusat klaster ke-i (centroid).
    \(u_{ik}\) : derajat keanggotaan data ke-k terhadap klaster ke-i.
    \(m\) : nilai fuzzifier.
    \(x_{k}\) : data ke-k.
    \(n\) : jumlah total data.

  1. Hitung jarak titik setiap data dari centroid menggunakan rumus Euclidian distance karena dibutuhkan untuk perhitungan update derajat keanggotaan.

  • Rumus :
    $\(d(A, B) = \sqrt{ \sum_{i=1}^{n} (x_i - y_i)^2 }\)$

  • Penjelasan :
    \(d(A,B)\) : jarak antara vektor \(A\) dan \(B\). \(x_{i}, y_{i}\) : komponen ke-\(i\) dari masing-masing vektor.
    \(n\) : jumlah total data.

  1. Update derajat keanggotaan

  • Rumus :
    $\(u_{ik} = \frac{1}{\sum_{j=1}^{c} \left( \frac{\lVert x_k - v_i \rVert}{\lVert x_k - v_j \rVert} \right)^{\frac{2}{m-1}}} \)$

  • Penjelasan :
    \(u_{ik}\) : derajat keanggotaan data ke-k terhadap klaster ke-i.
    \(x_{k}\) : data ke-k.
    \(v_{i},v_{j}\) : pusat klaster ke-i dan ke-j
    \(||.||\) : jarak Euclidean antara titik dan pusat klaster.
    \(m\) : nilai fuzzifier.
    \(c\) : jumlah klaster.

  • Jika \(m\) = 2, maka pangkatnya bisa menjadi 2.

  • Misalnya hitung \(u_{11}\), artinya hitung derajat keanggotaan data ke 1 pada klaster 1.

  1. Ulangi langkah 2 hingga 4 secara iteratif hingga mencapai kondisi konvergen, yaitu ketika perubahan yang terjadi pada nilai pusat klaster atau derajat keanggotaan antar iterasi sangat kecil atau tidak signifikan lagi. Pada titik ini, proses klasterisasi dianggap telah mencapai hasil yang optimal.

  2. Cek Konvergensi dengan fungsi objektif dengan membandingkan nilai fungsi objektif pada dua iterasi berurutan.

  • Rumus :
    $\(J_m = \sum_{k=1}^{n} \sum_{i=1}^{c} u_{ik}^m \cdot \|x_k - v_i\|^2\)$

  • Penjelasan :
    \(u_{ik}\) : derajat keanggotaan data ke-k terhadap klaster ke-i.
    \(m\) : nilai fuzzifier.
    \(x_{k}\) : data ke-k.
    \(v_{i}\) : pusat klaster ke-i (centroid).
    \(||x_{k}-v_{i}||\): jarak (umumnya menggunakan euclidean distance)

  • Jika selisih dari dua iterasi berturut-turut lebih kecil dari batas toleransi misalnya 0,00001, maka dianggap sudah konvergen dan iterasi berhenti.

  1. Menentukan klaster terakhir setelah proses iterasi selesai (konvergen), maka selanjutnya mengklasifikasi setiap data pada suatu klaster dengan cara: menentukan kluster dengan nilai \(u_{ij}\) tertinggi untuk data tersebut. Misalnya untuk data \(x_{i}\) diperoleh derajat keanggotaan :

  • \(u_{11}\) = 0,2

  • \(u_{21}\) = 0,7

  • \(u_{31}\) = 0,1

Maka kluster utama untuk \(x_{1}\) adalah kluster ke-2, karena \(u_{21}\) adalah yang terbesar.

Komputasi Fuzzy C-mean#

  • m = 2

  • maksimal iterasi = 100

  • epsilon (batas toleransi) = 0,00001

import numpy as np

# Data xi
X = np.array([
    [1, 2],
    [2, 3],
    [3, 4],
    [6, 7],
    [7, 8]
])

# Matriks keanggotaan awal U (acak)
U = np.array([
    [0.5, 0.5],
    [0.7, 0.3],
    [0.8, 0.2],
    [0.7, 0.3],
    [0.6, 0.4]
])

# Parameter
m = 2            # Fuzziness
max_iter = 100  # Maksimum iterasi
epsilon = 1e-5  # Toleransi konvergensi 0.00001

def calculate_centroids(U, X, m):
    um = U ** m
    return (um.T @ X) / np.sum(um.T, axis=1)[:, None]

def update_membership(X, centroids, m):
    n, c = X.shape[0], centroids.shape[0]
    U_new = np.zeros((n, c))
    for i in range(n):
        for j in range(c):
            num = np.linalg.norm(X[i] - centroids[j])
            denom = sum((num / np.linalg.norm(X[i] - centroids[k])) ** (2/(m-1))
                        for k in range(c))
            U_new[i, j] = 1 / denom
    return U_new

def calculate_objective_function(U, X, centroids, m):
    return sum((U[i,j]**m) * np.linalg.norm(X[i] - centroids[j])**2
               for i in range(X.shape[0])
               for j in range(centroids.shape[0]))

# Iterasi Fuzzy C-Means dengan detail output
prev_obj = None
for iteration in range(1, max_iter+1):
    centroids = calculate_centroids(U, X, m)
    U = update_membership(X, centroids, m)
    obj = calculate_objective_function(U, X, centroids, m)

    # Cetak detail iterasi
    print(f"--- Iterasi {iteration} ---")
    print("Centroids:")
    print(np.round(centroids, 4))
    print("Objektif J_m:", round(obj, 6))
    print("Keanggotaan U:")
    print(np.round(U, 4))
    print()

    # Cek konvergensi
    if prev_obj is not None and abs(obj - prev_obj) < epsilon:
        print(f"Konvergen pada iterasi ke-{iteration} (ΔJ = {abs(obj - prev_obj):.6f} < {epsilon})")
        break

    prev_obj = obj
else:
    print("Maksimum iterasi tercapai tanpa konvergensi.")

# Hasil akhir (jika belum tercetak oleh break)
if abs(obj - prev_obj) >= epsilon:
    print("\n=== Hasil Akhir ===")
    print("Centroids Akhir:")
    print(np.round(centroids, 4))
    print("Keanggotaan Akhir (U):")
    print(np.round(U, 4))
    print("Nilai Fungsi Objektif Akhir:", round(obj, 6))
--- Iterasi 1 ---
Centroids:
[[3.861  4.861 ]
 [3.5079 4.5079]]
Objektif J_m: 26.410066
Keanggotaan U:
[[0.4345 0.5655]
 [0.3963 0.6037]
 [0.2582 0.7418]
 [0.5758 0.4242]
 [0.5531 0.4469]]

--- Iterasi 2 ---
Centroids:
[[4.6034 5.6034]
 [3.2074 4.2074]]
Objektif J_m: 20.900504
Keanggotaan U:
[[0.2729 0.7271]
 [0.177  0.823 ]
 [0.0165 0.9835]
 [0.7999 0.2001]
 [0.7146 0.2854]]

--- Iterasi 3 ---
Centroids:
[[6.0097 7.0097]
 [2.4384 3.4384]]
Objektif J_m: 6.68533
Keanggotaan U:
[[0.0762 0.9238]
 [0.0118 0.9882]
 [0.0337 0.9663]
 [1.     0.    ]
 [0.955  0.045 ]]

--- Iterasi 4 ---
Centroids:
[[6.4581 7.4581]
 [2.0327 3.0327]]
Objektif J_m: 4.791511
Keanggotaan U:
[[3.460e-02 9.654e-01]
 [1.000e-04 9.999e-01]
 [7.260e-02 9.274e-01]
 [9.868e-01 1.320e-02]
 [9.882e-01 1.180e-02]]

--- Iterasi 5 ---
Centroids:
[[6.4879 7.4879]
 [1.9747 2.9747]]
Objektif J_m: 4.766811
Keanggotaan U:
[[0.0306 0.9694]
 [0.     1.    ]
 [0.0795 0.9205]
 [0.9855 0.0145]
 [0.9897 0.0103]]

--- Iterasi 6 ---
Centroids:
[[6.4882 7.4882]
 [1.9673 2.9673]]
Objektif J_m: 4.766461
Keanggotaan U:
[[3.010e-02 9.699e-01]
 [1.000e-04 9.999e-01]
 [8.060e-02 9.194e-01]
 [9.856e-01 1.440e-02]
 [9.898e-01 1.020e-02]]

--- Iterasi 7 ---
Centroids:
[[6.488  7.488 ]
 [1.9663 2.9663]]
Objektif J_m: 4.766454
Keanggotaan U:
[[3.010e-02 9.699e-01]
 [1.000e-04 9.999e-01]
 [8.070e-02 9.193e-01]
 [9.856e-01 1.440e-02]
 [9.898e-01 1.020e-02]]

Konvergen pada iterasi ke-7 (ΔJ = 0.000007 < 1e-05)

Link perhitungan manual hanya sampai dua iterasi :
https://docs.google.com/spreadsheets/d/1wLilhPFjf0tFwQjRFbflKhPWY88ZvvkZ3B9bXPkLoxw/edit?usp=sharing

Implementasi data iris 3 klaster#

  • n_cluster = 3

  • m = 2

  • epsilon = 0,00001

  • maksimal iterasi = 150

import pandas as pd
import numpy as np
from sklearn.preprocessing import MinMaxScaler
from fcmeans import FCM
from sklearn.metrics import silhouette_score, accuracy_score, confusion_matrix
from scipy.spatial.distance import cdist

# 1. Baca dan siapkan data
df = pd.read_excel("class.xlsx")  
df['id'] = df.index + 1
X = df[['petal_length', 'petal_width', 'sepal_length', 'sepal_width']].values

# 2. Normalisasi Min–Max
scaler = MinMaxScaler()
X_scaled = scaler.fit_transform(X)

# 3. Fuzzy C-Means clustering
n_clusters = 3
m = 2
epsilon = 1e-5
fcm = FCM(n_clusters=n_clusters, m=m, error=epsilon, max_iter=150, random_state=42)
fcm.fit(X_scaled)

# 4. Hasil prediksi
labels = fcm.predict(X_scaled)
U = fcm.u
centers = fcm.centers
df['cluster'] = labels

# 5. Silhouette Score
sil_score = silhouette_score(X_scaled, labels)
print(f"Silhouette Score: {sil_score:.4f}")

# 6. Inertia (dengan label keras)
inertia = np.sum((X_scaled - centers[labels])**2)
print(f"Inertia (crisp): {inertia:.4f}")

# 7. Fungsi Objektif J_m
dist_matrix = cdist(X_scaled, centers, metric='euclidean')
Jm = np.sum((U**m) * (dist_matrix**2))
print(f"FCM Objective J_m: {Jm:.4f}")

# 8. Gabungkan hasil ke DataFrame
result_df = pd.DataFrame({
    'id': df['id'],
    'cluster': df['cluster']  # ← Tambahkan ini
})
for j in range(n_clusters):
    result_df[f'membership_cluster_{j}'] = np.round(U[:, j], 4)

print(f"\n--- Hasil Clustering (epsilon: {epsilon}) ---")
print(result_df.to_string(index=False))


# 9. Mapping cluster ke class mayoritas
mapping = (
    df.groupby('cluster')['class']
      .agg(lambda x: x.mode()[0])
      .to_dict()
)
df['predicted_class'] = df['cluster'].map(mapping)

# 10. Evaluasi
y_true   = df['class']
y_pred   = df['predicted_class']
classes  = sorted(df['class'].unique())
cm       = confusion_matrix(y_true, y_pred, labels=classes)

# Akurasi keseluruhan
acc = accuracy_score(y_true, y_pred)
print(f"\nAkurasi keseluruhan clustering terhadap label asli: {acc:.4%}")

# Persentase error per kelas
error_per_class = {}
for i, c in enumerate(classes):
    total   = cm[i].sum()
    correct = cm[i, i]
    error   = 1 - correct/total if total > 0 else 0
    error_per_class[c] = error

print("\nPersentase kesalahan per kelas:")
for c, e in error_per_class.items():
    print(f" - {c}: {e:.2%}")

# Distribusi cluster per kelas (insight)
dist = pd.crosstab(df['class'], df['cluster'], 
                   rownames=['Class'], colnames=['Cluster'])
print("\nDistribusi cluster per kelas:")
print(dist)

# Simpan hasil lengkap ke Excel
df.to_excel("hasil_fuzzy.xlsx", index=False)

# Tampilkan perbandingan class vs cluster vs predicted_class
pd.set_option('display.max_rows', None)
print("\nPerbandingan lengkap:")
print(df[['class', 'cluster', 'predicted_class']])
Silhouette Score: 0.4955
Inertia (crisp): 7.0830
FCM Objective J_m: 5.2330

--- Hasil Clustering (epsilon: 1e-05) ---
 id  cluster  membership_cluster_0  membership_cluster_1  membership_cluster_2
  1        0                0.9930                0.0023                0.0048
  2        0                0.9313                0.0204                0.0483
  3        0                0.9660                0.0105                0.0235
  4        0                0.9371                0.0191                0.0439
  5        0                0.9839                0.0053                0.0108
  6        0                0.8700                0.0450                0.0850
  7        0                0.9711                0.0091                0.0198
  8        0                0.9989                0.0003                0.0008
  9        0                0.8635                0.0413                0.0952
 10        0                0.9534                0.0141                0.0325
 11        0                0.9390                0.0205                0.0405
 12        0                0.9907                0.0029                0.0064
 13        0                0.9251                0.0227                0.0522
 14        0                0.8778                0.0387                0.0835
 15        0                0.8158                0.0680                0.1162
 16        0                0.7127                0.1135                0.1738
 17        0                0.8806                0.0420                0.0775
 18        0                0.9927                0.0023                0.0050
 19        0                0.8550                0.0499                0.0951
 20        0                0.9397                0.0205                0.0398
 21        0                0.9636                0.0113                0.0251
 22        0                0.9531                0.0155                0.0315
 23        0                0.9529                0.0159                0.0312
 24        0                0.9548                0.0131                0.0321
 25        0                0.9791                0.0064                0.0145
 26        0                0.9278                0.0210                0.0512
 27        0                0.9884                0.0035                0.0081
 28        0                0.9878                0.0039                0.0083
 29        0                0.9911                0.0028                0.0061
 30        0                0.9651                0.0106                0.0243
 31        0                0.9523                0.0142                0.0335
 32        0                0.9550                0.0138                0.0312
 33        0                0.8579                0.0520                0.0901
 34        0                0.8063                0.0729                0.1208
 35        0                0.9534                0.0141                0.0325
 36        0                0.9771                0.0071                0.0158
 37        0                0.9493                0.0166                0.0342
 38        0                0.9534                0.0141                0.0325
 39        0                0.8913                0.0334                0.0753
 40        0                0.9971                0.0009                0.0020
 41        0                0.9928                0.0023                0.0049
 42        0                0.6619                0.0997                0.2384
 43        0                0.9297                0.0221                0.0482
 44        0                0.9349                0.0199                0.0451
 45        0                0.9129                0.0289                0.0582
 46        0                0.9237                0.0224                0.0539
 47        0                0.9408                0.0202                0.0391
 48        0                0.9565                0.0135                0.0300
 49        0                0.9508                0.0165                0.0327
 50        0                0.9939                0.0019                0.0042
 51        1                0.0659                0.5350                0.3990
 52        2                0.0521                0.3677                0.5802
 53        1                0.0453                0.6034                0.3513
 54        2                0.0669                0.1041                0.8290
 55        2                0.0321                0.2643                0.7036
 56        2                0.0079                0.0193                0.9728
 57        2                0.0519                0.4727                0.4753
 58        2                0.2488                0.1352                0.6160
 59        2                0.0472                0.2573                0.6955
 60        2                0.0704                0.0951                0.8345
 61        2                0.2198                0.1644                0.6158
 62        2                0.0238                0.0843                0.8919
 63        2                0.0996                0.1397                0.7607
 64        2                0.0166                0.0841                0.8992
 65        2                0.0598                0.0744                0.8658
 66        2                0.0599                0.3774                0.5627
 67        2                0.0311                0.0981                0.8708
 68        2                0.0542                0.0675                0.8783
 69        2                0.0525                0.1986                0.7489
 70        2                0.0639                0.0753                0.8608
 71        2                0.0474                0.4434                0.5092
 72        2                0.0160                0.0401                0.9439
 73        2                0.0313                0.2043                0.7644
 74        2                0.0202                0.0646                0.9152
 75        2                0.0362                0.1413                0.8225
 76        2                0.0493                0.3004                0.6502
 77        2                0.0474                0.3981                0.5544
 78        1                0.0237                0.7364                0.2399
 79        2                0.0131                0.0648                0.9221
 80        2                0.1103                0.0922                0.7975
 81        2                0.0898                0.0963                0.8139
 82        2                0.1196                0.1063                0.7740
 83        2                0.0236                0.0365                0.9399
 84        2                0.0246                0.1982                0.7772
 85        2                0.0472                0.1203                0.8325
 86        2                0.0738                0.3699                0.5563
 87        1                0.0462                0.4997                0.4540
 88        2                0.0501                0.1543                0.7956
 89        2                0.0403                0.0685                0.8911
 90        2                0.0418                0.0654                0.8928
 91        2                0.0331                0.0550                0.9119
 92        2                0.0243                0.1155                0.8601
 93        2                0.0232                0.0385                0.9382
 94        2                0.2311                0.1384                0.6306
 95        2                0.0139                0.0261                0.9600
 96        2                0.0406                0.0669                0.8925
 97        2                0.0161                0.0326                0.9513
 98        2                0.0217                0.0729                0.9054
 99        2                0.2323                0.1272                0.6405
100        2                0.0109                0.0209                0.9681
101        1                0.0338                0.8200                0.1462
102        2                0.0389                0.3788                0.5824
103        1                0.0088                0.9449                0.0463
104        1                0.0208                0.7458                0.2334
105        1                0.0071                0.9480                0.0448
106        1                0.0380                0.8123                0.1497
107        2                0.0945                0.1962                0.7093
108        1                0.0293                0.8230                0.1477
109        1                0.0343                0.6766                0.2892
110        1                0.0473                0.7988                0.1539
111        1                0.0122                0.9007                0.0871
112        1                0.0233                0.7048                0.2719
113        1                0.0011                0.9916                0.0073
114        2                0.0509                0.3674                0.5817
115        1                0.0516                0.6175                0.3309
116        1                0.0173                0.8861                0.0966
117        1                0.0127                0.8654                0.1219
118        1                0.0737                0.7220                0.2043
119        1                0.0556                0.7361                0.2083
120        2                0.0518                0.2129                0.7353
121        1                0.0105                0.9399                0.0496
122        2                0.0499                0.3806                0.5696
123        1                0.0473                0.7645                0.1882
124        2                0.0291                0.4611                0.5098
125        1                0.0080                0.9502                0.0419
126        1                0.0225                0.8682                0.1094
127        2                0.0286                0.4177                0.5537
128        1                0.0312                0.4870                0.4818
129        1                0.0142                0.8710                0.1148
130        1                0.0331                0.7768                0.1900
131        1                0.0296                0.8226                0.1478
132        1                0.0809                0.7004                0.2187
133        1                0.0157                0.8697                0.1146
134        2                0.0282                0.2777                0.6942
135        2                0.0401                0.2616                0.6982
136        1                0.0384                0.8156                0.1459
137        1                0.0328                0.8213                0.1459
138        1                0.0160                0.8375                0.1465
139        2                0.0341                0.4040                0.5619
140        1                0.0027                0.9817                0.0156
141        1                0.0133                0.9203                0.0664
142        1                0.0130                0.9196                0.0674
143        2                0.0389                0.3788                0.5824
144        1                0.0108                0.9373                0.0518
145        1                0.0249                0.8698                0.1053
146        1                0.0106                0.9271                0.0622
147        1                0.0367                0.4890                0.4743
148        1                0.0077                0.9260                0.0663
149        1                0.0342                0.8014                0.1645
150        2                0.0358                0.4381                0.5261

Akurasi keseluruhan clustering terhadap label asli: 89.3333%

Persentase kesalahan per kelas:
 - Iris-setosa: 0.00%
 - Iris-versicolor: 8.00%
 - Iris-virginica: 24.00%

Distribusi cluster per kelas:
Cluster           0   1   2
Class                      
Iris-setosa      50   0   0
Iris-versicolor   0   4  46
Iris-virginica    0  38  12
Perbandingan lengkap:
               class  cluster  predicted_class
0        Iris-setosa        0      Iris-setosa
1        Iris-setosa        0      Iris-setosa
2        Iris-setosa        0      Iris-setosa
3        Iris-setosa        0      Iris-setosa
4        Iris-setosa        0      Iris-setosa
5        Iris-setosa        0      Iris-setosa
6        Iris-setosa        0      Iris-setosa
7        Iris-setosa        0      Iris-setosa
8        Iris-setosa        0      Iris-setosa
9        Iris-setosa        0      Iris-setosa
10       Iris-setosa        0      Iris-setosa
11       Iris-setosa        0      Iris-setosa
12       Iris-setosa        0      Iris-setosa
13       Iris-setosa        0      Iris-setosa
14       Iris-setosa        0      Iris-setosa
15       Iris-setosa        0      Iris-setosa
16       Iris-setosa        0      Iris-setosa
17       Iris-setosa        0      Iris-setosa
18       Iris-setosa        0      Iris-setosa
19       Iris-setosa        0      Iris-setosa
20       Iris-setosa        0      Iris-setosa
21       Iris-setosa        0      Iris-setosa
22       Iris-setosa        0      Iris-setosa
23       Iris-setosa        0      Iris-setosa
24       Iris-setosa        0      Iris-setosa
25       Iris-setosa        0      Iris-setosa
26       Iris-setosa        0      Iris-setosa
27       Iris-setosa        0      Iris-setosa
28       Iris-setosa        0      Iris-setosa
29       Iris-setosa        0      Iris-setosa
30       Iris-setosa        0      Iris-setosa
31       Iris-setosa        0      Iris-setosa
32       Iris-setosa        0      Iris-setosa
33       Iris-setosa        0      Iris-setosa
34       Iris-setosa        0      Iris-setosa
35       Iris-setosa        0      Iris-setosa
36       Iris-setosa        0      Iris-setosa
37       Iris-setosa        0      Iris-setosa
38       Iris-setosa        0      Iris-setosa
39       Iris-setosa        0      Iris-setosa
40       Iris-setosa        0      Iris-setosa
41       Iris-setosa        0      Iris-setosa
42       Iris-setosa        0      Iris-setosa
43       Iris-setosa        0      Iris-setosa
44       Iris-setosa        0      Iris-setosa
45       Iris-setosa        0      Iris-setosa
46       Iris-setosa        0      Iris-setosa
47       Iris-setosa        0      Iris-setosa
48       Iris-setosa        0      Iris-setosa
49       Iris-setosa        0      Iris-setosa
50   Iris-versicolor        1   Iris-virginica
51   Iris-versicolor        2  Iris-versicolor
52   Iris-versicolor        1   Iris-virginica
53   Iris-versicolor        2  Iris-versicolor
54   Iris-versicolor        2  Iris-versicolor
55   Iris-versicolor        2  Iris-versicolor
56   Iris-versicolor        2  Iris-versicolor
57   Iris-versicolor        2  Iris-versicolor
58   Iris-versicolor        2  Iris-versicolor
59   Iris-versicolor        2  Iris-versicolor
60   Iris-versicolor        2  Iris-versicolor
61   Iris-versicolor        2  Iris-versicolor
62   Iris-versicolor        2  Iris-versicolor
63   Iris-versicolor        2  Iris-versicolor
64   Iris-versicolor        2  Iris-versicolor
65   Iris-versicolor        2  Iris-versicolor
66   Iris-versicolor        2  Iris-versicolor
67   Iris-versicolor        2  Iris-versicolor
68   Iris-versicolor        2  Iris-versicolor
69   Iris-versicolor        2  Iris-versicolor
70   Iris-versicolor        2  Iris-versicolor
71   Iris-versicolor        2  Iris-versicolor
72   Iris-versicolor        2  Iris-versicolor
73   Iris-versicolor        2  Iris-versicolor
74   Iris-versicolor        2  Iris-versicolor
75   Iris-versicolor        2  Iris-versicolor
76   Iris-versicolor        2  Iris-versicolor
77   Iris-versicolor        1   Iris-virginica
78   Iris-versicolor        2  Iris-versicolor
79   Iris-versicolor        2  Iris-versicolor
80   Iris-versicolor        2  Iris-versicolor
81   Iris-versicolor        2  Iris-versicolor
82   Iris-versicolor        2  Iris-versicolor
83   Iris-versicolor        2  Iris-versicolor
84   Iris-versicolor        2  Iris-versicolor
85   Iris-versicolor        2  Iris-versicolor
86   Iris-versicolor        1   Iris-virginica
87   Iris-versicolor        2  Iris-versicolor
88   Iris-versicolor        2  Iris-versicolor
89   Iris-versicolor        2  Iris-versicolor
90   Iris-versicolor        2  Iris-versicolor
91   Iris-versicolor        2  Iris-versicolor
92   Iris-versicolor        2  Iris-versicolor
93   Iris-versicolor        2  Iris-versicolor
94   Iris-versicolor        2  Iris-versicolor
95   Iris-versicolor        2  Iris-versicolor
96   Iris-versicolor        2  Iris-versicolor
97   Iris-versicolor        2  Iris-versicolor
98   Iris-versicolor        2  Iris-versicolor
99   Iris-versicolor        2  Iris-versicolor
100   Iris-virginica        1   Iris-virginica
101   Iris-virginica        2  Iris-versicolor
102   Iris-virginica        1   Iris-virginica
103   Iris-virginica        1   Iris-virginica
104   Iris-virginica        1   Iris-virginica
105   Iris-virginica        1   Iris-virginica
106   Iris-virginica        2  Iris-versicolor
107   Iris-virginica        1   Iris-virginica
108   Iris-virginica        1   Iris-virginica
109   Iris-virginica        1   Iris-virginica
110   Iris-virginica        1   Iris-virginica
111   Iris-virginica        1   Iris-virginica
112   Iris-virginica        1   Iris-virginica
113   Iris-virginica        2  Iris-versicolor
114   Iris-virginica        1   Iris-virginica
115   Iris-virginica        1   Iris-virginica
116   Iris-virginica        1   Iris-virginica
117   Iris-virginica        1   Iris-virginica
118   Iris-virginica        1   Iris-virginica
119   Iris-virginica        2  Iris-versicolor
120   Iris-virginica        1   Iris-virginica
121   Iris-virginica        2  Iris-versicolor
122   Iris-virginica        1   Iris-virginica
123   Iris-virginica        2  Iris-versicolor
124   Iris-virginica        1   Iris-virginica
125   Iris-virginica        1   Iris-virginica
126   Iris-virginica        2  Iris-versicolor
127   Iris-virginica        1   Iris-virginica
128   Iris-virginica        1   Iris-virginica
129   Iris-virginica        1   Iris-virginica
130   Iris-virginica        1   Iris-virginica
131   Iris-virginica        1   Iris-virginica
132   Iris-virginica        1   Iris-virginica
133   Iris-virginica        2  Iris-versicolor
134   Iris-virginica        2  Iris-versicolor
135   Iris-virginica        1   Iris-virginica
136   Iris-virginica        1   Iris-virginica
137   Iris-virginica        1   Iris-virginica
138   Iris-virginica        2  Iris-versicolor
139   Iris-virginica        1   Iris-virginica
140   Iris-virginica        1   Iris-virginica
141   Iris-virginica        1   Iris-virginica
142   Iris-virginica        2  Iris-versicolor
143   Iris-virginica        1   Iris-virginica
144   Iris-virginica        1   Iris-virginica
145   Iris-virginica        1   Iris-virginica
146   Iris-virginica        1   Iris-virginica
147   Iris-virginica        1   Iris-virginica
148   Iris-virginica        1   Iris-virginica
149   Iris-virginica        2  Iris-versicolor