0todd0000 / spm1d

One-Dimensional Statistical Parametric Mapping in Python
GNU General Public License v3.0
60 stars 21 forks source link

Plotting issue #287

Open ThiagoBorges81 opened 1 month ago

ThiagoBorges81 commented 1 month ago

Hello, Todd.

I hope you are well. Please, I was wondering if you could point me towards a direction to format the plot for the anova2nested. The labels for the p-values, the alphas, and the F stats are overlapping in the plot, which makes it difficult to see and understand the effects. Also, I would like to extend the x-axis so that the number 40 also appears in the axis. Please, see a sample of what is happening:

image

I searched for the code that generate the plots, but I could not find it in the directories where spm1d is installed. Any assistance would be appreciated.

Kind regards

Thiago

0todd0000 commented 1 month ago

spm1d supports just basic plotting by default so you'd need to customize things a bit...

I would like to extend the x-axis so that the number 40 also appears in the axis

Try using set_xlim like this:

import matplotlib.pyplot as plt
import spm1d

dataset = spm1d.data.uv1d.anova2nested.SPM1D_ANOVA2NESTED_3x3()
Y,A,B   = dataset.get_data()
FF      = spm1d.stats.anova2nested(Y, A, B, equal_var=True).inference(0.05)
F       = FF[0]

plt.figure()
ax = plt.axes()
F.plot( ax=ax )
ax.set_xlim(0, 40)
plt.show()



The labels for the p-values, the alphas, and the F stats are overlapping in the plot, which makes it difficult to see and understand the effects.

Either (a) use the offsets keyword argument to offset the p-value labels from their default positions (the default position is the cluster centroid). For example

# offsets in vertical direction
# --- number of 2-tuples must be the same as the number of clusters
offsets = [(0,3), (0,4), (0,5), (0,6)]  
F.plot_p_values( ax=ax, offsets=offsets )

or (b) you could create your own custom version of plot_p_values whose source code is available here.

ThiagoBorges81 commented 1 month ago

Hello, Todd.

Thank you very much for getting back to me. Most appreciated.

The solutions you provided are pointing me to the class SPMiPlotter source code. However, the spm1d package I have installed is using the class SPMList. This class is neither in the _plot.py nor in the plot.py files.

This is the version I have installed: spm1d==0.4.28

Please, find below the code I am using:

#(1) Conduct ANOVA:
alpha        = 0.05
FF           = spm1d.stats.anova2nested(Y_vel, A_vel, B_vel, equal_var=True)
FFi          = FF.inference(0.05)
print( FFi )

#(2) Plot results:
plt.close('all')
FFi.plot(plot_threshold_label=True, plot_p_values=True)
plt.show()

And here is the output: image

As I was not getting the results I expected, I threw a random argument to double-check which class the package was calling, and I got the following error:

{
    "name": "TypeError",
    "message": "SPMFList.plot() got an unexpected keyword argument 'color'",
    "stack": "---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[67], line 14
      8 #(2) Plot results:
      9 # plt.close('all')
     10 # plt.figure()
     11 # plt.axes()
     13 offsets = [(0,3),(0,4),(0,5),(0,6),(0,7)]
---> 14 fig = FFi.plot(plot_threshold_label=True, plot_p_values=True, color='g')
     16 plt.show()

TypeError: SPMFList.plot() got an unexpected keyword argument 'color'"
}

So, it is pointing to the SPMFList class instead of the SPMiPlotter.

Any help would be appreciated.

Kind regards

Thiago

0todd0000 commented 1 month ago

Understood, thank you for clarifying. Note that FFi.plot calls _plot_F_list which is in _plot.py here. You'll see in _plot_F_list that the p-value plotting function is the same as the one above: F.plot_p_values. Please note that the FFi.plot method (like all plotting methods in spm1d) is just meant to be a convenience visualization method and is not meant to offer comprehensive figure customization options. When plotting from a list of F objects it is particularly difficult to offer separate customizations for each F plot, so if you wish to customize the default plot I suggest creating a multi-panel figure, plotting each F object in a separate panel, and then customizing that panel as desired. You can use _plot_F_list as a template if you wish but I think it might be easiest just to call something like plt.subplots and the customizing.