-
Notifications
You must be signed in to change notification settings - Fork 0
/
circ_ksdensity.m
134 lines (95 loc) · 4.12 KB
/
circ_ksdensity.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
function [vfEstimate] = circ_ksdensity(vfObservations, vfPDFSamples, vfDomain, fSigma, vfWeights)
% circ_ksdensity FUNCTION - Compute a kernel density estimate over a periodic domain
%
% Usage: [vfEstimate] = circ_ksdensity(vfObservations, vfPDFSamples,
% <vfDomain, fSigma, vfWeights>)
%
% This function calculates a kernel density estimate of an (optionally
% weighted) data sample, over a periodic domain.
%
% 'vfObservations' is a set of observations made over a periodic domain,
% optionally defined by 'vfDomain': [fMin fMax]. The default domain is
% [0..2*pi]. 'vfPDFSamples' defines the sample points over which to perform
% the kernel density estimate, over the same domain as 'vfObservations'.
%
% Weighted estimations can be performed by providing the optional argument
% 'vfWeights', where each element in 'vfWeights' corresponds to the
% matching element in 'vfObservations'.
%
% The kernel density estimate will be performed using a wrapped Gaussian
% kernel, with a width estimated as
% (4/3)^0.2 * circ_std(vfObservations, vfWeights) * (length(vfObservations^-0.2)
%
% The optional argument 'fSigma' can be provided to set the width of the
% kernel.
%
% 'vfEstimate' will be a vector with a (weighted) estimate of the
% underlying distribution, with an entry for each element of
% 'vfPDFSamples'. If no weighting is supplied, the estimate will be scaled
% such that it forms a PDF estimate over the supplied sample domain, taking
% into account sample bin widths. If a weight vector is supplied then the
% estimate will be scaled such that the sum over the domain attempts to
% match the sum of weights, taking into account sample bin widths.
% Author: Dylan Muir <dylan.muir@unibas.ch>
% Created: 23rd October, 2013
% -- Defaults
DEF_vfDomain = [0 2*pi];
% -- Check arguments
if (nargin < 2)
help circ_ksdensity;
error('circ_ksdensity:Usage', ...
'*** circ_ksdensity: Incorrect usage');
end
if (~exist('vfDomain', 'var') || isempty(vfDomain))
vfDomain = DEF_vfDomain;
end
% - Do we need to estimate fSigma?
if (~exist('fSigma', 'var'))
% - Sigma will be estimated
fSigma = [];
end
vfObservations = vfObservations(:);
vnPSFSamplesSize = size(vfPDFSamples);
vfPDFSamples = vfPDFSamples(:);
% - If weights are not provided, weight each observation equally
if (~exist('vfWeights', 'var'))
vfWeights = ones(size(vfObservations)) ./ numel(vfObservations);
% - Check the number of observations matches the number of weights
elseif (numel(vfObservations) ~= numel(vfWeights))
error('circ_ksdensity:Usage', ...
'*** circ_ksdensity: The number of observations must be equal to the number of weights.');
end
% -- Map everything to [0..2pi] and wrap over domain
vfObservations = (vfObservations - vfDomain(1)) ./ diff(vfDomain) .* 2*pi;
vfObservations = mod(vfObservations, 2*pi);
vfPDFSamples = (vfPDFSamples - vfDomain(1)) ./ diff(vfDomain) .* 2*pi;
vfPDFSamples = mod(vfPDFSamples, 2*pi);
% - Estimate sigma, if necessary
if (isempty(fSigma))
fSigma = (4/3)^0.2 * circ_std(vfObservations, vfWeights) * (numel(vfObservations)^-0.2);
end
% -- Pad observations above and below domain
vfObservations = [vfObservations;
vfObservations - 2*pi;
vfObservations + 2*pi];
vfWeights = repmat(vfWeights, 3, 1) ./ 3;
% -- Perform kernel density estimate
vfEstimate = ksdensity(vfObservations, vfPDFSamples, 'weights', vfWeights', 'width', fSigma);
% - Reshape return to match shape of 'vfPDFSamples'
vfEstimate = reshape(vfEstimate, vnPSFSamplesSize);
% - Correct scaling of histogram estimate
vfEstimate = vfEstimate .* 3 .* sum(vfWeights);
% --- END of circ_ksdensity FUNCTION ---
% circ_std FUNCTION Estimate the weighted circular standard deviation of a dataset
function [s s0] = circ_std(alpha, w)
% compute mean resultant vector length
r = circ_r(alpha,w);
s = sqrt(2*(1-r)); % 26.20
s0 = sqrt(-2*log(r)); % 26.21
% circ_r FUNCTION Compute the weighted resultant of a dataset
function r = circ_r(alpha, w)
% compute weighted sum of cos and sin of angles
r = sum(w.*exp(1i*alpha),1);
% obtain length
r = abs(r)./sum(w,1);
% --- END of circ_ksdensity.m ---