Completed
Pull Request — master (#141)
by Chris
11:04
created

SnowballDutch._undouble()   A

Complexity

Conditions 4

Size

Total Lines 17
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 7
nop 2
dl 0
loc 17
ccs 4
cts 4
cp 1
crap 4
rs 10
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
3
# Copyright 2014-2018 by Christopher C. Little.
4
# This file is part of Abydos.
5
#
6
# Abydos is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation, either version 3 of the License, or
9
# (at your option) any later version.
10
#
11
# Abydos is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with Abydos. If not, see <http://www.gnu.org/licenses/>.
18
19 1
"""abydos.stemmer._snowball_dutch.
20
21
Snowball Dutch stemmer
22
"""
23
24 1
from __future__ import (
25
    absolute_import,
26
    division,
27
    print_function,
28
    unicode_literals,
29
)
30
31 1
from unicodedata import normalize
32
33 1
from six import text_type
34 1
from six.moves import range
35
36 1
from ._snowball import _Snowball
37
38 1
__all__ = ['SnowballDutch', 'sb_dutch']
39
40
41 1
class SnowballDutch(_Snowball):
0 ignored issues
show
Unused Code introduced by
The variable __class__ seems to be unused.
Loading history...
42
    """Snowball Dutch stemmer.
43
44
    The Snowball Dutch stemmer is defined at:
45
    http://snowball.tartarus.org/algorithms/dutch/stemmer.html
46
    """
47
48 1
    _vowels = {'a', 'e', 'i', 'o', 'u', 'y', 'è'}
49 1
    _not_s_endings = {'a', 'e', 'i', 'j', 'o', 'u', 'y', 'è'}
50 1
    _accented = dict(zip((ord(_) for _ in 'äëïöüáéíóú'), 'aeiouaeiou'))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable _ does not seem to be defined.
Loading history...
51
52 1
    def _undouble(self, word):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
53
        """Undouble endings -kk, -dd, and -tt.
54
55
        Args:
56
            word (str): The word to stem
57
58
        Returns:
59
            str: The word with doubled endings undoubled
60
61
        """
62 1
        if (
63
            len(word) > 1
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
64
            and word[-1] == word[-2]
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
65
            and word[-1] in {'d', 'k', 't'}
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
66
        ):
67 1
            return word[:-1]
68 1
        return word
69
70 1
    def stem(self, word):
0 ignored issues
show
Bug introduced by
Parameters differ from overridden 'stem' method
Loading history...
71
        """Return Snowball Dutch stem.
72
73
        Args:
74
            word (str): The word to stem
75
76
        Returns:
77
            str: Word stem
78
79
        Examples:
80
            >>> stmr = SnowballDutch()
81
            >>> stmr.stem('lezen')
82
            'lez'
83
            >>> stmr.stem('opschorting')
84
            'opschort'
85
            >>> stmr.stem('ongrijpbaarheid')
86
            'ongrijp'
87
88
        """
89
        # lowercase, normalize, decompose, filter umlauts & acutes out, and
90
        # compose
91 1
        word = normalize('NFC', text_type(word.lower()))
92 1
        word = word.translate(self._accented)
93
94 1
        for i in range(len(word)):
0 ignored issues
show
unused-code introduced by
Consider using enumerate instead of iterating with range and len
Loading history...
95 1
            if i == 0 and word[0] == 'y':
96 1
                word = 'Y' + word[1:]
97 1
            elif word[i] == 'y' and word[i - 1] in self._vowels:
98 1
                word = word[:i] + 'Y' + word[i + 1 :]
99 1
            elif (
100
                word[i] == 'i'
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
101
                and word[i - 1] in self._vowels
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
102
                and i + 1 < len(word)
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
103
                and word[i + 1] in self._vowels
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
104
            ):
105 1
                word = word[:i] + 'I' + word[i + 1 :]
106
107 1
        r1_start = max(3, self._sb_r1(word))
108 1
        r2_start = self._sb_r2(word)
109
110
        # Step 1
111 1
        if word[-5:] == 'heden':
112 1
            if len(word[r1_start:]) >= 5:
113 1
                word = word[:-3] + 'id'
114 1
        elif word[-3:] == 'ene':
115 1
            if len(word[r1_start:]) >= 3 and (
116
                word[-4] not in self._vowels and word[-6:-3] != 'gem'
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
117
            ):
118 1
                word = self._undouble(word[:-3])
119 1
        elif word[-2:] == 'en':
120 1
            if len(word[r1_start:]) >= 2 and (
121
                word[-3] not in self._vowels and word[-5:-2] != 'gem'
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
122
            ):
123 1
                word = self._undouble(word[:-2])
124 1
        elif word[-2:] == 'se':
125 1
            if (
126
                len(word[r1_start:]) >= 2
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
127
                and word[-3] not in self._not_s_endings
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
128
            ):
129 1
                word = word[:-2]
130 1
        elif word[-1:] == 's':
131 1
            if (
132
                len(word[r1_start:]) >= 1
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
133
                and word[-2] not in self._not_s_endings
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
134
            ):
135 1
                word = word[:-1]
136
137
        # Step 2
138 1
        e_removed = False
139 1
        if word[-1:] == 'e':
140 1
            if len(word[r1_start:]) >= 1 and word[-2] not in self._vowels:
141 1
                word = self._undouble(word[:-1])
142 1
                e_removed = True
143
144
        # Step 3a
145 1
        if word[-4:] == 'heid':
146 1
            if len(word[r2_start:]) >= 4 and word[-5] != 'c':
147 1
                word = word[:-4]
148 1
                if word[-2:] == 'en':
149 1
                    if len(word[r1_start:]) >= 2 and (
150
                        word[-3] not in self._vowels and word[-5:-2] != 'gem'
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
151
                    ):
152 1
                        word = self._undouble(word[:-2])
153
154
        # Step 3b
155 1
        if word[-4:] == 'lijk':
156 1
            if len(word[r2_start:]) >= 4:
157 1
                word = word[:-4]
158
                # Repeat step 2
159 1
                if word[-1:] == 'e':
160 1
                    if (
161
                        len(word[r1_start:]) >= 1
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
162
                        and word[-2] not in self._vowels
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
163
                    ):
164 1
                        word = self._undouble(word[:-1])
165 1
        elif word[-4:] == 'baar':
166 1
            if len(word[r2_start:]) >= 4:
167 1
                word = word[:-4]
168 1
        elif word[-3:] in ('end', 'ing'):
169 1
            if len(word[r2_start:]) >= 3:
170 1
                word = word[:-3]
171 1
                if (
172
                    word[-2:] == 'ig'
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
173
                    and len(word[r2_start:]) >= 2
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
174
                    and word[-3] != 'e'
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
175
                ):
176 1
                    word = word[:-2]
177
                else:
178 1
                    word = self._undouble(word)
179 1
        elif word[-3:] == 'bar':
180 1
            if len(word[r2_start:]) >= 3 and e_removed:
181 1
                word = word[:-3]
182 1
        elif word[-2:] == 'ig':
183 1
            if len(word[r2_start:]) >= 2 and word[-3] != 'e':
184 1
                word = word[:-2]
185
186
        # Step 4
187 1
        if (
188
            len(word) >= 4
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
best-practice introduced by
Too many boolean expressions in if statement (6/5)
Loading history...
189
            and word[-3] == word[-2]
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
190
            and word[-2] in {'a', 'e', 'o', 'u'}
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
191
            and word[-4] not in self._vowels
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
192
            and word[-1] not in self._vowels
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
193
            and word[-1] != 'I'
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
194
        ):
195 1
            word = word[:-2] + word[-1]
196
197
        # Change 'Y' and 'U' back to lowercase if survived stemming
198 1
        for i in range(0, len(word)):
199 1
            if word[i] == 'Y':
200 1
                word = word[:i] + 'y' + word[i + 1 :]
201 1
            elif word[i] == 'I':
202 1
                word = word[:i] + 'i' + word[i + 1 :]
203
204 1
        return word
205
206
207 1
def sb_dutch(word):
208
    """Return Snowball Dutch stem.
209
210
    This is a wrapper for :py:meth:`SnowballDutch.stem`.
211
212
    Args:
213
        word (str): The word to stem
214
215
    Returns:
216
        str: Word stem
217
218
    Examples:
219
        >>> sb_dutch('lezen')
220
        'lez'
221
        >>> sb_dutch('opschorting')
222
        'opschort'
223
        >>> sb_dutch('ongrijpbaarheid')
224
        'ongrijp'
225
226
    """
227 1
    return SnowballDutch().stem(word)
228
229
230
if __name__ == '__main__':
231
    import doctest
232
233
    doctest.testmod()
234