Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does substitute change noquote text to a string in R?

Tags:

r

I wanted to answer a question regarding plotmath but I failed to get my desired substitute output. My desired output:paste("Hi", paste(italic(yes),"why not?"))
and what I get: paste("Hi", "paste(italic(yes),\"why not?\")")

text<-'paste(italic(yes),"why not?")'
text
[1] "paste(italic(yes),\"why not?\")"
noqoute_text<-noquote(text)
noqoute_text
[1] paste(italic(yes),"why not?")
sub<-substitute(paste("Hi",noqoute_text),
           env=list(noqoute_text=noqoute_text))
sub
paste("Hi", "paste(italic(yes),\"why not?\")")
like image 314
Iman Avatar asked Aug 26 '18 12:08

Iman


2 Answers

You're using the wrong function, use parse instead of noquote :

text<-'paste(italic(yes),"why not?")'
noquote_text <- parse(text=text)[[1]]

sub<- substitute(paste("Hi",noquote_text),env=list(noquote_text= noquote_text))
# paste("Hi", paste(italic(yes), "why not?"))

noquote just applies a class to an object of type character, with a specific print method not to show the quotes.

str(noquote("a"))
Class 'noquote'  chr "a"
unclass(noquote("a"))
[1] "a"

Would you please elaborate on your answer?

In R you ought to be careful about the difference between what's in an object, and what is printed.

What noquote does is :

  • add "noquote" to the class attribute of the object
  • That's it

The code is :

function (obj) 
{
    if (!inherits(obj, "noquote")) 
        class(obj) <- c(attr(obj, "class"), "noquote")
    obj
}

Then when you print it, the methods print.noquote :

  • Removes the class "noquote" from the object if it's there
  • calls print with the argument quote = FALSE
  • that's it

You can actually call print.noquote on a string too :

print.noquote("a")
[1] a

It does print in a similar fashion as quote(a) or substitute(a) would but it's a totally different beast.

In the code you tried, you've been substituting a string instead of a call.

like image 73
Moody_Mudskipper Avatar answered Nov 19 '22 08:11

Moody_Mudskipper


For solving the question I think Moody_Mudskipperss answer works fine, but as you asked for some elaboration...

You need to be careful about different ways similar-looking things are actually stored in R, which means they behave differently.
Especially with the way plotmath handles labels, as they try to emulate the way character-strings are normally handled, but then applies its own rules. The 3 things you are mixing I think:

  • character() is the most familiar: just a string. Printing can be confusing when quotes etc. are escaped. The function noquote basically tells R to mark it's argument, so that quotes are not escaped.
  • calls are "unevaluated function-calls": it's an instruction as to what R should do, but it's not yet executed. Any errors in this call don't come up yet, and you can inspect it.
    Note that a call does not have its own evironment given with it, which means a call can give different results if evaluated e.g. from within a function.
  • Expressions are like calls, but applied more generally, i.e. not always a function that needs to be executed. An expression can be a variable-name, but also a simple value such as "why not?". Also, expressions can consist of multiple units, like you would have with {

Different functions can convert between these classes, but sometimes functions (such as paste!) also convert unexpectedly:

  • noquote does not do that much useful, as Moody_Mudskipper already pointed out: it only changes the printing. But the object basically remains a character
  • substitute not only substitutes variables, but also converts its first argument into (most often) a call. Here, the print bites you, for when printing a call, there is no provision for special classes of its members. Try it: sub[[3]] from the question gives
    [1] paste(italic(yes),"why not?")
    without any backslashes! Only when printing the full call the noquote-part is lost.
  • parse is used to transform a character to an expression. Nothing is evaluated yet, but some structure is introduced, so that you could manipulate the expression.
  • paste is often behaving annoyingly (although as documented), as it can only paste together character-strings. Therefore, if you feed it anything but a character, it firs calls as.character. So if you give it a call, you just get a text-line again. So in your question, even if you'd use parse, as soon as you start pasting thing together, you get the quotes again.

Finally, your problem is harder because it's using plotmaths internal logic.
That means that as soon as you try to evaluate your text, you'll probably get an error "could not find function italic" (or a more confusing error if there is a function italic defined elsewhere). When providing it in plotmath, it works because the call is only evaluated by plotmath, which will give it a nice environment, where italic works as expected.

This all means you need to treat it all as an expression or call. As long as evaluation cannot be done (as long as it's you that handles the expression, instead of plotmath) it all needs to remain an expression or call. Giving substitute a call works, but you can also emulate more closely what happens in R, with

call('paste', 'Hi', parse(text=text)[[1]])
like image 4
Emil Bode Avatar answered Nov 19 '22 08:11

Emil Bode