Eric2mangel commited on
Commit
c051e07
·
verified ·
1 Parent(s): a860e2e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +228 -69
app.py CHANGED
@@ -1,79 +1,238 @@
1
- import streamlit as st
2
  import pandas as pd
3
  import duckdb
4
  import polars as pl
5
- import pyarrow.csv as pv
 
6
  import time
7
  import os
 
 
8
  import tempfile
9
- import matplotlib.pyplot as plt
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- st.set_page_config(page_title="Speed Benchmark", layout="wide", initial_sidebar_state="expanded")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  # --- SIDEBAR ---
14
- st.sidebar.header("Fichiers de test")
 
 
 
15
  c1, c2 = st.sidebar.columns(2)
16
- if c1.button("Faker Text"):
17
- st.session_state.file = "faker_text.csv"
18
- if c2.button("Numeric Only"):
19
- st.session_state.file = "numeric_only.csv"
20
-
21
- uploaded = st.sidebar.file_uploader("Ou ton fichier", type=["csv","parquet"])
22
- if uploaded:
23
- with tempfile.NamedTemporaryFile(delete=False, suffix=".csv") as f:
24
- f.write(uploaded.read())
25
- st.session_state.file = f.name
26
- st.session_state.temp = f.name
27
-
28
- # --- MAIN ---
29
- st.title("Comparaison vitesse de chargement")
30
-
31
- if 'file' not in st.session_state:
32
- st.info("Choisis un fichier")
33
- st.stop()
34
-
35
- path = st.session_state.file
36
- st.write(f"**Fichier** : {os.path.basename(path)}")
37
-
38
- if st.button("Lancer le benchmark", type="primary"):
39
- results = []
40
-
41
- # Pandas
42
- t0 = time.time()
43
- df1 = pd.read_csv(path)
44
- results.append(("Pandas", time.time()-t0, len(df1)))
45
-
46
- # Polars
47
- t0 = time.time()
48
- df2 = pl.read_csv(path).to_pandas()
49
- results.append(("Polars", time.time()-t0, len(df2)))
50
-
51
- # DuckDB
52
- t0 = time.time()
53
- df3 = duckdb.read_csv(path).df()
54
- results.append(("DuckDB", time.time()-t0, len(df3)))
55
-
56
- # PyArrow (fix newlines)
57
- t0 = time.time()
58
- table = pv.read_csv(path, parse_options=pv.ParseOptions(newlines_in_values=True))
59
- df4 = table.to_pandas()
60
- results.append(("PyArrow", time.time()-t0, len(df4)))
61
-
62
- # Nettoyage
63
- if hasattr(st.session_state, 'temp'):
64
- os.unlink(st.session_state.temp)
65
-
66
- # Résultats
67
- df = pd.DataFrame(results, columns=["Moteur","Temps","Lignes"]).sort_values("Temps")
68
- winner_lines = int(df.iloc[0]["Lignes"]) # correction du bug len()
69
-
70
- col1, col2 = st.columns(2)
71
- col1.metric("Vainqueur", df.iloc[0]["Moteur"])
72
- col2.metric("Lignes", f"{winner_lines:,}")
73
-
74
- fig, ax = plt.subplots()
75
- ax.barh(df["Moteur"], df["Temps"])
76
- for i, v in enumerate(df["Temps"]):
77
- ax.text(v+0.01, i, f"{v:.3f}s", va='center')
78
- ax.set_xlabel("Secondes")
79
- st.pyplot(fig)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import pandas as pd
2
  import duckdb
3
  import polars as pl
4
+
5
+
6
  import time
7
  import os
8
+ import matplotlib.pyplot as plt
9
+ import numpy as np
10
  import tempfile
