Я изучаю книгу «Функциональный анализ данных с помощью R и MATLAB» , используя R и пакет R fda, и у меня возникла небольшая проблема с воспроизведением рисунка 1.12. Это очень легко исправить (см. ниже), но я не понимаю основную проблему.
Репрекс проблемы:
library(fda)
#> Loading required package: splines
#> Loading required package: fds
#> Loading required package: rainbow
#> Loading required package: MASS
#> Loading required package: pcaPP
#> Loading required package: RCurl
#> Loading required package: deSolve
#>
#> Attaching package: 'fda'
#> The following object is masked from 'package:graphics':
#>
#> matplot
# Create an fd object represeting the weather in 4 canadian stations
fig1.11Stns = c('Montreal', 'Edmonton', 'Pr. Rupert', 'Resolute')
fig1.11Temp = CanadianWeather$dailyAv[
,
fig1.11Stns,
'Temperature.C'
]
Temp.fourier = create.fourier.basis(c(0, 365), 13)
fig1.11Temp.fd = Data2fd(day.5, fig1.11Temp, Temp.fourier)
class(fig1.11Temp.fd)
#> [1] "fd"
# Define a linear differential operator
yearRng = c(0,365)
Lcoef = matrix(c(0,(2*pi/365)^2,0),1,3)
harmaccelLfd = vec2Lfd(Lcoef, yearRng)
# Compute the derivative according to the linear differential operator
deriv(fig1.11Temp.fd, harmaccelLfd) # fails
#> Error in deriv.default(fig1.11Temp.fd, harmaccelLfd): invalid variable names
# However, when I explicitly call the S3 method, everything works fine
deriv.fd(fig1.11Temp.fd, harmaccelLfd) |> plot()
#> [1] "done"
Created on 2024-08-26 with reprex v2.0.2
Почему возникает ошибка, когда я вызываю общий deriv
, но нет ошибки, когда я явно вызываю метод S3 для fd
объектов?
Несколько примечаний:
scripts
в загруженном пакете R: system.file("scripts", package = "fda")
. В частности, я использую скрипт fdarm-ch01.R
.deriv.fd
вместо deriv
).Спасибо @I_O за ваш комментарий. Однако я до сих пор не понимаю, почему stats::deriv
не отправляет fda::deriv.fd
Агила, у меня нет глубокого понимания области применения R, так что, возможно, кто-то более опытный сможет помочь. AFAIK, это происходит так: R получает звонок на deriv
. {stat}, как один из «привилегированных» пакетов, принимает вызов, но все равно не получает derive.default
от {fda} (разверните fda:::
, тройное двоеточие!, с помощью табуляции, чтобы проверить отсутствие такового). Итак, мы возвращаемся к тому значению по умолчанию, которое может найти {stat}, которое является его собственным. Кстати, спасибо FDA за подсказку, звучит очень интересно.
Похоже, это связано с тем, что пакет {fda} не регистрирует свой метод deriv.fd()
S3, как можно было бы ожидать.
Если вы посмотрите на ПРОСТРАНСТВО ИМЕН, вы найдете следующую строку:
S3method(deriv, fd)
Приведенная выше строка NAMESPACE регистрируется fda::deriv.fd()
с общим fda:::deriv()
, поскольку fda:::deriv()
обнаруживается раньше stats::deriv()
«как видно из пакета» (см. эту запись в блоге). Однако fda:::deriv()
не экспортируется из пакета.
Результатом этой регистрации и отсутствия экспорта является то, что общий fda:::deriv()
не будет отображаться в пути поиска через library(fda)
. Поэтому вызовы deriv()
, как в вашем коде, используют общий stats::deriv()
, который не знает о методе fda::deriv.fd()
.
Если общий fda:::deriv()
был экспортирован, когда вы library(fda)
универсальный fda::deriv()
будет иметь приоритет над ранее существовавшим stats::deriv()
универсальным, и R сможет затем отправить вызов fda::deriv.fd()
на основе класса первого аргумента в соответствии с диспетчеризацией S3.
Я не уверен, является ли это намеренным поведением авторов пакетов или нет. На мой взгляд, наличие дженерика fda:::deriv()
предполагает, что авторы намеревались экспортировать. Как предлагает Бен, вы можете связаться с ними для получения разъяснений.
Есть несколько способов изменить это поведение. Вот семь из тех, что пришли мне на ум, чтобы проиллюстрировать имеющиеся проблемы с регистрацией S3 и путями поиска:
fda::deriv.fd()
Метод S3 напрямуюfda:::deriv()
Дженерик напрямуюfda::deriv.fd()
с помощью stats::deriv()
fda:::deriv()
Genericfda:::derive()
Универсальныйfda::deriv.fd()
с помощью stats::deriv()
fda::deriv.fd()
метод S3 напрямуюПолностью обойдите проблемы S3 и позвоните fda::deriv.fd()
напрямую, как вы это сделали.
library(fda)
# Create an fd object represeting the weather in 4 canadian stations
fig1.11Stns = c('Montreal', 'Edmonton', 'Pr. Rupert', 'Resolute')
fig1.11Temp = CanadianWeather$dailyAv[
,
fig1.11Stns,
'Temperature.C'
]
Temp.fourier = create.fourier.basis(c(0, 365), 13)
fig1.11Temp.fd = Data2fd(day.5, fig1.11Temp, Temp.fourier)
class(fig1.11Temp.fd)
#> [1] "fd"
# Define a linear differential operator
yearRng = c(0,365)
Lcoef = matrix(c(0,(2*pi/365)^2,0),1,3)
harmaccelLfd = vec2Lfd(Lcoef, yearRng)
# Compute the derivative according to the linear differential operator
deriv(fig1.11Temp.fd, harmaccelLfd) # fails
#> Error in deriv.default(fig1.11Temp.fd, harmaccelLfd): invalid variable names
# call fda::deriv.fd() directly
deriv.fd(fig1.11Temp.fd, harmaccelLfd) |> plot()
#> [1] "done"
Created on 2024-08-28 with reprex v2.1.1
fda:::deriv()
Generic напрямуюВы можете вызвать fda:::deriv()
напрямую:
library(fda)
# Create an fd object represeting the weather in 4 canadian stations
fig1.11Stns = c('Montreal', 'Edmonton', 'Pr. Rupert', 'Resolute')
fig1.11Temp = CanadianWeather$dailyAv[
,
fig1.11Stns,
'Temperature.C'
]
Temp.fourier = create.fourier.basis(c(0, 365), 13)
fig1.11Temp.fd = Data2fd(day.5, fig1.11Temp, Temp.fourier)
class(fig1.11Temp.fd)
#> [1] "fd"
# Define a linear differential operator
yearRng = c(0,365)
Lcoef = matrix(c(0,(2*pi/365)^2,0),1,3)
harmaccelLfd = vec2Lfd(Lcoef, yearRng)
# Compute the derivative according to the linear differential operator
deriv(fig1.11Temp.fd, harmaccelLfd) # fails
#> Error in deriv.default(fig1.11Temp.fd, harmaccelLfd): invalid variable names
# call fda:::deriv() generic
# for which fda::deriv.fd() has been registered via NAMESPACE
fda:::deriv(fig1.11Temp.fd, harmaccelLfd) |> plot()
#> [1] "done"
Created on 2024-08-28 with reprex v2.1.1
Вы можете сделать копии fda:::deriv()
и fda::deriv.fd()
в глобальном масштабе:
library(fda)
# Create an fd object represeting the weather in 4 canadian stations
fig1.11Stns = c('Montreal', 'Edmonton', 'Pr. Rupert', 'Resolute')
fig1.11Temp = CanadianWeather$dailyAv[
,
fig1.11Stns,
'Temperature.C'
]
Temp.fourier = create.fourier.basis(c(0, 365), 13)
fig1.11Temp.fd = Data2fd(day.5, fig1.11Temp, Temp.fourier)
class(fig1.11Temp.fd)
#> [1] "fd"
# Define a linear differential operator
yearRng = c(0,365)
Lcoef = matrix(c(0,(2*pi/365)^2,0),1,3)
harmaccelLfd = vec2Lfd(Lcoef, yearRng)
# Compute the derivative according to the linear differential operator
deriv(fig1.11Temp.fd, harmaccelLfd) # fails
#> Error in deriv.default(fig1.11Temp.fd, harmaccelLfd): invalid variable names
# place a copy of fda:::deriv() generic in global
deriv <- fda:::deriv
# and a copy of fda::deriv.fd() so that the new generic can find the fd method
deriv.fd <- fda::deriv.fd
# call your global deriv() generic
# - the global deriv() generic is found before stats::deriv() generic due to being defined in global environment
# - the global deriv() generic can find deriv.fd() due to it also being defined in the global environment
deriv(fig1.11Temp.fd, harmaccelLfd) |> plot()
#> [1] "done"
Created on 2024-08-28 with reprex v2.1.1
fda::deriv.fd()
с помощью stats::deriv()
Вы можете использовать registerS3method("deriv", "fd", deriv.fd)
для регистрации fda::deriv.fd()
на stats::deriv()
:
library(fda)
# Create an fd object represeting the weather in 4 canadian stations
fig1.11Stns = c('Montreal', 'Edmonton', 'Pr. Rupert', 'Resolute')
fig1.11Temp = CanadianWeather$dailyAv[
,
fig1.11Stns,
'Temperature.C'
]
Temp.fourier = create.fourier.basis(c(0, 365), 13)
fig1.11Temp.fd = Data2fd(day.5, fig1.11Temp, Temp.fourier)
class(fig1.11Temp.fd)
#> [1] "fd"
# Define a linear differential operator
yearRng = c(0,365)
Lcoef = matrix(c(0,(2*pi/365)^2,0),1,3)
harmaccelLfd = vec2Lfd(Lcoef, yearRng)
# Compute the derivative according to the linear differential operator
deriv(fig1.11Temp.fd, harmaccelLfd) # fails
#> Error in deriv.default(fig1.11Temp.fd, harmaccelLfd): invalid variable names
# register fda::deriv.fd() S3 method with stats::deriv() generic
registerS3method("deriv", "fd", deriv.fd)
# call stats::deriv() generic
# - stats::deriv() generic is found before fda:::deriv() generic because fda:::deriv() is not exported and therefore not on search path
# - fda::deriv.fd() is registered to stats::deriv() via registerS3method()
deriv(fig1.11Temp.fd, harmaccelLfd) |> plot()
#> [1] "done"
Created on 2024-08-28 with reprex v2.1.1
fda:::deriv()
GenericВы можете разветвить источник и добавить export(deriv)
в файл пространства имен.
Я сделал это для вас здесь.
# install patched version
remotes::install_github("the-mad-statter/fda@export-deriv-generic")
library(fda)
# Create an fd object represeting the weather in 4 canadian stations
fig1.11Stns = c('Montreal', 'Edmonton', 'Pr. Rupert', 'Resolute')
fig1.11Temp = CanadianWeather$dailyAv[
,
fig1.11Stns,
'Temperature.C'
]
Temp.fourier = create.fourier.basis(c(0, 365), 13)
fig1.11Temp.fd = Data2fd(day.5, fig1.11Temp, Temp.fourier)
class(fig1.11Temp.fd)
#> [1] "fd"
# Define a linear differential operator
yearRng = c(0,365)
Lcoef = matrix(c(0,(2*pi/365)^2,0),1,3)
harmaccelLfd = vec2Lfd(Lcoef, yearRng)
# call fda::deriv() generic
# - fda::deriv() generic is found before stats::deriv() generic because fda::deriv() generic is now exported via NAMESPACE and therefore on the search path before stats::deriv() generic due to library(fda)
# - fda::deriv.fd() is registered to fda::deriv() via NAMESPACE
deriv(fig1.11Temp.fd, harmaccelLfd) |> plot()
#> [1] "done"
Created on 2024-08-28 with reprex v2.1.1
fda:::derive()
УниверсальныйВы можете форкнуть исходный код и удалить общий fda::deriv()
из пакета.
Я сделал это для вас здесь.
# install patched version
remotes::install_github("the-mad-statter/fda@remove-generic")
library(fda)
# Create an fd object represeting the weather in 4 canadian stations
fig1.11Stns = c('Montreal', 'Edmonton', 'Pr. Rupert', 'Resolute')
fig1.11Temp = CanadianWeather$dailyAv[
,
fig1.11Stns,
'Temperature.C'
]
Temp.fourier = create.fourier.basis(c(0, 365), 13)
fig1.11Temp.fd = Data2fd(day.5, fig1.11Temp, Temp.fourier)
class(fig1.11Temp.fd)
#> [1] "fd"
# Define a linear differential operator
yearRng = c(0,365)
Lcoef = matrix(c(0,(2*pi/365)^2,0),1,3)
harmaccelLfd = vec2Lfd(Lcoef, yearRng)
# call stats::deriv() generic
# - fda:::deriv() has been removed from package
# - fda::deriv.fd() is registered to stats::deriv() via NAMESPACE
deriv(fig1.11Temp.fd, harmaccelLfd) |> plot()
#> [1] "done"
Created on 2024-08-28 with reprex v2.1.1
fda::deriv.fd()
с помощью stats::deriv()
Вы можете разветвить источник и изменить S3method(deriv, fd)
на S3method(stats::deriv, fd)
в файле NAMESPACE.
Я сделал это для вас здесь.
# install patched version
remotes::install_github("the-mad-statter/fda@register-with-stats")
library(fda)
# Create an fd object represeting the weather in 4 canadian stations
fig1.11Stns = c('Montreal', 'Edmonton', 'Pr. Rupert', 'Resolute')
fig1.11Temp = CanadianWeather$dailyAv[
,
fig1.11Stns,
'Temperature.C'
]
Temp.fourier = create.fourier.basis(c(0, 365), 13)
fig1.11Temp.fd = Data2fd(day.5, fig1.11Temp, Temp.fourier)
class(fig1.11Temp.fd)
#> [1] "fd"
# Define a linear differential operator
yearRng = c(0,365)
Lcoef = matrix(c(0,(2*pi/365)^2,0),1,3)
harmaccelLfd = vec2Lfd(Lcoef, yearRng)
# call stats::deriv() generic
# - fda:::deriv() is not exported and therefore not on search path
# - fda::deriv.fd() is registered to stats::deriv() via NAMESPACE
deriv(fig1.11Temp.fd, harmaccelLfd) |> plot()
#> [1] "done"
Created on 2024-08-28 with reprex v2.1.1
Согласованный. Хотя не помешало бы написать об этом мейнтейнеру по электронной почте...
Большое спасибо! Не могли бы вы добавить немного больше подробностей, объясняющих, почему (в настоящее время) неэкспортированная функция fda::deriv
мешает обычной отправке S3? Мое (вероятно, неправильное) понимание состоит в том, что когда я вызываю deriv()
для объектов fd, я на самом деле вызываю stats::deriv
(который является универсальным S3), и это должно отправляться в fda::deriv.fd
(который экспортируется и метод S3 правильно зарегистрирован). Почему мои рассуждения ошибочны? В любом случае я свяжусь с сопровождающим, как только выясню это!
Я бы сказал, что fda::deriv.fd()
не зарегистрирован должным образом. По крайней мере, он не зарегистрирован, как вы ожидаете. В настоящее время fda::deriv.fd()
регистрируется в fda:::deriv()
через файл NAMESPACE, но последний не экспортируется из пакета. Следовательно, в вашем исходном коде, когда вы запускаете deriv(fig1.11Temp.fd, harmaccelLfd)
, вы действительно вызываете stats::deriv()
, но этот дженерик не знает о методе fda::deriv.fd()
. По сути, здесь задействованы две общие функции, но вы можете легко получить доступ только к одной из них. Пожалуйста, ознакомьтесь с моими изменениями для дальнейших разъяснений.
Спасибо, совершенно ясно! Если возможно, еще пара вопросов (при необходимости я также могу открыть новый пост): 1) Есть ли какая-либо польза от переопределения общего deriv
S3 в пакете? Потому что, по моему мнению, решение №6 является наиболее разумным. 2) Когда я загружаю текущую версию CRAN fda
и запускаю methods("deriv")
, я вижу, что deriv.fd
находится среди выходных данных. Правильно ли это, учитывая соображения, перечисленные в вашем ответе? Или это ошибка в methods()
?
Вот github этого пакета: https://github.com/cran/fda/blob/master/R/deriv.fd.R А также дополнительную информацию можно найти на странице 79: https://cran.r-project.org/web/packages/fda/fda.pdf
Я только что проверил с их github.
deriv <- function(expr, ...) UseMethod('deriv')
deriv.fd <- function(expr, Lfdobj=int2Lfd(1), ...)`
Они использовали deriv() в качестве интерфейса для deriv.fd(). Вы получаете ошибку недопустимых имен переменных, поскольку производная ожидает выражение и другие аргументы. Но когда вы используете его как производный. Он не соответствует истинным параметрам. Итак, если мы просто пропустим deriv и вместо него используем deriv.fd вот так
deriv.fd(fig1.11Temp.fd, harmaccelLfd)
Это будет работать так. Если вы хотите обернуть его в deriv, просто перезапишите его как функцию, которая использует deriv с deriv.fd, а затем
Вот окончательное решение:
library(fda)
#> Loading required package: splines
#> Loading required package: fds
#> Loading required package: rainbow
#> Loading required package: MASS
#> Loading required package: pcaPP
#> Loading required package: RCurl
#> Loading required package: deSolve
#>
#> Attaching package: 'fda'
#> The following object is masked from 'package:graphics':
#>
#> matplot
# Create an fd object represeting the weather in 4 canadian stations
fig1.11Stns = c('Montreal', 'Edmonton', 'Pr. Rupert', 'Resolute')
fig1.11Temp = CanadianWeather$dailyAv[
,
fig1.11Stns,
'Temperature.C'
]
deriv <- function(expr, variable) {
return(deriv.fd(expr,variable))
}
Temp.fourier = create.fourier.basis(c(0, 365), 13)
fig1.11Temp.fd = Data2fd(day.5, fig1.11Temp, Temp.fourier)
class(fig1.11Temp.fd)
#> [1] "fd"
# Define a linear differential operator
yearRng = c(0,365)
Lcoef = matrix(c(0,(2*pi/365)^2,0),1,3)
harmaccelLfd = vec2Lfd(Lcoef, yearRng)
# Compute the derivative according to the linear differential operator
deriv(fig1.11Temp.fd, harmaccelLfd) # fails
#> Error in deriv.default(fig1.11Temp.fd, harmaccelLfd): invalid variable names
# However, when I explicitly call the S3 method, everything works fine
deriv.fd(fig1.11Temp.fd, harmaccelLfd) |> plot()
Привет и спасибо за ваш ответ! Я знаю, что могу использовать deriv.fd вместо вызова deriv, но, по моему мнению, это неудовлетворительно в рамках S3, и я хотел бы понять, почему не удается выполнить отправку S3.
Этот репозиторий GitHub полезен лишь незначительно; это не репозиторий, созданный сопровождающими пакетов. Вместо этого это зеркало кода пакета, доступного в CRAN. (Поэтому публикация проблем, запросов на включение и т. д. в этом репозитории будет бесполезна.)
Насколько я понимаю, R автоматически загружает пакет {stats}, среди прочего, при запуске. Затем {stats} регистрируется в качестве обработчика вызовов
deriv
. Несмотря на то, что {fda} имеет внутреннее значение по умолчанию (проверка с помощьюfda:::deriv
), это значение по умолчанию будет «только»UseMethod
для отправки в то, что считается предшествующей областью для вызововderiv
, где {stat} берет верх (см. пространство имен, показанноеfind("deriv")
) .