1

i'm trying to execute some python class object (using reticulate) which works fine when i'm using it outside of Shiny, but when i run my script within Shiny I get the following error message 'Warning: Error in py_run_string_impl: RuntimeError: Evaluation error: object 'pydata2' not found.' ('pydata2' is a dataframe that is actually created ok, I can print it etc so I don't understand why it says it's missing..). My code is a bit long but i've made a mention where it works until (#all fine until here!). I suspect that the problem is that the command r.pydata2 does not seem to work within Shiny (it does work outside of Shiny so it's strange) ?

Thank you in advance if someone can help!

library(dplyr)
library(shinyWidgets)
library(shinythemes)
library(DT)

fpath <- '/dbfs/dbfs/ShinyApp'

# Define UI
ui <- fluidPage(
  theme = shinytheme("spacelab"),
  navbarPage(
    "APP Platform",
    tabPanel(
      "Select File",
      sidebarPanel(
        selectInput("selectfile", "Select File", choice = list.files(fpath, pattern = ".csv")),
        mainPanel("Main Panel", dataTableOutput("ftxtout"), style = "font-size:50%") # mainPanel
      ), # sidebarPanel
    ), # tabPanel
    tabPanel(
      "Subset Data",
      sidebarPanel(
        dropdown(
          size   = "xs",
          label  = "Please Select Columns to Display",
          icon   = icon("sliders"),
          status = "primary",
          pickerInput(
            inputId  = "columns",
            #       label = "Select Columns",
            choices  = NULL,
            multiple = TRUE
          ) # pickerInput
        ), # dropdown
        selectInput("v_attribute1", "First Attribute to Filter Data", choices = NULL),
        selectInput("v_attribute2", "Second Attribute to Filter Data", choices = NULL),
        selectInput("v_filter1", "First Filter", choices = NULL),
        selectInput("v_filter2", "Second Filter", choices = NULL),
        textInput("save_file", "Save to file:", value = ""),
        actionButton("doSave", "Save Selected Data"),
        actionButton("doBert", "Run Bert Model")
      ), # sidebarPanel
      mainPanel(
        tags$br(),
        tags$br(),
        h4("Data Selection"),
        dataTableOutput("txtout"),
        style = "font-size:70%"
      ), # mainPanel
      mainPanel(
        tags$br(),
        tags$br(),
        h4("Topic Words"),
        dataTableOutput("topicwrds"),
        style = "font-size:70%"
      ) # mainPanel
      
      
    ), # Navbar 1, tabPanel
    tabPanel("Create Label", "This panel is intentionally left blank")
  ) # navbarPage
) # fluidPage


