DQLab Fashion adalah sebuah toko fashion yang menjual berbagai produk seperti jeans, kemeja, kosmetik, dan lain-lain. Walaupun cukup berkembang, namun dengan semakin banyaknya kompetitor dan banyak produk yang stoknya masih banyak tentunya membuat khawatir Pak Agus, manajer DQLab Fashion. Salah satu solusi adalah membuat paket yang inovatif. Dimana produk yang sebelumnya tidak terlalu laku tapi punya pangsa pasar malah bisa dipaketkan dan laku.

Tujuan :

  • Mendapatkan insight top 10 dan bottom 10 dari produk yang terjual.
  • Mendapatkan daftar seluruh kombinasi paket produk dengan korelasi yang kuat.
  • Mendapatkan daftar seluruh kombinasi paket produk dengan item tertentu.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
sns.set_style('darkgrid')

import warnings
warnings.filterwarnings('ignore')
# import dataset
fashion = pd.read_csv('transaksi_dqlab_retail.tsv', sep='\t')
# print first five rows
fashion.head()
Kode Transaksi Nama Barang
0 #1 Kaos
1 #1 Shampo Biasa
2 #1 Sepatu Sport merk Z
3 #1 Serum Vitamin
4 #1 Baju Renang Pria Dewasa
# check dimension
print(f"DQLab Fashion Data terdiri dari {fashion.shape[0]} baris dan {fashion.shape[1]} kolom")
DQLab Fashion Data terdiri dari 33668 baris dan 2 kolom
# chcek info data
fashion.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 33668 entries, 0 to 33667
Data columns (total 2 columns):
 #   Column          Non-Null Count  Dtype 
---  ------          --------------  ----- 
 0   Kode Transaksi  33668 non-null  object
 1   Nama Barang     33668 non-null  object
dtypes: object(2)
memory usage: 526.2+ KB

Tidak ditemukan missing values dan format data sudah sesuai.

print("Jumlah transaksi unik adalah", fashion['Kode Transaksi'].nunique())
Jumlah transaksi unik adalah 3450
# cek jumlah produk dari transaksi
print("Jumlah produk unik adalah", fashion['Nama Barang'].nunique())
Jumlah produk unik adalah 69
print("Berikut ini adalah list produk yang telah terjual :")
fashion['Nama Barang'].unique()
Berikut ini adalah list produk yang telah terjual :

array(['Kaos', 'Shampo Biasa', 'Sepatu Sport merk Z', 'Serum Vitamin',
       'Baju Renang Pria Dewasa', 'Baju Renang Wanita Dewasa',
       'Baju Kaos Olahraga', 'Celana Jogger Casual', 'Dompet Card Holder',
       'Celana Jeans Sobek Wanita', 'Blouse Denim', 'Baju Batik Wanita',
       'Hair and Scalp', 'Minyak Rambut', 'Wedges Hitam',
       'Sepatu Sandal Anak', 'Tas Sekolah Anak Perempuan',
       'Baju Kemeja Putih', 'Dompet Flip Cover', 'Hair Tonic',
       'Baju Kaos Anak - Superheroes', 'Celana Pendek Casual',
       'Jeans Jumbo', 'Celana Pendek Jeans', 'Sepatu Sekolah Hitam W',
       'Tas Ransel Mini', 'Dompet Kulit Pria', 'Hair Dryer',
       'Flat Shoes Ballerina', 'Tas Sekolah Anak Laki-laki',
       'Cover Koper', 'Gembok Koper', 'Sweater Top Panjang', 'Tank Top',
       'Atasan Kaos Putih', 'Atasan Baju Belang', 'Shampo Anti Dandruff',
       'Tas Pinggang Wanita', 'Koper Fiber', 'Sepatu Sport merk Y',
       'Dompet STNK Gantungan', 'Cream Whitening', 'Celana Tactical ',
       'Woman Ripped Jeans ', 'Tas Travel', 'Tali Pinggang Anak',
       'Dompet Unisex', 'Tali Pinggang Gesper Pria', 'Tas Waist Bag',
       'Tas Tangan', 'Celana Pendek Green/Hijau', 'Obat Penumbuh Rambut',
       'Pelembab', 'Baju Kaos Anak - Karakter Kartun',
       'Sepatu Kulit Casual', 'Tas Makeup', 'Baju Renang Anak Perempuan',
       'Kuas Makeup ', 'Sunblock Cream', 'Tas Kulit Selempang',
       'Tas Multifungsi', 'Hair Dye', 'Celana Panjang Format Hitam',
       'Celana Jeans Sobek Pria', 'Mascara', 'Tali Ban Ikat Pinggang',
       'Stripe Pants', 'Tas Kosmetik', 'Baju Renang Pria Anak-anak'],
      dtype=object)

Statistik Top 10

