Skip to content

Commit 97a5550

Browse files
committed
made fakedata generation better
1 parent 860a21c commit 97a5550

File tree

4 files changed

+34
-71
lines changed

4 files changed

+34
-71
lines changed

pypillometry/baseline.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ def prominence_to_lambda(w, lam_min=1, lam_max=100):
295295
fname="stan/baseline_model_asym_laplac.stan"
296296
fpath=os.path.join(os.path.split(__file__)[0], fname)
297297
sm = cmdstanpy.CmdStanModel(stan_file=fpath)
298-
sm.compile()
298+
#sm.compile()
299299
#sm = StanModel_cache(stan_code_baseline_model_asym_laplac)
300300

301301
## put the data for the model together
@@ -314,7 +314,7 @@ def prominence_to_lambda(w, lam_min=1, lam_max=100):
314314
## variational optimization
315315
vprint(10, "Optimizing Stan model")
316316
opt=sm.variational(data=data)
317-
vbc=opt.stan_variable("coef")
317+
vbc=opt.stan_variable("coef")#, mean=True)
318318
meansigvb=np.dot(B, vbc)
319319
vprint(10, "Done optimizing Stan model")
320320

@@ -357,7 +357,7 @@ def prominence_to_lambda(w, lam_min=1, lam_max=100):
357357
## variational optimization
358358
vprint(10, "2nd Optimizing Stan model")
359359
opt=sm.variational(data=data2)
360-
vbc2=opt.stan_variable("coef")
360+
vbc2=opt.stan_variable("coef")#, mean=True)
361361
meansigvb2=np.dot(B2, vbc2)
362362
vprint(10, "Done 2nd Optimizing Stan model")
363363

pypillometry/fakedata.py

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
from .pupil import *
1111

