Knitr и интерактивное поведение R

Я публикую здесь мою проблему после того, как заметил, что это подход, рекомендованный автором Knitr для получить больше помощи.

Я немного озадачен файлом .Rmd, который я могу выполнять построчно в интерактивном сеансе R, а также с R CMD BATCH, но это не удается при использовании knit("test.Rmd"). Я не уверен, в чем проблема, и я попытался сузить проблему настолько, насколько мог. Вот пример (в test.Rmd):

```{r Rinit, include = FALSE, cache = FALSE}
opts_knit$set(stop_on_error = 2L)
library(adehabitatLT)
```

The functions to be used later:

```{r functions}
ld <- function(ltraj) {
    if (!inherits(ltraj, "ltraj")) 
        stop("ltraj should be of class ltraj")
    inf <- infolocs(ltraj)
    df <- data.frame(
        x = unlist(lapply(ltraj, function(x) x$x)),
        y = unlist(lapply(ltraj, function(x) x$y)),
        date = unlist(lapply(ltraj, function(x) x$date)),
        dx = unlist(lapply(ltraj, function(x) x$dx)),
        dy = unlist(lapply(ltraj, function(x) x$dy)),
        dist = unlist(lapply(ltraj, function(x) x$dist)),
        dt = unlist(lapply(ltraj, function(x) x$dt)),
        R2n = unlist(lapply(ltraj, function(x) x$R2n)),
        abs.angle = unlist(lapply(ltraj, function(x) x$abs.angle)),
        rel.angle = unlist(lapply(ltraj, function(x) x$rel.angle)),
        id = rep(id(ltraj), sapply(ltraj, nrow)),
        burst = rep(burst(ltraj), sapply(ltraj, nrow)))
    class(df$date) <- c("POSIXct", "POSIXt")
    attr(df$date, "tzone") <- attr(ltraj[[1]]$date, "tzone")
    if (!is.null(inf)) {
        nc <- ncol(inf[[1]])
        infdf <- as.data.frame(matrix(nrow = nrow(df), ncol = nc))
        names(infdf) <- names(inf[[1]])
        for (i in 1:nc) infdf[[i]] <- unlist(lapply(inf, function(x) x[[i]]))
        df <- cbind(df, infdf)
    }
    return(df)
}
ltraj2sldf <- function(ltr, proj4string = CRS(as.character(NA))) {
    if (!inherits(ltr, "ltraj")) 
        stop("ltr should be of class ltraj")
    df <- ld(ltr)
    df <- subset(df, !is.na(dist))
    coords <- data.frame(df[, c("x", "y", "dx", "dy")], id = as.numeric(row.names(df)))
    res <- apply(coords, 1, function(dfi) Lines(Line(matrix(c(dfi["x"], 
        dfi["y"], dfi["x"] + dfi["dx"], dfi["y"] + dfi["dy"]), 
        ncol = 2, byrow = TRUE)), ID = format(dfi["id"], scientific = FALSE)))
    res <- SpatialLinesDataFrame(SpatialLines(res, proj4string = proj4string), 
        data = df)
    return(res)
}
```

I load the object and apply the `ltraj2sldf` function:

```{r fail}
load("tr.RData")
juvStp <- ltraj2sldf(trajjuv, proj4string = CRS("+init=epsg:32617"))
dim(juvStp)
```

Использование knitr("test.Rmd") завершается ошибкой:

