first commit
This commit is contained in:
commit
e2d43bfd44
|
@ -0,0 +1 @@
|
|||
3.10.0
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,17 @@
|
|||
# Optimasi Pembelian Barang dengan Analisis Penjualan menggunakan Association Rule Mining
|
||||
|
||||
## Deskripsi Proyek
|
||||
Proyek ini merupakan bagian dari skripsi yang bertujuan untuk mengoptimalkan stok barang di sebuah toko dengan menerapkan analisis penjualan menggunakan metode Association Rule Mining. Association Rule Mining merupakan teknik dalam data mining yang digunakan untuk menemukan pola atau asosiasi antara item-item dalam dataset transaksi. Dengan menganalisis pola pembelian konsumen, toko dapat membuat strategi yang lebih efektif dalam pengelolaan stok barang, seperti menentukan produk-produk yang sering dibeli bersamaan atau menetapkan promosi produk yang tepat.
|
||||
|
||||
## Fitur Utama
|
||||
1. **Upload Dataset**: Pengguna dapat mengunggah dataset transaksi penjualan dalam format CSV.
|
||||
2. **Prapemrosesan Data**: Dataset akan diproses untuk memastikan bahwa format tanggal dan kolom-kolom lain sesuai dengan persyaratan analisis.
|
||||
3. **Analisis Penjualan**: Menampilkan ringkasan dataset transaksi penjualan, termasuk jumlah produk yang terjual, jumlah transaksi, dan produk terlaris.
|
||||
4. **Association Rule Mining**: Melakukan analisis Association Rule Mining menggunakan algoritma Apriori untuk menemukan aturan asosiasi antara produk.
|
||||
5. **Rekomendasi Pembelian Barang**: Berdasarkan hasil analisis, memberikan rekomendasi stok barang yang harus dibeli untuk meningkatkan penjualan.
|
||||
|
||||
## Persyaratan Sistem
|
||||
- Python 3.9
|
||||
- Libraries: Pandas, Streamlit, PIL (Pillow), Seaborn, Matplotlib, Re, Mlxtend, Datetime
|
||||
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
import pandas as pd
|
||||
import streamlit as st
|
||||
from PIL import Image
|
||||
from komputasimanual import data_summary, MBA
|
||||
|
||||
|
||||
st.set_page_config(page_title="Apriori Toko Handari", page_icon="images/basket.png", layout="wide")
|
||||
|
||||
st.markdown("""<style>
|
||||
.big-font { font-size: 30px !important; font-weight: bold; }
|
||||
body {
|
||||
font-size: 20px; /* Atur ukuran font keseluruhan */
|
||||
zoom: 125%;
|
||||
}
|
||||
</style>""", unsafe_allow_html=True)
|
||||
|
||||
st.title('Data Mining Apriori')
|
||||
st.write('ANALISIS DATA PENJUALAN')
|
||||
|
||||
|
||||
image = Image.open('images/image1.jpg')
|
||||
st.image(image)
|
||||
|
||||
|
||||
dataset_file = st.file_uploader("Upload Dataset Anda", type=['csv'])
|
||||
# st.write('Contoh format dataset : ')
|
||||
# st.write('- ID,DATE,ITEM')
|
||||
# st.write('- [Kaggle Groceries Dataset](https://www.kaggle.com/datasets/heeraldedhia/groceries-dataset?datasetId=877335&sortBy=voteCount)')
|
||||
|
||||
|
||||
if dataset_file is None:
|
||||
st.warning('Mohon upload dataset Anda!')
|
||||
st.stop()
|
||||
|
||||
try:
|
||||
df = pd.read_csv(dataset_file)
|
||||
except Exception as e:
|
||||
st.error(f"Terjadi kesalahan saat membaca file: {str(e)}")
|
||||
st.stop()
|
||||
|
||||
|
||||
if df is not None and not df.empty:
|
||||
try:
|
||||
pembeli, tanggal, produk = df.columns[0], df.columns[1], df.columns[2]
|
||||
|
||||
# Memanggil fungsi untuk prapemrosesan data
|
||||
df = data_summary(df, pembeli, tanggal, produk)
|
||||
|
||||
# Memanggil fungsi untuk melakukan Association Rule Mining menggunakan Apriori
|
||||
MBA(df, pembeli, produk)
|
||||
except IndexError:
|
||||
st.warning("Indeks di luar batas. Periksa bahwa dataset memiliki setidaknya tiga kolom.")
|
||||
except ValueError:
|
||||
st.warning("Terjadi kesalahan saat prapemrosesan data. Pastikan format data yang sesuai.")
|
||||
else:
|
||||
st.warning("Dataset kosong atau tidak valid. Mohon unggah dataset yang valid.")
|
Binary file not shown.
After Width: | Height: | Size: 150 KiB |
Binary file not shown.
After Width: | Height: | Size: 77 KiB |
Binary file not shown.
After Width: | Height: | Size: 104 KiB |
|
@ -0,0 +1,251 @@
|
|||
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, 4)) # 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), 'kombinasi')
|
||||
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 Pola 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 {a}, maka juga lakukan pembelian {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
|
|
@ -0,0 +1,196 @@
|
|||
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 mlxtend.preprocessing import TransactionEncoder
|
||||
from mlxtend.frequent_patterns import association_rules, apriori
|
||||
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('Tahun ', (year_list))
|
||||
if by_year != 'Semua':
|
||||
df = df[df['Tahun'] == int(by_year)]
|
||||
by_month = c2.slider('Bulan', 1, 12, (1, 12))
|
||||
df = df[df['Bulan'].between(int(by_month[0]), int(by_month[1]), inclusive="both")]
|
||||
return df
|
||||
|
||||
def show_transaction_info(df, produk, pembeli):
|
||||
try:
|
||||
col1, col2 = st.columns(2)
|
||||
st.subheader(f'Informasi Transaksi:')
|
||||
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}')
|
||||
col2.info(f'Total transaksi : {total_transaksi}')
|
||||
col2.info(f'Frekuensi total produk terjual : {total_frekuensi_produk}') #menampilkan frekuensi total produk terjual
|
||||
sort = col1.radio('Tentukan kategori produk', ('Terlaris', 'Kurang Laris'))
|
||||
jumlah = col2.slider('Tentukan jumlah produk', 0, total_produk, 10)
|
||||
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((2, 1))
|
||||
plt.figure(figsize=(8, 4))
|
||||
most_sold.plot(kind='bar')
|
||||
plt.title('Grafik Penjualan')
|
||||
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.header('Ringkasan Dataset')
|
||||
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 tanggal tidak sesuai! Silakan cek kembali dan pastikan format yang benar.')
|
||||
st.stop()
|
||||
except IndexError:
|
||||
st.warning('Separator tanggal salah! Silakan cek kembali dan pastikan pemisah yang benar.')
|
||||
st.stop()
|
||||
st.write('Setelan Tampilan Dataset:')
|
||||
df = dataset_settings(df, pembeli, tanggal, produk)
|
||||
st.dataframe(df.sort_values(by=['Tahun', 'Bulan', 'Tanggal'], ascending=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')
|
||||
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)
|
||||
te = TransactionEncoder()
|
||||
te_ary = te.fit(transaction_list).transform(transaction_list)
|
||||
df2 = pd.DataFrame(te_ary, columns=te.columns_)
|
||||
frequent_itemsets = apriori(df2, min_support=0.01, use_colnames=True) #nilai support yang digunakan
|
||||
try:
|
||||
rules = association_rules(frequent_itemsets, metric='confidence', min_threshold=0.06) #nilai confidence yang diinginkan
|
||||
except ValueError as e:
|
||||
st.error(f"Terjadi kesalahan saat menghasilkan aturan asosiasi: {str(e)}")
|
||||
st.stop()
|
||||
end_time = time.time()
|
||||
processing_time = end_time - start_time
|
||||
col1, col2 = st.columns(2)
|
||||
col1.subheader('Hasil Rules (Pola Pembelian Pelanggan)')
|
||||
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: #tidak ada aturan yang dihasilkan
|
||||
st.write("Tidak ada aturan yang dihasilkan.")
|
||||
else:
|
||||
antecedents = rules['antecedents'].apply(prep_frozenset)
|
||||
consequents = rules['consequents'].apply(prep_frozenset)
|
||||
matrix = {
|
||||
'antecedents': antecedents,
|
||||
'consequents': consequents,
|
||||
'support': rules['support'],
|
||||
'confidence': rules['confidence'],
|
||||
'lift ratio': rules['lift'],
|
||||
'contribution': rules['support'] * rules['confidence']
|
||||
}
|
||||
matrix = pd.DataFrame(matrix)
|
||||
matrix.reset_index(drop=True, inplace=True)
|
||||
matrix.index += 1
|
||||
col1.write(matrix)
|
||||
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)
|
||||
# Menerima rekomendasi stok barang berdasarkan antecedents dan consequents
|
||||
col1.subheader("Rekomendasi stok 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)
|
||||
else:
|
||||
st.warning("Tidak ada data yang sesuai dengan kriteria yang dipilih.")
|
||||
|
||||
for a, c, supp, conf, lift in sorted(zip(matrix['antecedents'], matrix['consequents'], matrix['support'], matrix['confidence'], matrix['lift ratio']), key=lambda x: x[4], reverse=True):
|
||||
st.info(f'Jika customer membeli {a}, maka customer juga membeli {c}')
|
||||
st.write('Support : {:.3f}'.format(supp))
|
||||
st.write('Confidence : {:.3f}'.format(conf))
|
||||
st.write('Lift Ratio : {:.3f}'.format(lift))
|
||||
st.write('Contribution : {:.3f}'.format(supp * conf))
|
||||
st.write('')
|
|
@ -0,0 +1,251 @@
|
|||
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
|
|
@ -0,0 +1,8 @@
|
|||
matplotlib
|
||||
mlxtend
|
||||
numpy
|
||||
pandas
|
||||
Pillow
|
||||
seaborn
|
||||
streamlit
|
||||
scikit-learn
|
|
@ -0,0 +1,20 @@
|
|||
mkdir -p ~/.streamlit/
|
||||
|
||||
echo "\
|
||||
[general]\n\
|
||||
email = \"email@domain\"\n\
|
||||
" > ~/.streamlit/credentials.toml
|
||||
|
||||
echo "\
|
||||
[server]\n\
|
||||
headless = true\n\
|
||||
enableCORS=false\n\
|
||||
port = $PORT\n\
|
||||
" > ~/.streamlit/config.toml
|
||||
|
||||
echo "\
|
||||
[theme]
|
||||
wideMode=true
|
||||
[server]\n\
|
||||
wideMode=true
|
||||
" > ~/.streamlit/config.toml
|
Loading…
Reference in New Issue