sapor 0.1b1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Area Class Diagram.dia +0 -0
- data/Area Class Diagram.png +0 -0
- data/Class Diagram.dia +0 -0
- data/Class Diagram.png +0 -0
- data/Examples.md +361 -0
- data/LICENSE +674 -0
- data/README.md +70 -0
- data/Rakefile +18 -0
- data/Technical Documentation.md +14 -0
- data/bin/create_installation_package.sh +49 -0
- data/bin/install.sh +45 -0
- data/bin/sapor.rb +22 -0
- data/bin/sapor.sh +105 -0
- data/lib/sapor.rb +44 -0
- data/lib/sapor/binomials_cache.rb +45 -0
- data/lib/sapor/combinations_distribution.rb +180 -0
- data/lib/sapor/dichotomies.rb +98 -0
- data/lib/sapor/dichotomy.rb +138 -0
- data/lib/sapor/first_past_the_post.rb +78 -0
- data/lib/sapor/leveled_proportional.rb +64 -0
- data/lib/sapor/log4r_logger.rb +49 -0
- data/lib/sapor/log_facade.rb +40 -0
- data/lib/sapor/number_formatter.rb +45 -0
- data/lib/sapor/poll.rb +137 -0
- data/lib/sapor/polychotomy.rb +359 -0
- data/lib/sapor/proportional.rb +128 -0
- data/lib/sapor/pseudorandom_multirange_enumerator.rb +87 -0
- data/lib/sapor/regional_data/area.rb +80 -0
- data/lib/sapor/regional_data/catalonia-2012-2015.psv +100 -0
- data/lib/sapor/regional_data/catalonia-2012.psv +87 -0
- data/lib/sapor/regional_data/catalonia.rb +90 -0
- data/lib/sapor/regional_data/norway.rb +408 -0
- data/lib/sapor/regional_data/united_kingdom.rb +1075 -0
- data/lib/sapor/regional_data/utopia.rb +66 -0
- data/sapor.gemspec +35 -0
- data/spec/integration/area_spec.rb +28 -0
- data/spec/integration/poll_spec.rb +107 -0
- data/spec/integration/sample.poll +7 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/unit/area_spec.rb +115 -0
- data/spec/unit/binomials_cache_spec.rb +34 -0
- data/spec/unit/catalonia_spec.rb +82 -0
- data/spec/unit/combinations_distribution_spec.rb +241 -0
- data/spec/unit/denominators_spec.rb +34 -0
- data/spec/unit/dichotomies_spec.rb +154 -0
- data/spec/unit/dichotomy_spec.rb +320 -0
- data/spec/unit/first_past_the_post_spec.rb +53 -0
- data/spec/unit/leveled_proportional_spec.rb +51 -0
- data/spec/unit/norway_spec.rb +47 -0
- data/spec/unit/number_formatter_spec.rb +173 -0
- data/spec/unit/poll_spec.rb +105 -0
- data/spec/unit/polychotomy_spec.rb +332 -0
- data/spec/unit/proportional_spec.rb +86 -0
- data/spec/unit/pseudorandom_multirange_enumerator_spec.rb +82 -0
- metadata +119 -0
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
Statistical Analysis of Polling Results (SAPoR)
|
2
|
+
===============================================
|
3
|
+
|
4
|
+
Installation
|
5
|
+
------------
|
6
|
+
|
7
|
+
You can install this program by cloning this repository, and then executing the
|
8
|
+
following commands in the directory where it got cloned:
|
9
|
+
|
10
|
+
sudo gem build sapor.gemspec
|
11
|
+
sudo gem install sapor-0.1b1.gem
|
12
|
+
|
13
|
+
bin/create_installation_package.sh
|
14
|
+
tar -xzf sapor-0.1b1.tar.gz
|
15
|
+
cd sapor-0.1b1/
|
16
|
+
sudo ./install.sh
|
17
|
+
|
18
|
+
Usage
|
19
|
+
-----
|
20
|
+
|
21
|
+
Simply call `sapor` or `sapor help` to get instructions on how to use the
|
22
|
+
program.
|
23
|
+
|
24
|
+
Examples
|
25
|
+
--------
|
26
|
+
|
27
|
+
See [this](Examples.md) page.
|
28
|
+
|
29
|
+
Technical Documentation
|
30
|
+
-----------------------
|
31
|
+
|
32
|
+
See [this](Technical%20Documentation.md) page.
|
33
|
+
|
34
|
+
Changelog
|
35
|
+
---------
|
36
|
+
|
37
|
+
**Version 0.1**
|
38
|
+
|
39
|
+
* First round analysis of poll results as a set of dichotomies, reporting on the
|
40
|
+
most probable fraction, the 95% confidence interval for the vote share, and
|
41
|
+
probability to reach a threshold
|
42
|
+
* Second round analysis of poll results as a polychotomy, reporting on the most
|
43
|
+
probable fraction, the most probable rounded fraction, the 95% confidence
|
44
|
+
interval for the vote share, the probability to be larger than the next party,
|
45
|
+
the 95% confidence interval for the number of seats in parliament, and for
|
46
|
+
coalitions the most probable fraction, the most probable rounded fraction, the
|
47
|
+
95% confidence interval for the vote share, the probability to have a majority
|
48
|
+
of the popular vote (vote share larger than 50%), the 95% confidence interval
|
49
|
+
for the number of seats and the probability to have a majority in parliament.
|
50
|
+
* Areas: Catalan parliamentary election, 2015
|
51
|
+
|
52
|
+
License
|
53
|
+
-------
|
54
|
+
|
55
|
+
Statistical Analysis of Polling Results (SAPoR)
|
56
|
+
Copyright (C) 2014 Filip van Laenen <f.a.vanlaenen@ieee.org>
|
57
|
+
|
58
|
+
This program is free software; you can redistribute it and/or modify
|
59
|
+
it under the terms of the GNU General Public License as published by
|
60
|
+
the Free Software Foundation; either version 2 of the License, or
|
61
|
+
(at your option) any later version.
|
62
|
+
|
63
|
+
This program is distributed in the hope that it will be useful,
|
64
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
65
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
66
|
+
GNU General Public License for more details.
|
67
|
+
|
68
|
+
You should have received a copy of the GNU General Public License along
|
69
|
+
with this program; if not, write to the Free Software Foundation, Inc.,
|
70
|
+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
5
|
+
desc 'Default: run specs.'
|
6
|
+
task default: :spec
|
7
|
+
|
8
|
+
desc 'Run specs'
|
9
|
+
RSpec::Core::RakeTask.new do |t|
|
10
|
+
t.pattern = './spec/**/*_spec.rb'
|
11
|
+
end
|
12
|
+
|
13
|
+
desc 'Run Mutant'
|
14
|
+
task :mutant do
|
15
|
+
require 'mutant'
|
16
|
+
status = Mutant::CLI.run(['-I', 'lib', '-r', 'sapor', '--use', 'rspec', 'Sapor*'])
|
17
|
+
abort 'Mutant task is not successful' if status.nonzero?
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Technical Documentation
|
2
|
+
=======================
|
3
|
+
|
4
|
+
Class Diagram
|
5
|
+
-------------
|
6
|
+
|
7
|
+