label: fail
Quitting from lines 66-75 (test.Rmd) 
Error in SpatialLinesDataFrame(SpatialLines(res, proj4string = 
proj4string),  (from     <text>#32) : 
  row.names of data and Lines IDs do not match

Использование вызова непосредственно в консоли R после возникновения ошибки работает, как и ожидалось...

Проблема связана с тем, как format создает идентификатор (в вызове apply ltraj2sldf) непосредственно перед идентификатором 100 000: используя интерактивный вызов, R дает «99994», «99995», «99996», «99997», «99998». ", "99999", "100000"; использование вязальщика R дает «99994», «99995», «99996», «99997», «99998», «99999», «100000» с дополнительными пробелами в начале.

Есть ли причина такого поведения? Почему knitr должен вести себя иначе, чем прямой вызов в R? Я должен признать, что у меня с этим проблемы, так как я не могу его отладить (он работает в интерактивном сеансе)!

Любая подсказка будет высоко оценена. Я могу предоставить .RData, если это поможет (файл 4,5 мес), но меня больше всего интересует, почему происходит такая разница. Я безуспешно пытался придумать самовоспроизводимый пример, извините за это. Заранее спасибо за любой вклад!


После комментария baptiste, вот еще несколько подробностей о генерации идентификаторов. По сути, идентификатор генерируется в каждой строке фрейма данных с помощью вызова apply, который, в свою очередь, использует format следующим образом: format(dfi["id"], scientific = FALSE). Здесь столбец id — это просто ряд от 1 до количества строк (1:nrow(df)). scientific = FALSE просто для того, чтобы у меня не было таких результатов, как 1e+05 для 100000.

Основываясь на исследовании создания идентификаторов, проблема возникла только для тех, которые представлены в первом сообщении, то есть от 99995 до 99999, для которых добавлен начальный пробел. Этого не должно происходить с этим вызовом format, так как я не запрашивал конкретное количество цифр в выводе. Например:

> format(99994:99999, scientific = FALSE)
[1] "99994" "99995" "99996" "99997" "99998" "99999"

Однако, если идентификаторы генерируются фрагментами, может произойти следующее:

> format(99994:100000, scientific = FALSE)
[1] " 99994" " 99995" " 99996" " 99997" " 99998" " 99999" "100000"

Обратите внимание, что одно и то же обработанное по одному дает ожидаемый результат:

> for (i in 99994:100000) print(format(i, scientific = FALSE))
[1] "99994"
[1] "99995"
[1] "99996"
[1] "99997"
[1] "99998"
[1] "99999"
[1] "100000"

В конце концов, это точно так же, как если бы идентификаторы готовились не по одному (как я ожидал бы от apply вызова по строке), а в этом случае 6 за раз, и только когда близко к 1e+05.. , И, конечно, только при использовании Knitr, а не интерактивного или пакетного R.


Вот моя информация о сеансе:

> sessionInfo()
R version 3.0.1 (2013-05-16)
Platform: x86_64-pc-linux-gnu (64-bit)

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

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

other attached packages:
[1] knitr_1.2           adehabitatLT_0.3.12 CircStats_0.2-4    
[4] boot_1.3-9          MASS_7.3-27         adehabitatMA_0.3.6 
[7] ade4_1.5-2          sp_1.0-11           basr_0.5.3         

loaded via a namespace (and not attached):
[1] digest_0.6.3    evaluate_0.4.4  formatR_0.8     fortunes_1.5-0 
[5] grid_3.0.1      lattice_0.20-15 stringr_0.6.2   tools_3.0.1

person Mathieu Basille    schedule 25.07.2013    source источник
comment
длинный план: knitr может изменить некоторые параметры форматирования (например, количество значащих цифр), каким-то образом влияя на то, как генерируются идентификаторы.   -  person baptiste    schedule 25.07.2013
comment
Может быть. Но я до сих пор не вижу, какие параметры могут повлиять на этот вывод. Позвольте мне добавить больше подробностей о создании идентификаторов в моем первом сообщении... Посмотрите, будет ли это иметь больше смысла! Спасибо.   -  person Mathieu Basille    schedule 25.07.2013
comment
можете ли вы сделать минимальный пример только с этой проблемой формата? Я немного не понимаю о применении и заполнении пробелами. У вас в списке 10000 или нет?   -  person baptiste    schedule 26.07.2013
comment
Признаться, я тоже этого не понимаю! :) Но да, таблица состоит из › 100 000 строк, и сгенерированные таким образом идентификаторы тоже выше. Я уже пытался придумать минимальный воспроизводимый пример, пока безуспешно (по непонятной мне причине проблема исчезает, когда я слишком сильно уменьшаю этот пример). Я попробую еще раз и вернусь сюда.   -  person Mathieu Basille    schedule 26.07.2013
comment
в этом конкретном случае format() не является хорошим выбором, так как на результаты может повлиять ряд глобальных параметров; чтобы точно получить символьное представление ваших чисел, я бы рекомендовал вам просто использовать as.character(), тогда ничего необычного не произойдет. Кстати, я рад, что вы последовали моему предложению прийти в SO. Как вы видели, один-единственный автор (то есть я) гораздо менее полезен и отзывчив, чем тысячи экспертов здесь :)   -  person Yihui Xie    schedule 27.07.2013
comment
Действительно да, as.character вместо format решает проблему. Странно то, что я помню, как начал с as.character, а потом переключился на format по какой-то причине. Эта причина, которую я не помню, вероятно, уже недействительна, так как функция теперь работает так, как я хочу. Спасибо за ваш комментарий!   -  person Mathieu Basille    schedule 29.07.2013
comment
А, теперь я вспомнил, почему я использовал format, а не as.character (и я действительно упомянул об этом в своем первом посте): это просто для того, чтобы избежать научного форматирования, такого как "1e+05", которое является результатом as.character(100000) (и выдает мне ошибку, если это происходит в названиях строк). Таким образом, я буду придерживаться format(..., scientific = FALSE, trim = TRUE). R работает таинственным образом...   -  person Mathieu Basille    schedule 30.07.2013
comment
@basille извините, я ошибся насчет as.character(); спасибо за вашу поправку!   -  person Yihui Xie    schedule 30.07.2013


