0

I have an r markdown report (output is MS word using officedown::rdocx_document) which includes a paragraph of summary text at the top. It describes the number of confirmed, probable and possible cases based on my data.

However, if the number of cases for any of these categories is 0, I need to supress the sentence for that category. If the number of cases is greater than 0, I need to show the sentence, but also apply some inline code that colours the number based on another condition, using officer::ftext() and a officer::fp_text() style that I created in an earlier chunk. The code to apply this conditional text colour is a bit complex to reproduce here, but the main point is that the text style cannot be saved in an R object. It is only applied on rendering the document.

I was able to conditionally suppress sentences by following Yihui Xie's answer to a similar question here. This works well, and the conditional text colouring is preserved.

However - because this method puts each sentence to conditionally suppress in its own chunk, they are rendered as separate paragraphs with an empty line in between each one.

What I am looking for is a way to combine the non conditional text outside the code chunks, with the conditional sentences created by the code chunks in one single paragraph. I think this means changing the separator between these specific code chunks from the default '\n\n' to ' '. I tried with a lua filter as suggested in this SO post but using '\s' throws an error and halts the knitting, while using a plain space for the code_block_sep is just ignored and does nothing.


local code_block_sep = ' '
function Blocks (blocks)
  for i = #blocks, 2, -1 do -- start at end of list
    -- Both blocks must be code blocks and share the same primary class
    if blocks[i - 1].t == 'CodeBlock' and
       blocks[i].t == 'CodeBlock' and
       blocks[i - 1].classes[1] == blocks[i].classes[1] then
      blocks[i - 1].text = blocks[i - 1].text ..
        code_block_sep ..
        blocks[i].text
      blocks:remove(i)
    end
  end
  return blocks
end

Is there a way to remove the line breaks so that the non-conditional and conditional sentences all appear in the same paragraph?

Here is some example code.

First, I add the knit option to my r markdown setup chunk:

```{r setup, include=FALSE}
# Load knitr library:
library(knitr)

# Set options to accept conditional show/hide with text containing inline code:
knit_engines$set(asis = function(options) {
  if (options$echo && options$eval) knit_child(text = options$code)
})

```

Next, I add some example data in a code chunk:

```{r egdata}
# Number of confirmed cases
n_confirmed <- 14

# Number of probable cases
n_probable <- 0

# Number of possible cases
n_possible <- 5
```

Now I create the conditions in another code chunk:

```{r conditions}
# Create condition to show or hide confirmed cases sentence:
if(n_confirmed > 0){showconf <- TRUE} else {showconf <- FALSE}

# Create condition to show or hide probable cases sentence:
if(n_probable > 0){showprob <- TRUE} else {showprob <- FALSE}

# Create condition to show or hide possible cases sentence:
if(n_possible > 0){showposs <- TRUE} else {showposs <- FALSE}
```

Now I start my paragraph with the non-conditional text:

This summary describes the number of cases by case definition.

Finally I add the conditional sentences (using addition to illustrate the inline code):

```{asis, echo=showconf}
There are *n* = `r n_confirmed + 1` confirmed cases. 
```
```{asis, echo=showprob}
There are *n* = `r n_probable + 1` probable cases. 
```
```{asis, echo=showposs}
There are *n* = `r n_possible + 1` possible cases. 
```

I would like the text when knitted to print in a single paragraph like this:

This summary describes the number of cases by case definition. There are n = 15 confirmed cases. There are n = 6 possible cases.

Amy M
  • 967
  • 1
  • 9
  • 19

2 Answers2

1

You can use list() and do.call() to create an fpar() object made of ftext() objects.

library(officer)
compare <- data.frame(name = c("n_total", "n_children", "n_adults"), 
                      value_new = c(50, 0, 50), 
                      value_old = c(45, 0, 45), 
                      change = c(TRUE, FALSE, TRUE))

fp_default <- fp_text(font.size = 10, font.family = "Calibri")
fp_emphasized <- update(fp_default, color = "#4F81BD", bold = TRUE)

out <- list()
if( any(compare$change)) {
  out <- append(
    out,
    list(ftext("This is a summary paragraph describing the cases by agegroup.", fp_default))
  )
}