|
8
|
+
|
9
|
+
Areas
|
10
|
+
-----
|
11
|
+
|
12
|
+
The following diagram shows the hierarchy of all areas.
|
13
|
+
|
14
|
+

|
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#
|
3
|
+
# Statistical Analysis of Polling Results (SAPoR)
|
4
|
+
# Copyright © 2014 Filip van Laenen <f.a.vanlaenen@ieee.org>
|
5
|
+
#
|
6
|
+
# This file is part of SAPoR.
|
7
|
+
#
|
8
|
+
# SAPoR is free software: you can redistribute it and/or modify it under the terms of the GNU General
|
9
|
+
# Public License as published by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# SAPoR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
|
13
|
+
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
14
|
+
# Public License for more details.
|
15
|
+
#
|
16
|
+
# You can find a copy of the GNU General Public License in /doc/gpl.txt
|
17
|
+
#
|
18
|
+
|
19
|
+
#
|
20
|
+
# Creates an installation package.
|
21
|
+
#
|
22
|
+
|
23
|
+
# Create an empty temporary directory
|
24
|
+
|
25
|
+
SCRIPTDIR="$( cd "$( dirname "$0" )" && pwd )"
|
26
|
+
VERSION="0.1b1"
|
27
|
+
TEMPDIR="sapor-${VERSION}"
|
28
|
+
|
29
|
+
if [ -d "$TEMPDIR" ]; then
|
30
|
+
rm -R "$TEMPDIR"
|
31
|
+
fi
|
32
|
+
|
33
|
+
mkdir "$TEMPDIR"
|
34
|
+
|
35
|
+
# Copy all resources to the temporary directory
|
36
|
+
|
37
|
+
BINDIR=${SCRIPTDIR}/../bin
|
38
|
+
cp ${BINDIR}/install.sh "$TEMPDIR"
|
39
|
+
cp ${BINDIR}/sapor.sh "$TEMPDIR"
|
40
|
+
cp ${BINDIR}/sapor.rb "$TEMPDIR"
|
41
|
+
|
42
|
+
# Creates the archive file
|
43
|
+
|
44
|
+
TARFILE="sapor-${VERSION}.tar.gz"
|
45
|
+
tar -pczf $TARFILE "$TEMPDIR"
|
46
|
+
|
47
|
+
# Remove the temporary directory
|
48
|
+
|
49
|
+
rm -R $TEMPDIR
|
data/bin/install.sh
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#
|
3
|
+
# Statistical Analysis of Polling Results (SAPoR)
|
4
|
+
# Copyright © 2014 Filip van Laenen <f.a.vanlaenen@ieee.org>
|
5
|
+
#
|
6
|
+
# This file is part of SAPoR.
|
7
|
+
#
|
8
|
+
# SAPoR is free software: you can redistribute it and/or modify it under the terms of the GNU General
|
9
|
+
# Public License as published by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# SAPoR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
|
13
|
+
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
14
|
+
# Public License for more details.
|
15
|
+
#
|
16
|
+
# You can find a copy of the GNU General Public License in /doc/gpl.txt
|
17
|
+
#
|
18
|
+
|
19
|
+
#
|
20
|
+
# Installs SAPoR into /opt, and creates a link from /usr/bin to the sapor script.
|
21
|
+
#
|
22
|
+
# Note: Requires root permissions to create the directory. Use sudo to execute this script.
|
23
|
+
#
|
24
|
+
|
25
|
+
SAPORDIR="/opt/sapor"
|
26
|
+
|
27
|
+
if [ -d "$SAPORDIR" ]; then
|
28
|
+
rm -R "$SAPORDIR"
|
29
|
+
fi
|
30
|
+
|
31
|
+
mkdir "$SAPORDIR"
|
32
|
+
|
33
|
+
cp sapor* "$SAPORDIR"
|
34
|
+
chmod a+x "$SAPORDIR/sapor.sh"
|
35
|
+
ln -f "$SAPORDIR/sapor.sh" /usr/bin/sapor
|
36
|
+
|
37
|
+
LOG4R=$(gem list log4r | awk '/log4r/ {print $1}')
|
38
|
+
if [ ${#LOG4R[@]} -eq "0" ]; then
|
39
|
+
gem install -r log4r
|
40
|
+
fi
|
41
|
+
|
42
|
+
SAPORGEM=$(gem list sapor | awk '/sapor/ {print $1}')
|
43
|
+
if [ ${#SAPORGEM[@]} -eq "0" ]; then
|
44
|
+
gem install -r sapor
|
45
|
+
fi
|
data/bin/sapor.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Statistical Analysis of Polling Results (SAPoR)
|
4
|
+
# Copyright (C) 2014 Filip van Laenen <f.a.vanlaenen@ieee.org>
|
5
|
+
#
|
6
|
+
# This file is part of SAPoR.
|
7
|
+
#
|
8
|
+
# SAPoR is free software: you can redistribute it and/or modify it under the
|
9
|
+
# terms of the GNU General Public License as published by the Free Software
|
10
|
+
# Foundation, either version 3 of the License, or (at your option) any later
|
11
|
+
# version.
|
12
|
+
#
|
13
|
+
# SAPoR is distributed in the hope that it will be useful, but WITHOUT ANY
|
14
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
15
|
+
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You can find a copy of the GNU General Public License in /doc/gpl.txt
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'sapor'
|
21
|
+
|
22
|
+
Sapor.analyze(ARGV[1])
|
data/bin/sapor.sh
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#
|
3
|
+
# Statistical Analysis of Polling Results (SAPoR)
|
4
|
+
# Copyright © 2014 Filip van Laenen <f.a.vanlaenen@ieee.org>
|
5
|
+
#
|
6
|
+
# This file is part of SAPoR.
|
7
|
+
#
|
8
|
+
# SAPoR is free software: you can redistribute it and/or modify it under the terms of the GNU General
|
9
|
+
# Public License as published by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# SAPoR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
|
13
|
+
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
14
|
+
# Public License for more details.
|
15
|
+
#
|
16
|
+
# You can find a copy of the GNU General Public License in /doc/gpl.txt
|
17
|
+
#
|
18
|
+
|
19
|
+
#
|
20
|
+
# Central point to run SAPoR. The script accepts the following arguments:
|
21
|
+
# - help: To display the help information.
|
22
|
+
# - version: To display the version information.
|
23
|
+
# - copyright: To display the copyright information.
|
24
|
+
# - warranty: To display the warranty information.
|
25
|
+
#
|
26
|
+
|
27
|
+
ACTION="$1"
|
28
|
+
|
29
|
+
export SAPORDIR="/opt/sapor"
|
30
|
+
export LOCALSAPORDIR="${HOME}/.sapor"
|
31
|
+
export RUBY="ruby"
|
32
|
+
|
33
|
+
VERSION="0.1b1"
|
34
|
+
COPYRIGHTYEAR="2014"
|
35
|
+
|
36
|
+
case "$ACTION" in
|
37
|
+
analyze)
|
38
|
+
if [ ! -d "$LOCALSAPORDIR" ]; then
|
39
|
+
mkdir "$LOCALSAPORDIR"
|
40
|
+
fi
|
41
|
+
if [ ! -d "${LOCALSAPORDIR}/cache" ]; then
|
42
|
+
mkdir "${LOCALSAPORDIR}/cache"
|
43
|
+
fi
|
44
|
+
$RUBY -I "${SAPORDIR}/lib" "${SAPORDIR}/sapor.rb" "${LOCALSAPORDIR}" $2
|
45
|
+
;;
|
46
|
+
help)
|
47
|
+
echo "Statistical Analysis of Polling Results (SAPoR) v${VERSION}"
|
48
|
+
echo "Copyright © ${COPYRIGHTYEAR} Filip van Laenen <f.a.vanlaenen@ieee.org>"
|
49
|
+
echo
|
50
|
+
echo "Usage:"
|
51
|
+
echo " sapor action [parameters]"
|
52
|
+
echo
|
53
|
+
echo "where actions and parameters include:"
|
54
|
+
echo " analyze <poll-file> run an analysis on the poll file"
|
55
|
+
echo " help show this message"
|
56
|
+
echo " version show the version information"
|
57
|
+
echo " copyright show the copyright information"
|
58
|
+
echo " warranty show the warranty information"
|
59
|
+
;;
|
60
|
+
version)
|
61
|
+
echo "Statistical Analysis of Polling Results (SAPoR) v${VERSION}"
|
62
|
+
echo "Copyright © ${COPYRIGHTYEAR} Filip van Laenen <f.a.vanlaenen@ieee.org>"
|
63
|
+
echo "This program comes with ABSOLUTELY NO WARRANTY; for details run 'wruf warranty'."
|
64
|
+
echo "This is free software, and you are welcome to redistribute it"
|
65
|
+
echo "under certain conditions; run 'wruf copyright' for details."
|
66
|
+
;;
|
67
|
+
copyright)
|
68
|
+
echo "Statistical Analysis of Polling Results (SAPoR) v${VERSION}"
|
69
|
+
echo "Copyright © ${COPYRIGHTYEAR} Filip van Laenen <f.a.vanlaenen@ieee.org>"
|
70
|
+
echo
|
71
|
+
echo "This program is free software: you can redistribute it and/or modify"
|
72
|
+
echo "it under the terms of the GNU General Public License as published by"
|
73
|
+
echo "the Free Software Foundation, either version 3 of the License, or"
|
74
|
+
echo "(at your option) any later version."
|
75
|
+
echo
|
76
|
+
echo "This program is distributed in the hope that it will be useful,"
|
77
|
+
echo "but WITHOUT ANY WARRANTY; without even the implied warranty of"
|
78
|
+
echo "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"
|
79
|
+
echo "GNU General Public License for more details."
|
80
|
+
echo
|
81
|
+
echo "You should have received a copy of the GNU General Public License"
|
82
|
+
echo "along with this program. If not, see <http://www.gnu.org/licenses/>."
|
83
|
+
;;
|
84
|
+
warranty)
|
85
|
+
echo "Statistical Analysis of Polling Results (SAPoR) v${VERSION}"
|
86
|
+
echo "Copyright © ${COPYRIGHTYEAR} Filip van Laenen <f.a.vanlaenen@ieee.org>"
|
87
|
+
echo
|
88
|
+
echo "There is no warranty for the program, to the extent permitted by applicable law."
|
89
|
+
echo "Except when otherwise stated in writing the copyright holders and/or other"
|
90
|
+
echo "parties provide the program “as is” without warranty of any kind, either"
|
91
|
+
echo "expressed or implied, including, but not limited to, the implied warranties of"
|
92
|
+
echo "merchantability and fitness for a particular purpose. The entire risk as to the"
|
93
|
+
echo "quality and performance of the program is with you. Should the program prove"
|
94
|
+
echo "defective, you assume the cost of all necessary servicing, repair or correction."
|
95
|
+
;;
|
96
|
+
*)
|
97
|
+
echo "Statistical Analysis of Polling Results (SAPoR) v${VERSION}"
|
98
|
+
echo "Copyright © ${COPYRIGHTYEAR} Filip van Laenen <f.a.vanlaenen@ieee.org>"
|
99
|
+
echo
|
100
|
+
echo "Usage: sapor {analyze|help|version|warranty|copyright}" >&2
|
101
|
+
echo "Type 'sapor help' to get more information."
|
102
|
+
exit 1
|
103
|
+
;;
|
104
|
+
esac
|
105
|
+
|
data/lib/sapor.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Statistical Analysis of Polling Results (SAPoR)
|
4
|
+
# Copyright (C) 2014 Filip van Laenen <f.a.vanlaenen@ieee.org>
|
5
|
+
#
|
6
|
+
# This file is part of SAPoR.
|
7
|
+
#
|
8
|
+
# SAPoR is free software: you can redistribute it and/or modify it under the
|
9
|
+
# terms of the GNU General Public License as published by the Free Software
|
10
|
+
# Foundation, either version 3 of the License, or (at your option) any later
|
11
|
+
# version.
|
12
|
+
#
|
13
|
+
# SAPoR is distributed in the hope that it will be useful, but WITHOUT ANY
|
14
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
15
|
+
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You can find a copy of the GNU General Public License in /doc/gpl.txt
|
18
|
+
#
|
19
|
+
|
20
|
+
# Library namespace
|
21
|
+
module Sapor
|
22
|
+
def self.analyze(filename)
|
23
|
+
Poll.from_file(filename).analyze
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'sapor/number_formatter'
|
28
|
+
require 'sapor/dichotomies'
|
29
|
+
require 'sapor/combinations_distribution'
|
30
|
+
require 'sapor/dichotomy'
|
31
|
+
require 'sapor/log4r_logger'
|
32
|
+
require 'sapor/log_facade'
|
33
|
+
require 'sapor/pseudorandom_multirange_enumerator'
|
34
|
+
require 'sapor/binomials_cache'
|
35
|
+
require 'sapor/polychotomy'
|
36
|
+
require 'sapor/first_past_the_post'
|
37
|
+
require 'sapor/proportional'
|
38
|
+
require 'sapor/leveled_proportional'
|
39
|
+
require 'sapor/regional_data/area'
|
40
|
+
require 'sapor/regional_data/catalonia'
|
41
|
+
require 'sapor/regional_data/norway'
|
42
|
+
require 'sapor/regional_data/united_kingdom'
|
43
|
+
require 'sapor/regional_data/utopia'
|
44
|
+
require 'sapor/poll'
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Statistical Analysis of Polling Results (SAPoR)
|
4
|
+
# Copyright (C) 2014 Filip van Laenen <f.a.vanlaenen@ieee.org>
|
5
|
+
#
|
6
|
+
# This file is part of SAPoR.
|
7
|
+
#
|
8
|
+
# SAPoR is free software: you can redistribute it and/or modify it under the
|
9
|
+
# terms of the GNU General Public License as published by the Free Software
|
10
|
+
# Foundation, either version 3 of the License, or (at your option) any later
|
11
|
+
# version.
|
12
|
+
#
|
13
|
+
# SAPoR is distributed in the hope that it will be useful, but WITHOUT ANY
|
14
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
15
|
+
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You can find a copy of the GNU General Public License in /doc/gpl.txt
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'large_binomials'
|
21
|
+
|
22
|
+
module Sapor
|
23
|
+
#
|
24
|
+
# Caches binomials.
|
25
|
+
#
|
26
|
+
class BinomialsCache
|
27
|
+
include Singleton
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@cache = {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.binomial(k, n)
|
34
|
+
instance.get_binomial(k, n)
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_binomial(k, n)
|
38
|
+
@cache[n] = {} unless @cache.key?(n)
|
39
|
+
unless @cache[n].key?(k)
|
40
|
+
@cache[n][k] = k.large_float_binomial_by_product_of_divisions(n)
|
41
|
+
end
|
42
|
+
@cache[n][k]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Statistical Analysis of Polling Results (SAPoR)
|
4
|
+
# Copyright (C) 2014 Filip van Laenen <f.a.vanlaenen@ieee.org>
|
5
|
+
#
|
6
|
+
# This file is part of SAPoR.
|
7
|
+
#
|
8
|
+
# SAPoR is free software: you can redistribute it and/or modify it under the
|
9
|
+
# terms of the GNU General Public License as published by the Free Software
|
10
|
+
# Foundation, either version 3 of the License, or (at your option) any later
|
11
|
+
# version.
|
12
|
+
#
|
13
|
+
# SAPoR is distributed in the hope that it will be useful, but WITHOUT ANY
|
14
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
15
|
+
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You can find a copy of the GNU General Public License in /doc/gpl.txt
|
18
|
+
#
|
19
|
+
|
20
|
+
module Sapor
|
21
|
+
#
|
22
|
+
# Represents the distribution of combinations over a set of values. This is
|
23
|
+
# basically a simple hash with some helper methods for the Sapor domain.
|
24
|
+
#
|
25
|
+
class CombinationsDistribution
|
26
|
+
def initialize
|
27
|
+
@distribution = {}
|
28
|
+
end
|
29
|
+
|
30
|
+
def []=(value, combinations)
|
31
|
+
@distribution[value] = combinations
|
32
|
+
end
|
33
|
+
|
34
|
+
def [](value)
|
35
|
+
@distribution[value]
|
36
|
+
end
|
37
|
+
|
38
|
+
def +(other)
|
39
|
+
sum = CombinationsDistribution.new
|
40
|
+
@distribution.keys.each { |key| sum[key] = self[key] }
|
41
|
+
other.values.each do |key|
|
42
|
+
if self[key].nil?
|
43
|
+
sum[key] = other[key]
|
44
|
+
else
|
45
|
+
sum[key] += other[key]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
sum
|
49
|
+
end
|
50
|
+
|
51
|
+
def empty?
|
52
|
+
@distribution.empty?
|
53
|
+
end
|
54
|
+
|
55
|
+
def value_threshold_probability(threshold_value)
|
56
|
+
total = @distribution.values.inject(:+)
|
57
|
+
distribution_over_threshold = @distribution.select do |k, _|
|
58
|
+
k >= threshold_value
|
59
|
+
end
|
60
|
+
if distribution_over_threshold.empty?
|
61
|
+
0
|
62
|
+
else
|
63
|
+
over_threshold = distribution_over_threshold.values.inject(:+)
|
64
|
+
probability = over_threshold / total
|
65
|
+
probability.mantissa * (10**probability.exponent)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def threshold_probability(threshold, population_size)
|
70
|
+
value_threshold_probability(population_size * threshold)
|
71
|
+
end
|
72
|
+
|
73
|
+
def size
|
74
|
+
@distribution.size
|
75
|
+
end
|
76
|
+
|
77
|
+
def values
|
78
|
+
@distribution.keys
|
79
|
+
end
|
80
|
+
|
81
|
+
def most_probable_value
|
82
|
+
@distribution.max { |a, b| a.last <=> b.last }[0]
|
83
|
+
end
|
84
|
+
|
85
|
+
# Given all fractions rounded to one decimal, returns the one that has the
|
86
|
+
# highest probability.
|
87
|
+
def most_probable_rounded_fraction(population_size)
|
88
|
+
rf_combinations = \
|
89
|
+
calculate_rounded_fractions_combinations(population_size)
|
90
|
+
max_probability = rf_combinations.values.max
|
91
|
+
opt_rfs = rf_combinations.reject { |_, v| v < max_probability }.keys
|
92
|
+
opt_rfs.sort[opt_rfs.size / 2]
|
93
|
+
end
|
94
|
+
|
95
|
+
def confidence_interval(level, population_size = nil)
|
96
|
+
combinations_sum = @distribution.values.inject(:+)
|
97
|
+
one_side_level = (1 - level) / 2
|
98
|
+
one_side_threshold = combinations_sum * one_side_level
|
99
|
+
bottom = find_confidence_interval_bottom(one_side_threshold,
|
100
|
+
population_size)
|
101
|
+
top = find_confidence_interval_top(one_side_threshold, population_size)
|
102
|
+
[bottom, top]
|
103
|
+
end
|
104
|
+
|
105
|
+
def confidence_interval_values(level)
|
106
|
+
interval = confidence_interval(level)
|
107
|
+
@distribution.keys.reject do |value|
|
108
|
+
value < interval.first || value > interval.last
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def confidence_interval_index(sorted_combinations, one_side_threshold)
|
115
|
+
i = 0
|
116
|
+
sum_to_i = sorted_combinations[i][1]
|
117
|
+
while sum_to_i < one_side_threshold
|
118
|
+
i += 1
|
119
|
+
sum_to_i += sorted_combinations[i][1]
|
120
|
+
end
|
121
|
+
i
|
122
|
+
end
|
123
|
+
|
124
|
+
def find_confidence_interval_bottom(one_side_threshold, population_size)
|
125
|
+
sorted_combinations = @distribution.sort
|
126
|
+
i = confidence_interval_index(sorted_combinations, one_side_threshold)
|
127
|
+
if i == 0
|
128
|
+
population_size.nil? ? sorted_combinations[0][0] : 0
|
129
|
+
else
|
130
|
+
(sorted_combinations[i - 1][0] + sorted_combinations[i][0] + 1) / 2
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def find_confidence_interval_top(one_side_threshold, population_size)
|
135
|
+
sorted_combinations = @distribution.sort.reverse
|
136
|
+
i = confidence_interval_index(sorted_combinations, one_side_threshold)
|
137
|
+
if i == 0
|
138
|
+
population_size.nil? ? sorted_combinations[0][0] : population_size
|
139
|
+
else
|
140
|
+
(sorted_combinations[i - 1][0] + sorted_combinations[i][0]) / 2
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def calculate_rounded_fractions_combinations(population_size)
|
145
|
+
sorted_combinations = @distribution.sort
|
146
|
+
combinations_by_interval = []
|
147
|
+
sorted_combinations.each_with_index do |c, ix|
|
148
|
+
if ix == 0
|
149
|
+
bottom = 0
|
150
|
+
else
|
151
|
+
bottom = combinations_by_interval[ix - 1].first.last + 1
|
152
|
+
end
|
153
|
+
if ix == sorted_combinations.size - 1
|
154
|
+
top = population_size
|
155
|
+
else
|
156
|
+
top = (sorted_combinations[ix].first + sorted_combinations[ix + 1].first) / 2
|
157
|
+
end
|
158
|
+
combinations_by_interval << [[bottom, top], c.last]
|
159
|
+
end
|
160
|
+
result = Hash.new(0.to_lf)
|
161
|
+
combinations_by_interval.each do |i_c|
|
162
|
+
bottom = (i_c.first.first.to_f / population_size).round(3)
|
163
|
+
top = (i_c.first.last.to_f / population_size).round(3)
|
164
|
+
ix = bottom
|
165
|
+
loop do
|
166
|
+
interval_bottom = ((ix - 0.0005) * population_size).ceil
|
167
|
+
interval_top = ((ix + 0.0005) * population_size).ceil - 1
|
168
|
+
if (((interval_bottom + interval_top) / 2).to_f / population_size).round(3) == ix
|
169
|
+
low = [i_c.first.first, interval_bottom].max
|
170
|
+
high = [i_c.first.last, interval_top].min
|
171
|
+
result[ix] += i_c.last * (high + 1 - low)
|
172
|
+
end
|
173
|
+
break if ix == top
|
174
|
+
ix = (ix + 0.001).round(3)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
result
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|