252 lines
12 KiB
Python
252 lines
12 KiB
Python
from datetime import date
|
|
import re
|
|
import pandas as pd
|
|
import numpy as np
|
|
import seaborn as sns
|
|
import matplotlib.pyplot as plt
|
|
import streamlit as st
|
|
import time
|
|
from sklearn.preprocessing import MinMaxScaler
|
|
|
|
def normalize_data(df):
|
|
scaler = MinMaxScaler()
|
|
df[['Tanggal', 'Bulan', 'Tahun']] = scaler.fit_transform(df[['Tanggal', 'Bulan', 'Tahun']])
|
|
return df
|
|
|
|
def preprocess_data(df, tanggal, sep, dateformat):
|
|
df = prep_date(df, tanggal, sep, dateformat)
|
|
df = normalize_data(df)
|
|
return df
|
|
|
|
def prep_date(df, tanggal, sep, dateformat):
|
|
if dateformat == 'ddmmyy':
|
|
df['Tanggal'] = df[tanggal].apply(lambda x: int(x.split(sep)[0]))
|
|
df['Bulan'] = df[tanggal].apply(lambda x: int(x.split(sep)[1]))
|
|
df['Tahun'] = df[tanggal].apply(lambda x: int(x.split(sep)[2]))
|
|
elif dateformat == 'mmddyy':
|
|
df['Tanggal'] = df[tanggal].apply(lambda x: int(x.split(sep)[1]))
|
|
df['Bulan'] = df[tanggal].apply(lambda x: int(x.split(sep)[0]))
|
|
df['Tahun'] = df[tanggal].apply(lambda x: int(x.split(sep)[2]))
|
|
elif dateformat == 'yymmdd':
|
|
df['Tanggal'] = df[tanggal].apply(lambda x: int(x.split(sep)[2]))
|
|
df['Bulan'] = df[tanggal].apply(lambda x: int(x.split(sep)[1]))
|
|
df['Tahun'] = df[tanggal].apply(lambda x: int(x.split(sep)[0]))
|
|
return df
|
|
|
|
def dataset_settings(df, pembeli, tanggal, produk):
|
|
c1, c2 = st.columns((2, 1))
|
|
year_list = ['Semua']
|
|
year_list = np.append(year_list, df['Tahun'].unique())
|
|
by_year = c1.selectbox('Pilih Tahun ', (year_list))
|
|
if by_year != 'Semua':
|
|
df = df[df['Tahun'] == int(by_year)]
|
|
month_list = np.arange(1, 13) # Daftar bulan dari 1 sampai 12
|
|
by_months = c2.multiselect('Pilih Bulan', month_list)
|
|
if by_months:
|
|
df = df[df['Bulan'].isin(by_months)]
|
|
return df
|
|
|
|
def show_transaction_info(df, produk, pembeli):
|
|
try:
|
|
st.subheader(f'Informasi Transaksi:')
|
|
col1, col2 = st.columns(2)
|
|
total_produk = df[produk].nunique()
|
|
total_transaksi = df[pembeli].nunique()
|
|
total_barang_terjual = df[produk].sum() #menghitung jumlah total barang terjual
|
|
total_frekuensi_produk = len(df) #menghitung frekuensi total dari semua produk
|
|
col1.info(f'Produk terjual : {total_produk} Jenis')
|
|
col2.info(f'Total transaksi : {total_transaksi} Transaksi')
|
|
col2.info(f'Frekuensi total produk terjual : {total_frekuensi_produk} Produk Terjual') #menampilkan frekuensi total produk terjual
|
|
sort = col1.radio('Tentukan kategori produk', ('Terlaris', 'Kurang Laris'))
|
|
jumlah_options = list(range(1, total_produk + 1)) # Membuat daftar pilihan jumlah produk
|
|
default_index = 9 if total_produk >= 10 else 0 # Default index untuk 10, atau 0 jika kurang dari 10 produk
|
|
jumlah = col2.selectbox('Tentukan jumlah produk yang ingin ditampilkan', jumlah_options, index=default_index)
|
|
if sort == 'Terlaris':
|
|
most_sold = df[produk].value_counts().head(jumlah)
|
|
else:
|
|
most_sold = df[produk].value_counts().tail(jumlah)
|
|
most_sold = most_sold.sort_values(ascending=True)
|
|
if not most_sold.empty:
|
|
c1, c2 = st.columns([3, 1]) # Mengubah proporsi kolom
|
|
plt.figure(figsize=(10, 6)) # Meningkatkan ukuran grafik
|
|
plt.title('Grafik Penjualan', fontsize=20)
|
|
plt.xlabel('Produk', fontsize=14)
|
|
plt.ylabel('Jumlah', fontsize=14)
|
|
sns.barplot(data=most_sold)
|
|
plt.xticks(rotation=90) # Menjadikan label vertikal
|
|
c1.pyplot(plt)
|
|
c2.write(most_sold)
|
|
|
|
else:
|
|
st.warning("Tidak ada data yang sesuai dengan kriteria yang dipilih.")
|
|
except Exception as e:
|
|
st.error(f"Terjadi kesalahan saat menampilkan informasi transaksi: {str(e)}")
|
|
|
|
def data_summary(df, pembeli, tanggal, produk):
|
|
st.markdown('<p class="big-font">Setelan Dataset</p>', unsafe_allow_html=True)
|
|
col1, col2 = st.columns(2)
|
|
sep_option = col1.radio('Tentukan separator tanggal', options=[('-', 'Dash'), ('/', 'Slash')])
|
|
sep = sep_option[0]
|
|
dateformat = col2.radio('Tentukan format urutan tanggal', ('ddmmyy', 'mmddyy', 'yymmdd'))
|
|
try:
|
|
df = prep_date(df, tanggal, sep, dateformat)
|
|
except ValueError:
|
|
st.warning('Format Atau Separator tanggal salah! Silakan cek kembali dan pastikan pemisah yang benar.')
|
|
st.stop()
|
|
except IndexError:
|
|
st.warning('Format Atau Separator tanggal salah! Silakan cek kembali dan pastikan pemisah yang benar.')
|
|
st.stop()
|
|
df = dataset_settings(df, pembeli, tanggal, produk)
|
|
st.dataframe(df.sort_values(by=['Tahun', 'Bulan', 'Tanggal'], ascending=True), use_container_width=True)
|
|
show_transaction_info(df, produk, pembeli)
|
|
return df
|
|
|
|
def prep_frozenset(rules):
|
|
temp = re.sub(r'frozenset\({', '', str(rules))
|
|
temp = re.sub(r'}\)', '', temp)
|
|
return temp
|
|
|
|
def MBA(df, pembeli, produk):
|
|
st.header('Association Rule Mining Menggunakan Apriori')
|
|
|
|
# Input untuk menyesuaikan minimum support dan confidence
|
|
min_support = st.number_input("Masukkan minimum support:", min_value=0.0, max_value=1.0, format="%.3f")
|
|
min_confidence = st.number_input("Masukkan minimum confidence:", min_value=0.0, max_value=1.0, format="%.3f")
|
|
|
|
if st.button("Mulai Perhitungan Asosiasi"):
|
|
start_time = time.time()
|
|
transaction_list = []
|
|
for i in df[pembeli].unique():
|
|
tlist = list(set(df[df[pembeli]==i][produk]))
|
|
if len(tlist) > 0:
|
|
transaction_list.append(tlist)
|
|
|
|
# Hitung frekuensi itemset
|
|
item_counts = {}
|
|
total_transactions = len(transaction_list)
|
|
|
|
for transaction in transaction_list:
|
|
for item in transaction:
|
|
if item in item_counts:
|
|
item_counts[item] += 1
|
|
else:
|
|
item_counts[item] = 1
|
|
|
|
for i in range(len(transaction)):
|
|
for j in range(i + 1, len(transaction)):
|
|
itemset = frozenset([transaction[i], transaction[j]])
|
|
if itemset in item_counts:
|
|
item_counts[itemset] += 1
|
|
else:
|
|
item_counts[itemset] = 1
|
|
|
|
# Buat aturan asosiasi secara manual
|
|
rules = []
|
|
for itemset in item_counts:
|
|
if isinstance(itemset, frozenset) and len(itemset) == 2:
|
|
items = list(itemset)
|
|
support = item_counts[itemset] / total_transactions
|
|
|
|
if support >= min_support:
|
|
# Hitung confidence dan lift untuk setiap aturan
|
|
confidence_a_to_b = item_counts[itemset] / item_counts[items[0]]
|
|
confidence_b_to_a = item_counts[itemset] / item_counts[items[1]]
|
|
lift_a_to_b = confidence_a_to_b / (item_counts[items[1]] / total_transactions)
|
|
lift_b_to_a = confidence_b_to_a / (item_counts[items[0]] / total_transactions)
|
|
contribution_a_to_b = support * confidence_a_to_b
|
|
contribution_b_to_a = support * confidence_b_to_a
|
|
|
|
if confidence_a_to_b >= min_confidence:
|
|
rules.append({
|
|
'antecedents': items[0],
|
|
'consequents': items[1],
|
|
'support': support,
|
|
'confidence': confidence_a_to_b,
|
|
'lift': lift_a_to_b,
|
|
'contribution': contribution_a_to_b
|
|
})
|
|
|
|
if confidence_b_to_a >= min_confidence:
|
|
rules.append({
|
|
'antecedents': items[1],
|
|
'consequents': items[0],
|
|
'support': support,
|
|
'confidence': confidence_b_to_a,
|
|
'lift': lift_b_to_a,
|
|
'contribution': contribution_b_to_a
|
|
})
|
|
|
|
end_time = time.time()
|
|
processing_time = end_time - start_time
|
|
|
|
col1, col2 = st.columns(2)
|
|
col1.subheader('Hasil Rules (Aturan)')
|
|
st.write('Total rules yang dihasilkan :', len(rules))
|
|
col1.write(f'Waktu yang dibutuhkan untuk memproses rule: {processing_time:.2f} detik')
|
|
|
|
if len(rules) == 0:
|
|
st.write("Tidak ada aturan yang dihasilkan.")
|
|
else:
|
|
matrix = pd.DataFrame(rules)
|
|
matrix['antecedents'] = matrix['antecedents'].apply(lambda x: prep_frozenset(frozenset([x])))
|
|
matrix['consequents'] = matrix['consequents'].apply(lambda x: prep_frozenset(frozenset([x])))
|
|
matrix.reset_index(drop=True, inplace=True)
|
|
matrix.index += 1
|
|
col1.write(matrix, use_container_width=True)
|
|
|
|
col2.subheader('Keterangan')
|
|
col2.write("- Support = Seberapa sering sebuah rules tersebut muncul dalam data")
|
|
col2.write("- Confidence = Seberapa sering rules tersebut dikatakan benar")
|
|
col2.write("- Lift Ratio = Ukuran Kekuatan hubungan antara dua item")
|
|
col2.write("- Contribution = Kontribusi setiap rules terhadap peningkatan lift secara keseluruhan")
|
|
|
|
# Menampilkan rekomendasi stok barang untuk dibeli
|
|
col1, col2 = st.columns(2)
|
|
col1.subheader("Rekomendasi barang untuk dibeli:")
|
|
recommended_products = []
|
|
recommended_products_contribution = {}
|
|
|
|
# Ambil semua item dari antecedents dan consequents dari setiap aturan asosiasi
|
|
for antecedent, consequent, contribution in zip(matrix['antecedents'], matrix['consequents'], matrix['contribution']):
|
|
antecedent_list = antecedent.split(', ')
|
|
consequent_list = consequent.split(', ')
|
|
items = antecedent_list + consequent_list
|
|
|
|
# Hitung kontribusi masing-masing item
|
|
for item in items:
|
|
if item not in recommended_products_contribution:
|
|
recommended_products_contribution[item] = contribution
|
|
else:
|
|
recommended_products_contribution[item] += contribution
|
|
recommended_products.extend(items)
|
|
|
|
# Hapus duplikat item
|
|
recommended_products = list(set(recommended_products))
|
|
|
|
# Urutkan item berdasarkan kontribusi
|
|
recommended_products_sorted = sorted(recommended_products, key=lambda x: recommended_products_contribution[x], reverse=True)
|
|
|
|
# Tampilkan rekomendasi stok barang
|
|
for idx, item in enumerate(recommended_products_sorted, start=1):
|
|
col1.write(f"{idx}. <font color='red'>{item}</font>", unsafe_allow_html=True)
|
|
|
|
# Menampilkan informasi tentang produk yang paling laris terjual dalam bentuk tabel
|
|
most_sold = df[produk].value_counts()
|
|
if not most_sold.empty:
|
|
col2.subheader("Jumlah Produk Terjual")
|
|
col2.dataframe(most_sold, width=400, use_container_width=True)
|
|
else:
|
|
st.warning("Tidak ada data yang sesuai dengan kriteria yang dipilih.")
|
|
|
|
|
|
st.subheader('Rekomendasi Pembelian Barang:')
|
|
for a, c, supp, conf, lift, contrib in sorted(zip(matrix['antecedents'], matrix['consequents'], matrix['support'], matrix['confidence'], matrix['lift'], matrix['contribution']), key=lambda x: x[4], reverse=True):
|
|
st.info(f'Jika melakukan pembelian barang {a}, maka juga lakukan pembelian barang {c}')
|
|
st.write('Support : {:.4f}'.format(supp))
|
|
st.write('Confidence : {:.4f}'.format(conf))
|
|
st.write('Lift Ratio : {:.4f}'.format(lift))
|
|
st.write('Contribution : {:.4f}'.format(contrib))
|
|
st.write('')
|
|
|
|
st.markdown('<br><br>', unsafe_allow_html=True) # Menambahkan spasi vertikal
|