is_n_total <- compare$name %in% "n_total"
if( compare$change[is_n_total] ) {
  out <- append(
    out,
    list(
      ftext(" There were ", fp_default),
      ftext(compare$value_new[is_n_total], fp_emphasized),
      ftext(" cases in total.", fp_default)
    )
  )
}
is_n_children <- compare$name %in% "n_children"
if( compare$change[is_n_children] ) {
  out <- append(
    out,
    list(
      ftext(" There were ", fp_default),
      ftext(compare$value_new[is_n_children], fp_emphasized),
      ftext(" children.", fp_default)
    )
  )
}
is_n_adults <- compare$name %in% "n_adults"
if( compare$change[is_n_adults] ) {
  out <- append(
    out,
    list(
      ftext(" There were ", fp_default),
      ftext(compare$value_new[is_n_adults], fp_emphasized),
      ftext(" adults.", fp_default)
    )
  )
}

your_paragraph <- do.call(fpar, out)
# see the result in RStudio Viewer
your_paragraph |> to_html() |> htmltools::HTML() |> htmltools::browsable()

enter image description here

In an R Markdown document:


---
output: officedown::rdocx_document
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, fig.cap = TRUE)
library(officedown)
library(officer)

compare <- data.frame(name = c("n_total", "n_children", "n_adults"), 
                      value_new = c(50, 0, 50), 
                      value_old = c(45, 0, 45), 
                      change = c(TRUE, FALSE, TRUE))

fp_default <- fp_text(font.size = 10, font.family = "Calibri")
fp_emphasized <- update(fp_default, color = "#4F81BD", bold = TRUE)

out <- list()
if( any(compare$change)) {
  out <- append(
    out,
    list(ftext("This is a summary paragraph describing the cases by agegroup.", fp_default))
  )
}

is_n_total <- compare$name %in% "n_total"
if( compare$change[is_n_total] ) {
  out <- append(
    out,
    list(
      ftext(" There were ", fp_default),
      ftext(compare$value_new[is_n_total], fp_emphasized),
      ftext(" cases in total.", fp_default)
    )
  )
}
is_n_children <- compare$name %in% "n_children"
if( compare$change[is_n_children] ) {
  out <- append(
    out,
    list(
      ftext(" There were ", fp_default),
      ftext(compare$value_new[is_n_children], fp_emphasized),
      ftext(" children.", fp_default)
    )
  )
}
is_n_adults <- compare$name %in% "n_adults"
if( compare$change[is_n_adults] ) {
  out <- append(
    out,
    list(
      ftext(" There were ", fp_default),
      ftext(compare$value_new[is_n_adults], fp_emphasized),
      ftext(" adults.", fp_default)
    )
  )
}
```


```{r echo=FALSE}
do.call(fpar, out) 
```

enter image description here

David Gohel
  • 9,180
  • 2
  • 16
  • 34
  • Thanks, this is a good improvement on what I have currently (if statements in the main text for each part of the sentence which is very long winded and messy), as it will make the conditional statements much easier to track and edit if needed later. – Amy M Aug 25 '23 at 15:40
0

I would construct the sentences in one r chunk using if statements and then print them using asis in a different chunk. For example:

```{r, echo=FALSE}
phrases <- "This summary describes the number of cases by case definition."
if (showconf) {
  phrases <- paste(phrases, "There are *n* =", n_confirmed + 1,  "confirmed cases.") 
}
if (showprob) {
  phrases <- paste(phrases, "There are *n* =", n_probable + 1,  "probable cases.") 
}
if (showposs) {
  phrases <- paste(phrases, "There are *n* =", n_possible + 1,  "possible cases.") 
}
```


```{r, results="asis", echo=FALSE}
cat(phrases)
```

which gives:

screenshot of sentence

nrennie
  • 1,877
  • 1
  • 4
  • 14
  • I've tried using if statements with `glue()` but unfortunately this is ignoring what my real inline code is supposed to do (colour the text conditionally using a style created with `officer::fp_text()`. I think the difficulty is that this function only works in the main body of markdown, not in a chunk - hence why it has to be knit separately? – Amy M Jul 13 '23 at 14:47
  • To add - if I use if statements, the correct sentences are printed, but the numbers are not coloured, i.e. inline the call to `ftext()` is ignored. – Amy M Jul 13 '23 at 14:49
  • I have clarified my question and title - hopefully it is a bit clearer what I want to do. – Amy M Jul 13 '23 at 16:47