File Coverage

blib/lib/Lingua/PT/Actants.pm
Criterion Covered Total %
statement 128 167 76.6
branch 34 44 77.2
condition 5 6 83.3
subroutine 16 20 80.0
pod 6 8 75.0
total 189 245 77.1


line stmt bran cond sub pod time code
1             package Lingua::PT::Actants;
2             # ABSTRACT: compute verb actants for Portuguese
3             $Lingua::PT::Actants::VERSION = '0.03';
4 3     3   39106 use strict;
  3         3  
  3         72  
5 3     3   9 use warnings;
  3         3  
  3         4242  
6              
7             sub new {
8 5     5 1 158432 my ($class, %args) = @_;
9 5         5 my $data;
10              
11             # input is in conll* format
12 5 100       15 if (exists($args{conll})) {
13 4         11 $data = _conll2data($args{conll});
14             }
15              
16 5         12 my $self = bless({ data=>$data }, $class);
17 5         12 return $self;
18             }
19              
20             sub _conll2data {
21 4     4   6 my ($input) = @_;
22              
23 4         6 my @data;
24 4         40 foreach my $line (split /\n/, $input) {
25 26 50       63 next if $line =~ m/^\s*$/;
26              
27 26         154 my @l = split /\s+/, $line;
28 26         120 push @data, {
29             id=>$l[0], form=>$l[1], pos=>$l[3], dep=>$l[6], rule=>$l[7]
30             };
31             }
32              
33 4         13 return [@data];
34             }
35              
36             sub text {
37 0     0 1 0 my ($self) = @_;
38              
39 0         0 return join(' ', map {$_->{form}} @{$self->{data}});
  0         0  
  0         0  
40             }
41              
42             sub actants {
43 3     3 1 2159 my ($self, %args) = @_;
44              
45 3         6 my ($cores, $ranks) = $self->acts_cores($self->{data});
46 3         4 $self->{cores} = $cores;
47 3         3 $self->{ranks} = $ranks;
48              
49 3         8 my @acts = $self->acts_syntagmas($cores, $self->{data});
50 3         4 $self->{acts} = [@acts];
51              
52 3         6 return $self->{acts};
53             }
54              
55             # compute actant cores
56             sub acts_cores {
57 7     7 1 27 my ($self) = @_;
58 7         13 my $data = $self->{data};
59              
60             # compute main verbs in the sentence
61 7         12 my @verbs = _main_verbs($data);
62              
63 7         5 my @ranks;
64 7         10 foreach my $v (@verbs) {
65 7         12 my @result;
66 7         9 foreach (@$data) {
67 47         51 my $score = _score($_, $v);
68 47 50       98 if ($score >= 0) {
69 47         114 push @result, { token => $_, score => $score };
70             }
71             }
72              
73             # normalize results
74 7         7 my $total = 0;
75 7         30 $total += $_->{score} foreach @result;
76 7         33 $_->{score} = $_->{score}/$total foreach @result;
77              
78             # sort results by score
79 7         17 @result = sort {$b->{score} <=> $a->{score}} @result;
  81         94  
80              
81 7         29 push @ranks, { verb => $v, rank => [@result] };
82             }
83              
84 7         6 my @cores;
85 7         10 foreach (@ranks) {
86 7         7 my ($verb, @rank) = ( $_->{verb}, @{ $_->{rank} } );
  7         17  
87              
88             # trim cores in rank
89 7         5 my @final;
90 7         5 my $ac = 0;
91 7         9 foreach (@rank) {
92 47 100 66     126 next if ($ac > 0.9 or $_->{score} < 0.1);
93              
94 14         12 push @final, $_;
95 14         18 $ac += $_->{score};
96             }
97              
98             # sort cores by token position in sentence
99 7         12 @final = sort { $a->{token}->{id} <=> $b->{token}->{id} } @final;
  7         14  
100              
101             # set to simple list of tokens
102 7         13 @final = map {$_->{token}} @final;
  14         20  
103              
104 7         32 push @cores, { verb => $verb, cores => [@final] };
105             }
106              
107 7         16 return ([@cores], [@ranks]);
108             }
109              
110             sub _main_verbs {
111 7     7   7 my ($data) = @_;
112 7         7 my @verbs;
113              
114             my $i;
115 0         0 my @tmp;
116 7         21 for ($i = 0; $i < @$data-1; $i++) {
117 40         54 my ($a, $b) = ($data->[$i], $data->[$i+1]);
118              
119 40 100 100     112 unless ($a->{pos} eq 'VERB' and $b->{pos} eq 'VERB') {
120 36         38 push @tmp, $a;
121             }
122 40 50       94 push @tmp, $b if ($i >= @$data);
123             }
124              
125 7         11 foreach (@tmp) {
126 36 100       81 push @verbs, $_ if (lc($_->{pos}) eq 'verb');
127             }
128              
129 7         15 return @verbs;
130             }
131              
132             sub acts_syntagmas {
133 3     3 0 2 my ($self, $cores, $data) = @_;
134              
135 3         4 my @acts;
136 3         16 foreach (@$cores) {
137 3         5 my ($verb, @tokens) = ($_->{verb}, @{ $_->{cores} });
  3         5  
138              
139 3         2 my @list;
140 3         5 foreach my $t (@tokens) {
141 6         9 my @child = $self->_child($t);
142              
143             # remove tokens that are cores from child list
144 6         2 my @tmp;
145 6         8 foreach (@child) {
146 12 100       14 push @tmp, $_ unless $self->_is_core($_);
147 12 100       29 push @tmp, $_ if $_->{id} == $t->{id};
148             }
149 6         6 @child = @tmp;
150              
151 6 50       9 next unless @child;
152 6         19 push @list, { tokens=>[@child] };
153             }
154 3         9 push @acts, { verb=>$verb, acts=>[@list] };
155             }
156              
157 3         4 return @acts;
158             }
159              
160             sub _score {
161 47     47   35 my ($token, $verb) = @_;
162 47         30 my $score = 0;
163              
164 47         45 $score = _score_token($token, $verb);
165 47         47 my $dist = _dist($token, $verb);
166              
167 47 100       117 return ($dist ? $score/sqrt($dist) : 0);
168             }
169              
170             sub _score_token {
171 47     47   34 my ($token) = @_;
172              
173 47         49 my $score = (_score_pos($token->{pos}) + _score_rule($token->{rule})) / 2;
174              
175 47         47 return $score;
176             }
177              
178             sub _score_pos {
179 47     47   50 my ($pos) = @_;
180              
181 47 100       123 return 0.8 if ($pos =~ m/^(noun|propn|prop)$/i);
182 33 100       55 return 0 if ($pos =~ m/^(punct)$/i);
183              
184 26         48 return 0;
185             }
186              
187             sub _score_rule {
188 47     47   47 my ($rule) = @_;
189              
190 47 100       102 return 0.8 if ($rule =~ m/^(nsubj|nsubjpass)$/i);
191 40 100       94 return 0.6 if ($rule =~ m/^(dobj)$/i);
192              
193 33         44 return 0;
194             }
195              
196             sub _dist {
197 47     47   35 my ($token, $verb) = @_;
198              
199 47         31 my $dist = 0;
200 47         71 $dist = $token->{id} - $verb->{id};
201 47 100       80 $dist *= -1 if $dist < 0;
202              
203 47         80 return $dist;
204             }
205              
206             sub _child {
207 6     6   4 my ($self, $node) = @_;
208 6         6 my @child = ();
209 6         6 my $data = $self->{data};
210              
211 6         7 my $id_tree = {};
212 6         5 $id_tree = _id_tree($id_tree, $node, $data);
213              
214 6         16 foreach my $id (sort keys %$id_tree) {
215 12         12 foreach (@$data) {
216 84 100       123 push @child, $_ if $_->{id} == $id;
217             }
218             }
219              
220 6         15 return @child;
221             }
222              
223             sub _id_tree {
224 12     12   7 my ($id_tree, $node, $data) = @_;
225              
226 12         23 $id_tree->{$node->{id}}++;
227 12         13 foreach (@$data) {
228 84 100       142 if ($node->{id} == $_->{dep}) {
229 6         9 $id_tree = _id_tree($id_tree, $_, $data)
230             }
231             }
232              
233 12         17 return $id_tree;
234             }
235              
236             sub _is_core {
237 12     12   11 my ($self, $token) = @_;
238              
239 12         6 foreach my $i (@{$self->{cores}}) {
  12         13  
240 12         8 foreach (@{$i->{cores}}) {
  12         13  
241 21 100       46 return 1 if ($token->{id} == $_->{id});
242             }
243             }
244              
245 6         13 return 0;
246             }
247              
248             sub pp_acts_cores {
249 0     0 1   my ($self, $cores) = @_;
250 0 0         $cores = $self->{cores} unless $cores;
251              
252 0           my $r = "# Actants syntagma cores\n";
253 0           foreach (@$cores) {
254 0           my ($verb, @tokens) = ($_->{verb}, @{$_->{cores}} );
  0            
255              
256 0           $r .= " Verb: $verb->{form}\n";
257 0           foreach (@tokens) {
258             #$r .= sprintf " %.6f | %s\n", $_->{score}, $_->{form};
259 0           $r .= sprintf " + %s\n", $_->{form};
260             }
261             }
262              
263 0           return $r;
264             }
265              
266             sub pp_acts_syntagmas {
267 0     0 1   my ($self, $acts) = @_;
268 0 0         $acts = $self->{acts} unless $acts;
269              
270 0           my $r = "# Actants syntagmas\n";
271 0           foreach (@$acts) {
272 0           my ($verb, @list) = ($_->{verb}, @{ $_->{acts} });
  0            
273              
274 0           $r .= " Verb: $verb->{form}\n";
275 0           my $i = 1;
276 0           foreach (@list) {
277             $r .= sprintf " %s: %s\n",
278             "A$i",
279 0           join(' ', map {$_->{form}} @{ $_->{tokens}});
  0            
  0            
280 0           $i++;
281             }
282             }
283              
284 0           return $r;
285             }
286              
287             sub pp_acts_ranks {
288 0     0 0   my ($self, $ranks) = @_;
289 0 0         $ranks = $self->{ranks} unless $ranks;
290              
291 0           my $r = "# Actants cores ranks\n";
292 0           foreach (@$ranks) {
293 0           my ($verb, @rank) = ($_->{verb}, @{$_->{rank}} );
  0            
294              
295 0           $r .= " Verb: $verb->{form}\n";
296 0           foreach (@rank) {
297 0           $r .= sprintf " %.6f | %s\n", $_->{score}, $_->{token}->{form};
298             }
299             }
300              
301 0           return $r;
302             }
303              
304              
305             1;
306              
307             __END__