File Coverage

blib/lib/Algorithm/Bayesian.pm
Criterion Covered Total %
statement 70 70 100.0
branch 30 44 68.1
condition 7 7 100.0
subroutine 13 13 100.0
pod 7 7 100.0
total 127 141 90.0


line stmt bran cond sub pod time code
1             package Algorithm::Bayesian;
2              
3 3     3   1577 use Carp;
  3         5  
  3         196  
4 3     3   5818 use Math::BigFloat;
  3         137046  
  3         23  
5 3     3   186424 use strict;
  3         15  
  3         102  
6 3     3   18 use warnings;
  3         7  
  3         112  
7              
8 3     3   155 use constant HAMSTR => '*ham';
  3         6  
  3         196  
9 3     3   15 use constant SPAMSTR => '*spam';
  3         6  
  3         2494  
10              
11             our $VERSION = '0.5';
12              
13             =head1 NAME
14              
15             Algorithm::Bayesian - Bayesian Spam Filtering Algorithm
16              
17             =head1 SYNOPSIS
18              
19             use Algorithm::Bayesian;
20             use Tie::Foo;
21              
22             my %storage;
23             tie %storage, 'Tie:Foo', ...;
24             my $b = Algorithm::Bayesian->new(\%storage);
25              
26             $b->spam('spamword1', 'spamword2', ...);
27             $b->ham('hamword1', 'hamword2', ...);
28              
29             my $pr = $b->test('word1', 'word2', ...);
30              
31             =head1 DESCRIPTION
32              
33             Algorithm::Bayesian provide an easy way to handle Bayesian spam filtering algorithm.
34              
35             =head1 SUBROUTINES/METHODS
36              
37             =head2 new
38              
39             my $b = Algorithm::Bayesian->new(\%hash);
40              
41             Constructor. Simple hash would be fine. You can use L to store data to RDBM, or other key-value storage.
42              
43             =cut
44              
45             sub new {
46 3 50   3 1 55 my $self = shift or croak;
47              
48 3         6 my $s = shift;
49 3 50       21 $s->{HAMSTR} = 0 if !defined $s->{HAMSTR};
50 3 50       21 $s->{SPAMSTR} = 0 if !defined $s->{SPAMSTR};
51              
52 3         21 bless {storage => $s}, $self;
53             }
54              
55             =head2 getHam
56              
57             my $num = $b->getHam($word);
58              
59             Get C<$word> count in Ham.
60              
61             =cut
62              
63             sub getHam {
64 64 50   64 1 171 my $self = shift or croak;
65 64 50       157 my $s = $self->{storage} or croak;
66              
67 64         80 my $w = shift;
68              
69 64 100       191 return $s->{HAMSTR} if !defined $w;
70 30   100     138 return $s->{"h$w"} || 0;
71             }
72              
73             =head2 getSpam
74              
75             my $num = $b->getSpam($word);
76              
77             Get C<$word> count in Spam.
78              
79             =cut
80              
81             sub getSpam {
82 64 50   64 1 186 my $self = shift or croak;
83 64 50       160 my $s = $self->{storage} or croak;
84              
85 64         78 my $w = shift;
86              
87 64 100       367 return $s->{SPAMSTR} if !defined $w;
88 30   100     144 return $s->{"s$w"} || 0;
89             }
90              
91             =head2 ham
92              
93             $b->ham(@words);
94              
95             Train C<@words> as Ham.
96              
97             =cut
98              
99             sub ham {
100 2 50   2 1 1537 my $self = shift or croak;
101 2 50       12 my $s = $self->{storage} or croak;
102              
103 2         8 foreach my $w (@_) {
104 12         38 $s->{"h$w"}++;
105             }
106              
107 2         35 $s->{HAMSTR}++;
108             }
109              
110             =head2 spam
111              
112             $b->spam(@words);
113              
114             Train C<@words> as Spam.
115              
116             =cut
117              
118             sub spam {
119 2 50   2 1 2020 my $self = shift or croak;
120 2 50       12 my $s = $self->{storage} or croak;
121              
122 2         6 foreach my $w (@_) {
123 11         34 $s->{"s$w"}++;
124             }
125              
126 2         9 $s->{SPAMSTR}++;
127             }
128              
129             =head2 test
130              
131             my $pr = $b->test(@words);
132              
133             Calculate the spam probability of C<@words>. The range of C<$pr> will be in 0 to 1.
134              
135             =cut
136              
137             sub test {
138 5 50   5 1 2428 my $self = shift or croak;
139              
140 5         12 my $prec = 2 * scalar @_;
141 5         42 my $a1 = Math::BigFloat->new('1', $prec);
142 5         2379 my $a2 = $a1->copy;
143              
144 5         135 foreach my $w (@_) {
145 23         13300 my $pr = $self->testWord($w);
146              
147             # Avoid 0/1
148 23 100       65 $pr = 0.99 if $pr > 0.99;
149 23 100       63 $pr = 0.01 if $pr < 0.01;
150              
151 23         90 $a1 *= 2 * $pr;
152 23         9963 $a2 *= 2 * (1 - $pr);
153             }
154              
155 5         1705 return ($a1 / ($a1 + $a2))->bstr;
156             }
157              
158             =head2 testWord
159              
160             my $pr = $b->testWord($word);
161              
162             Calculate the spam probability of C<$word>.
163              
164             The range of C<$pr> will be in 0 to 1. For non-existence word, it will be 0.5.
165              
166             =cut
167              
168             sub testWord {
169 31 50   31 1 103 my $self = shift or croak;
170 31 50       80 my $w = shift or croak;
171              
172 31         74 my $hamNum = $self->getHam;
173 31         70 my $spamNum = $self->getSpam;
174 31         52 my $totalNum = $hamNum + $spamNum;
175              
176 31 100       85 return 0.5 if 0 == $totalNum;
177              
178 30         62 my $wSpam = $self->getSpam($w);
179 30         69 my $wHam = $self->getHam($w);
180              
181 30 100 100     115 return 0.5 if 0 == $wSpam and 0 == $wHam;
182 28 100       87 return 0 if 0 == $wSpam;
183 15 100       60 return 1 if 0 == $wHam;
184              
185 2         4 my $hamPr = $hamNum / $totalNum;
186 2         5 my $spamPr = $spamNum / $totalNum;
187              
188 2         4 my $a1 = $wSpam * $spamPr / $spamNum;
189 2         4 my $a2 = $wHam * $hamPr / $hamNum;
190              
191 2         9 return $a1 / ($a1 + $a2);
192             }
193              
194             =head1 AUTHOR
195              
196             Gea-Suan Lin, C<< >>
197              
198             =head1 LICENSE AND COPYRIGHT
199              
200             Copyright 2010 Gea-Suan Lin.
201              
202             This program is free software; you can redistribute it and/or modify it
203             under the terms of either: the GNU General Public License as published
204             by the Free Software Foundation; or the Artistic License.
205              
206             See http://dev.perl.org/licenses/ for more information.
207              
208              
209             =cut
210              
211             1; # End of Algorithm::Bayesian