sapor 0.1b1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![Class diagram](Class%20Diagram.png "Class diagram")
|
8
|
+
|
9
|
+
Areas
|
10
|
+
-----
|
11
|
+
|
12
|
+
The following diagram shows the hierarchy of all areas.
|
13
|
+
|
14
|
+
![Area Class diagram](Area%20Class%20Diagram.png "Area Class diagram")
|
@@ -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
|