top_10 = fashion['Nama Barang'].value_counts()[:10]
plt.figure(figsize=(10,5))
ax1 = sns.barplot(x=top_10, y=top_10.index, palette='rocket')
for p1 in ax1.patches:
    ax1.annotate('{:.2f}'.format(p1.get_width()/fashion['Kode Transaksi'].nunique()*100), 
                 xy=(p1.get_width()+0.5, p1.get_y()+0.4),
                 ha='left', va='center', color= 'black')
ax1.set_title('Statistik Top 10 Produk', fontweight='bold', fontsize=15)
ax1.set_xlabel('Jumlah Transaksi')
plt.show()

png

Statistik Bottom 10 Produk

bottom_10 = fashion['Nama Barang'].value_counts()[-10:]
plt.figure(figsize=(10,5))
ax2 = sns.barplot(x=bottom_10, y=bottom_10.index, palette='rocket')
for p2 in ax2.patches:
    ax2.annotate('{:.2f}'.format(p2.get_width()/fashion['Kode Transaksi'].nunique()*100), 
                 xy=(p2.get_width()+0.5, p2.get_y()+0.4),
                 ha='left', va='center', color= 'black')
ax2.set_title('Statistik Bottom 10 Produk', fontweight='bold', fontsize=15)
ax2.set_xlabel('Jumlah Transaksi')
plt.show()

png

Insights :

  • Shampo Biasa telah terjual sekitar 60,14% dari seluruh transaksi.
  • Hanya 0,26% Celana Jeans Sobek Pria yang terdapat pada keseluruhan transaksi.
# grouping berdasarkan kode transaksi
data = fashion.groupby('Kode Transaksi')['Nama Barang'].unique()
data.head()
Kode Transaksi
#1       [Kaos, Shampo Biasa, Sepatu Sport merk Z, Seru...
#10      [Jeans Jumbo, Celana Pendek Jeans, Kaos, Baju ...
#100     [Hair Dryer, Shampo Biasa, Hair Tonic, Sepatu ...
#1000    [Celana Jeans Sobek Wanita, Celana Pendek Jean...
#1001    [Hair Dryer, Wedges Hitam, Sepatu Sport merk Z...
Name: Nama Barang, dtype: object
# cek total transaksi
list_transaksi = data.tolist()
len(list_transaksi)
3450
# Hitung banyak produk per transaksi dari semua transaksi
counts = [len(transaksi) for transaksi in list_transaksi]

# Hitung nilai tengah jumlah produk pada sebuah transaksi
print("Median number of items in a transaction is", int(np.median(counts)))

# Hitung nilai maksimum jumlah produk pada sebuah transaksi
print("Maximum number of items in a transaction is", np.max(counts))
Median number of items in a transaction is 10
Maximum number of items in a transaction is 22

Encoding Transactions

# encoding produk pada sebuah list transaksi
from mlxtend.preprocessing import TransactionEncoder

encoder = TransactionEncoder()
data_encode = encoder.fit(list_transaksi).transform(list_transaksi)
df_encode = pd.DataFrame(data_encode, columns=encoder.columns_)
df_encode
Atasan Baju Belang Atasan Kaos Putih Baju Batik Wanita Baju Kaos Anak - Karakter Kartun Baju Kaos Anak - Superheroes Baju Kaos Olahraga Baju Kemeja Putih Baju Renang Anak Perempuan Baju Renang Pria Anak-anak Baju Renang Pria Dewasa ... Tas Multifungsi Tas Pinggang Wanita Tas Ransel Mini Tas Sekolah Anak Laki-laki Tas Sekolah Anak Perempuan Tas Tangan Tas Travel Tas Waist Bag Wedges Hitam Woman Ripped Jeans
0 False False False False False True False False False True ... False False False False False False False False False False
1 False False True False False False True False False False ... False False False False False False False False True False
2 False False False False False False False False False False ... False False False False False False False False False False
3 False False True False False False False False False True ... False False False False False False False False False False
4 False False False False False False False False False True ... False False False True False False True False True False
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
3445 False True False False False True False False False True ... False False False False False False True False True False
3446 False False False False False False False False False True ... True False False True False False True False False False
3447 False False True False True False True False False False ... False False False False False False False False False False
3448 False False True False True False False False False False ... False True False False False False True False False False
3449 False False True True False False False False False False ... False False False False False True False False False False

3450 rows × 69 columns


# Hitung support metric pada tiap kolom
df_encode.mean(axis=0)
Atasan Baju Belang                  0.016232
Atasan Kaos Putih                   0.118841
Baju Batik Wanita                   0.380290
Baju Kaos Anak - Karakter Kartun    0.121159
Baju Kaos Anak - Superheroes        0.115942
                                      ...   