1212
def generate_pupil_data(event_onsets, fs=1000, pad=5000, baseline_lowpass=0.2,
13+
scale_signal=(0,1), scale_evoked=(0.2,0.05),
1314
evoked_response_perc=0.02, response_fluct_sd=1,
1415
prf_npar=(10.35,0), prf_tmax=(917.0,0),
15-
prop_spurious_events=0.2, noise_amp=0.0005):
16+
num_spurious_events=0, noise_amp=0.01):
1617
"""
1718
Generate artificial pupil data as a sum of slow baseline-fluctuations
1819
on which event-evoked responses are "riding".
@@ -30,16 +31,12 @@ def generate_pupil_data(event_onsets, fs=1000, pad=5000, baseline_lowpass=0.2,
3031
baseline_lowpass: float
3132
cutoff for the lowpass-filter that defines the baseline
3233
(highest allowed frequency in the baseline fluctuations)
33-
34-
evoked_response_perc: float
35-
amplitude of the pupil-response as proportion of the baseline
36-
37-
response_fluct_sd: float
38-
How much do the amplitudes of the individual events fluctuate?
39-
This is determined by drawing each individual pupil-response to
40-
a single event from a (positive) normal distribution with mean as determined
41-
by `evoked_response_perc` and sd `response_fluct_sd` (in units of
42-
`evoked_response_perc`).
34+
scale_signal: (float,float)
35+
scale of the final signal (mean and SD); default is (0,1), i.e.,
36+
Z-scored data
37+
scale_evoked: (float,float)
38+
amplitude of the pupil-response (mean and SD) in Z-score units;
39+
responses<0 will be set to zero (truncated)
4340
prf_npar: tuple (float,float)
4441
(mean,std) of the npar parameter from :py:func:`pypillometry.pupil.pupil_kernel()`.
4542
If the std is exactly zero, then the mean is used for all pupil-responses.
@@ -48,13 +45,12 @@ def generate_pupil_data(event_onsets, fs=1000, pad=5000, baseline_lowpass=0.2,
4845
(mean,std) of the tmax parameter from :py:func:`pypillometry.pupil.pupil_kernel()`.
4946
If the std is exactly zero, then the mean is used for all pupil-responses.
5047
If the std is positive, tmax is taken i.i.d. from ~ normal(mean,std) for each event.
51-
prop_spurious_events: float
52-
Add random events to the pupil signal. `prop_spurious_events` is expressed
53-
as proportion of the number of real events.
54-
48+
num_spurious_events: float
49+
Add random events to the pupil signal. These are placed at random locations
50+
throughout the dataset.
5551
noise_amp: float
5652
Amplitude of random gaussian noise that sits on top of the simulated signal.
57-
Expressed in units of mean baseline pupil diameter.
53+
Expressed in SD-units before scaling up the signal
5854
5955
6056
Returns
@@ -96,14 +92,15 @@ def generate_pupil_data(event_onsets, fs=1000, pad=5000, baseline_lowpass=0.2,
9692
# create baseline-signal
9793
slack=int(0.50*n) # add slack to avoid edge effects of the filter
9894
x0=butter_lowpass_filter(np.random.rand(n+slack), baseline_lowpass, fs, 2)[slack:(n+slack)]
99-
x0=x0*1000+5000 # scale it up to a scale as usually obtained from eyetracker
100-
95+
x0 = (x0-np.mean(x0))/np.std(x0) # Z-score
10196

10297
### real events regressor
10398
## scaling
10499
event_ix=(np.array(event_onsets)/1000.*fs).astype(int)
105100
#a, b = (myclip_a - my_mean) / my_std, (myclip_b - my_mean) / my_std
106-
delta_weights=stats.truncnorm.rvs(-1/response_fluct_sd,np.inf, loc=1, scale=response_fluct_sd, size=event_ix.size)
101+
a = (0-scale_evoked[0])/scale_evoked[1]
102+
b = np.inf
103+
delta_weights=stats.truncnorm.rvs(a,b, loc=scale_evoked[0], scale=scale_evoked[1], size=event_ix.size)
107104
x1=np.zeros_like(sy)
108105

109106
for i,ev in enumerate(event_onsets):
@@ -113,9 +110,9 @@ def generate_pupil_data(event_onsets, fs=1000, pad=5000, baseline_lowpass=0.2,
113110

114111
## spurious events regressor
115112

116-
sp_event_ix=np.random.randint(low=0,high=np.ceil((T-maxdur-pad)/1000.*fs),size=int( nevents*prop_spurious_events ))
113+
sp_event_ix=np.random.randint(low=0,high=np.ceil((T-maxdur-pad)/1000.*fs),size=int( num_spurious_events ))
117114
sp_events=tx[ sp_event_ix ]
118-
n_sp_events=sp_events.size
115+
n_sp_events=num_spurious_events
119116

120117
## npar
121118
if prf_npar[1]==0: # deterministic parameter
@@ -131,20 +128,19 @@ def generate_pupil_data(event_onsets, fs=1000, pad=5000, baseline_lowpass=0.2,
131128

132129

133130
## scaling
134-
sp_delta_weights=stats.truncnorm.rvs(-1/response_fluct_sd,np.inf, loc=1, scale=response_fluct_sd, size=sp_event_ix.size)
131+
sp_delta_weights=stats.truncnorm.rvs(a,b, loc=scale_evoked[0], scale=scale_evoked[1], size=sp_event_ix.size)
135132
x2=np.zeros_like(sy)
136133

137134
for i,ev in enumerate(sp_events):
138135
# create kernel and delta-functions for events
139136
kernel=pupil_kernel(duration=maxdur,fs=fs,npar=npars[i], tmax=tmaxs[i])
140137
x2[sp_event_ix[i]:(sp_event_ix[i]+kernel.size)]=x2[sp_event_ix[i]:(sp_event_ix[i]+kernel.size)]+kernel*sp_delta_weights[i]
141138

142-
amp=np.mean(x0)*evoked_response_perc # mean amplitude for the evoked response
143-
noise=noise_amp*np.mean(x0)*np.random.randn(n)
144-
145-
sy = x0 + amp*x1 + amp*x2 + noise
139+
sy = x0 + x1 + x2 + noise_amp*np.random.randn(n)
140+
sy = (sy * scale_signal[1])+scale_signal[0] # scale to desired range
141+
x0s = (x0*scale_signal[1])+scale_signal[0]
146142

147-
return (tx,sy,x0,delta_weights)
143+
return (tx,sy,x0s,delta_weights)
148144

149145

150146
def get_dataset(ntrials=100, isi=2000, rtdist=(1000,500),fs=1000,pad=5000, **kwargs):

pypillometry/pupildata.py

Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,7 +1389,7 @@ def __init__(self,
13891389
## OBS: not the real model but a simplification (npar/tmax may be different per event)
13901390
x1=pupil_build_design_matrix(self.tx, self.event_onsets, self.fs,
13911391
sim_params["prf_npar"][0], sim_params["prf_tmax"][0], 6000)
1392-
amp=np.mean(real_baseline)*sim_params["evoked_response_perc"]
1392+
amp=np.mean(real_baseline)*sim_params["scale_evoked"][1]
13931393
real_response=amp*np.dot(x1.T, real_response_coef) ## predicted signal
13941394

13951395
self.sim_response=real_response
@@ -1399,7 +1399,7 @@ def __init__(self,
13991399
def unscale(self, mean: Optional[float]=None, sd: Optional[float]=None, inplace=_inplace):
14001400
"""
14011401
Scale back to original values using either values provided as arguments
1402-
or the values stored in `scale_params`.
1402+
or the values stored in `scale_params`.n
14031403
14041404
Parameters
14051405
----------
@@ -1594,56 +1594,23 @@ def create_fake_pupildata(**kwargs):
15941594
Parameters
15951595
-----------
15961596
1597-
ntrials:int
1598-
number of trials
1599-
isi: float
1600-
inter-stimulus interval in seconds
1601-
rtdist: tuple (float,float)
1602-
mean and std of a (truncated at zero) normal distribution to generate response times
1603-
pad: float
1604-
padding before the first and after the last event in seconds
1605-
fs: float
1606-
sampling rate in Hz
1607-
baseline_lowpass: float
1608-
cutoff for the lowpass-filter that defines the baseline
1609-
(highest allowed frequency in the baseline fluctuations)
1610-
evoked_response_perc: float
1611-
amplitude of the pupil-response as proportion of the baseline
1612-
response_fluct_sd: float
1613-
How much do the amplitudes of the individual events fluctuate?
1614-
This is determined by drawing each individual pupil-response to
1615-
a single event from a (positive) normal distribution with mean as determined
1616-
by `evoked_response_perc` and sd `response_fluct_sd` (in units of
1617-
`evoked_response_perc`).
1618-
prf_npar: tuple (float,float)
1619-
(mean,std) of the npar parameter from :py:func:`pypillometry.pupil.pupil_kernel()`.
1620-
If the std is exactly zero, then the mean is used for all pupil-responses.
1621-
If the std is positive, npar is taken i.i.d. from ~ normal(mean,std) for each event.
1622-
prf_tmax: tuple (float,float)
1623-
(mean,std) of the tmax parameter from :py:func:`pypillometry.pupil.pupil_kernel()`.
1624-
If the std is exactly zero, then the mean is used for all pupil-responses.
1625-
If the std is positive, tmax is taken i.i.d. from ~ normal(mean,std) for each event.
1626-
prop_spurious_events: float
1627-
Add random events to the pupil signal. `prop_spurious_events` is expressed
1628-
as proportion of the number of real events.
1629-
noise_amp: float
1630-
Amplitude of random gaussian noise that sits on top of the simulated signal.
1631-
Expressed in units of mean baseline pupil diameter.
1597+
Same parameters as :py:func:`pypillometry.fakedata.get_dataset()`.
16321598
"""
16331599
sim_params={
16341600
"ntrials":100,
16351601
"isi":1000.0,
16361602
"rtdist":(1000.0,500.0),
1603+
"scale_signal":(0,1),
1604+
"scale_evoked":(0.2, 0.1),
16371605
"pad":5000.0,
16381606
"fs":1000.0,
16391607
"baseline_lowpass":0.1,
1640-
"evoked_response_perc":0.001,
1641-
"response_fluct_sd":1,
16421608
"prf_npar":(10.35,0),
16431609
"prf_tmax":(917.0,0),
1644-
"prop_spurious_events":0.1,
1610+
"num_spurious_events":0,
16451611
"noise_amp":0.0001
16461612
}
1613+
16471614
sim_params.update(kwargs)
16481615
#print(sim_params)
16491616
tx,sy,baseline,event_onsets,response_coef=get_dataset(**sim_params)

pypillometry/stan/baseline_model_asym_laplac.stan

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ data{
2222
matrix[n,ncol] B; // spline basis functions
2323

2424
int<lower=1> npeaks; // number of lower peaks in the signal
25-
int<lower=1> peakix[npeaks]; // index of the lower peaks in sy
25+
array[npeaks] int<lower=1> peakix; // index of the lower peaks in sy
2626
vector<lower=0>[npeaks] lam_prominences; // lambda-converted prominence values
2727

2828
real<lower=0> lam_sig; // lambda for the signal where there is no peak

0 commit comments

Comments
 (0)