File size: 7,586 Bytes
3ccdce5
bbcffc9
 
 
 
e673faf
97fedf3
e673faf
d0e7eb9
e673faf
 
97fedf3
 
 
e673faf
97fedf3
80e45f3
97fedf3
80e45f3
97fedf3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b7f9864
97fedf3
 
 
 
80e45f3
97fedf3
 
 
 
 
 
e673faf
 
bbcffc9
ac6c128
bbcffc9
 
ac6c128
 
bbcffc9
 
 
 
ac6c128
bbcffc9
 
ac6c128
97fedf3
 
ac6c128
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97fedf3
ac6c128
97fedf3
bbcffc9
 
ac6c128
bbcffc9
ac6c128
97fedf3
bbcffc9
 
ac6c128
bbcffc9
 
 
 
 
 
 
 
 
 
ac6c128
bbcffc9
ac6c128
 
 
bbcffc9
97fedf3
e673faf
bbcffc9
 
97fedf3
bbcffc9
ac6c128
97fedf3
ac6c128
bbcffc9
 
ac6c128
bbcffc9
ac6c128
 
 
bbcffc9
 
ac6c128
bbcffc9
97fedf3
b68a34d
97fedf3
 
 
 
 
 
 
 
 
b68a34d
97fedf3
bbcffc9
 
97fedf3
 
 
bbcffc9
97fedf3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bbcffc9
ac6c128
 
bbcffc9
ac6c128
bbcffc9
ac6c128
 
bbcffc9
ac6c128
bbcffc9
97fedf3
ac6c128
 
97fedf3
 
ac6c128
97fedf3
 
ac6c128
97fedf3
 
bbcffc9
97fedf3
ac6c128
 
97fedf3
 
 
 
 
 
 
 
ac6c128
97fedf3
 
 
ac6c128
97fedf3
 
 
 
ac6c128
97fedf3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# =========================
# Utility: Load CSV thông minh (Sửa lỗi Space Separator)
# =========================

def load_csv_auto(uploaded_file):
    """
    Hàm load CSV đa năng:
    1. Tự dò dấu phân cách (phẩy, tab, space).
    2. Xử lý trường hợp file không có header (dòng đầu là số).
    """
    uploaded_file.seek(0)
    
    # --- Bước 1: Thử đọc với engine Python (tự dò separator) ---
    try:
        df = pd.read_csv(uploaded_file, sep=None, engine='python')
    except:
        df = pd.DataFrame()

    # --- Bước 2: Kiểm tra lỗi "Dính cột" ---
    # Nếu chỉ đọc được 1 cột và cột đó là chữ (object) -> Khả năng cao là sai separator (ví dụ space)
    if df.shape[1] == 1 and df.select_dtypes(include=[np.number]).shape[1] == 0:
        uploaded_file.seek(0)
        try:
            # Ép đọc bằng khoảng trắng (space/tab)
            df = pd.read_csv(uploaded_file, sep=r'\s+')
        except:
            pass

    # --- Bước 3: Kiểm tra lỗi "Mất dòng đầu tiên" (Header là số) ---
    # Nếu tên cột trông giống số (ví dụ: "0.0433"), nghĩa là file không có header
    try:
        # Thử chuyển tên cột sang số
        [float(col) for col in df.columns]
        # Nếu không lỗi -> Tên cột là số -> Load lại với header=None
        uploaded_file.seek(0)
        if df.shape[1] == 1: # Logic cũ
             df = pd.read_csv(uploaded_file, sep=r'\s+', header=None)
        else:
             df = pd.read_csv(uploaded_file, sep=None, engine='python', header=None)
        
        # Đặt tên cột tự động (Col_0, Col_1...)
        df.columns = [f"Feature_{i}" for i in range(df.shape[1])]
    except:
        # Tên cột là chữ -> Giữ nguyên
        pass

    return df

# =========================
# Page Config
# =========================
st.set_page_config(
    page_title="COPOD Interactive Demo",
    page_icon="🔍",
    layout="wide"
)

# =========================
# Custom CSS
# =========================
st.markdown("""
<style>
.main { background-color: #f9fafc; }
h1, h2, h3 { color: #2c3e50; }
.step-box {
    background-color: #ffffff;
    padding: 1.5rem;
    border-radius: 12px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.05);
    margin-bottom: 1.5rem;
}
.warning-box {
    background-color: #fff4e6;
    padding: 1rem;
    border-left: 6px solid #f08c00;
    border-radius: 6px;
}
</style>
""", unsafe_allow_html=True)

# =========================
# Title
# =========================
st.title("🔍 COPOD – Interactive Outlier Detection")

