Heim > Fragen und Antworten > Hauptteil
Ich habe große Schwierigkeiten, Folgendes zu tun. Ich habe eine mit CSS formatierte Dash-Anwendung zur Anzeige in verschiedenen Containern. Der Container mit der Bezeichnung „hexgrid-1-container“ ist der größte Container, während die anderen Container kleiner und um ihn herum angeordnet sind. Ich möchte meine Dash-App aktualisieren, sodass jedes Mal, wenn ich auf eine Abbildung/Grafik in einem anderen Container klicke, diese in „hexgrid-1-container“ angezeigt wird, während sich die vorherige Abbildung in „hexgrid-1-container“ befand. Container werden in kleineren Containern angezeigt .
Ich habe eine Dash-App wie diese:
import dash import dash_core_components as dcc import dash_html_components as html from dash.dependencies import Input, Output, State from jupyter_dash import JupyterDash import plotly.express as px import plotly.graph_objects as go import pandas as pd import sqlite3 import locale import numpy as np import plotly.figure_factory as ff import plotly.express as px # Set the locale for formatting locale.setlocale(locale.LC_ALL, '') app = dash.Dash(__name__) app.layout = html.Div(className= 'container glass', children=[ html.Div(className='hexgrid-1-container', style={'border': '1px solid black'}, children=[ dcc.Graph(id='hexgrid-1', style={"height": "75%", "width": "100%"}, className='hexgrid1') ]), html.Div(className='hexgrid-2-container', style={'border': '1px solid black'}, children=[ dcc.Graph(id='hexgrid-2', style={"height": "100%", "width": "100%"}, className='hexgrid2') ]), html.Div(className='base-plot-container', style={'border': '1px solid black'}, children=[ dcc.Graph(id='base-plot', style={"height": "100%", "width": "100%"}) ]), html.Div(className='us-map-container', style={'border': '1px solid black'}, children=[ dcc.Graph(id='us-map-graph', style={"height": "100%", "width": "100%"}) ]), html.Div(className='fifth-container', style={'border': '1px solid black'}, children=[ # dcc.Graph(id='us-map-graph', style={"height": "100%", "width": "100%"}) #html.H2("5th Container") ]), html.Div(className='sixth-container', style={'border': '1px solid black'}, children=[ # dcc.Graph(id='us-map-graph', style={"height": "100%", "width": "100%"}) #html.H2("6th Container") ]) ]) @app.callback( Output('hexgrid-1', 'figure'), Input('hexgrid-1', 'id') ) def render_hexgrid_1(_): # Open a new database connection conn = sqlite3.connect(r"C:\Users\HituJani\Downloads\txns.db") # Fetching all columns in the transactions table query = ''' PRAGMA table_info(transactions) ''' # Fetch data data = pd.read_sql_query(query, conn) # Filtering out the relevant columns, this step can be skipped if you want all columns. relevant_columns = data[data['name'].isin(['TranType', 'MessageTypeIdentifier', 'MerchantType', 'IResponseCode'])] # Creating the hex grid with column names plot_data = go.Scatter(x=relevant_columns.index, y=relevant_columns['name'], text=relevant_columns['name'], marker=dict(symbol='hexagon', size=30), mode='markers+text') layout = go.Layout(title="Select a Category") return go.Figure(data=[plot_data], layout=layout) @app.callback( Output('hexgrid-2', 'figure'), Output('hexgrid-2', 'style'), Input('hexgrid-1', 'clickData') ) def render_hexgrid_2(clickData): # Open a new database connection conn = sqlite3.connect(r"C:\Users\HituJani\Downloads\txns.db") if clickData is None: return go.Figure(), {"display": "none"} else: category = clickData['points'][0]['text'] # Fetching all columns in the transactions table query = ''' PRAGMA table_info(transactions) ''' # Fetch data data = pd.read_sql_query(query, conn) # Filtering out the relevant numerical features columns relevant_columns = data[data['name'].isin(['TransactionAmount', 'OutstandingAmount', 'CurrentBalance', 'TotalOutStgAuthAmt'])] # Generating random points on the surface of a sphere phi = np.random.uniform(0, np.pi, len(relevant_columns)) theta = np.random.uniform(0, 2*np.pi, len(relevant_columns)) x = np.cos(theta) * np.sin(phi) y = np.sin(theta) * np.sin(phi) z = np.cos(phi) # Create a 3D scatter plot with sphere markers scatter = go.Scatter3d( x=x, y=y, z=z, mode='markers+text', marker=dict( size=12, color=np.arange(len(relevant_columns)), colorscale='Viridis', symbol='circle', opacity=0.8 ), text=relevant_columns['name'], hoverinfo='text' ) # Create a wireframe sphere using a mesh u = np.linspace(0, 2 * np.pi, 100) v = np.linspace(0, np.pi, 100) x_sphere = np.outer(np.cos(u), np.sin(v)) y_sphere = np.outer(np.sin(u), np.sin(v)) z_sphere = np.outer(np.ones(np.size(u)), np.cos(v)) sphere = go.Mesh3d(x=x_sphere.ravel(), y=y_sphere.ravel(), z=z_sphere.ravel(), opacity=0.1, color='cyan') # Combine the scatter plot and wireframe into a single figure fig = go.Figure(data=[scatter, sphere]) fig.update_layout( margin=dict(l=0, r=0, b=0, t=0), scene=dict( xaxis=dict(title=None, visible=False), yaxis=dict(title=None, visible=False), zaxis=dict(title=None, visible=False), ), template='plotly_dark' ) return fig, {"height": "50vh", "width": "100%", "display": "inline-block"} # Define the callback function that will update the plot, highlight the sphere and enable drilling down @app.callback( Output('base-plot', 'figure'), Input('hexgrid-2', 'clickData'), State('hexgrid-1', 'clickData') ) def update_base_plot(clickData_hexgrid_2, clickData_hexgrid_1): try: # Open a new database connection conn = sqlite3.connect(r"C:\Users\HituJani\Downloads\txns.db") category = clickData_hexgrid_1['points'][0]['text'] numerical_feature = clickData_hexgrid_2['points'][0]['text'] # SQL query to retrieve aggregated data based on selected category and numerical feature query = f''' SELECT {category}, WeekOfMonth, SUM({numerical_feature}) AS TotalAmount FROM transactions GROUP BY {category}, WeekOfMonth ORDER BY TotalAmount DESC ''' # Fetch the data from the database df_base = pd.read_sql(query, conn) # Close the database connection conn.close() # Formatting the Total column df_base['TotalAmount'] = df_base['TotalAmount'].apply(lambda x: locale.currency(x, grouping=True)) # Creating 3D scatter plot with sphere markers fig = go.Figure() # Define colorscale for WeekOfMonth values colorscale = [ [0, 'blue'], # Week 1: Blue [0.25, 'purple'], # Week 2: Purple [0.5, 'darkorange'], # Week 3: Dark Orange [0.75, 'yellow'], # Week 4: Yellow [1, 'pink'] # Week 5: Pink ] # Add the scatter plot trace fig.add_trace( go.Scatter3d( x=df_base[category], y=df_base['WeekOfMonth'].astype(int), z=df_base['TotalAmount'], mode='markers+text', marker=dict( size=5, # Adjust the size of markers to make them smaller symbol='circle', # Use 'circle' symbol for spheres color=df_base['WeekOfMonth'], colorscale=colorscale, ), textposition='top center', hovertemplate=( f"<b>{category}</b>: %{{x}}<br>" + "<b>Total Amount</b>: %{z}<br>" + "<b>WeekOfMonth</b>: %{y}<br>" ) ) ) # Dynamically set y-axis range y_max = df_base['WeekOfMonth'].max() fig.update_layout( scene=dict( xaxis=dict( title=category, title_font=dict(size=14, color='darkorange'), visible=False # Hide the x-axis ), yaxis=dict( title='Week of Month', title_font=dict(size=14, color='purple'), tickmode='array', tickvals=[0.5, 1.5, 2.5, 3.5, 4.5, 5.5], ticktext=['1', '2', '3', '4', '5'], # Display tick labels as 1, 2, 3, 4, 5], visible=False # Hide the y-axis ), zaxis=dict( title=f'Total {numerical_feature} ($)', title_font=dict(size=14, color='yellow'), autorange='reversed', visible=False # Hide the z-axis ), ), xaxis=dict( type='category', tickmode='linear', tickangle=45, automargin=True, visible=False # Hide the x-axis labels ), margin=dict(l=10, r=10, t=10, b=10), # Increase the b value to enlarge the viewing window template='plotly_dark', ) return fig except Exception as e: print(f"Error: {e}") return go.Figure() @app.callback( Output('us-map-graph', 'figure'), Output('us-map-graph', 'style'), Input('base-plot', 'clickData'), State('hexgrid-1', 'clickData'), State('hexgrid-2', 'clickData') ) def display_transaction_amount(base_plot_click_data, hexgrid_1_clickData, hexgrid_2_clickData): try: # Check if data from hexgrid-1 and hexgrid-2 is available if hexgrid_1_clickData is None or hexgrid_2_clickData is None: # Return an empty figure and hide the map return go.Figure(), {"display": "none"} # Get the selected category from hexgrid-1 and numerical feature from hexgrid-2 selected_category = hexgrid_1_clickData['points'][0]['text'] numerical_feature = hexgrid_2_clickData['points'][0]['text'] # Get the selected subcategory from base_plot_click_data selected_subcategory = None if base_plot_click_data is not None: selected_subcategory = base_plot_click_data['points'][0]['x'] # Open a new database connection conn = sqlite3.connect(r"C:\Users\HituJani\Downloads\txns.db") # SQL query to retrieve transaction data by state for the selected category, subcategory, and numerical feature query = f''' SELECT StateCode, {selected_category}, SUM({numerical_feature}) AS TotalTransactionAmount FROM transactions WHERE {selected_category} = ? GROUP BY StateCode, {selected_category} ''' # Execute the query and fetch the results into a DataFrame state_data = pd.read_sql(query, conn, params=(selected_subcategory,)) # Close the database connection conn.close() # Create a Choropleth map using the filtered data fig = px.choropleth( data_frame=state_data, locationmode='USA-states', locations='StateCode', scope='usa', color='TotalTransactionAmount', hover_data={'StateCode': True, 'TotalTransactionAmount': ':$,f'}, color_continuous_scale='Reds', labels={'TotalTransactionAmount': 'Total Transaction Amount'}, template='plotly_dark' ) fig.update_traces( hovertemplate="<b>%{customdata[0]}</b><br>" + "<b>TotalTransactionAmount</b>: $%{customdata[1]:,.2f}<br>", customdata=list(zip(state_data['StateCode'], state_data['TotalTransactionAmount'])) ) fig.update_layout( title_text=f'Total Transaction Amount by State for Category: {selected_category}, Subcategory: {selected_subcategory}', title_xanchor='center', title_font=dict(size=12), title_x=0.5, geo=dict(scope='usa'), ) # Return the figure and set the display style to block (visible) return fig, {"display": "block"} except Exception as e: print(f"Error: {e}") return go.Figure(), {"display": "none"} if __name__ == '__main__': app.run_server(debug=True, use_reloader=False)
Außerdem habe ich diese CSS-Datei zum Formatieren der Webseite:
/* styles.css */ .container { display: grid; grid-template-rows: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr; grid-column-gap: 15px; grid-row-gap: 15px; background-image: url("https://plainbackground.com/plain1024/383c3d.png"); background-size: 100%; background-position: grid-ce; } .hexgrid-1-container { /* grid-area: 1/1/3/3; */ grid-row: span 2; grid-column: span 2; padding: 2px 2px 2px 2px; border: 1px solid hsl(176, 87%, 7%, 0.6); border-radius: 10px; box-shadow: rgba(250, 118, 3, 0.4) -5px 5px, rgba(250, 118, 3, 0.3) -10px 10px, rgba(250, 118, 3, 0.2) -15px 15px; background: hsla(0, 7%, 11%, 0.9); position: relative; } .hexgrid-2-container { grid-area: 1/3/2/4; padding: 3rem 4rem 4rem; width: 70%; border: 1px solid hsl(176, 87%, 7%, 0.6); border-radius: 10px; box-shadow: rgba(250, 118, 3, 0.4) -5px 5px, rgba(250, 118, 3, 0.3) -10px 10px, rgba(250, 118, 3, 0.2) -15px 15px; background: hsl(0, 7%, 11%, 0.9); position: relative; } .base-plot-container { /* Add your custom styles for base-plot container here */ grid-area: 2/3/3/4; padding: 3rem 4rem 4rem; width: 70%; border: 1px solid hsl(176, 87%, 7%, 0.6); border-radius: 10px; box-shadow: rgba(250, 118, 3, 0.4) -5px 5px, rgba(250, 118, 3, 0.3) -10px 10px, rgba(250, 118, 3, 0.2) -15px 15px; background: hsl(0, 7%, 11%, 0.9); position: relative; } .us-map-container { grid-area: 3/3/4/4; padding: 3rem 4rem 4rem; width: 70%; /* position: fixed; */ border: 1px solid hsl(176, 87%, 7%, 0.6); border-radius: 10px; box-shadow: rgba(250, 118, 3, 0.4) -5px 5px, rgba(250, 118, 3, 0.3) -10px 10px, rgba(250, 118, 3, 0.2) -15px 15px; background: hsl(0, 7%, 11%, 0.9); position: relative; } .fifth-container { grid-area: 3/2/4/3; border: 1px solid hsl(176, 87%, 7%, 0.6); border-radius: 10px; box-shadow: rgba(250, 118, 3, 0.4) -5px 5px, rgba(250, 118, 3, 0.3) -10px 10px, rgba(250, 118, 3, 0.2) -15px 15px; background: hsl(0, 7%, 11%, 0.9); position: relative; } .sixth-container { grid-area: 3/1/4/2; border: 1px solid hsl(176, 87%, 7%, 0.6); border-radius: 10px; box-shadow: rgba(250, 118, 3, 0.4) -5px 5px, rgba(250, 118, 3, 0.3) -10px 10px, rgba(250, 118, 3, 0.2) -15px 15px; background: hsl(0, 7%, 11%, 0.9); position: relative; }
Ich habe versucht, die Rückruffunktion zu aktualisieren, aber ich habe keine großen Fortschritte gemacht und es schien, als hätte ich viele Dinge falsch gemacht. Wie lässt sich diese Funktionalität am einfachsten implementieren? Vielen Dank im Voraus.
P粉2524239062024-02-27 15:34:56
您可以进行回调来侦听单击事件,交换图形位置,然后将图形返回到更新的位置。您需要根据您的用例设置点击逻辑。你可以实现这样的东西:
@app.callback( Output('hexgrid-1-container', 'children'), Output('hexgrid-2-container', 'children'), Input('hexgrid-2', 'clickData'), State('hexgrid-1-container', 'children'), State('hexgrid-2-container', 'children') ) def swap_graphs(clickData, hexgrid_1_children, hexgrid_2_children): clicked_graph_id = 1 # Assuming hexgrid-2 was clicked, you can change this logic based on your use case if clicked_graph_id == 1: # Swap graph between hexgrid-1 and hexgrid-2 return hexgrid_2_children, hexgrid_1_children return hexgrid_1_children, hexgrid_2_children