# Define server function
server <- function(input, output, session) {
  # DECLARE REACTIVEVALUES FUNCTION HERE
  rResult <- reactiveValues(df_sub = NULL , r_top_words2 = NULL, r_output=NULL)
  
  output$fileselected <- renderText({
    paste0("You have selected: ", input$selectfile)
  })

  info <- eventReactive(input$selectfile, {
    fullpath <- file.path(fpath, input$selectfile)
    read.csv(fullpath, header = TRUE, sep = ",")
  })

  observeEvent(info(), {
    df <- info()
    vars <- names(df)
    # Update select input immediately after clicking on the action button.
    updatePickerInput(session, "columns", "Select Columns", choices = vars, selected = vars[1:2])
  })

  observeEvent(input$columns, {
    vars <- input$columns
    updateSelectInput(session, "v_attribute1", "First Attribute to Filter Data", choices = vars)
    updateSelectInput(session, "v_attribute2", "Second Attribute to Filter Data", choices = vars, selected = vars[2])
  })

  observeEvent(input$v_attribute1, {
    choicesvar1 <- unique(info()[[input$v_attribute1]])
    req(choicesvar1)
    updateSelectInput(session, "v_filter1", "First Filter", choices = choicesvar1)
  })

  observeEvent(input$v_attribute2, {
    choicesvar2 <- unique(info()[[input$v_attribute2]])
    req(choicesvar2)
    updateSelectInput(session, "v_filter2", "Second Filter", choices = choicesvar2)
  })

  output$ftxtout <- renderDataTable(
    {
      head(info())
    },
    options = list(pageLength = 5)
  )

  output$txtout <- renderDataTable(
    {
      f <- info() %>% subset(select = input$columns)
      f$var1 <- f[[input$v_attribute1]]
      f$var2 <- f[[input$v_attribute2]]
      ff <- f %>% dplyr::filter(var1 == input$v_filter1 & var2 == input$v_filter2)
      fff <- ff %>% subset(select = -c(var1, var2))
      head(fff)
    },
    options = list(pageLength = 5)
  ) # renderDataTable

  # Saving data
  observeEvent(input$doSave, {
    req(
      input$columns, input$v_attribute1,
      input$v_attribute2, input$v_filter1,
      input$v_filter2, input$save_file
    )
    df <- info() %>% select(all_of(input$columns))
    df_filtered <- df %>%
      dplyr::filter(
        .data[[input$v_attribute1]] == input$v_filter1 &
          .data[[input$v_attribute2]] == input$v_filter2
      )
    fullfpath <- paste0(file.path(fpath, input$save_file), ".csv", sep = "")
    write.csv(df_filtered, fullfpath, row.names = TRUE)
    showNotification(paste("Data Has been saved"), duration = NULL)
    rResult$df_sub <-df_filtered #passed to other object below
  })
  
  observeEvent(input$doBert, {
  pydata2=rResult$df_sub
  showNotification(paste("pydata2 loaded"), duration = NULL) #all fine until here!
py_run_string("
pydata=r.pydata2
pipeline=RunBert(pydata[\"transcript\"],pydata[\"conversation_id\"],36)
pipeline.get_ready_docs()
pipeline.create_model()
top_words=pipeline.get_top_words()
output = pipeline.get_output()
                 ")
# rResult$r_output <-py$output
# rResult$r_top_words2 <-py$top_words
showNotification(paste("Topic have been run"), duration = NULL)
# output$topicwrds <- renderDataTable({rResult$r_top_words2},options = list(pageLength = 5))
  })
} # server

# Create Shiny object
shinyApp(ui = ui, server = server)
tezzaaa
  • 459
  • 1
  • 6
  • 17

1 Answers1

1

The problem is that pydata2 is scoped only locally. python can only access global variables through r..

Thus, you have to define pydata2 on global scope. Yet a better way would be to define a python function which accepts your data as an argument and returns whatever is needed. Then you do not have to pollute your global environment with a copy of pydata2.

This sample code illustrates the problem behind and shows

  1. how to use assign to circumvent it
  2. how to use a function to avoid spamming the .GlobalEnv
library(shiny)
library(reticulate)

globally <- "I am defined globally"

ui <- fluidPage(
  fluidRow(
    actionButton("run", "Run!")
  ))

server <- function(input, output, session) {
  observeEvent(input$run, {
    locally <- "I am defined locally"
    assign("local_globally", "I am defined locally but in global scope", envir = .GlobalEnv)
    script1 <- paste("try:",
      "   print(r.globally)",
      "   print(r.local_globally)",
      "   print(r.locally)",
      "except Exception as inst:",
      "   print(inst)", sep = "\n")
    script2 <- paste("def fun(x):",
                     "   return(x)", sep = "\n")
    py_run_string(script1)
    py_run_string(script2)
    print(py$fun(locally))
  })
}

shinyApp(ui, server)

The output on the cosole after pressing the actionButton is:

I am defined globally

I am defined locally but in global scope

Evaluation error: object 'locally' not found.

[1] "I am defined locally"

thothal
  • 16,690
  • 3
  • 36
  • 71
  • thanks a lot for your help, the problem is I can't use something like globally <- "I am defined globally" as my data would change every time, and the functions/object i am trying to apply are quite long so i tend to have them in memory prior to running the app. Could I just use something like assign("local_globally", "I am defined locally but in global scope", envir = .GlobalEnv) but assign the data itself? – tezzaaa Aug 19 '22 at 09:43
  • Sure, `assign` is basically the same as `<-` but you can chose the scope. But as said, I woudl refactor your `python` code to use a function and then you can simply pass your data without bothering about the scope. – thothal Aug 19 '22 at 09:46
  • i tried with this but it does not work : pydata2 <-assign(rResult$df_sub,envir=.GlobalEnv) ; I get the following error msg : Warning: Error in assign: argument "value" is missing, with no default – tezzaaa Aug 19 '22 at 09:51
  • Ah! i just got it work i think, I did set the following to global: rResult <- reactiveValues(df_sub = NULL , r_top_words2 = NULL, r_output=NULL, envir=.GlobalEnv) – tezzaaa Aug 19 '22 at 10:04
  • Good that itt worked finally, but it is a bit of an overkill to store the reactive on the global environment, when all you want to do is that the data (once all the dependencies are solved) is accessible in python. So you could do `assign("pydata2", rResult$df_sub, envir = .GlobalEnv)`. That's what the error message was saying, you forgot to provide the name, Check `?assign` for further information. – thothal Aug 19 '22 at 11:18
  • thank you so much for your help, have a great week end – tezzaaa Aug 19 '22 at 11:53