Enhancing spontaneous recovery after stroke: a randomized controlled trial.
The 6 matches
- [1] § Materials and methods › Data processing and dependent measures › TMS-derived stimulus-response curves ↔ Neurophys/Grand_Average_SR_current.m, lines 278 to 362 · score 0.63 · TMS intensity, MEP amplitude, sigmoid, threshold, fitting, muscle · Report a mismatch
- [2] § Materials and methods › Data processing and dependent measures › TMS-derived stimulus-response curves ↔ Neurophys/ESPRESSo_SRcurve_current.m, lines 327 to 367 · score 0.62 · eRMT, sigmoid function, background, threshold, amplitude, fitting · Report a mismatch
- [3] § Materials and methods › Blinding and assessments › Stimulus-response curves ↔ Neurophys/ESPRESSo_SRcurve_current.m, lines 369 to 407 · score 0.59 · 30–100 %, MSO, muscles, intensities · Report a mismatch
- [4] § Materials and methods › Data processing and dependent measures › Reaching kinematics ↔ Kinematics/Script4_ConcatenationTimePoints.m, lines 302 to 409 · score 0.56 · normalized wrist velocity, wrist velocity profile, locations, height, SPARC, uniformly · Report a mismatch
- [5] § Materials and methods › Data processing and dependent measures › Reaching kinematics ↔ Kinematics/Script1_DataProcessingLeft.m, lines 110 to 236 · score 0.55 · Trunk tilt angles, velocity profile, height, wrist, kinematics · Report a mismatch
- [6] § Results › Secondary outcomes › Stimulus-response curves ↔ Neurophys/ESPRESSo_SRcurve_current.m, lines 539 to 594 · score 0.54 · stimulus response curves, MEPsum, RMT, ADM, ECR, FCR · 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
MATLAB · 680 lines · 37 KB · no license · 3 matches
- % All-in-one pipeline that integrates signal processing, data analysis, data formatting, and S-R curve fitting.
- % A Matlab sigmoid function is fitted which returns 4 parameters [minimum y value, maximum y value, S50, slope parameter k]
- % This code has adjustable constraints to improve the fit of the curve (e.g., y values cannot be negative).
- % There are also adjustable inital parameters (search 'initialparams') to give the algorithm somewhere to start.
- %% LOADING THE FILES
- % Currently this code runs one participants data at one time point.
- % All variables need to be cleared in between running data, the code does this in the very first line. If there's just one section you want to run
- % again (e.g., SR curve section with altered parameters) clear all and run again from the start.
- %
- % The code requires a signal file in .mat format. All of the matlab files are on the ESPRESSo research drive and are ready to be inputted to this code and processed.
- % The name of the file gets entered in the file prefix section in General Info.
- % The required input is the file prefix. Please enter the ESPRESSo alphanumeric code
- % Please make sure to exclude the three zeros at the end.
- % E.g., '#########_SR_L_XX##ESP'
- %
- %% MODIFIABLE ASPECTS OF THE CODE
- % Enter your initials in the assessor variable in the General Info section (search 'assessor').
- %
- % The second argument asks for the side that was analysed (specify as either 'P/NP').
- %
- % If the SR fit is not great the first time around, change the initial parameters (search 'initialparams' within the script to find it) to give a more accurate prediction of S50 and/or k.
- %
- % The title of the S-R curve will need to change between P/NP (search 'title')
- %
- %% SAVING THE OUTPUT FILES
- % The output file and SR curve image will automatically be saved in an Output File folder within the folder that you are using the data from.
- % The folder will have the initials of the assessor at the start of it.
- % Enter your initials in the assessor variable in the General Info section.
- %
- % When you press the Run button you will have the SR curve in a separate figure window and an Output structure and SlopeAtS50 (in mV/1%MSO) variable in the
- % workplace.
- % SR CURVE FIGURE
- % The SR curve will pop up in a separate figure. It will the plots for the 4 muscles in different colours and a legend to label the muscles.
- % It will have shaded regions at the bottom of the plot showing the background RMS lower and upper 95% confidence interval. The shaded regions are colour coded to match the muscle.
- % The x-axis is fixed from 0 to 100 and the y-axis is auto scaled.
- %
- % OUTPUT STRUCTURE IN WORKSPACE
- % Inside the output structure are all the variables calculated including the slope variables, I've just put a copy outside the structure
- % too for easy access. The output structure is what get saved for each participant.
- % The conventional reporting for the outputs that do not have the muscle names explicitly displayed is:
- % 1 = FDI
- % 2 = ADM
- % 3 = ECR
- % 4 = FCR
- % The output structure contains:
- % FDI, ADM, ECR, FCR structures - all the information pertaining to just the specfic muscle
- % Data table
- % State and stimulus intensity (linked i.e., state 1 = 30% MSO, state 2 = 40% MSO etc.)
- % BGamp = background amplitude calculated in the prestimulus window
- % defined in the script (-55 to -5 ms unless changed)
- % MEPamp = average amplitude for each state/intensity. Note the state 0
- % value is the average BGamplitude value. This is to help with curve
- % fitting to anchor the curve to a value at x = 0.
- % StD = standard deviation of the MEP amplitudes
- % RMS = background RMS calculated in the prestimulus window defined in
- % the script (-55 to -5 ms unless changed)
- % Norm2Max = MEP amplitude values normalised to the maximum MEP
- % amplitude value. Not being used for analysis currently but retained
- % it in the output just in case
- % SI = array of the stimulus intensities used for SR curve (i.e.,
- % including 0% MSO)
- % MEPvalues table = actual MEP amplitude values used for SR curve
- % SigX = array of x values plotted for the actual sigmoid function
- % SigY = array of y values plotted for the sigmoid function
- % S50 = S50 value (i.e. x value when y value is half it's maximum)
- % k = slope parameter value k. Used to calculate slope at S50 with the
- % formula Max/4*k
- % Slope_at_S50 = slope of the curve at S50 in mV per 10% MSO
- % The following tables in the Output are contain the same information as what is described in the individual muscle structures. It is just displayed in a table showing all 4 muscles at once.
- % MEPamplitude table
- % StD table
- % BGamp table
- % RMS table
- % RMSlowerCI = used as the lower boundary of the shaded area of RMS on
- % the SR curve
- % RMSupperCI = used as the upper boundary of the shaded area of RMS on
- % the SR curve
- % AverageRMS = one average background RMS value for each muscle across
- % all trials
- % RMS_thresholds = the lowest background rms threshold (starting at 10
- % microvolts) for each muscle to ensure at least 5 frames at each intensity
- % k = slope parameter k value for the 4 muscles
- % S50 = S50 value for all four muscles (i.e. x value when y value is half it's maximum)
- % SlopeAtS50_mV1MSO = the slope of the SR curve at S50 for all 4
- % muscles in mV per 1%MSO
- % SlopeAtS50_mV10MSO = the slope of the SR curve at S50 for all 4
- % muscles in mV per 10%MSO
- % GoodnessofFit = Goodness of fit of the curve to the data points using
- % total sums squared and r squared values
- % FrameNumber table = showing the number of frames used for each muscle
- % at each intensity after excluding based on background RMS. This will
- % always be at least 5.
- % TotalTrials table = showing the total number of frames for each
- % muscles/intensity combination before any cleaning/trimming of the
- % data
- % MEPsum table = the sum of the MEP values used to create the S-R curve
- % for each muscle (i.e., the sum of the average MEP amplitude at each
- % stimulus intensity)
- % Code created by Maxine Shanks and Gene Tang
- % Last update: Maxine Shanks 29-08-2024 with the update of checking that the
- % estimated RMT matches the actualy datapoints and adjusting accordingly if not
- %% General Info
- clear all;
- close all;
- assessor = 'MS' % Enter your initials
- n_channels = 4; % set number of channels/muscles recorded from
- fileprefix = '180324_SR_L_xxxxESP' % enter the name of the file to analyse (excluding the part of the filename that is in line 31 e.g., '000.mat')
- side = 'NP' % enter the label for the side to be analysed (e.g., L/R, D/ND, P/NP)
- Target_muscles_labels = {'FDI' 'ADM' 'ECR' 'FCR'}; % enter the names for the 4 muscle labels
- channelNames = {[side Target_muscles_labels{1}],[side Target_muscles_labels{2}],[side Target_muscles_labels{3}],[side Target_muscles_labels{4}]}; % the channel names will then become the side+channel name e.g., PFDI for paretic FDI
- fs = 2000; % sampling rate of the signal
- %% Loading data and assigning time
- r_data = load([fileprefix '000.mat']); % load in rest data using the fileprefix set above in General Info + the suffix i.e., '000.mat'
- fnames = fieldnames(r_data); % extract relevant data from imported .cfs file
- r_data = r_data.(fnames{1});
- % Create table to store relevant information in
- restData = struct2table(r_data.frameinfo); % put frameinfo into a table
- restData.points = []; % remove unwanted points column
- restData.sweeps = []; % remove unwanted sweeps column
- % Create list of times from -0.5 to 0.5 to correspond with the samples
- times = [];
- for i = 1:length(r_data.values)
- times(i) = i *(1/fs)-0.5; % convert times to -0.5 to 0.5 pre/post trigger
- end
- % Set background analysis window
- baseline_time = [-0.055, -0.005]; % use time point -50ms to -5ms to calculate background rms
- baseline_s = find(times >= baseline_time(1), 1):find(times <= baseline_time(2), 1, 'last'); % find times that are within the background window
- % Set p2p time
- MEP_time = [0.015, 0.06]; % setting MEP window to 15-60 ms
- MEP_s = find(times >= MEP_time(1), 1):find(times <= MEP_time(2), 1, 'last'); % find times that are within the p2p window
- % Create temporary data to work on
- temp_signal_data = r_data;
- temp_data = restData;
- % compute RMS/RMS amplitude/MEP amplitude/MEP latency
- for c = 1:n_channels % iterate through the number of channels there are (e.g., 4)
- for f = 1:size(temp_data.number) % iterate through the number of frames (e.g., 90)
- % Background RMS
- temp_data.rms(f,:) = rms(r_data.values(baseline_s(1,1):baseline_s(1,end),c,f)); % calculate rms in the background window set above (e.g., -50 to -5 ms)
- % Background amplitude
- temp_data.BGamp(f,:) = peak2peak(r_data.values(baseline_s(1,1):baseline_s(1,end),c,f)); % caluculate the peak2peak amplitude in the background window
- % MEP amplitude
- temp_data.MEPamp(f,:) = peak2peak(r_data.values(MEP_s(1,1):MEP_s(1,end),c,f)); % calculate the peak2peak amplitude in the p2p window set above (e.g., 0.01 to 0.06)
- end
- % Update variable names in data table
- temp_data.Properties.VariableNames{'rms'} = [channelNames{c} '_rms']; % creates a rms column with the data calculated above
- temp_data.Properties.VariableNames{'BGamp'} = [channelNames{c} '_BGamp']; % creates a Bgamp column with the data calculated above
- temp_data.Properties.VariableNames{'MEPamp'} = [channelNames{c} '_MEPamp']; % creates a MEPamp column with the data calculated above
- end
- % Store temporary data back into relevant variables
- raw_data = temp_data;
- raw_data.start = [];
- raw_data = sortrows(raw_data,'state','ascend'); % sort the 90 frames by state in ascending order (e.g., 30% - 100% MSO)
- rest_signal_data = temp_signal_data;
- n_frames = size(raw_data,1);
- clear baseline_s f i MEP_s rest_signal_data restData temp_data temp_signal_data % clears all the unnecessary variables from the workspace
- %% Cleaning the data
- %this section takes BGamp MEPamp and rms of each muscle and assigns them
- %into respective variable.
- % Splits the background rms, background amplitude and MEP amplitude data into the individual muscles
- State_index = raw_data(:,"state");
- FDI_temp= raw_data(:,[{'state'}, {[side 'FDI_BGamp']}, {[side 'FDI_MEPamp']}, {[side 'FDI_rms']}]);
- ADM_temp = raw_data(:,[{'state'}, {[side 'ADM_BGamp']}, {[side 'ADM_MEPamp']}, {[side 'ADM_rms']}]);
- ECR_temp = raw_data(:,[{'state'}, {[side 'ECR_BGamp']}, {[side 'ECR_MEPamp']}, {[side 'ECR_rms']}]);
- FCR_temp = raw_data(:,[{'state'}, {[side 'FCR_BGamp']}, {[side 'FCR_MEPamp']}, {[side 'FCR_rms']}]);
- var_names = {'FDI_temp', 'ADM_temp', 'ECR_temp', 'FCR_temp'}; % sets the names of the 4 muscles
- outliers_flags = []; % creates an array to store outlier flags. %matrix contains 4 columns each for each muscle. 1 represents the row that an outlier has been removed
- var_names = {'FDI_temp', 'ADM_temp', 'ECR_temp', 'FCR_temp'};
- muscle_names = {'FDI', 'ADM', 'ECR', 'FCR'};
- remaining_trials_percentage = 0.3;
- threshold = 0.5;
- rms_threshold_default = 0.010; % Initial RMS threshold in mV
- rms_thresholds = rms_threshold_default * ones(1, numel(var_names)); % Initialize RMS thresholds for each muscle
- frames_per_intensity = zeros(9, numel(var_names)); % Initialize the array to store frame counts
- total_trials = ones(9, numel(var_names));
- outliers_flags = false(size(eval(var_names{1}), 1), numel(var_names)); % Initialize the array to document outliers
- original_data = struct(); % Create a structure to store the original data
- for vx = 1:numel(var_names) % cycle through the muscles
- original_data.(var_names{vx}) = eval(var_names{vx}); % grab the data for the muscle
- end
- % Loop through all states and make sure there are at least the minimum number of frames remaining after the exclusion based on background RMS.
- % The RMS criterion increases by 0.005 mV each iteration if there's not enough frames across all intensities. This process works for each muscle
- % separately
- while any(frames_per_intensity < threshold*total_trials, 'all')%remaining_trials_percentage < 0.5)
- for vx = 1:numel(var_names) % For each muscle
- var_value = original_data.(var_names{vx}); % Restore the original _temp data;
- rms_threshold = rms_thresholds(vx);
- for RMS_col = 4 % For the RMS column
- outlier_index = (var_value{:,RMS_col}) > rms_threshold;
- outliers_flags(:, vx) = outliers_flags(:, vx) | outlier_index; % Store the outlier index into a column corresponding to the muscle
- var_value{outlier_index, 2:end} = NaN; % Turn all the rows with outliers into NaNs
- eval([var_names{vx} ' = var_value']);
- end
- end
- for vx = 1:numel(var_names) % For each muscle
- var_value = eval(var_names{vx}); % Get the muscle's _temp table
- increase_threshold = true; % Flag to indicate whether threshold needs to be increased
- for state = 1:9 % For each state
- valid_rows = sum(~isnan(var_value{var_value.state == state, 2}), 2); % Count valid rows for the state and store as 1's. Non valid frames are 0.
- total_frames = sum(valid_rows); % total number of valid frames
- frames_per_intensity(state,vx) = total_frames;
- total_trials(state, vx) = sum(var_value.state == state);
- min_trials_required = ceil(total_trials(state,vx)*0.5);
- remaining_trials_percentage = total_frames/total_trials(state,vx);
- if remaining_trials_percentage < 0.5 && increase_threshold
- rms_thresholds(vx) = rms_thresholds(vx) + 0.005;
- increase_threshold = false;
- end
- end
- end
- end
- avg_rms_values = zeros(1, numel(var_names)); % Initialize the array to store average RMS values
- for vx = 1:numel(var_names) % For each muscle
- var_value = eval(var_names{vx}); % Get the muscle's _temp table
- rms_column = var_value{:, 4}; % Get the RMS values from column 4
- % Calculate the average RMS value excluding NaNs
- avg_rms_values(vx) = mean(rms_column, 'omitnan');
- end
- outliers_flags = array2table(outliers_flags, 'VariableNames', {[side 'FDI'], [side 'ADM'], [side 'ECR'], [side 'FCR']}); % create a table for outliers_flags
- data = horzcat(State_index, FDI_temp(:,2:end), ADM_temp(:,2:end), ECR_temp(:,2:end), FCR_temp(:,2:end)); % concatenate all tables back into 1 file called rest data
- clear outlier_index outliers_flags RMS_col State_index vx var_value var_names increase_threshold rms_column rms_threshold_default rms_threhsold total_frames valid_rows state min_frames_per_intensity
- %% Calculate percentage of frames retained
- frames_retained = sum(sum(frames_per_intensity)); %add all elements of the array
- percent_retained = (frames_retained/(n_frames*4))*100 %calculte percentage of frames retained out of 360 total frames
- %% Calculate averages of BGamp/MEPamp/RMS
- BGamp = table; %create table for each values
- MEPamp = table;
- RMS = table;
- StD = table;
- SI = [30;40;50;60;65;70;80;90;100]; % set the intensities
- BGamp.Intensities = SI; % assign the above intensities to the following tables
- MEPamp.Intensities = SI;
- RMS.Intensities = SI;
- StD.Intensities = SI;
- n_states = 9; % number of states
- n_muscles = 4; % number of muscles
- for c = 1:n_channels % cycle through each of the muscles/channels
- var = [channelNames{c} '_BGamp']; % the variable is set as the BGamp column
- tidx = find(string(data.Properties.VariableNames) == var); % find the BGamp column
- for s = 1:n_states % for each of the states
- for f = 1:size(data(data.state==s,:),1) % for each of the frames
- state_data = data(data.state==s,:);
- end
- BGav(s) = mean(table2array(data(data.state==s, tidx)), 'omitnan'); % calculate the mean of the BGamplitude for each state, omitting any NaN values
- MEPav(s) = mean(table2array(data(data.state==s, tidx+1)), 'omitnan'); % calculate the mean of the MEPamplitude for each state, omitting any NaN values
- RMSav(s) = mean(table2array(data(data.state==s, tidx+2)), 'omitnan'); % calculate the mean of the RMS for each state, omitting any NaN values
- Stdev(s) = std(table2array(data(data.state==s, tidx+1)), 'omitnan'); % calculate the standard deviation of the MEPamplitude values for each state, omitting any NaN values
- end
- BGamp.([channelNames{c}]) = BGav([1:9]).'; % create a table for the background amplitude with the average value for each muscle at each intensity
- MEPamp.([channelNames{c}]) = MEPav([1:9]).'; % create a table for the MEP amplitude with the average value for each muscle at each intensity
- RMS.([channelNames{c}]) = RMSav([1:9]).'; % create a table for the RMS with the average value for each muscle at each intensity
- StD.([channelNames{c}]) = Stdev([1:9]).'; % create a table for the St Dev with the average value for each muscle at each intensity
- end
- clear BGav c f MEP_time MEPav n_channels n_muscles n_states RMSav s state_data Stdev tidx var
- %% Reformat the data for S-R curve plotting
- %this section takes BGamp MEPamp and RMS values from the previous section
- %and assign them into their respective muscles (table format)
- FDI = table; ADM = table; ECR = table; FCR = table; % create a table for each muscle
- Target_muscles_table = {FDI ADM ECR FCR};
- State = [1;2;3;4;5;6;7;8;9]; % set the state names
- SI = [30;40;50;60;65;70;80;90;100]; % set the SI column
- for mx = 1:length(Target_muscles_labels) % for each of the 4 muscles
- Target_muscles_table{mx}.State = State; % assigning state array to the state column
- Target_muscles_table{mx}.SI = SI; % assigning intensity to the SI column
- Target_muscles_table{mx}.BGamp = table2array(BGamp(:,[side,Target_muscles_labels{mx}])); % assinging BGamp to BGamp column
- Target_muscles_table{mx}.MEPamp = table2array(MEPamp(:,[side,Target_muscles_labels{mx}])); % assinging MEPamp to MEPamp column
- Target_muscles_table{mx}.StD = table2array(StD(:,[side,Target_muscles_labels{mx}])); % assigning StDev to StDev column
- Target_muscles_table{mx}.RMS = table2array(RMS(:,[side,Target_muscles_labels{mx}])); % assinging RMS to RMS column
- norm2max = [];
- for sx = 1:9
- norm2max(sx,1) = table2array(Target_muscles_table{mx}(sx,4))/(max(Target_muscles_table{mx}{:,4})); % creating a normalised to max value (currently not using)
- end
- Target_muscles_table{mx}.Norm2Max = norm2max; % adding normalised values to the table (currently not using)
- end
- clear sx State norm2max mx
- %% Plot the points and add function stats to the table
- %This section plots x = SI, y = MEPamp and fit a sigmoid function.
- colors = {'#A2142F', '#0072BD', '#77AC30', '#D95319'}; % specify colour of the plot
- Target_muscles_labels = {'FDI' 'ADM' 'ECR' 'FCR'};
- % Temporarily storing S50 and slope of all 4 muscles
- SIarray = zeros(4,10); % array to store the SI data points
- MEPampArray = zeros(4,10); % array to store the MEPamp data points
- SigXarray = zeros(4,101); % array to store the sigmoid x values
- SigYarray = zeros(4,101); % array to store the sigmoid y values
- S50 = []; % array to store S50
- k = []; % array to store the slope parameter
- dy = {}; % cell to contain the derivative of the sigmoid functions
- Max = []; % storing max slope from the derivative
- RMSmean = zeros(10,4); % array to store the RMS mean
- RMSupperCI = zeros(10,4); % array to store the RMS upper confidence interval
- RMSlowerCI = zeros(10,4); % array to store the RMS lower confidence interval
- GoodnessofFit = zeros(1,4); % array to store the goodness of fit of the SR curve
- estimated_threshold = zeros(1,4); % array to store the estimated RMT from the sigmoid curve
- adj_estimated_threshold = zeros(1,4); % array to store the adjusted estimated RMT from the sigmoid curve
- new_adj_estimated_threshold = zeros(1,4); % array to store the new adjusted estimated RMT from the actual datapoints if the eRMT from the sigmoid doesn't make sense
- fdi_bgampstd = std(table2array(BGamp(:,2))); % finding the standard deviation of the background amplitude for FDI
- adm_bgampstd = std(table2array(BGamp(:,3))); % finding the standard deviation of the background amplitude for ADM
- ecr_bgampstd = std(table2array(BGamp(:,4))); % finding the standard deviation of the background amplitude for ECR
- fcr_bgampstd = std(table2array(BGamp(:,5))); % finding the standard deviation of the background amplitude for FCR
- newRowStD = [0,fdi_bgampstd,adm_bgampstd,ecr_bgampstd,fcr_bgampstd]; % creating a new array with the 4 st deviation values
- newRowStD = array2table(newRowStD); % convert the new array to a table
- newRowStD.Properties.VariableNames = {'Intensities',channelNames{1,1},channelNames{1,2},channelNames{1,3},channelNames{1,4}}; % Naming the variables in the table
- StD = [newRowStD; StD]; % adding the new standard deviation table to the existing (i.e., adding in a 0% MSO row)
- for ii = 1:numel(Target_muscles_labels) %loop through each muscle
- values = Target_muscles_table{ii};
- bgampav = mean(table2array(values(:,3))); % finding the mean background amplitude value
- newRowAmp = [0,0,0,bgampav,table2array(StD(1,ii+1)),0,0]; % creating a new array with the mean background amplitude
- newRowAmp = array2table(newRowAmp); % convert the new array to a table
- newRowAmp.Properties.VariableNames = {'State', 'SI', 'BGamp', 'MEPamp', 'StD', 'RMS', 'Norm2Max'}; % Naming the variables in the table
- values = [newRowAmp; values]; % adding the new background amplitude row to the existing table (i.e., 0% MSO row)
- % Calculating the 95% CI for RMS from the state 1 (i.e., 30% MSO)
- % for the values to use at 0% MSO on the graph
- muscle_name = Target_muscles_labels{ii}; % Get muscle name
- muscle_temp_table_name = [muscle_name '_temp']; % Construct the table name
- muscle_temp_table = eval(muscle_temp_table_name); % access the specific muscle's _temp table
- alpha = 0.05; % significance level for 95% confidence level
- valid_rows = sum(~isnan(muscle_temp_table{muscle_temp_table.state == 1, 4}), 2); % Get the valid rows of the RMS column for state 1 (30% MSO)
- n = sum(valid_rows); % number of valid rows
- mean_RMS_state1 = mean(muscle_temp_table{muscle_temp_table.state == 1,4}, 'omitnan'); % find the mean, omitting NaN values
- std_RMS_state1 = std(muscle_temp_table{muscle_temp_table.state == 1,4}, 'omitnan'); % find the standard deviation, excluding NaN values
- t_score = tinv(1-alpha/2, n-1);
- margin_of_error = t_score*(std_RMS_state1/sqrt(n));
- CI_lower_BGamp = mean_RMS_state1 - margin_of_error;
- CI_upper_BGamp = mean_RMS_state1 + margin_of_error;
- RMSmean(1,ii) = mean_RMS_state1; % storing the mean RMS value
- RMSlowerCI(1,ii) = CI_lower_BGamp; % storing the RMS lower CI
- RMSupperCI(1,ii) = CI_upper_BGamp; % storing the RMS upper CI
- % Calculating the 95% Confidence Interval for RMS for the states
- % (i.e., 30 - 100% MSO). Same process as above but cycling through
- % the states
- for s = 1:numel(unique(muscle_temp_table.state))
- rows_of_interest = muscle_temp_table.state == s;
- valid_rows = sum(~isnan(muscle_temp_table{rows_of_interest, 4}), 2);
- n = sum(valid_rows);
- mean_rms_for_state = mean(muscle_temp_table{rows_of_interest, 4}, 'omitnan');
- std_rms_for_state = std(muscle_temp_table{rows_of_interest, 4}, 'omitnan');
- t_score = tinv(1-alpha/2, n-1);
- margin_of_error = t_score*(std_rms_for_state/sqrt(n));
- CI_lower = mean_rms_for_state - margin_of_error;
- CI_upper = mean_rms_for_state + margin_of_error;
- RMSmean(s+1,ii) = mean_rms_for_state;
- RMSlowerCI(s+1,ii) = CI_lower;
- RMSupperCI(s+1,ii) = CI_upper;
- end
- % Assigning the x and y values
- x = table2array(values(:,"SI")); %assiging stim intensity as X
- y = table2array(values(:,"MEPamp")); %assigning MEPamp as y
- % Define sigmoid function
- sigmoid = @(params, x) params(1) + (params(2) - params(1)) ./ (1 + exp(-(x - params(3)) / params(4))); % sigmoid curve equation
- % Initial parameter guess [min y value, max y value, S50, k] A good
- % starting value for S50 is 65 and k of 5
- initialParams = [min(y),max(y),65,5]; % Having the 0 for min(y) is ensure min(y) is non-negative. These initial parameters can be changed to refine the fit.
- % Fit the sigmoid function to the data using lsqcurvefit
- fitParams = lsqcurvefit(sigmoid, initialParams, x, y);
- % Additional fmincon optimization for parameter constraints
- lb = [0,0,20, 1]; % lower bounds for parameters
- ub = [min(y),15,90,20]; % upper bounds for parameters
- options = optimoptions('fmincon', 'Display', 'off'); % Set optimization options
- fitParams_constrained = fmincon(@(params)sum((y - sigmoid(params, x)).^2), fitParams, [], [], [], [], lb, ub, [], options);
- % Generate curve using the fitted parameters
- curveX = 0:100;
- curveY_constrained = sigmoid(fitParams_constrained, curveX);
- % Plot data and fitted curve for the current muscle
- plot(x, y, 'Color', colors{1,ii}, 'Marker', '.', 'LineStyle', 'none', 'MarkerSize', 10,'HandleVisibility','off'); % Plot data for the current muscle
- hold on;
- plot(curveX, curveY_constrained, 'Color', colors{1,ii}, 'DisplayName', Target_muscles_labels{ii}, 'LineWidth', 2);
- hold off;
- Target_muscles_table{ii} = values;
- %Store S50 and slope for each muscle in a 1x4 array
- SIarray(ii,:) = x;
- MEPampArray(ii,:) = y;
- SigXarray(ii,:) = curveX;
- SigYarray(ii,:) = curveY_constrained;
- S50(1,ii) = fitParams_constrained(1,3); % S50 the 3rd value from the params output
- k(1,ii) = (fitParams_constrained(1,4)); %divide by 10 to adjust for magnitude scaling %no longer used
- SlopeAtS50(1,ii) = fitParams_constrained(1,2)/(4*fitParams_constrained(1,4)); % equation for getting slope parameter (k) into slope at S50. Equation = Max/4*k
- %plot error bars
- % hold on
- % errorbar(x,y,table2array(StD(:,ii+1)));
- % eb = get(gca, 'Children');
- % set(eb(1), 'Color', colors{ii}, 'DisplayName', Target_muscles_labels{ii},'Linewidth', 1,'HandleVisibility','off','Linestyle','none');
- % Convert hexidecimal colour to RGB triplet
- hex_color = colors{ii};
- color_rgb = sscanf(hex_color(2:end), '%2x%2x%2x', [1 3])/255;
- % Plot the shaded region for the 95% CI for RMS
- hold on;
- fill([x; flipud(x)], [RMSlowerCI(:,ii); flipud(RMSupperCI(:,ii))], color_rgb, 'FaceAlpha', 0.5, 'EdgeColor', 'none','DisplayName', "Background RMS");
- % Calculating TSS
- % Calculate the mean of the y values (actual data points)
- meanY = mean(y);
- % Calculate the total sum of squares
- tss = sum((y-meanY).^2);
- % Matching the SigYarray values to the actual data points
- indices = [1, 30, 40, 50, 60, 65, 70, 80, 90, 100];
- fittedValues = SigYarray(ii,indices);
- % Calculate the residuals
- residuals = y.' - fittedValues;
- % Calculate the sum of squares of the residuals (sum of squared differences between fitted curve and data points)
- ssr = sum((residuals).^2);
- % Calculate the coefficient of determination (R-squared value)
- rSquared = 1 - (ssr/tss);
- GoodnessofFit(1,ii) = rSquared;
- % Identify the x value when the y value first goes above 0.05 for 3 data points
- y_threshold = 0.05; % set the MEP amplitude value in mV for the threshold
- consecutive_count = 0;
- for j = 1:numel(SigXarray(1,:)) % cycle through the stimulus intensities in the Sigmoid curve fit
- if SigYarray(ii,j) > y_threshold % if the MEP amplitude value from the sigmoid curve data points is above the threshold add one value to the consecutive count
- consecutive_count = consecutive_count + 1;
- if consecutive_count == 3 % once the consecutive count = 3
- estimated_threshold(1,ii) = SigXarray(ii,j-(consecutive_count-1)); % go back to the first x value when y > 0.05 and put that in the estimated_threshold variable
- break; % Stop searching when you find the x value
- end
- else
- consecutive_count = 0; % Reset consecutive count if y is below the threshold
- end
- end
- % Check if the threshold was not reached in the loop
- if consecutive_count < 3
- adj_estimated_threshold(1,ii) = 101; % Set to 101 if threshold is not reached
- end
- % Identify the x value when the y value first goes above 0.05 for 3 data points
- adj_y_threshold = mean(SigYarray(ii,1:5)) + 0.05; % set the MEP amplitude value in mV for the threshold
- consecutive_count2 = 0;
- for j = 1:numel(SigXarray(1,:)) % cycle through the stimulus intensities in the Sigmoid curve fit
- if SigYarray(ii,j) > adj_y_threshold % if the MEP amplitude value from the sigmoid curve data points is above the threshold add one value to the consecutive count
- consecutive_count2 = consecutive_count2 + 1;
- if consecutive_count2 == 3 % once the consecutive count = 3
- adj_estimated_threshold(1,ii) = SigXarray(ii,j-(consecutive_count2-1)); % go back to the first x value when y > 0.05 and put that in the estimated_threshold variable
- break; % Stop searching when you find the x value
- end
- else
- consecutive_count2 = 0; % Reset consecutive count if y is below the threshold
- end
- end
- % Check if the threshold was not reached in the loop
- if consecutive_count2 < 3
- adj_estimated_threshold(1,ii) = 101; % Set to 101 if threshold is not reached
- end
- % Find the closest x value upwards
- closest_x_idx = find(x >= adj_estimated_threshold(1,ii),1);
- closest_x = x(closest_x_idx);
- % Get the corresponding y value
- corresponding_y = y(closest_x_idx);
- % Compare y value with adj_y_threshold
- if corresponding_y < adj_y_threshold
- % Move upwards until a y value is found that meets the criteria
- for t = closest_x_idx+1:length(x)
- if y(t) >= adj_y_threshold
- closest_x_idx = t;
- closest_x = x(t); % update closest x value to the new one
- break; % stop the loop once the condition is met
- end
- end
- % Take the value one lower than closest_x
- new_eRMT = x(closest_x_idx - 1);
- new_adj_estimated_threshold(1,ii) = new_eRMT;
- else
- new_adj_estimated_threshold(1,ii) = adj_estimated_threshold(1,ii);
- end
- % Ensure the minimum value of new_eRMT is not < 20% MSO
- new_adj_estimated_threshold(1,ii) = max(new_adj_estimated_threshold(1,ii), 20);
- end
- % Creating the RMS CI values from S-R curve into a table
- RMSlowerCI = array2table(RMSlowerCI); % convert the new array to a table
- Intensities = [0;30;40;50;60;65;70;80;90;100];
- RMSlowerCI = addvars(RMSlowerCI, Intensities, 'Before', 1);
- RMSlowerCI.Properties.VariableNames = {'Intensities',channelNames{1,1},channelNames{1,2},channelNames{1,3},channelNames{1,4}}; % Naming the variables in the table
- RMSupperCI = array2table(RMSupperCI);
- RMSupperCI = addvars(RMSupperCI, Intensities, 'Before', 1);
- RMSupperCI.Properties.VariableNames = {'Intensities',channelNames{1,1},channelNames{1,2},channelNames{1,3},channelNames{1,4}}; % Naming the variables in the table
- RMSmean = array2table(RMSmean);
- RMSmean = addvars(RMSmean, Intensities, 'Before', 1);
- frames_per_intensity = array2table(frames_per_intensity);
- frames_per_intensity = addvars(frames_per_intensity, SI, 'Before', 1);
- frames_per_intensity.Properties.VariableNames = {'Intensities',channelNames{1,1},channelNames{1,2},channelNames{1,3},channelNames{1,4}}; % Naming the variables in the table
- total_trials = array2table(total_trials);
- total_trials = addvars(total_trials, SI, 'Before', 1);
- total_trials.Properties.VariableNames = {'Intensities',channelNames{1,1},channelNames{1,2},channelNames{1,3},channelNames{1,4}};
- % Add labels and title for the plot
- xlim([0 100]); %set x-axis limit from 0 - 100
- ylim([0,6]); %auto set the y-axis limit
- xticks(0:10:100);
- yticks(0:1:6);
- xlabel('Stimulus intensity (%MSO)');
- ylabel('Evoked Motor Response (mV)');
- %title('Non-Paretic Stimulus-Response Curve'); %label this NP/P as required
- title(side); %NP/P
- legend('show','Location','northwest'); % determining the location of the legend
- box off; % removing the box around the graph
- %As all tables are stored in one array, here we breaks them back into 4 separate variables
- FDI = Target_muscles_table{1}; ADM = Target_muscles_table{2}; ECR = Target_muscles_table{3}; FCR = Target_muscles_table{4};
- % Combining the MEP data from the S-R curve into a table
- MEPamplitude = table;
- MEPamplitude.Intensities = Intensities;
- MEPamplitude.FDI = FDI.MEPamp
- MEPamplitude.ADM = ADM.MEPamp
- MEPamplitude.ECR = ECR.MEPamp
- MEPamplitude.FCR = FCR.MEPamp
- MEPsum = zeros(1,4);
- MEPadjusted = MEPamplitude;
- for muscle = 1:4;
- subvalue = MEPadjusted(1,muscle+1)
- m = table2array(MEPadjusted(:,muscle+1))
- m = m-table2array(subvalue(1,1))
- MEPadjusted(:,muscle+1) = array2table(m)
- MEPsum(1,muscle) = sum(table2array(MEPadjusted(:,muscle+1)))
- end
- clear adm_bgampstd bgampav colors curveX curveY dy eb ecr_bgampstd fcr_bgampstd fdi_bgampstd fitParams ii initialParams newRowAmp newRowStD sigmoid ADM_temp ECR_temp FCR_temp FDI_temp alpha CI_lower CI_lower_BGamp CI_upper CI_upper_BGamp color_rgb hex_color margin_of_error mean_rms_for_state mean_RMS_state1 MEPamp MEPampArray muscle_name muscle_temp_table muscle_temp_table_name n rows_of_interest s SI std_rms_for_state std_RMS_state1 t_score valid_rows
- %% Calculating other stats and put them into on struct
- %this section take the data from the tables and calculate k, meany, df,
- %SEy, TSS, Critical T and CI. It groups these statistics together with the
- %corresponding table (for each muscle), storing them in structs.
- FDI_All = []; ADM_All = []; ECR_All = []; FCR_All = []; %create structs
- All = {FDI_All, ADM_All, ECR_All, FCR_All}; %store structs in one cell for looping
- variables = {FDI, ADM, ECR, FCR};
- for i = 1:length(variables)
- current_variable = variables{i};
- current_all = All{i};
- current_all.Data = current_variable;
- current_all.SI = SIarray(i,:);
- current_all.MEPvalues = MEPamplitude(:,i+1);
- current_all.SigX = SigXarray(i,:); % X values
- current_all.SigY = SigYarray(i,:); % Y values from the sigmoid fit
- current_all.S50 = S50(1,i); % assigning S50
- current_all.k = k(1,i); % k parameter is the 4th value in the parameter output from the sigmoid function
- current_all.Slope_at_S50 =SlopeAtS50(1,i)*10; %assigning slope at maximum in mV per 10% MSO
- All{i} = current_all; %assigning the modified struct back into the cell
- end
- %breaks the cell back into 4 separate variables
- FDI = All{1};
- ADM = All{2};
- ECR = All{3};
- FCR = All{4};
- % Adding the variables you want saved to the Output file
- Output.FDI = FDI; % All FDI related info
- Output.ADM = ADM; % All ADM related info
- Output.ECR = ECR; % All ECR related info
- Output.FCR = FCR; % All FCR related info
- Output.MEPamplitude = MEPamplitude; % MEPamp table across intensities and muscles
- Output.StD = StD; % StDev table across intensities and muscles
- Output.BGamp = BGamp; % BGamp table across intensities and muscles
- Output.RMS = RMSmean; % RMS table across intensities and muscles
- Output.RMSlowerCI = RMSlowerCI; % RMS confidence interval table across intensities and muscles
- Output.RMSupperCI = RMSupperCI; % RMS confidence interval table across intensities and muscles
- Output.AverageRMS = avg_rms_values; % average rms per muscle
- Output.RMS_thresholds = rms_thresholds.'; % the lowesr rms cut off (in 5 microvolt increments) value required for each muscle to include at least 5 frames per intensity
- Output.k = k.' % adding the k values to the output
- Output.S50 = S50.'; % S50 values for the four muscles
- Output.SlopeAtS50_mV1MSO = SlopeAtS50.'; % Slope at S50 in mV/1%MSO for the 4 muscles
- Output.SlopeAtS50_mV10MSO = SlopeAtS50*10.'; % Slope at S50 in mV/10%MSO for the 4 muscles
- Output.GoodnessofFit = GoodnessofFit.'; % Goodness of fit of the SR curve for the 4 muscles
- Output.FrameNumber = frames_per_intensity; % Number of frames per intensity per muscle included for analysis
- Output.TotalTrials = total_trials;
- Output.MEPsum = MEPsum;
- Output.Adjusted_MEPamplitude = MEPadjusted;
- Output.estimated_threshold = estimated_threshold;
- Output.adj_estimated_threshold = adj_estimated_threshold;
- Output.new_adj_estimated_threshold = new_adj_estimated_threshold;
- Output.percent_retained = percent_retained;
- clear ADM_All baseline_time current_variable ECR_All FCR_All FDI_All i
- %% Saving the output file and SR curve figure
- % Get the current working directory (where the script is located)
- scriptFolder = pwd;
- % Define the subfolder for output files
- outputSubfolder = strcat(assessor, '_Output Files')
- % Create the full path for the output subfolder
- outputFolder = fullfile(scriptFolder, outputSubfolder);
- % Make sure the output subfolder exists
- if ~exist(outputFolder, 'dir')
- mkdir(outputFolder);
- end
- % Save the analysis Output
- filename = strcat(fileprefix, 'output', '.mat');
- fullpath = fullfile(outputFolder, filename);
- save(fullpath, 'Output');
- % Save the figure
- figureFilename = strcat(fileprefix, '_SR Curve.png');
- figureFullPath = fullfile(outputFolder, figureFilename);
- saveas(gcf, figureFullPath);
- % % clearvars -except Output SlopeAtS50 MEPsum
ESPRESSo_SRcurve_current.m at commit e50e60b, no license · at the source
Overview
- School of Exercise, Sport and Rehabilitation Sciences, University of Auckland, Auckland 1142, New Zealand
- Centre for Brain Research, University of Auckland, Auckland 1142, New Zealand
- Department of Medicine, University of Auckland, Auckland 1142, New Zealand
- Department of Epidemiology and Biostatistics, University of Auckland, Auckland 1142, New Zealand
- Discipline of Physiology, School of Biomedicine, University of Adelaide, Adelaide 5000, Australia
- Department of Neurology, David Geffen School of Medicine, UCLA, Los Angeles, CA 90095, USA
- MindMaze SA, Lausanne 1006, Switzerland
- Department of Neurology, Westchester Medical Center, NewYork Medical College, Valhalla, NY 10595, USA
- Department of Neurology, Johns Hopkins University, Baltimore, MD 21287, USA
- Department of Neuroscience, Johns Hopkins University, Baltimore, MD 21287, USA
- Department of Physical Medicine and Rehabilitation, Johns Hopkins University, Baltimore, MD 21287, USA
- The Santa Fe Institute, Santa Fe, NM 87501, USA
Abstract
This Phase IIa biomarker-guided stroke rehabilitation trial aimed to determine if a 3-week programme of high-dose, high-intensity (HDHI) virtual exploratory movement (VEM) using MindPod Dolphin could improve upper limb recovery and outcomes early after stroke. Sixty-four participants were randomized into VEM (n = 31) or conventional therapy (CoT) groups (n = 33) and began intervention within 2 weeks of stroke. Participants were given 90 min of therapist time per weekday for 3 weeks to undertake intensive upper limb therapy over and above their usual customary care. Outcomes were obtained immediately post-intervention, 3 and 6 months post-stroke. The primary endpoint was change in Action Research Arm Test (ARAT) score between baseline and 3 months post-stroke. Secondary outcomes were Fugl-Meyer assessment (FM-UE), hand dexterity, reaching kinematics and transcranial magnetic stimulation-derived measures across post-intervention time points up to 6 months. Data from all participants were analysed for intention to treat (ITT), while 54 met the weekly target of active therapy minutes, permitting inclusion in a per protocol (PP) analysis. The average weekly time on task increased over the intervention period, with progressively greater distances of arm movements (VEM) or task repetitions (CoT) made each week. Still, during the final week of intervention, participants analysed PP spent an average of only 50 (VEM)—66% (CoT) of the extra time available on task. For the ITT analysis, there was no effect of group for the primary endpoint. For the PP analysis, there was no effect of group or group by time interaction for any secondary outcome measure. An expected effect of time was observed indicative of recovery from impairment (mean ΔFM at 6 months = 23 points), increased activity capacity (mean ΔARAT at 6 months = 31 points) and improved secondary neuroscientific measures. PP participants from both groups were compared with an historical cohort matched for baseline age, stroke severity and impairment who only received usual customary care. Despite a 3-fold difference in active upper limb therapy minutes there were no differences in the 3-month ARAT or ΔFM. Indeed, the recovery in FM-UE at 3 months was proportional to baseline impairment, suggesting that the gains could be largely attributable to spontaneous biological recovery. While patient-related factors limited the dose of therapy achievable in this trial, clinician and service-related factors would also need to be overcome in routine clinical practice, to deliver equivalent doses at this early phase. It may be necessary to forego HDHI therapy until the late sub-acute phase.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repository
Its files are read in the Code ↔ Paper reader above, with 6 matches between paragraphs and lines of code.
wbyblow/ESPRESSo
e50e60ba814b5ec36b498e42642b5332857287a1, 28 January 2026Availability: 1 check, the latest on 30 September 2026: the link answers
- 30 September 2026: the link answers
12 files
- Dexterity/
Script1_Participant.m , MATLAB, 684 lines - Dexterity/
Script2_ParticipantFull. , MATLAB, 166 linesm - Dexterity/
Script3_GroupAnalysis.m , MATLAB, 569 lines - Kinematics/
Script1_DataProcessingLe , MATLAB, 293 lines, 1 matchft.m - Kinematics/
Script1_DataProcessingRi , MATLAB, 301 linesght.m - Kinematics/
Script2_SPARC.py , Python, 169 lines - Kinematics/
Script3_ConcatenationLef , MATLAB, 499 linestRight.m - Kinematics/
Script4_ConcatenationTim , MATLAB, 701 lines, 1 matchePoints.m - Kinematics/
Script5_GroupAnalysis.m , MATLAB, 1,076 lines - Neurophys/
ESPRESSo_SRcurve_current , MATLAB, 680 lines, 3 matches.m - Neurophys/
Grand_Average_SR_current , MATLAB, 450 lines, 1 match.m - README.md, Text, 1 line
The paper's code and data availability statement is in the Data section.
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:
- 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 11 scripts, each with its path and the digest of its content;
- 6 matches 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
No dataset and no data link were found in the paper.
Data availability
The data that support the findings of this study are available from the corresponding author, upon reasonable request. The code used to analyse neuroscientific data can be found here https://
Reproduced under the paper's license (CC BY), from the paper cited above.
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, 16 authors, 5 keywords, 1 funder, 64 references.
Cite
This paper
Byblow, W. D., Shanks, M. J., Scrivener, B., Duval, L., Stinear, C. M., Lee, A., Barber, P. A., Colle, P., Ren, A., Cirillo, J., Arac, A., Ejaz, N., Chan-Cortes, M., Garipelli, G., Kitago, T., & Krakauer, J. W. (2026). Enhancing spontaneous recovery after stroke: a randomized controlled trial. Brain communications, 8(2), fcag057. https://
BibTeX
@article{byblow2026enhan
author = {Byblow, Winston D and Shanks, Maxine J and Scrivener, Benjamin and Duval, Laura and Stinear, Cathy M and Lee, Arier and Barber, P Alan and Colle, Patricia and Ren, April and Cirillo, John and Arac, Ahmet and Ejaz, Naveed and Chan-Cortes, Michelle and Garipelli, Gangadhar and Kitago, Tomoko and Krakauer, John W},
title = {{Enhancing spontaneous recovery after stroke: a randomized controlled trial}},
journal = {Brain communications},
year = {2026},
month = mar,
volume = {8},
number = {2},
pages = {fcag057},
publisher = {Oxford University Press},
issn = {2632-1297},
doi = {10.1093/
url = {https://
pmid = {42022291},
pmcid = {PMC13098183}
}
RIS
TY - JOUR
AU - Byblow, Winston D
AU - Shanks, Maxine J
AU - Scrivener, Benjamin
AU - Duval, Laura
AU - Stinear, Cathy M
AU - Lee, Arier
AU - Barber, P Alan
AU - Colle, Patricia
AU - Ren, April
AU - Cirillo, John
AU - Arac, Ahmet
AU - Ejaz, Naveed
AU - Chan-Cortes, Michelle
AU - Garipelli, Gangadhar
AU - Kitago, Tomoko
AU - Krakauer, John W
TI - Enhancing spontaneous recovery after stroke: a randomized controlled trial
T2 - Brain communications
J2 - Brain Commun
PY - 2026
DA - 2026/
VL - 8
IS - 2
SP - fcag057
SN - 2632-1297
PB - Oxford University Press
DO - 10.1093/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1093/
"type": "article-journal",
"title": "Enhancing spontaneous recovery after stroke: a randomized controlled trial",
"container-title": "Brain communications",
"author": [
{
"family": "Byblow",
"given": "Winston D"
},
{
"family": "Shanks",
"given": "Maxine J"
},
{
"family": "Scrivener",
"given": "Benjamin"
},
{
"family": "Duval",
"given": "Laura"
},
{
"family": "Stinear",
"given": "Cathy M"
},
{
"family": "Lee",
"given": "Arier"
},
{
"family": "Barber",
"given": "P Alan"
},
{
"family": "Colle",
"given": "Patricia"
},
{
"family": "Ren",
"given": "April"
},
{
"family": "Cirillo",
"given": "John"
},
{
"family": "Arac",
"given": "Ahmet"
},
{
"family": "Ejaz",
"given": "Naveed"
},
{
"family": "Chan-Cortes",
"given": "Michelle"
},
{
"family": "Garipelli",
"given": "Gangadhar"
},
{
"family": "Kitago",
"given": "Tomoko"
},
{
"family": "Krakauer",
"given": "John W"
}
],
"container-title-short":
"volume": "8",
"issue": "2",
"page": "fcag057",
"DOI": "10.1093/
"PMID": "42022291",
"PMCID": "PMC13098183",
"ISSN": "2632-1297",
"publisher": "Oxford University Press",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
28
]
]
}
}
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.1186/s12984-025-01743-4 [code]
- Can machine learning improve on the early prediction of upper limb recovery after stroke?Journal: Journal of neuroengineering and rehabilitationIn common: stroke, clinical / translational, 8 references
- [2] doi:10.1002/hbm.26105 [code]
- Use of multi-perturbation Shapley analysis in lesion studies of functional networks: The case of upper limb paresis.Journal: Human brain mappingIn common: Statistics and Machine Learning Toolbox, stroke, clinical / translational, 5 references
- [3] doi:10.1080/07853890.2025.2598930
- Predictive value of motor-evoked potentials for upper limb functional outcomes in acute ischemic stroke.Journal: Annals of medicineIn common: stroke, other, clinical / translational, 5 references
- [4] doi:10.1063/5.0263191 [code]
- Post-stroke spontaneous motor recovery in mice can be predicted from acute-phase local field potential using machine learning.Journal: APL bioengineeringIn common: pandas, NumPy, stroke, clinical / translational, 5 references
- [5] doi:10.1177/0271678x241254718 [code]
- Refined movement analysis in the staircase test reveals differential motor deficits in mouse models of stroke.Journal: Journal of cerebral blood flow and metabolism : official journal of the International Society of Cerebral Blood Flow and MetabolismIn common: Optimization Toolbox, Statistics and Machine Learning Toolbox, pandas, 1 other tool, stroke, clinical / translational, 3 references
- [6] doi:10.1038/s41467-025-57860-0 [code]
- Parvalbumin interneurons regulate rehabilitation-induced functional recovery after stroke and identify a rehabilitation drug.Journal: Nature communicationsIn common: Statistics and Machine Learning Toolbox, stroke, 4 references
- [7] doi:10.1093/ptj/pzae148
- Modeling Upper Limb Rehabilitation-Induced Recovery After Stroke: The Role of Attention as a Clinical Confounder.Journal: Physical therapyIn common: stroke, clinical / translational, 4 references
- [8] doi:10.3389/fnhum.2022.1023246
- Home-based portable fNIRS-derived cortical laterality correlates with impairment and function in chronic stroke.Journal: Frontiers in human neuroscienceIn common: stroke, 4 references
- [9] doi:10.1093/braincomms/fcad055
- Prediction of post-stroke motor recovery benefits from measures of sub-acute widespread network damages.Journal: Brain communicationsIn common: stroke, 4 references
- [10] doi:10.1126/sciadv.abo3505 [code]
- Dissecting motor skill acquisition: Spatial coordinates take precedence.Journal: Science advancesIn common: pandas, NumPy, author John W Krakauer
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.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 1 repository of the authors' code, each at its verified commit and with its license, 11 scripts, and 6 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:2da2bbbe815646e4…
Add the badge to the code's README
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 -->
[](https://openscicode.org/badge/doi/10.1093/braincomms/fcag057)
<!-- oscr-badge:end -->reStructuredText
.. oscr-badge:start
.. image:: https://openscicode.org/badge/doi/10.1093/braincomms/fcag057.svg
:target: https://openscicode.org/badge/doi/10.1093/braincomms/fcag057
:alt: OSCR
.. oscr-badge:endHTML
<!-- oscr-badge:start -->
<a href="https://openscicode.org/badge/doi/10.1093/braincomms/fcag057"><img src="https://openscicode.org/badge/doi/10.1093/braincomms/fcag057.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.
