In R, the same code cannot be knit out in package Vignette file. "list" object cannot be coerced to type integer
Asked Answered
S

1

6

This question is about generate_msts() function in package GRATIS.

I add some new stuff (make the function has options to transform its output into a lovely tsibble format or keep the original ‘list’ format) and prepare update to CRAN.

New code add as below (detail of the codes with example shown at the bottom of the question)

I wonder should I get tsibble a index? But the generated data seems like do not have a index?

  output <- if (output_format == "list") {
    res                                    #this is output name defined before
  } else if (output_format == "tsibble") {
    as_tsibble(res)
  }
  return(output)
}

And as a guidance, I update the corresponding example for this function in Vignette. Then things become wired.

If I did not save the generated time series output (e.g. x <- my_function()), the vignette cannot knit out. (However, I can use this function directly in an independent normal RMD file)

Use this code directly can show output inside RStudio, but cannot be knit out.

my_function(seasonal.periods = c(7, 365), n = 800, nComp = 2,output_format="tsibble")

enter image description here

Error in Fun(X[[i]],...): 'list' object cannot be coerced to type 'integer' Calls: <Anonymous>... 
as.data.frame -> head  -> head.data.frame -> lappy -> FUN Execution halted.

But, this works fine. It can knit out the vignette and shows head of tsibble.

x <- my_function(seasonal.periods = c(7, 365), n = 800, nComp = 2,output_format="tsibble")
head(x)

However, this is very inconvenience to save it each time before you can use it. I wonder if this is because there is any default setting I used in package or vignette does not change? Or there is some extra step I need to do after I change the function inside R package? Or even the if else content I add need to be improved?

I have tried devtools::document("C:/Users/mreal/Documents/GitHub/package_name");devtools::install("C:/Users/mreal/Documents/GitHub/package_name") to update the re-build function. But this still does not help vignette.

I also tried rm(list=ls()) in console. It does not work as well

Code I used in vignette is as below

Github link:

https://github.com/BocongZhao823/gratis/blob/master/vignettes/QuickStart.Rmd

---
title: "Introduction to gratis"
author: "Bocong Zhao"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Introduction to gratis}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

{r initial, echo = FALSE, cache = FALSE, results = 'hide'}
library(knitr)
opts_chunk$set(
  warning = FALSE, message = FALSE, echo = TRUE,
  fig.width = 7, fig.height = 6, fig.align = 'centre',
  comment = "#>"
)
original <- options("tibble.print_min")
options(tibble.print_min = 5)
# <---- Do stuff with changed option, e.g. print some tibbles ----> 
options(tibble.print_min = original)


{r, message=FALSE, include = FALSE}
library(forecast)
library(tsibble)

{r setup}
# load package
library(gratis)

## Generate mutiple seasonal time series

Time series can exhibit multiple seasonal pattern of different length, especially when series observed at a high frequency such as daily or hourly data.

We use function **generate_msts()** to generate mutiple seasonal time series.

**Definitions**

Here are the definitions of parameter settings in function generate_msts():

|parameter settings | Definition|
|:----|:-----|
|seasonal.periods | a vector of seasonal periods of the time series to be generated|
|nComp|number of mixing components when simulating time series using MAR models|
|n    |length of the generated time series|

**Example**

Suppose we want to use MAR model to generate a time series with **2** mixing components and the length **800** from random parameter spaces. Particularly, this time series has two seasonal periods **7** and **365**.

{r fig.height = 6, fig.width = 7}
# Generate mutiple seasonal time series with 'tsibble' output format
x <- generate_msts(seasonal.periods = c(7, 365), n = 800, nComp = 2,output_format="tsibble")
head(x)

**Plot time series**

{r fig.height = 6, fig.width = 7}
# Generate mutiple seasonal time series with 'list' output format
x <- generate_msts(seasonal.periods = c(7, 365), n = 800, nComp = 2,output_format="list")
autoplot(x)

(generated.R file) The R code used inside a package is as below

** Github link**

https://github.com/BocongZhao823/gratis/blob/master/R/generate_ts.R

#' Generate mutiple seasonal time series from random parameter spaces of the mixture autoregressive (MAR) models.
#'
#' Generate mutiple seasonal time series from random parameter spaces of the mixture autoregressive (MAR) models.
#' @param seasonal.periods a vector of seasonal periods of the time series to be generated.
#' @param n length of the generated time series.
#' @param nComp number of mixing components when simulating time series using MAR models.
#' @param output_format An optional argument which allows to choose output format between "list" and "tsibble"
#' @return a time series with multiple seasonal periods.
#' @export
#' @examples
#' x <- generate_msts(seasonal.periods = c(7, 365), n = 800, nComp = 2, output_format= "list")
#' forecast::autoplot(x)
generate_msts <- function(seasonal.periods = c(7, 365), n = 800, nComp = NULL,output_format="list") {
  x.list <- map(seasonal.periods, function(p) {
    generate_ts(n.ts = 1, freq = p, n = n, nComp = nComp)$N1$x
  })
  names(x.list) <- paste0("Season", seasonal.periods)
  x.list[1:(length(x.list) - 1)] <- lapply(x.list[1:(length(x.list) - 1)], function(x) {
    x - trendcycle(stl(x, "per"))
  })
  weights <- msts_weights(length(seasonal.periods))
  res <- as_tibble(scale(x.list %>% bind_cols())[, ]) %>%
    mapply("*", ., weights) %>%
    as_tibble() %>%
    mutate(x = rowSums(.)) %>%
    select(x) %>%
    msts(seasonal.periods = seasonal.periods)
  # New content
  output <- if (output_format == "list") {
    res
  } else if (output_format == "tsibble") {
    as_tsibble(res)
  }
  return(output)
}

