Batch processes a sequences of measurements.

        Parameters
        ----------

        zs : list-like
            list of measurements at each time step `self.dt`. Missing
            measurements must be represented by `None`.

        Fs : None, list-like, default=None
            optional value or list of values to use for the state transition
            matrix F.

            If Fs is None then self.F is used for all epochs.

            Otherwise it must contain a list-like list of F's, one for
            each epoch.  This allows you to have varying F per epoch.

        Qs : None, np.array or list-like, default=None
            optional value or list of values to use for the process error
            covariance Q.

            If Qs is None then self.Q is used for all epochs.

            Otherwise it must contain a list-like list of Q's, one for
            each epoch.  This allows you to have varying Q per epoch.

        Hs : None, np.array or list-like, default=None
            optional list of values to use for the measurement matrix H.

            If Hs is None then self.H is used for all epochs.

            If Hs contains a single matrix, then it is used as H for all
            epochs.

            Otherwise it must contain a list-like list of H's, one for
            each epoch.  This allows you to have varying H per epoch.

        Rs : None, np.array or list-like, default=None
            optional list of values to use for the measurement error
            covariance R.

            If Rs is None then self.R is used for all epochs.

            Otherwise it must contain a list-like list of R's, one for
            each epoch.  This allows you to have varying R per epoch.

        Bs : None, np.array or list-like, default=None
            optional list of values to use for the control transition matrix B.

            If Bs is None then self.B is used for all epochs.

            Otherwise it must contain a list-like list of B's, one for
            each epoch.  This allows you to have varying B per epoch.

        us : None, np.array or list-like, default=None
            optional list of values to use for the control input vector;

            If us is None then None is used for all epochs (equivalent to 0,
            or no control input).

            Otherwise it must contain a list-like list of u's, one for
            each epoch.

       update_first : bool, optional, default=False
            controls whether the order of operations is update followed by
            predict, or predict followed by update. Default is predict->update.

        saver : filterpy.common.Saver, optional
            filterpy.common.Saver object. If provided, saver.save() will be
            called after every epoch

        Returns
        -------

        means : np.array((n,dim_x,1))
            array of the state for each time step after the update. Each entry
            is an np.array. In other words `means[k,:]` is the state at step
            `k`.

        covariance : np.array((n,dim_x,dim_x))
            array of the covariances for each time step after the update.
            In other words `covariance[k,:,:]` is the covariance at step `k`.

        means_predictions : np.array((n,dim_x,1))
            array of the state for each time step after the predictions. Each
            entry is an np.array. In other words `means[k,:]` is the state at
            step `k`.

        covariance_predictions : np.array((n,dim_x,dim_x))
            array of the covariances for each time step after the prediction.
            In other words `covariance[k,:,:]` is the covariance at step `k`.

        Examples
        --------

        .. code-block:: Python

            # this example demonstrates tracking a measurement where the time
            # between measurement varies, as stored in dts. This requires
            # that F be recomputed for each epoch. The output is then smoothed
            # with an RTS smoother.

            zs = [t + random.randn()*4 for t in range (40)]
            Fs = [np.array([[1., dt], [0, 1]] for dt in dts]

            (mu, cov, _, _) = kf.batch_filter(zs, Fs=Fs)
            (xs, Ps, Ks) = kf.rts_smoother(mu, cov, Fs=Fs)

def batch_filter(self, zs, Fs=None, Qs=None, Hs=None,
                     Rs=None, Bs=None, us=None, update_first=False,
                     saver=None):
        """ Batch processes a sequences of measurements.

        Parameters
        ----------

        zs : list-like
            list of measurements at each time step `self.dt`. Missing
            measurements must be represented by `None`.

        Fs : None, list-like, default=None
            optional value or list of values to use for the state transition
            matrix F.

            If Fs is None then self.F is used for all epochs.

            Otherwise it must contain a list-like list of F's, one for
            each epoch.  This allows you to have varying F per epoch.

        Qs : None, np.array or list-like, default=None
            optional value or list of values to use for the process error
            covariance Q.

            If Qs is None then self.Q is used for all epochs.

            Otherwise it must contain a list-like list of Q's, one for
            each epoch.  This allows you to have varying Q per epoch.

        Hs : None, np.array or list-like, default=None
            optional list of values to use for the measurement matrix H.

            If Hs is None then self.H is used for all epochs.

            If Hs contains a single matrix, then it is used as H for all
            epochs.

            Otherwise it must contain a list-like list of H's, one for
            each epoch.  This allows you to have varying H per epoch.

        Rs : None, np.array or list-like, default=None
            optional list of values to use for the measurement error
            covariance R.

            If Rs is None then self.R is used for all epochs.

            Otherwise it must contain a list-like list of R's, one for
            each epoch.  This allows you to have varying R per epoch.

        Bs : None, np.array or list-like, default=None
            optional list of values to use for the control transition matrix B.

            If Bs is None then self.B is used for all epochs.

            Otherwise it must contain a list-like list of B's, one for
            each epoch.  This allows you to have varying B per epoch.

        us : None, np.array or list-like, default=None
            optional list of values to use for the control input vector;

            If us is None then None is used for all epochs (equivalent to 0,
            or no control input).

            Otherwise it must contain a list-like list of u's, one for
            each epoch.

       update_first : bool, optional, default=False
            controls whether the order of operations is update followed by
            predict, or predict followed by update. Default is predict->update.

        saver : filterpy.common.Saver, optional
            filterpy.common.Saver object. If provided, saver.save() will be
            called after every epoch

        Returns
        -------

        means : np.array((n,dim_x,1))
            array of the state for each time step after the update. Each entry
            is an np.array. In other words `means[k,:]` is the state at step
            `k`.

        covariance : np.array((n,dim_x,dim_x))
            array of the covariances for each time step after the update.
            In other words `covariance[k,:,:]` is the covariance at step `k`.

        means_predictions : np.array((n,dim_x,1))
            array of the state for each time step after the predictions. Each
            entry is an np.array. In other words `means[k,:]` is the state at
            step `k`.

        covariance_predictions : np.array((n,dim_x,dim_x))
            array of the covariances for each time step after the prediction.
            In other words `covariance[k,:,:]` is the covariance at step `k`.

        Examples
        --------

        .. code-block:: Python

            # this example demonstrates tracking a measurement where the time
            # between measurement varies, as stored in dts. This requires
            # that F be recomputed for each epoch. The output is then smoothed
            # with an RTS smoother.

            zs = [t + random.randn()*4 for t in range (40)]
            Fs = [np.array([[1., dt], [0, 1]] for dt in dts]

            (mu, cov, _, _) = kf.batch_filter(zs, Fs=Fs)
            (xs, Ps, Ks) = kf.rts_smoother(mu, cov, Fs=Fs)
        """

        #pylint: disable=too-many-statements
        n = np.size(zs, 0)
        if Fs is None:
            Fs = [self.F] * n
        if Qs is None:
            Qs = [self.Q] * n
        if Hs is None:
            Hs = [self.H] * n
        if Rs is None:
            Rs = [self.R] * n
        if Bs is None:
            Bs = [self.B] * n
        if us is None:
            us = [0] * n

        # mean estimates from Kalman Filter
        if self.x.ndim == 1:
            means = zeros((n, self.dim_x))
            means_p = zeros((n, self.dim_x))
        else:
            means = zeros((n, self.dim_x, 1))
            means_p = zeros((n, self.dim_x, 1))

        # state covariances from Kalman Filter
        covariances = zeros((n, self.dim_x, self.dim_x))
        covariances_p = zeros((n, self.dim_x, self.dim_x))

        if update_first:
            for i, (z, F, Q, H, R, B, u) in enumerate(zip(zs, Fs, Qs, Hs, Rs, Bs, us)):

                self.update(z, R=R, H=H)
                means[i, :] = self.x
                covariances[i, :, :] = self.P

                self.predict(u=u, B=B, F=F, Q=Q)
                means_p[i, :] = self.x
                covariances_p[i, :, :] = self.P

                if saver is not None:
                    saver.save()
        else:
            for i, (z, F, Q, H, R, B, u) in enumerate(zip(zs, Fs, Qs, Hs, Rs, Bs, us)):

                self.predict(u=u, B=B, F=F, Q=Q)
                means_p[i, :] = self.x
                covariances_p[i, :, :] = self.P

                self.update(z, R=R, H=H)
                means[i, :] = self.x
                covariances[i, :, :] = self.P

                if saver is not None:
                    saver.save()

        return (means, covariances, means_p, covariances_p)