# ============================================================================ # Quality Growth Strategy Dashboard # グローバルマーケティング戦略及びソートリーダーシップの高度化 # データドリブンな経営ナラティブによる「Quality Growth」の実現 # ============================================================================ library(shiny) library(shinydashboard) library(ggplot2) library(dplyr) library(tidyr) library(plotly) library(DT) library(scales) # ============================================================================ # データ生成(架空データ) # ============================================================================ set.seed(2026) # 1. 海外ブランディング時系列データ(24ヶ月) create_branding_data <- function() { n_months <- 24 dates <- seq(as.Date("2024-01-01"), by = "month", length.out = n_months) # トレンド + 季節性 + ノイズ trend <- seq(200, 1000, length.out = n_months) seasonal <- 100 * sin(seq(0, 4*pi, length.out = n_months)) noise <- rnorm(n_months, 0, 50) spend <- trend + seasonal + noise search <- 0.8 * lag(spend, 1, default = 200) + 1.2 * lag(spend, 2, default = 200) + rnorm(n_months, 0, 30) revenue <- 2000 + 3.5 * lag(spend, 2, default = 300) + 2.8 * lag(search, 1, default = 400) + rnorm(n_months, 0, 200) data.frame( Date = dates, Spend = round(spend, 0), Search = round(pmax(search, 100), 0), Revenue = round(pmax(revenue, 2000), 0) ) } # 2. 地域別ROIデータ create_regional_data <- function() { regions <- c("US", "Europe", "Asia", "LATAM", "MEA") n_per_region <- 12 regional_data <- do.call(rbind, lapply(regions, function(region) { base_spend <- switch(region, "US" = 800, "Europe" = 600, "Asia" = 400, "LATAM" = 200, "MEA" = 150 ) roi_mult <- switch(region, "US" = 3.99, "Europe" = 3.53, "Asia" = 6.48, "LATAM" = 4.2, "MEA" = 5.1 ) spend <- base_spend + rnorm(n_per_region, 0, base_spend * 0.2) search <- spend * runif(n_per_region, 1.2, 1.8) + rnorm(n_per_region, 0, 50) revenue <- spend * roi_mult + rnorm(n_per_region, 0, spend * 0.3) data.frame( Region = region, Month = seq(as.Date("2025-01-01"), by = "month", length.out = n_per_region), Spend = round(spend, 0), Search = round(search, 0), Revenue = round(revenue, 0), Tech_Score = round(runif(n_per_region, 55, 90), 1), Design_Score = round(runif(n_per_region, 55, 90), 1), Trust_Score = round(runif(n_per_region, 55, 90), 1) ) })) regional_data } # 3. ソートリーダーシップデータ create_tl_data <- function() { n_sessions <- 600 channels <- c("Organic", "Paid", "Social", "Direct", "Referral") reports <- c( "AI活用事例2025", "サステナビリティ報告", "DX推進白書", "次世代インフラ展望", "グローバル市場分析" ) channel_probs <- c(0.32, 0.19, 0.20, 0.19, 0.10) tl_data <- data.frame( session_id = paste0("S", sprintf("%04d", 1:n_sessions)), channel = sample(channels, n_sessions, replace = TRUE, prob = channel_probs), report = sample(reports, n_sessions, replace = TRUE), duration_sec = NA, scrolled = NA, pages_viewed = NA ) # チャネル別の行動パターン for (i in 1:nrow(tl_data)) { ch <- tl_data$channel[i] if (ch == "Referral") { tl_data$duration_sec[i] <- round(rnorm(1, 130, 40)) tl_data$scrolled[i] <- runif(1) < 0.85 tl_data$pages_viewed[i] <- rpois(1, 3) + 1 } else if (ch == "Organic") { tl_data$duration_sec[i] <- round(rnorm(1, 105, 35)) tl_data$scrolled[i] <- runif(1) < 0.75 tl_data$pages_viewed[i] <- rpois(1, 2.5) + 1 } else if (ch == "Direct") { tl_data$duration_sec[i] <- round(rnorm(1, 70, 30)) tl_data$scrolled[i] <- runif(1) < 0.60 tl_data$pages_viewed[i] <- rpois(1, 2) + 1 } else if (ch == "Social") { tl_data$duration_sec[i] <- round(rnorm(1, 65, 35)) tl_data$scrolled[i] <- runif(1) < 0.50 tl_data$pages_viewed[i] <- rpois(1, 1.5) + 1 } else { # Paid tl_data$duration_sec[i] <- round(rnorm(1, 35, 20)) tl_data$scrolled[i] <- runif(1) < 0.30 tl_data$pages_viewed[i] <- rpois(1, 1) + 1 } } tl_data$duration_sec <- pmax(tl_data$duration_sec, 5) # TL成功判定: 滞在60秒以上 + スクロール + 2ページ以上閲覧 tl_data$is_tl_success <- with(tl_data, duration_sec >= 60 & scrolled == TRUE & pages_viewed >= 2 ) tl_data } # データ生成 branding_data <- create_branding_data() regional_data <- create_regional_data() tl_data <- create_tl_data() # ============================================================================ # UI定義 # ============================================================================ ui <- dashboardPage( skin = "blue", dashboardHeader( title = tags$span( tags$img(src = "", height = "30px", style = "margin-right: 10px;"), "Quality Growth Dashboard" ), titleWidth = 350 ), dashboardSidebar( width = 280, sidebarMenu( id = "sidebar", menuItem("Executive Summary", tabName = "summary", icon = icon("chart-line")), menuItem("海外ブランディング分析", tabName = "branding", icon = icon("globe")), menuItem("地域別ROI分析", tabName = "regional", icon = icon("map")), menuItem("ソートリーダーシップ", tabName = "thought_leadership", icon = icon("lightbulb")), menuItem("統計モデル", tabName = "models", icon = icon("calculator")), hr(), tags$div( style = "padding: 15px; color: #b8c7ce; font-size: 12px;", tags$p(tags$strong("プロジェクト情報")) ) ) ), dashboardBody( tags$head( tags$style(HTML(" .content-wrapper { background-color: #f4f6f9; } .box { border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .box-header { border-bottom: 2px solid #3c8dbc; } .info-box { border-radius: 8px; } .small-box { border-radius: 8px; } .nav-tabs-custom > .nav-tabs > li.active { border-top-color: #3c8dbc; } .skin-blue .main-header .logo { background-color: #1a3a5c; } .skin-blue .main-header .navbar { background-color: #1a3a5c; } .metric-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 12px; padding: 20px; color: white; margin-bottom: 15px; } .metric-card h3 { margin: 0; font-size: 2em; } .metric-card p { margin: 5px 0 0; opacity: 0.9; } ")) ), tabItems( # Executive Summary タブ tabItem( tabName = "summary", fluidRow( column(12, tags$div( style = "background: linear-gradient(135deg, #1a3a5c 0%, #2d5a87 100%); padding: 30px; border-radius: 12px; color: white; margin-bottom: 20px;", tags$h2("グローバルマーケティング戦略及びソートリーダーシップの高度化", style = "margin-top: 0;"), tags$p("データドリブンな経営ナラティブによる「Quality Growth」の実現", style = "font-size: 1.2em; opacity: 0.9;") ) ) ), fluidRow( valueBoxOutput("total_investment", width = 3), valueBoxOutput("avg_roi", width = 3), valueBoxOutput("tl_success_rate", width = 3), valueBoxOutput("top_region", width = 3) ), fluidRow( box( title = "Strategic Context: Quality Growthへの戦略的整合", status = "primary", solidHeader = TRUE, width = 6, tags$div( style = "display: flex; gap: 20px;", tags$div( style = "flex: 1; padding: 15px; background: #f8f9fa; border-radius: 8px;", tags$h4("Current Context (現状)", style = "color: #6c757d;"), tags$ul( tags$li("Volume Expansion(量的拡大)"), tags$li("Technology-focused Context(技術文脈)") ), tags$p("課題:経営層への認知・顧客化に最適化されていない", style = "color: #dc3545; font-size: 0.9em;") ), tags$div( style = "flex: 1; padding: 15px; background: #e8f4f8; border-radius: 8px;", tags$h4("Strategic Pivot (新方針)", style = "color: #007bff;"), tags$ul( tags$li("Quality Growth(質的成長)"), tags$li("Management-focused Narrative(経営ナラティブ)"), tags$li("Key Themes: Powered & Next Gen Infrastructure") ) ) ) ), box( title = "Project Scope: 2つの並行ワークストリーム", status = "primary", solidHeader = TRUE, width = 6, tags$div( style = "display: flex; gap: 20px;", tags$div( style = "flex: 1; padding: 15px; background: #fff3cd; border-radius: 8px;", tags$h4("Region A: 思想リーダーシップ", style = "color: #856404;"), tags$p("Focus: 基準の再定義と経営ナラティブ"), tags$ul( tags$li("現状分析(回遊ログ・アクセス解析)"), tags$li("競合ベンチマーク調査"), tags$li("「質」を測る新KPIの定義") ) ), tags$div( style = "flex: 1; padding: 15px; background: #d4edda; border-radius: 8px;", tags$h4("Region B: グローバルマーケティング", style = "color: #155724;"), tags$p("Focus: 標準化と投資対効果(ROI)"), tags$ul( tags$li("拠点間横比較(市場規模 vs 成長率)"), tags$li("ROI分析と投資効率の可視化"), tags$li("レポートフォーマットの標準化") ) ) ) ) ), fluidRow( box( title = "Schedule & Roadmap", status = "info", solidHeader = TRUE, width = 12, plotlyOutput("timeline_chart", height = "250px") ) ) ), # 海外ブランディング分析 タブ tabItem( tabName = "branding", fluidRow( box( title = "海外ブランディング指標の推移(24ヶ月)", status = "primary", solidHeader = TRUE, width = 12, plotlyOutput("branding_timeseries", height = "400px") ) ), fluidRow( box( title = "相互相関分析: Spend vs Revenue", status = "info", solidHeader = TRUE, width = 6, plotOutput("ccf_plot", height = "300px"), tags$p("タイムラグの特定:予算投下から効果が最大化するまでの期間を可視化", style = "font-size: 0.9em; color: #6c757d;") ), box( title = "回帰分析結果", status = "info", solidHeader = TRUE, width = 6, verbatimTextOutput("regression_summary"), tags$div( style = "margin-top: 15px; padding: 10px; background: #e8f4f8; border-radius: 8px;", tags$h5("解釈ポイント:"), tags$ul( tags$li("lag(Spend, 2)の係数: 2ヶ月前の投資が現在の売上に寄与"), tags$li("lag(Search, 1)の係数: 指名検索数が売上をドライブ"), tags$li("R-squared: モデルの説明力") ) ) ) ), fluidRow( box( title = "投資効率サマリー", status = "success", solidHeader = TRUE, width = 12, fluidRow( column(4, tags$div(class = "metric-card", tags$h3(textOutput("total_spend_text")), tags$p("総投資額") ) ), column(4, tags$div(class = "metric-card", style = "background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);", tags$h3(textOutput("total_revenue_text")), tags$p("総売上額") ) ), column(4, tags$div(class = "metric-card", style = "background: linear-gradient(135deg, #ee0979 0%, #ff6a00 100%);", tags$h3(textOutput("simple_roi_text")), tags$p("単純ROI") ) ) ) ) ) ), # 地域別ROI分析 タブ tabItem( tabName = "regional", fluidRow( box( title = "地域別ROI比較", status = "primary", solidHeader = TRUE, width = 6, plotlyOutput("regional_roi_chart", height = "350px") ), box( title = "Brand Search vs Revenue", status = "primary", solidHeader = TRUE, width = 6, plotlyOutput("search_revenue_scatter", height = "350px") ) ), fluidRow( box( title = "定性データ: ブランド認知スコア(0-100)", status = "info", solidHeader = TRUE, width = 12, plotlyOutput("qualitative_heatmap", height = "300px"), tags$div( style = "margin-top: 15px; padding: 10px; background: #fff3cd; border-radius: 8px;", tags$h5("地域別インサイト:"), tags$ul( tags$li(tags$strong("Asia:"), " 技術力(Technology)が高評価 → 最新技術をアピール"), tags$li(tags$strong("Europe:"), " 信頼度(Trust)が重要 → 認証取得・伝統の訴求"), tags$li(tags$strong("US:"), " デザイン(Design)が先行指標 → クリエイティブ重視") ) ) ) ), fluidRow( box( title = "地域別詳細データ", status = "success", solidHeader = TRUE, width = 12, DTOutput("regional_table") ) ) ), # ソートリーダーシップ タブ tabItem( tabName = "thought_leadership", fluidRow( box( title = "チャネル別TL成功率", status = "primary", solidHeader = TRUE, width = 6, plotlyOutput("tl_channel_chart", height = "350px"), tags$p("TL成功条件: 滞在60秒以上 + スクロール発生 + 2ページ以上閲覧", style = "font-size: 0.9em; color: #6c757d;") ), box( title = "コンテンツ別パフォーマンス", status = "primary", solidHeader = TRUE, width = 6, plotlyOutput("content_performance", height = "350px") ) ), fluidRow( box( title = "チャネル別統計サマリー", status = "info", solidHeader = TRUE, width = 12, DTOutput("tl_summary_table") ) ), fluidRow( box( title = "流入チャネルの「質」の評価", status = "warning", solidHeader = TRUE, width = 6, tags$div( style = "padding: 15px;", tags$h5("Referral / Organicの成功率が高い場合:"), tags$p("外部メディアでの紹介や検索意図が、レポートの内容と合致しており、信頼獲得に成功している。"), tags$hr(), tags$h5("Paid / Socialの成功率が低い場合:"), tags$p("「タイトル釣り」などで流入は稼げているが、中身が期待に沿っていない、あるいは「ながら読み」層が多い可能性がある。") ) ), box( title = "次のアクション提案", status = "success", solidHeader = TRUE, width = 6, tags$div( style = "padding: 15px;", tags$ul( tags$li("スター・コンテンツ(High PV, High Success)の横展開"), tags$li("ニッチ・リーダーシップコンテンツの広報強化"), tags$li("Low Successコンテンツのトピック・ターゲット見直し"), tags$li("GA4実データへの移行とスコアリングカスタマイズ") ) ) ) ) ), # 統計モデル タブ tabItem( tabName = "models", fluidRow( box( title = "Analytical Methodology: 統計的アプローチの採用", status = "primary", solidHeader = TRUE, width = 12, tags$div( style = "display: flex; gap: 20px; flex-wrap: wrap;", tags$div( style = "flex: 1; min-width: 250px; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 12px; color: white;", tags$h4("Regression Analysis(回帰分析)"), tags$p("成功に寄与している変数を統計的に切り分け、投資効率を算出する。") ), tags$div( style = "flex: 1; min-width: 250px; padding: 20px; background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); border-radius: 12px; color: white;", tags$h4("Weighted Average Models(加重平均モデル)"), tags$p("各国の市場環境の違いを考慮に入れた、公平な評価モデルの設計。") ), tags$div( style = "flex: 1; min-width: 250px; padding: 20px; background: linear-gradient(135deg, #ee0979 0%, #ff6a00 100%); border-radius: 12px; color: white;", tags$h4("Statistical Tools(分析ツール)"), tags$p("専門的な統計解析ソフトウェアを活用し、ブラックボックス化しない「説明可能なロジック」を構築。") ) ) ) ), fluidRow( box( title = "地域別回帰分析結果", status = "info", solidHeader = TRUE, width = 12, selectInput("region_select", "地域を選択:", choices = c("US", "Europe", "Asia", "LATAM", "MEA"), selected = "US"), verbatimTextOutput("regional_regression") ) ), fluidRow( box( title = "モデル診断プロット", status = "warning", solidHeader = TRUE, width = 6, plotOutput("residual_plot", height = "300px") ), box( title = "予測 vs 実績", status = "warning", solidHeader = TRUE, width = 6, plotlyOutput("predicted_vs_actual", height = "300px") ) ) ) ) ) ) # ============================================================================ # Server定義 # ============================================================================ server <- function(input, output, session) { # --- Value Boxes --- output$total_investment <- renderValueBox({ valueBox( "¥10,500,000", "Total Investment (税別)", icon = icon("yen-sign"), color = "blue" ) }) output$avg_roi <- renderValueBox({ roi_summary <- regional_data %>% group_by(Region) %>% summarise(ROI = sum(Revenue) / sum(Spend)) %>% summarise(Avg_ROI = mean(ROI)) valueBox( sprintf("%.2fx", roi_summary$Avg_ROI), "平均ROI(全地域)", icon = icon("chart-line"), color = "green" ) }) output$tl_success_rate <- renderValueBox({ success_rate <- mean(tl_data$is_tl_success) * 100 valueBox( sprintf("%.1f%%", success_rate), "TL成功率", icon = icon("lightbulb"), color = "yellow" ) }) output$top_region <- renderValueBox({ top <- regional_data %>% group_by(Region) %>% summarise(ROI = sum(Revenue) / sum(Spend)) %>% arrange(desc(ROI)) %>% slice(1) valueBox( top$Region, sprintf("Top ROI Region (%.2fx)", top$ROI), icon = icon("trophy"), color = "purple" ) }) # --- Timeline Chart --- output$timeline_chart <- renderPlotly({ phases <- data.frame( Phase = c("Phase 1: Mobilization", "Phase 2: Deep Analysis", "Phase 3: Reporting", "Phase 4: Close"), Start = as.Date(c("2026-01-19", "2026-02-02", "2026-02-23", "2026-03-02")), End = as.Date(c("2026-02-01", "2026-02-22", "2026-03-01", "2026-03-15")), Color = c("#3498db", "#e74c3c", "#2ecc71", "#9b59b6") ) plot_ly() %>% add_segments( data = phases, x = ~Start, xend = ~End, y = ~Phase, yend = ~Phase, color = ~Phase, colors = phases$Color, line = list(width = 20), showlegend = FALSE ) %>% layout( xaxis = list(title = "", tickformat = "%b %d"), yaxis = list(title = "", categoryorder = "array", categoryarray = rev(phases$Phase)), margin = list(l = 150) ) }) # --- Branding Time Series --- output$branding_timeseries <- renderPlotly({ df_long <- branding_data %>% pivot_longer(cols = c(Spend, Search, Revenue), names_to = "Metric", values_to = "Value") plot_ly(df_long, x = ~Date, y = ~Value, color = ~Metric, type = 'scatter', mode = 'lines+markers', colors = c("Revenue" = "#2ecc71", "Search" = "#e74c3c", "Spend" = "#3498db")) %>% layout( xaxis = list(title = ""), yaxis = list(title = "Value (Standardized Units)"), legend = list(orientation = "h", y = -0.1), hovermode = "x unified" ) }) # --- CCF Plot --- output$ccf_plot <- renderPlot({ ccf_result <- ccf(branding_data$Spend, branding_data$Revenue, lag.max = 10, plot = FALSE) df_ccf <- data.frame( Lag = ccf_result$lag, ACF = ccf_result$acf ) ggplot(df_ccf, aes(x = Lag, y = ACF)) + geom_hline(yintercept = 0, color = "gray50") + geom_hline(yintercept = c(-0.4, 0.4), linetype = "dashed", color = "blue", alpha = 0.5) + geom_segment(aes(xend = Lag, yend = 0), color = "#3498db", size = 1) + geom_point(color = "#3498db", size = 3) + labs(title = "Cross-Correlation: Spend vs Revenue", x = "Lag (months)", y = "ACF") + theme_minimal(base_size = 14) + theme(plot.title = element_text(hjust = 0.5, face = "bold")) }) # --- Regression Summary --- output$regression_summary <- renderPrint({ df <- branding_data model <- lm(Revenue ~ lag(Spend, 2) + lag(Search, 1), data = df) summary(model) }) # --- Metric Cards --- output$total_spend_text <- renderText({ paste0("¥", format(sum(branding_data$Spend) * 1000, big.mark = ",")) }) output$total_revenue_text <- renderText({ paste0("¥", format(sum(branding_data$Revenue) * 1000, big.mark = ",")) }) output$simple_roi_text <- renderText({ roi <- sum(branding_data$Revenue) / sum(branding_data$Spend) sprintf("%.2fx", roi) }) # --- Regional ROI Chart --- output$regional_roi_chart <- renderPlotly({ roi_data <- regional_data %>% group_by(Region) %>% summarise(ROI = sum(Revenue) / sum(Spend)) %>% arrange(desc(ROI)) plot_ly(roi_data, x = ~reorder(Region, ROI), y = ~ROI, type = 'bar', marker = list(color = c("#2ecc71", "#3498db", "#e74c3c", "#9b59b6", "#f39c12"))) %>% layout( xaxis = list(title = "Region"), yaxis = list(title = "ROI (Revenue / Spend)"), title = list(text = "Revenue per 1 Unit of Spend") ) }) # --- Search vs Revenue Scatter --- output$search_revenue_scatter <- renderPlotly({ agg_data <- regional_data %>% group_by(Region) %>% summarise( Avg_Search = mean(Search), Avg_Revenue = mean(Revenue) ) plot_ly(agg_data, x = ~Avg_Search, y = ~Avg_Revenue, color = ~Region, type = 'scatter', mode = 'markers', marker = list(size = 15), text = ~Region) %>% layout( xaxis = list(title = "Brand Search Volume (Avg)"), yaxis = list(title = "Revenue (Avg)") ) }) # --- Qualitative Heatmap --- output$qualitative_heatmap <- renderPlotly({ qual_data <- regional_data %>% group_by(Region) %>% summarise( Technology = mean(Tech_Score), Design = mean(Design_Score), Trust = mean(Trust_Score) ) %>% pivot_longer(cols = c(Technology, Design, Trust), names_to = "Attribute", values_to = "Score") plot_ly(qual_data, x = ~Attribute, y = ~Region, z = ~Score, type = 'heatmap', colorscale = list(c(0, "#f7fbff"), c(0.5, "#6baed6"), c(1, "#08306b")), text = ~round(Score, 1), texttemplate = "%{text}", showscale = TRUE) %>% layout( xaxis = list(title = ""), yaxis = list(title = "") ) }) # --- Regional Table --- output$regional_table <- renderDT({ summary_data <- regional_data %>% group_by(Region) %>% summarise( `Total Spend` = sum(Spend), `Total Revenue` = sum(Revenue), `ROI` = round(sum(Revenue) / sum(Spend), 2), `Avg Tech Score` = round(mean(Tech_Score), 1), `Avg Design Score` = round(mean(Design_Score), 1), `Avg Trust Score` = round(mean(Trust_Score), 1) ) datatable(summary_data, options = list(pageLength = 10, dom = 't'), rownames = FALSE) %>% formatCurrency(c('Total Spend', 'Total Revenue'), currency = "¥", digits = 0) }) # --- TL Channel Chart --- output$tl_channel_chart <- renderPlotly({ channel_summary <- tl_data %>% group_by(channel) %>% summarise( sessions = n(), success_count = sum(is_tl_success), success_rate = mean(is_tl_success) ) %>% arrange(desc(success_rate)) plot_ly(channel_summary, y = ~reorder(channel, success_rate), x = ~success_rate, type = 'bar', orientation = 'h', marker = list(color = c("#00bcd4", "#8bc34a", "#ff9800", "#e91e63", "#9c27b0"))) %>% layout( xaxis = list(title = "Success Rate", tickformat = ".0%"), yaxis = list(title = "") ) }) # --- Content Performance --- output$content_performance <- renderPlotly({ content_summary <- tl_data %>% group_by(report) %>% summarise( sessions = n(), success_rate = mean(is_tl_success), avg_duration = mean(duration_sec) ) plot_ly(content_summary, x = ~sessions, y = ~success_rate, size = ~avg_duration, color = ~report, type = 'scatter', mode = 'markers', marker = list(sizemode = 'diameter', sizeref = 2), text = ~paste(report, "
Sessions:", sessions, "
Success Rate:", scales::percent(success_rate), "
Avg Duration:", round(avg_duration), "sec")) %>% layout( xaxis = list(title = "Sessions (PV)"), yaxis = list(title = "TL Success Rate", tickformat = ".0%") ) }) # --- TL Summary Table --- output$tl_summary_table <- renderDT({ tl_data %>% group_by(channel) %>% summarise( `Sessions` = n(), `Success Count` = sum(is_tl_success), `Success Rate` = scales::percent(mean(is_tl_success), accuracy = 0.1), `Avg Duration (sec)` = round(mean(duration_sec), 1), `Avg Pages Viewed` = round(mean(pages_viewed), 2) ) %>% arrange(desc(`Success Count`)) %>% datatable(options = list(pageLength = 10, dom = 't'), rownames = FALSE) }) # --- Regional Regression --- output$regional_regression <- renderPrint({ region_df <- regional_data %>% filter(Region == input$region_select) model <- lm(Revenue ~ Tech_Score + Design_Score + Trust_Score, data = region_df) summary(model) }) # --- Residual Plot --- output$residual_plot <- renderPlot({ df <- branding_data %>% filter(!is.na(lag(Spend, 2)) & !is.na(lag(Search, 1))) model <- lm(Revenue ~ lag(Spend, 2) + lag(Search, 1), data = branding_data) residuals_df <- data.frame( Fitted = fitted(model), Residuals = residuals(model) ) ggplot(residuals_df, aes(x = Fitted, y = Residuals)) + geom_point(color = "#3498db", size = 3, alpha = 0.7) + geom_hline(yintercept = 0, linetype = "dashed", color = "red") + geom_smooth(method = "loess", se = FALSE, color = "#e74c3c") + labs(title = "Residuals vs Fitted", x = "Fitted Values", y = "Residuals") + theme_minimal(base_size = 14) }) # --- Predicted vs Actual --- output$predicted_vs_actual <- renderPlotly({ model <- lm(Revenue ~ lag(Spend, 2) + lag(Search, 1), data = branding_data) pred_df <- data.frame( Actual = branding_data$Revenue[3:24], Predicted = predict(model, branding_data)[3:24] ) plot_ly(pred_df) %>% add_trace(x = ~Actual, y = ~Predicted, type = 'scatter', mode = 'markers', marker = list(color = "#3498db", size = 10), name = "Data Points") %>% add_trace(x = c(min(pred_df$Actual), max(pred_df$Actual)), y = c(min(pred_df$Actual), max(pred_df$Actual)), type = 'scatter', mode = 'lines', line = list(color = "red", dash = "dash"), name = "Perfect Fit") %>% layout( xaxis = list(title = "Actual Revenue"), yaxis = list(title = "Predicted Revenue") ) }) } # ============================================================================ # アプリ起動 # ============================================================================ shinyApp(ui = ui, server = server)