Plot in asis output before a `knit_child` gets placed after
Hi @yihui!
In the next minimal reproducible example, for j=1, the scatterplot between Speed and Distance is placed before the mtcars kable, as expected, but for j=2 it is placed just before the plot in knit_child. This issue happens only when including the knit_child block. Further, I include 2 different plots for j=1 and j=2, since if it is the same plot it is printed a unique time just before the plot in knit_child (would be this another bug?).
I included the cat('\n\n<!-- -->\n\n') thinking if they could solve the issue, as you suggests in https://bookdown.org/yihui/rmarkdown-cookbook/kable.html#generate-multiple-tables-from-a-for-loop.
Since that does not work, how may I avoid this issue?
Also, may this issue be related to https://github.com/yihui/knitr/issues/2166 ?
Thanks!
Below the minrep and at the end the promises you ask for to fill an issue.
```{r setup, echo=TRUE, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, results = 'asis',
fig.height = 6, fig.width = 10,
eval = TRUE, message = FALSE, warning = FALSE)
library(knitr)
Speed <- cars$speed
Distance <- cars$dist
```
```{r pressure, echo=TRUE}
for(i in 1:1){
for(j in 1:2){
cat("j = ", j, "\n")
cat('\n\n<!-- -->\n\n')
if(j==1) plot(Speed, Distance, panel.first = grid(8, 8), pch = 0, cex = 1.2, col = "blue")
if(j==2) plot(Speed, Distance, panel.first = lines(stats::lowess(Speed, Distance), lty = "dashed"), pch = 0, cex = 1.2, col = "blue")
cat('\n\n<!-- -->\n\n')
kable(mtcars) |> print()
cat('\n\n<!-- -->\n\n')
}
knitr::knit_child(text = c(
'```{r}',
'plot(pressure)',
'```'
), envir = environment(), quiet = TRUE) |> cat(sep="\n")
cat('\n\n<!-- -->\n\n')
}
```
j = 1

| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Mazda RX4 | 21.0 | 6 | 160.0 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| Mazda RX4 Wag | 21.0 | 6 | 160.0 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| Datsun 710 | 22.8 | 4 | 108.0 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
j = 2
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Mazda RX4 | 21.0 | 6 | 160.0 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| Mazda RX4 Wag | 21.0 | 6 | 160.0 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| Datsun 710 | 22.8 | 4 | 108.0 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |

R version 4.3.2 (2023-10-31) Platform: x86_64-pc-linux-gnu (64-bit) Running under: Debian GNU/Linux 12 (bookworm)
Locale:
LC_CTYPE=en_GB.UTF-8 LC_NUMERIC=C
LC_TIME=en_GB.UTF-8 LC_COLLATE=en_GB.UTF-8
LC_MONETARY=en_GB.UTF-8 LC_MESSAGES=en_GB.UTF-8
LC_PAPER=en_GB.UTF-8 LC_NAME=C
LC_ADDRESS=C LC_TELEPHONE=C
LC_MEASUREMENT=en_GB.UTF-8 LC_IDENTIFICATION=C
Package version:
evaluate_0.23 graphics_4.3.2 grDevices_4.3.2 highr_0.10
knitr_1.45 methods_4.3.2 stats_4.3.2 tools_4.3.2
utils_4.3.2 xfun_0.41 yaml_2.3.8
Created on 2023-12-20 with reprex v2.0.2
By filing an issue to this repo, I promise that
- [x] I have fully read the issue guide at https://yihui.org/issue/.
- [x] I have provided the necessary information about my issue.
- If I'm asking a question, I have already asked it on Stack Overflow or RStudio Community, waited for at least 24 hours, and included a link to my question there.
- If I'm filing a bug report, I have included a minimal, self-contained, and reproducible example, and have also included
xfun::session_info('knitr'). I have upgraded all my packages to their latest versions (e.g., R, RStudio, and R packages), and also tried the development version:remotes::install_github('yihui/knitr'). - If I have posted the same issue elsewhere, I have also mentioned it in this issue.
- [x] I have learned the Github Markdown syntax, and formatted my issue correctly.
I understand that my issue may be closed if I don't fulfill my promises.
Not sure from which data Distance or Speed comes from to reproduce, but several comments.
Instead of for loop with cat(), you should consider going all knit_child way, and generate the all md content you want to iterate on as a child using knit_expand() or directly text content in knit_child with inline R code
See the others recipes in the cookbook about that:
- https://bookdown.org/yihui/rmarkdown-cookbook/knit-expand.html
- https://bookdown.org/yihui/rmarkdown-cookbook/child-document.html
This will allow you to add your plots inside a chunk, with right conditional. That way each plot will be differently handle and correctly by knitr.
You can't just cat() plot, so having it process by knit_child() could be rather important.
I let you try a version. I can show you if you share with the data.
@cderv Sorry because the minrep was not complete. Now I updated including data source.
Can you format correctly please ? https://yihui.org/issue/#please-format-your-issue-correctly
Also you can try with what I shared. I believe it will make your intention works
@cderv Yes. I updated the format again. Sorry for the inconvenience.
Going to matter, I do not understand how should I do. https://bookdown.org/yihui/rmarkdown-cookbook/child-document.html with knit_child is what I am already using. How should be the exemple adapted to your proposal?
Thanks!
Several examples
directly creating text to knit as a child
This is close to what you are doing, but instead, you are just generating the child content as a whole as a vector. Not using cat() and letting knit_child() to all the printing when knitting
---
title: test
output:
html_document:
keep_md: true
---
```{r setup, echo=TRUE, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, results = 'asis',
fig.height = 6, fig.width = 10,
eval = TRUE, message = FALSE, warning = FALSE)
library(knitr)
Speed <- cars$speed
Distance <- cars$dist
```
```{r echo = FALSE, results='asis'}
content <- c()
for (i in 1:1) {
for (j in 1:2) {
content <- c(
content,
sprintf("j = %d", j),
"\n\n",
"```{r, echo = FALSE}",
if (j == 1) 'plot(Speed, Distance, panel.first = grid(8, 8), pch = 0, cex = 1.2, col = "blue")',
if (j == 2) 'plot(Speed, Distance, panel.first = lines(stats::lowess(Speed, Distance), lty = "dashed"), pch = 0, cex = 1.2, col = "blue")',
"```",
"\n\n",
"```{r, echo = FALSE}",
"kable(mtcars)",
"```",
"\n\n"
)
}
content <- c(
content,
c(
'```{r}',
'plot(pressure)',
"```",
"\n\n"
)
)
}
knitr::knit_child(text = content, envir = environment(), quiet = TRUE) |> cat(sep = "\n")
```
Using an external child document
You write the content to use in knit_child in another doc, using inline R code and code chunk as you would in a generic doc, but to access variable from your main document
test.Rmd
---
title: test
output:
html_document:
keep_md: true
---
```{r setup, echo=TRUE, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, results = 'asis',
fig.height = 6, fig.width = 10,
eval = TRUE, message = FALSE, warning = FALSE)
library(knitr)
Speed <- cars$speed
Distance <- cars$dist
```
```{r echo = FALSE, results='asis'}
res <- lapply(1:2, function(j) {
knit_child("_child.Rmd", envir = environment(), quiet = TRUE)
})
cat(unlist(res), sep = '\n')
```
```{r}
kable(mtcars)
```
```{r}
plot(pressure)
```
_child.Rmd
j = `r j`
```{r, eval = j == 1, echo = j == 1}
plot(Speed, Distance, panel.first = grid(8, 8), pch = 0, cex = 1.2, col = "blue")
```
```{r, eval = j == 2, echo = j == 2}
plot(Speed, Distance, panel.first = lines(stats::lowess(Speed, Distance), lty = "dashed"), pch = 0, cex = 1.2, col = "blue")
```
Same but using knit_expand() or another template function to fill a child file
You write the content to knit_child in an external file, using templating expansion to fill the value you need to be variable
test.Rmd
---
title: test
output:
html_document:
keep_md: true
---
```{r setup, echo=TRUE, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, results = 'asis',
fig.height = 6, fig.width = 10,
eval = TRUE, message = FALSE, warning = FALSE)
library(knitr)
Speed <- cars$speed
Distance <- cars$dist
```
```{r echo = FALSE, results='asis'}
res <- lapply(1:2, function(j) {
knit_child("_child.Rmd", envir = environment(), quiet = TRUE)
})
cat(unlist(res), sep = '\n')
```
```{r}
kable(mtcars)
```
```{r}
plot(pressure)
```
Details
j = {{j}}
```{r, eval = {{ j == 1 }}, echo = {{ j == 1 }} }
plot(Speed, Distance, panel.first = grid(8, 8), pch = 0, cex = 1.2, col = "blue")
```
```{r, eval = {{ j == 2 }}, echo = {{ j == 2 }} }
plot(Speed, Distance, panel.first = lines(stats::lowess(Speed, Distance), lty = "dashed"), pch = 0, cex = 1.2, col = "blue")
```
Other templating tools like brew, whisker , even glue could be used with same purpose.
Hope it helps see what you can do to avoid cat() and print() mixing text and R object output
@cderv Wow! That is impressive.
Finally I could get what I wanted using an external child document and splitting code in distinct chunks there.
Thank you for your help.
I keep the issue open as I believe the behaviour reported is yet a bug. Feel free to close it if that behaviour is expected (and already documented?)
It is tricky as issue because you are missing
cat() of raw markdow, and print() of plots and kable(), without any results = "asis" set is quite specific, and does not seem correct usage. So I don't know if this is a bug or not.
It would require to minimized the example I believe.
@yihui do you think there is something to look into ?
Actually results = 'asis' is specified by default through knitr::opts_chunk$set, and I only use cat for knit_child and the separator cat('\n\n<!-- -->\n\n'), which I would not use if they were unneeded.
Actually results = 'asis' is specified by default through knitr::opts_chunk$set,
Oh that is why. Ok thanks for clarification !
A smaller reprex with knit_child()
---
title: test
output:
html_document:
keep_md: true
---
```{r pressure, results='asis'}
plot(cars$speed, cars$dist, panel.first = grid(8, 8), pch = 0, cex = 1.2, col = "blue")
knitr::kable(head(cars))
knitr::knit_child(text = c(
'```{r}',
'plot(pressure)',
'```'
), envir = environment(), quiet = TRUE) |> cat(sep="\n")
```
Which do no happen without
---
title: test
output:
html_document:
keep_md: true
---
```{r pressure, results='asis'}
plot(cars$speed, cars$dist, panel.first = grid(8, 8), pch = 0, cex = 1.2, col = "blue")
knitr::kable(head(cars))
plot(pressure)
```
So probably something to look into 🤔
This does seem to be a complicated bug. I haven't figured out the reason after some investigation. Sorry.