Cosmographer commited on
Commit
39c32e8
Β·
verified Β·
1 Parent(s): ea7bd0c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +158 -76
app.py CHANGED
@@ -103,30 +103,71 @@ def read_file_to_df(uploaded) -> pd.DataFrame:
103
  else:
104
  # attempt to read bytes
105
  content = uploaded.read()
106
- try:
107
- return pd.read_csv(io.BytesIO(content))
108
- except Exception:
109
- return pd.read_excel(io.BytesIO(content))
110
- # now path-based read
111
- if path.lower().endswith(".csv"):
112
- return pd.read_csv(path)
113
- elif path.lower().endswith((".xls", ".xlsx")):
114
- return pd.read_excel(path)
115
- elif path.lower().endswith(".json"):
116
- return pd.read_json(path)
117
- else:
118
- # try csv then excel then json
119
- try:
120
- return pd.read_csv(path)
121
- except Exception:
122
- try:
123
- return pd.read_excel(path)
124
- except Exception:
125
- return pd.read_json(path)
126
  except Exception as e:
127
- print("read_file_to_df error:", e)
 
 
 
 
 
 
128
  raise
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  def comprehensive_data_profile(df: pd.DataFrame) -> Dict[str, Any]:
131
  """Enhanced data profiling with comprehensive analysis"""
132
  if df is None or df.empty:
@@ -233,36 +274,38 @@ def detect_outliers_iqr(series):
233
  return ((series < lower_bound) | (series > upper_bound)).sum()
234
 
235
  def profile_to_enhanced_markdown(profile: Dict[str, Any]) -> str:
236
- """Convert comprehensive profile to rich markdown with visual indicators"""
237
  if not profile:
238
  return "No data loaded."
239
 
240
  md = []
241
 
242
  # Header with key metrics
243
- md.append(f"## πŸ“Š Comprehensive Data Profile Report")
244
  md.append("---")
245
 
246
- # Dataset Overview
247
- md.append("### 🎯 Dataset Overview")
248
- md.append(f"- **Rows:** {profile['rows']:,}")
249
- md.append(f"- **Columns:** {profile['columns']}")
250
- md.append(f"- **Memory Usage:** {profile['memory_usage']:.2f} MB")
251
- md.append(f"- **Duplicate Rows:** {profile['duplicate_rows']} ({profile['duplicate_pct']:.2%})")
 
 
252
  md.append("")
253
 
254
  # Data Quality Scorecard
255
- md.append("### πŸ“ˆ Data Quality Scorecard")
256
  quality = profile["quality_metrics"]
257
- md.append(f"- **Completeness:** {quality['completeness']:.2%}")
258
- md.append(f"- **Uniqueness:** {quality['uniqueness']:.2%}")
259
  md.append("")
260
 
261
  # Column Type Summary
262
- md.append("### πŸ—‚οΈ Column Type Summary")
263
- md.append(f"- **Numeric Columns:** {len(profile['numeric_cols'])}")
264
- md.append(f"- **Categorical Columns:** {len(profile['categorical_cols'])}")
265
- md.append(f"- **DateTime Columns:** {len(profile['datetime_cols'])}")
266
  md.append("")
267
 
268
  # Detailed Column Analysis
@@ -761,6 +804,45 @@ css = """
761
  margin-top: 8px;
762
  text-align: center;
763
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
764
  """
765
 
766
  vanta_html = """
@@ -824,31 +906,36 @@ with gr.Blocks(css=css, title="DataSynth β€” Analytics Hub") as demo:
824
 
825
  # --- Main app (hidden until login) ---
826
  with gr.Column(visible=False) as main_area:
827
- gr.Markdown("## Workspace")
828
- with gr.Tabs():
829
- with gr.TabItem("Data"):
830
- gr.Markdown("Upload CSV / Excel / JSON for comprehensive profiling and analysis.")
831
- upload = gr.File(label="Upload CSV / Excel / JSON", file_types=[".csv", ".xlsx", ".xls", ".json"])
 
832
  with gr.Row():
833
- profile_btn = gr.Button("πŸ“Š Data Profiling", variant="primary")
834
- view_data_btn = gr.Button("πŸ‘€ View Data", variant="secondary")
835
- download_raw_btn = gr.Button("πŸ“₯ Download Raw CSV")
836
  with gr.Tabs() as data_tabs:
837
- with gr.TabItem("Profile Report"):
838
- profile_md = gr.Markdown("No dataset loaded. Upload data and click 'Data Profiling'.")
839
-
840
- with gr.TabItem("Data Preview"):
841
- sample_table = gr.Dataframe(interactive=False, label="Sample Data (First 100 rows)")
842
-
843
- with gr.TabItem("Visualizations"):
844
- with gr.Row():
845
- dist_plot1 = gr.Plot(label="Distribution Plot 1")
846
- dist_plot2 = gr.Plot(label="Distribution Plot 2")
847
- with gr.Row():
848
- dist_plot3 = gr.Plot(label="Distribution Plot 3")
849
- corr_plot = gr.Plot(label="Correlation Heatmap")
 
 
 
 
850
 
851
- with gr.TabItem("Prepare"):
852
  gr.Markdown("Data cleaning and feature engineering options.")
853
  with gr.Row():
854
  with gr.Column(scale=1):
@@ -868,12 +955,12 @@ with gr.Blocks(css=css, title="DataSynth β€” Analytics Hub") as demo:
868
  with gr.Column(scale=1):
869
  prep_output = gr.Markdown("Preparation preview will appear here.")
870
  preview_clean = gr.Dataframe(interactive=False)
871
- with gr.TabItem("Visualize (NL)"):
872
  gr.Markdown("Type a natural-language request to create a plot (e.g., 'histogram of age', 'scatter income vs age', 'bar of country').")
873
  nl_input = gr.Textbox(label="Describe chart")
874
  nl_btn = gr.Button("Create Chart")
875
  nl_plot = gr.Plot()
876
- with gr.TabItem("Model"):
877
  gr.Markdown("Choose task, target variable, and model. Scaling and encoding will be applied automatically.")
878
  task_select = gr.Radio(choices=["regression","classification"], value="regression", label="Task")
879
  target_col = gr.Dropdown(choices=[], label="Target variable")
@@ -884,7 +971,7 @@ with gr.Blocks(css=css, title="DataSynth β€” Analytics Hub") as demo:
884
  model_out_md = gr.Markdown("Model results will show here.")
885
  model_feature_imp = gr.Dataframe(interactive=False)
886
  model_plots = gr.Plot()
887
- with gr.TabItem("Report"):
888
  gr.Markdown("Generate a short executive report that summarizes profiling, cleaning, and model results.")
889
  report_btn = gr.Button("Generate Report")
890
  report_download = gr.File(label="Download Report (.md)")
@@ -907,35 +994,30 @@ with gr.Blocks(css=css, title="DataSynth β€” Analytics Hub") as demo:
907
  if uploaded is None:
908
  return (gr.update(value="No file uploaded."),
909
  pd.DataFrame(), None, None,
910
- None, None, None, None)
911
  df = read_file_to_df(uploaded)
912
  prof = comprehensive_data_profile(df)
913
  md = profile_to_enhanced_markdown(prof)
914
  #create visulaizations
915
- plots = create_distribution_plots(df, prof)
916
- corr_plot = create_correlation_plot(df, prof)
917
 
918
  # Prepare plot outputs
919
- plot1 = plots.get(list(plots.keys())[0]) if plots else None
920
- plot2 = plots.get(list(plots.keys())[1]) if len(plots) > 1 else None
921
- plot3 = plots.get(list(plots.keys())[2]) if len(plots) > 2 else None
922
  # prepare choices for date and target selectors
923
  #cols = df.columns.tolist()
924
  # save df to temp csv for persistence (store path in state)
925
- tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".csv")
926
- df.to_csv(tmp.name, index=False)
927
 
928
- return (md, df.head(100), tmp.name, prof,
929
- plot1, plot2, plot3, corr_plot)
930
  except Exception as e:
931
- return (gr.update(value=f"Error loading file: {e}"),
932
- pd.DataFrame(), None, None,
933
- None, None, None, None)
934
 
935
  profile_btn.click(fn=_load_and_profile,
936
  inputs=[upload],
937
- outputs=[profile_md, sample_table, df_state, profile_state,
938
- dist_plot1, dist_plot2, dist_plot3, corr_plot])
939
 
940
  def _view_data(uploaded):
941
  try:
 
103
  else:
104
  # attempt to read bytes
105
  content = uploaded.read()
106
+ return _read_bytes_content(content)
107
+
108
+ return _read_file_path(path)
109
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  except Exception as e:
111
+ print(f"read_file_to_df error: {e}")
112
+
113
+ try:
114
+ if 'path' in locals():
115
+ return pd.read_csv(path, encoding_errors='ignore', engine='python')
116
+ except Exception:
117
+ pass
118
  raise
119
 
120
+ def _read_bytes_content(content) -> pd.DataFrame:
121
+ formats_to_try = [
122
+ ('csv', lambda: pd.read_csv(io.BytesIO(content))),
123
+ ('csv_utf8', lambda: pd.read_csv(io.BytesIO(content), encoding='utf-8')),
124
+ ('csv_latin1', lambda: pd.read_csv(io.BytesIO(content), encoding='latin1')),
125
+ ('csv_ignore', lambda: pd.read_csv(io.BytesIO(content), encoding_errors='ignore')),
126
+ ('excel', lambda: pd.read_excel(io.BytesIO(content))),
127
+ ('json', lambda: pd.read_json(io.BytesIO(content))),
128
+ ]
129
+ for format_name, reader_func in formats_to_try:
130
+ try:
131
+ return reader_func()
132
+ except Exception as e:
133
+ print(f"Failed to read as {format_name}: {e}")
134
+ continue
135
+
136
+ raise ValueError("Could not read file with any supported format")
137
+
138
+ def _read_file_path(path) -> pd.DataFrame:
139
+ """Handle file path reading with multiple format attempts"""
140
+ file_ext = os.path.splitext(path)[1].lower()
141
+
142
+ if file_ext in ['.csv', '.txt', '.tsv']:
143
+ encodings = [None, 'utf-8', 'latin1', 'cp1252', 'iso-8859-1']
144
+ for encoding in encodings:
145
+ try:
146
+ if file_ext == '.tsv':
147
+ return pd.read_csv(path, sep='\t', encoding=encoding, encoding_errors='ignore')
148
+ else:
149
+ return pd.read_csv(path, encoding=encoding, encoding_errors='ignore')
150
+ except Exception:
151
+ continue
152
+
153
+ elif file_ext in ['.xlsx', '.xls', '.xlsm', '.xlsb']:
154
+ return pd.read_excel(path)
155
+
156
+ elif file_ext == '.json':
157
+ return pd.read_json(path)
158
+
159
+ elif file_ext in ['.parquet']:
160
+ return pd.read_parquet(path)
161
+
162
+ elif file_ext in ['.feather']:
163
+ return pd.read_feather(path)
164
+
165
+ # Final fallback
166
+ try:
167
+ return pd.read_csv(path, encoding_errors='ignore', engine='python')
168
+ except Exception:
169
+ return pd.read_excel(path)
170
+
171
  def comprehensive_data_profile(df: pd.DataFrame) -> Dict[str, Any]:
172
  """Enhanced data profiling with comprehensive analysis"""
173
  if df is None or df.empty:
 
274
  return ((series < lower_bound) | (series > upper_bound)).sum()
275
 
276
  def profile_to_enhanced_markdown(profile: Dict[str, Any]) -> str:
277
+ """Convert comprehensive profile to rich markdown with better formatting"""
278
  if not profile:
279
  return "No data loaded."
280
 
281
  md = []
282
 
283
  # Header with key metrics
284
+ md.append("# πŸ“Š Comprehensive Data Profile Report")
285
  md.append("---")
286
 
287
+ # Dataset Overview in a compact grid
288
+ md.append("## 🎯 Dataset Overview")
289
+ with io.StringIO() as overview:
290
+ overview.write(f"**Rows:** {profile['rows']:,} | ")
291
+ overview.write(f"**Columns:** {profile['columns']} | ")
292
+ overview.write(f"**Memory:** {profile['memory_usage']:.2f} MB | ")
293
+ overview.write(f"**Duplicates:** {profile['duplicate_rows']} ({profile['duplicate_pct']:.2%})")
294
+ md.append(overview.getvalue())
295
  md.append("")
296
 
297
  # Data Quality Scorecard
298
+ md.append("## πŸ“ˆ Data Quality Scorecard")
299
  quality = profile["quality_metrics"]
300
+ md.append(f"**Completeness:** {quality['completeness']:.2%} | ")
301
+ md.append(f"**Uniqueness:** {quality['uniqueness']:.2%}")
302
  md.append("")
303
 
304
  # Column Type Summary
305
+ md.append("## πŸ—‚οΈ Column Type Summary")
306
+ md.append(f"**Numeric:** {len(profile['numeric_cols'])} | ")
307
+ md.append(f"**Categorical:** {len(profile['categorical_cols'])} | ")
308
+ md.append(f"**DateTime:** {len(profile['datetime_cols'])}")
309
  md.append("")
310
 
311
  # Detailed Column Analysis
 
804
  margin-top: 8px;
805
  text-align: center;
806
  }
807
+
808
+ /* Improved table styling for profile report */
809
+ #profile-report table {
810
+ width: 100%;
811
+ border-collapse: collapse;
812
+ font-size: 14px;
813
+ table-layout: fixed;
814
+ }
815
+
816
+ #profile-report th, #profile-report td {
817
+ padding: 8px 12px;
818
+ border: 1px solid #ddd;
819
+ text-align: left;
820
+ word-wrap: break-word;
821
+ white-space: nowrap;
822
+ overflow: hidden;
823
+ text-overflow: ellipsis;
824
+ }
825
+
826
+ #profile-report th {
827
+ background-color: #f5f5f5;
828
+ font-weight: 600;
829
+ }
830
+
831
+ #profile-report tr:nth-child(even) {
832
+ background-color: #f9f9f9;
833
+ }
834
+
835
+ /* Ensure markdown content uses full width */
836
+ .markdown-body {
837
+ max-width: 100% !important;
838
+ }
839
+
840
+ /* Better scrolling for large content */
841
+ .gr-markdown {
842
+ max-height: 70vh;
843
+ overflow-y: auto;
844
+ }
845
+
846
  """
