在空的 tibble 中,列类型会根据添加第一行的方式而改变

Posted

技术标签:

【中文标题】在空的 tibble 中,列类型会根据添加第一行的方式而改变【英文标题】:In an empty tibble, column types change depending on how the first row is added 【发布时间】:2020-08-13 21:12:48 【问题描述】:

我正在开发一个闪亮的应用程序,其中一部分需要编辑(更改现有值,添加新行)到各种小标题。需要编辑许多小标题,每个小标题都有不同的列名和数据类型。我想使用通用编辑器。每个小标题都是空的。

我用零行和键入的列创建小标题。这行得通。

makeTibble <- function() 
  tibble(W=character(), X=numeric(), Y=logical(), Z=structure(NA_real_, class = "Date"))

v <- reactiveValues(data=makeTibble())

当我添加一行并直接按名称操作每一列时,列类型被保留:

observeEvent(input$addDirect, 
    v$data <- v$data %>% add_row(W="Testing...", X=pi, Y=TRUE, Z=as.Date("2020-04-29", origin="1970-01-01"))
  )

但这意味着每个 tibble 都有一个不同的编辑器,并且每次相应 tibble 的结构发生变化时都必须编辑每个编辑器(它会)。所以我尝试自动添加新行:

newValues <- c("W"="Testing...", "X"=pi, "Y"=TRUE, "Z"=as.Date("2020-04-29")) 
observeEvent(input$addLoop, 
  columns <- v$data %>% dplyr::summarise_all(class) %>% tidyr::gather(variable, class)
  v$data <- v$data %>% add_row()
  for (f in columns$variable) 
    v$data <- v$data %>% mutate(!!f := newValues[f])
  
)

但这会将 tibble 中每一列的类型转换为字符。不是我想要的。所以我尽量保留每一列的类型:

asTypedValue <- function(value, type) 
  print(paste0("type: ", type, "; value: ", value))
  switch(type, 
         "character"="Testing...",
         "logical"=TRUE,
         "numeric"=pi,
         "Date"=as.Date("2020-04-29", origin="1970-01-01")
   )


observeEvent(input$addTypedLoop, 
  columns <- v$data %>% summarise_all(class) %>% gather(variable, class)
  v$data <- v$data %>% add_row()
  targetRow <- nrow(v$data)
  for (col in columns$variable) 
    colClass <- (columns %>% filter(variable  == col))$class
    v$data <- v$data %>% mutate(!! col := ifelse(row_number() == targetRow, asTypedValue(newValues[col], colClass), !! col)) 
  
)

这适用于characterlogicalnumeric 列,但Date 列将转换为dbl。我做错了什么?

这是一个 foo Shiny 应用程序来演示发生了什么。

library(shiny)
library(tidyverse)

ui <- fluidPage(
  wellPanel(
    actionButton("reset", "Reset"),
    actionButton("addDirect", "Add row (direct manipulation)"),
    actionButton("addLoop", "Add row (apply loop)"),
    actionButton("addTypedLoop", "Add row (apply loop with typing)")
  ),
  wellPanel(
    verbatimTextOutput("tibble")
  )
)

server <- function(input, output) 
  newValues <- c("W"="Testing...", "X"=pi, "Y"=TRUE, "Z"=as.Date("2020-04-29"))

  makeTibble <- function() 
    tibble(W=character(), X=numeric(), Y=logical(), Z=structure(NA_real_, class = "Date"))
  

  v <- reactiveValues(
    data=makeTibble()
  )

  output$tibble <- renderPrint(
    v$data
  )

  observeEvent(input$reset, 
    v$data <- makeTibble()
  )

  observeEvent(input$addDirect, 
    v$data <- v$data %>% add_row(W="Testing...", X=pi, Y=TRUE, Z=as.Date("2020-04-29", origin="1970-01-01"))
  )

  observeEvent(input$addLoop, 
    columns <- v$data %>% dplyr::summarise_all(class) %>% tidyr::gather(variable, class)
    v$data <- v$data %>% add_row()
    for (f in columns$variable) 
      v$data <- v$data %>% mutate(!!f := newValues[f])
    
  )

  asTypedValue <- function(value, type) 
    print(paste0("type: ", type, "; value: ", value))
    switch(type, 
           "character"="Testing...",
           "logical"=TRUE,
           "numeric"=pi,
           "Date"=as.Date("2020-04-29", origin="1970-01-01")
    )
  

  observeEvent(input$addTypedLoop, 
    columns <- v$data %>% summarise_all(class) %>% gather(variable, class)
    v$data <- v$data %>% add_row()
    targetRow <- nrow(v$data)
    for (col in columns$variable) 
      colClass <- (columns %>% filter(variable  == col))$class
      v$data <- v$data %>% mutate(!! col := ifelse(row_number() == targetRow, asTypedValue(newValues[col], colClass), !! col)) 
    
  )


shinyApp(ui, server)

提前感谢您的帮助。

【问题讨论】:

【参考方案1】:

在这里你连接成一个原子向量:

newValues <- c("W"="Testing...", "X"=pi, "Y"=TRUE, "Z"=as.Date("2020-04-29"))

原子向量包含相同模式的元素,所以一切都被强制转换为字符模式:

> newValues
                 W                  X                  Y                  Z 
      "Testing..." "3.14159265358979"             "TRUE"            "18381" 

改用列表:

newValues <- list("W"="Testing...", "X"=pi, "Y"=TRUE, "Z"=as.Date("2020-04-29"))

(然后使用newValues[[f]])。

【讨论】:

斯蒂芬,谢谢!就我而言,那是树木的时刻!当我使用addLoop 选项时,您的建议已经解决了这个问题[这给了我一个可以在我的应用程序中使用的解决方案],但是 addTypedLoop 仍然将最后一列从Date 转换为double。那里发生了什么?

以上是关于在空的 tibble 中,列类型会根据添加第一行的方式而改变的主要内容,如果未能解决你的问题,请参考以下文章

是否在空的初始化程序列表(并明确指定类型)上调用 std::min 未定义的行为?

如何删除小标题的第一行[重复]

vue添加第一行为空的多选框

如何将网格线保留在空的 TableView Javafx 中

我可以在空的arraylist索引上替换或放置一个新字符串吗

如何根据R中小标题中另一列指示的列的值添加列