R Shiny:创建 X 个等于变量数的数字输入框

R Shiny: Creating X numbers of numericInput boxes equal to number of variables

我正在尝试创建与财务相关的 R Shiny 应用程序。截至目前,我有一个用于创建自定义投资组合的选项卡,这要求我为原始 source_file 中的每个变量设置一个 numericInput。这是示例 source_file 的样子:

Date CSI500 Shanghai CSI300 HSI STI
2016-01-01 +5% -2% +5% +10% +12%
2016-01-08 +3% +13% -8% -3% +10%
2016-01-15 +2% +11% -3% +4% -15%

目前,我必须为每个变量手动硬编码每个 numericInput 框,如下所示:

tabPanel("Custom Portfolio by Weight",
                           sidebarPanel(
                             tags$h3("Create your own Custom Portfolio by Asset Weight"),
                             tags$h4("Input:"),
                             numericInput(inputId = "custom_csi500", "CSI500 (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_shanghai", "Shanghai Stock Exchange (%)",  min = 0, max = 100),
                             numericInput(inputId = "custom_csi300", "CSI300 (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_hsi", "HSI (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_sti", "STI (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_twse", "TWSE (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_msciw", "MSCI World (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_sp500", "S&P500 (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_n225", "Nikkei 225 (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_lse", "London Stock Exchange (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_asx", "ASX (%)", min = 0, max = 100),

但是,我想创建一些东西,它可以扩展到我指定的具有不同数量变量的任何数据帧,而无需手动硬编码它们。我希望任何人都能够帮助我编写代码它能够读取我的数据框中的变量数量(日期列除外)并为每个变量创建那么多 numericInput 框。非常感谢您的帮助!如果需要,我在下面附上了我的 App.R 和 Global.R 以供参考。干杯!

App.R

# Load packages
library(shiny)
library(shinythemes)
source("./global.R")

# Defining UI
ui <- fluidPage(theme = shinytheme("darkly"),
                navbarPage(
                  "Test App",
                  tabPanel("Custom Portfolio by Weight",
                           sidebarPanel(
                             tags$h3("Create your own Custom Portfolio by Asset Weight"),
                             tags$h4("Input:"),
                             numericInput(inputId = "custom_csi500", "CSI500 (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_shanghai", "Shanghai Stock Exchange (%)",  min = 0, max = 100),
                             numericInput(inputId = "custom_csi300", "CSI300 (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_hsi", "HSI (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_sti", "STI (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_twse", "TWSE (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_msciw", "MSCI World (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_sp500", "S&P500 (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_n225", "Nikkei 225 (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_lse", "London Stock Exchange (%)", min = 0, max = 100),
                             numericInput(inputId = "custom_asx", "ASX (%)", min = 0, max = 100),
                             mainPanel(
                             plotOutput(outputId = "custom_returns"),
                           ), #mainPanel
                ), #tabpanel
                ) #navbarPage
) #fluidPage

server <- function(input, output, session) {

#to output custom_returns for Custom Portfolio by Asset Weight
    output$custom_returns <- renderPlot({
      calculate_portfolio_returns(
        customrange = input$customrange,
        asset_weights = c(input$custom_csi500/100,
          input$custom_shanghai/100,                
          input$custom_csi300/100,
          input$custom_hsi/100,
          input$custom_sti/100,
          input$custom_twse/100,
          input$custom_msciw/100,
          input$custom_sp500/100,
          input$custom_n225/100,
          input$custom_lse/100,
          input$custom_asx/100,
          input$custom_custom/100))
    })
}
  
# Create Shiny Object
  shinyApp(ui = ui, server = server)

Global.R

# Load packages
library(tidyverse)
library(ggcorrplot)
library(zoo)
library(xts)
library(testit)
library(PerformanceAnalytics)

#choose source file to work with
file_name = file.choose()
source_file = read_csv(file_name)
source_file$Date = as.Date(source_file$Date, format = "%Y-%m-%d")

########################
calculate_portfolio_returns = function(customrange, asset_weights)
{
  #filter source file by date range specified
  source_file_filtered <- source_file %>% 
    filter(Date >= customrange[1] & Date <= customrange[2])
  
  source_file_filtered_no_date <- source_file_filtered[,2:length(source_file_filtered)]
  
  #create new column called Cumulative_Returns to convert % daily returns
  Cumulative_Returns <- cumsum(source_file_filtered_no_date)
  
  # Extract necessary parameters
  n_cols = ncol(Cumulative_Returns)
  n_assets = n_cols
  
  #To ensure portfolio is always weighted at 100% at all times
  assert(length(asset_weights) == n_assets)
  assert(abs(sum(asset_weights)-1) <= 0.001)
  
  portfolio_returns = data.matrix(Cumulative_Returns) %*% asset_weights
  portfolio_returns_with_date = cbind(source_file_filtered[,1], portfolio_returns)
  g = ggplot(data = portfolio_returns_with_date, mapping = aes(x=Date, y=portfolio_returns)) +
   geom_line(color="blue") + ggtitle(paste("Custom Portfolio Returns from", customrange[1], "to", customrange[2])) +
   geom_hline(yintercept = 0, linetype = "dashed") + theme(plot.title = element_text(hjust=0.5)) +
    ylab("Portfolio Returns (%)")
  print(g)
}

这是一个可能对您有所帮助的演示。

您可以将 .csv 文件上传到闪亮的应用程序。它会忽略第一列(或者您可以修改以专门删除日期列)。

数字输入将根据读入的 header 列名称动态生成。

该演示有一个 calc 按钮,它将存储输入数据并进行进一步处理(计算 returns)。还添加了一个 table 来显示输入的数据。

编辑:如果在按下 calc 按钮后您想调用自定义函数 (calculate_portfolio_returns),您可以添加对该函数的调用在 eventReactive 中,因为这取决于输入按钮。要传递来自数字输入的值,您可以将这些值临时存储在向量 vals 中,然后将 vals 作为参数传递给函数(请参阅下面的修改代码)。在演示中,我调用了一个自定义函数 calc_sum,它将在控制台中打印数字输入的总和。最后一点,我在 input_vals() 的末尾添加了一个明确的 return,这样 vals 就可以在 output$table.

中共享使用
library(shiny)

ui <- fluidPage(
  fileInput(inputId = "upload_file", "", accept = '.csv'),
  uiOutput("num_inputs"),
  actionButton("calc", "Calculate"),
  tableOutput("table")
)

server <- function(input, output, session) {
  
  data <- reactive({
    infile <- input$upload_file
    if (is.null(infile))
      return(NULL)
    read.csv(infile$datapath, header = TRUE, sep = ",")
  })
  
  header_vars <- reactive({
    names(data()[-1])
  })
  
  output$num_inputs <- renderUI({
    vars <- length(header_vars())
    if (vars > 0) {
      div(
        lapply(seq(vars), function(x) {
          numericInput(inputId = paste0("var_", x), label = header_vars()[x], value = 0, min = 0, max = 100)
        })
      )
    }
  })
  
  input_vals <- eventReactive(input$calc, {
    n_vars <- length(header_vars())
    vals <- c()
    if (n_vars > 0) {
      vals <- sapply(seq(n_vars), function(i) {
        input[[paste0("var_", i)]]
      }) 
    }
    calc_sum(vals)
    return(vals)
  })
  
  calc_sum <- function(vals) {
    print(sum(vals))
  }
  
  output$table <- renderTable({
    data.frame(
      vars = header_vars(),
      vals = input_vals()
    )
  })
  
}

shinyApp(ui = ui, server = server)