【问题标题】:How to convert R formula to text?如何将R公式转换为文本?
【发布时间】:2013-02-03 09:40:20
【问题描述】:

我无法像使用文本一样使用公式。我想要做的是将公式连接到图表的标题。但是,当我尝试将公式与文本一起使用时,我失败了:

model <- lm(celkem ~ rok + mesic)
formula(model)
# celkem ~ rok + mesic

这很好。现在我想构建像"my text celkem ~ rok + mesic" 这样的字符串 - 这就是问题所在:

paste("my text", formula(model))
# [1] "my text ~"           "my text celkem"      "my text rok + mesic"

paste("my text", as.character(formula(model)))
# [1] "my text ~"           "my text celkem"      "my text rok + mesic"

paste("my text", toString(formula(model)))
# [1] "my text ~, celkem, rok + mesic"

现在我看到gtools 包中有一个sprint 函数,但我认为这是一个基本的东西,值得在默认环境中解决!!

【问题讨论】:

  • sprint 不再是gtools 的一部分。有人知道这个帖子的解决方案是什么吗?
  • 有人知道为什么这个列表体操是formula(model)的默认行为吗?

标签: r r-formula


【解决方案1】:

formula.tools 包中的一个简短解决方案,作为函数as.character.formula

frm <- celkem ~ rok + mesic
Reduce(paste, deparse(frm))
# [1] "celkem ~ rok + mesic"

library(formula.tools)
as.character(frm)
# [1] "celkem ~ rok + mesic"

Reduce 在长公式的情况下可能有用:

frm <- formula(paste("y ~ ", paste0("x", 1:12, collapse = " + ")))

deparse(frm)
# [1] "y ~ x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + "
# [2] "    x12"                                                      
Reduce(paste, deparse(frm))
# [1] "y ~ x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 +      x12"

这是因为width.cutoff = 60L 中的?deparse

【讨论】:

  • 啊啊,deparse!太感谢了!但是,您为什么使用Reduce?只需deparse(frm) 就足够了。
  • @Tomas,刚刚添加了关于Reduce的解释。
  • Erghh,我对deparse非常热情,但现在看来它很骗人!必须有一些函数可以将公式转换为字符串没有换行、截断等!!
  • @Tomas,我认为没有比您的解决方案短得多的解决方案。我认为 deparse 单独用于视觉使用就足够了,Reduce + deparse 在非视觉使用的长公式中并没有那么糟糕。
  • 不再有Reduce(paste, deparse(.))paste(deparse(.), collapse=' '):R 4.0.0(2020 年 4 月 24 日发布)引入了 deparse1,它永远不会将结果拆分为多个字符串(参见 my answer
【解决方案2】:

试试format

paste("my text", format(frm))
## [1] "my text celkem ~ rok + mesic"

【讨论】:

  • 谢谢,但是这与包装长公式有同样的问题 deparse 有,忽略宽度和对齐选项......
  • 当我尝试你的例子时它工作了,但是,是的,我现在检查了源代码,它确实调用了deparse。似乎deparse(fo, cutoff.width = 200) 有效,但在使用format 时无法将该参数传递给 deparse。
【解决方案3】:

涵盖所有内容的最简单解决方案:

f <- formula(model)
paste(deparse(f, width.cutoff = 500), collapse="")

【讨论】:

  • R 4.0.0(2020-04-24 发布)引入了 deparse1,它从不将结果拆分为多个字符串(参见 my answer
【解决方案4】:

或作为 Julius 版本的替代品(注意:您的代码不是独立的)

celkem = 1
rok = 1
mesic = 1
model <- lm(celkem ~ rok + mesic)
paste("my model ", deparse(formula(model)))

【讨论】:

  • .. 但要小心,它会引起 Julius 提到的问题 - deparse 会截断长公式。
  • 有趣...从来没想过。
【解决方案5】:

R 4.0.0(2020-04-24 发布)引入了deparse1,它从不将结果拆分为多个字符串:

f <- y ~ a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + 
     p + q + r + s + t + u + v + w + x + y + z
deparse(f)
# [1] "y ~ a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + " "    p + q + r + s + t + u + v + w + x + y + z"                   
deparse1(f)
# [1] "y ~ a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z"



但是,它仍然有一个 width.cutoff 参数(默认(最大):500),之后引入了换行符,但行由collapse(默认:" ")分隔,而不是\n,离开额外的空白(即使使用collapse = "")(如果需要,使用gsub 删除它们,请参阅Ross D's answer):

> f <- rlang::parse_expr( paste0("y~", paste0(rep(letters, 20), collapse="+")))
> deparse1(f, collapse = "")
[1] "y ~ a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u +     v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p +     q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k +     l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z + a + b + c + d + e + f +     g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z"

要在 R backports(推荐) 或复制它的实现:

#  Part of the R package, https://www.R-project.org
#
#  Copyright (C) 1995-2019 The R Core Team
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  A copy of the GNU General Public License is available at
#  https://www.R-project.org/Licenses/

deparse1 <- function (expr, collapse = " ", width.cutoff = 500L, ...) 
    paste(deparse(expr, width.cutoff, ...), collapse = collapse)

【讨论】:

    【解决方案6】:

    最简单的方法是这样的:

    f = formula(model)
    paste(f[2],f[3],sep='~')
    

    完成!

    【讨论】:

    • 这是迄今为止最好的通用答案。它是基础 R 并且没有 width.cutoffdeparseformat() 这样的问题。
    【解决方案7】:

    这里有一个使用print.formula 的解决方案,它看起来很巧妙,但它可以在单行中完成工作,避免使用deparse,并且不需要使用额外的包。我只是捕获打印公式的输出,使用capture.output

    paste("my text",capture.output(print(formula(celkem ~ rok + mesic))))
    [1] "my text celkem ~ rok + mesic"
    

    如果是长公式:

     ff <- formula(paste("y ~ ", paste0("x", 1:12, collapse = " + ")))
     paste("my text",paste(capture.output(print(ff)), collapse= ' '))
    
     "my text y ~ x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 +      x12"
    

    【讨论】:

    • 谢谢,但 capture.output(print()) 可以按照 Julius 的建议简化为 deparse(),结果完全相同:paste(deparse(frm), collapse= ' ')...
    【解决方案8】:

    另一个基于deparse 的解决方案是rlang::expr_text()(和rlang::quo_text()):

    f <- Y ~ 1 + a + b + c + d + e + f + g + h + i +j + k + l + m + n + o + p + q + r + s + t + u
    rlang::quo_text(f)
    #> [1] "Y ~ 1 + a + b + c + d + e + f + g + h + i + j + k + l + m + n + \n    o + p + q + r + s + t + u"
    

    它们确实有一个宽度参数来避免换行,但也限制为 500 个字符。至少它是一个很可能已经加载的函数......

    【讨论】:

      【解决方案9】:

      然后添加 gsub 去除空格

      gsub("  ", "", paste(format(frm), collapse = ""))
      

      【讨论】:

      • 然后呢?这个关键字前面不是有if吗?
      • 我对@G 的补充。格洛腾迪克
      • 这更像是对另一个帖子的评论,而不是其本身的答案。一旦你有足够的代表,你可以评论任何帖子。您可以随时对自己的帖子发表评论。
      【解决方案10】:

      今天正在优化一些功能。到目前为止还没有提到的一些方法。

      f <- Y ~ 1 + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u
      bench::mark(
        expression = as.character(as.expression(f)),
        deparse = deparse(f, width.cutoff = 500L),
        deparse1 = deparse1(f),
        tools = formula.tools:::as.character.formula(f),
        stringi = stringi::stri_c(f),
        I = as.character(I(f)),
        as = as(f, "character"),
        txt = gettext(f),
        txtf = gettextf(f),
        sub = sub("", "", f),
        chr = as.character(f),
        str = substring(f, 1L),
        paste = paste0(f),
      )[c(1, 3, 5, 7)]
      
      #> # A tibble: 13 x 3
      #>    expression   median mem_alloc
      #>    <bch:expr> <bch:tm> <bch:byt>
      #>  1 expression   15.4us        0B
      #>  2 deparse        31us        0B
      #>  3 deparse1       34us        0B
      #>  4 tools        58.7us    1.74MB
      #>  5 stringi        67us    3.09KB
      #>  6 I            64.1us        0B
      #>  7 as          100.5us  521.61KB
      #>  8 txt          83.4us        0B
      #>  9 txtf         85.8us    3.12KB
      #> 10 sub          64.6us        0B
      #> 11 chr            60us        0B
      #> 12 str          62.8us        0B
      #> 13 paste        63.5us        0B
      

      【讨论】:

      • 速度和基准测试根本不是问题。如果您的方法在功能上(在任何其他方面)比此处的其他解决方案更好,请描述。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-23
      • 2020-10-15
      • 2017-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多