Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plot in asis output before a knit_child gets placed after #2313

Open
3 tasks done
iagogv3 opened this issue Dec 20, 2023 · 11 comments
Open
3 tasks done

Plot in asis output before a knit_child gets placed after #2313

iagogv3 opened this issue Dec 20, 2023 · 11 comments

Comments

@iagogv3
Copy link

iagogv3 commented Dec 20, 2023

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 #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

  • I have fully read the issue guide at https://yihui.org/issue/.
  • 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.
  • 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.

@cderv
Copy link
Collaborator

cderv commented Dec 20, 2023

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:

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.

@iagogv3
Copy link
Author

iagogv3 commented Dec 20, 2023

@cderv Sorry because the minrep was not complete. Now I updated including data source.

@cderv
Copy link
Collaborator

cderv commented Dec 20, 2023

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

@iagogv3
Copy link
Author

iagogv3 commented Dec 20, 2023

@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!

@cderv
Copy link
Collaborator

cderv commented Dec 21, 2023

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

@iagogv3
Copy link
Author

iagogv3 commented Dec 21, 2023

@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?)

@cderv
Copy link
Collaborator

cderv commented Dec 21, 2023

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 ?

@iagogv3
Copy link
Author

iagogv3 commented Dec 21, 2023

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.

@cderv
Copy link
Collaborator

cderv commented Dec 21, 2023

Actually results = 'asis' is specified by default through knitr::opts_chunk$set,

Oh that is why. Ok thanks for clarification !

@cderv
Copy link
Collaborator

cderv commented Dec 21, 2023

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")
```

image

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)
```

image

So probably something to look into 🤔

@cderv cderv changed the title Bug: Plot in for loop wrongly placed in last iteration, seemingly due to knit_child Plot in asis output before a knit_child gets placed after Dec 21, 2023
@yihui
Copy link
Owner

yihui commented Dec 22, 2023

This does seem to be a complicated bug. I haven't figured out the reason after some investigation. Sorry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants