59std::array<QList<int>, 3> pcaChannelGroups(
const FiffInfo& info)
61 std::array<QList<int>, 3> groups;
62 for (
int k = 0; k < info.
chs.size(); ++k) {
79QList<FiffProj> pcaProjectors(
const MatrixXd& data,
const QList<int>& chIdx,
const QStringList& chNames,
80 int nVec,
const QStringList& descs)
82 QList<FiffProj> projs;
83 if (nVec <= 0 || chIdx.isEmpty()) {
86 MatrixXd sub(chIdx.size(), data.cols());
87 for (
int c = 0; c < chIdx.size(); ++c) {
88 sub.row(c) = data.row(chIdx[c]);
90 const JacobiSVD<MatrixXd>
svd(sub, ComputeThinU);
91 const int nComp = qMin(nVec,
static_cast<int>(
svd.matrixU().cols()));
92 for (
int v = 0; v < nComp; ++v) {
96 proj.
desc = descs.value(v);
98 proj.
data->ncol =
static_cast<int>(chNames.size());
99 proj.
data->col_names = chNames;
100 proj.
data->data = MatrixXd::Zero(1, chNames.size());
101 for (
int c = 0; c < chIdx.size(); ++c) {
102 proj.
data->data(0, chIdx[c]) =
svd.matrixU()(c, v);
154 QList<FiffProj>::Iterator it;
155 for (it = p_qListFiffProj.begin(); it != p_qListFiffProj.end(); ++it)
158 qInfo(
"\t%lld projection items activated.\n",
static_cast<long long>(p_qListFiffProj.size()));
167 throw std::invalid_argument(
"No channel names specified");
172 proj = MatrixXd::Identity(nchan, nchan);
179 if (projs.size() == 0)
184 for (k = 0; k < projs.size(); ++k) {
187 nvec += projs[k].data->nrow;
192 qWarning(
"FiffProj::make_projector - No projectors nproj=0\n");
197 qWarning(
"FiffProj::make_projector - No rows in projector matrices found nvec<=0\n");
204 MatrixXd vecs = MatrixXd::Zero(nchan, nvec);
207 qint32 p, c, i, j, v;
210 RowVectorXi sel(nchan);
211 RowVectorXi vecSel(nchan);
213 vecSel.setConstant(-1);
214 for (k = 0; k < projs.size(); ++k) {
218 QMap<QString, int> uniqueMap;
219 for (l = 0; l < one.
data->col_names.size(); ++l)
220 uniqueMap[one.
data->col_names[l]] = 0;
222 if (one.
data->col_names.size() != uniqueMap.keys().size()) {
223 qWarning(
"Channel name list in projection item %d contains duplicate items", k);
232 vecSel.resize(nchan);
234 vecSel.setConstant(-1);
236 for (c = 0; c < nchan; ++c) {
237 for (i = 0; i < one.
data->col_names.size(); ++i) {
238 if (QString::compare(ch_names.at(c), one.
data->col_names[i]) == 0) {
240 for (j = 0; j < bads.size(); ++j) {
241 if (QString::compare(ch_names.at(c), bads.at(j)) == 0) {
246 if (!isBad && sel[p] != c) {
254 sel.conservativeResize(p);
255 vecSel.conservativeResize(p);
260 for (v = 0; v < one.
data->nrow; ++v)
261 for (i = 0; i < p; ++i)
262 vecs(sel[i], nvec + v) = one.
data->data(v, vecSel[i]);
267 for (v = 0; v < one.
data->nrow; ++v) {
268 onesize = sqrt((vecs.col(nvec + v).transpose() * vecs.col(nvec + v))(0, 0));
270 vecs.col(nvec + v) = vecs.col(nvec + v) / onesize;
274 nvec += one.
data->nrow;
286 JacobiSVD<MatrixXd>
svd(vecs.block(0, 0, vecs.rows(), nvec), ComputeFullU);
288 VectorXd
S =
svd.singularValues();
289 MatrixXd t_U =
svd.matrixU();
296 for (k = 0; k <
S.size(); ++k)
297 if (
S[k] /
S[0] > 1e-2)
300 U = t_U.block(0, 0, t_U.rows(), nproj);
305 proj -= U * U.transpose();
313 const MatrixXi& events,
320 const QMap<QString, double>& mapReject)
325 int minSamp =
static_cast<int>(std::round(tmin * sfreq));
326 int maxSamp =
static_cast<int>(std::round(tmax * sfreq));
327 int ns = maxSamp - minSamp + 1;
330 qWarning() <<
"[FiffProj::compute_from_raw] Invalid time window.";
335 QList<MatrixXd> epochs;
336 double gradReject = mapReject.value(
"grad", 0.0);
337 double magReject = mapReject.value(
"mag", 0.0);
338 double eegReject = mapReject.value(
"eeg", 0.0);
340 for (
int k = 0; k < events.rows(); ++k) {
341 if (events(k, 1) != 0 || events(k, 2) != eventCode)
344 int evSample = events(k, 0);
345 int epochStart = evSample + minSamp;
346 int epochEnd = evSample + maxSamp;
348 if (epochStart < raw.first_samp || epochEnd > raw.
last_samp)
351 MatrixXd epochData, epochTimes;
357 for (
int c = 0; c < nchan && ok; ++c) {
360 double pp = epochData.row(c).maxCoeff() - epochData.row(c).minCoeff();
373 epochs.append(epochData);
376 if (epochs.isEmpty()) {
377 qWarning() <<
"[FiffProj::compute_from_raw] No valid epochs found for event" << eventCode;
381 qInfo() <<
"[FiffProj::compute_from_raw]" << epochs.size() <<
"epochs collected for event" << eventCode;
385 MatrixXd
data(nchan, epochs.size() * ns);
386 for (
int e = 0; e < epochs.size(); ++e) {
387 data.middleCols(e * ns, ns) = epochs[e];
389 const std::array<QList<int>, 3> groups = pcaChannelGroups(raw.
info);
390 const int nVec[3] = {nGrad, nMag, nEeg};
391 const char* kinds[3] = {
"grad",
"mag",
"eeg"};
392 QList<FiffProj> projs;
393 for (
int g = 0; g < 3; ++g) {
395 for (
int v = 0; v < nVec[g]; ++v) {
396 descs << QString(
"PCA-%1-v%2").arg(kinds[g]).arg(v + 1);
408 if (evoked.
data.cols() == 0 || evoked.
times.size() == 0) {
411 const QString prefix = QString(
"%1-%2").arg(evoked.
times(0), 0,
'f', 3).arg(evoked.
times(evoked.
times.size() - 1), 0,
'f', 3);
412 const std::array<QList<int>, 3> groups = pcaChannelGroups(evoked.
info);
413 const int nVec[3] = {nGrad, nMag, nEeg};
414 const char* kinds[3] = {
"planar",
"axial",
"eeg"};
415 QList<FiffProj> projs;
416 for (
int g = 0; g < 3; ++g) {
418 for (
int v = 0; v < nVec[g]; ++v) {
419 descs << QString(
"%1-%2-PCA-%3").arg(kinds[g], prefix).arg(v + 1, 2, 10, QChar(
'0'));
421 projs += pcaProjectors(evoked.
data, groups[g], evoked.
info.
ch_names, nVec[g], descs);
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
constexpr auto FIFF_UNIT_T
Single averaged evoked response: time axis, samples, baseline, channel info and processing history.
FIFF tag-kind, block-kind and type-code numerical definitions, authoritative for FIFFLIB.
constexpr auto FIFFV_PROJ_ITEM_FIELD
Eigen::JacobiSVD< Eigen::Matrix3f > svd(S, Eigen::ComputeFullU|Eigen::ComputeFullV)
SSP projection item: a named projection vector set with active/desired flags, parsed from FIFFB_PROJ_...
FIFF continuous raw recording: FiffInfo plus a directory of FIFF_DATA_BUFFER tags for random-access s...
Static linear-algebra helpers: SVD-based conditioning, block-diagonal assembly, sorted index pairs.
FIFF file I/O, in-memory data structures and high-level readers/writers.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Single averaged evoked response: time axis, data, baseline, channel info and averaging metadata.
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...
FIFF named matrix: dense / sparse Eigen matrix plus row-name and column-name string lists.
Single SSP projection item: kind, active flag, desired flag and the named projection vector matrix.
FiffNamedMatrix::SDPtr data
static QList< FiffProj > compute_from_raw(const FiffRawData &raw, const Eigen::MatrixXi &events, int eventCode, float tmin, float tmax, int nGrad, int nMag, int nEeg, const QMap< QString, double > &mapReject=QMap< QString, double >())
static fiff_int_t make_projector(const QList< FiffProj > &projs, const QStringList &ch_names, Eigen::MatrixXd &proj, const QStringList &bads=defaultQStringList, Eigen::MatrixXd &U=defaultMatrixXd)
static void activate_projs(QList< FiffProj > &p_qListFiffProj)
static QList< FiffProj > compute_from_evoked(const FiffEvoked &evoked, int nGrad=2, int nMag=2, int nEeg=2)
Continuous FIFF raw recording: FiffInfo plus a random-access directory of FIFF_DATA_BUFFER tags.
bool read_raw_segment(Eigen::MatrixXd &data, Eigen::MatrixXd ×, fiff_int_t from=-1, fiff_int_t to=-1, const Eigen::RowVectorXi &sel=defaultRowVectorXi, bool do_debug=false) const
static Eigen::VectorXi sort(Eigen::Matrix< T, Eigen::Dynamic, 1 > &v, bool desc=true)