Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -299,9 +299,20 @@ st.markdown("""
|
|
| 299 |
border: 1px solid #e0e0e0;
|
| 300 |
border-radius: 12px;
|
| 301 |
margin-bottom: 25px;
|
|
|
|
|
|
|
|
|
|
| 302 |
}
|
| 303 |
.header-title {
|
| 304 |
text-align: center;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
}
|
| 306 |
</style>
|
| 307 |
""", unsafe_allow_html=True)
|
|
@@ -326,8 +337,22 @@ with st.container():
|
|
| 326 |
</div>
|
| 327 |
""", unsafe_allow_html=True)
|
| 328 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
|
| 330 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
|
| 332 |
import streamlit as st
|
| 333 |
import plotly.graph_objects as go
|
|
@@ -422,16 +447,19 @@ if 'temuan_kode_distrik' in df_local.columns:
|
|
| 422 |
]
|
| 423 |
pln_color = "#FFD700" # Kuning PLN
|
| 424 |
|
| 425 |
-
def
|
| 426 |
colors = []
|
|
|
|
| 427 |
blue_idx = 0
|
| 428 |
for company in df['nama_perusahaan']:
|
| 429 |
if 'PLN' in str(company).upper():
|
| 430 |
colors.append(pln_color)
|
|
|
|
| 431 |
else:
|
| 432 |
colors.append(pastel_blues[blue_idx % len(pastel_blues)])
|
|
|
|
| 433 |
blue_idx += 1
|
| 434 |
-
return colors
|
| 435 |
|
| 436 |
# Fungsi untuk membuat polar bar chart
|
| 437 |
def create_polar_bar_chart(df, area_name):
|
|
@@ -442,7 +470,7 @@ if 'temuan_kode_distrik' in df_local.columns:
|
|
| 442 |
df = df.sort_values('avg_monthly_ratio', ascending=True)
|
| 443 |
companies = df['nama_perusahaan'].tolist()
|
| 444 |
ratios = df['avg_monthly_ratio'].tolist()
|
| 445 |
-
colors =
|
| 446 |
|
| 447 |
# Hitung total temuan untuk menghitung sudut (proporsi)
|
| 448 |
total_findings = df_local[df_local['Area_Type'] == area_name].groupby('nama_perusahaan').size()
|
|
@@ -461,17 +489,22 @@ if 'temuan_kode_distrik' in df_local.columns:
|
|
| 461 |
current_angle += a
|
| 462 |
|
| 463 |
fig = go.Figure()
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 475 |
|
| 476 |
fig.update_layout(
|
| 477 |
title=f'{area_name} Area',
|
|
@@ -490,7 +523,15 @@ if 'temuan_kode_distrik' in df_local.columns:
|
|
| 490 |
showline=False
|
| 491 |
),
|
| 492 |
),
|
| 493 |
-
showlegend=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 494 |
margin=dict(t=40, b=20, l=20, r=20),
|
| 495 |
height=400,
|
| 496 |
paper_bgcolor="rgba(0,0,0,0)",
|
|
@@ -653,111 +694,6 @@ if not avg_ratio_per_location.empty:
|
|
| 653 |
st.markdown(insight_text, unsafe_allow_html=True)
|
| 654 |
else:
|
| 655 |
st.warning("No data available for location ratio calculation or all ratios are NaN.")
|
| 656 |
-
# =================== OBJECTIVE 2 — Active vs Inactive Locations (Treemap with Color Gradient) ===================
|
| 657 |
-
st.markdown(
|
| 658 |
-
"""
|
| 659 |
-
<style>
|
| 660 |
-
.section-title {
|
| 661 |
-
text-align: center;
|
| 662 |
-
font-size: 1.5rem;
|
| 663 |
-
font-weight: 600;
|
| 664 |
-
color: #2c3e50;
|
| 665 |
-
margin-bottom: 1.2rem;
|
| 666 |
-
}
|
| 667 |
-
.ai-insight {
|
| 668 |
-
background-color: #f8f9fa;
|
| 669 |
-
padding: 12px;
|
| 670 |
-
border-left: 4px solid #27ae60;
|
| 671 |
-
border-radius: 0 4px 4px 0;
|
| 672 |
-
font-size: 0.95rem;
|
| 673 |
-
line-height: 1.5;
|
| 674 |
-
margin-top: 1rem;
|
| 675 |
-
}
|
| 676 |
-
</style>
|
| 677 |
-
<h3 class='section-title'>OBJECTIVE 3 — Active vs Inactive Division: Who Leads?</h3>
|
| 678 |
-
""",
|
| 679 |
-
unsafe_allow_html=True
|
| 680 |
-
)
|
| 681 |
-
|
| 682 |
-
df_local = df_filtered.copy()
|
| 683 |
-
if df_local.empty:
|
| 684 |
-
st.warning("No data available after filtering.")
|
| 685 |
-
st.stop()
|
| 686 |
-
|
| 687 |
-
df_local['created_month'] = df_local['created_at'].dt.to_period('M')
|
| 688 |
-
|
| 689 |
-
# Hitung temuan per bulan per lokasi
|
| 690 |
-
findings_by_location_month = df_local.groupby(['created_month', 'nama']).size().reset_index(name='findings_count')
|
| 691 |
-
# Hitung jumlah orang unik per bulan per lokasi
|
| 692 |
-
creators_by_location_month = df_local.groupby(['created_month', 'nama'])['creator_nid'].nunique().reset_index(name='unique_creators')
|
| 693 |
-
# Gabung
|
| 694 |
-
merged_loc = findings_by_location_month.merge(creators_by_location_month, on=['created_month', 'nama'], how='outer')
|
| 695 |
-
# Isi NaN dengan 0 untuk kolom yang mungkin hilang dari merge
|
| 696 |
-
merged_loc = merged_loc.fillna({'findings_count': 0, 'unique_creators': 0})
|
| 697 |
-
# Filter untuk menghindari pembagian dengan nol
|
| 698 |
-
merged_loc = merged_loc[merged_loc['unique_creators'] > 0]
|
| 699 |
-
# Hitung rasio (ignore NaN)
|
| 700 |
-
merged_loc['ratio'] = merged_loc['findings_count'] / merged_loc['unique_creators']
|
| 701 |
-
merged_loc['ratio'] = merged_loc['ratio'].replace([np.inf, -np.inf], np.nan)
|
| 702 |
-
|
| 703 |
-
# Rata-rata bulanan per lokasi
|
| 704 |
-
avg_ratio_per_location = merged_loc.groupby('nama')['ratio'].mean().reset_index(name='avg_monthly_ratio')
|
| 705 |
-
|
| 706 |
-
# Filter hasil akhir untuk menghindari NaN
|
| 707 |
-
avg_ratio_per_location = avg_ratio_per_location.dropna(subset=['avg_monthly_ratio'])
|
| 708 |
-
|
| 709 |
-
# Plot Treemap dengan gradasi warna
|
| 710 |
-
if not avg_ratio_per_location.empty:
|
| 711 |
-
# Gunakan color_continuous_scale untuk gradasi warna: merah → kuning → hijau
|
| 712 |
-
fig_treemap = px.treemap(
|
| 713 |
-
avg_ratio_per_location,
|
| 714 |
-
path=['nama'], # Path untuk hierarki (hanya satu level di sini)
|
| 715 |
-
values='avg_monthly_ratio', # Nilai yang menentukan ukuran area
|
| 716 |
-
title='Avg Monthly Finding by Division',
|
| 717 |
-
labels={'avg_monthly_ratio': 'Avg Monthly Finding/Person Ratio', 'nama': 'Location'},
|
| 718 |
-
color='avg_monthly_ratio', # Warna berdasarkan nilai rasio (bukan kategori)
|
| 719 |
-
color_continuous_scale=[
|
| 720 |
-
[0.0, '#D32F2F'], # Merah untuk rendah
|
| 721 |
-
[0.5, '#FFB300'], # Kuning untuk sedang
|
| 722 |
-
[1.0, '#4CAF50'] # Hijau untuk tinggi
|
| 723 |
-
]
|
| 724 |
-
)
|
| 725 |
-
# Format hover
|
| 726 |
-
fig_treemap.update_traces(
|
| 727 |
-
hovertemplate="<b>%{label}</b><br>Avg Ratio: %{value:.2f}<extra></extra>"
|
| 728 |
-
)
|
| 729 |
-
fig_treemap.update_layout(height=600)
|
| 730 |
-
st.plotly_chart(fig_treemap, use_container_width=True)
|
| 731 |
-
|
| 732 |
-
# AI Insight untuk Treemap Lokasi (Business-focused)
|
| 733 |
-
if not avg_ratio_per_location.empty:
|
| 734 |
-
# Temukan lokasi dengan rasio tertinggi dan terendah
|
| 735 |
-
top_location = avg_ratio_per_location.loc[avg_ratio_per_location['avg_monthly_ratio'].idxmax()]
|
| 736 |
-
low_location = avg_ratio_per_location.loc[avg_ratio_per_location['avg_monthly_ratio'].idxmin()]
|
| 737 |
-
|
| 738 |
-
st.markdown("### Insight")
|
| 739 |
-
insight_text = (
|
| 740 |
-
f"<div class='ai-insight'>"
|
| 741 |
-
f"The treemap visualizes the average finding-to-person ratio per location using a <strong>color gradient</strong>, indicating reporting activity levels. "
|
| 742 |
-
f"Locations with <span style='color:#4CAF50; font-weight:bold;'>green</span> color have a high ratio, indicating high reporting activity or exposure. "
|
| 743 |
-
f"Those with <span style='color:#FFB300; font-weight:bold;'>yellow</span> color have a medium ratio, indicating moderate reporting. "
|
| 744 |
-
f"Locations with <span style='color:#D32F2F; font-weight:bold;'>red</span> color have a low ratio, indicating lower activity levels or potentially under-reporting. "
|
| 745 |
-
f"<strong>{top_location['nama']}</strong> shows the highest activity level "
|
| 746 |
-
f"(<strong>{top_location['avg_monthly_ratio']:.2f}</strong>). "
|
| 747 |
-
f"<strong>{low_location['nama']}</strong> shows the lowest activity level "
|
| 748 |
-
f"(<strong>{low_location['avg_monthly_ratio']:.2f}</strong>). "
|
| 749 |
-
f"Areas with high activity (green) warrant investigation into the underlying causes of frequent findings. "
|
| 750 |
-
f"Areas with low activity (red) should be reviewed to ensure reporting completeness and identify any hidden risks."
|
| 751 |
-
f"</div>"
|
| 752 |
-
)
|
| 753 |
-
st.markdown(insight_text, unsafe_allow_html=True)
|
| 754 |
-
else:
|
| 755 |
-
st.warning("No data available for location ratio calculation or all ratios are NaN.")
|
| 756 |
-
|
| 757 |
-
import streamlit as st
|
| 758 |
-
import plotly.express as px
|
| 759 |
-
import numpy as np
|
| 760 |
-
import pandas as pd
|
| 761 |
|
| 762 |
# =================== OBJECTIVE 3 - Frequency & Response Time ===================
|
| 763 |
st.markdown(
|
|
@@ -780,7 +716,7 @@ st.markdown(
|
|
| 780 |
margin-top: 1rem;
|
| 781 |
}
|
| 782 |
</style>
|
| 783 |
-
<h3 class='section-title'>OBJECTIVE
|
| 784 |
""",
|
| 785 |
unsafe_allow_html=True
|
| 786 |
)
|
|
@@ -869,11 +805,11 @@ avg_leadtime_per_executor = compute_executor_leadtime_by_pic(df_local)
|
|
| 869 |
col_3a, col_3c = st.columns(2)
|
| 870 |
|
| 871 |
with col_3a:
|
| 872 |
-
st.markdown("<h5>
|
| 873 |
if avg_ratio_per_nama.empty:
|
| 874 |
st.warning("No data for reporter analysis by division.")
|
| 875 |
else:
|
| 876 |
-
sort_option_3a = st.selectbox("Show
|
| 877 |
|
| 878 |
# Urutkan data dari tertinggi ke terendah
|
| 879 |
sorted_data_all = avg_ratio_per_nama.sort_values('avg_monthly_ratio', ascending=False)
|
|
@@ -932,11 +868,11 @@ with col_3a:
|
|
| 932 |
st.markdown(insight_text, unsafe_allow_html=True)
|
| 933 |
|
| 934 |
with col_3c:
|
| 935 |
-
st.markdown("<h5>
|
| 936 |
if avg_rate_per_creator.empty:
|
| 937 |
st.warning("No data for reporter analysis by creator_name.")
|
| 938 |
else:
|
| 939 |
-
sort_option_3c = st.selectbox("Show
|
| 940 |
|
| 941 |
# Urutkan data dari tertinggi ke terendah
|
| 942 |
sorted_data_all = avg_rate_per_creator.sort_values('avg_monthly_rate', ascending=False)
|
|
@@ -999,11 +935,11 @@ with col_3c:
|
|
| 999 |
col_3b, col_3d = st.columns(2)
|
| 1000 |
|
| 1001 |
with col_3b:
|
| 1002 |
-
st.markdown("<h5>
|
| 1003 |
if avg_leadtime_nama.empty:
|
| 1004 |
st.warning("No data for executor analysis by division.")
|
| 1005 |
else:
|
| 1006 |
-
sort_option_3b = st.selectbox("Show
|
| 1007 |
|
| 1008 |
# Urutkan data dari tertinggi ke terendah
|
| 1009 |
sorted_data_all = avg_leadtime_nama.sort_values('avg_monthly_leadtime', ascending=False)
|
|
@@ -1060,11 +996,11 @@ with col_3b:
|
|
| 1060 |
st.markdown(insight_text, unsafe_allow_html=True)
|
| 1061 |
|
| 1062 |
with col_3d:
|
| 1063 |
-
st.markdown("<h5>
|
| 1064 |
if avg_leadtime_per_executor.empty:
|
| 1065 |
st.warning("No data for executor analysis by nama_pic.")
|
| 1066 |
else:
|
| 1067 |
-
sort_option_3d = st.selectbox("Show
|
| 1068 |
|
| 1069 |
# Urutkan data dari tertinggi ke terendah
|
| 1070 |
sorted_data_all = avg_leadtime_per_executor.sort_values('avg_monthly_leadtime', ascending=False)
|
|
|
|
| 299 |
border: 1px solid #e0e0e0;
|
| 300 |
border-radius: 12px;
|
| 301 |
margin-bottom: 25px;
|
| 302 |
+
display: flex;
|
| 303 |
+
align-items: center;
|
| 304 |
+
justify-content: space-between;
|
| 305 |
}
|
| 306 |
.header-title {
|
| 307 |
text-align: center;
|
| 308 |
+
flex: 1;
|
| 309 |
+
}
|
| 310 |
+
.logo-container {
|
| 311 |
+
display: flex;
|
| 312 |
+
align-items: center;
|
| 313 |
+
justify-content: flex-end;
|
| 314 |
+
width: 100px; /* Sesuaikan ukuran logo */
|
| 315 |
+
height: auto;
|
| 316 |
}
|
| 317 |
</style>
|
| 318 |
""", unsafe_allow_html=True)
|
|
|
|
| 337 |
</div>
|
| 338 |
""", unsafe_allow_html=True)
|
| 339 |
|
| 340 |
+
# RIGHT — LOGO
|
| 341 |
+
with col3:
|
| 342 |
+
try:
|
| 343 |
+
st.image("pln.png", width=80) # Sesuaikan width sesuai kebutuhan
|
| 344 |
+
except:
|
| 345 |
+
st.write("") # Jika logo tidak ditemukan, kosongkan
|
| 346 |
+
|
| 347 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 348 |
+
|
| 349 |
+
|
| 350 |
|
| 351 |
|
| 352 |
+
import streamlit as st
|
| 353 |
+
import plotly.graph_objects as go
|
| 354 |
+
import numpy as np
|
| 355 |
+
import pandas as pd
|
| 356 |
|
| 357 |
import streamlit as st
|
| 358 |
import plotly.graph_objects as go
|
|
|
|
| 447 |
]
|
| 448 |
pln_color = "#FFD700" # Kuning PLN
|
| 449 |
|
| 450 |
+
def assign_colors_and_create_legend(df):
|
| 451 |
colors = []
|
| 452 |
+
names = []
|
| 453 |
blue_idx = 0
|
| 454 |
for company in df['nama_perusahaan']:
|
| 455 |
if 'PLN' in str(company).upper():
|
| 456 |
colors.append(pln_color)
|
| 457 |
+
names.append(company)
|
| 458 |
else:
|
| 459 |
colors.append(pastel_blues[blue_idx % len(pastel_blues)])
|
| 460 |
+
names.append(company)
|
| 461 |
blue_idx += 1
|
| 462 |
+
return colors, names
|
| 463 |
|
| 464 |
# Fungsi untuk membuat polar bar chart
|
| 465 |
def create_polar_bar_chart(df, area_name):
|
|
|
|
| 470 |
df = df.sort_values('avg_monthly_ratio', ascending=True)
|
| 471 |
companies = df['nama_perusahaan'].tolist()
|
| 472 |
ratios = df['avg_monthly_ratio'].tolist()
|
| 473 |
+
colors, legend_names = assign_colors_and_create_legend(df)
|
| 474 |
|
| 475 |
# Hitung total temuan untuk menghitung sudut (proporsi)
|
| 476 |
total_findings = df_local[df_local['Area_Type'] == area_name].groupby('nama_perusahaan').size()
|
|
|
|
| 489 |
current_angle += a
|
| 490 |
|
| 491 |
fig = go.Figure()
|
| 492 |
+
|
| 493 |
+
# Tambahkan satu trace untuk setiap perusahaan agar muncul di legend
|
| 494 |
+
for i, (comp, ratio, color, angle) in enumerate(zip(companies, ratios, colors, angles)):
|
| 495 |
+
fig.add_trace(go.Barpolar(
|
| 496 |
+
r=[ratio],
|
| 497 |
+
theta=[mid_angles[i]],
|
| 498 |
+
width=[angle],
|
| 499 |
+
marker_color=[color],
|
| 500 |
+
marker_line_color="white",
|
| 501 |
+
marker_line_width=1.2,
|
| 502 |
+
opacity=0.9,
|
| 503 |
+
hovertemplate="<b>%{text}</b><br>Avg Ratio: %{r[0]:.2f}<extra></extra>",
|
| 504 |
+
text=[comp],
|
| 505 |
+
name=comp, # 🔥 Nama perusahaan untuk legend
|
| 506 |
+
showlegend=True # 🔥 Tampilkan di legend
|
| 507 |
+
))
|
| 508 |
|
| 509 |
fig.update_layout(
|
| 510 |
title=f'{area_name} Area',
|
|
|
|
| 523 |
showline=False
|
| 524 |
),
|
| 525 |
),
|
| 526 |
+
showlegend=True, # 🔥 Aktifkan legend
|
| 527 |
+
legend=dict(
|
| 528 |
+
orientation="v",
|
| 529 |
+
yanchor="top",
|
| 530 |
+
y=1,
|
| 531 |
+
xanchor="left",
|
| 532 |
+
x=1.02,
|
| 533 |
+
font=dict(size=10)
|
| 534 |
+
),
|
| 535 |
margin=dict(t=40, b=20, l=20, r=20),
|
| 536 |
height=400,
|
| 537 |
paper_bgcolor="rgba(0,0,0,0)",
|
|
|
|
| 694 |
st.markdown(insight_text, unsafe_allow_html=True)
|
| 695 |
else:
|
| 696 |
st.warning("No data available for location ratio calculation or all ratios are NaN.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 697 |
|
| 698 |
# =================== OBJECTIVE 3 - Frequency & Response Time ===================
|
| 699 |
st.markdown(
|
|
|
|
| 716 |
margin-top: 1rem;
|
| 717 |
}
|
| 718 |
</style>
|
| 719 |
+
<h3 class='section-title'>OBJECTIVE 3 — Frequency & Response Time: Who Reports Well? Who Executes Well?</h3>
|
| 720 |
""",
|
| 721 |
unsafe_allow_html=True
|
| 722 |
)
|
|
|
|
| 805 |
col_3a, col_3c = st.columns(2)
|
| 806 |
|
| 807 |
with col_3a:
|
| 808 |
+
st.markdown("<h5>3a. Average Finding by Division (Reporter)</h5>", unsafe_allow_html=True)
|
| 809 |
if avg_ratio_per_nama.empty:
|
| 810 |
st.warning("No data for reporter analysis by division.")
|
| 811 |
else:
|
| 812 |
+
sort_option_3a = st.selectbox("Show 3a:", ["Top 10", "Bottom 10"], key='sort_3a')
|
| 813 |
|
| 814 |
# Urutkan data dari tertinggi ke terendah
|
| 815 |
sorted_data_all = avg_ratio_per_nama.sort_values('avg_monthly_ratio', ascending=False)
|
|
|
|
| 868 |
st.markdown(insight_text, unsafe_allow_html=True)
|
| 869 |
|
| 870 |
with col_3c:
|
| 871 |
+
st.markdown("<h5>3b. Average Finding Rate per Reporter (Name)</h5>", unsafe_allow_html=True)
|
| 872 |
if avg_rate_per_creator.empty:
|
| 873 |
st.warning("No data for reporter analysis by creator_name.")
|
| 874 |
else:
|
| 875 |
+
sort_option_3c = st.selectbox("Show 3b:", ["Top 10", "Bottom 10"], key='sort_3c')
|
| 876 |
|
| 877 |
# Urutkan data dari tertinggi ke terendah
|
| 878 |
sorted_data_all = avg_rate_per_creator.sort_values('avg_monthly_rate', ascending=False)
|
|
|
|
| 935 |
col_3b, col_3d = st.columns(2)
|
| 936 |
|
| 937 |
with col_3b:
|
| 938 |
+
st.markdown("<h5>3c. Average Lead Time by Division (Executor)</h5>", unsafe_allow_html=True)
|
| 939 |
if avg_leadtime_nama.empty:
|
| 940 |
st.warning("No data for executor analysis by division.")
|
| 941 |
else:
|
| 942 |
+
sort_option_3b = st.selectbox("Show 3c:", ["Top 10", "Bottom 10"], key='sort_3b')
|
| 943 |
|
| 944 |
# Urutkan data dari tertinggi ke terendah
|
| 945 |
sorted_data_all = avg_leadtime_nama.sort_values('avg_monthly_leadtime', ascending=False)
|
|
|
|
| 996 |
st.markdown(insight_text, unsafe_allow_html=True)
|
| 997 |
|
| 998 |
with col_3d:
|
| 999 |
+
st.markdown("<h5>3d. Average Lead Time by Executor (Name)</h5>", unsafe_allow_html=True)
|
| 1000 |
if avg_leadtime_per_executor.empty:
|
| 1001 |
st.warning("No data for executor analysis by nama_pic.")
|
| 1002 |
else:
|
| 1003 |
+
sort_option_3d = st.selectbox("Show 3d:", ["Top 10", "Bottom 10"], key='sort_3d')
|
| 1004 |
|
| 1005 |
# Urutkan data dari tertinggi ke terendah
|
| 1006 |
sorted_data_all = avg_leadtime_per_executor.sort_values('avg_monthly_leadtime', ascending=False)
|