From f0bafa7b17970e60c623e62eecdc6b8646708870 Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Wed, 5 Jun 2024 19:29:40 +0200 Subject: [PATCH 01/20] Add Ruff linter in CI --- .drone.yml | 13 +++++++++++++ requirements.txt | 5 +++++ 2 files changed, 18 insertions(+) create mode 100644 .drone.yml create mode 100644 requirements.txt diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..09b59cd --- /dev/null +++ b/.drone.yml @@ -0,0 +1,13 @@ +kind: pipeline +name: default +type: docker + +on: + push: + +steps: + - name: lint + image: python:3.12 + commands: + - pip install --root-user-action=ignore -r requirements.txt + - ruff check . diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..63b5398 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +matplotlib>=3.5.0 +pandas>=1.5.0 +seaborn>=0.12.0 +streamlit>=1.35.0 +ruff>=0.4.8 -- 2.43.0 From 7ceae78dfca3e451a5cd861a35893dc9ad0b7161 Mon Sep 17 00:00:00 2001 From: bastien ollier Date: Fri, 21 Jun 2024 16:07:48 +0200 Subject: [PATCH 02/20] add dockerfile --- frontend/__pycache__/clusters.cpython-39.pyc | Bin 0 -> 2206 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 frontend/__pycache__/clusters.cpython-39.pyc diff --git a/frontend/__pycache__/clusters.cpython-39.pyc b/frontend/__pycache__/clusters.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3100a5c338bccfe4e96b7f036a3382b8a55c9c08 GIT binary patch literal 2206 zcmb7FO>f&a7$zx+Qk;(l#kOSV))j4mO&btnABP@>AzcdWFw6rQ^kQrf6jRBZNU}r9 zLu1INcE|pM9h_r+$1eK~yzZ30Fksl8m$Ku;YX^)1Uy&5~lJCR!d6cfNw;0Osm(k(x zA!C0~=X&wb*+n&vU=&k4X9e%_LUaYEzR10<{|!@~@?SFLzYyI3y+BFyBzjVbeHMq8 zSj}Q#<&&@X_a1kw{Pw#nElsCKoH!z?T09w(B z#UiR$YI3s}u&akS(okor->Y2AdKD9}W(_YZ)q&}yr9+`H8#L_cQAyJb!NvF{-{9lh zOEF-|jpGxjRNe7^Mx$a=cldKY;YVyDYW{@%_`}4jJ;hIizJK}_LZE;|?VX5s*~G8> z+JC_(K^;^GuJ)Bs-YbE&oVT~xG(2Z_*hJQ18X-4=S}Oln@0@c6f0V~T=kjk9P0+^N z`swq&i9H+6u4){W2ls}9ep!uHFhgaf=~us9WbulozLlf0e>%z>N)9jU?XWq(+42pn9A#DF zylmoB<~a3hRJaKMLp>$FsZ;&K2VI6j@+`lL^b^V_v%j>|J50Aaiw zBcYX#*I1m#UK zLL%ZpSV%17Of2{fv4DDd`fd2u_h77Sq5`X3BD_N%NCz7Lwx~sM=sPeo>T9B=exJ-; zG9Qq+N9Hp!jy($s5w~dEY{NK0i^!3=wIFja-d;jxL1s; Date: Fri, 21 Jun 2024 16:08:46 +0200 Subject: [PATCH 03/20] add dockerfile --- .drone.yml | 25 +++++++++++++++++++++++++ Dockerfile | 20 ++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 Dockerfile diff --git a/.drone.yml b/.drone.yml index 09b59cd..3fea3b0 100644 --- a/.drone.yml +++ b/.drone.yml @@ -11,3 +11,28 @@ steps: commands: - pip install --root-user-action=ignore -r requirements.txt - ruff check . + + - name: docker-image + image: plugins/docker + settings: + context: frontend/ + dockerfile: Dockerfile + registry: hub.codefirst.iut.uca.fr + repo: hub.codefirst.iut.uca.fr/bastien.ollier/miner + username: + from_secret: REGISTRY_USER + password: + from_secret: REGISTRY_PASSWORD + cache_from: + - hub.codefirst.iut.uca.fr/bastien.ollier/miner:latest + depends_on: [ lint ] + + - name: deploy-miner + image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest + environment: + IMAGENAME: hub.codefirst.iut.uca.fr/bastien.ollier/miner:latest + CONTAINERNAME: miner + COMMAND: create + OVERWRITE: true + ADMINS: baollier1 + depends_on: [ docker-image ] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..337af25 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.9-slim + +WORKDIR /app + +RUN apt-get update && apt-get install -y \ + build-essential \ + curl \ + software-properties-common \ + git \ + && rm -rf /var/lib/apt/lists/* + +RUN ls + +RUN pip3 install -r requirements.txt + +EXPOSE 8501 + +HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health + +ENTRYPOINT ["streamlit", "run", "streamlit_app.py", "--server.port=8501", "--server.address=0.0.0.0"] \ No newline at end of file -- 2.43.0 From d0399e045204fb368349e64a52b237f28f4045fc Mon Sep 17 00:00:00 2001 From: bastien ollier Date: Fri, 21 Jun 2024 16:11:36 +0200 Subject: [PATCH 04/20] add dockerfile --- Dockerfile | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 337af25..7a16a15 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,12 +9,8 @@ RUN apt-get update && apt-get install -y \ git \ && rm -rf /var/lib/apt/lists/* -RUN ls - +COPY . . RUN pip3 install -r requirements.txt -EXPOSE 8501 - -HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health - -ENTRYPOINT ["streamlit", "run", "streamlit_app.py", "--server.port=8501", "--server.address=0.0.0.0"] \ No newline at end of file +EXPOSE 80 +ENTRYPOINT ["streamlit", "run", "streamlit_app.py", "--server.port=80", "--server.address=0.0.0.0"] \ No newline at end of file -- 2.43.0 From d8f7b63922c45b8af885eb7f0eef78d893f0c715 Mon Sep 17 00:00:00 2001 From: bastien ollier Date: Fri, 21 Jun 2024 16:14:15 +0200 Subject: [PATCH 05/20] add dockerfile --- .drone.yml | 1 - Dockerfile | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 3fea3b0..9ed1d7c 100644 --- a/.drone.yml +++ b/.drone.yml @@ -15,7 +15,6 @@ steps: - name: docker-image image: plugins/docker settings: - context: frontend/ dockerfile: Dockerfile registry: hub.codefirst.iut.uca.fr repo: hub.codefirst.iut.uca.fr/bastien.ollier/miner diff --git a/Dockerfile b/Dockerfile index 7a16a15..bee20cf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,4 +13,4 @@ COPY . . RUN pip3 install -r requirements.txt EXPOSE 80 -ENTRYPOINT ["streamlit", "run", "streamlit_app.py", "--server.port=80", "--server.address=0.0.0.0"] \ No newline at end of file +ENTRYPOINT ["streamlit", "run", "frontend/main.py", "--server.port=80", "--server.address=0.0.0.0"] \ No newline at end of file -- 2.43.0 From f1eb757bcaf4d53954c5526ee4e9201eb8e5406c Mon Sep 17 00:00:00 2001 From: bastien ollier Date: Fri, 21 Jun 2024 16:23:19 +0200 Subject: [PATCH 06/20] add dockerfile --- .drone.yml | 3 ++- Dockerfile | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 9ed1d7c..dc45553 100644 --- a/.drone.yml +++ b/.drone.yml @@ -33,5 +33,6 @@ steps: CONTAINERNAME: miner COMMAND: create OVERWRITE: true - ADMINS: baollier1 + ADMINS: bastienollier,clementfreville2,hugopradier2 + DRONE_REPO_OWNER: bastien.ollier depends_on: [ docker-image ] diff --git a/Dockerfile b/Dockerfile index bee20cf..279f3fe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,4 +13,4 @@ COPY . . RUN pip3 install -r requirements.txt EXPOSE 80 -ENTRYPOINT ["streamlit", "run", "frontend/main.py", "--server.port=80", "--server.address=0.0.0.0"] \ No newline at end of file +ENTRYPOINT ["streamlit", "run", "frontend/main.py", "--server.port=80", "--server.address=0.0.0.0", "--server.baseUrlPath=/containers/bastienollier-miner"] \ No newline at end of file -- 2.43.0 From af1d5b0beee6847d710a22180118d2f753b1ad32 Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Fri, 21 Jun 2024 16:34:14 +0200 Subject: [PATCH 07/20] Update to Python 3.12 --- .drone.yml | 22 ++++++++++++++-------- Dockerfile | 11 ++--------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/.drone.yml b/.drone.yml index dc45553..4610933 100644 --- a/.drone.yml +++ b/.drone.yml @@ -2,8 +2,9 @@ kind: pipeline name: default type: docker -on: - push: +trigger: + event: + - push steps: - name: lint @@ -27,12 +28,17 @@ steps: depends_on: [ lint ] - name: deploy-miner - image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest + image: hub.codefirst.iut.uca.fr/clement.freville2/codefirst-dockerproxy-clientdrone:latest + settings: + image: hub.codefirst.iut.uca.fr/bastien.ollier/miner:latest + container: miner + command: create + overwrite: true + admins: bastienollier,clementfreville2,hugopradier2 environment: - IMAGENAME: hub.codefirst.iut.uca.fr/bastien.ollier/miner:latest - CONTAINERNAME: miner - COMMAND: create - OVERWRITE: true - ADMINS: bastienollier,clementfreville2,hugopradier2 DRONE_REPO_OWNER: bastien.ollier depends_on: [ docker-image ] + when: + branch: + - main + - ci/* diff --git a/Dockerfile b/Dockerfile index 279f3fe..dd96397 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,9 @@ -FROM python:3.9-slim +FROM python:3.12-slim WORKDIR /app -RUN apt-get update && apt-get install -y \ - build-essential \ - curl \ - software-properties-common \ - git \ - && rm -rf /var/lib/apt/lists/* - COPY . . RUN pip3 install -r requirements.txt EXPOSE 80 -ENTRYPOINT ["streamlit", "run", "frontend/main.py", "--server.port=80", "--server.address=0.0.0.0", "--server.baseUrlPath=/containers/bastienollier-miner"] \ No newline at end of file +ENTRYPOINT ["streamlit", "run", "frontend/exploration.py", "--server.port=80", "--server.address=0.0.0.0", "--server.baseUrlPath=/containers/bastienollier-miner"] -- 2.43.0 From c87308cc21f36963f8cfff5735e5a25b5c0afd64 Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Fri, 21 Jun 2024 16:46:35 +0200 Subject: [PATCH 08/20] Support multiple column delimiters --- frontend/exploration.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/exploration.py b/frontend/exploration.py index 4cac622..7c233b4 100644 --- a/frontend/exploration.py +++ b/frontend/exploration.py @@ -1,5 +1,6 @@ import pandas as pd import streamlit as st +import codecs st.set_page_config( page_title="Project Miner", @@ -9,10 +10,13 @@ st.set_page_config( st.title("Home") ### Exploration -uploaded_file = st.file_uploader("Upload your CSV file", type=["csv"]) +uploaded_file = st.file_uploader("Upload your CSV file", type=["csv", "tsv"]) +separator = st.selectbox("Separator", [",", ";", "\\t"]) +separator = codecs.getdecoder("unicode_escape")(separator)[0] +has_header = st.checkbox("Has header", value=True) if uploaded_file is not None: - st.session_state.data = pd.read_csv(uploaded_file) + st.session_state.data = pd.read_csv(uploaded_file, sep=separator, header=0 if has_header else 1) st.session_state.original_data = st.session_state.data st.success("File loaded successfully!") -- 2.43.0 From 4293eafa86d392986d9f0ddf78f9e55795c68b42 Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Fri, 21 Jun 2024 16:50:03 +0200 Subject: [PATCH 09/20] Remove cache file --- frontend/__pycache__/clusters.cpython-39.pyc | Bin 2206 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 frontend/__pycache__/clusters.cpython-39.pyc diff --git a/frontend/__pycache__/clusters.cpython-39.pyc b/frontend/__pycache__/clusters.cpython-39.pyc deleted file mode 100644 index 3100a5c338bccfe4e96b7f036a3382b8a55c9c08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2206 zcmb7FO>f&a7$zx+Qk;(l#kOSV))j4mO&btnABP@>AzcdWFw6rQ^kQrf6jRBZNU}r9 zLu1INcE|pM9h_r+$1eK~yzZ30Fksl8m$Ku;YX^)1Uy&5~lJCR!d6cfNw;0Osm(k(x zA!C0~=X&wb*+n&vU=&k4X9e%_LUaYEzR10<{|!@~@?SFLzYyI3y+BFyBzjVbeHMq8 zSj}Q#<&&@X_a1kw{Pw#nElsCKoH!z?T09w(B z#UiR$YI3s}u&akS(okor->Y2AdKD9}W(_YZ)q&}yr9+`H8#L_cQAyJb!NvF{-{9lh zOEF-|jpGxjRNe7^Mx$a=cldKY;YVyDYW{@%_`}4jJ;hIizJK}_LZE;|?VX5s*~G8> z+JC_(K^;^GuJ)Bs-YbE&oVT~xG(2Z_*hJQ18X-4=S}Oln@0@c6f0V~T=kjk9P0+^N z`swq&i9H+6u4){W2ls}9ep!uHFhgaf=~us9WbulozLlf0e>%z>N)9jU?XWq(+42pn9A#DF zylmoB<~a3hRJaKMLp>$FsZ;&K2VI6j@+`lL^b^V_v%j>|J50Aaiw zBcYX#*I1m#UK zLL%ZpSV%17Of2{fv4DDd`fd2u_h77Sq5`X3BD_N%NCz7Lwx~sM=sPeo>T9B=exJ-; zG9Qq+N9Hp!jy($s5w~dEY{NK0i^!3=wIFja-d;jxL1s; Date: Fri, 21 Jun 2024 16:53:00 +0200 Subject: [PATCH 10/20] Add CI/CD (#9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: clfreville2 Co-authored-by: bastien ollier Reviewed-on: https://codefirst.iut.uca.fr/git/clement.freville2/miner/pulls/9 Reviewed-by: Clément FRÉVILLE Co-authored-by: Bastien OLLIER Co-committed-by: Bastien OLLIER --- .drone.yml | 44 ++++++++++++++++++++++++++++++++++++++++++++ Dockerfile | 9 +++++++++ requirements.txt | 5 +++++ 3 files changed, 58 insertions(+) create mode 100644 .drone.yml create mode 100644 Dockerfile create mode 100644 requirements.txt diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..4610933 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,44 @@ +kind: pipeline +name: default +type: docker + +trigger: + event: + - push + +steps: + - name: lint + image: python:3.12 + commands: + - pip install --root-user-action=ignore -r requirements.txt + - ruff check . + + - name: docker-image + image: plugins/docker + settings: + dockerfile: Dockerfile + registry: hub.codefirst.iut.uca.fr + repo: hub.codefirst.iut.uca.fr/bastien.ollier/miner + username: + from_secret: REGISTRY_USER + password: + from_secret: REGISTRY_PASSWORD + cache_from: + - hub.codefirst.iut.uca.fr/bastien.ollier/miner:latest + depends_on: [ lint ] + + - name: deploy-miner + image: hub.codefirst.iut.uca.fr/clement.freville2/codefirst-dockerproxy-clientdrone:latest + settings: + image: hub.codefirst.iut.uca.fr/bastien.ollier/miner:latest + container: miner + command: create + overwrite: true + admins: bastienollier,clementfreville2,hugopradier2 + environment: + DRONE_REPO_OWNER: bastien.ollier + depends_on: [ docker-image ] + when: + branch: + - main + - ci/* diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..dd96397 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM python:3.12-slim + +WORKDIR /app + +COPY . . +RUN pip3 install -r requirements.txt + +EXPOSE 80 +ENTRYPOINT ["streamlit", "run", "frontend/exploration.py", "--server.port=80", "--server.address=0.0.0.0", "--server.baseUrlPath=/containers/bastienollier-miner"] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..63b5398 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +matplotlib>=3.5.0 +pandas>=1.5.0 +seaborn>=0.12.0 +streamlit>=1.35.0 +ruff>=0.4.8 -- 2.43.0 From 4d82767c68d3a81402ba97a5e6d86df2fb62a16c Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Fri, 21 Jun 2024 16:59:51 +0200 Subject: [PATCH 11/20] Add SkLearn to requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 63b5398..03bf2ac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ matplotlib>=3.5.0 pandas>=1.5.0 seaborn>=0.12.0 +scikit-learn>=0.23.0 streamlit>=1.35.0 ruff>=0.4.8 -- 2.43.0 From 9da6e2d594688346d398f9fb8bf2e6ee5684050b Mon Sep 17 00:00:00 2001 From: Bastien OLLIER Date: Tue, 25 Jun 2024 08:37:38 +0200 Subject: [PATCH 12/20] Add cluster stats (#13) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: bastien ollier Reviewed-on: https://codefirst.iut.uca.fr/git/clement.freville2/miner/pulls/13 Reviewed-by: Hugo PRADIER Reviewed-by: Clément FRÉVILLE Co-authored-by: Bastien OLLIER Co-committed-by: Bastien OLLIER --- frontend/clusters.py | 63 +++++++++++++++++++++++++++++ frontend/pages/clustering_dbscan.py | 11 ++--- frontend/pages/clustering_kmeans.py | 14 +++---- 3 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 frontend/clusters.py diff --git a/frontend/clusters.py b/frontend/clusters.py new file mode 100644 index 0000000..ac2af4c --- /dev/null +++ b/frontend/clusters.py @@ -0,0 +1,63 @@ +from sklearn.cluster import DBSCAN, KMeans +import numpy as np + +class DBSCAN_cluster(): + def __init__(self, eps, min_samples,data): + self.eps = eps + self.min_samples = min_samples + self.data = data + self.labels = np.array([]) + + def run(self): + dbscan = DBSCAN(eps=self.eps, min_samples=self.min_samples) + self.labels = dbscan.fit_predict(self.data) + return self.labels + + def get_stats(self): + unique_labels = np.unique(self.labels) + stats = [] + for label in unique_labels: + if label == -1: + continue + cluster_points = self.data[self.labels == label] + num_points = len(cluster_points) + density = num_points / (np.max(cluster_points, axis=0) - np.min(cluster_points, axis=0)).prod() + stats.append({ + "cluster": label, + "num_points": num_points, + "density": density + }) + + return stats + + +class KMeans_cluster(): + def __init__(self, n_clusters, n_init, max_iter, data): + self.n_clusters = n_clusters + self.n_init = n_init + self.max_iter = max_iter + self.data = data + self.labels = np.array([]) + self.centers = [] + + def run(self): + kmeans = KMeans(n_clusters=self.n_clusters, init="random", n_init=self.n_init, max_iter=self.max_iter, random_state=111) + self.labels = kmeans.fit_predict(self.data) + self.centers = kmeans.cluster_centers_ + return self.labels + + + def get_stats(self): + unique_labels = np.unique(self.labels) + stats = [] + + for label in unique_labels: + cluster_points = self.data[self.labels == label] + num_points = len(cluster_points) + center = self.centers[label] + stats.append({ + 'cluster': label, + 'num_points': num_points, + 'center': center + }) + return stats diff --git a/frontend/pages/clustering_dbscan.py b/frontend/pages/clustering_dbscan.py index d06b10a..7ca16f6 100644 --- a/frontend/pages/clustering_dbscan.py +++ b/frontend/pages/clustering_dbscan.py @@ -1,10 +1,9 @@ import streamlit as st import matplotlib.pyplot as plt -from sklearn.cluster import DBSCAN +from clusters import DBSCAN_cluster st.header("Clustering: dbscan") - if "data" in st.session_state: data = st.session_state.data @@ -17,8 +16,9 @@ if "data" in st.session_state: if len(data_name) >= 2 and len(data_name) <=3: x = data[data_name].to_numpy() - dbscan = DBSCAN(eps=eps, min_samples=min_samples) - y_dbscan = dbscan.fit_predict(x) + dbscan = DBSCAN_cluster(eps,min_samples,x) + y_dbscan = dbscan.run() + st.table(dbscan.get_stats()) fig = plt.figure() if len(data_name) == 2: @@ -28,8 +28,5 @@ if "data" in st.session_state: ax = fig.add_subplot(projection='3d') ax.scatter(x[:, 0], x[:, 1],x[:, 2], c=y_dbscan, s=50, cmap="viridis") st.pyplot(fig) - - - else: st.error("file not loaded") \ No newline at end of file diff --git a/frontend/pages/clustering_kmeans.py b/frontend/pages/clustering_kmeans.py index c61bf40..63c7d55 100644 --- a/frontend/pages/clustering_kmeans.py +++ b/frontend/pages/clustering_kmeans.py @@ -1,10 +1,9 @@ import streamlit as st -from sklearn.cluster import KMeans import matplotlib.pyplot as plt +from clusters import KMeans_cluster st.header("Clustering: kmeans") - if "data" in st.session_state: data = st.session_state.data @@ -23,21 +22,22 @@ if "data" in st.session_state: if len(data_name) >= 2 and len(data_name) <=3: x = data[data_name].to_numpy() - kmeans = KMeans(n_clusters=n_clusters, init="random", n_init=n_init, max_iter=max_iter, random_state=111) - y_kmeans = kmeans.fit_predict(x) + kmeans = KMeans_cluster(n_clusters, n_init, max_iter, x) + y_kmeans = kmeans.run() + st.table(kmeans.get_stats()) + + centers = kmeans.centers fig = plt.figure() if len(data_name) == 2: ax = fig.add_subplot(projection='rectilinear') plt.scatter(x[:, 0], x[:, 1], c=y_kmeans, s=50, cmap="viridis") - centers = kmeans.cluster_centers_ plt.scatter(centers[:, 0], centers[:, 1], c="black", s=200, marker="X") else: ax = fig.add_subplot(projection='3d') ax.scatter(x[:, 0], x[:, 1],x[:, 2], c=y_kmeans, s=50, cmap="viridis") - centers = kmeans.cluster_centers_ - ax.scatter(centers[:, 0], centers[:, 1],centers[:, 2], c="black", s=200, marker="X") + ax.scatter(centers[:, 0], centers[:, 1], centers[:, 2], c="black", s=200, marker="X") st.pyplot(fig) else: -- 2.43.0 From 01168f3588d9449ad92d36333635334642b8cef8 Mon Sep 17 00:00:00 2001 From: bastien Date: Tue, 25 Jun 2024 18:06:30 +0200 Subject: [PATCH 13/20] add visu to prediction regression --- frontend/pages/prediction_regression.py | 31 +++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/frontend/pages/prediction_regression.py b/frontend/pages/prediction_regression.py index 377274e..42acf34 100644 --- a/frontend/pages/prediction_regression.py +++ b/frontend/pages/prediction_regression.py @@ -1,6 +1,8 @@ import streamlit as st from sklearn.linear_model import LinearRegression import pandas as pd +import matplotlib.pyplot as plt +import numpy as np st.header("Prediction: Regression") @@ -25,5 +27,34 @@ if "data" in st.session_state: prediction = model.predict(pd.DataFrame([pred_values], columns=data_name)) st.write("Prediction:", prediction[0]) + + fig = plt.figure() + dataframe_sorted = pd.concat([X, y], axis=1).sort_values(by=data_name) + + if len(data_name) == 1: + X = dataframe_sorted[data_name[0]] + y = dataframe_sorted[target_name] + + prediction_array_y = [ + model.predict(pd.DataFrame([[dataframe_sorted[data_name[0]].iloc[i]]], columns=data_name))[0] + for i in range(dataframe_sorted.shape[0]) + ] + + plt.scatter(dataframe_sorted[data_name[0]], dataframe_sorted[target_name], color='b') + plt.scatter(dataframe_sorted[data_name[0]], prediction_array_y, color='r') + else: + ax = fig.add_subplot(111, projection='3d') + + prediction_array_y = [ + model.predict(pd.DataFrame([[dataframe_sorted[data_name[0]].iloc[i], dataframe_sorted[data_name[1]].iloc[i]]], columns=data_name))[0] + for i in range(dataframe_sorted.shape[0]) + ] + + ax.scatter(dataframe_sorted[data_name[0]], dataframe_sorted[data_name[1]], dataframe_sorted[target_name], color='b') + ax.scatter(dataframe_sorted[data_name[0]], dataframe_sorted[data_name[1]], prediction_array_y, color='r') + + st.pyplot(fig) + + else: st.error("File not loaded") -- 2.43.0 From 405439564147fc6430b9a933e156fe1e480372da Mon Sep 17 00:00:00 2001 From: bastien Date: Tue, 25 Jun 2024 19:54:35 +0200 Subject: [PATCH 14/20] update --- frontend/pages/prediction_classification.py | 23 +++++++++++++++++++++ frontend/pages/prediction_regression.py | 7 +++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/frontend/pages/prediction_classification.py b/frontend/pages/prediction_classification.py index 5aaf52f..20ae5e1 100644 --- a/frontend/pages/prediction_classification.py +++ b/frontend/pages/prediction_classification.py @@ -4,6 +4,8 @@ from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score from sklearn.preprocessing import LabelEncoder import pandas as pd +import matplotlib.pyplot as plt + st.header("Prediction: Classification") @@ -60,5 +62,26 @@ if "data" in st.session_state: prediction = label_encoders[target_name].inverse_transform(prediction) st.write("Prediction:", prediction[0]) + + + + + fig = plt.figure() + dataframe_sorted = pd.concat([X, y], axis=1).sort_values(by=data_name) + + X = dataframe_sorted[data_name[0]] + y = dataframe_sorted[target_name] + + prediction_array_y = [ + model.predict(pd.DataFrame([[dataframe_sorted[data_name[0]].iloc[i]]], columns=data_name))[0] + for i in range(dataframe_sorted.shape[0]) + ] + + plt.scatter(dataframe_sorted[data_name[0]], dataframe_sorted[target_name], color='b') + plt.scatter(dataframe_sorted[data_name[0]], prediction_array_y, color='r') + + st.pyplot(fig) + + else: st.error("File not loaded") diff --git a/frontend/pages/prediction_regression.py b/frontend/pages/prediction_regression.py index 42acf34..6d125e0 100644 --- a/frontend/pages/prediction_regression.py +++ b/frontend/pages/prediction_regression.py @@ -41,8 +41,8 @@ if "data" in st.session_state: ] plt.scatter(dataframe_sorted[data_name[0]], dataframe_sorted[target_name], color='b') - plt.scatter(dataframe_sorted[data_name[0]], prediction_array_y, color='r') - else: + plt.plot(dataframe_sorted[data_name[0]], prediction_array_y, color='r') + elif len(data_name) == 2: ax = fig.add_subplot(111, projection='3d') prediction_array_y = [ @@ -51,10 +51,9 @@ if "data" in st.session_state: ] ax.scatter(dataframe_sorted[data_name[0]], dataframe_sorted[data_name[1]], dataframe_sorted[target_name], color='b') - ax.scatter(dataframe_sorted[data_name[0]], dataframe_sorted[data_name[1]], prediction_array_y, color='r') + ax.plot(dataframe_sorted[data_name[0]], dataframe_sorted[data_name[1]], prediction_array_y, color='r') st.pyplot(fig) - else: st.error("File not loaded") -- 2.43.0 From 27e69b2af8b4dfd1adb7c5441f8e0a713d1192d4 Mon Sep 17 00:00:00 2001 From: bastien ollier Date: Wed, 26 Jun 2024 10:45:50 +0200 Subject: [PATCH 15/20] add confusion_matrix --- frontend/pages/prediction_classification.py | 26 +++++++++------------ frontend/pages/prediction_regression.py | 2 +- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/frontend/pages/prediction_classification.py b/frontend/pages/prediction_classification.py index 20ae5e1..c11d7ee 100644 --- a/frontend/pages/prediction_classification.py +++ b/frontend/pages/prediction_classification.py @@ -1,11 +1,11 @@ import streamlit as st from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split -from sklearn.metrics import accuracy_score +from sklearn.metrics import accuracy_score,confusion_matrix from sklearn.preprocessing import LabelEncoder import pandas as pd import matplotlib.pyplot as plt - +import seaborn as sns st.header("Prediction: Classification") @@ -63,24 +63,20 @@ if "data" in st.session_state: st.write("Prediction:", prediction[0]) + if len(data_name) == 1: + fig = plt.figure() + y_pred = [model.predict(pd.DataFrame([pred_value[0]], columns=data_name)) for pred_value in X.values.tolist()] + print([x[0] for x in X.values.tolist()]) + cm = confusion_matrix(y, y_pred) + sns.heatmap(cm, annot=True, fmt="d") - fig = plt.figure() - dataframe_sorted = pd.concat([X, y], axis=1).sort_values(by=data_name) + plt.xlabel('Predicted') + plt.ylabel('True') - X = dataframe_sorted[data_name[0]] - y = dataframe_sorted[target_name] + st.pyplot(fig) - prediction_array_y = [ - model.predict(pd.DataFrame([[dataframe_sorted[data_name[0]].iloc[i]]], columns=data_name))[0] - for i in range(dataframe_sorted.shape[0]) - ] - - plt.scatter(dataframe_sorted[data_name[0]], dataframe_sorted[target_name], color='b') - plt.scatter(dataframe_sorted[data_name[0]], prediction_array_y, color='r') - - st.pyplot(fig) else: diff --git a/frontend/pages/prediction_regression.py b/frontend/pages/prediction_regression.py index 6d125e0..e06fa12 100644 --- a/frontend/pages/prediction_regression.py +++ b/frontend/pages/prediction_regression.py @@ -2,7 +2,6 @@ import streamlit as st from sklearn.linear_model import LinearRegression import pandas as pd import matplotlib.pyplot as plt -import numpy as np st.header("Prediction: Regression") @@ -31,6 +30,7 @@ if "data" in st.session_state: fig = plt.figure() dataframe_sorted = pd.concat([X, y], axis=1).sort_values(by=data_name) + if len(data_name) == 1: X = dataframe_sorted[data_name[0]] y = dataframe_sorted[target_name] -- 2.43.0 From da1e97f07f3dca68e0762700ba4a1b79f023dad3 Mon Sep 17 00:00:00 2001 From: bastien ollier Date: Wed, 26 Jun 2024 10:59:25 +0200 Subject: [PATCH 16/20] add r2 score --- frontend/pages/prediction_classification.py | 6 +----- frontend/pages/prediction_regression.py | 5 +++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/frontend/pages/prediction_classification.py b/frontend/pages/prediction_classification.py index c11d7ee..bb6bb22 100644 --- a/frontend/pages/prediction_classification.py +++ b/frontend/pages/prediction_classification.py @@ -67,7 +67,6 @@ if "data" in st.session_state: fig = plt.figure() y_pred = [model.predict(pd.DataFrame([pred_value[0]], columns=data_name)) for pred_value in X.values.tolist()] - print([x[0] for x in X.values.tolist()]) cm = confusion_matrix(y, y_pred) sns.heatmap(cm, annot=True, fmt="d") @@ -75,9 +74,6 @@ if "data" in st.session_state: plt.xlabel('Predicted') plt.ylabel('True') - st.pyplot(fig) - - - + st.pyplot(fig, figsize=(1, 1)) else: st.error("File not loaded") diff --git a/frontend/pages/prediction_regression.py b/frontend/pages/prediction_regression.py index e06fa12..35b648d 100644 --- a/frontend/pages/prediction_regression.py +++ b/frontend/pages/prediction_regression.py @@ -1,5 +1,6 @@ import streamlit as st from sklearn.linear_model import LinearRegression +from sklearn.metrics import r2_score import pandas as pd import matplotlib.pyplot as plt @@ -21,6 +22,10 @@ if "data" in st.session_state: model = LinearRegression() model.fit(X, y) + y_pred = [model.predict(pd.DataFrame([pred_value[0]], columns=data_name)) for pred_value in X.values.tolist()] + r2 = r2_score(y, y_pred) + st.write('R-squared score:', r2) + st.subheader("Enter values for prediction") pred_values = [st.number_input(f"Value for {feature}", value=0.0) for feature in data_name] prediction = model.predict(pd.DataFrame([pred_values], columns=data_name)) -- 2.43.0 From 9bc9e21e45412fca2f2ffce6230cb61573ad928c Mon Sep 17 00:00:00 2001 From: bastien ollier Date: Wed, 26 Jun 2024 11:05:04 +0200 Subject: [PATCH 17/20] add r2 score --- frontend/pages/prediction_regression.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/pages/prediction_regression.py b/frontend/pages/prediction_regression.py index 35b648d..a290c10 100644 --- a/frontend/pages/prediction_regression.py +++ b/frontend/pages/prediction_regression.py @@ -22,10 +22,6 @@ if "data" in st.session_state: model = LinearRegression() model.fit(X, y) - y_pred = [model.predict(pd.DataFrame([pred_value[0]], columns=data_name)) for pred_value in X.values.tolist()] - r2 = r2_score(y, y_pred) - st.write('R-squared score:', r2) - st.subheader("Enter values for prediction") pred_values = [st.number_input(f"Value for {feature}", value=0.0) for feature in data_name] prediction = model.predict(pd.DataFrame([pred_values], columns=data_name)) @@ -35,8 +31,11 @@ if "data" in st.session_state: fig = plt.figure() dataframe_sorted = pd.concat([X, y], axis=1).sort_values(by=data_name) - if len(data_name) == 1: + y_pred = [model.predict(pd.DataFrame([pred_value[0]], columns=data_name)) for pred_value in X.values.tolist()] + r2 = r2_score(y, y_pred) + st.write('R-squared score:', r2) + X = dataframe_sorted[data_name[0]] y = dataframe_sorted[target_name] -- 2.43.0 From 01ef19a2f80888d559a8fff910039bc0b057c2bc Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Wed, 26 Jun 2024 12:00:21 +0200 Subject: [PATCH 18/20] Merge files using strategies --- frontend/clusters.py | 78 ++++++++++++++++++----------- frontend/pages/clustering.py | 48 ++++++++++++++++++ frontend/pages/clustering_dbscan.py | 32 ------------ frontend/pages/clustering_kmeans.py | 44 ---------------- 4 files changed, 97 insertions(+), 105 deletions(-) create mode 100644 frontend/pages/clustering.py delete mode 100644 frontend/pages/clustering_dbscan.py delete mode 100644 frontend/pages/clustering_kmeans.py diff --git a/frontend/clusters.py b/frontend/clusters.py index ac2af4c..20e1927 100644 --- a/frontend/clusters.py +++ b/frontend/clusters.py @@ -1,25 +1,40 @@ from sklearn.cluster import DBSCAN, KMeans import numpy as np +from dataclasses import dataclass +from abc import ABC, abstractmethod +from typing import Any, Optional -class DBSCAN_cluster(): - def __init__(self, eps, min_samples,data): +@dataclass +class ClusterResult: + labels: np.array + centers: Optional[np.array] + statistics: list[dict[str, Any]] + + +class Cluster(ABC): + @abstractmethod + def run(self, data: np.array) -> ClusterResult: + pass + + +class DBSCANCluster(Cluster): + def __init__(self, eps: float = 0.5, min_samples: int = 5): self.eps = eps self.min_samples = min_samples - self.data = data - self.labels = np.array([]) - def run(self): + #@typing.override + def run(self, data: np.array) -> ClusterResult: dbscan = DBSCAN(eps=self.eps, min_samples=self.min_samples) - self.labels = dbscan.fit_predict(self.data) - return self.labels + labels = dbscan.fit_predict(data) + return ClusterResult(labels, None, self.get_statistics(data, labels)) - def get_stats(self): - unique_labels = np.unique(self.labels) + def get_statistics(self, data: np.array, labels: np.array) -> list[dict[str, Any]]: + unique_labels = np.unique(labels) stats = [] for label in unique_labels: if label == -1: continue - cluster_points = self.data[self.labels == label] + cluster_points = data[labels == label] num_points = len(cluster_points) density = num_points / (np.max(cluster_points, axis=0) - np.min(cluster_points, axis=0)).prod() stats.append({ @@ -27,37 +42,42 @@ class DBSCAN_cluster(): "num_points": num_points, "density": density }) - return stats - -class KMeans_cluster(): - def __init__(self, n_clusters, n_init, max_iter, data): + def __str__(self) -> str: + return "DBScan" + + +class KMeansCluster(Cluster): + def __init__(self, n_clusters: int = 8, n_init: int = 1, max_iter: int = 300): self.n_clusters = n_clusters self.n_init = n_init self.max_iter = max_iter - self.data = data - self.labels = np.array([]) - self.centers = [] - def run(self): + #@typing.override + def run(self, data: np.array) -> ClusterResult: kmeans = KMeans(n_clusters=self.n_clusters, init="random", n_init=self.n_init, max_iter=self.max_iter, random_state=111) - self.labels = kmeans.fit_predict(self.data) - self.centers = kmeans.cluster_centers_ - return self.labels + labels = kmeans.fit_predict(data) + centers = kmeans.cluster_centers_ + return ClusterResult(labels, centers, self.get_statistics(data, labels, centers)) - - def get_stats(self): - unique_labels = np.unique(self.labels) + def get_statistics(self, data: np.array, labels: np.array, centers: np.array) -> list[dict[str, Any]]: + unique_labels = np.unique(labels) stats = [] for label in unique_labels: - cluster_points = self.data[self.labels == label] + cluster_points = data[labels == label] num_points = len(cluster_points) - center = self.centers[label] + center = centers[label] stats.append({ - 'cluster': label, - 'num_points': num_points, - 'center': center + "cluster": label, + "num_points": num_points, + "center": center, }) return stats + + def __str__(self) -> str: + return "KMeans" + + +CLUSTERING_STRATEGIES = [DBSCANCluster(), KMeansCluster()] diff --git a/frontend/pages/clustering.py b/frontend/pages/clustering.py new file mode 100644 index 0000000..b3bf971 --- /dev/null +++ b/frontend/pages/clustering.py @@ -0,0 +1,48 @@ +import streamlit as st +import matplotlib.pyplot as plt +from clusters import DBSCANCluster, KMeansCluster, CLUSTERING_STRATEGIES + +st.header("Clustering") + +if "data" in st.session_state: + data = st.session_state.data + + general_row = st.columns([1, 1]) + clustering = general_row[0].selectbox("Clustering method", CLUSTERING_STRATEGIES) + data_name = general_row[1].multiselect("Data Name",data.select_dtypes(include="number").columns, max_selections=3) + + with st.form("cluster_form"): + if isinstance(clustering, KMeansCluster): + row1 = st.columns([1, 1, 1]) + clustering.n_clusters = row1[0].number_input("Number of clusters", min_value=1, max_value=data.shape[0], value=clustering.n_clusters) + clustering.n_init = row1[1].number_input("n_init", min_value=1, value=clustering.n_init) + clustering.max_iter = row1[2].number_input("max_iter", min_value=1, value=clustering.max_iter) + elif isinstance(clustering, DBSCANCluster): + clustering.eps = st.slider("eps", min_value=0.0001, max_value=1.0, step=0.1, value=clustering.eps) + clustering.min_samples = st.number_input("min_samples", min_value=1, value=clustering.min_samples) + + st.form_submit_button("Launch") + + if len(data_name) >= 2 and len(data_name) <=3: + x = data[data_name].to_numpy() + + result = clustering.run(x) + + st.table(result.statistics) + + fig = plt.figure() + if len(data_name) == 2: + ax = fig.add_subplot(projection='rectilinear') + plt.scatter(x[:, 0], x[:, 1], c=result.labels, s=50, cmap="viridis") + if result.centers is not None: + plt.scatter(result.centers[:, 0], result.centers[:, 1], c="black", s=200, marker="X") + else: + ax = fig.add_subplot(projection='3d') + + ax.scatter(x[:, 0], x[:, 1],x[:, 2], c=result.labels, s=50, cmap="viridis") + if result.centers is not None: + ax.scatter(result.centers[:, 0], result.centers[:, 1], result.centers[:, 2], c="black", s=200, marker="X") + st.pyplot(fig) + +else: + st.error("file not loaded") diff --git a/frontend/pages/clustering_dbscan.py b/frontend/pages/clustering_dbscan.py deleted file mode 100644 index 7ca16f6..0000000 --- a/frontend/pages/clustering_dbscan.py +++ /dev/null @@ -1,32 +0,0 @@ -import streamlit as st -import matplotlib.pyplot as plt -from clusters import DBSCAN_cluster - -st.header("Clustering: dbscan") - -if "data" in st.session_state: - data = st.session_state.data - - with st.form("my_form"): - data_name = st.multiselect("Data Name", data.select_dtypes(include="number").columns, max_selections=3) - eps = st.slider("eps", min_value=0.0, max_value=1.0, value=0.5, step=0.01) - min_samples = st.number_input("min_samples", step=1, min_value=1, value=5) - st.form_submit_button("launch") - - if len(data_name) >= 2 and len(data_name) <=3: - x = data[data_name].to_numpy() - - dbscan = DBSCAN_cluster(eps,min_samples,x) - y_dbscan = dbscan.run() - st.table(dbscan.get_stats()) - - fig = plt.figure() - if len(data_name) == 2: - ax = fig.add_subplot(projection='rectilinear') - plt.scatter(x[:, 0], x[:, 1], c=y_dbscan, s=50, cmap="viridis") - else: - ax = fig.add_subplot(projection='3d') - ax.scatter(x[:, 0], x[:, 1],x[:, 2], c=y_dbscan, s=50, cmap="viridis") - st.pyplot(fig) -else: - st.error("file not loaded") \ No newline at end of file diff --git a/frontend/pages/clustering_kmeans.py b/frontend/pages/clustering_kmeans.py deleted file mode 100644 index 63c7d55..0000000 --- a/frontend/pages/clustering_kmeans.py +++ /dev/null @@ -1,44 +0,0 @@ -import streamlit as st -import matplotlib.pyplot as plt -from clusters import KMeans_cluster - -st.header("Clustering: kmeans") - -if "data" in st.session_state: - data = st.session_state.data - - with st.form("my_form"): - row1 = st.columns([1,1,1]) - n_clusters = row1[0].selectbox("Number of clusters", range(1,data.shape[0])) - data_name = row1[1].multiselect("Data Name",data.select_dtypes(include="number").columns, max_selections=3) - n_init = row1[2].number_input("n_init",step=1,min_value=1) - - row2 = st.columns([1,1]) - max_iter = row1[0].number_input("max_iter",step=1,min_value=1) - - - st.form_submit_button("launch") - - if len(data_name) >= 2 and len(data_name) <=3: - x = data[data_name].to_numpy() - - kmeans = KMeans_cluster(n_clusters, n_init, max_iter, x) - y_kmeans = kmeans.run() - - st.table(kmeans.get_stats()) - - centers = kmeans.centers - fig = plt.figure() - if len(data_name) == 2: - ax = fig.add_subplot(projection='rectilinear') - plt.scatter(x[:, 0], x[:, 1], c=y_kmeans, s=50, cmap="viridis") - plt.scatter(centers[:, 0], centers[:, 1], c="black", s=200, marker="X") - else: - ax = fig.add_subplot(projection='3d') - - ax.scatter(x[:, 0], x[:, 1],x[:, 2], c=y_kmeans, s=50, cmap="viridis") - ax.scatter(centers[:, 0], centers[:, 1], centers[:, 2], c="black", s=200, marker="X") - st.pyplot(fig) - -else: - st.error("file not loaded") -- 2.43.0 From 7cb0d559699de6c6b4285a9af8ad23a674dbec37 Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Wed, 26 Jun 2024 20:45:55 +0200 Subject: [PATCH 19/20] Allow using PCA to reduce dataset dimensions --- frontend/pages/clustering.py | 52 +++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/frontend/pages/clustering.py b/frontend/pages/clustering.py index b3bf971..2c2fb8e 100644 --- a/frontend/pages/clustering.py +++ b/frontend/pages/clustering.py @@ -1,15 +1,19 @@ import streamlit as st import matplotlib.pyplot as plt from clusters import DBSCANCluster, KMeansCluster, CLUSTERING_STRATEGIES +from sklearn.decomposition import PCA +from sklearn.metrics import silhouette_score +import numpy as np st.header("Clustering") if "data" in st.session_state: data = st.session_state.data - general_row = st.columns([1, 1]) + general_row = st.columns([1, 1, 1]) clustering = general_row[0].selectbox("Clustering method", CLUSTERING_STRATEGIES) - data_name = general_row[1].multiselect("Data Name",data.select_dtypes(include="number").columns, max_selections=3) + data_name = general_row[1].multiselect("Columns", data.select_dtypes(include="number").columns) + n_components = general_row[2].number_input("Reduce dimensions to (PCA)", min_value=1, max_value=3, value=2) with st.form("cluster_form"): if isinstance(clustering, KMeansCluster): @@ -18,20 +22,50 @@ if "data" in st.session_state: clustering.n_init = row1[1].number_input("n_init", min_value=1, value=clustering.n_init) clustering.max_iter = row1[2].number_input("max_iter", min_value=1, value=clustering.max_iter) elif isinstance(clustering, DBSCANCluster): - clustering.eps = st.slider("eps", min_value=0.0001, max_value=1.0, step=0.1, value=clustering.eps) - clustering.min_samples = st.number_input("min_samples", min_value=1, value=clustering.min_samples) + row1 = st.columns([1, 1]) + clustering.eps = row1[0].slider("eps", min_value=0.0001, max_value=1.0, step=0.05, value=clustering.eps) + clustering.min_samples = row1[1].number_input("min_samples", min_value=1, value=clustering.min_samples) st.form_submit_button("Launch") - if len(data_name) >= 2 and len(data_name) <=3: + if len(data_name) > 0: x = data[data_name].to_numpy() + n_components = min(n_components, len(data_name)) + if len(data_name) > n_components: + pca = PCA(n_components) + x = pca.fit_transform(x) + if n_components == 2: + (fig, ax) = plt.subplots(figsize=(8, 8)) + for i in range(0, pca.components_.shape[1]): + ax.arrow( + 0, + 0, + pca.components_[0, i], + pca.components_[1, i], + head_width=0.1, + head_length=0.1 + ) + + plt.text( + pca.components_[0, i] + 0.05, + pca.components_[1, i] + 0.05, + data_name[i] + ) + circle = plt.Circle((0, 0), radius=1, edgecolor='b', facecolor='None') + ax.add_patch(circle) + plt.axis("equal") + ax.set_title("PCA result - Correlation circle") + st.pyplot(fig) result = clustering.run(x) - + st.write("## Cluster stats") st.table(result.statistics) + st.write("## Graphical representation") fig = plt.figure() - if len(data_name) == 2: + if n_components == 1: + plt.scatter(x, np.zeros_like(x)) + elif n_components == 2: ax = fig.add_subplot(projection='rectilinear') plt.scatter(x[:, 0], x[:, 1], c=result.labels, s=50, cmap="viridis") if result.centers is not None: @@ -43,6 +77,10 @@ if "data" in st.session_state: if result.centers is not None: ax.scatter(result.centers[:, 0], result.centers[:, 1], result.centers[:, 2], c="black", s=200, marker="X") st.pyplot(fig) + if not (result.labels == 0).all(): + st.write("Silhouette score:", silhouette_score(x, result.labels)) + else: + st.error("Select at least one column") else: st.error("file not loaded") -- 2.43.0 From f464f6166ae0cd045df8b7a064af26fcd5e66f74 Mon Sep 17 00:00:00 2001 From: gorky1234 Date: Wed, 26 Jun 2024 21:31:01 +0200 Subject: [PATCH 20/20] corection bug figsize --- frontend/pages/prediction_classification.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/pages/prediction_classification.py b/frontend/pages/prediction_classification.py index bb6bb22..648db06 100644 --- a/frontend/pages/prediction_classification.py +++ b/frontend/pages/prediction_classification.py @@ -74,6 +74,6 @@ if "data" in st.session_state: plt.xlabel('Predicted') plt.ylabel('True') - st.pyplot(fig, figsize=(1, 1)) + st.pyplot(fig) else: st.error("File not loaded") -- 2.43.0