I'm studying the book "Functional Data Analysis with R and MATLAB" using R and the R package fda
and I have a small problem replicating Figure 1.12. It's very easy to fix it (see below) but I don't understand the underlying issue.
A reprex of the problem:
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
Why is there an error when I call the generic deriv
but there is no error when I explicitly call the S3 method for fd
objects?
A few notes:
scripts
within the downloaded R package: system.file("scripts", package = "fda")
. In particular, I'm using the script named fdarm-ch01.R
.deriv.fd
instead of deriv
).It seems this is because the {fda} package does not register its deriv.fd()
S3 method as one might expect.
If you look at the NAMESPACE, you will find this line:
S3method(deriv, fd)
The above NAMESPACE line is registering fda::deriv.fd()
with the fda:::deriv()
generic because fda:::deriv()
is found before stats::deriv()
"as seen from the package" (see this blog post). However, fda:::deriv()
is not exported from the package.
The effect of this registration and lack of export is that the fda:::deriv()
generic will not show up on the search path via library(fda)
. Therefore calls to deriv()
as in your code use the stats::deriv()
generic which does not know about the fda::deriv.fd()
method.
If the fda:::deriv()
generic was exported, when you library(fda)
the fda::deriv()
generic would take precedence over the pre-existing stats::deriv()
generic, and R could then dispatch the call to fda::deriv.fd()
based on the class of the first argument according to S3 dispatch.
I am unsure if this is intended behavior or not on the part of the package authors. To me the presence of a fda:::deriv()
generic suggests the authors intended to export. As Ben suggests, you might consider contacting them for clarification.
There are a number of ways to change this behavior. Here are seven off the top of my head to illustrate the S3 registration and search path issues at hand:
fda::deriv.fd()
S3 Method Directlyfda:::deriv()
Generic Directlyfda::deriv.fd()
with stats::deriv()
fda:::deriv()
Genericfda:::derive()
Genericfda::deriv.fd()
with stats::deriv()
fda::deriv.fd()
S3 Method DirectlyBypass the S3 issues altogether and call fda::deriv.fd()
directly as you did.
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 DirectlyYou could call fda:::deriv()
directly:
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
You could make copies of fda:::deriv()
and fda::deriv.fd()
in global:
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()
with stats::deriv()
You could use registerS3method("deriv", "fd", deriv.fd)
to register fda::deriv.fd()
with 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()
GenericYou could fork the source and add export(deriv)
to the namespace file.
I have done so for you here.
# 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()
GenericYou could fork the source and remove the fda::deriv()
generic from the package.
I have done so for you here.
# 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()
with stats::deriv()
You could fork the source and change S3method(deriv, fd)
to S3method(stats::deriv, fd)
in the NAMESPACE file.
I have done so for you here.
# 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