Проблема с вызовом функции deriv для объектов fd

Я изучаю книгу «Функциональный анализ данных с помощью 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()

Проблема с вызовом функции deriv для объектов fd

#> [1] "done"

Created on 2024-08-26 with reprex v2.0.2

Почему возникает ошибка, когда я вызываю общий deriv, но нет ошибки, когда я явно вызываю метод S3 для fd объектов?

Несколько примечаний:

  1. код (до последней строки) взят из сопутствующих сценариев книги, которые доступны в папке с именем scripts в загруженном пакете R: system.file("scripts", package = "fda"). В частности, я использую скрипт fdarm-ch01.R.
  2. Я вручную добавил последнюю строку (т. е. deriv.fd вместо deriv).

Насколько я понимаю, R автоматически загружает пакет {stats}, среди прочего, при запуске. Затем {stats} регистрируется в качестве обработчика вызовов deriv. Несмотря на то, что {fda} имеет внутреннее значение по умолчанию (проверка с помощью fda:::deriv), это значение по умолчанию будет «только» UseMethod для отправки в то, что считается предшествующей областью для вызовов deriv, где {stat} берет верх (см. пространство имен, показанное find("deriv")) .

I_O 26.08.2024 12:50

Спасибо @I_O за ваш комментарий. Однако я до сих пор не понимаю, почему stats::deriv не отправляет fda::deriv.fd

agila 26.08.2024 14:21

Агила, у меня нет глубокого понимания области применения R, так что, возможно, кто-то более опытный сможет помочь. AFAIK, это происходит так: R получает звонок на deriv. {stat}, как один из «привилегированных» пакетов, принимает вызов, но все равно не получает derive.default от {fda} (разверните fda:::, тройное двоеточие!, с помощью табуляции, чтобы проверить отсутствие такового). Итак, мы возвращаемся к тому значению по умолчанию, которое может найти {stat}, которое является его собственным. Кстати, спасибо FDA за подсказку, звучит очень интересно.

I_O 26.08.2024 16:53
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
3
112
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Похоже, это связано с тем, что пакет {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 и путями поиска:

  1. Позвоните fda::deriv.fd() Метод S3 напрямую
  2. Звоните fda:::deriv() Дженерик напрямую
  3. Копии в глобальной среде
  4. Зарегистрируйтесь fda::deriv.fd() с помощью stats::deriv()
  5. Форк и экспорт fda:::deriv() Generic
  6. Вилка и удаление fda:::derive() Универсальный
  7. Форкните и зарегистрируйтесь fda::deriv.fd() с помощью stats::deriv()

1. Вызовите 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

2. Позвоните 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

3. Копии в глобальной среде

Вы можете сделать копии 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

4. Зарегистрируйтесь 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

5. Форк и экспорт 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

6. Вилка и удаление 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

7. Форкните и зарегистрируйтесь 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

Согласованный. Хотя не помешало бы написать об этом мейнтейнеру по электронной почте...

Ben Bolker 28.08.2024 18:29

Большое спасибо! Не могли бы вы добавить немного больше подробностей, объясняющих, почему (в настоящее время) неэкспортированная функция fda::deriv мешает обычной отправке S3? Мое (вероятно, неправильное) понимание состоит в том, что когда я вызываю deriv() для объектов fd, я на самом деле вызываю stats::deriv (который является универсальным S3), и это должно отправляться в fda::deriv.fd (который экспортируется и метод S3 правильно зарегистрирован). Почему мои рассуждения ошибочны? В любом случае я свяжусь с сопровождающим, как только выясню это!

agila 28.08.2024 19:45

Я бы сказал, что fda::deriv.fd() не зарегистрирован должным образом. По крайней мере, он не зарегистрирован, как вы ожидаете. В настоящее время fda::deriv.fd() регистрируется в fda:::deriv() через файл NAMESPACE, но последний не экспортируется из пакета. Следовательно, в вашем исходном коде, когда вы запускаете deriv(fig1.11Temp.fd, harmaccelLfd), вы действительно вызываете stats::deriv(), но этот дженерик не знает о методе fda::deriv.fd(). По сути, здесь задействованы две общие функции, но вы можете легко получить доступ только к одной из них. Пожалуйста, ознакомьтесь с моими изменениями для дальнейших разъяснений.

the-mad-statter 29.08.2024 00:10

Спасибо, совершенно ясно! Если возможно, еще пара вопросов (при необходимости я также могу открыть новый пост): 1) Есть ли какая-либо польза от переопределения общего deriv S3 в пакете? Потому что, по моему мнению, решение №6 является наиболее разумным. 2) Когда я загружаю текущую версию CRAN fda и запускаю methods("deriv"), я вижу, что deriv.fd находится среди выходных данных. Правильно ли это, учитывая соображения, перечисленные в вашем ответе? Или это ошибка в methods()?

agila 29.08.2024 15:29

Вот 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.

agila 28.08.2024 19:48

Этот репозиторий GitHub полезен лишь незначительно; это не репозиторий, созданный сопровождающими пакетов. Вместо этого это зеркало кода пакета, доступного в CRAN. (Поэтому публикация проблем, запросов на включение и т. д. в этом репозитории будет бесполезна.)

Ben Bolker 28.08.2024 21:11
Кажется, это репозиторий исходников.
the-mad-statter 28.08.2024 21:17

Другие вопросы по теме