import streamlit as st import pandas as pd import numpy as np import plotly.express as px import plotly.graph_objects as go import time import random # Import from other modules from ui_components import ( set_custom_css, metric_card, create_sidebar, header_section, create_tabs ) from data_generators import ( generate_physician_segments, generate_prescription_data, generate_key_drivers, generate_regional_data, generate_formulary_scenario_data, generate_message_testing_data ) from api_simulator import simulate_claude_api_call # Main application def main(): # Set page configuration st.set_page_config( page_title="PhysicianTwin™ - Digital Twin Simulator", page_icon="🧬", layout="wide", initial_sidebar_state="expanded" ) # Apply custom CSS set_custom_css() # Create sidebar and get parameters sidebar_params = create_sidebar() brand = sidebar_params["brand"] therapeutic_area = sidebar_params["therapeutic_area"] # Main content header header_section(brand, therapeutic_area) # Create tabs for different analyses tabs = create_tabs() # Generate simulated data df_segments = generate_physician_segments() df_prescriptions = generate_prescription_data() df_drivers = generate_key_drivers() df_regional = generate_regional_data() df_formulary = generate_formulary_scenario_data() df_messages = generate_message_testing_data() # Tab 1: Market Overview with tabs[0]: display_market_overview(df_prescriptions, df_regional, df_drivers) # Tab 2: Physician Segments with tabs[1]: display_physician_segments(df_segments) # Tab 3: Message Testing with tabs[2]: display_message_testing(df_messages) # Tab 4: Predictive Scenarios with tabs[3]: display_predictive_scenarios(df_formulary) # Tab 5: Interactive Twin with tabs[4]: display_interactive_twin() def display_market_overview(df_prescriptions, df_regional, df_drivers): """Display Market Overview tab content""" st.markdown("## Market Performance Dashboard") st.markdown("Analyze current market position and trends for XenoGlip in the diabetes market") # Metrics row col1, col2, col3, col4 = st.columns(4) with col1: st.metric(label="Market Share", value="11.8%") with col2: st.metric(label="YoY Growth", value="+3.2%") with col3: st.metric(label="Formulary Access", value="42.3%") with col4: st.metric(label="Physician Awareness", value="68.7%") st.markdown("---") # Prescription trends chart st.markdown("### Prescription Trends") # Chart for prescription trends fig_rx = px.line( df_prescriptions, x='Date', y='Prescriptions', color='Product', title='Weekly Prescription Volume', color_discrete_sequence=px.colors.qualitative.Bold ) fig_rx.update_layout( xaxis_title="Date", yaxis_title="Total Prescriptions", legend_title="Product", height=450 ) st.plotly_chart(fig_rx, use_container_width=True) # Regional breakdown and key drivers in two columns col1, col2 = st.columns(2) with col1: st.markdown("### Regional Performance") fig_regional = px.bar( df_regional, x='Region', y='Market Share', color='Growth Rate', color_continuous_scale='RdYlGn', title='Market Share by Region', text_auto='.1%' ) fig_regional.update_layout(height=400) st.plotly_chart(fig_regional, use_container_width=True) with col2: st.markdown("### Key Performance Drivers") fig_drivers = px.bar( df_drivers[df_drivers['Segment'] == 'PCP'].sort_values('Importance', ascending=False).head(6), x='Importance', y='Driver', orientation='h', title='Key Drivers for PCPs', color='Importance', color_continuous_scale='Blues' ) fig_drivers.update_traces(texttemplate='%{x:.0%}', textposition='outside') fig_drivers.update_layout(height=400) st.plotly_chart(fig_drivers, use_container_width=True) def display_physician_segments(df_segments): """Display Physician Segments tab content""" st.markdown("## Physician Segment Analysis") st.markdown("Analyze behaviors and preferences across physician microsegments") # Segment selection selected_segments = st.multiselect( "Select segments to compare:", df_segments['Segment'].tolist(), default=df_segments['Segment'].tolist()[:3] ) if not selected_segments: st.warning("Please select at least one segment to display.") else: filtered_df = df_segments[df_segments['Segment'].isin(selected_segments)] # Segment comparison chart st.markdown("### Segment Comparison") # Normalize prescribing volume to 0-1 range for radar chart radar_df = filtered_df.copy() max_rx = radar_df['Prescribing Volume'].max() min_rx = radar_df['Prescribing Volume'].min() radar_df['Normalized Prescribing'] = (radar_df['Prescribing Volume'] - min_rx) / (max_rx - min_rx) if max_rx > min_rx else 0.5 # Radar chart for segment comparison categories = ['Normalized Prescribing', 'Digital Engagement', 'XenoGlip Affinity', 'Message Receptivity'] fig_radar = go.Figure() for segment in selected_segments: segment_data = radar_df[radar_df['Segment'] == segment] values = segment_data[categories].values.flatten().tolist() # Add the first value again to close the loop values.append(values[0]) fig_radar.add_trace(go.Scatterpolar( r=values, theta=categories + [categories[0]], fill='toself', name=segment )) fig_radar.update_layout( polar=dict( radialaxis=dict( visible=True, range=[0, 1] )), showlegend=True, height=500 ) st.plotly_chart(fig_radar, use_container_width=True) # Segment details table st.markdown("### Segment Details") # Format the dataframe for display display_df = filtered_df.copy() display_df['Digital Engagement'] = display_df['Digital Engagement'].map('{:.1%}'.format) display_df['XenoGlip Affinity'] = display_df['XenoGlip Affinity'].map('{:.1%}'.format) display_df['Message Receptivity'] = display_df['Message Receptivity'].map('{:.1%}'.format) st.dataframe(display_df, use_container_width=True) # Segment profiles st.markdown("### Detailed Segment Profiles") for segment in selected_segments: with st.expander(f"Profile: {segment}"): segment_data = df_segments[df_segments['Segment'] == segment].iloc[0] st.markdown(f"**Size:** {segment_data['Size']:,} physicians") st.markdown(f"**Prescribing Volume:** {segment_data['Prescribing Volume']} Rx/month (average)") st.markdown(f"**XenoGlip Affinity:** {segment_data['XenoGlip Affinity']:.1%}") st.markdown("#### Behavioral Characteristics") if "High Volume" in segment: st.markdown("- Treats large numbers of diabetes patients") st.markdown("- Typically relies on established treatment algorithms") st.markdown("- Highly sensitive to formulary positioning") st.markdown("- Values patient affordability and adherence") elif "Early Adopter" in segment: st.markdown("- Quickly evaluates and adopts new treatments") st.markdown("- Actively follows clinical trial data") st.markdown("- Influenced by scientific evidence more than commercial messaging") st.markdown("- Often participates in speaker bureaus and advisory boards") else: st.markdown("- Moderate adoption timeline for new treatments") st.markdown("- Evidence-based decision making") st.markdown("- Balance of clinical and practical considerations") st.markdown("- Responsive to peer influence and educational programs") def display_message_testing(df_messages): """Display Message Testing tab content""" st.markdown("## Message Testing & Optimization") st.markdown("Evaluate message effectiveness across different physician segments") # Message testing chart st.markdown("### Message Effectiveness by Segment") messages_df = df_messages.pivot(index='Message', columns='Segment', values='Receptivity') fig_heatmap = px.imshow( messages_df, text_auto='.0%', aspect="auto", color_continuous_scale='Blues', title="Message Receptivity Heatmap" ) fig_heatmap.update_layout(height=500) st.plotly_chart(fig_heatmap, use_container_width=True) # Message impact chart st.markdown("### Message Impact Analysis") # Calculate average receptivity across segments message_impact = df_messages.groupby('Message').agg({ 'Receptivity': 'mean', 'Impact Score': 'mean' }).reset_index() fig_impact = px.scatter( message_impact, x='Receptivity', y='Impact Score', text='Message', size='Impact Score', color='Receptivity', color_continuous_scale='Viridis', title="Message Impact vs. Receptivity" ) fig_impact.update_traces(textposition='top center') fig_impact.update_layout(height=500) st.plotly_chart(fig_impact, use_container_width=True) # Message optimization tool st.markdown("### Message Optimization Tool") st.markdown("Generate optimized messaging for specific physician segments") col1, col2 = st.columns(2) with col1: target_segment = st.selectbox( "Target Physician Segment", ["Primary Care Physicians", "Endocrinologists", "Cardiologists"] ) message_focus = st.multiselect( "Message Focus Areas", ["Efficacy", "Safety", "Convenience", "Cost", "Guidelines", "Mechanism of Action"], default=["Efficacy", "Safety"] ) with col2: clinical_context = st.selectbox( "Clinical Context", ["Second-line after metformin", "Add-on to current therapy", "Special populations", "Switch from competitor"] ) message_tone = st.select_slider( "Message Tone", options=["Very Clinical", "Balanced", "Practice-Oriented"], value="Balanced" ) if st.button("Generate Optimized Messages"): with st.spinner("Analyzing digital twin data and generating messages..."): time.sleep(2) # Simulate processing time st.markdown("### Optimized Message Recommendations") if target_segment == "Primary Care Physicians": st.markdown("#### Primary Message (92% predicted effectiveness)") st.markdown("**XenoGlip delivers reliable A1C reduction with once-daily dosing and a proven safety profile, making it an ideal choice for your patients after metformin.**") st.markdown("#### Supporting Points:") st.markdown("• A1C reductions of 0.7-0.9% in clinical trials when added to metformin") st.markdown("• Established cardiovascular safety profile with no increased risk of major adverse cardiac events") st.markdown("• Simple once-daily dosing that doesn't require titration") elif target_segment == "Endocrinologists": st.markdown("#### Primary Message (87% predicted effectiveness)") st.markdown("**XenoGlip provides a flexible treatment option with established efficacy and minimal drug interactions, complementing your individualized approach to glycemic control.**") st.markdown("#### Supporting Points:") st.markdown("• Demonstrated efficacy across diverse patient populations in 25+ clinical trials") st.markdown("• Compatible with multiple antihyperglycemic agents for tailored therapy") st.markdown("• Minimal risk of drug-drug interactions with commonly prescribed medications") def display_predictive_scenarios(df_formulary): """Display Predictive Scenarios tab content""" st.markdown("## Predictive Scenario Simulation") st.markdown("Explore the impact of different market events and strategic decisions") # Scenario selection scenario_type = st.radio( "Select Scenario Type:", ["Formulary Change Impact", "Competitive Launch", "Clinical Data Update", "Campaign Optimization"], horizontal=True ) # Formulary Change Scenario if scenario_type == "Formulary Change Impact": # Create a grouped bar chart for formulary scenarios formulary_melted = df_formulary.copy() fig_formulary = px.bar( formulary_melted, x='Scenario', y='Value', color='Metric', barmode='group', title='Projected Impact of Formulary Changes', color_discrete_sequence=px.colors.qualitative.Safe ) fig_formulary.update_layout( xaxis_title="Formulary Scenario", yaxis_title="Impact Value", yaxis_tickformat='.0%', height=500 ) st.plotly_chart(fig_formulary, use_container_width=True) # Other scenario types would be implemented similarly else: st.info(f"Selected scenario: {scenario_type}. Configure settings and run the simulation to see results.") def display_interactive_twin(): """Display Interactive Twin tab content""" st.markdown("## Interactive Physician Digital Twin") st.markdown("Ask questions to the digital twin model and receive AI-powered insights about physician behavior and preferences") # Create a text input for questions query = st.text_area( "Ask a question about physician behavior and prescribing patterns:", placeholder="Example: What factors drive primary care physicians to prescribe XenoGlip for diabetes?", height=100 ) # Add some example questions with st.expander("Example questions to ask the Digital Twin"): st.markdown(""" - What messaging resonates best with endocrinologists for XenoGlip? - How would a formulary change from Tier 3 to Tier 2 impact prescribing? - What patient profile is most likely to receive XenoGlip from PCPs? - How do cardiologists view XenoGlip compared to other diabetes medications? """) # Additional filters for the query col1, col2 = st.columns(2) with col1: specialty_filter = st.multiselect( "Filter by specialty (optional):", ["Primary Care", "Endocrinology", "Cardiology", "NP/PA"], default=[] ) with col2: region_filter = st.multiselect( "Filter by region (optional):", ["Northeast", "Southeast", "Midwest", "Southwest", "West"], default=[] ) # Submit button for the query if st.button("Ask Digital Twin") and query: with st.spinner("Digital twin is analyzing physician data..."): # In a real app, this would make an API call to Claude # Here we'll simulate it with our function time.sleep(2) # Prepare the prompt with filters if applicable prompt = query if specialty_filter: prompt += f" Focus on {', '.join(specialty_filter)}." if region_filter: prompt += f" Consider regional factors in {', '.join(region_filter)}." # Get simulated response from Claude response = simulate_claude_api_call(prompt) # Display response in a nice format st.markdown("### Digital Twin Response") # Use a card-like container for the response with st.container(): st.markdown(f'
{response}
', unsafe_allow_html=True) # Display confidence metrics st.markdown("### Analysis Confidence") col1, col2, col3 = st.columns(3) with col1: # Simulate confidence scores data_confidence = random.uniform(0.7, 0.95) st.metric( label="Data Confidence", value=f"{data_confidence:.0%}" ) with col2: response_consistency = random.uniform(0.75, 0.98) st.metric( label="Response Consistency", value=f"{response_consistency:.0%}" ) with col3: physician_sample = random.randint(1500, 8000) st.metric( label="Physician Sample Size", value=f"{physician_sample:,}" ) if __name__ == "__main__": main()