When emotions hurt: negative interpretations of bodily signals and interoceptive difficulties in fibromyalgia.
The 1 match
- [1] § Study 1 › Data analyses › Body coverage ↔ WP1 - Behavioural project/emBODY and interoception/PAID embody analysis.m, lines 275–354 · score 0.55 · pain today, PPP, coverage, painted, body, pixels
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
MATLAB · 921 lines · 28 KB · no license · 1 match
- % Info:
- % Written by Aleksnadra Herman 2025, rev 2026
- %
- % The script has been tested on Windows computer with MATLAB R2024a
- % Ensure data structure is correct.
- %
- % Based on functions available here: https://version.aalto.fi/gitlab/eglerean/embody Reported in:
- % Nummenmaa L., Glerean E., Hari R., Hietanen, J.K. (2014)
- % Bodily maps of emotions, Proceedings of the National Academy of Sciences of United States of America doi:10.1073/pnas.1321664111
- % http://www.pnas.org/content/111/2/646.abstract
- %%%%
- %% 1. Data Loading and Setup
- clear all
- close all
- % the diretory wich contains all necessary functions (change):
- GTDIR = 'C:\Users\hermana\OneDrive - University of South Australia\TASKS backup\emBody\functions-Sven\functions';
- % the directory with all the project data (chage):
- main_dir = 'C:\Users\hermana\OneDrive - University of South Australia\TASKS backup\emBody\';
- addpath(genpath(main_dir));
- cd(GTDIR)
- if ~exist([main_dir 'outdata'], 'dir')
- mkdir([main_dir 'outdata'])
- end
- cfg.outdata = [main_dir 'outdata\'];
- cfg.datapath = [main_dir 'subjects\'];
- labels={'Fear'
- 'Sadness'
- 'Happiness'
- 'Anger'
- 'Disgust'
- 'Surprise'
- 'Anxiety'
- 'Stomach ache'
- 'Headache'
- 'Your pain today'
- 'Neutral state'
- 'Physical fatigue'
- 'Mental fatigue'
- };
- cfg.Nstimuli = length(labels);
- cfg.Nempty = 1;
- cfg.phenodata = 0;
- bspm=bodySPM_parseSubjects(cfg);
- out=[];
- ids=find(bspm.data_filter(:,3)==1);
- for i = 1:length(ids)
- out{i,1}=bspm.subjects(ids(i)).name;
- end
- fileID=fopen([cfg.outdata '/list.txt'],'w');
- for i=1:length(out)
- if(strcmp(out{i}(1),'.'))
- disp([out{i} ' has invalid ID for this study']);
- continue;
- end
- fprintf(fileID,'%s\n',[out{i}]);
- end
- fclose(fileID)
- %% 2. Preprocesssing
- cfg.list = [cfg.outdata 'list.txt']; %txt file with the list of subjects to process
- cfg.hasBaseline = 1;
- cfg.posneg=0;
- cfg.overwrite=1;
- base=uint8(imread('bodySPM_base2.png'));
- mask=uint8(imread('bodySPM_base3.png'));
- mask=mask*.85;
- base2=base(10:531,33:203,:);
- in_mask=find(mask>128);
- subjects=textread(cfg.list,'%s');
- Nsubj=size(subjects,1);
- allTimes=zeros(cfg.Nstimuli,2,Nsubj);
- tocheck=zeros(Nsubj,1);
- tocheck3=[];
- for ns=1:Nsubj
- subjID=subjects{ns,:};
- disp(['Processing subject ' subjID ' which is number ' num2str(ns) ' out of ' num2str(Nsubj)]);
- if(cfg.overwrite==0)
- matname=[cfg.outdata '/' subjID '.mat'];
- if(exist(matname)==2)
- a=load(matname);
- allTimes(:,:,ns)=a.times;
- disp('Already preprocessed, no overwrite')
- continue;
- end
- end
- try
- a=bodySPM_load([cfg.datapath '/' subjID '/'],2);
- S=length(a);
- if( S ~= cfg.Nstimuli)
- disp('Mismatch between expected number of trials and loaded trials')
- end
- resmat=zeros(522,171,cfg.Nstimuli);
- times=zeros(S,2);
- % go through each stimulus
- for n=1:S
- T=length(a(n).paint(:,2));
- over=zeros(size(base,1),size(base,2));
- for t=1:T
- y=ceil(a(n).paint(t,3)+1);
- x=ceil(a(n).paint(t,2)+1);
- if(x<=0) x=1; end
- if(y<=0) y=1; end
- if(x>=900) x=900; end
- if(y>=600) y=600; end
- over(y,x)=over(y,x)+1;
- end
- %Smoothing
- h=fspecial('gaussian',[15 15],5);
- over=imfilter(over,h);
- M1=1;
- M2=1;
- if(cfg.posneg==1)
- M2=0;
- end
- if(cfg.posneg==-1)
- M1=0;
- M2=-1;
- end
- over2=M1*over(10:531,33:203,:)-M2*over(10:531,696:866,:);
- resmat(:,:,n)=over2;
- % times vector, the first one is the amount of time in milliseconds, the second one is the total number of pixels painted
- if(size(a(n).paint,1)>0 && size(a(n).mouse,1)>0)
- times(n,1)=a(n).mouse(end,1)-a(n).mouse(1,1);
- else
- tocheck(ns)=tocheck(ns)+1;
- times(n,1)=0;
- end
- times(n,2)=T;
- end
- matname=[cfg.outdata subjID '.mat'];
- save (matname, 'resmat','times');
- % store all times for diagnostic purposes
- allTimes(:,:,ns)=times;
- %% visualize subject's data
- M=max(abs(resmat(:))); % max range for colorbar
- NumCol=64;
- hotmap=hot(NumCol);
- coldmap=flipud([hotmap(:,3) hotmap(:,2) hotmap(:,1) ]);
- hotcoldmap=[
- coldmap
- hotmap
- ];
- % visualize all responses for each subject into a grid of numcolumns
- plotcols = 5; %set as desired
- plotrows = 3;%ceil((NC+1)/plotcols); % number of rows is equal to number of conditions+1 (for the colorbar)
- for n=1:S%NC
- figure(ns)
- set(gcf, 'Visible', 'on');
- subplot(plotrows,plotcols,n)
- imagesc(base2);
- axis('off');
- set(gcf,'Color',[1 1 1],'Position', [200, 200, 1000, 800]);
- hold on;
- over2=resmat(:,:,n);
- fh=imagesc(over2,[-M,M]);
- axis('off');
- axis equal
- colormap(hotcoldmap);
- set(fh,'AlphaData',mask)
- title(labels(n),'FontSize',12)
- sgtitle(subjID)
- if(n==S) %(n==NC)
- subplot(plotrows,plotcols,n+1)
- fh=imagesc(ones(size(base2)),[-M,M]);
- axis('off');
- colorbar;
- % save a screenshot, useful for quality control (commented)
- saveas(gcf,[cfg.outdata subjID '.png'])
- end
- end
- clf
- catch
- disp(['Sth is wrong with subject ' subjID ' which is number ' num2str(ns) ' out of ' num2str(Nsubj)])
- tocheck3=[tocheck3;ns];
- end
- end
- tocheck2 =[];
- for i=1:length(tocheck)
- if tocheck(i)~= 0
- tocheck2 =[tocheck2;i];
- end
- end
- bspm=cfg;
- bspm.allTimes=allTimes;
- bspm.tocheck=tocheck;
- bspm.tocheck2=tocheck2;
- bspm.tocheck3=tocheck3;
- save([cfg.outdata '/bspm.mat'], 'bspm')
- %% 3. Quality control and filtering
- % find the number of stimuli painted by each subjects
- B = squeeze(bspm.allTimes(:,2,:)); %squeezing pos and neg matrices
- B(B==0) = NaN; %I'm replacing all zero's with NaN --> cause zeros means that nothing has been painted
- npainted = sum(~isnan(B));
- figure
- subplot(2,1,1)
- stem(npainted);
- xticks(1:(length(subjects)))
- set(gca, 'XTickLabel', subjects','fontsize',10);
- xtickangle(30)
- title('N pained Body Maps', 'Fontsize', 20)
- subplot(2,1,2)
- [sortedB, sortedI] = sort(npainted);
- stem(sortedB);
- xticks(1:(length(subjects)))
- sortedL = subjects(sortedI)';
- set(gca, 'Xticklabel',sortedL,'fontsize',10);
- xtickangle(30)
- title('N pained Body Maps - Ascending', 'Fontsize', 20)
- M = mean(nonzeros(npainted));
- Std = std(nonzeros(npainted));
- thr_std = 0.5*length(labels);
- disp(['Mean N BSMs pained: ' num2str(M) ' +/- ' num2str(Std) '. Threshold to accept is ' num2str(thr_std)]);
- disp(['Min N BSMs pained is: ' num2str(min(nonzeros(npainted)))]);
- ids=find(npainted>=thr_std);
- exc=find(npainted<thr_std);
- out=[];
- for i = 1:length(ids)
- out{i,1}=subjects{ids(i)};
- end
- % Descriptive stats in kept subjects
- Descr(1,1) = mean(nonzeros(npainted(ids)));
- Descr(1,2) = std(nonzeros(npainted(ids)));
- Descr(1,3) = min(nonzeros(npainted(ids)));
- % saving the list of participants to inlude in the analysis
- fileID=fopen([cfg.outdata 'whitelist.txt'],'w');
- for i=1:length(out)
- fprintf(fileID,'%s\n',[out{i}]);
- end
- fclose(fileID);
- cfg.whitelist = [cfg.outdata 'whitelist.txt'];
- %% 4. Identyfy groups
- % comparing BSMs between groups
- numArray = str2double(out);
- subjects=textread(cfg.whitelist,'%s');
- numArray = str2double(subjects);
- g1 = find(numArray<200); %FM
- g2 = find(numArray>199); %CO
- cfg.g1=g1; %FM
- cfg.g2=g2; %Control
- %% 5. Total pixels painted analysis
- % Exploring the coverage of embodiment.
- TPP = bodySPM_global_rev(cfg);
- save([cfg.outdata '/TPP.mat'],'TPP')
- %excluding pain today
- labels2 = labels;
- labels2(10) = [];
- scr_siz = get(0,'ScreenSize') ;
- %bar plot
- figure
- set(gcf,'Color',[1 1 1]); %,'Position', [200, 200, 1400, 800]);
- data = [mean((TPP.tpp(:,g1)/50291),2), mean((TPP.tpp(:,g2)/50291),2)];
- data(10,:) = []; %excluding pain today from vizualization
- k = bar(data);
- k(1).FaceColor = [0.4961 0.7461 0.4805]; %green FM
- k(2).FaceColor = [0.6836 0.5508 0.7617]; %purple CO
- %ylabel(cases(c),'fontsize',14)
- ylabel('Proportion of body coloured','fontsize',14)
- ylim([0 1])
- % errhigh = [std(TPP.tpp(:,g1)'/50291)', std(TPP.tpp(:,g2)'/50291)']; %std
- errhigh = [std(TPP.tpp(:,g1)'/50291)'/sqrt(length(TPP.tpp(:,g1)')),...
- std(TPP.tpp(:,g2)'/50291)'/sqrt(length(TPP.tpp(:,g2)'))]; %SE
- errhigh(10,:) = [];
- hold on
- [ngroups,nbars] = size(data);
- x = nan(nbars, ngroups);
- for i = 1:nbars
- x(i,:) = k(i).XEndPoints;
- end
- hold on
- errorbar(x',data,errhigh,'k','linestyle','none');
- legend([k(1),k(2)],'FM','CO')
- set(gca, 'Xticklabel',labels2,'fontsize',12)
- xtickangle(30)
- title('Body image coverage','fontsize',14)
- hold off
- saveas(gcf,[cfg.outdata '/PPP_SE.png'])
- % 2-way Interaction Type x Group plot
- Act = mean(TPP.tppPOS(:,:)/50291,1)';
- Dea = mean(TPP.tppNEG(:,:)/50291,1)';
- figure
- set(gcf,'Color',[1 1 1]); %,'Position', [200, 200, 1400, 800]);
- data = [mean(Act(g1),'all','omitnan'), mean(Act(g2),'all','omitnan');...
- mean(Dea(g1),'all','omitnan'), mean(Dea(g2),'all','omitnan')];
- k = bar(data);
- k(1).FaceColor = [0.4961 0.7461 0.4805]; %green FM
- k(2).FaceColor = [0.6836 0.5508 0.7617]; %purple CO
- ylabel('PPP','fontsize',14)
- ylim([0 0.6])
- set(gca, 'Xticklabel',{'Activations', 'Deactivations'},'fontsize',14)
- errhigh = [std(Act(g1)) std(Act(g2));std(Dea(g1)) std(Dea(g2))];
- hold on
- [ngroups,nbars] = size(data);
- x = nan(nbars, ngroups);
- for i = 1:nbars
- x(i,:) = k(i).XEndPoints;
- end
- allData = {Act(g1),Act(g2),...
- Dea(g1),Dea(g2)};
- spread = 0.1; % 0=no spread; 0.5=random spread within box bounds (can be any value)
- for i = 1:numel(allData)
- p = plot(rand(size(allData{i}))*spread -(spread/2) + x(i), allData{i}, 'o','MarkerEdgeColor',[0.5 0.5 0.5]);
- end
- hold on
- e = errorbar(x',data,errhigh,'k','linestyle','none');
- hold off
- legend([k,p,e],'FM','CNT')
- saveas(gcf,[cfg.outdata '/PPP_2way_interaction.png'])
- % save coverage information
- grouping = [repmat(1,length(g1),1);repmat(2,length(g2),1)];
- Table = array2table([numArray,(TPP.tpp'),grouping],'VariableNames',['subjectNo',labels','group']);
- writetable(Table,[cfg.outdata 'PPP.csv'])
- %% 6. Load data
- Nsubj=size(subjects,1);
- for ns=1:Nsubj
- disp(['Processing subject ' subjects{ns} ' which is number ' num2str(ns) ' out of ' num2str(Nsubj)]);
- %matname=[cfg.outdata subjects{ns} '.mat'];
- matname = fullfile(cfg.outdata, [num2str(subjects{ns}) '.mat']);
- load (matname) % 'resmat','times'
- tempdata=reshape(resmat,[],size(resmat,3));
- if(ns==1)
- alldata=zeros(length(in_mask),Nsubj,size(tempdata,2));
- end
- alldata(:,ns,:)=tempdata(in_mask,:);
- end
- outfile = fullfile(cfg.outdata, 'alldata.mat');
- save(outfile, 'alldata');
- %% 7. Calculate mean intensity
- meanIntensity = zeros(size(alldata,2),size(alldata,3)); %no subjects x no emotions
- for ns=1:Nsubj
- for e = 1:cfg.Nstimuli
- map = abs(alldata(:,ns,e));
- %find non-zero pixels
- validPixels = map(map ~= 0);
- %sum of intensity
- meanIntensity(ns,e) = sum(validPixels)/numel(validPixels);
- end
- end
- Table = array2table([numArray,(meanIntensity),grouping],'VariableNames',['subjectNo',labels','group']);
- writetable(Table,[cfg.outdata 'Intensity.csv'])
- % bar plot - intensity
- figure
- set(gcf,'Color',[1 1 1]); %,'Position', [200, 200, 1400, 800]);
- data = [nanmean(meanIntensity(g1,:),1)', nanmean(meanIntensity(g2,:),1)'];
- k = bar(data);
- k(1).FaceColor = [0.4961 0.7461 0.4805]; %green FM
- k(2).FaceColor = [0.6836 0.5508 0.7617]; %purple CO
- %ylabel(cases(c),'fontsize',14)
- ylabel('Colouring intensity','fontsize',14)
- %ylim([0 1])
- errhigh = [nanstd(meanIntensity(g1,:))', nanstd(meanIntensity(g2,:))'];
- hold on
- [ngroups,nbars] = size(data);
- x = nan(nbars, ngroups);
- for i = 1:nbars
- x(i,:) = k(i).XEndPoints;
- end
- hold on
- errorbar(x',data,errhigh,'k','linestyle','none');
- legend([k(1),k(2)],'FM','CN','Location','northwest')
- set(gca, 'Xticklabel',labels,'fontsize',12)
- xtickangle(30)
- %title('Painting ','fontsize',14)
- hold off
- saveas(gcf,[cfg.outdata '/Painting_Intensity.png'])
- %% 8. Nonparametric body-maps analysis
- %Analysis for small sample sizes (N < 40 per group).
- %The analysis modeled after:
- %Torregrossa LJ, Snodgress MA, Hong SJ, Nichols HS, Glerean E, Nummenmaa L,
- %Park S. Anomalous Bodily Maps of Emotions in Schizophrenia. Schizophr
- %Bull. 2019 Sep 11;45(5):1060-1067. doi: 10.1093/schbul/sby179. PMID:
- %30551180; PMCID: PMC6737484.
- groups = {'FM', 'Control'};
- num_groups = length(groups);
- num_subjects = size(alldata, 2);
- num_emotions = size(alldata, 3);
- body_map_size = size(alldata,1);
- % Initialize matrices to store group maps
- activation_maps = zeros([body_map_size, num_emotions, num_groups]);
- deactivation_maps = zeros([body_map_size, num_emotions, num_groups]);
- for group_id = 1:num_groups
- if strcmp(groups{group_id}, 'FM')
- g = g1;
- else
- g=g2;
- end
- activation_sum = zeros(body_map_size,num_emotions);
- deactivation_sum = zeros(body_map_size,num_emotions);
- for subject_id = 1:length(g)
- map = alldata(:,g(subject_id),:);
- map = squeeze(map);
- % Count activation and deactivation
- activation_sum = activation_sum + (map > 0);
- deactivation_sum = deactivation_sum + (map < 0);
- end
- % Compute proportions
- activation_maps(:, :, group_id) = activation_sum / length(g);
- deactivation_maps(:, :, group_id) = deactivation_sum / length(g);
- end
- % Combine activation and deactivation maps for each group
- product_maps = (activation_maps - deactivation_maps) .* (activation_maps + deactivation_maps);
- D = zeros([body_map_size, num_emotions, num_groups]);
- D(:,:,1) = activation_maps(:, :, 2)- activation_maps(:, :, 1); % Activations CO - FM (FM green)
- D(:,:,2) = deactivation_maps(:, :, 2)- deactivation_maps(:, :, 1); % Deactivations Co - FM
- disp(['Max endorsement FM = ' num2str(max(abs(product_maps(:,:,1)),[],'all')) ' Max endorsement Control = ' num2str(max(abs(product_maps(:,:,2)),[],'all'))])
- % Visualization
- base=uint8(imread('bodySPM_base2.png'));
- mask=uint8(imread('bodySPM_base3.png'));
- mask=mask*.85;
- in_mask=find(mask>128);
- base2=base(10:531,33:203,:);
- %Product map
- NumCol=21;
- non_sig=1;
- hotmap=hot(NumCol-non_sig);
- coldmap=flipud([hotmap(:,3) hotmap(:,2) hotmap(:,1) ]);
- hotcoldmap=[
- coldmap
- zeros(2*non_sig,3)+0.3; %grey background
- hotmap
- ];
- M=max(abs(product_maps(:)));
- figure;
- set(gcf,'Color',[1 1 1],'Position', [100, 100, 1200, 380]);
- i=0;
- for group_id = 1:num_groups
- for emotion_id = 1:num_emotions
- i=i+1;
- temp=zeros(size(mask));
- temp(in_mask)=product_maps(:,emotion_id,group_id);
- subplot(2,num_emotions,i)
- %imagesc(temp);
- h=imagesc(temp,[-M M]);
- set(h,'AlphaData',mask)
- colormap(hotcoldmap)
- axis('off');
- axis equal
- if i==1
- text(-100, 250, 'FM', 'Fontsize', 12, 'FontWeight', 'bold', 'HorizontalAlignment','center','Rotation', 90);
- elseif i==num_emotions+1
- text(-100, 220, 'Control', 'Fontsize', 12, 'FontWeight', 'bold', 'HorizontalAlignment','center','Rotation', 90);
- end
- box off
- lbl = labels{emotion_id};
- lbl = strrep(lbl, ' ', newline);
- if group_id == 1
- title(lbl, 'FontSize', 12);
- end
- % if group_id == 1
- % title(labels(emotion_id),'fontsize',12);
- % end
- end
- end
- h = colorbar;
- h.Position = [0.93 0.1 0.02 0.8];
- saveas(gcf,[cfg.outdata 'Products_maps_by_group.png'])
- % Difference maps
- map=cbrewer('div','PiYG',21);
- map=flipud(map);
- map(11,:)=[.3 .3 .3];
- M=max(abs(D(:)));
- figure
- set(gcf,'Color',[1 1 1],'Position', [100, 100, 1200, 380]);
- i=0;
- for group_id = 1:num_groups
- for emotion_id = 1:num_emotions
- i=i+1;
- temp=zeros(size(mask));
- temp(in_mask)=D(:,emotion_id,group_id);
- subplot(2,num_emotions,i)
- h=imagesc(temp,[-M M]);
- set(h,'AlphaData',mask)
- colormap(map)
- axis('off');
- axis equal
- if i==1
- text(-100, 200, 'Activations', 'Fontsize', 12, 'FontWeight', 'bold', 'HorizontalAlignment','center','Rotation', 90);
- elseif i==num_emotions+1
- text(-100, 200, 'Deactivations', 'Fontsize', 12, 'FontWeight', 'bold', 'HorizontalAlignment','center','Rotation', 90);
- end
- box off
- title(labels(emotion_id),'fontsize',12);
- end
- end
- h = colorbar;
- h.Position = [0.93 0.1 0.02 0.8];
- saveas(gcf,[cfg.outdata 'Difference_maps.png'])
- %Legend: Group difference maps for bodily sensation of emotions. Magenta
- %represents regions where CO consistently reported more activation (top),
- %or deactivation (bottom) than FM. Green represents regions where FM
- %consistently reported more activation (top), or deactivation (bottom) than
- %CO. The color bar represents the difference between FM and CO in pixel
- %values.
- %% 9. Cosine simmilarity
- % To what extent are group-level BSMs similar to each other?
- % Based on Herman et al 2024: 10.1111/adb.13364
- CS_FM = zeros(cfg.Nstimuli-1); % cosine similarity for FM
- CS_CO = zeros(cfg.Nstimuli-1); % cosine similarity for CO
- CS = zeros(cfg.Nstimuli-1); % cosine similarity of both
- %taking out pain today as it is not relevant for CO
- product_maps2 = product_maps;
- product_maps2(:,10,:) = [];
- labels2 = labels;
- labels2(10) = [];
- for e = 1:cfg.Nstimuli-1
- for f = 1:cfg.Nstimuli-1
- CS_FM(e,f) = getCosineSimilarity(product_maps2(:,e,1),product_maps2(:,f,1));
- CS_CO(e,f) = getCosineSimilarity(product_maps2(:,e,2),product_maps2(:,f,2));
- CS(e,f) = getCosineSimilarity(product_maps2(:,e,1),product_maps2(:,f,2));
- end
- end
- save([cfg.outdata '/CSs.mat'],'CS_FM','CS_CO','CS');
- %Cosine figure
- figure
- set(gcf,'Color',[1 1 1],'Position', [200, 200, 900, 600]);
- subplot(2,2,1)
- imagesc(CS_FM); % Display correlation matrix as an image
- set(gca, 'XTick', 1:cfg.Nstimuli-1); % center x-axis ticks on bins
- set(gca, 'YTick', 1:cfg.Nstimuli-1); % center y-axis ticks on bins
- set(gca, 'XTickLabel', labels2, 'FontSize', 11); % set x-axis labels
- xtickangle(45)
- set(gca, 'YTickLabel', labels2, 'FontSize', 11); % set y-axis labels
- title('FM', 'FontSize', 14); % set title
- colormap('jet'); % Choose jet or any other color scheme
- colorbar;
- caxis([-1 1])
- subplot(2,2,2)
- imagesc(CS_CO); % Display correlation matrix as an image
- set(gca, 'XTick', 1:cfg.Nstimuli-1); % center x-axis ticks on bins
- set(gca, 'YTick', 1:cfg.Nstimuli-1); % center y-axis ticks on bins
- set(gca, 'XTickLabel', labels2, 'FontSize', 11); % set x-axis labels
- xtickangle(45)
- set(gca, 'YTickLabel', labels2, 'FontSize', 11); % set y-axis labels
- title('CO', 'FontSize', 14); % set title
- colormap('jet'); % Choose jet or any other color scheme
- caxis([-1 1])
- colorbar;
- subplot(2,2,3)
- imagesc(CS); % Display correlation matrix as an image
- set(gca, 'XTick', 1:cfg.Nstimuli-1); % center x-axis ticks on bins
- set(gca, 'YTick', 1:cfg.Nstimuli-1); % center y-axis ticks on bins
- set(gca, 'XTickLabel', labels2, 'FontSize', 10); % set x-axis labels
- xtickangle(45)
- set(gca, 'YTickLabel', labels2, 'FontSize', 10); % set y-axis labels
- xlabel('Control')
- ylabel('FM')
- title('FM:CO', 'FontSize', 14); % set title
- colormap('jet'); % Choose jet or any other color scheme
- caxis([-1 1])
- colorbar;
- subplot(2,2,4)
- DCS=CS_FM-CS_CO;
- imagesc(DCS); % Display correlation matrix as an image
- set(gca, 'XTick', 1:cfg.Nstimuli-1); % center x-axis ticks on bins
- set(gca, 'YTick', 1:cfg.Nstimuli-1); % center y-axis ticks on bins
- set(gca, 'XTickLabel', labels2, 'FontSize', 11); % set x-axis labels
- xtickangle(45)
- set(gca, 'YTickLabel', labels2, 'FontSize', 11); % set y-axis labels
- title('Difference: FM > CO', 'FontSize', 14); % set title
- map=cbrewer('div','PiYG',101);
- map=flip(map); % flip map so that green detones greater FM
- map=flipud(map);
- colormap(gca,map);
- m = round(max(abs(DCS),[],'all'),1);
- caxis([-m m]);
- hcb3=colorbar;
- saveas(gcf,[cfg.outdata '/Cosine_simmilarity.png'])
- pos = length(find(tril(DCS,-1)>0));
- total = (length(labels2)*length(labels2)-length(labels2))/2;
- disp(['FM had greater cosine similarity for ' num2str(pos) ' out of ' num2str(total) ' BSMs pairs than CO.']);
- figure
- set(gcf,'Color',[1 1 1]);
- b=diag(CS,0);
- [sortedB, sortedI] = sort(b,'descend');
- bar(sortedB,'k');
- sortedL = labels2(sortedI);
- set(gca, 'Xticklabel',sortedL,'fontsize',14);
- % get(gca,'OuterPosition')
- % set(gca,'OuterPosition',[0.3781 0.05 0.5822 1.0000])
- xlim([0 length(labels2)+1])
- set(gca,'xtick',1:length(labels2),'fontsize',12)
- xtickangle(30)
- ylabel('Cosine similarity','fontsize',14)
- box off
- saveas(gcf,[cfg.outdata '/Cosine_simmilarity_diag.png'])
- disp(['Average cosine simmilarity ' num2str(mean(b))]);
- %% 10. LDA - revised
- % Are BSMs of emotions unique in each group? Can the algoryth correctly
- % classify them?
- for ns=1:length(subjects)
- subjID=subjects{ns,:};
- disp(['Processing subject ' subjID ' which is number ' num2str(ns) ' out of ' num2str(length(subjects))]);
- matname=[cfg.outdata '/' subjID '.mat'];
- load (matname) % 'resmat','times'
- all_data(:,:,:,ns) = resmat;
- end
- all_data = all_data(:,:,(1:cfg.Nstimuli),:);
- all_data = all_data(:,:,[1:9,11:end],:);
- % for FM only
- [FM_confusion, FM_accuracy, FM_ids, FM_subject_acc] = calc_model_rev(all_data(:,:,:,g1), labels2);
- save([cfg.outdata '/LDA_FM_rev_k5.mat'],'FM_confusion', 'FM_accuracy', 'FM_ids', 'FM_subject_acc');
- % Control only
- [CO_confusion, CO_accuracy, CO_ids, CO_subject_acc] = calc_model_rev(all_data(:,:,:,g2), labels2);
- save([cfg.outdata '/LDA_CO_rev_k5.mat'],'CO_confusion', 'CO_accuracy', 'CO_ids', 'CO_subject_acc');
- Chance_Level = round(100/length(labels2));
- mean_acc_FM = mean(FM_accuracy)*100;
- disp(['For FM Chance Level = ' num2str(Chance_Level) '%. Mean classification accuracy is ' num2str(mean_acc_FM)]);
- % One-sample t-tests
- FM_subj_mean = mean(FM_subject_acc, 2);
- CO_subj_mean = mean(CO_subject_acc, 2);
- chance = round(100/length(labels2));
- [h_FM, p_FM, ci_FM, stats_FM] = ttest(FM_subj_mean, chance);
- disp(['FM t-test vs chance: p = ' num2str(p_FM)]);
- disp(stats_FM);
- [h_CO, p_CO, ci_CO, stats_CO] = ttest(CO_subj_mean, chance);
- disp(['CO t-test vs chance: p = ' num2str(p_CO)]);
- disp(stats_CO);
- % PERMUTATION TESTING
- % FM group
- [p_FM, null_FM, acc_FM, thr95_FM, thr99_FM] = ...
- permutation_test_LDA(all_data(:,:,:,g1), labels2, 1000);
- % CO group
- [p_CO, null_CO, acc_CO, thr95_CO, thr99_CO] = ...
- permutation_test_LDA(all_data(:,:,:,g2), labels2, 1000);
- % Plot it
- figure;
- tiledlayout(1,2)
- nexttile %FM
- histogram(null_FM, 30);
- hold on;
- xline(acc_FM, 'r', 'LineWidth', 2);
- xline(thr95_FM, '--k');
- title('Permutation test - FM');
- xlabel('Accuracy');
- ylabel('Frequency');
- nexttile % CO
- histogram(null_CO, 30);
- hold on;
- xline(acc_CO, 'r', 'LineWidth', 2);
- xline(thr95_CO, '--k');
- title('Permutation test - CO');
- xlabel('Accuracy');
- ylabel('Frequency');
- % check for differences:
- all_acc = [FM_subject_acc; CO_subject_acc];
- nFM = size(FM_subject_acc,1);
- labels = [ones(nFM,1); zeros(size(CO_subject_acc,1),1)];
- obs_diff = mean(FM_subject_acc(:)) - mean(CO_subject_acc(:));
- n_perm = 1000;
- perm_diff = zeros(n_perm,1);
- for i = 1:n_perm
- perm_labels = labels(randperm(length(labels2)));
- FM_perm = all_acc(perm_labels == 1,:);
- CO_perm = all_acc(perm_labels == 0,:);
- perm_diff(i) = mean(FM_perm(:)) - mean(CO_perm(:));
- end
- p_group = (sum(abs(perm_diff) >= abs(obs_diff)) + 1) / (n_perm + 1);
- %get the results:
- chance = 1 / length(labels2); % e.g., 1/12
- fprintf('\n--- FM ---\n');
- fprintf('Accuracy: %.3f (%.1f%%)\n', acc_FM, acc_FM*100);
- fprintf('Chance: %.3f (%.1f%%)\n', chance, chance*100);
- fprintf('p-value: %.4f\n', p_FM);
- fprintf('95%% threshold: %.3f\n', thr95_FM);
- fprintf('99%% threshold: %.3f\n', thr99_FM);
- fprintf('\n--- CO ---\n');
- fprintf('Accuracy: %.3f (%.1f%%)\n', acc_CO, acc_CO*100);
- fprintf('Chance: %.3f (%.1f%%)\n', chance, chance*100);
- fprintf('p-value: %.4f\n', p_CO);
- fprintf('95%% threshold: %.3f\n', thr95_CO);
- fprintf('99%% threshold: %.3f\n', thr99_CO);
- fprintf('group differences p-val: %.3f\n', p_group);
- % ANCOVA with permutations (correcting for education and socioeconomic
- % status)
- all_acc = [FM_subject_acc; CO_subject_acc];
- nFM = size(FM_subject_acc,1);
- group = [ones(nFM,1); zeros(size(CO_subject_acc,1),1)];
- [nSubj, nLabels] = size(all_acc);
- z_edu = zscore(edu);
- z_ses = zscore(ses);
- % Remove covariance effects for group effect
- subj_mean = mean(all_acc,2);
- Xcov_group = [ones(nSubj,1), z_edu, z_ses];
- b_group = Xcov_group \ subj_mean;
- % residualised subject-level accuracy
- subj_mean_adj = subj_mean - Xcov_group(:,2:end) * b_group(2:end);
- % Main effect of GROUP
- obs_group = mean(subj_mean_adj(group==1)) - ...
- mean(subj_mean_adj(group==0));
- % permutation test
- n_perm = 1000;
- perm_group_stat = zeros(n_perm,1);
- for p = 1:n_perm
- perm_group = group(randperm(nSubj));
- perm_group_stat(p) = ...
- mean(subj_mean_adj(perm_group==1)) - ...
- mean(subj_mean_adj(perm_group==0));
- end
- p_group = ...
- (sum(abs(perm_group_stat) >= abs(obs_group)) + 1) ...
- / (n_perm + 1);
- disp(['Main effect of GROUP p = ' num2str(p_group)]);
- % Main effect of BODYMAP
- Xcov = [ones(nSubj,1), z_edu, z_ses];
- Y_adj = zeros(size(all_acc));
- for lab = 1:nLabels
- y = all_acc(:,lab);
- b = Xcov \ y;
- % residualise covariates
- Y_adj(:,lab) = y - Xcov(:,2:end) * b(2:end);
- end
- label_means = mean(Y_adj,1);
- obs_label = sum((label_means - mean(label_means)).^2);
- perm_label_stat = zeros(n_perm,1);
- for p = 1:n_perm
- Y_perm = zeros(size(Y_adj));
- % permute labels WITHIN subjects
- for s = 1:nSubj
- Y_perm(s,:) = Y_adj(s, randperm(nLabels));
- end
- perm_label_means = mean(Y_perm,1);
- perm_label_stat(p) = ...
- sum((perm_label_means - mean(perm_label_means)).^2);
- end
- p_label = ...
- (sum(perm_label_stat >= obs_label) + 1) ...
- / (n_perm + 1);
- disp(['Main effect of BODYMAP p = ' num2str(p_label)]);
- % INTERACTION
- FM_mean = mean(Y_adj(group==1,:),1);
- CO_mean = mean(Y_adj(group==0,:),1);
- obs_interaction = FM_mean - CO_mean;
- obs_interaction_stat = sum(obs_interaction.^2);
- perm_interaction_stat = zeros(n_perm,1);
- for p = 1:n_perm
- perm_group = group(randperm(nSubj));
- FM_perm = mean(Y_adj(perm_group==1,:),1);
- CO_perm = mean(Y_adj(perm_group==0,:),1);
- perm_diff = FM_perm - CO_perm;
- perm_interaction_stat(p) = sum(perm_diff.^2);
- end
- p_interaction = ...
- (sum(perm_interaction_stat >= obs_interaction_stat) + 1) ...
- / (n_perm + 1);
- disp(['INTERACTION p = ' ...
- num2str(p_interaction)]);
- %Variance explainced:
- subj_mean = mean(all_acc,2);
- mdl = fitlm([edu ses], subj_mean);
- disp(mdl.Rsquared)
- % Calculate PCA variance CV
- [mean_explained_FM, cumvar_FM] = ...
- calc_pca_variance(all_data(:,:,:,g1), labels2);
- [mean_explained_CO, cumvar_CO] = ...
- calc_pca_variance(all_data(:,:,:,g2), labels2);
- cumvar_5FM = cumvar_FM(5);
- cumvar_5CO = cumvar_CO(5);
- fprintf('FM: Variance explained by 5 PCs = %.2f%%\n', cumvar_5FM);
- fprintf('CO: Variance explained by 5 PCs = %.2f%%\n', cumvar_5CO);
- fprintf('FM: Variance explained by 30 PCs = %.2f%%\n', cumvar_FM(30));
- fprintf('CO: Variance explained by 30 PCs = %.2f%%\n', cumvar_CO(30));
- % Plot confusion matrix
- cm = CO_confusion{:,:};
- row_sums = sum(cm, 2);
- rev_ct = cm .* 100 ./ row_sums;
- global_max = max(rev_ct(:));
- fprintf('CO global max is %.2f%%\n', global_max);
- limits2 = [0 round(global_max)];
- figure;
- fig = gcf;
- fig.Position = [100 100 1500 600]; % wider figure, adjust as needed
- tiledlayout(1,2, 'Padding', 'compact', 'TileSpacing', 'compact');
- ax1 = nexttile;
- confusion_matrix_perc_v2(FM_confusion, labels2, limits2, ax1);
- title(ax1, 'A. Fibromyalgia Group');
- ax2 = nexttile;
- confusion_matrix_perc_v2(CO_confusion, labels2, limits2, ax2);
- title(ax2, 'B. Control Group');
- saveas(gcf,[cfg.outdata '/Confusion_matrix_rev.png'])
PAID embody analysis.m, no license · at the source
Overview
- Laboratory of Brain Imaging, Nencki Institute of Experimental Biology of the Polish Academy of Sciences, Pasteur 3 St, Warsaw, Poland
- IIMPACT in Health, Adelaide University, Adelaide, South Australia Australia
- Persistent Pain Research Group, Hopwood Centre for Neurobiology, Lifelong Health Theme, South Australian Health and Medical Research Institute (SAHMRI), Adelaide, South Australia Australia
- Brain Stimulation, Imaging and Cognition Research Group, College of Health, Adelaide University, Adelaide, SA Australia
- Hopwood Centre for Neurobiology, South Australian Health and Medical Research Institute, Adelaide, SA Australia
- Women’s and Children’s Hospital, Adelaide, SA Australia
Abstract
People with fibromyalgia often demonstrate hypersensitivity to benign bodily signals and misattribute non-noxious physical signals as pain, suggesting alterations in body-brain communication and interoception. However, little is known about whether altered body-brain communication may extend to misattribution of emotion as pain. Here, using a cross-sectional design, we aimed to investigate how individuals with fibromyalgia perceive, identify, and interpret emotions and other bodily sensations, including pain, compared to healthy controls. Across two independent studies, individuals with fibromyalgia and age- and gender-matched pain-free controls completed the emBODY task, which assesses the topography of bodily sensations experienced for different states (emotional states, neutral state, pain- and physiology-related states, fatigue). Additionally, participants completed assessments of alexithymia, bodily sensation interpretation, and interoception. In both studies, linear discriminant analysis, performed to test whether different emotions and states are associated with statistically distinct bodily patterns, indicated comparable classification accuracy of body sensation maps in the fibromyalgia group and controls. However, compared with controls, the fibromyalgia group showed higher levels of alexithymia, higher awareness of bodily signals, and more negative interpretation of ambiguous bodily sensations in daily life, together with greater self-reported interoceptive difficulties. Our findings support an amplified perception of bodily signals coupled with interoceptive difficulties in people with fibromyalgia. This raises the possibility that interventions that target perceptual retraining, and re-interpretation of bodily sensations might decrease the impact of pain and promote engagement in everyday activities for people with fibromyalgia.
Supplementary Information: The online version contains supplementary material available at https://
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 1 match between paragraphs and lines of code.
OSF 4du3f
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
- 27 September 2026: the link answers (HTTP 200)
8 files
- WP1 - Behavioural project/
Outputs/ , R, 345 linesBehavioural_analysis_com plete.R - WP1 - Behavioural project/
Outputs/ , R, 364 linesSensitivity_analysis.R - WP1 - Behavioural project/
Tasks/ , MATLAB, 1,001 linesCognitiveEffortDiscounti ngTest.m - WP1 - Behavioural project/
Tasks/ , MATLAB, 804 linesDelayDiscountingTest.m - WP1 - Behavioural project/
Tasks/ , MATLAB, 849 linesEffortDisc_Treadmill_tes t.m - WP1 - Behavioural project/
Tasks/ , MATLAB, 200 linesVAS.m - WP1 - Behavioural project/
Tasks/ , MATLAB, 77 linesnBackCreateTrialList_rev .m - WP1 - Behavioural project/
emBODY and interoception/ , MATLAB, 921 lines, 1 matchPAID embody analysis.m
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;
- 8 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
No dataset and no data link were found in the paper.
Data availability
Data and analyses scripts are available via Open Science Framework (OSF): < 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 2, 28 September 2026
- Publisher: n/a → Springer Science+Business Media
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 4 authors, 12 MeSH terms, 1 funder, 61 references.
Cite
This paper
Herman, A. M., Szczawińska, J., Berryman, C., & Stanton, T. R. (2026). When emotions hurt: negative interpretations of bodily signals and interoceptive difficulties in fibromyalgia. Psychological research, 90(4), 125. https://
BibTeX
@article{herman2026when,
author = {Herman, Aleksandra M and Szczawińska, Julia and Berryman, Carolyn and Stanton, Tasha R},
title = {{When emotions hurt: negative interpretations of bodily signals and interoceptive difficulties in fibromyalgia}},
journal = {Psychological research},
year = {2026},
month = jul,
volume = {90},
number = {4},
pages = {125},
publisher = {Springer Science+Business Media},
issn = {0340-0727},
doi = {10.1007/
url = {https://
pmid = {42390617},
pmcid = {PMC13328158}
}
RIS
TY - JOUR
AU - Herman, Aleksandra M
AU - Szczawińska, Julia
AU - Berryman, Carolyn
AU - Stanton, Tasha R
TI - When emotions hurt: negative interpretations of bodily signals and interoceptive difficulties in fibromyalgia
T2 - Psychological research
J2 - Psychol Res
PY - 2026
DA - 2026/
VL - 90
IS - 4
SP - 125
SN - 0340-0727
PB - Springer Science+Business Media
DO - 10.1007/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1007/
"type": "article-journal",
"title": "When emotions hurt: negative interpretations of bodily signals and interoceptive difficulties in fibromyalgia",
"container-title": "Psychological research",
"author": [
{
"family": "Herman",
"given": "Aleksandra M"
},
{
"family": "Szczawińska",
"given": "Julia"
},
{
"family": "Berryman",
"given": "Carolyn"
},
{
"family": "Stanton",
"given": "Tasha R"
}
],
"container-title-short":
"volume": "90",
"issue": "4",
"page": "125",
"DOI": "10.1007/
"PMID": "42390617",
"PMCID": "PMC13328158",
"ISSN": "0340-0727",
"publisher": "Springer Science+Business Media",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
2
]
]
}
}
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/s41467-026-74331-2 [code]
- Trial-by-trial fMRI-neurofeedback dissociates fusiform and occipital contributions to face detection and recognition.Journal: Nature communicationsIn common: Psychtoolbox, emmeans, lme4, 4 other tools
- [2] doi:10.1093/braincomms/fcag279 [code]
- Network flexibility facilitates treatment-induced recovery in post-stroke aphasia.Journal: Brain communicationsIn common: emmeans, lme4, ggpubr, 4 other tools
- [3] doi:10.1111/ejn.70481 [code]
- Neural and Behavioral Tracking of Musical Phrases Occurs Without Temporal Regularity.Journal: The European journal of neuroscienceIn common: Psychtoolbox, lme4, ggpubr, 3 other tools, cognitive
- [4] doi:10.1016/j.isci.2026.116458 [code]
- Neural tracking of prosodic and statistical rhythms jointly supports artificial language learning.Journal: iScienceIn common: Psychtoolbox, emmeans, lme4, 3 other tools, cognitive
- [5] doi:10.7554/elife.103846 [code]
- Overt visual attention modulates decision-related signals in the frontal cortex.Journal: eLifeIn common: Psychtoolbox, lme4, ggpubr, 3 other tools, cognitive
- [6] doi:10.1192/bjp.2026.10664 [code]
- Early effects of a novel 5-HT&
lt;sub& gt;4& lt;/ sub& gt;R agonist (PF-04995274) and the SSRI citalopram on emotional cognition in unmedicated depression: RESTAND study. Journal: The British journal of psychiatry : the journal of mental scienceIn common: emmeans, lme4, ggpubr, 2 other tools, cognitive, 1 reference - [7] doi:10.1038/s41593-026-02345-6 [code]
- Human hippocampal ripples tune cortical responses based on predicted uncertainty.Journal: Nature neuroscienceIn common: emmeans, lme4, ggpubr, 3 other tools, cognitive
- [8] doi:10.1016/j.neuroimage.2026.122115 [code]
- Midfrontal theta power relates to response speeding following frustrative nonreward.Journal: NeuroImageIn common: emmeans, lme4, ggpubr, 3 other tools, cognitive
- [9] doi:10.1038/s42003-026-10040-2 [code]
- Functional dissociation of language and theory of mind in the developing superior temporal lobe.Journal: Communications biologyIn common: emmeans, lme4, ggpubr, 3 other tools, cognitive
- [10] doi:10.1523/eneuro.0076-26.2026 [code]
- Exogenously Driven Neural Reactivation of Spatially Matching Visual Working-Memory Contents.Journal: eNeuroIn common: emmeans, lme4, ggpubr, 3 other tools, cognitive
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, 8 scripts, and 1 match 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:d087c770706bf827…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[.
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