# ===========================================================================
# Simulated weights for the simulation of msts
# ===========================================================================
msts_weights <- function(n.periods) {
  gamma <- runif(n.periods, 0)
  weights <- gamma / sum(gamma)
  return(weights)
}
Sikorski answered 9/9, 2020 at 4:23 Comment(7)
Can you try else if( output_format == "tsibble") {res <- map(res, ~ {.x <- as_tsibble(.x$x); .x})}Lupelupee
sorry, there is a typo. it is .x$x <- as_tsibble(.x$x)Lupelupee
It sounds odd. Hvae you tried tibble::as_tsibble instead of as_tsibble? The idea being that it might load packages weirdly, so specifying 'where' might remove confusion.Amass
Tried. I don't know why it keep saying "list object cannot be coerced to type integer"Sikorski
Could you please provide a full reproducible example? Without a reproducible example, it is really hard to guess what went wrong.Savil
Thank you! Question update. The code and example shown at the bottom of the question with their Github link.Sikorski
@YihuiXie For me the problems only occurs when using rmarkdown/knitr. Can you have a look at my answer - guess you know more about knitr than anyone - could this be the reason for the error?Toolmaker
T
1

I tried to run this for you - my first guess was a NAMESPACE problem. But it seems also related to the generate_msts() function.

I really don't think this has to do with first saving it to a variable x.

Here are my findings:

DOES NOT WORK:

x <- generate_msts(seasonal.periods = c(7, 365), n = 800, nComp = 2,output_format="tsibble")

x

DOES NOT WORK:

print(generate_msts(seasonal.periods = c(7, 365), n = 800, nComp = 2,output_format="tsibble"))

DOES NOT WORK:

x <- generate_msts(seasonal.periods = c(7, 365), n = 800, nComp = 2,output_format="tsibble")

print(x)

WORKS:

 head(generate_msts(seasonal.periods = c(7, 365), n = 800, nComp = 2,output_format="tsibble"))

In the failure cases it is always the same error message as for you:

Error: processing vignette 'QuickStart.Rmd' failed with diagnostics: 'list' object cannot be coerced to type 'integer'

So since head(), str(), class() always worked for me and only print() did not work, I am assuming it is a problem with the print function. So your workaround with saving it into variable x only worked fine, because you did not call the print function.

Also important the problem only occurred for me when using generate_msts() inside Rmarkdown. As I explain later this seems reasonable, since printing in knitr is different from printing on the console.

When I alter your generate_msts() and rebuild the package:

output <- if (output_format == "list") {
    res
  } else if (output_format == "tsibble") {
    tsibble(date = as.Date("2017-01-01") + 0:9,value = rnorm(10))
  }

The Rmarkdown suddenly runs without an error.

My guess would be it is a problem with the print() for your specific data in interaction with knitr.

Printing in knitr seems to be different from printing on the console (might be why it works without rmarkdown)

Here is a nice link about custom print methods and knitr: https://cran.r-project.org/web/packages/knitr/vignettes/knit_print.html

Before knitr v1.6, printing objects in R code chunks basically emulates the R console.

I could imagine the S3 method for knit_print from the tsibble package (which just uses all the printing methods from tibble?) might just not work properly for your specific dataset (I mean it worked for the tsibble I created with tsibble() ). But just a (wild?) guess...the error and behavior overall is really strange ...

Edit: Here is also the R Markdown callstack for the error:

 1. ├─base::print(x)
  2. └─tibble:::print.tbl(x)
  3.   ├─cli::cat_line(format(x, ..., n = n, width = width, n_extra = n_extra))
  4.   │ └─base::paste0(..., collapse = "\n")
  5.   ├─base::format(x, ..., n = n, width = width, n_extra = n_extra)
  6.   └─tsibble:::format.tbl_ts(x, ..., n = n, width = width, n_extra = n_extra)
  7.     ├─base::format(trunc_mat(x, n = n, width = width, n_extra = n_extra))
  8.     └─tibble::trunc_mat(x, n = n, width = width, n_extra = n_extra)
  9.       ├─base::as.data.frame(head(x, n))
 10.       ├─utils::head(x, n)
 11.       └─utils:::head.data.frame(x, n)
 12.         └─base::lapply(...)
 13.           └─utils:::FUN(X[[i]], ...)

