/
dayekb
/
vibecoding_visualize_csv
Обзор
Документация
Войти
/
dayekb
/
vibecoding_visualize_csv
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
simple_interactive_visualizer.py
469 строк
16 KB
dayekb
upload files
20 авг 2025, 18:31
20 авг 2025, 18:31
81ccc4f
Код
Авторство
О чём код?
""" Simple Interactive CSV Visualization Application ============================================= A working, interactive CSV visualization app using Plotly and Dash. This version is simplified but fully functional with: - Interactive plots with zoom, pan, and hover - Real-time data filtering - Multiple plot types - Data export capabilities """ import dash from dash import dcc, html, Input, Output, State import plotly.express as px import plotly.graph_objects as go import pandas as pd import numpy as np import base64 import io # Initialize the Dash app app = dash.Dash(__name__, title="Interactive CSV Visualizer") # Global variable to store the loaded data global_data = None def create_sample_data(): """Create sample Titanic dataset""" np.random.seed(42) n_passengers = 500 data = pd.DataFrame({ 'PassengerId': range(1, n_passengers + 1), 'Survived': np.random.choice([0, 1], n_passengers, p=[0.62, 0.38]), 'Pclass': np.random.choice([1, 2, 3], n_passengers, p=[0.24, 0.21, 0.55]), 'Sex': np.random.choice(['male', 'female'], n_passengers, p=[0.65, 0.35]), 'Age': np.random.normal(29.7, 14.5, n_passengers).clip(0, 80), 'SibSp': np.random.poisson(0.5, n_passengers), 'Parch': np.random.poisson(0.4, n_passengers), 'Fare': np.random.exponential(32.2, n_passengers), 'Embarked': np.random.choice(['S', 'C', 'Q'], n_passengers, p=[0.72, 0.19, 0.09]) }) # Add some missing values data.loc[np.random.choice(data.index, size=50), 'Age'] = np.nan return data # App layout app.layout = html.Div([ # Header html.H1("🚢 Interactive CSV Visualizer", style={'textAlign': 'center', 'color': '#2c3e50', 'marginBottom': '20px'}), html.Div([ # Left column - Controls html.Div([ html.H3("📁 Data Loading"), dcc.Upload( id='upload-data', children=html.Div([ 'Drag and Drop or ', html.A('Select a CSV File') ]), style={ 'width': '100%', 'height': '60px', 'lineHeight': '60px', 'borderWidth': '1px', 'borderStyle': 'dashed', 'borderRadius': '5px', 'textAlign': 'center', 'margin': '10px' }, multiple=False ), html.Button("Load Sample Titanic Data", id="load-sample-btn", style={'width': '100%', 'margin': '10px 0', 'padding': '10px'}), html.Div(id="file-info"), html.Hr(), html.H3("🔍 Data Filters"), html.Label("Filter by Survival:"), dcc.Dropdown( id='survival-filter', options=[ {'label': 'All Passengers', 'value': 'all'}, {'label': 'Survived Only', 'value': 1}, {'label': 'Died Only', 'value': 0} ], value='all' ), html.Label("Filter by Passenger Class:"), dcc.Dropdown( id='class-filter', options=[ {'label': 'All Classes', 'value': 'all'}, {'label': 'First Class', 'value': 1}, {'label': 'Second Class', 'value': 2}, {'label': 'Third Class', 'value': 3} ], value='all' ), html.Label("Filter by Gender:"), dcc.Dropdown( id='gender-filter', options=[ {'label': 'All Genders', 'value': 'all'}, {'label': 'Male', 'value': 'male'}, {'label': 'Female', 'value': 'female'} ], value='all' ), html.Label("Age Range:"), dcc.RangeSlider( id='age-range', min=0, max=80, step=1, value=[0, 80], marks={i: str(i) for i in range(0, 81, 10)} ), html.Hr(), html.H3("📊 Plot Controls"), html.Label("Plot Type:"), dcc.Dropdown( id='plot-type', options=[ {'label': 'Scatter Plot', 'value': 'scatter'}, {'label': 'Histogram', 'value': 'histogram'}, {'label': 'Box Plot', 'value': 'boxplot'}, {'label': '3D Scatter', 'value': '3d-scatter'}, {'label': 'Correlation Heatmap', 'value': 'heatmap'} ], value='scatter' ), html.Label("X-Axis Feature:"), dcc.Dropdown(id='x-feature'), html.Label("Y-Axis Feature:"), dcc.Dropdown(id='y-feature'), html.Label("Color By:"), dcc.Dropdown(id='color-feature'), html.Button("Create Plot", id="create-plot-btn", style={'width': '100%', 'margin': '10px 0', 'padding': '10px', 'backgroundColor': '#27ae60', 'color': 'white'}), html.Hr(), html.H3("💾 Export"), html.Button("Export Data as CSV", id="export-csv-btn", style={'width': '100%', 'margin': '5px 0', 'padding': '10px', 'backgroundColor': '#3498db', 'color': 'white'}), dcc.Download(id="download-csv") ], style={'width': '30%', 'float': 'left', 'padding': '20px', 'backgroundColor': '#ecf0f1', 'borderRadius': '10px'}), # Right column - Display html.Div([ html.Div([ html.H3("📋 Data Overview"), html.Div(id="data-overview") ], style={'marginBottom': '20px', 'padding': '20px', 'backgroundColor': 'white', 'borderRadius': '10px'}), html.Div([ html.H3("📈 Interactive Plot"), html.Div(id="plot-container") ], style={'marginBottom': '20px', 'padding': '20px', 'backgroundColor': 'white', 'borderRadius': '10px'}), html.Div([ html.H3("📊 Statistics"), html.Div(id="stats-container") ], style={'padding': '20px', 'backgroundColor': 'white', 'borderRadius': '10px'}) ], style={'width': '65%', 'float': 'right', 'padding': '20px'}) ], style={'display': 'flex', 'justifyContent': 'space-between'}) ], style={'fontFamily': 'Arial, sans-serif', 'backgroundColor': '#bdc3c7', 'minHeight': '100vh', 'padding': '20px'}) def parse_contents(contents, filename): """Parse uploaded CSV file""" global global_data content_type, content_string = contents.split(',') decoded = base64.b64decode(content_string) try: if 'csv' in filename: df = pd.read_csv(io.StringIO(decoded.decode('utf-8'))) else: return html.Div(['Please upload a CSV file.']) global_data = df return html.Div([ html.H5(f'Successfully loaded: {filename}'), html.H6(f'Shape: {df.shape[0]} rows × {df.shape[1]} columns'), html.P(f'Memory usage: {df.memory_usage(deep=True).sum() / 1024:.2f} KB') ]) except Exception as e: return html.Div([ html.H5('Error processing this file.'), html.P(str(e)) ]) def create_plot(plot_type, x_feature, y_feature, color_feature, filtered_data): """Create interactive plots""" if filtered_data is None or filtered_data.empty: return go.Figure().add_annotation( text="No data available for plotting", xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False ) if plot_type == 'scatter': fig = px.scatter( filtered_data, x=x_feature, y=y_feature, color=color_feature if color_feature != 'None' else None, title=f"{x_feature} vs {y_feature}", template="plotly_white" ) elif plot_type == 'histogram': fig = px.histogram( filtered_data, x=x_feature, color=color_feature if color_feature != 'None' else None, title=f"Distribution of {x_feature}", template="plotly_white" ) elif plot_type == 'boxplot': fig = px.box( filtered_data, x=x_feature, y=y_feature, color=color_feature if color_feature != 'None' else None, title=f"{y_feature} by {x_feature}", template="plotly_white" ) elif plot_type == '3d-scatter': fig = px.scatter_3d( filtered_data, x=x_feature, y=y_feature, z=color_feature if color_feature != 'None' else 'Age', color=color_feature if color_feature != 'None' else 'Sex', title=f"3D Scatter: {x_feature} vs {y_feature}", template="plotly_white" ) elif plot_type == 'heatmap': numeric_data = filtered_data.select_dtypes(include=[np.number]) if len(numeric_data.columns) > 1: corr_matrix = numeric_data.corr() fig = px.imshow( corr_matrix, title="Correlation Matrix", template="plotly_white", color_continuous_scale="RdBu" ) else: fig = go.Figure().add_annotation( text="Need at least 2 numerical columns for correlation", xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False ) fig.update_layout(height=500) return fig def get_data_overview(data): """Create data overview display""" if data is None: return html.Div("No data loaded") overview = [] overview.append(html.H4("Dataset Information")) overview.append(html.P(f"Shape: {data.shape[0]} rows × {data.shape[1]} columns")) overview.append(html.H4("Columns:")) for col in data.columns: col_info = f"{col}: {data[col].dtype}" if pd.api.types.is_numeric_dtype(data[col]): col_info += f" | Range: {data[col].min():.1f} to {data[col].max():.1f}" overview.append(html.P(col_info)) return html.Div(overview) def get_stats(data): """Create statistical summary""" if data is None: return html.Div("No data loaded") stats = [] numeric_cols = data.select_dtypes(include=[np.number]).columns if len(numeric_cols) > 0: stats.append(html.H4("Numerical Statistics")) for col in numeric_cols: col_stats = data[col].describe() stats.append(html.H5(col)) stats.append(html.P(f"Mean: {col_stats['mean']:.2f}")) stats.append(html.P(f"Std: {col_stats['std']:.2f}")) stats.append(html.P(f"Min: {col_stats['min']:.2f}")) stats.append(html.P(f"Max: {col_stats['max']:.2f}")) stats.append(html.Hr()) return html.Div(stats) # Callbacks @app.callback( Output('file-info', 'children'), Input('upload-data', 'contents'), State('upload-data', 'filename') ) def update_file_info(contents, filename): if contents is not None: return parse_contents(contents, filename) return "No file uploaded" @app.callback( Output('data-overview', 'children'), Input('load-sample-btn', 'n_clicks'), Input('upload-data', 'contents'), prevent_initial_call=True ) def update_data_overview(n_clicks, contents): global global_data if n_clicks: global_data = create_sample_data() return get_data_overview(global_data) elif contents: return get_data_overview(global_data) return "No data loaded" @app.callback( [Output('x-feature', 'options'), Output('y-feature', 'options'), Output('color-feature', 'options')], [Input('load-sample-btn', 'n_clicks'), Input('upload-data', 'contents')], prevent_initial_call=True ) def update_feature_dropdowns(n_clicks, contents): global global_data if global_data is not None: columns = [{'label': col, 'value': col} for col in global_data.columns] none_option = [{'label': 'None', 'value': 'None'}] return columns, columns, none_option + columns return [], [], [] @app.callback( Output('plot-container', 'children'), [Input('create-plot-btn', 'n_clicks'), Input('survival-filter', 'value'), Input('class-filter', 'value'), Input('gender-filter', 'value'), Input('age-range', 'value')], [State('plot-type', 'value'), State('x-feature', 'value'), State('y-feature', 'value'), State('color-feature', 'value')], prevent_initial_call=True ) def update_plot(n_clicks, survival_filter, class_filter, gender_filter, age_range, plot_type, x_feature, y_feature, color_feature): global global_data if global_data is None or not all([plot_type, x_feature]): return html.Div("Please load data and select plot parameters") # Apply filters filtered_data = global_data.copy() if survival_filter != 'all': filtered_data = filtered_data[filtered_data['Survived'] == survival_filter] if class_filter != 'all': filtered_data = filtered_data[filtered_data['Pclass'] == class_filter] if gender_filter != 'all': filtered_data = filtered_data[filtered_data['Sex'] == gender_filter] if age_range: filtered_data = filtered_data[ (filtered_data['Age'] >= age_range[0]) & (filtered_data['Age'] <= age_range[1]) ] # Create the plot fig = create_plot(plot_type, x_feature, y_feature, color_feature, filtered_data) return dcc.Graph( id='main-plot', figure=fig, config={'displayModeBar': True, 'displaylogo': False} ) @app.callback( Output('stats-container', 'children'), [Input('survival-filter', 'value'), Input('class-filter', 'value'), Input('gender-filter', 'value'), Input('age-range', 'value')], prevent_initial_call=True ) def update_stats(survival_filter, class_filter, gender_filter, age_range): global global_data if global_data is None: return "No data loaded" # Apply the same filters filtered_data = global_data.copy() if survival_filter != 'all': filtered_data = filtered_data[filtered_data['Survived'] == survival_filter] if class_filter != 'all': filtered_data = filtered_data[filtered_data['Pclass'] == class_filter] if gender_filter != 'all': filtered_data = filtered_data[filtered_data['Sex'] == gender_filter] if age_range: filtered_data = filtered_data[ (filtered_data['Age'] >= age_range[0]) & (filtered_data['Age'] <= age_range[1]) ] return get_stats(filtered_data) @app.callback( Output('download-csv', 'data'), Input('export-csv-btn', 'n_clicks'), prevent_initial_call=True ) def export_csv(n_clicks): global global_data if global_data is None: return None return dcc.send_data_frame(global_data.to_csv, "titanic_data.csv") if __name__ == '__main__': print("🚀 Starting Simple Interactive CSV Visualizer...") print("📱 Open your web browser and go to: http://127.0.0.1:8050") print("💡 This version features interactive plots with zoom, pan, and hover!") app.run(debug=True, host='127.0.0.1', port=8050)