Tas Tangan                          0.037101
Tas Travel                          0.220870
Tas Waist Bag                       0.161159
Wedges Hitam                        0.246087
Woman Ripped Jeans                  0.034203
Length: 69, dtype: float64
# Plotting distribution of product counts
distribusi = df_encode.sum(axis=1).value_counts().sort_index()
plt.figure(figsize=(10,5))
ax3 = sns.barplot(x=distribusi.index, y=distribusi, palette='Set2')
for p3 in ax3.patches:
    ax3.annotate('{:.0f}'.format(p3.get_height()), (p3.get_x()+0.4, p3.get_height()),
                 ha='center', va='bottom', color= 'black')
ax3.set_title('Distribusi Jumlah Produk per Transaksi', fontweight='bold', fontsize=15)
ax3.set_xlabel('Jumlah Produk')
ax3.set_ylabel('Jumlah Transaksi')
plt.show()

png

Kombinasi Produk Menarik :

  • Memiliki asosiasi atau hubungan erat.
  • Kombinasi produk minimal 2 item, dan maksimum 3 item.
  • Kombinasi produk itu muncul setidaknya 10 dari dari seluruh transaksi.
  • Memiliki tingkat confidence minimal 50 persen.
# setting transaksi berdasarkan kriteria menarik
df_transaksi = df_encode[df_encode.sum(axis=1) >= 2]
kriteria = 10 / len(list_transaksi)
kriteria
0.002898550724637681

Apriori Algorithm

from mlxtend.frequent_patterns import apriori

