How to prevent R from inserting newline character when coercing long formula to character?

Viewed 43

I am using missRanger::missRanger that coerces a formula into it's character components, similar to this:

x1 <- as.character(as.formula(paste(paste(rep('foo', 84), collapse=' + '), '~ .')))[[2]]
x1
# [1] "foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
# foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
# foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo +
# foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
# foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo +
# foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
# foo + foo + foo + foo + foo + foo + foo"

If a specific number of terms is exceeded, I noticed R inserts a \n into the string,

x2 <- as.character(as.formula(paste(paste(rep('foo', 85), collapse=' + '), '~ .')))[[2]]
x2
# [1] "foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo +
# foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo +
# foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
# foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo +
# foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
# foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo +
# foo + foo + foo + foo + foo + foo + foo + \n    foo"

which is why missRanger::missRanger probably fails then.*

*the function actually failed due to another problem

According to the debugger, debug: .Internal(as.vector(x, "character")) is used.

The bytes of the threshold appear to be somewhere between 501 and 512.

nchar(x1, type='bytes')
# [1] 501

nchar(x2, type='bytes')
# [1] 512

I'm aware of the console buffer length at 4095 bytes, however I'm dealing with strings here.

Is this string buffer (?) limit already documented anywhere? Is there a way to work around the issue without changing the function code (e.g. by setting options)?

And here comes the session info:

R version 4.1.0 (2021-05-18)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: CentOS Linux 7 (Core)

Matrix products: default
BLAS/LAPACK: /usr/lib64/libopenblasp-r0.3.3.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8    LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] parallel  stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] TraMineR_2.2-3     sandwich_3.0-1     Rfast_2.0.6        RcppZiggurat_0.1.6 Rcpp_1.0.8         missRanger_2.1.3  
 [7] missForest_1.4     itertools_0.1-3    randomForest_4.7-1 matrixStats_0.61.0 lmtest_0.9-39      zoo_1.8-9         
[13] doParallel_1.0.17  iterators_1.0.14   foreach_1.5.2      cobalt_4.3.2       zgp_0.0.10113     

loaded via a namespace (and not attached):
 [1] tidyr_1.2.0         splines_4.1.0       tmvnsim_1.0-2       gtools_3.9.2        Formula_1.2-4       assertthat_0.2.1   
 [7] latticeExtra_0.6-29 pillar_1.7.0        backports_1.4.1     lattice_0.20-45     glue_1.6.1          psy_1.1            
[13] digest_0.6.29       RColorBrewer_1.1-2  checkmate_2.0.0     minqa_1.2.4         colorspace_2.0-2    htmltools_0.5.2    
[19] Matrix_1.4-0        psych_2.1.9         pkgconfig_2.0.3     broom_0.7.12        weights_1.0.4       purrr_0.3.4        
[25] scales_1.1.1        gdata_2.18.0        ranger_0.13.1       jpeg_0.1-9          openxlsx_4.2.5      lme4_1.1-28        
[31] tibble_3.1.6        htmlTable_2.4.0     generics_0.1.2      ggplot2_3.3.5       ellipsis_0.3.2      withr_2.4.3        
[37] nnet_7.3-17         cli_3.2.0           mnormt_2.0.2        survival_3.2-13     magrittr_2.0.2      crayon_1.5.0       
[43] mice_3.14.0         fansi_1.0.2         MASS_7.3-55         nlme_3.1-155        foreign_0.8-82      FNN_1.1.3          
[49] data.table_1.14.2   tools_4.1.0         lifecycle_1.0.1     stringr_1.4.0       cNORM_2.1.0         munsell_0.5.0      
[55] cluster_2.1.2       zip_2.2.0           compiler_4.1.0      rlang_1.0.1         nloptr_1.2.2.3      grid_4.1.0         
[61] rstudioapi_0.13     htmlwidgets_1.5.4   leaps_3.1           base64enc_0.1-3     boot_1.3-28         gtable_0.3.0       
[67] codetools_0.2-18    DBI_1.1.2           R6_2.5.1            gridExtra_2.3       knitr_1.37          dplyr_1.0.8        
[73] fastmap_1.1.0       utf8_1.2.2          Hmisc_4.6-0         stringi_1.7.6       vctrs_0.3.8         rpart_4.1.16       
[79] png_0.1-7           tidyselect_1.1.1    xfun_0.29  
1 Answers

Perhaps str2lang:

str2lang(eval(paste0(rep('foo', 100), collapse = ' + ')))
foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo

And, for me, just guessing this preserves the as.formula ~., my class(x3) [1] "call".

Well, that was wrong, or misleading, and shows how infrequently I use formula(s). But the issue, as noted above is the as.character limit of 500L, which, when not used:

x2_form <- as.formula(paste(paste(rep('foo', 100), collapse=' + '), '~ .'))
class(x2_form)
[1] "formula"
x2_form[2]
(foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo + 
    foo + foo + foo + foo + foo + foo + foo + foo + foo + foo)()

And having established how little I know about formula, I defer to you concerning whether the (foo + ...)() of x2_form[2] is properly formed for your purposes.

If, it has to be character, not call, nor formula:

exper <- vector(mode='character', length=3L)
exper[1] <- '~'
exper[3] <- '.'
x_4k <- paste(rep('foo', 4000L), collapse = ' + ')
exper[2] <- x_4k
>grep('\\n', exper[2])
integer(0)
exper[1]
[1] "~"
lengths(strsplit(exper[2], split = '\\+'))
[1] 4000
exper[3]
[1] "."

and missRanger is riding the Zamboni.

Related