当前位置: 首页 > 知识库问答 >
问题:

从可变函数调用内外的相同函数中获得不同的结果

司空祯
2023-03-14

有人能向我解释一下为什么当我在Mutate中运行转换器ToDisplayTime函数时得到的结果与我自己运行它时不同吗?正确的结果是我自己运行它时获得的结果。还有,为什么我会收到这些警告?当我在可变函数中调用转换器ToDisplayTime时,感觉我可能会将整个timeIn秒列作为参数传递,但我不确定我是否真的了解这里的机制。

library('tidyverse')
#> Warning: package 'tibble' was built under R version 4.1.2

convertToDisplayTime <- function(timeInSeconds){
   ## Takes a time in seconds and converts it
   ## to a xx:xx:xx string format
   
   if(timeInSeconds>86400){   #Not handling time over a day
      stop(simpleError("Enter a time below 86400 seconds (1 day)"))
   } else if(timeInSeconds>3600){
      numberOfMinutes = 0
      numberOfHours = timeInSeconds%/%3600
      remainingSeconds = timeInSeconds%%3600
      
      if(remainingSeconds>60){
         numberOfMinutes = remainingSeconds%/%60
         remainingSeconds = remainingSeconds%%60
      }
      
      if(numberOfMinutes<10){displayMinutes = paste0("0",numberOfMinutes)} 
      else{displayMinutes = numberOfMinutes}
      
      remainingSeconds = round(remainingSeconds)
      if(remainingSeconds<10){displaySeconds = paste0("0",remainingSeconds)} 
      else{displaySeconds = remainingSeconds}
      
      return(paste0(numberOfHours,":",displayMinutes,":", displaySeconds))
      
   } else if(timeInSeconds>60){
      numberOfMinutes = timeInSeconds%/%60
      remainingSeconds = timeInSeconds%%60
      
      remainingSeconds = round(remainingSeconds)
      if(remainingSeconds<10){displaySeconds = paste0("0",remainingSeconds)} 
      else{displaySeconds = remainingSeconds}
      
      return(paste0(numberOfMinutes,":", displaySeconds))
      
   } else{
      return(paste0("0:",timeInSeconds))
   }
}


(df <- tibble(timeInSeconds = c(2710.46, 2705.04, 2691.66, 2708.10)) %>% mutate(displayTime = convertToDisplayTime(timeInSeconds)))
#> Warning in if (timeInSeconds > 86400) {: the condition has length > 1 and only
#> the first element will be used
#> Warning in if (timeInSeconds > 3600) {: the condition has length > 1 and only
#> the first element will be used
#> Warning in if (timeInSeconds > 60) {: the condition has length > 1 and only the
#> first element will be used
#> Warning in if (remainingSeconds < 10) {: the condition has length > 1 and only
#> the first element will be used
#> # A tibble: 4 x 2
#>   timeInSeconds displayTime
#>           <dbl> <chr>      
#> 1         2710. 45:10      
#> 2         2705. 45:5       
#> 3         2692. 44:52      
#> 4         2708. 45:8


convertToDisplayTime(2710.46)
#> [1] "45:10"
convertToDisplayTime(2705.04)
#> [1] "45:05"
convertToDisplayTime(2691.66)
#> [1] "44:52"
convertToDisplayTime(2708.10)
#> [1] "45:08"

创建于 2022-01-06 由 reprex 软件包 (v2.0.1)

共有1个答案

寿鸣
2023-03-14

就像注释中提到的,这里的问题是你的函数没有矢量化:它使用输入的单个值并输出单个值。但是,当输入是值的向量时,这不起作用,因此条件的长度为 1,您会收到警告:

1: Problem with `mutate()` column `displayTime`.\
ℹ `displayTime = convertToDisplayTime(timeInSeconds)`.  
ℹ the condition has length > 1 and only the first element will be used 

在这里,当你使用dplyr::mutate时,从技术上讲,你试图向你的函数提供一个向量,而这个向量没有被格式化来处理它。

您可以考虑以下几个选项:

1.“又快又丑”的方式:

df <- data.frame(timeInSeconds = c(2710.46, 2705.04, 2691.66, 2708.10))

## This one does not work
df %>% mutate(displayTime = convertToDisplayTime(timeInSeconds))

