我在做以下事情时遇到了很大的困难。我有一个由 css 格式化的破折号应用程序,以显示在不同的容器中。标记为“hexgrid-1-container”的容器是最大的容器,而其他容器较小,并围绕该容器组织。我想更新我的破折号应用程序,以便每当单击另一个容器中的图形/图形时,它都会出现在“hexgrid-1-container”中,而之前位于“hexgrid-1-container”中的图形会出现在较小的容器中容器。
我有一个破折号应用程序,如下所示:
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)
此外,我还有这个格式化网页的css文件:
/* 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; }
我尝试更新回调函数,但进展不大,而且似乎我做错了很多事情。实现此功能的最直接方法是什么?预先感谢您。
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