Should be similar for you, but if you want to get this on your own, you have to the following commands to your rmarkdown document

options(rlang_trace_top_env = rlang::current_env())
options(error = function() {
  sink()
  print(rlang::trace_back(bottom = sys.frame(-1)), simplify = "none")
})

But as you can see in the callstack, the error is caused by base::print(x), which calls the S3 method tibble:::print.tbl(x), this method then internally calls tsibble:::format.tbl_ts, which calls tibble::trunc_mat, ... and somewhere inside the error is caused.

Ok ... I followed this further down the road and ... what in the end messes inside these function calls, are the knitr options you set in the beginning.

You write at the beginning of your rmarkdown:

original <- options("tibble.print_min")
options(tibble.print_min = 5)

# <---- Do stuff with changed option, e.g. print some tibbles ----> 
options(tibble.print_min = original)

Change this to just:

options(tibble.print_min = 5)

Should work then.

Toolmaker answered 16/9, 2020 at 13:34 Comment(15)
Thank you for helping! Yep, and this problem (cannot knit out) happen each time when I tried to give different function an "if else" option. (The generate_msts() is just one of them).Sikorski
And hey, I am not quite sure why you change the code to tsibble(date = as.Date("2017-01-01") + 0:9,value = rnorm(10)) instead of my original tsibble(res). As that changes might solve the error, but opposite my wishes, which is give the ts data (here the output be defined as res) an option to be transformed into a tsibble format. Is this changed code just an example to proof this question may relate to print()? I did not use print() in this ìf/else or vignette. So do you mean apart from what I mentioned, the other R codes need to change?Sikorski
Yes, just to show, that it can work. You did use print in the vignette. If you just write my_function(seasonal.periods = c(7, 365), n = 800, nComp = 2,output_format="tsibble") without assignment to a variable this automatically causes a print() (because the result will be printed on the console or respectively when using rmarkdown in the document).Toolmaker
So just to explain this, when you just call myfunction() - then the output is printed on the console - if its output is e.g. a tibble, then R doesn't do the nice output per default, there is a print.tbl S3 method somewhere in the tibble package that defines how the tibble is printed. So you basically also call print.tbl - when you call the function without assignment to a variable.Toolmaker
Thank you for explaining, the principle behind is pretty clear! And regarding to how to fix this problem. Are you suggested that I change the code to something like output <- if (output_format == "list") {res } else if (output_format == "tsibble") {x <- as_tsibble(res) print.tbl(x)}return(output)}? 1) As I use tsibble() for time series data instead of tibble, is there a function similar to print.tbl I can apply? 2) And I do check Yihui's link you provided, looks cool! Just considering if there is any function I need to apply from his blog as well. Very appreciate!Sikorski
Might be, you are confusing things a little ... a tsibble is basically a tibble. As you can see when you are calling class() on a tsibble object. So when you print a tsibble it will automatically call print.tbl.Toolmaker
added the callstack for the error for you in the answerToolmaker
Sad sad, the code I just mentioned still cannot solve the error :( Checking your answering nowSikorski
Any idea how to structure of the tsibble/tibble in order to fit input processing/printing function?Sikorski
Ok, I debugged this further - you are lucky in the end it will be a quick fix. What deep down actually in the end caused the error were the initial knitr option settings. See the end of my answer.Toolmaker
Thank you so much, you are very kind and patient. But this further create another problem. The feedback I get from my previous CRAN package release (that the reason I made this part change). The error said "Please always make sure to reset to user's options(), working directory or par() after you changed it in examples and vignettes and demos. e.g.: old <- options(digits = 3) ... options(old) e.g. vignette." Is there a better way to avoid both knit and this cran error? Thank you so muchSikorski
Is that good I delete #original <- options("tibble.print_min") #options(tibble.print_min = 5) # <---- Do stuff with changed option, e.g. print some tibbles ----> #options(tibble.print_min = original) directly?Sikorski
Yes, you could also delete the whole part about options(tibble.print_min = 5) completely - the default is 10 anyway. So it is no big difference in the rmarkdown. It just shows the first 10 lines instead of the first 5 lines of your created tsibble.Toolmaker
Just another hint: your implementation of this CRAN comments was anyway wrong. You misunderstood something there. Like you did it, doing the reset directly at the beginning made no sense at all, since you attempted to change the settings and 1 line later directly reseted the settings. The options(tibble.print_min = original) should have been at the end of the code chunk. So in a code chunk you first save the old settings, then change the settings - THEN you do all the output you want to do with the changed settings - and then you reset to the old settings.Toolmaker
So nice to hear the explanation and correction! Make sense! I will delete the whole part of 'option' then.Sikorski

© 2022 - 2024 — McMap. All rights reserved.