## This one works
df %>% 
  rowwise() %>% 
  mutate(displayTime = convertToDisplayTime(timeInSeconds)) %>% 
  ungroup()

dplyr::rowwise()允许dpryr::mutate()独立处理每一行,而不是按列处理。我想这是你最初期望的行为dplyr::ungroup()sorta按行还原,例如返回默认的按列行为。在这一点上我可能有点苛刻,但这是我以前在不太了解数据帧及其操作方式时使用的伎俩。。。

2. 直接从您的 dplyr 动词进行矢量化:

df %>% 
  mutate(displayTime = base::mapply(convertToDisplayTime, timeInSeconds))
## or
df %>% 
  mutate(displayTime = purrr::map_chr(timeInSeconds, convertToDisplayTime))

两种选择都相似。

3.矢量化您的函数:

convertToDisplayTime_vec <- base::Vectorize(convertToDisplayTime)
# class(convertToDisplayTime_vec)

df %>% mutate(displayTime = convertToDisplayTime_vec(timeInSeconds))

## or
convertToDisplayTime_vec2 <- function(timeInSeconds_vec) {
  mapply(FUN = convertToDisplayTime, timeInSeconds_vec)
}
# class(convertToDisplayTime_vec2)

df %>% 
  mutate(displayTime = convertToDisplayTime_vec2(timeInSeconds))

# Still works on single variables!
# convertToDisplayTime_vec2(6475)

这是我最喜欢的选项,因为一旦它被实现,你就可以在单个变量、向量或数据帧上使用它,而不用担心它。

一个小留档来深入研究这个话题。

PS:顺便说一句,有一点值得记住:在操作data.frametibble对象时,您可能需要小心。尽管它们相似,但它们略有不同,并且某些功能与一个或另一个函数的处理方式不同,或者实际上将一个转换为另一个而您没有注意到...

 类似资料:
  • 问题内容: 今天,我在运行此查询时在PostgreSQL 9.6中遇到无法解释的结果: 两列的预期结果:。但是,仅在2018年5月19日至2018年6月30日的时间间隔内,我能达到我的期望,而对于2018年5月20日至2018年7月1日,我将获得更多的一天: 我不明白为什么会这样,据我所知,在2018-05-20 2018-07-01之间只是一个间隔,这里的Postgres结果是错误的。 我找不到

  • 问题内容: 我正在尝试获得类似这样的功能的参考: 我有以下错误: 如何获得具有指定参数的功能? 问题答案: 由于有两个名称相同但签名不同的方法,因此您必须指定所需的方法: 或者(如@Antonio正确指出的): 如果您需要将类的实例作为第一个参数的咖喱函数,则可以以相同的方式进行,只有签名不同(将@Antonios注释与您的问题进行比较):

  • 当我多次调用同一个函数时,每次都传递了不同的参数,我会这样做: 有没有更方便的方法做到这一点呢?

  • 我是一个本地开发人员和新的Unity。我试图发挥我的优势,在Kotlin编码,然后使用我的代码作为一个插件在Unity。我遇到的一个问题是,我不知道如何引用伴生对象内部的函数。 但我怎样才能在团结中也这样做呢?以下是我尝试过的。 我收到的错误是, AndroidJavaException:java.lang.NosuchMethodError:没有具有name='factory'signature

  • 我有一个内联变量函数内联int foo(…) 我需要调用一个宏(我们称它为宏),它也是可变的 基本上,我需要将其所有输入参数传递给宏。由于使用了选项,将重新定义为另一个宏将是一个简单的解决方案,但我还需要来返回值<注意:我正在尝试连接已编写代码的两部分,不允许更改它们<代码>foo(…) 用于代码的第一部分,宏定义于第二部分。我要做的唯一一件事就是定义一个使用宏的foo(),我不能,因为它们都是可

  • 问题内容: 我是python的新手,正尝试从java调用python的函数。 我的主要要求是: 调用应该是透明的,从某种意义上说,它不应仅需要修改文件就可以从Java对其进行调用。我可能会得到任何带有某些功能的python文件。我应该能够调用任何这些功能,而无需修改文件。 我希望能够同时发送原语类型(的参数,,等等)或非原语类型(,从Java)来Python函数和接收返回所返回的对象(其原始类型或