11
+ from io import BytesIO
12
+
13
+ print("=== APP STARTING ===") # Ça apparaîtra dans les logs
14
+ st.write("Hello, world!") # Un truc simple pour tester
15
+
16
+
17
+ # Configuration de la page Streamlit
18
+ st.set_page_config(
19
+ page_title="Comparaison de vitesse de chargement des données",
20
+ layout="wide"
21
+
22
+
23
+ )
24
+
25
+ # --- FONCTION DE CHARGEMENT TECHNIQUE ---
26
+ def load_file_and_measure_time(file_path, library, file_ext, read_kwargs):
27
+ try:
28
+ start_time = time.time()
29
+ df = None
30
+
31
+ # --- PARQUET ---
32
+ if file_ext == '.parquet':
33
+ if library == 'pandas':
34
+ df = pd.read_parquet(file_path)
35
+ elif library == 'pyarrow':
36
+ df = pd.read_parquet(file_path, engine='pyarrow')
37
+ elif library == 'duckdb':
38
+ con = duckdb.connect()
39
+ df = con.execute(f"SELECT * FROM read_parquet('{file_path}')").fetchdf()
40
+ con.close()
41
+ elif library == 'polars':
42
+ df = pl.read_parquet(file_path)
43
+
44
+ # --- EXCEL ---
45
+ elif file_ext in ['.xlsx', '.xls']:
46
+ sheet_idx = read_kwargs.get('sheet_idx', 0)
47
+ header = 0 if read_kwargs.get('header') else None
48
+ if library in ['pandas', 'pyarrow']:
49
+ df = pd.read_excel(file_path, sheet_name=sheet_idx, header=header)
50
+ elif library == 'duckdb':
51
+ df = pd.read_excel(file_path, sheet_name=sheet_idx, header=header)
52
+ elif library == 'polars':
53
+ df = pl.read_excel(file_path, sheet_id=sheet_idx + 1)
54
+
55
+ # --- CSV ---
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
+ else:
83
+ header_val = 0 if read_kwargs.get('header') else None
84
+ if library == 'pandas':
85
+ df = pd.read_csv(file_path, sep=None, engine='python', header=header_val)
86
+ elif library == 'pyarrow':
87
+ df = pd.read_csv(file_path, sep=None, engine='python', header=header_val, dtype_backend='pyarrow')
88
+ elif library == 'duckdb':
89
+ con = duckdb.connect()
90
+ header_flag = "TRUE" if read_kwargs.get('header') else "FALSE"
91
+ df = con.execute(f"SELECT * FROM read_csv_auto('{file_path}', HEADER={header_flag})").fetchdf()
92
+ con.close()
93
+ elif library == 'polars':
94
+ df = pl.read_csv(file_path, has_header=read_kwargs.get('header'))
95
+
96
+ end_time = time.time()
97
+ return end_time - start_time, len(df)
98
+ except Exception as e:
99
+ return f"Erreur: {e}", 0
100
+
101
+
102
+ # --- FONCTION POUR CHARGER DEPUIS UN BUFFER UPLOADÉ ---
103
+ def load_from_buffer(uploaded_file, library, file_ext, read_kwargs):
104
+ """Charge un fichier depuis un buffer Streamlit et mesure le temps"""
105
+ try:
106
+ # Créer un fichier temporaire
107
+ with tempfile.NamedTemporaryFile(delete=False, suffix=file_ext) as tmp_file:
108
+ tmp_file.write(uploaded_file.getvalue())
109
+ tmp_path = tmp_file.name
110
+
111
+ # Utiliser la fonction existante avec le fichier temporaire
112
+ load_time, row_count = load_file_and_measure_time(tmp_path, library, file_ext, read_kwargs)
113
+
114
+ # Nettoyer le fichier temporaire
115
+ os.unlink(tmp_path)
116
+
117
+ return load_time, row_count
118
+
119
+ except Exception as e:
120
+ return f"Erreur: {e}", 0
121
+
122
+
123
+ # --- GESTION DU FICHIER CIBLE DANS LE SESSION STATE ---
124
+ if 'target_file' not in st.session_state:
125
+ st.session_state.target_file = None
126
+ if 'file_ext' not in st.session_state:
127
+ st.session_state.file_ext = None
128
+ if 'uploaded_buffer' not in st.session_state:
129
+ st.session_state.uploaded_buffer = None
130
+
131
+ st.title("⚡ Comparaison de vitesse de chargement des données")
132
+ st.markdown("Téléchargez un fichier **CSV, Excel ou Parquet** pour comparer **Pandas**, **PyArrow**, **DuckDB** et **Polars**.")
133
 
134
  # --- SIDEBAR ---
135
+ st.sidebar.header("⚙️ Paramètres du fichier")
136
+
137
+ # Boutons de démo
138
+ st.sidebar.subheader("🧪 Fichiers de test (30Mo)")
139
  c1, c2 = st.sidebar.columns(2)
140
+ if c1.button("📄 Faker Text"):
141
+ if os.path.exists("faker_text.csv"):
142
+ st.session_state.target_file = "faker_text.csv"
143
+ st.session_state.file_ext = ".csv"
144
+ # Vérification des dimensions
145
+ try:
146
+ test_df = pd.read_csv("faker_text.csv", nrows=5)
147
+ st.sidebar.info(f"✅ Fichier détecté : {len(pd.read_csv('faker_text.csv'))} lignes, {len(test_df.columns)} colonnes")
148
+ except:
149
+ pass
150
+ else:
151
+ st.sidebar.error("❌ Fichier faker_text.csv introuvable à la racine")
152
+
153
+ if c2.button("📊 Numeric Only"):
154
+ if os.path.exists("numeric_only.csv"):
155
+ st.session_state.target_file = "numeric_only.csv"
156
+ st.session_state.file_ext = ".csv"
157
+ # Vérification des dimensions
158
+ try:
159
+ test_df = pd.read_csv("numeric_only.csv", nrows=5)
160
+ st.sidebar.info(f"Fichier détecté : {len(pd.read_csv('numeric_only.csv'))} lignes, {len(test_df.columns)} colonnes")
161
+ except:
162
+ pass
163
+ else:
164
+ st.sidebar.error("❌ Fichier numeric_only.csv introuvable à la racine")
165
+
166
+ # Uploader manuel
167
+ uploaded_file = st.sidebar.file_uploader("Ou choisissez un fichier", type=["csv", "parquet", "xlsx", "xls"])
168
+ if uploaded_file is not None:
169
+ try:
170
+ file_ext = os.path.splitext(uploaded_file.name)[1].lower()
171
+
172
+ # Stockage du buffer dans session_state
173
+ st.session_state.uploaded_buffer = uploaded_file
174
+ st.session_state.target_file = "uploaded_file" # Marqueur pour savoir qu'on a un upload
175
+ st.session_state.file_ext = file_ext
176
+
177
+ # Afficher la taille du fichier uploadé
178
+ file_size_mb = uploaded_file.size / (1024 * 1024)
179
+ st.sidebar.success(f"✅ Fichier uploadé : {uploaded_file.name} ({file_size_mb:.2f} Mo)")
180
+ except Exception as e:
181
+ st.sidebar.error(f"❌ Erreur lors de l'upload : {str(e)}")
182
+
183
+ # --- ACTIONS ET AFFICHAGE ---
184
+ if st.session_state.target_file is not None:
185
+ st.sidebar.success(f"Actif : **{st.session_state.target_file}**")
186
+
187
+ has_header = st.sidebar.radio("Ligne de titres en première ligne ?", ["Oui", "Non"], index=0) == "Oui"
188
+ read_kwargs = {'header': has_header}
189
+
190
+ if st.session_state.file_ext in ['.xlsx', '.xls']:
191
+ sheet_num = st.sidebar.number_input("Numéro de l'onglet (1 = premier)", min_value=1, value=1)
192
+ read_kwargs['sheet_idx'] = sheet_num - 1
193
+
194
+ run_comparison = st.sidebar.button("Lancer la comparaison")
195
+
196
+ if run_comparison:
197
+ st.subheader("⏱️ Résultats de la vitesse de chargement")
198
+ libraries = {'Pandas (Baseline)': 'pandas', 'PyArrow': 'pyarrow', 'DuckDB': 'duckdb', 'Polars': 'polars'}
199
+ results = []
200
+
201
+ for lib_name, lib_key in libraries.items():
202
+ with st.spinner(f"Test en cours : **{lib_name}**..."):
203
+ # Choix de la fonction selon la source
204
+ if st.session_state.target_file == "uploaded_file" and st.session_state.uploaded_buffer is not None:
205
+ # Fichier uploadé : passer directement l'objet uploaded_file
206
+ load_time, row_count = load_from_buffer(st.session_state.uploaded_buffer, lib_key, st.session_state.file_ext, read_kwargs)
207
+ else:
208
+ # Fichier de test : utiliser le chemin
209
+ load_time, row_count = load_file_and_measure_time(st.session_state.target_file, lib_key, st.session_state.file_ext, read_kwargs)
210
+ results.append({'Librairie': lib_name, 'Temps de chargement (s)': load_time, 'Nombre de lignes': row_count})
211
+
212
+ results_df = pd.DataFrame(results)
213
+
214
+ valid_counts = results_df[results_df['Nombre de lignes'] > 0]['Nombre de lignes']
215
+ if not valid_counts.empty:
216
+ st.markdown(f"**Nombre de lignes détectées :** **{int(valid_counts.iloc[0]):,}**".replace(',', ' '))
217
+
218
+ chart_data = results_df[results_df['Temps de chargement (s)'].apply(lambda x: isinstance(x, (int, float)))]
219
+
220
+ if not chart_data.empty:
221
+ chart_data = chart_data.sort_values(by='Temps de chargement (s)', ascending=True)
222
+ fig, ax = plt.subplots(figsize=(8, 2.5))
223
+ bars = ax.barh(chart_data['Librairie'], chart_data['Temps de chargement (s)'],
224
+ color=['#4CAF50', '#2196F3', '#FFC107', '#E91E63'])
225
+
226
+ max_time = chart_data['Temps de chargement (s)'].max()
227
+ ax.set_xlim(right=max_time * 1.35)
228
+ for bar in bars:
229
+ ax.text(bar.get_width() + (max_time * 0.03), bar.get_y() + bar.get_height()/2,
230
+ f'{bar.get_width():.4f}s', va='center', fontsize=10, fontweight='bold')
231
+
232
+ ax.set_xlabel('Temps (secondes)')
233
+ ax.set_title('Comparaison de vitesse')
234
+ st.pyplot(fig)
235
+ plt.close(fig)
236
+
237
+ else:
238
+ st.info("Veuillez charger un fichier ou utiliser un bouton de test à gauche.")