The spectrum of my data set shows 3 periodic components in the time serie. I would like to substract the periodic components to keep the data without these periodicities.
It points out periodic events with the periodicity of (1/144 = daily), (1/72 = 1/2daily), and (1/6 = hourly).
My idea was to find out the Fourier components (mag and phase) of my dataset and to extract the Fourier components for these 3 specific frequencies and to create a new signal with is :
Data - PeriodicSignal_1h - PeriodicSignal_1/2day - PeriodicSignal_1day
I try with fft but I do not know how to extract the signal at these specific frequencies.
My dataset is complicated but I'm working on an example to understand the process. Here is the example :
samplingFrequency = 1000;
timeInterval = 1/samplingFrequency;
signalIndex = seq(0, 1, by=timeInterval);
N = 1000
a1 = 2;
a2 = 3;
f1 = 10;
f2 = 20;
signal1 = a1 * sin(2 * pi * f1 * signalIndex);
signal2 = a2 * sin(2 * pi * f2 * signalIndex);
inputSignal = signal1 + signal2;
Y <- fft(inputSignal)
mag <-sqrt(Re(Y)^2+Im(Y)^2)*2/length(inputSignal)
phase <-atan(Im(Y)/Re(Y)) Yr <- Re(Y) Yi <- Im(Y)
I'm trying to extract the mag and the phase of the signal with frequency f1. And I would like to generate a new signal with is :
ImputSignal - Signal_f1
I believe the following does what you are looking for ... I changed some of the variable names. At the bottom is the frequency selection that you asked about.
Set up the time and frequency parameters
samplingFrequency = 1000;
f_Hz = samplingFrequency
N = 1000
df_Hz = f_Hz / N
T = 1 / df_Hz
dt=T/N
t = dt*(seq(1,N)-1)
Generate a fake signal, no noise
a1 = 2;
a2 = 3;
f1 = 10;
f2 = 20;
signal1 = a1 * sin(2 * pi * f1 * t);
signal2 = a2 * sin(2 * pi * f2 * t);
inputSignal = signal1 + signal2;
Plot the fake signal
plot(t, signal1,type='l',col='green',ylim=c(-6,6))
lines(t, signal2,col='red')
lines(t, inputSignal,col='black')
Get the fft, and plot positive frequency portion
Y <- fft(inputSignal)
m <- floor(N/2)-1
posFreqIndices <- 2:(m+1)
negFreqIndices <- N:(m+3)
mag <-sqrt(Re(Y)^2+Im(Y)^2)*2/length(inputSignal)
phase <-atan(Im(Y)/Re(Y))
Yr <- Re(Y)
Yi <- Im(Y)
freq <- seq(df_Hz,f_Hz/2-df_Hz,df_Hz)
plot(freq,mag[posFreqIndices],type='l',xlab='Freq (Hz)', ylab='Magnitude',xlim=c(0,30))
# plot(freq,10*log10(mag[posFreqIndices]),type='l',xlab='Freq (Hz)', ylab='Magnitude (db)',xlim=c(0,30))
# plot(freq,phase[posFreqIndices]*180/pi,type='l',xlab='Freq (Hz)', ylab='Phase (deg)',xlim=c(0,30))
Identify the frequencies for the filtered signal based on amplitude
ampSelectIndices <- which(mag>1.9 & mag < 2.1)
Generate the filtered fft for the selected frequencies
YAmpSelect <- Y*0
YAmpSelect[ampSelectIndices] = Y[ampSelectIndices]
Calculate the inverse fft
yAmpSelect = Re(fft(YAmpSelect, inverse = TRUE))/length(YAmpSelect)
Plot the filtered signal
plot(t,yAmpSelect,t='l',xlab='t (sec)',ylab='Filtered for mag ~ 2')
Plot the original signal minus the filtered signal
plot(t,inputSignal-yAmpSelect,type='l')
The fft is calculated with frequencies folded. The following checks the unfolding process, this check works for real valued signals (not complex value time signals). The process is correct for complex valued time signals.
checkFreqWrapping = all.equal(mag[posFreqIndices], mag[negFreqIndices])
stopifnot(checkFreqWrapping)
Select fft values by frequency
freqSelectIndices_a <- which(9.95 < freq & freq < 10.05)
freqSelectIndices = union(posFreqIndices[freqSelectIndices_a],negFreqIndices[freqSelectIndices_a])
Create the fft for selected frequencies
YFreqSelect <- Y*0
YFreqSelect[freqSelectIndices ] = Y[freqSelectIndices ]
Calculate the time signal, plot it.
yFreqSelect = Re(fft(YFreqSelect, inverse = TRUE))/length(YFreqSelect)
plot(t,yFreqSelect,t='l',xlab='t (sec)',ylab='Filtered for mag ~ 2')
plot(t,inputSignal-yFreqSelect,type='l')
OK, I think that explains how to select fft values based on frequencies... Good luck...
Related
I have created a curve that shows for each level of spend (X) a unique output in revenue (Y).
The curve is defined by the following (monotone) function:
calculate_abc_revenue <- function(a, b, c, spend) {
res <- ifelse(
a/(1+b*(spend)^c) >= 0,
a/(1+b*(spend)^c),
0
)
return(res)
}
Where a, b and c are given parameters and should be treated as constant:
a0 <- 1303066.36937866
b0 <- 15560519.9999999
c0 <- -1.09001859302511
Now, if we define ROI as:
revenue <- calculate_abc_revenue(a = a0, b = b0, c = c0, spend)
ROI <- revenue/spend
How do I find the exact values of revenue and spend that make ROI max?
I currently use a spend vector of length n that helps me finding approximately the max ROI, but most of the times the result is not 100% exact as the real max ROI can fall between two points sent as input.
I would like to avoid to increase the length of the spend vector as it would increase the calculation time (and it wouldn't guarantee that the solution found is a global max anyway).
By setting ROI as a function, we can use optimize:
ROI <- function(spend) calculate_abc_revenue(a0, b0, c0, spend)/spend
optspend <- optimize(ROI, c(0, 1e12), maximum = TRUE)$maximum
c("optimal spend" = optspend, revenue = calculate_abc_revenue(a0, b0, c0, optspend))
#> optimal spend revenue
#> 435274.1 107613.0
Here is another approach. It uses unitroot applied to the derivative of the ROI-function f. (only valid for spend > 0)
From this function the derivative is build from which a function is created. This function is then fed into unitroot.
Plots in the upper row show ROI over a wide range of investment and its derivative. The lower row shows a zoomed version of those.
#| function related to investment (spend) ROI
f = expression(1 / spend * (a0 / (1 + b0 * spend^c0)))
#| get the derivative
ff_2 <- D(f, 'spend')
#| uniroot
ff_2 <- function(spend){}
body(ff_2) <- ff
res <- uniroot(ff_2, c(1e5, 1e6))
c("optimal spend" = res$root, revenue = calculate_abc_revenue(spend = res$root))
# optimal spend revenue
# 435274.1 107613.0
Original Data
a0 <- 1303066.36937866
b0 <- 15560519.9999999
c0 <- -1.09001859302511
#| Original function
calculate_abc_revenue <- function(a = a0,b = b0, c = c0,spend) {
res <- ifelse(
a/(1+b*(spend)^c) >= 0,
a/(1+b*(spend)^c),
0
)
return(res)
}
I am trying to simulate a realistic age structured model where all individuals could shift into the following age group at the end of the time step (and not age continuously at a given rate) using ODE from the deSolve package.
Considering for example a model with two states Susceptible (S) and Infectious (I), each state being divided in 4 age groups (S1, S2, S3, S4, and I1, I2, I3, I4), all individuals in S1 should go into S2 at the end of the time step, those in S2 should go into S3, and so on.
I tried to make this in two steps, the first by solving the ODE, the second by shifting individuals into the following age group at the end of the time step, but without success.
Below is one of my attempts :
library(deSolve)
times <- seq(from = 0, to = 100, by = 1)
n_agecat <- 4
#Initial number of individuals in each state
S_0 = c(999,rep(0,n_agecat-1))
I_0 = c(1,rep(0,n_agecat-1))
si_initial_state_values <- c(S = S_0,
I = I_0)
# Parameter values
si_parameters <- c(beta = 0.01) #contact rate assuming random mixing
si_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
n_agegroups <- 4
S <- state[1:n_agegroups]
I <- state[(n_agegroups+1):(2*n_agegroups)]
# Total population
N <- S+I
# Force of infection
lambda <- beta * I/N
# Solving the differential equations
dS <- -lambda * S
dI <- lambda * S
# Trying to shift all individuals into the following age group
S <- c(0,S[-n_agecat])
I <- c(0,I[-n_agecat])
return(list(c(dS, dI)))
})
}
output <- as.data.frame(ode(y = si_initial_state_values,
times = times,
func = si_model,
parms = si_parameters))
Any guidance will be much appreciated, thank you in advance!
I had a look at your model. Implementing the shift in an event function works, in principle, but the main model has still several problems:
die out: if the age groups are shifted per time step and the first element is just filled with zero, everything is shifted to the end within 4 time steps and the population dies out.
infection: in your case, the infected can only infect the same age group, so you need to summarize over the "age" groups before calculating lambda.
Finally, what is "age" group? Do you want the time since infection?
To sum up, there are several options: I would personally prefer a discrete model for such a simulation, i.e. difference equations, a age structured matrix model or an individual-based model.
If you want to keep it an ODE, I recommend to let the susceptible together as one state and to implement only the infected as stage structured.
Here a quick example, please check:
library(deSolve)
times <- seq(from = 0, to = 100, by = 1)
n_agegroups <- 14
n_agecat <- 14
# Initial number of individuals in each state
S_0 = c(999) # only one state
I_0 = c(1, rep(0,n_agecat-1)) # several stages
si_initial_state_values <- c(S = S_0,
I = I_0)
# Parameter values
si_parameters <- c(beta = 0.1) # set contact parameter to a higher value
si_model <- function(time, state, parameters) {
with(as.list(c(state, parameters)), {
S <- state[1]
I <- state[2:(n_agegroups + 1)]
# Total population
N <- S + sum(I)
# Force of infection
#lambda <- beta * I/N # old
lambda <- beta * sum(I) / N # NEW
# Solving the differential equations
dS <- -lambda * S
dI <- lambda * S
list(c(dS, c(dI, rep(0, n_agegroups-1))))
})
}
shift <- function(t, state, p) {
S <- state[1]
I <- state[2:(n_agegroups + 1)]
I <- c(0, I[-n_agecat])
c(S, I)
}
# output time steps (note: ode uses automatic simulation steps!)
times <- 1:200
# time step of events (i.e. shifting), not necessarily same as times
evt_times <- 1:200
output <- ode(y = si_initial_state_values,
times = times,
func = si_model,
parms = si_parameters,
events=list(func=shift, time=evt_times))
## default plot function
plot(output, ask=FALSE)
## plot totals
S <- output[,2]
I <- rowSums(output[, -(1:2)])
par(mfrow=c(1,2))
plot(times, S, type="l", ylim=c(0, max(S)))
lines(times, I, col="red", lwd=1)
## plot stage groups
matplot(times, output[, -(1:2)], col=rainbow(n=14), lty=1, type="l", ylab="S")
Note: This is just a technical demonstration, not a valid stage structured SIR model!
suppose I have a data like y and I fit a smooth function to this data with Fourier basis
y<- c(1,2,5,8,9,2,5)
x <- seq_along(y)
Fo <- create.fourier.basis(c(0, 7), 4)
precfd = smooth.basis(x,y,Fo)
plotfit.fd(y, x, precfd$fd)
precfd <- smooth.basis(x, y, Fo);coef(precfd)
the out put of last line gives me this:
const 411.1060285
sin1 -30.5584033
cos1 6.5740933
sin2 26.2855849
cos2 -26.0153965
I know what is the coefficient but what in const? in original formula there is no constant part as this link say:
http://lampx.tugraz.at/~hadley/num/ch3/3.3a.php
The first basis function in create.fourier.basis is a constant function to allow for a non-zero mean (intercept) in the data. From the documentation of the create.fourier.basis function:
The first basis function is the unit function with the value one everywhere. The next two are the sine/cosine pair with period defined in the argument period. The fourth and fifth are the sin/cosine series with period one half of period. And so forth. The number of basis functions is usually odd.
You can drop the first (unit) basis function in create.fourier.basis with the argument dropind = 1. Below some example code that illustrates which basis functions are used in create.fourier.basis. Note: the scaling of the basis functions depends on the period argument in create.fourier.basis.
Example 1: non-zero mean
library(fda)
## time sequence
tt <- seq(from = 0, to = 1, length = 100)
## basis functions
phi_0 <- 1
phi_1 <- function(t) sin(2 * pi * t) / sqrt(1 / 2)
phi_2 <- function(t) cos(2 * pi * t) / sqrt(1 / 2)
## signal
f1 <- 10 * phi_0 + 5 * phi_1(tt) - 5 * phi_2(tt)
## noise
eps <- rnorm(100)
## data
X1 <- f1 + eps
## create Fourier basis with intercept
four.basis1 <- create.fourier.basis(rangeval = range(tt), nbasis = 3)
## evaluate values basis functions
## eval.basis(tt, four.basis1)
## fit Fourier basis to data
four.fit1 <- smooth.basis(tt, X1, four.basis1)
coef(four.fit1)
Example 2: zero mean
## signal
f2 <- 5 * phi_1(tt) - 5 * phi_2(tt)
## data
X2 <- f2 + eps
## create Fourier basis without intercept
four.basis2 <- create.fourier.basis(rangeval = range(tt), nbasis = 3, dropind = 1)
## evaluate values basis functions
## eval.basis(tt, four.basis2)
## fit Fourier basis to data
four.fit2 <- smooth.basis(tt, X2, four.basis2)
coef(four.fit2)
I'm trying to visualize a signal and its frequency spectrum in Julia.
I found the FFTW package that provides the FFT and DSP for the frequencies.
Here is what I'm trying, with a sinusoidal signal:
using Plots
using FFTW
using DSP
# Number of points
N = 2^14 - 1
# Sample rate
fs = 1 / (1.1 * N)
# Start time
t0 = 0
tmax = t0 + N * fs
# time coordinate
t = [t0:fs:tmax;]
# signal
signal = sin.(2π * 60 * t) # sin (2π f t)
# Fourier Transform of it
F = fft(signal)
freqs = fftfreq(length(t), fs)
freqs = fftshift(freqs)
# plots
time_domain = plot(t, signal, title = "Signal")
freq_domain = plot(freqs, abs.(F), title = "Spectrum")
plot(time_domain, freq_domain, layout = 2)
savefig("Wave.pdf")
I expected to see a nice plot with a peak in the 60 Hz, but all I got was a weird result:
I'm ignoring the negative frequencies for now.
How should I do that in Julia?
What you call fs in your code is not your sampling rate but the inverse of it: the sampling period.
The function fftfreq takes the sampling rate as its second argument. Since what you give as the second argument is the sampling period, the frequencies returned by the function are incorrectly scaled by (1/(Ts^2)).
I renamed fs to Ts and changed the second argument to fftfreq to the sampling rate 1.0/Ts. I think you also need to shift the result of fft.
# Number of points
N = 2^14 - 1
# Sample period
Ts = 1 / (1.1 * N)
# Start time
t0 = 0
tmax = t0 + N * Ts
# time coordinate
t = t0:Ts:tmax
# signal
signal = sin.(2π * 60 .* t) # sin (2π f t)
# Fourier Transform of it
F = fft(signal) |> fftshift
freqs = fftfreq(length(t), 1.0/Ts) |> fftshift
# plots
time_domain = plot(t, signal, title = "Signal")
freq_domain = plot(freqs, abs.(F), title = "Spectrum", xlim=(-1000, +1000))
plot(time_domain, freq_domain, layout = 2)
savefig("Wave.pdf")
time <- 1:12
y <- c(0,0,0,0,0,0,0,12,34,69,100,100)
mdl <- gcFitModel(time,y,control = grofit.control(fit.opt = "m", model.type = "gompertz"))
I get my parameters from the above mdl to fit the gompertz equation
y <- A* exp(-exp(mu * e * (lambda - time)/mu + 1))
mu <- 36.162016
lambda <- 7.9800164
A <- 100
time <- 1:12
time here is in a step of 15 days. For e.g time = 1 implies mid-day of a 15 day period, time 2 implies mid day of the next 15 days period, time 3 implies implies the mid-day of the next 15 days period and so on.
I fitted the following function:
e <- exp(1)
y <- 100 * exp(-exp(mu * e * (lambda - time)/mu + 1))
plot(time,y)
The lambda controls the movement along the x-axis.
I am looking to modify this curve so that I get more data points by converting the ids into weeks i.e instead of mid-point of every 15 days, I want to get y for every 7 days. How can I do this?
Your code doesn't include any fitting routines, so I am assuming this is a question about plotting.
Here is an example of a plot with 100 points between time = 1 and time = 12.
time <- seq(1, 12, length.out = 100);
mu <- 34.55844
y <- 100 * exp(-exp(mu * e * (3 - time)/mu + 1))
plot(time,y)