847
 
848
  vanta_html = """
 
906
 
907
  # --- Main app (hidden until login) ---
908
  with gr.Column(visible=False) as main_area:
909
+ gr.Markdown("# πŸš€ DataSynth Analytics Workspace")
910
+ with gr.Tabs() as main_tabs:
911
+ with gr.TabItem("πŸ“Š Data Profiling"):
912
+ gr.Markdown("### πŸ“ Upload & Analyze Your Dataset")
913
+ upload = gr.File(label="Upload Dataset", file_types=[".csv", ".xlsx", ".xls", ".xlsm", ".xlsb", ".json", ".parquet", ".feather", ".txt", ".tsv"],
914
+ type="filepath")
915
  with gr.Row():
916
+ profile_btn = gr.Button("πŸš€ Run Comprehensive Data Profiling", variant="primary", size="lg")
917
+ #view_data_btn = gr.Button("πŸ‘€ View Data", variant="secondary")
918
+ #download_raw_btn = gr.Button("πŸ“₯ Download Raw CSV")
919
  with gr.Tabs() as data_tabs:
920
+ with gr.TabItem("πŸ“Š Profile Report"):
921
+ profile_md = gr.Markdown(
922
+ "No dataset loaded. Upload your data and click 'Run Comprehensive Data Profiling'.",
923
+ elem_id="profile-report"
924
+ )
925
+
926
+ with gr.TabItem("πŸ‘οΈ Data Preview"):
927
+ sample_table = gr.Dataframe(
928
+ interactive=False,
929
+ label="Sample Data (First 100 rows)",
930
+ height = 400
931
+ )
932
+
933
+ with gr.TabItem("πŸ“ˆ Visualizations"):
934
+ gr.Markdown("### Correlation Analysis")
935
+ corr_plot = gr.Plot(label="Correlation Heatmap")
936
+ gr.Markdown("*Note: Only numeric columns are included in correlation analysis*")
937
 
938
+ with gr.TabItem("πŸ”§ Prepare"):
939
  gr.Markdown("Data cleaning and feature engineering options.")
940
  with gr.Row():
941
  with gr.Column(scale=1):
 
955
  with gr.Column(scale=1):
956
  prep_output = gr.Markdown("Preparation preview will appear here.")
957
  preview_clean = gr.Dataframe(interactive=False)
958
+ with gr.TabItem("πŸ“Š Visualize (NL)"):
959
  gr.Markdown("Type a natural-language request to create a plot (e.g., 'histogram of age', 'scatter income vs age', 'bar of country').")
960
  nl_input = gr.Textbox(label="Describe chart")
961
  nl_btn = gr.Button("Create Chart")
962
  nl_plot = gr.Plot()
963
+ with gr.TabItem("πŸ€– Model"):
964
  gr.Markdown("Choose task, target variable, and model. Scaling and encoding will be applied automatically.")
965
  task_select = gr.Radio(choices=["regression","classification"], value="regression", label="Task")
966
  target_col = gr.Dropdown(choices=[], label="Target variable")
 
971
  model_out_md = gr.Markdown("Model results will show here.")
972
  model_feature_imp = gr.Dataframe(interactive=False)
973
  model_plots = gr.Plot()
974
+ with gr.TabItem("πŸ“„ Report"):
975
  gr.Markdown("Generate a short executive report that summarizes profiling, cleaning, and model results.")
976
  report_btn = gr.Button("Generate Report")
977
  report_download = gr.File(label="Download Report (.md)")
 
994
  if uploaded is None:
995
  return (gr.update(value="No file uploaded."),
996
  pd.DataFrame(), None, None,
997
+ None)
998
  df = read_file_to_df(uploaded)
999
  prof = comprehensive_data_profile(df)
1000
  md = profile_to_enhanced_markdown(prof)
1001
  #create visulaizations
1002
+ corr_plot_fig = create_correlation_plot(df, prof)
 
1003
 
1004
  # Prepare plot outputs
1005
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".csv")
1006
+ df.to_csv(tmp.name, index=False)
 
1007
  # prepare choices for date and target selectors
1008
  #cols = df.columns.tolist()
1009
  # save df to temp csv for persistence (store path in state)
1010
+ #tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".csv")
1011
+ #df.to_csv(tmp.name, index=False)
1012
 
1013
+ return (md, df.head(100), tmp.name, prof, corr_plot_fig)
 
1014
  except Exception as e:
1015
+ return (gr.update(value=f"Error loading file: {e}"),
1016
+ pd.DataFrame(), None, None, None)
 
1017
 
1018
  profile_btn.click(fn=_load_and_profile,
1019
  inputs=[upload],
1020
+ outputs=[profile_md, sample_table, df_state, profile_state, corr_plot])
 
1021
 
1022
  def _view_data(uploaded):
1023
  try: