2

Using a modification (adapted from Jason French's blog post here) to the default inline knitr hook, I am printing numeric output rounded to 3 decimal places. If the value is less than 0.001, it returns "< 0.001".

Here's an MWE showing the hook modification, and how I might use it in practice in R Markdown:

```{r setup, echo=FALSE}
library(knitr)
inline_hook <- function(x) {
  if (is.numeric(x)) {
    res <- ifelse(x == round(x),
      sprintf("%d", x),
      sprintf("%.3f", x)
    )
    res <- ifelse(x < 0.001, '< 0.001', res)
    paste(res, collapse = ", ")
  } else paste(as.character(x), collapse = ", ")
}

knit_hooks$set(inline = inline_hook)
``` 

```{r, echo=FALSE}
stat <- 1.2345
p <- 0.000001
```

Blah was significant (test statistic = `r stat`, p = `r p`)

The above renders as:

Blah was significant (test statistic = 1.234, p = < 0.001)

Note the p = < 0.001. Preferably, this would be p < 0.001. Is it possible to have knitr check whether an equals sign precedes the inline expression, and if it does, suppress it when appropriate?

In case it makes a difference, I'm actually knitting a Sweave document, not R Markdown.

jbaums
  • 27,115
  • 5
  • 79
  • 119
  • "Is it possible to have knitr check whether an equals sign precedes the inline expression[?]" No, I don't think so. Would a solution without the inline hook (but comparably simple to use) also be OK? – CL. Feb 27 '16 at 07:36
  • @CL. - I had thought I might have to fall back on defining a function, `fun`, that returns the rounded value(s) as a character string, with the appropriate `=` or `<` prepended. And then call it when necessary, e.g. ```Blah was significant (test statistic `r fun(stat)`, p `r fun(p)`)```, and don't call the `fun` when values are desired without a leading `=`/`<`. Is that what you had in mind? I'm open to any solution, but obviously the simpler (and more readable), the better. – jbaums Feb 27 '16 at 07:41
  • 1
    That's exactly what I have in mind, yes. You can use the hook for normal numbers like `stat`, only for p-values you'd use `fun(p)` (or with more funny naming `p(p)`). – CL. Feb 27 '16 at 07:43
  • @CL. - feel free to post an example for posterity, and if no simpler solution turns up, I'll accept (and I think you're right.. the expressions can't be aware of the surrounding text... I could `readLines('file.tex')`, `gsub` the `= <` out, and write out again before knitting, but that seems pretty clunky...) – jbaums Feb 27 '16 at 07:56

1 Answers1

1

Personally, I don't like the inline_hook used in the question / the linked blog. Automatically converting output like this seems risky to me. The current question is a case in point: Sometimes the conversion is harmful.

There is a trivial and a somewhat complex solution to the issue.

Trivial solution

The hook can be circumvented using asis_output:

`r asis_output(0.0001)`

outputs 1e-04, i.e. the hook doesn't apply. Otherwise the output was < 0.001.

Drawback: You either use the hook, but then you dont know whether you get [number] or < [number] as output (which is a problem when you want output like "p = [number]" / "p < [number]"). Or you use asis_output; then you know there will be no (in-)equality sign, but you cannot take advantage of the hook.

Advanced solution

The following function applies inline_hook but adds an additional = sign if the hook doesn't add <:

le <- function(x) {
  rel <- if (x >= 0.001) "=" else ""
  return(asis_output(paste(rel, inline_hook(x))))
}

This has the following advantages:

  1. The hook can be used.
  2. There always is an (in-) equality sign.

Note that le is not vectorized.

Examples:

```{r setup, echo=FALSE}
library(knitr)
inline_hook <- function(x) {
  if (is.numeric(x)) {
    res <- ifelse(x == round(x),
                  sprintf("%d", x),
                  sprintf("%.3f", x)
    )
    res <- ifelse(x < 0.001, '< 0.001', res)
    paste(res, collapse = ", ")
  } else paste(as.character(x), collapse = ", ")
}

knit_hooks$set(inline = inline_hook)

le <- function(x) {
  rel <- if (x >= 0.001) "=" else ""
  return(asis_output(paste(rel, inline_hook(x))))
}

```

* p `r le(0.01)` (value: 0.01)
* p `r le(0.001)` (value: 0.001)
* p `r le(0.0001)` (value: 0.0001)
* p `r le(0.00001)` (value: 0.00001)

Output:

p = 0.010 (value: 0.01)
p = 0.001 (value: 0.001)
p < 0.001 (value: 0.0001)
p < 0.001 (value: 0.00001)
CL.
  • 14,577
  • 5
  • 46
  • 73