circ_wwtest.m 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. function [pval table] = circ_wwtest(varargin)
  2. % [pval, table] = circ_wwtest(alpha, idx, [w])
  3. % [pval, table] = circ_wwtest(alpha1, alpha2, [w1, w2])
  4. % Parametric Watson-Williams multi-sample test for equal means. Can be
  5. % used as a one-way ANOVA test for circular data.
  6. %
  7. % H0: the s populations have equal means
  8. % HA: the s populations have unequal means
  9. %
  10. % Note:
  11. % Use with binned data is only advisable if binning is finer than 10 deg.
  12. % In this case, alpha is assumed to correspond
  13. % to bin centers.
  14. %
  15. % The Watson-Williams two-sample test assumes underlying von-Mises
  16. % distributrions. All groups are assumed to have a common concentration
  17. % parameter k.
  18. %
  19. % Input:
  20. % alpha angles in radians
  21. % idx indicates which population the respective angle in alpha
  22. % comes from, 1:s
  23. % [w number of incidences in case of binned angle data]
  24. %
  25. % Output:
  26. % pval p-value of the Watson-Williams multi-sample test. Discard H0 if
  27. % pval is small.
  28. % table cell array containg the ANOVA table
  29. %
  30. % PHB 3/19/2009
  31. %
  32. % References:
  33. % Biostatistical Analysis, J. H. Zar
  34. %
  35. % Circular Statistics Toolbox for Matlab
  36. % By Philipp Berens, 2009
  37. % berens@tuebingen.mpg.de - www.kyb.mpg.de/~berens/circStat.html
  38. [alpha, idx, w] = processInput(varargin{:});
  39. % number of groups
  40. u = unique(idx);
  41. s = length(u);
  42. % number of samples
  43. n = sum(w);
  44. % compute relevant quantitites
  45. pn = zeros(s,1); pr = pn;
  46. for t=1:s
  47. pidx = idx == u(t);
  48. pn(t) = sum(pidx.*w);
  49. pr(t) = circ_r(alpha(pidx),w(pidx));
  50. end
  51. r = circ_r(alpha,w);
  52. rw = sum(pn.*pr)/n;
  53. % make sure assumptions are satisfied
  54. checkAssumption(rw,mean(pn))
  55. % test statistic
  56. kk = circ_kappa(rw);
  57. beta = 1+3/(8*kk); % correction factor
  58. A = sum(pr.*pn) - r*n;
  59. B = n - sum(pr.*pn);
  60. F = beta * (n-s) * A / (s-1) / B;
  61. pval = 1 - fcdf(F,s-1,n-s);
  62. na = nargout;
  63. if na < 2
  64. printTable;
  65. end
  66. prepareOutput;
  67. function printTable
  68. fprintf('\nANALYSIS OF VARIANCE TABLE (WATSON-WILLIAMS TEST)\n\n');
  69. fprintf('%s\t\t\t\t%s\t%s\t\t%s\t\t%s\t\t\t%s\n', ' ' ,'d.f.', 'SS', 'MS', 'F', 'P-Value');
  70. fprintf('--------------------------------------------------------------------\n');
  71. fprintf('%s\t\t\t%u\t\t%.2f\t%.2f\t%.2f\t\t%.4f\n', 'Columns', s-1 , A, A/(s-1), F, pval);
  72. fprintf('%s\t\t%u\t\t%.2f\t%.2f\n', 'Residual ', n-s, B, B/(n-s));
  73. fprintf('--------------------------------------------------------------------\n');
  74. fprintf('%s\t\t%u\t\t%.2f', 'Total ',n-1,A+B);
  75. fprintf('\n\n')
  76. end
  77. function prepareOutput
  78. if na > 1
  79. table = {'Source','d.f.','SS','MS','F','P-Value'; ...
  80. 'Columns', s-1 , A, A/(s-1), F, pval; ...
  81. 'Residual ', n-s, B, B/(n-s), [], []; ...
  82. 'Total',n-1,A+B,[],[],[]};
  83. end
  84. end
  85. end
  86. function checkAssumption(rw,n)
  87. if n > 10 && rw<.45
  88. warning('Test not applicable. Average resultant vector length < 0.45.') %#ok<WNTAG>
  89. elseif n > 6 && rw<.5
  90. warning('Test not applicable. Average number of samples per population < 11 and average resultant vector length < 0.5.') %#ok<WNTAG>
  91. elseif n >=5 && rw<.55
  92. warning('Test not applicable. Average number of samples per population < 7 and average resultant vector length < 0.55.') %#ok<WNTAG>
  93. elseif n < 5
  94. warning('Test not applicable. Average number of samples per population < 5.') %#ok<WNTAG>
  95. end
  96. end
  97. function [alpha, idx, w] = processInput(varargin)
  98. if nargin == 4
  99. alpha1 = varargin{1}(:);
  100. alpha2 = varargin{2}(:);
  101. w1 = varargin{3}(:);
  102. w2 = varargin{4}(:);
  103. alpha = [alpha1; alpha2];
  104. idx = [ones(size(alpha1)); ones(size(alpha2))];
  105. w = [w1; w2];
  106. elseif nargin==2 && sum(abs(round(varargin{2})-varargin{2}))>1e-5
  107. alpha1 = varargin{1}(:);
  108. alpha2 = varargin{2}(:);
  109. alpha = [alpha1; alpha2];
  110. idx = [ones(size(alpha1)); 2*ones(size(alpha2))];
  111. w = ones(size(alpha));
  112. elseif nargin==2
  113. alpha = varargin{1}(:);
  114. idx = varargin{2}(:);
  115. if ~(size(idx,1)==size(alpha,1))
  116. error('Input dimensions do not match.')
  117. end
  118. w = ones(size(alpha));
  119. elseif nargin==3
  120. alpha = varargin{1}(:);
  121. idx = varargin{2}(:);
  122. w = varargin{3}(:);
  123. if ~(size(idx,1)==size(alpha,1))
  124. error('Input dimensions do not match.')
  125. end
  126. if ~(size(w,1)==size(alpha,1))
  127. error('Input dimensions do not match.')
  128. end
  129. else
  130. error('Invalid use of circ_wwtest. Type help circ_wwtest.')
  131. end
  132. end