OSCR

Filter bank CSP with Riemannian weighting for disability-centric motor imagery brain computer interface.

Code ↔ Paper

1 match between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 1 match
  1. [1] § Materials and methods › Multi-classification ↔ BCI_R-FBCSP.ipynb, lines 1066 to 1125 · score 0.51 · Random Forest Classifier, Linear Discriminant, Layer, MLP, LDA · Report a mismatch

Paper

Loaded from Europe PMC by your browser; OSCR does not store it: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Jupyter notebook · 1,259 lines · 47 KB · CC-BY-4.0 · 1 match

  1. # %%
  2. from scipy import signal
  3. from scipy.signal import firwin, lfilter
  4. import mne
  5. import numpy as np
  6. from sklearn.metrics import classification_report, accuracy_score
  7. from sklearn.preprocessing import StandardScaler
  8. from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
  9. from scipy.signal import butter, sosfiltfilt, filtfilt
  10. from sklearn.metrics import accuracy_score, cohen_kappa_score, precision_score, recall_score
  11. from mne.decoding import CSP
  12. from scipy.signal import welch
  13. from sklearn.preprocessing import StandardScaler
  14. import logging
  15. import scipy.io as io
  16. import os
  17. # %%
  18. class IV2aGdfReader:
  19. stimcodes = ('769', '770', '771', '772')
  20. channels_to_remove = ['EOG-left', 'EOG-central', 'EOG-right']
  21. def __init__(self, data_dir: str=None):
  22. self.data_dir = data_dir
  23. if data_dir is not None:
  24. self.filenames = [name for name in os.listdir(
  25. self.data_dir) if name.endswith(".gdf")]
  26. else:
  27. self.filenames = []
  28. def load_raw_data_gdf(self, filename: str):
  29. if self.data_dir is not None:
  30. filepath = os.path.join(self.data_dir, filename)
  31. else:
  32. filepath = filename
  33. logging.info(f"Read {filepath}")
  34. return mne.io.read_raw_gdf(filepath, verbose="ERROR")
  35. def read_file(self, filename: str, tmin=0.5, tmax=3.0, baseline=None, matfile=None): #tmin=0.5, tmax=2.5
  36. gdf_name = os.path.basename(filename).split(".")[0]
  37. subject = gdf_name[1:3]
  38. raw_data = self.load_raw_data_gdf(filename)
  39. sample_freq = raw_data.info.get('sfreq')
  40. events, event_ids = mne.events_from_annotations(raw_data)
  41. if "T" == gdf_name[-1]:
  42. stims = [value for key, value in event_ids.items()
  43. if key in self.stimcodes]
  44. elif "E" == gdf_name[-1]:
  45. stims = [value for key, value in event_ids.items() if key == "783"]
  46. else:
  47. all_stimcodes = self.stimcodes + ["783"]
  48. stims = [value for key, value in event_ids.items() if key in all_stimcodes]
  49. epochs = mne.Epochs(raw_data, events, event_id=stims, tmin=tmin, tmax=tmax,
  50. baseline=baseline, preload=True, proj=False)
  51. epochs = epochs.drop_channels(self.channels_to_remove)
  52. # Récupérer les noms et indices des canaux EEG restants
  53. ch_names = epochs.ch_names # Liste des noms de canaux
  54. ch_indices = {name: idx for idx, name in enumerate(ch_names)}
  55. if matfile is not None:
  56. mat = io.loadmat(matfile)
  57. y_labels = mat["classlabel"].reshape(-1) - 1
  58. elif self.data_dir is not None:
  59. mat = io.loadmat(os.path.join(
  60. self.data_dir, "true_labels", f"{gdf_name}.mat"))
  61. y_labels = mat["classlabel"].reshape(-1) - 1
  62. else:
  63. y_labels = None
  64. x_data = epochs.get_data()#*1e6
  65. eeg_data = {'x_data': x_data,
  66. 'y_labels': y_labels,
  67. 'fs': sample_freq,
  68. 'ch_names':ch_names,
  69. 'ch_indices':ch_indices,
  70. "subject": subject}
  71. return eeg_data
  72. # %%
  73. base_path ="C:/Users/Client/Desktop/Jihen_BCI/BCI_IV_DSIIa"
  74. reader = IV2aGdfReader(data_dir=base_path)
  75. # %%
  76. %%capture
  77. # Liste des fichiers à traiter
  78. file_extensions = ['T', 'E']
  79. base_filename = "A{:02d}{}.gdf"
  80. # Initialiser les listes pour stocker toutes les données et étiquettes .T et .E
  81. all_x_data_T = []
  82. all_x_data_E = []
  83. all_y_labels_T = []
  84. all_y_labels_E = []
  85. all_ch_namesT = []
  86. all_ch_indicesT = []
  87. all_ch_namesE = []
  88. all_ch_indicesE = []
  89. # Boucle sur les extensions de fichiers
  90. for ext in file_extensions:
  91. # Boucle sur les fichiers (9 fichiers au total)
  92. for i in range(1, 10):
  93. filename = os.path.join(base_path, base_filename.format(i, ext))
  94. data = reader.read_file(filename)
  95. # Accéder aux données et aux étiquettes
  96. x_data = data['x_data']
  97. y_labels = data['y_labels']
  98. ch_names = data['ch_names']
  99. ch_indices = data['ch_indices']
  100. # Ajouter les données et étiquettes aux listes correspondantes
  101. if ext == 'T':
  102. all_x_data_T.append(x_data)
  103. all_y_labels_T.append(y_labels)
  104. all_ch_namesT .append(ch_names)
  105. all_ch_indicesT .append(ch_indices)
  106. elif ext == 'E':
  107. all_x_data_E.append(x_data)
  108. all_y_labels_E.append(y_labels)
  109. all_ch_namesE .append(ch_names)
  110. all_ch_indicesE .append(ch_indices)
  111. # Imprimer la taille des données et des étiquettes pour chaque fichier
  112. print(f"Taille des données ({filename}):", x_data.shape)
  113. print(f"Taille des étiquettes ({filename}):", y_labels.shape)
  114. # Convertir les listes en tableaux numpy
  115. all_x_data_T = np.array(all_x_data_T)
  116. all_y_labels_T = np.array(all_y_labels_T)
  117. all_x_data_E = np.array(all_x_data_E)
  118. all_y_labels_E = np.array(all_y_labels_E)
  119. # %%
  120. # Imprimer la taille totale des données et des étiquettes pour chaque catégorie
  121. print("Taille totale des données .T (all_x_data_T):", all_x_data_T.shape)
  122. print("Taille totale des étiquettes .T (all_y_labels_T):", all_y_labels_T.shape)
  123. print("Taille totale des données .E (all_x_data_E):", all_x_data_E.shape)
  124. print("Taille totale des étiquettes .E (all_y_labels_E):", all_y_labels_E.shape)
  125. # %%
  126. def my_filter_function( data, lowcut, highcut, fs, order):
  127. nyquist = 0.5 * fs
  128. low = lowcut / nyquist
  129. high = highcut / nyquist
  130. sos = butter(order, [low, high], btype='band', output='sos')
  131. # Increase the padlen parameter to increase the filter window size
  132. filtered_data = sosfiltfilt(sos, data, axis=-1, padlen=250)
  133. return filtered_data
  134. # %% [markdown]
  135. # # fonction R-FBCSP
  136. # %%
  137. import numpy as np
  138. from pyriemann.utils.mean import mean_riemann
  139. from scipy.linalg import eigh
  140. def CSP_OVR_optim(epochs, labels, n_components=3):
  141. n_trials, n_channels, _ = epochs.shape
  142. classes = np.unique(labels)
  143. all_features = []
  144. all_W = []
  145. # 1. Calcul des matrices de covariance normalisées
  146. covmats = []
  147. for epoch in epochs:
  148. cov = epoch @ epoch.T
  149. trace = np.trace(cov)
  150. cov_norm = cov / (trace + 1e-10)
  151. covmats.append(cov_norm)
  152. covmats = np.array(covmats)
  153. for c in classes:
  154. y_binary = (labels == c).astype(int)
  155. # 2. Moyennes riemanniennes
  156. cov_A = mean_riemann(covmats[y_binary == 1])
  157. cov_B = mean_riemann(covmats[y_binary == 0])
  158. # 3. Blanchiment robuste
  159. cov_sum = cov_A + cov_B
  160. eigvals, eigvecs = eigh(cov_sum)
  161. eigvals = np.clip(eigvals, 1e-6, None)
  162. P = eigvecs @ np.diag(1.0 / np.sqrt(eigvals)) @ eigvecs.T
  163. # 4. Diagonalisation
  164. S_A = P @ cov_A @ P.T
  165. eigvals_s, eigvecs_s = eigh(S_A)
  166. # 5. Tri des vecteurs
  167. idx = np.argsort(eigvals_s)[::-1]
  168. W = eigvecs_s[:, idx].T @ P
  169. W_c = np.vstack([W[:n_components], W[-n_components:]])
  170. all_W.append(W_c)
  171. # 6. Extraction des log-variances
  172. features = []
  173. for epoch in epochs:
  174. Z = W_c @ epoch
  175. var = np.var(Z, axis=1)
  176. var_sum = np.sum(var)
  177. log_var = np.log((var + 1e-10) / (var_sum + 1e-10))
  178. features.append(log_var)
  179. all_features.append(np.array(features))
  180. # 7. Concatenation des features de tous les classifieurs OVR
  181. final_features = np.concatenate(all_features, axis=1)
  182. return final_features, all_W
  183. # %% [markdown]
  184. # # Global filtering
  185. # %%
  186. order_filter = 6
  187. lowcut = 8.0
  188. highcut = 30.0
  189. fs = 250
  190. n_components_csp = 22
  191. # Train
  192. all_person_featuresT = []
  193. for person_index, (x_data_T, y_labels_T) in enumerate(zip(all_x_data_T, all_y_labels_T)):
  194. filtered_dataT = my_filter_function(x_data_T, lowcut, highcut, fs, order=order_filter)
  195. features_T, _ = CSP_OVR_optim(filtered_dataT, y_labels_T, n_components=n_components_csp)
  196. all_person_featuresT.append(features_T)
  197. # Test
  198. all_person_featuresE = []
  199. for person_index, (x_data_E, y_labels_E) in enumerate(zip(all_x_data_E, all_y_labels_E)):
  200. filtered_dataE = my_filter_function(x_data_E, lowcut, highcut, fs, order=order_filter)
  201. features_E, _ = CSP_OVR_optim(filtered_dataE, y_labels_E, n_components=n_components_csp)
  202. all_person_featuresE.append(features_E)
  203. # Convertir en numpy arrays
  204. all_person_featuresT = np.array(all_person_featuresT)
  205. all_person_featuresE = np.array(all_person_featuresE)
  206. # %%
  207. print(np.shape(all_person_featuresT))
  208. print(np.shape(all_person_featuresE))
  209. # %% [markdown]
  210. # # LDA Classification
  211. # %%
  212. from sklearn.model_selection import KFold
  213. from sklearn.preprocessing import StandardScaler
  214. from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
  215. from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
  216. from sklearn.pipeline import make_pipeline
  217. import numpy as np
  218. # Initialisation des listes
  219. cv_train_accuracies, cv_val_accuracies, cv_test_accuracies = [], [], []
  220. cv_train_precisions, cv_val_precisions, cv_test_precisions = [], [], []
  221. cv_train_recalls, cv_val_recalls, cv_test_recalls = [], [], []
  222. cv_train_f1s, cv_val_f1s, cv_test_f1s = [], [], []
  223. kfold = KFold(n_splits=5, shuffle=True, random_state=42)
  224. for i, (features_train, y_labels_T, features_test, y_labels_E) in enumerate(zip(all_person_featuresT, all_y_labels_T,
  225. all_person_featuresE, all_y_labels_E)):
  226. print(f"\nProcessing subject {i + 1}")
  227. # Pour accumuler les scores par pli
  228. fold_train_accs, fold_val_accs = [], []
  229. fold_train_prec, fold_val_prec = [], []
  230. fold_train_rec, fold_val_rec = [], []
  231. fold_train_f1, fold_val_f1 = [], []
  232. model = make_pipeline(StandardScaler(),
  233. LinearDiscriminantAnalysis(solver='lsqr', shrinkage='auto',
  234. n_components=3, tol=1e-4))
  235. # Validation croisée
  236. for train_idx, val_idx in kfold.split(features_train):
  237. X_train_fold, X_val_fold = features_train[train_idx], features_train[val_idx]
  238. y_train_fold, y_val_fold = y_labels_T[train_idx], y_labels_T[val_idx]
  239. model.fit(X_train_fold, y_train_fold)
  240. # Prédictions
  241. y_train_pred = model.predict(X_train_fold)
  242. y_val_pred = model.predict(X_val_fold)
  243. # Accuracy
  244. fold_train_accs.append(accuracy_score(y_train_fold, y_train_pred))
  245. fold_val_accs.append(accuracy_score(y_val_fold, y_val_pred))
  246. # Precision, Recall, F1
  247. fold_train_prec.append(precision_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  248. fold_val_prec.append(precision_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  249. fold_train_rec.append(recall_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  250. fold_val_rec.append(recall_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  251. fold_train_f1.append(f1_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  252. fold_val_f1.append(f1_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  253. # Réentraînement sur tout l'ensemble d'apprentissage
  254. model.fit(features_train, y_labels_T)
  255. y_test_pred = model.predict(features_test)
  256. # Test scores
  257. test_acc = accuracy_score(y_labels_E, y_test_pred)
  258. test_prec = precision_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  259. test_rec = recall_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  260. test_f1 = f1_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  261. # Moyennes sur les plis
  262. cv_train_accuracies.append(np.mean(fold_train_accs))
  263. cv_val_accuracies.append(np.mean(fold_val_accs))
  264. cv_test_accuracies.append(test_acc)
  265. cv_train_precisions.append(np.mean(fold_train_prec))
  266. cv_val_precisions.append(np.mean(fold_val_prec))
  267. cv_test_precisions.append(test_prec)
  268. cv_train_recalls.append(np.mean(fold_train_rec))
  269. cv_val_recalls.append(np.mean(fold_val_rec))
  270. cv_test_recalls.append(test_rec)
  271. cv_train_f1s.append(np.mean(fold_train_f1))
  272. cv_val_f1s.append(np.mean(fold_val_f1))
  273. cv_test_f1s.append(test_f1)
  274. # %%
  275. import pandas as pd
  276. # Conversion des scores en pourcentages
  277. test_accuracies_percent = [acc * 100 for acc in cv_test_accuracies]
  278. test_precisions_percent = [prec * 100 for prec in cv_test_precisions]
  279. test_recalls_percent = [rec * 100 for rec in cv_test_recalls]
  280. test_f1_scores_percent = [f1 * 100 for f1 in cv_test_f1s]
  281. # Création d'un DataFrame avec les scores
  282. results_df = pd.DataFrame({
  283. "Subject": [f"Person {i+1}" for i in range(len(test_accuracies_percent))],
  284. "Accuracy (%)": test_accuracies_percent,
  285. "Precision (%)": test_precisions_percent,
  286. "Recall (%)": test_recalls_percent,
  287. "F1-score (%)": test_f1_scores_percent
  288. })
  289. # Arrondir les valeurs à 2 décimales
  290. results_df = results_df.round(2)
  291. # Affichage du tableau
  292. print("\nTest Metrics per Subject:\n")
  293. print(results_df.to_string(index=False))
  294. # Moyennes globales
  295. average_results = results_df.mean(numeric_only=True)
  296. print("\nAverage Test Metrics Across All Subjects:")
  297. print(f"Accuracy: {average_results['Accuracy (%)']:.2f}%")
  298. print(f"Precision: {average_results['Precision (%)']:.2f}%")
  299. print(f"Recall: {average_results['Recall (%)']:.2f}%")
  300. print(f"F1-score: {average_results['F1-score (%)']:.2f}%")
  301. # %% [markdown]
  302. # # RFC Classification
  303. # %%
  304. from sklearn.model_selection import KFold
  305. from sklearn.preprocessing import StandardScaler
  306. from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
  307. from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
  308. from sklearn.pipeline import make_pipeline
  309. import numpy as np
  310. from sklearn.ensemble import RandomForestClassifier
  311. # Initialisation des listes
  312. cv_train_accuracies, cv_val_accuracies, cv_test_accuracies = [], [], []
  313. cv_train_precisions, cv_val_precisions, cv_test_precisions = [], [], []
  314. cv_train_recalls, cv_val_recalls, cv_test_recalls = [], [], []
  315. cv_train_f1s, cv_val_f1s, cv_test_f1s = [], [], []
  316. kfold = KFold(n_splits=5, shuffle=True, random_state=42)
  317. for i, (features_train, y_labels_T, features_test, y_labels_E) in enumerate(zip(all_person_featuresT, all_y_labels_T,
  318. all_person_featuresE, all_y_labels_E)):
  319. print(f"\nProcessing subject {i + 1}")
  320. # Pour accumuler les scores par pli
  321. fold_train_accs, fold_val_accs = [], []
  322. fold_train_prec, fold_val_prec = [], []
  323. fold_train_rec, fold_val_rec = [], []
  324. fold_train_f1, fold_val_f1 = [], []
  325. model = make_pipeline(StandardScaler(),
  326. RandomForestClassifier(n_estimators=100, class_weight='balanced', random_state=42))
  327. # Validation croisée
  328. for train_idx, val_idx in kfold.split(features_train):
  329. X_train_fold, X_val_fold = features_train[train_idx], features_train[val_idx]
  330. y_train_fold, y_val_fold = y_labels_T[train_idx], y_labels_T[val_idx]
  331. model.fit(X_train_fold, y_train_fold)
  332. # Prédictions
  333. y_train_pred = model.predict(X_train_fold)
  334. y_val_pred = model.predict(X_val_fold)
  335. # Accuracy
  336. fold_train_accs.append(accuracy_score(y_train_fold, y_train_pred))
  337. fold_val_accs.append(accuracy_score(y_val_fold, y_val_pred))
  338. # Precision, Recall, F1
  339. fold_train_prec.append(precision_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  340. fold_val_prec.append(precision_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  341. fold_train_rec.append(recall_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  342. fold_val_rec.append(recall_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  343. fold_train_f1.append(f1_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  344. fold_val_f1.append(f1_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  345. # Réentraînement sur tout l'ensemble d'apprentissage
  346. model.fit(features_train, y_labels_T)
  347. y_test_pred = model.predict(features_test)
  348. # Test scores
  349. test_acc = accuracy_score(y_labels_E, y_test_pred)
  350. test_prec = precision_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  351. test_rec = recall_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  352. test_f1 = f1_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  353. # Moyennes sur les plis
  354. cv_train_accuracies.append(np.mean(fold_train_accs))
  355. cv_val_accuracies.append(np.mean(fold_val_accs))
  356. cv_test_accuracies.append(test_acc)
  357. cv_train_precisions.append(np.mean(fold_train_prec))
  358. cv_val_precisions.append(np.mean(fold_val_prec))
  359. cv_test_precisions.append(test_prec)
  360. cv_train_recalls.append(np.mean(fold_train_rec))
  361. cv_val_recalls.append(np.mean(fold_val_rec))
  362. cv_test_recalls.append(test_rec)
  363. cv_train_f1s.append(np.mean(fold_train_f1))
  364. cv_val_f1s.append(np.mean(fold_val_f1))
  365. cv_test_f1s.append(test_f1)
  366. # %%
  367. import pandas as pd
  368. # Conversion des scores en pourcentages
  369. test_accuracies_percent = [acc * 100 for acc in cv_test_accuracies]
  370. test_precisions_percent = [prec * 100 for prec in cv_test_precisions]
  371. test_recalls_percent = [rec * 100 for rec in cv_test_recalls]
  372. test_f1_scores_percent = [f1 * 100 for f1 in cv_test_f1s]
  373. # Création d'un DataFrame avec les scores
  374. results_df = pd.DataFrame({
  375. "Subject": [f"Person {i+1}" for i in range(len(test_accuracies_percent))],
  376. "Accuracy (%)": test_accuracies_percent,
  377. "Precision (%)": test_precisions_percent,
  378. "Recall (%)": test_recalls_percent,
  379. "F1-score (%)": test_f1_scores_percent
  380. })
  381. # Arrondir les valeurs à 2 décimales
  382. results_df = results_df.round(2)
  383. # Affichage du tableau
  384. print("\nTest Metrics per Subject:\n")
  385. print(results_df.to_string(index=False))
  386. # Moyennes globales
  387. average_results = results_df.mean(numeric_only=True)
  388. print("\nAverage Test Metrics Across All Subjects:")
  389. print(f"Accuracy: {average_results['Accuracy (%)']:.2f}%")
  390. print(f"Precision: {average_results['Precision (%)']:.2f}%")
  391. print(f"Recall: {average_results['Recall (%)']:.2f}%")
  392. print(f"F1-score: {average_results['F1-score (%)']:.2f}%")
  393. # %% [markdown]
  394. # # MLP classification
  395. # %%
  396. from sklearn.model_selection import KFold
  397. from sklearn.preprocessing import StandardScaler
  398. from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
  399. from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
  400. from sklearn.pipeline import make_pipeline
  401. import numpy as np
  402. from sklearn.ensemble import RandomForestClassifier
  403. from sklearn.neural_network import MLPClassifier
  404. # Initialisation des listes
  405. cv_train_accuracies, cv_val_accuracies, cv_test_accuracies = [], [], []
  406. cv_train_precisions, cv_val_precisions, cv_test_precisions = [], [], []
  407. cv_train_recalls, cv_val_recalls, cv_test_recalls = [], [], []
  408. cv_train_f1s, cv_val_f1s, cv_test_f1s = [], [], []
  409. kfold = KFold(n_splits=5, shuffle=True, random_state=42)
  410. for i, (features_train, y_labels_T, features_test, y_labels_E) in enumerate(zip(all_person_featuresT, all_y_labels_T,
  411. all_person_featuresE, all_y_labels_E)):
  412. print(f"\nProcessing subject {i + 1}")
  413. # Pour accumuler les scores par pli
  414. fold_train_accs, fold_val_accs = [], []
  415. fold_train_prec, fold_val_prec = [], []
  416. fold_train_rec, fold_val_rec = [], []
  417. fold_train_f1, fold_val_f1 = [], []
  418. model = make_pipeline(StandardScaler(),
  419. MLPClassifier(hidden_layer_sizes=(100,), max_iter=300, activation='tanh', solver='adam',
  420. learning_rate_init=0.001, random_state=42 ))
  421. # Validation croisée
  422. for train_idx, val_idx in kfold.split(features_train):
  423. X_train_fold, X_val_fold = features_train[train_idx], features_train[val_idx]
  424. y_train_fold, y_val_fold = y_labels_T[train_idx], y_labels_T[val_idx]
  425. model.fit(X_train_fold, y_train_fold)
  426. # Prédictions
  427. y_train_pred = model.predict(X_train_fold)
  428. y_val_pred = model.predict(X_val_fold)
  429. # Accuracy
  430. fold_train_accs.append(accuracy_score(y_train_fold, y_train_pred))
  431. fold_val_accs.append(accuracy_score(y_val_fold, y_val_pred))
  432. # Precision, Recall, F1
  433. fold_train_prec.append(precision_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  434. fold_val_prec.append(precision_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  435. fold_train_rec.append(recall_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  436. fold_val_rec.append(recall_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  437. fold_train_f1.append(f1_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  438. fold_val_f1.append(f1_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  439. # Réentraînement sur tout l'ensemble d'apprentissage
  440. model.fit(features_train, y_labels_T)
  441. y_test_pred = model.predict(features_test)
  442. # Test scores
  443. test_acc = accuracy_score(y_labels_E, y_test_pred)
  444. test_prec = precision_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  445. test_rec = recall_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  446. test_f1 = f1_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  447. # Moyennes sur les plis
  448. cv_train_accuracies.append(np.mean(fold_train_accs))
  449. cv_val_accuracies.append(np.mean(fold_val_accs))
  450. cv_test_accuracies.append(test_acc)
  451. cv_train_precisions.append(np.mean(fold_train_prec))
  452. cv_val_precisions.append(np.mean(fold_val_prec))
  453. cv_test_precisions.append(test_prec)
  454. cv_train_recalls.append(np.mean(fold_train_rec))
  455. cv_val_recalls.append(np.mean(fold_val_rec))
  456. cv_test_recalls.append(test_rec)
  457. cv_train_f1s.append(np.mean(fold_train_f1))
  458. cv_val_f1s.append(np.mean(fold_val_f1))
  459. cv_test_f1s.append(test_f1)
  460. # %%
  461. import pandas as pd
  462. # Conversion des scores en pourcentages
  463. test_accuracies_percent = [acc * 100 for acc in cv_test_accuracies]
  464. test_precisions_percent = [prec * 100 for prec in cv_test_precisions]
  465. test_recalls_percent = [rec * 100 for rec in cv_test_recalls]
  466. test_f1_scores_percent = [f1 * 100 for f1 in cv_test_f1s]
  467. # Création d'un DataFrame avec les scores
  468. results_df = pd.DataFrame({
  469. "Subject": [f"Person {i+1}" for i in range(len(test_accuracies_percent))],
  470. "Accuracy (%)": test_accuracies_percent,
  471. "Precision (%)": test_precisions_percent,
  472. "Recall (%)": test_recalls_percent,
  473. "F1-score (%)": test_f1_scores_percent
  474. })
  475. # Arrondir les valeurs à 2 décimales
  476. results_df = results_df.round(2)
  477. # Affichage du tableau
  478. print("\nTest Metrics per Subject:\n")
  479. print(results_df.to_string(index=False))
  480. # Moyennes globales
  481. average_results = results_df.mean(numeric_only=True)
  482. print("\nAverage Test Metrics Across All Subjects:")
  483. print(f"Accuracy: {average_results['Accuracy (%)']:.2f}%")
  484. print(f"Precision: {average_results['Precision (%)']:.2f}%")
  485. print(f"Recall: {average_results['Recall (%)']:.2f}%")
  486. print(f"F1-score: {average_results['F1-score (%)']:.2f}%")
  487. # %% [markdown]
  488. # # Multiband filtering
  489. # %%
  490. def process_bands(data, bands, fs, order, n_components, labels):
  491. all_features = []
  492. for band in bands:
  493. lowcut, highcut = band
  494. # Étape 1 : Filtrage des données pour la bande actuelle
  495. filtered_data = my_filter_function(data, lowcut, highcut, fs, order)
  496. # Étape 2 : Extraction des caractéristiques CSP
  497. csp_features, _ = CSP_OVR_optim(filtered_data, labels, n_components)
  498. # Ajouter les caractéristiques de cette bande
  499. all_features.append(csp_features)
  500. # Étape 3 : Combiner les caractéristiques de toutes les bandes
  501. combined_features = np.concatenate(all_features, axis=1)
  502. return combined_features
  503. # %%
  504. # %%capture
  505. all_person_featuresT = []
  506. all_person_featuresE = []
  507. order_filter=6
  508. lowcut =8.0
  509. highcut =30.0
  510. fs=250
  511. n_components_csp=22
  512. bands = [(8, 12), (12, 30), (30, 45)]
  513. i=0
  514. # Loop on each person for Train data
  515. for person_index, (x_data_T, y_labels_T) in enumerate(zip(all_x_data_T, all_y_labels_T)):
  516. i=i+1
  517. print(i)
  518. # CSPPP
  519. CSP_T= process_bands(x_data_T, bands, fs, order_filter, n_components_csp, y_labels_T)
  520. all_person_featuresT.append(CSP_T)
  521. i=0
  522. # Loop on each person for test data
  523. for person_index, (x_data_E, y_labels_E) in enumerate(zip(all_x_data_E, all_y_labels_E)):
  524. i=i+1
  525. print(i)
  526. # CSPPP
  527. CSP_E=process_bands(x_data_E, bands, fs, order_filter, n_components_csp, y_labels_E)
  528. all_person_featuresE.append(CSP_E)
  529. # Convertissez les listes en tableaux numpy
  530. all_person_featuresT = np.array(all_person_featuresT)
  531. all_person_featuresE = np.array(all_person_featuresE)
  532. # %% [markdown]
  533. # # LDA classification
  534. # %%
  535. from sklearn.model_selection import KFold
  536. from sklearn.preprocessing import StandardScaler
  537. from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
  538. from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
  539. from sklearn.pipeline import make_pipeline
  540. import numpy as np
  541. # Initialisation des listes
  542. cv_train_accuracies, cv_val_accuracies, cv_test_accuracies = [], [], []
  543. cv_train_precisions, cv_val_precisions, cv_test_precisions = [], [], []
  544. cv_train_recalls, cv_val_recalls, cv_test_recalls = [], [], []
  545. cv_train_f1s, cv_val_f1s, cv_test_f1s = [], [], []
  546. kfold = KFold(n_splits=5, shuffle=True, random_state=42)
  547. for i, (features_train, y_labels_T, features_test, y_labels_E) in enumerate(zip(all_person_featuresT, all_y_labels_T,
  548. all_person_featuresE, all_y_labels_E)):
  549. print(f"\nProcessing subject {i + 1}")
  550. # Pour accumuler les scores par pli
  551. fold_train_accs, fold_val_accs = [], []
  552. fold_train_prec, fold_val_prec = [], []
  553. fold_train_rec, fold_val_rec = [], []
  554. fold_train_f1, fold_val_f1 = [], []
  555. model = make_pipeline(StandardScaler(),
  556. LinearDiscriminantAnalysis(solver='lsqr', shrinkage='auto',
  557. n_components=3, tol=1e-4))
  558. # Validation croisée
  559. for train_idx, val_idx in kfold.split(features_train):
  560. X_train_fold, X_val_fold = features_train[train_idx], features_train[val_idx]
  561. y_train_fold, y_val_fold = y_labels_T[train_idx], y_labels_T[val_idx]
  562. model.fit(X_train_fold, y_train_fold)
  563. # Prédictions
  564. y_train_pred = model.predict(X_train_fold)
  565. y_val_pred = model.predict(X_val_fold)
  566. # Accuracy
  567. fold_train_accs.append(accuracy_score(y_train_fold, y_train_pred))
  568. fold_val_accs.append(accuracy_score(y_val_fold, y_val_pred))
  569. # Precision, Recall, F1
  570. fold_train_prec.append(precision_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  571. fold_val_prec.append(precision_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  572. fold_train_rec.append(recall_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  573. fold_val_rec.append(recall_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  574. fold_train_f1.append(f1_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  575. fold_val_f1.append(f1_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  576. # Réentraînement sur tout l'ensemble d'apprentissage
  577. model.fit(features_train, y_labels_T)
  578. y_test_pred = model.predict(features_test)
  579. # Test scores
  580. test_acc = accuracy_score(y_labels_E, y_test_pred)
  581. test_prec = precision_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  582. test_rec = recall_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  583. test_f1 = f1_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  584. # Moyennes sur les plis
  585. cv_train_accuracies.append(np.mean(fold_train_accs))
  586. cv_val_accuracies.append(np.mean(fold_val_accs))
  587. cv_test_accuracies.append(test_acc)
  588. cv_train_precisions.append(np.mean(fold_train_prec))
  589. cv_val_precisions.append(np.mean(fold_val_prec))
  590. cv_test_precisions.append(test_prec)
  591. cv_train_recalls.append(np.mean(fold_train_rec))
  592. cv_val_recalls.append(np.mean(fold_val_rec))
  593. cv_test_recalls.append(test_rec)
  594. cv_train_f1s.append(np.mean(fold_train_f1))
  595. cv_val_f1s.append(np.mean(fold_val_f1))
  596. cv_test_f1s.append(test_f1)
  597. # %%
  598. import pandas as pd
  599. # Conversion des scores en pourcentages
  600. test_accuracies_percent = [acc * 100 for acc in cv_test_accuracies]
  601. test_precisions_percent = [prec * 100 for prec in cv_test_precisions]
  602. test_recalls_percent = [rec * 100 for rec in cv_test_recalls]
  603. test_f1_scores_percent = [f1 * 100 for f1 in cv_test_f1s]
  604. # Création d'un DataFrame avec les scores
  605. results_df = pd.DataFrame({
  606. "Subject": [f"Person {i+1}" for i in range(len(test_accuracies_percent))],
  607. "Accuracy (%)": test_accuracies_percent,
  608. "Precision (%)": test_precisions_percent,
  609. "Recall (%)": test_recalls_percent,
  610. "F1-score (%)": test_f1_scores_percent
  611. })
  612. # Arrondir les valeurs à 2 décimales
  613. results_df = results_df.round(2)
  614. # Affichage du tableau
  615. print("\nTest Metrics per Subject:\n")
  616. print(results_df.to_string(index=False))
  617. # Moyennes globales
  618. average_results = results_df.mean(numeric_only=True)
  619. print("\nAverage Test Metrics Across All Subjects:")
  620. print(f"Accuracy: {average_results['Accuracy (%)']:.2f}%")
  621. print(f"Precision: {average_results['Precision (%)']:.2f}%")
  622. print(f"Recall: {average_results['Recall (%)']:.2f}%")
  623. print(f"F1-score: {average_results['F1-score (%)']:.2f}%")
  624. # %% [markdown]
  625. # # RFC classification
  626. # %%
  627. from sklearn.model_selection import KFold
  628. from sklearn.preprocessing import StandardScaler
  629. from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
  630. from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
  631. from sklearn.pipeline import make_pipeline
  632. import numpy as np
  633. from sklearn.ensemble import RandomForestClassifier
  634. # Initialisation des listes
  635. cv_train_accuracies, cv_val_accuracies, cv_test_accuracies = [], [], []
  636. cv_train_precisions, cv_val_precisions, cv_test_precisions = [], [], []
  637. cv_train_recalls, cv_val_recalls, cv_test_recalls = [], [], []
  638. cv_train_f1s, cv_val_f1s, cv_test_f1s = [], [], []
  639. kfold = KFold(n_splits=5, shuffle=True, random_state=42)
  640. for i, (features_train, y_labels_T, features_test, y_labels_E) in enumerate(zip(all_person_featuresT, all_y_labels_T,
  641. all_person_featuresE, all_y_labels_E)):
  642. print(f"\nProcessing subject {i + 1}")
  643. # Pour accumuler les scores par pli
  644. fold_train_accs, fold_val_accs = [], []
  645. fold_train_prec, fold_val_prec = [], []
  646. fold_train_rec, fold_val_rec = [], []
  647. fold_train_f1, fold_val_f1 = [], []
  648. model = make_pipeline(StandardScaler(),
  649. RandomForestClassifier(n_estimators=100, random_state=42))
  650. # Validation croisée
  651. for train_idx, val_idx in kfold.split(features_train):
  652. X_train_fold, X_val_fold = features_train[train_idx], features_train[val_idx]
  653. y_train_fold, y_val_fold = y_labels_T[train_idx], y_labels_T[val_idx]
  654. model.fit(X_train_fold, y_train_fold)
  655. # Prédictions
  656. y_train_pred = model.predict(X_train_fold)
  657. y_val_pred = model.predict(X_val_fold)
  658. # Accuracy
  659. fold_train_accs.append(accuracy_score(y_train_fold, y_train_pred))
  660. fold_val_accs.append(accuracy_score(y_val_fold, y_val_pred))
  661. # Precision, Recall, F1
  662. fold_train_prec.append(precision_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  663. fold_val_prec.append(precision_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  664. fold_train_rec.append(recall_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  665. fold_val_rec.append(recall_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  666. fold_train_f1.append(f1_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  667. fold_val_f1.append(f1_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  668. # Réentraînement sur tout l'ensemble d'apprentissage
  669. model.fit(features_train, y_labels_T)
  670. y_test_pred = model.predict(features_test)
  671. # Test scores
  672. test_acc = accuracy_score(y_labels_E, y_test_pred)
  673. test_prec = precision_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  674. test_rec = recall_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  675. test_f1 = f1_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  676. # Moyennes sur les plis
  677. cv_train_accuracies.append(np.mean(fold_train_accs))
  678. cv_val_accuracies.append(np.mean(fold_val_accs))
  679. cv_test_accuracies.append(test_acc)
  680. cv_train_precisions.append(np.mean(fold_train_prec))
  681. cv_val_precisions.append(np.mean(fold_val_prec))
  682. cv_test_precisions.append(test_prec)
  683. cv_train_recalls.append(np.mean(fold_train_rec))
  684. cv_val_recalls.append(np.mean(fold_val_rec))
  685. cv_test_recalls.append(test_rec)
  686. cv_train_f1s.append(np.mean(fold_train_f1))
  687. cv_val_f1s.append(np.mean(fold_val_f1))
  688. cv_test_f1s.append(test_f1)
  689. # %%
  690. import pandas as pd
  691. # Conversion des scores en pourcentages
  692. test_accuracies_percent = [acc * 100 for acc in cv_test_accuracies]
  693. test_precisions_percent = [prec * 100 for prec in cv_test_precisions]
  694. test_recalls_percent = [rec * 100 for rec in cv_test_recalls]
  695. test_f1_scores_percent = [f1 * 100 for f1 in cv_test_f1s]
  696. # Création d'un DataFrame avec les scores
  697. results_df = pd.DataFrame({
  698. "Subject": [f"Person {i+1}" for i in range(len(test_accuracies_percent))],
  699. "Accuracy (%)": test_accuracies_percent,
  700. "Precision (%)": test_precisions_percent,
  701. "Recall (%)": test_recalls_percent,
  702. "F1-score (%)": test_f1_scores_percent
  703. })
  704. # Arrondir les valeurs à 2 décimales
  705. results_df = results_df.round(2)
  706. # Affichage du tableau
  707. print("\nTest Metrics per Subject:\n")
  708. print(results_df.to_string(index=False))
  709. # Moyennes globales
  710. average_results = results_df.mean(numeric_only=True)
  711. print("\nAverage Test Metrics Across All Subjects:")
  712. print(f"Accuracy: {average_results['Accuracy (%)']:.2f}%")
  713. print(f"Precision: {average_results['Precision (%)']:.2f}%")
  714. print(f"Recall: {average_results['Recall (%)']:.2f}%")
  715. print(f"F1-score: {average_results['F1-score (%)']:.2f}%")
  716. # %% [markdown]
  717. # # MLP classification
  718. # %%
  719. from sklearn.model_selection import KFold
  720. from sklearn.preprocessing import StandardScaler
  721. from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
  722. from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
  723. from sklearn.pipeline import make_pipeline
  724. import numpy as np
  725. from sklearn.ensemble import RandomForestClassifier
  726. from sklearn.neural_network import MLPClassifier
  727. # Initialisation des listes
  728. cv_train_accuracies, cv_val_accuracies, cv_test_accuracies = [], [], []
  729. cv_train_precisions, cv_val_precisions, cv_test_precisions = [], [], []
  730. cv_train_recalls, cv_val_recalls, cv_test_recalls = [], [], []
  731. cv_train_f1s, cv_val_f1s, cv_test_f1s = [], [], []
  732. kfold = KFold(n_splits=5, shuffle=True, random_state=42)
  733. for i, (features_train, y_labels_T, features_test, y_labels_E) in enumerate(zip(all_person_featuresT, all_y_labels_T,
  734. all_person_featuresE, all_y_labels_E)):
  735. print(f"\nProcessing subject {i + 1}")
  736. # Pour accumuler les scores par pli
  737. fold_train_accs, fold_val_accs = [], []
  738. fold_train_prec, fold_val_prec = [], []
  739. fold_train_rec, fold_val_rec = [], []
  740. fold_train_f1, fold_val_f1 = [], []
  741. model = make_pipeline(StandardScaler(),
  742. MLPClassifier(hidden_layer_sizes=(100,), max_iter=300, activation='tanh', solver='adam',
  743. learning_rate_init=0.001, random_state=42 ))
  744. # Validation croisée
  745. for train_idx, val_idx in kfold.split(features_train):
  746. X_train_fold, X_val_fold = features_train[train_idx], features_train[val_idx]
  747. y_train_fold, y_val_fold = y_labels_T[train_idx], y_labels_T[val_idx]
  748. model.fit(X_train_fold, y_train_fold)
  749. # Prédictions
  750. y_train_pred = model.predict(X_train_fold)
  751. y_val_pred = model.predict(X_val_fold)
  752. # Accuracy
  753. fold_train_accs.append(accuracy_score(y_train_fold, y_train_pred))
  754. fold_val_accs.append(accuracy_score(y_val_fold, y_val_pred))
  755. # Precision, Recall, F1
  756. fold_train_prec.append(precision_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  757. fold_val_prec.append(precision_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  758. fold_train_rec.append(recall_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  759. fold_val_rec.append(recall_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  760. fold_train_f1.append(f1_score(y_train_fold, y_train_pred, average='weighted', zero_division=0))
  761. fold_val_f1.append(f1_score(y_val_fold, y_val_pred, average='weighted', zero_division=0))
  762. # Réentraînement sur tout l'ensemble d'apprentissage
  763. model.fit(features_train, y_labels_T)
  764. y_test_pred = model.predict(features_test)
  765. # Test scores
  766. test_acc = accuracy_score(y_labels_E, y_test_pred)
  767. test_prec = precision_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  768. test_rec = recall_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  769. test_f1 = f1_score(y_labels_E, y_test_pred, average='weighted', zero_division=0)
  770. # Moyennes sur les plis
  771. cv_train_accuracies.append(np.mean(fold_train_accs))
  772. cv_val_accuracies.append(np.mean(fold_val_accs))
  773. cv_test_accuracies.append(test_acc)
  774. cv_train_precisions.append(np.mean(fold_train_prec))
  775. cv_val_precisions.append(np.mean(fold_val_prec))
  776. cv_test_precisions.append(test_prec)
  777. cv_train_recalls.append(np.mean(fold_train_rec))
  778. cv_val_recalls.append(np.mean(fold_val_rec))
  779. cv_test_recalls.append(test_rec)
  780. cv_train_f1s.append(np.mean(fold_train_f1))
  781. cv_val_f1s.append(np.mean(fold_val_f1))
  782. cv_test_f1s.append(test_f1)
  783. # %%
  784. import pandas as pd
  785. # Conversion des scores en pourcentages
  786. test_accuracies_percent = [acc * 100 for acc in cv_test_accuracies]
  787. test_precisions_percent = [prec * 100 for prec in cv_test_precisions]
  788. test_recalls_percent = [rec * 100 for rec in cv_test_recalls]
  789. test_f1_scores_percent = [f1 * 100 for f1 in cv_test_f1s]
  790. # Création d'un DataFrame avec les scores
  791. results_df = pd.DataFrame({
  792. "Subject": [f"Person {i+1}" for i in range(len(test_accuracies_percent))],
  793. "Accuracy (%)": test_accuracies_percent,
  794. "Precision (%)": test_precisions_percent,
  795. "Recall (%)": test_recalls_percent,
  796. "F1-score (%)": test_f1_scores_percent
  797. })
  798. # Arrondir les valeurs à 2 décimales
  799. results_df = results_df.round(2)
  800. # Affichage du tableau
  801. print("\nTest Metrics per Subject:\n")
  802. print(results_df.to_string(index=False))
  803. # Moyennes globales
  804. average_results = results_df.mean(numeric_only=True)
  805. print("\nAverage Test Metrics Across All Subjects:")
  806. print(f"Accuracy: {average_results['Accuracy (%)']:.2f}%")
  807. print(f"Precision: {average_results['Precision (%)']:.2f}%")
  808. print(f"Recall: {average_results['Recall (%)']:.2f}%")
  809. print(f"F1-score: {average_results['F1-score (%)']:.2f}%")
  810. # %% [markdown]
  811. # # votting simple
  812. # %%
  813. from sklearn.ensemble import VotingClassifier
  814. from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
  815. from sklearn.neural_network import MLPClassifier
  816. from sklearn.ensemble import RandomForestClassifier
  817. from sklearn.pipeline import make_pipeline
  818. from sklearn.preprocessing import StandardScaler
  819. from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
  820. # Initialisation des listes pour stocker les performances
  821. voting_accuracies, voting_precisions, voting_recalls, voting_f1_scores = [], [], [], []
  822. # Boucle sur chaque personne
  823. for i, (X_train, y_train, X_test, y_test) in enumerate(zip(all_person_featuresT, all_y_labels_T,
  824. all_person_featuresE, all_y_labels_E)):
  825. print(f"\n>>> Voting classifier - Subject {i + 1}")
  826. # Définition des 3 classificateurs de base
  827. lda = make_pipeline(StandardScaler(), LinearDiscriminantAnalysis(solver='lsqr', shrinkage='auto', n_components=3, tol=1e-4))
  828. rf = make_pipeline(StandardScaler(), RandomForestClassifier(n_estimators=100, random_state=42))
  829. mlp = make_pipeline(StandardScaler(),MLPClassifier(hidden_layer_sizes=(100,), max_iter=300, activation='tanh', solver='adam',
  830. learning_rate_init=0.001, random_state=42 ))
  831. # Voting classifier (majority vote)
  832. voting_clf = VotingClassifier(estimators=[
  833. ('lda', lda),
  834. ('mlp', mlp),
  835. ('rf', rf)
  836. ], voting='hard')
  837. # Entraînement
  838. voting_clf.fit(X_train, y_train)
  839. # Prédiction
  840. y_pred = voting_clf.predict(X_test)
  841. # Évaluation
  842. acc = accuracy_score(y_test, y_pred)
  843. prec = precision_score(y_test, y_pred, average='weighted', zero_division=0)
  844. rec = recall_score(y_test, y_pred, average='weighted', zero_division=0)
  845. f1 = f1_score(y_test, y_pred, average='weighted', zero_division=0)
  846. # Stockage
  847. voting_accuracies.append(acc)
  848. voting_precisions.append(prec)
  849. voting_recalls.append(rec)
  850. voting_f1_scores.append(f1)
  851. # Résultats moyens
  852. print("\n>>> Voting Classifier - Average Test Metrics:")
  853. print(f"Accuracy: {np.mean(voting_accuracies) * 100:.2f}%")
  854. print(f"Precision: {np.mean(voting_precisions) * 100:.2f}%")
  855. print(f"Recall: {np.mean(voting_recalls) * 100:.2f}%")
  856. print(f"F1-score: {np.mean(voting_f1_scores) * 100:.2f}%")
  857. # %% [markdown]
  858. # # votting+ Cross validation
  859. # %%
  860. from sklearn.ensemble import VotingClassifier
  861. from sklearn.model_selection import cross_val_score
  862. from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
  863. # Initialisation des métriques
  864. metrics = {
  865. 'subject': [], 'acc': [], 'prec': [], 'rec': [], 'f1': [],
  866. 'cv_acc_mean': [], 'cv_acc_std': []
  867. }
  868. for i, (X_train, y_train, X_test, y_test) in enumerate(zip(all_person_featuresT, all_y_labels_T,
  869. all_person_featuresE, all_y_labels_E)):
  870. print(f"\n>>> Subject {i + 1}")
  871. # Définition des classifieurs
  872. lda = make_pipeline(StandardScaler(), LinearDiscriminantAnalysis(solver='lsqr', shrinkage='auto'))
  873. rf = make_pipeline(StandardScaler(), RandomForestClassifier(n_estimators=100, class_weight='balanced', random_state=42))
  874. mlp = make_pipeline(StandardScaler(), MLPClassifier(hidden_layer_sizes=(100,), max_iter=300, early_stopping=True, random_state=42))
  875. # Voting Classifier
  876. voting_clf = VotingClassifier(
  877. estimators=[('lda', lda), ('rf', rf), ('mlp', mlp)],
  878. voting='soft' # ou 'hard'
  879. )
  880. # Validation croisée
  881. cv_scores = cross_val_score(voting_clf, X_train, y_train, cv=5, scoring='accuracy')
  882. metrics['cv_acc_mean'].append(np.mean(cv_scores))
  883. metrics['cv_acc_std'].append(np.std(cv_scores))
  884. # Entraînement final
  885. voting_clf.fit(X_train, y_train)
  886. y_pred = voting_clf.predict(X_test)
  887. # Stockage des résultats
  888. metrics['subject'].append(i+1)
  889. metrics['acc'].append(accuracy_score(y_test, y_pred))
  890. metrics['prec'].append(precision_score(y_test, y_pred, average='weighted', zero_division=0))
  891. metrics['rec'].append(recall_score(y_test, y_pred, average='weighted', zero_division=0))
  892. metrics['f1'].append(f1_score(y_test, y_pred, average='weighted', zero_division=0))
  893. # Affichage détaillé
  894. print(f"CV Accuracy: {metrics['cv_acc_mean'][-1]:.3f} ± {metrics['cv_acc_std'][-1]:.3f}")
  895. print(f"Test Accuracy: {metrics['acc'][-1]:.3f}")
  896. # Résultats finaux
  897. print("\n>>> Final Metrics <<<")
  898. print(f"Mean Test Accuracy: {np.mean(metrics['acc']) * 100:.2f}%")
  899. print(f"Mean Test Precision: {np.mean(metrics['prec']) * 100:.2f}%")
  900. print(f"Mean Test Recall: {np.mean(metrics['rec']) * 100:.2f}%")
  901. print(f"Mean Test F1-score: {np.mean(metrics['f1']) * 100:.2f}%")
  902. # %%
  903. from sklearn.ensemble import VotingClassifier
  904. from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
  905. from sklearn.neural_network import MLPClassifier
  906. from sklearn.ensemble import RandomForestClassifier
  907. from sklearn.pipeline import make_pipeline
  908. from sklearn.preprocessing import StandardScaler
  909. from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
  910. from collections import Counter
  911. import numpy as np
  912. # Initialisation des listes pour stocker les performances
  913. voting_accuracies, voting_precisions, voting_recalls, voting_f1_scores = [], [], [], []
  914. # Boucle sur chaque personne
  915. for i, (X_train, y_train, X_test, y_test) in enumerate(zip(all_person_featuresT, all_y_labels_T,
  916. all_person_featuresE, all_y_labels_E)):
  917. print(f"\n>>> Voting classifier - Subject {i + 1}")
  918. # Définition des 3 classificateurs de base
  919. lda = make_pipeline(StandardScaler(), LinearDiscriminantAnalysis(solver='lsqr', shrinkage='auto'))
  920. rf = make_pipeline(StandardScaler(), RandomForestClassifier(n_estimators=100, random_state=42))
  921. mlp = make_pipeline(StandardScaler(), MLPClassifier(hidden_layer_sizes=(100,), max_iter=300,
  922. activation='tanh', solver='adam',
  923. learning_rate_init=0.001, random_state=42))
  924. # Voting classifier (majority vote)
  925. voting_clf = VotingClassifier(estimators=[
  926. ('lda', lda),
  927. ('mlp', mlp),
  928. ('rf', rf)
  929. ], voting='hard') # Pour vote majoritaire
  930. # Entraînement
  931. voting_clf.fit(X_train, y_train)
  932. # Prédiction finale
  933. y_pred = voting_clf.predict(X_test)
  934. # Évaluation
  935. acc = accuracy_score(y_test, y_pred)
  936. prec = precision_score(y_test, y_pred, average='weighted', zero_division=0)
  937. rec = recall_score(y_test, y_pred, average='weighted', zero_division=0)
  938. f1 = f1_score(y_test, y_pred, average='weighted', zero_division=0)
  939. # Stockage des métriques
  940. voting_accuracies.append(acc)
  941. voting_precisions.append(prec)
  942. voting_recalls.append(rec)
  943. voting_f1_scores.append(f1)
  944. # Prédictions individuelles
  945. lda_pred = voting_clf.named_estimators_['lda'].predict(X_test)
  946. mlp_pred = voting_clf.named_estimators_['mlp'].predict(X_test)
  947. rf_pred = voting_clf.named_estimators_['rf'].predict(X_test)
  948. # Affichage des votes pour les 10 premiers échantillons
  949. print("\nIndividual classifier decisions (first 10 samples) :")
  950. print("Index | LDA | MLP | RF | Final Decision | Majority vote")
  951. print("------------------------------------------------------------")
  952. for j in range(min(10, len(X_test))):
  953. votes = [lda_pred[j], mlp_pred[j], rf_pred[j]]
  954. vote_count = Counter(votes)
  955. majority_class, count = vote_count.most_common(1)[0]
  956. print(f"{j:5d} | {lda_pred[j]:3} | {mlp_pred[j]:3} | {rf_pred[j]:3} | {y_pred[j]:14} | {majority_class} ({count} votes)")
  957. # Résultats moyens sur tous les sujets
  958. print("\n>>> Voting Classifier - Average Test Metrics:")
  959. print(f"Accuracy: {np.mean(voting_accuracies) * 100:.2f}%")
  960. print(f"Precision: {np.mean(voting_precisions) * 100:.2f}%")
  961. print(f"Recall: {np.mean(voting_recalls) * 100:.2f}%")
  962. print(f"F1-score: {np.mean(voting_f1_scores) * 100:.2f}%")
  963. # %%

BCI_R-FBCSP.ipynb, under CC-BY-4.0 · at the source

Overview

Authors: Souissi Jihen1, Sourour Karmani2,3, Kais Belwafi4, Mahdi Jemmali5, Ridha Djemal1
  1. ATMS Laboratory, National Engineering School of Sfax, University of Sfax,Sfax, Tunisia
  2. Laboratory of Electronics and Microelectronics (FSM), University of Monastir,Monastir, Tunisia
  3. Higher Institute of Applied Sciences and Technology of Sousse, University of Sousse,Sousse, Tunisia
  4. Department of Computer Engineering, College of Computing and Informatics, University of Sharjah,Sharjah, UAE
  5. Department of Computer Science, College of Computing and Informatics, University of Sharjah,Sharjah, UAE
Institutions: University of Sfax (Tunisia); University of Monastir (Tunisia); University of Sousse (Tunisia); University of Sharjah (United Arab Emirates)
Journal: Brain informatics, volume 13, issue 1, article 12
Dates: received 26 September 2025; accepted 28 February 2026; published online 26 March 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1186/s40708-026-00295-0 · PMID 41886201 · PMCID PMC13133326 · OpenAlex W7140680856
Open access: gold, a free copy (OpenAlex)
Status: code verified (how the code is found and verified)
Categories: EEG (modality), methods / tools (subfield)
Methods: Spectral & time-frequency, Preprocessing, Statistics, Machine learning
Keywords: Brain-computer interface, Motor imagery, ElectroEncephaloGram, Feature extraction, Riemannian geometry, Multi-classification
Topic: EEG and Brain-Computer Interfaces (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: University of Sharjah (2502150165)
Citations: not cited yet (Europe PMC); 47 references in the paper
Research resources: Scikit-learn 0.23.2 RRID:SCR_002577, MNE-Python RRID:SCR_005972, Python 3.9 RRID:SCR_008394, NumPy 1.19 RRID:SCR_008633, EEG Competition IV Dataset 2a RRID:SCR_014177, with statsmodels RRID:SCR_015794

Abstract

The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.

Repositories

Its files are read in the Code ↔ Paper reader above, with 1 match between paragraphs and lines of code.

Zenodo 15825805

License: CC-BY-4.0
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Size: 3 files, 0 scripts
Software Heritage: not checked
Found in: the text, “Key resources”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 30 September 2026: the link answers (HTTP 200)
  • 30 September 2026: the link answers (HTTP 200)
At the source:

souissijihen/BCI_RW_FBCSP_GigaDB

License: none: the authors keep all their rights
State: the link is dead, verified on 30 September 2026
Evidence: found in the paper
Software Heritage: not archived
Found in: the text, “Key resources”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 30 September 2026: the link is dead
  • 30 September 2026: the link is dead

Zenodo 15828534

License: CC-BY-4.0
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: the text, “Key resources”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: MNE-Python (3 files), NumPy (3 files), pyRiemann (3 files), scikit-learn (3 files), SciPy (3 files), pandas (2 files), Matplotlib (1 file)
Availability: 1 check, the latest on 30 September 2026: the link answers (HTTP 200)
  • 30 September 2026: the link answers (HTTP 200)
4 files
At the source:

souissijihen/bci_rw_fbcsp-

License: none: the authors keep all their rights
State: the link is dead, verified on 30 September 2026
Evidence: found in the paper
Software Heritage: not archived
Found in: the Zenodo archive record
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 30 September 2026: the link is dead
  • 30 September 2026: the link is dead

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 4 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 3 scripts, each with its path and the digest of its content;
  • 1 match between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

Datasets cited

Data availability statement

The paper has a data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

  • no repository, dataset or request procedure was recognized in it

Read it in the paper: doi.org/10.1186/s40708-026-00295-0.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 30 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 5 authors, 6 keywords, 1 funder, 36 references, 6 RRIDs.

Cite

This paper

Jihen, S., Karmani, S., Belwafi, K., Jemmali, M., & Djemal, R. (2026). Filter bank CSP with Riemannian weighting for disability-centric motor imagery brain computer interface. Brain informatics, 13(1), 12. https://doi.org/10.1186/s40708-026-00295-0

BibTeX

@article{jihen2026filter,
author = {Jihen, Souissi and Karmani, Sourour and Belwafi, Kais and Jemmali, Mahdi and Djemal, Ridha},
title = {{Filter bank CSP with Riemannian weighting for disability-centric motor imagery brain computer interface}},
journal = {Brain informatics},
year = {2026},
month = mar,
volume = {13},
number = {1},
pages = {12},
publisher = {Springer},
issn = {2198-4018},
doi = {10.1186/s40708-026-00295-0},
url = {https://doi.org/10.1186/s40708-026-00295-0},
pmid = {41886201},
pmcid = {PMC13133326}
}

RIS

TY - JOUR
AU - Jihen, Souissi
AU - Karmani, Sourour
AU - Belwafi, Kais
AU - Jemmali, Mahdi
AU - Djemal, Ridha
TI - Filter bank CSP with Riemannian weighting for disability-centric motor imagery brain computer interface
T2 - Brain informatics
J2 - Brain Inform
PY - 2026
DA - 2026/03/26
VL - 13
IS - 1
SP - 12
SN - 2198-4018
PB - Springer
DO - 10.1186/s40708-026-00295-0
UR - https://doi.org/10.1186/s40708-026-00295-0
LA - en
ER -

CSL-JSON

{
"id": "10.1186/s40708-026-00295-0",
"type": "article-journal",
"title": "Filter bank CSP with Riemannian weighting for disability-centric motor imagery brain computer interface",
"container-title": "Brain informatics",
"author": [
{
"family": "Jihen",
"given": "Souissi"
},
{
"family": "Karmani",
"given": "Sourour"
},
{
"family": "Belwafi",
"given": "Kais"
},
{
"family": "Jemmali",
"given": "Mahdi"
},
{
"family": "Djemal",
"given": "Ridha"
}
],
"container-title-short": "Brain Inform",
"volume": "13",
"issue": "1",
"page": "12",
"DOI": "10.1186/s40708-026-00295-0",
"PMID": "41886201",
"PMCID": "PMC13133326",
"ISSN": "2198-4018",
"publisher": "Springer",
"URL": "https://doi.org/10.1186/s40708-026-00295-0",
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
26
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1038/s41598-024-73755-4 [code]
Compact convolutional transformer for subject-independent motor imagery EEG-based BCIs.
Journal: Scientific reports
In common: MNE-Python, scikit-learn, pandas, 3 other tools, bbci.de/competition/iv, methods / tools, EEG, 5 references
[2] doi:10.3390/brainsci15010027 [code]
A Task-Related EEG Microstate Clustering Algorithm Based on Spatial Patterns, Riemannian Distance, and a Deep Autoencoder.
Journal: Brain sciences
In common: MNE-Python, scikit-learn, pandas, 3 other tools, EEG, 4 references
[3] doi:10.1038/s41598-023-41326-8 [code]
Signature methods for brain-computer interfaces.
Journal: Scientific reports
In common: pyRiemann, MNE-Python, scikit-learn, 1 other tool, bbci.de/competition/iv, methods / tools, EEG, 1 reference
[4] doi:10.3390/s22155771 [code]
Subject-Dependent Artifact Removal for Enhancing Motor Imagery Classifier Performance under Poor Skills.
Journal: Sensors (Basel, Switzerland)
In common: MNE-Python, scikit-learn, pandas, 3 other tools, bbci.de/competition/iv, methods / tools, EEG, 1 reference
[5] doi:10.1038/s41598-025-16219-7 [code]
Temporal convolutional transformer for EEG based motor imagery decoding.
Journal: Scientific reports
In common: MNE-Python, scikit-learn, pandas, 3 other tools, bbci.de/competition/iv, EEG, 1 reference
[6] doi:10.1162/imag_a_00511 [code]
Deep Riemannian Networks for end-to-end EEG decoding.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: pyRiemann, scikit-learn, pandas, 2 other tools, EEG, 3 references
[7] doi:10.3390/s25072259
EEG Signal Prediction for Motor Imagery Classification in Brain-Computer Interfaces.
Journal: Sensors (Basel, Switzerland)
In common: bbci.de/competition/iv, EEG, 4 references
[8] doi:10.3389/fninf.2024.1459970 [code]
hvEEGNet: a novel deep learning model for high-fidelity EEG reconstruction.
Journal: Frontiers in neuroinformatics
In common: MNE-Python, scikit-learn, pandas, 3 other tools, bbci.de/competition/iv, EEG, 1 reference
[9] doi:10.3389/fnhum.2026.1895016 [code]
A confidence-gated source selection strategy for cross-session transfer in brain-computer interfaces.
Journal: Frontiers in human neuroscience
In common: pyRiemann, MNE-Python, scikit-learn, 2 other tools, methods / tools, EEG, 2 references
[10] doi:10.1038/s41598-025-06364-4 [code]
Advancing BCI with a transformer-based model for motor imagery classification.
Journal: Scientific reports
In common: scikit-learn, SciPy, NumPy, bbci.de/competition/iv, methods / tools, EEG, 2 references

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Add the badge to the code's README

OSCR: the badge of this paper

If you are an author or a maintainer of this paper's code, you can add this badge. It says what OSCR knows of the paper (its code, its validated tracing map) and links the code to this page. Paste one of these snippets under the README's title. Keep the two marks around it, so that the badge can be replaced in place and is never added twice. OSCR changes nothing in your repository.

Markdown
<!-- oscr-badge:start -->
[![OSCR](https://openscicode.org/badge/doi/10.1186/s40708-026-00295-0.svg)](https://openscicode.org/badge/doi/10.1186/s40708-026-00295-0)
<!-- oscr-badge:end -->
reStructuredText
.. oscr-badge:start

.. image:: https://openscicode.org/badge/doi/10.1186/s40708-026-00295-0.svg
   :target: https://openscicode.org/badge/doi/10.1186/s40708-026-00295-0
   :alt: OSCR

.. oscr-badge:end
HTML
<!-- oscr-badge:start -->
<a href="https://openscicode.org/badge/doi/10.1186/s40708-026-00295-0"><img src="https://openscicode.org/badge/doi/10.1186/s40708-026-00295-0.svg" alt="OSCR"></a>
<!-- oscr-badge:end -->

On GitHub, open the README, choose Edit (the pencil), paste the snippet under the title, then "Commit changes".

If you choose to, you can have the badge kept up to date for you, with oscr badge --write from the command line or with the GitHub Action in your repository's workflows. Every other form and option (a card, AsciiDoc, LaTeX, shields.io) is on the badge page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: code errors and mismatches between the code and the paper about this paper and its code, from readers and its authors, beside the paper and its tracing map.

Report a mismatch between the code and the paper · Report a code error (signed in)

Reproductions: reports from readers who ran the authors' code and did not get the paper's result, what they ran, with which environment, commit and data.

Report a reproduction failure (signed in)

Versions of its code: the releases of its code tied to a version of this paper (the preprint, the accepted manuscript, the version of record…), each with the tracing map versioned with it.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.