Ответы (3)


И Джефф, и Батист были действительно правы! Это проблема опции, связанная с аргументом digits. Мне удалось придумать работающий минимальный пример (например, в test.Rmd):

Simple reproducible example : df1 is a data frame of 110,000 rows,
with 2 random normal variables + an `id` variable which is a series
from 1 to the number of row. 

```{r example}
df1 <- data.frame(x = rnorm(110000), y = rnorm(110000), id = 1:110000)
```

From this, we create a `id2` variable using `format` and `scientific =
FALSE` to have results with all numbers instead of scientific
notations (e.g. 100,000 instead of 1e+05):

```{r example-continued}
df1$id2 <- apply(df1, 1, function(dfi) format(dfi["id"], scientific = FALSE))
df1$id2[99990:100010]
```

Он работает, как и ожидалось, используя R в интерактивном режиме, что приводит к:

 [1] "99990"  "99991"  "99992"  "99993"  "99994"  "99995"  "99996" 
 [8] "99997"  "99998"  "99999"  "100000" "100001" "100002" "100003"
[15] "100004" "100005" "100006" "100007" "100008" "100009" "100010"

Однако при использовании knit результаты совсем другие:

> library(knitr)
> knit("test.Rmd")

[...]

##  [1] "99990"  "99991"  "99992"  "99993"  "99994"  " 99995" " 99996"
##  [8] " 99997" " 99998" " 99999" "100000" "100001" "100002" "100003"
## [15] "100004" "100005" "100006" "100007" "100008" "100009" "100010"

Обратите внимание на дополнительные начальные пробелы после 99994. На самом деле разница возникает из-за опции digits, как справедливо предположил Джефф: R по умолчанию использует 7, а Knitr использует 4. Это различие влияет на вывод format, хотя я действительно не понимаю, что происходит здесь. R-стиль:

> options(digits = 7)
> format(99999, scientific = FALSE)
[1] "99999"

вязальный стиль:

> options(digits = 4)
> format(99999, scientific = FALSE)
[1] " 99999"

Но это должно влиять на все числа, а не только после 99994 (ну, если честно, я даже не понимаю, зачем вообще добавляются начальные пробелы):

> options(digits = 4)
> format(c(1:10, 99990:100000), scientific = FALSE)
 [1] "     1" "     2" "     3" "     4" "     5" "     6" "     7"
 [8] "     8" "     9" "    10" " 99990" " 99991" " 99992" " 99993"
[15] " 99994" " 99995" " 99996" " 99997" " 99998" " 99999" "100000"    

Отсюда я понятия не имею, кто виноват: knitr, apply или format? По крайней мере, я придумал обходной путь, используя аргумент trim = TRUE в format. Это не решает причину проблемы, но удаляет начальный пробел в результатах...

person Mathieu Basille    schedule 26.07.2013
comment
ну, этого я тоже не совсем понимаю; хотя, возможно, это где-то задокументировано - person Yihui Xie; 30.07.2013
comment
Я отправил сообщение в список R-help, давайте посмотрим, есть ли у кого-то еще идеи по этому поводу... stat.ethz.ch/pipermail/r-help/2013-July/357642.html - person Mathieu Basille; 30.07.2013

Я добавил комментарий к вашей проблеме knitr GitHub с этой информацией.

format() добавляет дополнительные пробелы, когда параметра digits недостаточно для отображения значения, но также указано scientific=FALSE. Knitr устанавливает digits в 4 блока кода, что приводит к описанному вами поведению:

options(digits=4)
format(99999, scientific=FALSE)

Производит:

[1] " 99999"

Пока:

options(digits=5)
format(99999, scientific=FALSE)

Производит:

[1] "99999"
person Jeff Johnston    schedule 26.07.2013
comment
Совершенно верно! Это заставило меня максимально сузить проблему, и options(digits = X) действительно является причиной проблемы! Смотрите мой ответ ниже. - person Mathieu Basille; 26.07.2013

Благодаря Алексею Вороне и Дункану Мердоку эта ошибка исправлена ​​в R-devel!

См.: https://bugs.r-project.org/bugzilla3/show_bug.cgi?id=15411

person Mathieu Basille    schedule 27.08.2013