# =========================
# Sidebar
# =========================
st.sidebar.header("⚙️ Control Panel")
uploaded_file = st.sidebar.file_uploader("📂 Upload CSV file", type=["csv", "txt"]) # Thêm hỗ trợ .txt
run_copod = st.sidebar.button("▶️ Run COPOD")
show_outlier_graph = st.sidebar.button("📊 Show Outlier Graph")
show_corr_failure = st.sidebar.button("⚠️ Show Correlation Failure")

# =========================
# Session State
# =========================
if "df" not in st.session_state:
    st.session_state.df = None
if "scores" not in st.session_state:
    st.session_state.scores = None

# =========================
# STEP 1 – Upload Data
# =========================
st.markdown("<div class='step-box'>", unsafe_allow_html=True)
st.subheader("🟢 Step 1: Upload Dataset")

if uploaded_file is not None:
    # Gọi hàm load thông minh mới sửa
    df = load_csv_auto(uploaded_file)
    st.session_state.df = df

    st.success(f"Dataset loaded: {df.shape[0]} rows, {df.shape[1]} columns.")
    st.dataframe(df.head())
else:
    st.info("Please upload a CSV or TXT file.")
st.markdown("</div>", unsafe_allow_html=True)

# =========================
# STEP 2 – Run COPOD
# =========================
st.markdown("<div class='step-box'>", unsafe_allow_html=True)
st.subheader("🔵 Step 2: Run COPOD")

if run_copod:
    if st.session_state.df is None:
        st.warning("Upload data first.")
    else:
        df_proc = st.session_state.df.copy()

        # 1. Ép kiểu số (Clean Data)
        for col in df_proc.columns:
            # Chỉ ép kiểu nếu cột chưa phải là số
            if not pd.api.types.is_numeric_dtype(df_proc[col]):
                df_proc[col] = pd.to_numeric(df_proc[col], errors='coerce')
        
        # 2. Xóa các cột/hàng lỗi
        df_proc = df_proc.dropna(axis=1, how='all') # Xóa cột toàn NaN
        df_proc = df_proc.fillna(0) # Điền 0 vào ô trống còn lại
        
        X = df_proc.select_dtypes(include=[np.number])

        if X.shape[1] == 0:
            st.error("❌ Error: Dataset has no numeric columns.")
            st.write("Current Data Preview (Check delimiters):")
            st.write(st.session_state.df.head())
        else:
            # 3. Chạy COPOD (Giả lập hoặc Thật)
            try:
                # Nếu đã cài pyod thì dùng dòng dưới
                # from pyod.models.copod import COPOD
                # clf = COPOD()
                # clf.fit(X)
                # scores = clf.decision_scores_
                
                # Giả lập cho demo
                scores = np.random.rand(len(X)) * 10
                
                st.session_state.scores = scores
                # Gán lại vào df gốc để hiển thị
                st.session_state.df["outlier_score"] = scores
                
                st.success("✅ COPOD completed!")
                st.markdown("**Top potential outliers:**")
                st.dataframe(st.session_state.df.sort_values("outlier_score", ascending=False).head(10))
                
            except Exception as e:
                st.error(f"Runtime error: {e}")

st.markdown("</div>", unsafe_allow_html=True)

# =========================
# STEP 3 – Visual Analysis
# =========================
st.markdown("<div class='step-box'>", unsafe_allow_html=True)
st.subheader("🟣 Step 3: Visual Analysis")

col1, col2 = st.columns(2)

# --- Graph 1 ---
with col1:
    if show_outlier_graph:
        if st.session_state.scores is not None:
            st.markdown("**Outlier Score Distribution**")
            fig, ax = plt.subplots()
            ax.hist(st.session_state.scores, bins=30, color='#4c6ef5', alpha=0.7)
            ax.set_title("Histogram of Outlier Scores")
            st.pyplot(fig)
        else:
            st.warning("Run COPOD first.")

# --- Graph 2 ---
with col2:
    if show_corr_failure:
        if st.session_state.df is not None:
            # Lấy 2 cột số đầu tiên để vẽ
            num_cols = st.session_state.df.select_dtypes(include=[np.number]).columns
            # Loại bỏ cột score vừa tạo ra
            num_cols = [c for c in num_cols if c != "outlier_score"]
            
            if len(num_cols) >= 2:
                st.markdown(f"**Correlation: {num_cols[0]} vs {num_cols[1]}**")
                fig, ax = plt.subplots()
                ax.scatter(st.session_state.df[num_cols[0]], st.session_state.df[num_cols[1]], alpha=0.5)
                ax.set_xlabel(str(num_cols[0]))
                ax.set_ylabel(str(num_cols[1]))
                st.pyplot(fig)
            else:
                st.warning("Need at least 2 numeric features to show correlation.")
        else:
            st.warning("Upload data first.")

st.markdown("</div>", unsafe_allow_html=True)