frekuensi_itemset = apriori(df_transaksi, min_support=kriteria, max_len=3, use_colnames=True)
frekuensi_itemset
support itemsets
0 0.016274 (Atasan Baju Belang)
1 0.119151 (Atasan Kaos Putih)
2 0.380703 (Baju Batik Wanita)
3 0.121476 (Baju Kaos Anak - Karakter Kartun)
4 0.116245 (Baju Kaos Anak - Superheroes)
... ... ...
16214 0.006103 (Tas Waist Bag, Tas Travel, Tas Sekolah Anak L...
16215 0.008718 (Wedges Hitam, Tas Travel, Tas Sekolah Anak La...
16216 0.009300 (Wedges Hitam, Tas Waist Bag, Tas Sekolah Anak...
16217 0.012206 (Tas Waist Bag, Wedges Hitam, Tas Travel)
16218 0.003487 (Woman Ripped Jeans , Wedges Hitam, Tas Travel)

16219 rows × 2 columns

Association Rules

from mlxtend.frequent_patterns import association_rules

# Recover association rules using metric lift
rules = association_rules(frekuensi_itemset, metric = 'lift')
rules
antecedents consequents antecedent support consequent support support confidence lift leverage conviction
0 (Baju Batik Wanita) (Atasan Baju Belang) 0.380703 0.016274 0.005522 0.014504 0.891208 -0.000674 0.998203
1 (Atasan Baju Belang) (Baju Batik Wanita) 0.016274 0.380703 0.005522 0.339286 0.891208 -0.000674 0.937314
2 (Atasan Baju Belang) (Baju Kemeja Putih) 0.016274 0.364720 0.004940 0.303571 0.832342 -0.000995 0.912198
3 (Baju Kemeja Putih) (Atasan Baju Belang) 0.364720 0.016274 0.004940 0.013546 0.832342 -0.000995 0.997234
4 (Blouse Denim) (Atasan Baju Belang) 0.182796 0.016274 0.003487 0.019078 1.172269 0.000512 1.002858
... ... ... ... ... ... ... ... ... ...
75439 (Woman Ripped Jeans , Tas Travel) (Wedges Hitam) 0.006684 0.246731 0.003487 0.521739 2.114611 0.001838 1.575018
75440 (Wedges Hitam, Tas Travel) (Woman Ripped Jeans ) 0.079628 0.034292 0.003487 0.043796 1.277125 0.000757 1.009939
75441 (Woman Ripped Jeans ) (Wedges Hitam, Tas Travel) 0.034292 0.079628 0.003487 0.101695 1.277125 0.000757 1.024565
75442 (Wedges Hitam) (Woman Ripped Jeans , Tas Travel) 0.246731 0.006684 0.003487 0.014134 2.114611 0.001838 1.007557
75443 (Tas Travel) (Woman Ripped Jeans , Wedges Hitam) 0.221447 0.014821 0.003487 0.015748 1.062529 0.000205 1.000942

75444 rows × 9 columns

# pilih rules dengan nilai confindence 50% dan positif leverage
result = rules[(rules['confidence'] >= 0.5) & (rules['leverage'] > 0)] \
                .sort_values(by='lift', ascending=False)[:10]
result.reset_index(drop=True, inplace=True)
result
antecedents consequents antecedent support consequent support support confidence lift leverage conviction
0 (Tas Makeup, Tas Pinggang Wanita) (Baju Renang Anak Perempuan) 0.011915 0.036036 0.010462 0.878049 24.365854 0.010033 7.904505
1 (Tas Makeup, Tas Travel) (Baju Renang Anak Perempuan) 0.012496 0.036036 0.010171 0.813953 22.587209 0.009721 5.181306
2 (Tas Makeup, Tas Ransel Mini) (Baju Renang Anak Perempuan) 0.015402 0.036036 0.011334 0.735849 20.419811 0.010779 3.649292
3 (Tas Pinggang Wanita, Sunblock Cream) (Kuas Makeup ) 0.023540 0.034292 0.016274 0.691358 20.160703 0.015467 3.128893
4 (Tas Pinggang Wanita, Baju Renang Anak Perempuan) (Tas Makeup) 0.013078 0.040976 0.010462 0.800000 19.523404 0.009926 4.795118
5 (Tas Ransel Mini, Baju Renang Anak Perempuan) (Tas Makeup) 0.014240 0.040976 0.011334 0.795918 19.423795 0.010750 4.699215
6 (Baju Renang Anak Perempuan, Celana Pendek Gre... (Tas Makeup) 0.013078 0.040976 0.010171 0.777778 18.981087 0.009636 4.315606
7 (Tas Makeup, Tas Waist Bag) (Baju Renang Anak Perempuan) 0.006393 0.036036 0.004359 0.681818 18.920455 0.004129 3.029601
8 (Tas Makeup, Celana Pendek Green/Hijau) (Baju Renang Anak Perempuan) 0.015112 0.036036 0.010171 0.673077 18.677885 0.009627 2.948596
9 (Sunblock Cream, Dompet Flip Cover) (Kuas Makeup ) 0.025865 0.034292 0.016274 0.629213 18.348505 0.015387 2.604484

Insights :

  • Kombinasi Tas Makeup dan Tas Pinggang Wanita terhadap Baju Renang Anak Perempuan menghasilkan asosiasi atau hubungan yang paling erat dimana nilai confidence cukup tinggi diikuti metric lift.

Slow Moving Product

Slow Moving Product adalah produk yang memiliki tingkat penjualan relatif lama karena bisa dipengaruhi musiman.

# Item slow moving berdasarkan top 3 rules
tas_makeup = rules[rules['consequents'] == {'Tas Makeup'}] \
             .sort_values(by='lift', ascending=False)[:3] 
baju_renang_pria = rules[rules['consequents'] == {'Baju Renang Pria Anak-anak'}] \
             .sort_values(by='lift', ascending=False)[:3]
slow_moving = pd.concat([tas_makeup, baju_renang_pria])
slow_moving
antecedents consequents antecedent support consequent support support confidence lift leverage conviction
26581 (Tas Pinggang Wanita, Baju Renang Anak Perempuan) (Tas Makeup) 0.013078 0.040976 0.010462 0.800000 19.523404 0.009926 4.795118
26587 (Tas Ransel Mini, Baju Renang Anak Perempuan) (Tas Makeup) 0.014240 0.040976 0.011334 0.795918 19.423795 0.010750 4.699215
26158 (Baju Renang Anak Perempuan, Celana Pendek Gre... (Tas Makeup) 0.013078 0.040976 0.010171 0.777778 18.981087 0.009636 4.315606
26714 (Gembok Koper, Tas Waist Bag) (Baju Renang Pria Anak-anak) 0.014821 0.009300 0.004069 0.274510 29.518382 0.003931 1.365560
26690 (Flat Shoes Ballerina, Gembok Koper) (Baju Renang Pria Anak-anak) 0.021796 0.009300 0.004069 0.186667 20.072500 0.003866 1.218074
26655 (Jeans Jumbo, Celana Jeans Sobek Wanita) (Baju Renang Pria Anak-anak) 0.045626 0.009300 0.005522 0.121019 13.013336 0.005097 1.127101

Insights :

  • Terlihat pada item yang dipasangkan dengan Tas Makeup menghasilkan asosiasi atau hubungan yang cukup erat dimana nilai confidence yang tinggi diikuti metric lift. Selain itu dari ketiga rules yang terbentuk masing-masing terdapat produk Baju Renang Anak Perempuan.
  • Sedangkan item yang dipasangkan dengan Baju Renang Pria Anak-anak menghasilkan asosiasi atau hubungan yang tidak begitu erat dilihat dari nilai confidence yang rendah walaupun metric lift lebih tinggi.

Kesimpulan

  • Produk Tas Makeup, Tas Pinggang Wanita, dan Baju Renang Anak Perempuan dapat diletakkan di area atau rak yang berdekatan agar memudahkan pembeli mengambilnya.
  • Produk Tas Makeup, Tas Pinggang Wanita, dan Baju Renang Anak Perempuan dapat dibuat promo diskon agar pembeli lebih tertarik membelinya.