1function [Res] = update_nowcast(X_old,X_new,Time,Spec,Res,series,period,vintage_old,vintage_new)
2
3
4if ~isnumeric(vintage_old)
5    vintage_old = datenum(vintage_old,'yyyy-mm-dd');
6end
7if ~isnumeric(vintage_new)
8    vintage_new = datenum(vintage_new,'yyyy-mm-dd');
9end
10
11% Make sure datasets are the same size
12N = size(X_new,2);
13T_old = size(X_old,1); T_new = size(X_new,1);
14if T_new > T_old
15    X_old = [X_old; NaN(T_new-T_old,N)];
16end
17% append 1 year (12 months) of data to each dataset to allow for
18% forecasting at different horizons
19X_old = [X_old; NaN(12,N)];
20X_new = [X_new; NaN(12,N)];
21
22[y, m, d] = datevec(Time(end));
23
24Time = [Time; datenum(y,(m+1:m+12)',d)];
25
26i_series = find(strcmp(series,Spec.SeriesID));
27
28series_name = Spec.SeriesName{i_series};
29freq        = Spec.Frequency{i_series};
30
31switch freq
32    case 'm'
33        [y,m] = strtok(period,freq);
34        y = str2double(y);
35        m = str2double(strrep(m,freq,''));
36        d = 1;
37        t_nowcast = find(Time==datenum(y,m,d));
38    case 'q'
39        [y,q] = strtok(period,freq);
40        y = str2double(y);
41        q = str2double(strrep(q,freq,''));
42        m = 3*q;
43        d = 1;
44        t_nowcast = find(Time==datenum(y,m,d));
45end
46
47if isempty(t_nowcast)
48    error('Period is out of nowcasting horizon (up to one year ahead).');
49end
50
51
52
53% Update nowcast for target variable 'series' (i) at horizon 'period' (t)
54%   > Relate nowcast update into news from data releases:
55%     a. Compute the impact from data revisions
56%     b. Compute the impact from new data releases
57
58
59X_rev = X_new;
60X_rev(isnan(X_old)) = NaN;
61
62% Compute news --------------------------------------------------------
63
64% Compute impact from data revisions
65[y_old] = News_DFM(X_old,X_rev,Res,t_nowcast,i_series);
66
67% Compute impact from data releases
68[y_rev,y_new,~,actual,forecast,weight] = News_DFM(X_rev,X_new,Res,t_nowcast,i_series);
69
70% Display output
71fprintf('\n\n\n');
72fprintf('Nowcast Update: %s \n', datestr(vintage_new, 'mmmm dd, yyyy'))
73fprintf('Nowcast for %s (%s), %s \n',Spec.SeriesName{i_series},Spec.UnitsTransformed{i_series},datestr(Time(t_nowcast),'YYYY:QQ'));
74
75if(isempty(forecast))  % Only display table output if a forecast is made
76    fprintf('\n  No forecast was made.\n')
77else
78
79    impact_revisions = y_rev - y_old;      % Impact from revisions
80    news = actual - forecast;              % News from releases
81    impact_releases = weight .* (news);    % Impact of releases
82
83    % Store results
84    news_table = array2table([forecast, actual, weight, impact_releases],...
85                            'VariableNames', {'Forecast', 'Actual', 'Weight', 'Impact'},...
86                            'RowNames', Spec.SeriesID);
87
88    % Select only series with updates
89    data_released = any(isnan(X_old) & ~isnan(X_new), 1);
90
91    % Display the impact decomposition
92    fprintf('\n  Nowcast Impact Decomposition \n')
93    fprintf('  Note: The displayed output is subject to rounding error\n\n')
94    fprintf('                  %s nowcast:                  %5.2f\n', ...
95            datestr(vintage_old, 'mmm dd'), y_old)
96    fprintf('      Impact from data revisions:      %5.2f\n', impact_revisions)
97    fprintf('       Impact from data releases:      %5.2f\n', sum(news_table.Impact,'omitnan'))
98    fprintf('                                     +_________\n')
99    fprintf('                    Total impact:      %5.2f\n', ...
100            impact_revisions + sum(news_table.Impact,'omitnan'))
101    fprintf('                  %s nowcast:                  %5.2f\n\n', ...
102        datestr(vintage_new, 'mmm dd'), y_new)
103
104    % Display the table output
105    fprintf('\n  Nowcast Detail Table \n\n')
106    disp(news_table(data_released, :))
107
108
109    % Output results to structure
110    Res.impact_revisions = impact_revisions;
111    Res.weight = weight;
112    Res.news_table = news_table;
113
114end
115
116end
117
118
119function [y_old,y_new,singlenews,actual,forecast,weight,t_miss,v_miss,innov] = News_DFM(X_old,X_new,Res,t_fcst,v_news)
120%News_DFM()    Calculates changes in news
121%
122%  Syntax:
123%  [y_old, y_new, singlenews, actual, fore, weight ,t_miss, v_miss, innov] = ...
124%    News_DFM(X_old, X_new, Q, t_fcst, v_news)
125%
126%  Description:
127%   News DFM() inputs two datasets, DFM parameters, target time index, and
128%   target variable index. The function then produces Nowcast updates and
129%   decomposes the changes into news.
130%
131%  Input Arguments:
132%    X_old:  Old data matrix (old vintage)
133%    X_new:  New data matrix (new vintage)
134%    Res:    DFM() output results (see DFM for more details)
135%    t_fcst: Index for target time
136%    v_news: Index for target variable
137%
138%  Output Arguments:
139%    y_old:       Old nowcast
140%    y_new:       New nowcast
141%    single_news: News for each data series
142%    actual:      Observed series release values
143%    fore:        Forecasted series values
144%    weight:      News weight
145%    t_miss:      Time index for data releases
146%    v_miss:      Series index for data releases
147%    innov:       Difference between observed and predicted series values ("innovation")
148
149%% Initialize variables
150
151r = size(Res.C,2);
152[~, N] = size(X_new);
153singlenews = zeros(1,N);  % Initialize news vector (will store news for each series)
154
155%% NO FORECAST CASE: Already values for variables v_news at time t_fcst
156
157if ~isnan(X_new(t_fcst,v_news))
158    Res_old = para_const(X_old, Res, 0);  % Apply Kalman filter for old data
159
160
161    for i=1:size(v_news,2)      % Loop for each target variable
162        % (Observed value) - (predicted value)
163        singlenews(:,v_news(i)) = X_new(t_fcst,v_news(i)) ...
164                                    - Res_old.X_sm(t_fcst,v_news(i));
165
166        % Set predicted and observed y values
167        y_old(1,i) = Res_old.X_sm(t_fcst,v_news(i));
168        y_new(1,i) = X_new(t_fcst,v_news(i));
169    end
170
171    % Forecast-related output set to empty
172    actual=[];forecast=[];weight=[];t_miss=[];v_miss=[];innov=[];
173
174
175else
176    %% FORECAST CASE (these are broken down into (A) and (B))
177
178    % Initialize series mean/standard deviation respectively
179    Mx = Res.Mx;
180    Wx = Res.Wx;
181
182    % Calculate indicators for missing values (1 if missing, 0 otherwise)
183    miss_old=isnan(X_old);
184    miss_new=isnan(X_new);
185
186    % Indicator for missing--combine above information to single matrix where:
187    % (i) -1: Value is in the old data, but missing in new data
188    % (ii) 1: Value is in the new data, but missing in old data
189    % (iii) 0: Values are missing from/available in both datasets
190    i_miss = miss_old - miss_new;
191
192    % Time/variable indicies where case (b) is true
193    [t_miss,v_miss]=find(i_miss==1);
194
195    %% FORECAST SUBCASE (A): NO NEW INFORMATION
196
197    if isempty(v_miss)
198        % Fill in missing variables using a Kalman filter
199        Res_old = para_const(X_old, Res, 0);
200        Res_new = para_const(X_new, Res, 0);
201
202        % Set predicted and observed y values. New y value is set to old
203        y_old = Res_old.X_sm(t_fcst,v_news);
204        y_new = y_old;
205        % y_new = Res_new.X_sm(t_fcst,v_news);
206
207        % No news, so nothing returned for news-related output
208        groupnews=[];singlenews=[];gain=[];gainSer=[];
209        actual=[];forecast=[];weight=[];t_miss=[];v_miss=[];innov=[];
210    else
211    %----------------------------------------------------------------------
212    %     v_miss=[1:size(X_new,2)]';
213    %     t_miss=t_miss(1)*ones(size(X_new,2),1);
214    %----------------------------------------------------------------------
215    %% FORECAST SUBCASE (B): NEW INFORMATION
216
217    % Difference between forecast time and new data time
218    lag = t_fcst-t_miss;
219
220    % Gives biggest time interval between forecast and new data
221    k = max([abs(lag); max(lag)-min(lag)]);
222
223    C = Res.C;   % Observation matrix
224    R = Res.R';  % Covariance for observation matrix residuals
225
226    % Number of new events
227    n_news = size(lag,1);
228
229    % Smooth old dataset
230    Res_old = para_const(X_old, Res, k);
231    Plag = Res_old.Plag;
232
233    % Smooth new dataset
234    Res_new = para_const(X_new, Res, 0);
235
236    % Subset for target variable and forecast time
237    y_old = Res_old.X_sm(t_fcst,v_news);
238    y_new = Res_new.X_sm(t_fcst,v_news);
239
240
241
242    P = Res_old.P(:,:,2:end);
243    P1=[];  % Initialize projection onto updates
244
245    % Cycle through total number of updates
246    for i=1:n_news
247        h = abs(t_fcst-t_miss(i));
248        m = max([t_miss(i) t_fcst]);
249
250        % If location of update is later than the forecasting date
251        if t_miss(i)>t_fcst
252            Pp=Plag{h+1}(:,:,m);  %P(1:r,h*r+1:h*r+r,m)';
253        else
254            Pp=Plag{h+1}(:,:,m)';  %P(1:r,h*r+1:h*r+r,m);
255        end
256        P1=[P1 Pp*C(v_miss(i),1:r)'];  % Projection on updates
257    end
258
259    for i=1:size(t_miss,1)
260        % Standardize predicted and observed values
261        X_new_norm = (X_new(t_miss(i),v_miss(i)) - Mx(v_miss(i)))./Wx(v_miss(i));
262        X_sm_norm = (Res_old.X_sm(t_miss(i),v_miss(i))- Mx(v_miss(i)))./Wx(v_miss(i));
263
264        % Innovation: Gives [observed] data - [predicted data]
265        innov(i)= X_new_norm - X_sm_norm;
266    end
267
268    ins=size(innov,2);
269    P2=[];
270    p2=[];
271
272    % Gives non-standardized series weights
273    for i=1:size(lag,1)
274        for j=1:size(lag,1)
275            h=abs(lag(i)-lag(j));
276            m=max([t_miss(i),t_miss(j)]);
277
278            if t_miss(j)>t_miss(i)
279                Pp=Plag{h+1}(:,:,m); %P(1:r,h*r+1:(h+1)*r,m)';
280            else
281                Pp=Plag{h+1}(:,:,m)'; %P(1:r,h*r+1:(h+1)*r,m);
282            end
283            if v_miss(i)==v_miss(j) & t_miss(i)~=t_miss(j)
284                WW(v_miss(i),v_miss(j))=0;
285            else
286                WW(v_miss(i),v_miss(j))=R(v_miss(i),v_miss(j));
287            end
288            p2=[p2 C(v_miss(i),1:r)*Pp*C(v_miss(j),1:r)'+WW(v_miss(i),v_miss(j))];
289        end
290        P2=[P2;p2];
291        p2=[];
292    end
293
294    clear temp
295    for i=1:size(v_news,2)      % loop on v_news
296        % Convert to real units (unstadardized data)
297        totnews(1,i) = Wx(v_news(i))*C(v_news(i),1:r)*P1*inv(P2)*innov';
298        temp(1,:,i) = Wx(v_news(i))*C(v_news(i),1:r)*P1*inv(P2).*innov;
299        gain(:,:,i) = Wx(v_news(i))*C(v_news(i),1:r)*P1*inv(P2);
300    end
301
302    % Initialize output objects
303    singlenews = NaN(max(t_miss)-min(t_miss)+1,N); %,size(v_news,2)
304    actual     = NaN(N,1);  % Actual forecasted values
305    forecast   = NaN(N,1);  % Forecasted values
306    weight     = NaN(N,1,size(v_news,2));
307
308    % Fill in output values
309    for i=1:size(innov,2)
310        actual(v_miss(i),1) = X_new(t_miss(i),v_miss(i));
311        forecast(v_miss(i),1) = Res_old.X_sm(t_miss(i),v_miss(i));
312
313        for j=1:size(v_news,2)
314            singlenews(t_miss(i)-min(t_miss)+1,v_miss(i),j) = temp(1,i,j);
315            weight(v_miss(i),:,j) = gain(:,i,j)/Wx(v_miss(i));
316        end
317    end
318
319    singlenews = sum(singlenews,1);      % Returns total news
320
321
322    [v_miss, ~, ~] = unique(v_miss);
323
324end
325
326end
327
328end
329
330function Res = para_const(X, P, lag)
331%para_const()    Implements Kalman filter for "News_DFM.m"
332%
333%  Syntax:
334%    Res = para_const(X,P,lag)
335%
336%  Description:
337%    para_const() implements the Kalman filter for the news calculation
338%    step. This procedure smooths and fills in missing data for a given
339%    data matrix X. In contrast to runKF(), this function is used when
340%    model parameters are already estimated.
341%
342%  Input parameters:
343%    X: Data matrix.
344%    P: Parameters from the dynamic factor model.
345%    lag: Number of lags
346%
347%  Output parameters:
348%    Res [struc]: A structure containing the following:
349%      Res.Plag: Smoothed factor covariance for transition matrix
350%      Res.P:    Smoothed factor covariance matrix
351%      Res.X_sm: Smoothed data matrix
352%      Res.F:    Smoothed factors
353%
354
355
356% Kalman filter with specified paramaters
357% written for
358% "MAXIMUM LIKELIHOOD ESTIMATION OF FACTOR MODELS ON DATA SETS WITH
359% ARBITRARY PATTERN OF MISSING DATA."
360% by Marta Banbura and Michele Modugno
361
362%% Set model parameters and data preparation
363
364% Set model parameters
365Z_0 = P.Z_0;
366V_0 = P.V_0;
367A = P.A;
368C = P.C;
369Q = P.Q;
370R = P.R;
371Mx = P.Mx;
372Wx = P.Wx;
373
374% Prepare data
375[T,~] = size(X);
376
377% Standardise x
378Y = ((X-repmat(Mx,T,1))./repmat(Wx,T,1))';
379
380%% Apply Kalman filter and smoother
381% See runKF() for details about FIS and SKF
382
383Sf = SKF(Y, A, C, Q, R, Z_0, V_0);  % Kalman filter
384
385Ss = FIS(A, Sf);  % Smoothing step
386
387%% Calculate parameter output
388Vs = Ss.VmT(:,:,2:end);  % Smoothed factor covariance for transition matrix
389Vf = Sf.VmU(:,:,2:end);  % Filtered factor posterior covariance
390Zsmooth = Ss.ZmT;        % Smoothed factors
391Vsmooth = Ss.VmT;        % Smoothed covariance values
392
393Plag{1} = Vs;
394
395for jk = 1:lag
396    for jt = size(Plag{1},3):-1:lag+1
397        As = Vf(:,:,jt-jk)*A'*pinv(A*Vf(:,:,jt-jk)*A'+Q);
398        Plag{jk+1}(:,:,jt) = As*Plag{jk}(:,:,jt);
399    end
400end
401
402% Prepare data for output
403Zsmooth=Zsmooth';
404x_sm = Zsmooth(2:end,:)*C';  % Factors to series representation
405X_sm = repmat(Wx,T,1).*x_sm+repmat(Mx,T,1);  % Standardized to unstandardized
406
407%--------------------------------------------------------------------------
408%   Loading the structure with the results
409%--------------------------------------------------------------------------
410Res.Plag = Plag;
411Res.P = Vsmooth;
412Res.X_sm = X_sm;
413Res.F = Zsmooth(2:end,:);
414
415end
416
417
418%______________________________________________________________________
419function S = SKF(Y, A, C, Q, R, Z_0, V_0)
420%SKF    Applies Kalman filter
421%
422%  Syntax:
423%    S = SKF(Y, A, C, Q, R, Z_0, V_0)
424%
425%  Description:
426%    SKF() applies a Kalman filter. This is a bayesian algorithm. Looping
427%    forward in time, a 'prior' estimate is calculated from the previous
428%    period. Then, using the observed data, a 'posterior' value is obtained.
429%
430%  Input parameters:
431%    y:   Input data.
432%    A:   Transition matrix coefficients.
433%    C:   Observation matrix coefficients.
434%    Q:   Covariance matrix (factors and idiosyncratic component)
435%    R:   Variance-Covariance for observation equation residuals
436%    Z_0: Initial factor values
437%    V_0: Initial factor covariance matrix
438%
439%  Output parameters:
440%    S.Zm:     Prior/predicted factor state vector (Z_t|t-1)
441%    S.ZmU:    Posterior/updated state vector (Z_t|t)
442%    S.Vm:     Prior/predicted covariance of factor state vector (V_t|t-1)
443%    S.VmU:    Posterior/updated covariance of factor state vector (V_t|t)
444%    S.loglik: Value of likelihood function
445%    S.k_t:    Kalman gain
446%
447%  Model:
448%   Y_t = C_t Z_t + e_t,     e_t ~ N(0, R)
449%   Z_t = A Z_{t-1} + mu_t,  mu_t ~ N(0, Q)
450
451%% INSTANTIATE OUTPUT VALUES ---------------------------------------------
452  % Output structure & dimensions of state space matrix
453  [~, m] = size(C);
454
455  % Outputs time for data matrix. "number of observations"
456  nobs  = size(Y,2);
457
458  % Initialize output
459  S.Zm  = NaN(m, nobs);       % Z_t | t-1 (prior)
460  S.Vm  = NaN(m, m, nobs);    % V_t | t-1 (prior)
461  S.ZmU = NaN(m, nobs+1);     % Z_t | t (posterior/updated)
462  S.VmU = NaN(m, m, nobs+1);  % V_t | t (posterior/updated)
463  S.loglik = 0;
464
465%% SET INITIAL VALUES ----------------------------------------------------
466  Zu = Z_0;  % Z_0|0 (In below loop, Zu gives Z_t | t)
467  Vu = V_0;  % V_0|0 (In below loop, Vu guvse V_t | t)
468
469  % Store initial values
470  S.ZmU(:,1)    = Zu;
471  S.VmU(:,:,1)  = Vu;
472
473%% KALMAN FILTER PROCEDURE ----------------------------------------------
474  for t = 1:nobs
475      %%% CALCULATING PRIOR DISTIBUTION----------------------------------
476
477      % Use transition eqn to create prior estimate for factor
478      % i.e. Z = Z_t|t-1
479      Z   = A * Zu;
480
481      % Prior covariance matrix of Z (i.e. V = V_t|t-1)
482      %   Var(Z) = Var(A*Z + u_t) = Var(A*Z) + Var(\epsilon) =
483      %   A*Vu*A' + Q
484      V   = A * Vu* A' + Q;
485      V   =  0.5 * (V+V');  % Trick to make symmetric
486
487      %%% CALCULATING POSTERIOR DISTRIBUTION ----------------------------
488
489      % Removes missing series: These are removed from Y, C, and R
490      [Y_t, C_t, R_t, ~] = MissData(Y(:,t), C, R);
491
492      % Check if y_t contains no data. If so, replace Zu and Vu with prior.
493      if isempty(Y_t)
494          Zu = Z;
495          Vu = V;
496      else
497          % Steps for variance and population regression coefficients:
498          % Var(c_t*Z_t + e_t) = c_t Var(A) c_t' + Var(u) = c_t*V *c_t' + R
499          VC  = V * C_t';
500          iF  = inv(C_t * VC + R_t);
501
502          % Matrix of population regression coefficients (QuantEcon eqn #4)
503          VCF = VC*iF;
504
505          % Gives difference between actual and predicted observation
506          % matrix values
507          innov  = Y_t - C_t*Z;
508
509          % Update estimate of factor values (posterior)
510          Zu  = Z  + VCF * innov;
511
512          % Update covariance matrix (posterior) for time t
513          Vu  = V  - VCF * VC';
514          Vu   =  0.5 * (Vu+Vu'); % Approximation trick to make symmetric
515
516          % Update log likelihood
517          S.loglik = S.loglik + 0.5*(log(det(iF))  - innov'*iF*innov);
518      end
519
520      %%% STORE OUTPUT----------------------------------------------------
521
522      % Store covariance and observation values for t-1 (priors)
523      S.Zm(:,t)   = Z;
524      S.Vm(:,:,t) = V;
525
526      % Store covariance and state values for t (posteriors)
527      % i.e. Zu = Z_t|t   & Vu = V_t|t
528      S.ZmU(:,t+1)    = Zu;
529      S.VmU(:,:,t+1)  = Vu;
530  end
531
532  % Store Kalman gain k_t
533  if isempty(Y_t)
534      S.k_t = zeros(m,m);
535  else
536      S.k_t = VCF * C_t;
537  end
538end
539
540
541
542%______________________________________________________________________
543function S = FIS(A, S)
544%FIS()    Applies fixed-interval smoother
545%
546%  Syntax:
547%    S = FIS(A, S)
548%
549%  Description:
550%    SKF() applies a fixed-interval smoother, and is used in conjunction
551%    with SKF(). Starting from the final value, this uses a set of
552%    recursions and works backwards. Smoothers are advantageous in that
553%    future values are used for estimation. See  page 154 of 'Forecasting,
554%    structural time series models and the Kalman filter' for more details
555%    (Harvey, 1990).
556%
557%  Input parameters:
558%    A: Transition matrix coefficients.
559%    S: Output from SKF() step. See SKF() for details.
560%
561%  Output parameters:
562%    S: In addition to the output from SKF(), FIS() adds the following
563%       smoothed estimates. Note that t = 1...T gives time:
564%    - S.ZmT: Smoothed estimate of factor values (Z_t|T)
565%    - S.VmT: Smoothed estimate of factor covariance matrix (V_t|T = Cov(Z_t|T))
566%    - S.VmT_1: Smoothed estimate of Lag 1 factor covariance matrix (Cov(Z_tZ_t-1|T))
567%
568%  Model:
569%   Y_t = C_t Z_t + e_t for e_t ~ N(0, R)
570%   Z_t = A Z_{t-1} + mu_t for mu_t ~ N(0, Q)
571
572%% ORGANIZE INPUT ---------------------------------------------------------
573
574% Initialize output matrices
575  [m, nobs] = size(S.Zm);
576  S.ZmT = zeros(m,nobs+1);
577  S.VmT = zeros(m,m,nobs+1);
578
579  % Fill the final period of ZmT, VmT with SKF() posterior values
580  S.ZmT(:,nobs+1)   = squeeze(S.ZmU(:, nobs+1));
581  S.VmT(:,:,nobs+1) = squeeze(S.VmU(:,:, nobs+1));
582
583  % Initialize VmT_1 lag 1 covariance matrix for final period
584  S.VmT_1(:,:,nobs) = (eye(m)-S.k_t) *A*squeeze(S.VmU(:,:,nobs));
585
586  % Used for recursion process. See companion file for details
587  J_2 = squeeze(S.VmU(:,:,nobs)) * A' * pinv(squeeze(S.Vm(:,:,nobs)));
588
589  %% RUN SMOOTHING ALGORITHM ----------------------------------------------
590
591  % Loop through time reverse-chronologically (starting at final period nobs)
592    for t = nobs:-1:1
593
594        % Store posterior and prior factor covariance values
595        VmU = squeeze(S.VmU(:,:,t));
596        Vm1 = squeeze(S.Vm(:,:,t));
597
598        % Store previous period smoothed factor covariance and lag-1 covariance
599        V_T = squeeze(S.VmT(:,:,t+1));
600        V_T1 = squeeze(S.VmT_1(:,:,t));
601
602        J_1 = J_2;
603
604        % Update smoothed factor estimate
605        S.ZmT(:,t) = S.ZmU(:,t) + J_1 * (S.ZmT(:,t+1) - A * S.ZmU(:,t)) ;
606
607        % Update smoothed factor covariance matrix
608        S.VmT(:,:,t) = VmU + J_1 * (V_T - Vm1) * J_1';
609
610        if t>1
611            % Update weight
612            J_2 = squeeze(S.VmU(:, :, t-1)) * A' * pinv(squeeze(S.Vm(:,:,t-1)));
613
614            % Update lag 1 factor covariance matrix
615            S.VmT_1(:,:,t-1) = VmU * J_2'+J_1 * (V_T1 - A * VmU) * J_2';
616        end
617    end
618
619end
620
621
622function [y,C,R,L]  = MissData(y,C,R)
623%______________________________________________________________________
624% PROC missdata
625% PURPOSE: eliminates the rows in y & matrices Z, G that correspond to
626%          missing data (NaN) in y
627% INPUT    y             vector of observations at time t
628%          S             KF system matrices (structure)
629%                        must contain Z & G
630% OUTPUT   y             vector of observations (reduced)
631%          Z G           KF system matrices     (reduced)
632%          L             To restore standard dimensions
633%                        where # is the nr of available data in y
634%______________________________________________________________________
635
636  % Returns 1 for nonmissing series
637  ix = ~isnan(y);
638
639  % Index for columns with nonmissing variables
640  e  = eye(size(y,1));
641  L  = e(:,ix);
642
643  % Removes missing series
644  y  = y(ix);
645
646  % Removes missing series from observation matrix
647  C  =  C(ix,:);
648
649  % Removes missing series from transition matrix
650  R  =  R(ix,ix);
651
652end
653
654