mawo-razdel 1.0.1__py3-none-any.whl → 1.0.3__py3-none-any.whl
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.
- mawo_razdel/__init__.py +34 -8
- mawo_razdel/syntagrus_patterns.py +166 -83
- {mawo_razdel-1.0.1.dist-info → mawo_razdel-1.0.3.dist-info}/METADATA +1 -1
- {mawo_razdel-1.0.1.dist-info → mawo_razdel-1.0.3.dist-info}/RECORD +7 -7
- {mawo_razdel-1.0.1.dist-info → mawo_razdel-1.0.3.dist-info}/WHEEL +0 -0
- {mawo_razdel-1.0.1.dist-info → mawo_razdel-1.0.3.dist-info}/licenses/LICENSE +0 -0
- {mawo_razdel-1.0.1.dist-info → mawo_razdel-1.0.3.dist-info}/top_level.txt +0 -0
mawo_razdel/__init__.py
CHANGED
|
@@ -69,21 +69,47 @@ class Substring:
|
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
def tokenize(text: str, use_enhanced: bool = True) -> list[Substring]:
|
|
72
|
-
"""
|
|
72
|
+
"""Токенизация русского текста.
|
|
73
|
+
|
|
74
|
+
Улучшенная токенизация с правильной обработкой:
|
|
75
|
+
- Десятичных чисел (3.14, 3,14)
|
|
76
|
+
- Процентов (95.5%)
|
|
77
|
+
- Диапазонов (1995-1999, 10:30-11:00)
|
|
78
|
+
- Дробей (1/2, 3/4)
|
|
79
|
+
- Телефонов, ID и т.д.
|
|
73
80
|
|
|
74
81
|
Args:
|
|
75
|
-
text:
|
|
76
|
-
use_enhanced:
|
|
82
|
+
text: Текст для токенизации
|
|
83
|
+
use_enhanced: Использовать улучшенные паттерны
|
|
77
84
|
|
|
78
85
|
Returns:
|
|
79
|
-
|
|
86
|
+
Список объектов Substring (токенов)
|
|
87
|
+
"""
|
|
88
|
+
# Улучшенный паттерн на основе современных практик NLP (2024-2025)
|
|
89
|
+
# Сохраняет целостность чисел при обработке русского текста
|
|
90
|
+
pattern = r"""
|
|
91
|
+
# Десятичные числа с точкой или запятой (3.14159 или 3,14159)
|
|
92
|
+
\d+[.,]\d+
|
|
93
|
+
# Диапазоны и временные интервалы (1995-1999, 10:30-11:00)
|
|
94
|
+
|\d+[-:]\d+(?:[-:]\d+)*
|
|
95
|
+
# Дроби (1/2, 3/4)
|
|
96
|
+
|\d+/\d+
|
|
97
|
+
# Проценты (с числом)
|
|
98
|
+
|\d+\s*%
|
|
99
|
+
# Обычные числа
|
|
100
|
+
|\d+
|
|
101
|
+
# Русские и латинские слова (включая ё)
|
|
102
|
+
|[\w\u0400-\u04FF]+
|
|
103
|
+
# Любой другой непробельный символ
|
|
104
|
+
|\S
|
|
80
105
|
"""
|
|
81
|
-
# Simple but effective tokenization with Russian support
|
|
82
|
-
pattern = r"\b[\w\u0400-\u04FF]+\b|\S"
|
|
83
106
|
|
|
84
107
|
tokens: list[Substring] = []
|
|
85
|
-
for match in re.finditer(pattern, text):
|
|
86
|
-
|
|
108
|
+
for match in re.finditer(pattern, text, re.VERBOSE | re.UNICODE):
|
|
109
|
+
token_text = match.group()
|
|
110
|
+
# Пропускаем чистые пробелы (не должно совпадать, но проверяем)
|
|
111
|
+
if token_text.strip():
|
|
112
|
+
tokens.append(Substring(match.start(), match.end(), token_text))
|
|
87
113
|
|
|
88
114
|
return tokens
|
|
89
115
|
|
|
@@ -33,109 +33,126 @@ class SegmentationRule:
|
|
|
33
33
|
class SynTagRusPatterns:
|
|
34
34
|
"""SynTagRus-based patterns для сегментации предложений."""
|
|
35
35
|
|
|
36
|
-
#
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"гг",
|
|
41
|
-
"г-н",
|
|
42
|
-
"г-жа", # Год, годы, господин, госпожа
|
|
36
|
+
# Головные аббревиатуры (HEAD) - идут ПЕРЕД именами/названиями
|
|
37
|
+
# После них может быть заглавная буква, но это не начало предложения
|
|
38
|
+
HEAD_ABBREVIATIONS = {
|
|
39
|
+
# Географические (перед названиями)
|
|
43
40
|
"ул",
|
|
44
41
|
"пр",
|
|
45
42
|
"пл",
|
|
46
43
|
"пер",
|
|
47
44
|
"просп",
|
|
48
|
-
"наб", #
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"корп",
|
|
52
|
-
"стр",
|
|
53
|
-
"кв", # Дом, корпус, строение, квартира
|
|
45
|
+
"наб", # улица Тверская
|
|
46
|
+
"г",
|
|
47
|
+
"гор", # г. Москва (город, не год!)
|
|
54
48
|
"обл",
|
|
55
49
|
"р-н",
|
|
56
50
|
"п",
|
|
57
51
|
"с",
|
|
58
52
|
"дер",
|
|
59
|
-
"пос", #
|
|
60
|
-
#
|
|
61
|
-
|
|
53
|
+
"пос", # область, район...
|
|
54
|
+
"им", # им. Пушкина
|
|
55
|
+
# Титулы и звания (перед именами)
|
|
56
|
+
"г-н",
|
|
57
|
+
"г-жа",
|
|
58
|
+
"гн",
|
|
59
|
+
"госп", # господин Иванов
|
|
62
60
|
"проф",
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"докт", # Академик, профессор...
|
|
67
|
-
"м",
|
|
68
|
-
"н",
|
|
69
|
-
"мл",
|
|
70
|
-
"ст", # Младший, старший научный сотрудник
|
|
71
|
-
# Титулы
|
|
72
|
-
"им",
|
|
61
|
+
"акад",
|
|
62
|
+
"доц", # профессор Петров
|
|
63
|
+
"св", # св. Иоанн
|
|
73
64
|
"ген",
|
|
74
65
|
"полк",
|
|
75
66
|
"подп",
|
|
76
67
|
"лейт",
|
|
77
|
-
"кап", #
|
|
78
|
-
|
|
68
|
+
"кап", # генерал Иванов
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# Хвостовые аббревиатуры (TAIL) - идут ПОСЛЕ чисел/слов
|
|
72
|
+
# После них НЕ должно быть заглавной буквы (иначе новое предложение)
|
|
73
|
+
TAIL_ABBREVIATIONS = {
|
|
74
|
+
# Года и века (после чисел)
|
|
75
|
+
"г",
|
|
76
|
+
"гг",
|
|
79
77
|
"в",
|
|
80
78
|
"вв",
|
|
81
|
-
"р",
|
|
82
|
-
|
|
83
|
-
"
|
|
79
|
+
"р", # 1799 г., XXI в., 250 г. до Р. Х.
|
|
80
|
+
# Адресные (после чисел)
|
|
81
|
+
"д",
|
|
82
|
+
"дом",
|
|
83
|
+
"корп",
|
|
84
|
+
"стр",
|
|
85
|
+
"кв", # д. 1, стр. 5
|
|
86
|
+
# Временные
|
|
84
87
|
"ч",
|
|
85
88
|
"час",
|
|
86
89
|
"мин",
|
|
87
|
-
"сек", #
|
|
88
|
-
#
|
|
90
|
+
"сек", # 10 ч. 30 мин.
|
|
91
|
+
# Деньги и измерения (после чисел)
|
|
92
|
+
"руб",
|
|
93
|
+
"коп",
|
|
94
|
+
"тыс",
|
|
95
|
+
"млн",
|
|
96
|
+
"млрд",
|
|
97
|
+
"трлн",
|
|
98
|
+
"кг",
|
|
99
|
+
"мг",
|
|
100
|
+
"ц",
|
|
101
|
+
"л",
|
|
102
|
+
"мм",
|
|
103
|
+
"км",
|
|
104
|
+
"га",
|
|
105
|
+
"м",
|
|
106
|
+
# Страницы, тома (после чисел)
|
|
89
107
|
"т",
|
|
90
108
|
"тт",
|
|
109
|
+
"с",
|
|
91
110
|
"пп",
|
|
92
111
|
"рис",
|
|
93
112
|
"илл",
|
|
94
|
-
"табл", #
|
|
113
|
+
"табл", # стр уже в адресных
|
|
114
|
+
# Научные степени (инициалы перед)
|
|
115
|
+
"к",
|
|
116
|
+
"канд",
|
|
117
|
+
"докт",
|
|
118
|
+
"н", # к.т.н., д.ф.н.
|
|
119
|
+
# Общие (обычно внутри текста или в конце)
|
|
95
120
|
"см",
|
|
96
121
|
"ср",
|
|
97
122
|
"напр",
|
|
98
|
-
"в т.ч",
|
|
99
|
-
"и т.д",
|
|
100
|
-
"и т.п",
|
|
101
|
-
"и др", # Смотри, сравни...
|
|
102
123
|
"др",
|
|
103
124
|
"проч",
|
|
104
125
|
"прим",
|
|
105
|
-
"примеч",
|
|
106
|
-
|
|
107
|
-
"
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
"л", # Килограмм, грамм...
|
|
111
|
-
"мм",
|
|
112
|
-
"км",
|
|
113
|
-
"га", # Метр, сантиметр...
|
|
114
|
-
"млн",
|
|
115
|
-
"млрд",
|
|
116
|
-
"тыс",
|
|
117
|
-
"трлн", # Миллион, миллиард...
|
|
126
|
+
"примеч",
|
|
127
|
+
"т.е",
|
|
128
|
+
"т.д",
|
|
129
|
+
"т.п",
|
|
130
|
+
"т.к", # и т.д., и т.п.
|
|
118
131
|
# Организационные
|
|
119
132
|
"о-во",
|
|
120
133
|
"о-ва",
|
|
121
134
|
"о-ние",
|
|
122
|
-
"о-ния",
|
|
135
|
+
"о-ния",
|
|
123
136
|
"зам",
|
|
124
137
|
"пом",
|
|
125
138
|
"зав",
|
|
126
|
-
"нач",
|
|
139
|
+
"нач",
|
|
127
140
|
# Прочие
|
|
128
141
|
"etc",
|
|
129
142
|
"et al",
|
|
130
143
|
"ibid",
|
|
131
|
-
"op cit",
|
|
144
|
+
"op cit",
|
|
132
145
|
"англ",
|
|
133
146
|
"нем",
|
|
134
147
|
"франц",
|
|
135
148
|
"итал",
|
|
136
|
-
"исп",
|
|
149
|
+
"исп",
|
|
150
|
+
"лат", # Языки
|
|
137
151
|
}
|
|
138
152
|
|
|
153
|
+
# Объединенный список всех аббревиатур
|
|
154
|
+
ABBREVIATIONS = HEAD_ABBREVIATIONS | TAIL_ABBREVIATIONS
|
|
155
|
+
|
|
139
156
|
# Почетные звания и должности (часто перед ФИО)
|
|
140
157
|
TITLES = {
|
|
141
158
|
"президент",
|
|
@@ -224,43 +241,106 @@ class SynTagRusPatterns:
|
|
|
224
241
|
self.sentence_end_pattern = re.compile(r"[.!?]+\s+[А-ЯЁ«\"\'(]")
|
|
225
242
|
|
|
226
243
|
def is_abbreviation(self, text: str, pos: int) -> bool:
|
|
227
|
-
"""Проверяет, является ли точка
|
|
244
|
+
"""Проверяет, является ли точка перед позицией pos частью аббревиатуры.
|
|
245
|
+
|
|
246
|
+
Улучшено с проверкой контекста - на основе современных практик NLP (2024-2025).
|
|
247
|
+
Проверяет ПЕРЕД и ПОСЛЕ точки, чтобы определить, действительно ли это аббревиатура,
|
|
248
|
+
которая должна блокировать границу предложения.
|
|
228
249
|
|
|
229
250
|
Args:
|
|
230
|
-
text:
|
|
231
|
-
pos:
|
|
251
|
+
text: Текст для проверки
|
|
252
|
+
pos: Позиция ПОСЛЕ точки (граница)
|
|
232
253
|
|
|
233
254
|
Returns:
|
|
234
|
-
True
|
|
255
|
+
True если точка - часть аббревиатуры, блокирующей границу предложения
|
|
235
256
|
"""
|
|
236
|
-
if pos <=
|
|
257
|
+
if pos <= 1 or pos > len(text):
|
|
237
258
|
return False
|
|
238
259
|
|
|
239
|
-
#
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
preceding = text[pos - look_back : pos].lower().strip()
|
|
243
|
-
if preceding in self.ABBREVIATIONS:
|
|
244
|
-
return True
|
|
260
|
+
# Проверяем, что перед pos действительно точка
|
|
261
|
+
if text[pos - 1] != ".":
|
|
262
|
+
return False
|
|
245
263
|
|
|
246
|
-
|
|
264
|
+
# Ищем токен аббревиатуры ПЕРЕД точкой
|
|
265
|
+
# Извлекаем слово/токен перед точкой
|
|
266
|
+
before_match = re.search(r"(\w+)\.?$", text[: pos - 1])
|
|
267
|
+
if not before_match:
|
|
268
|
+
return False
|
|
269
|
+
|
|
270
|
+
preceding = before_match.group(1).lower()
|
|
271
|
+
|
|
272
|
+
# Проверяем, есть ли в нашем списке аббревиатур
|
|
273
|
+
if preceding not in self.ABBREVIATIONS:
|
|
274
|
+
return False
|
|
275
|
+
|
|
276
|
+
# КРИТИЧНО: Проверяем что идет ПОСЛЕ точки
|
|
277
|
+
# Это ключевое улучшение на основе современных практик NLP
|
|
278
|
+
remaining = text[pos:].lstrip()
|
|
279
|
+
|
|
280
|
+
if not remaining:
|
|
281
|
+
# Конец текста - аббревиатура в конце
|
|
282
|
+
return True
|
|
283
|
+
|
|
284
|
+
# Проверяем первый символ после пробелов
|
|
285
|
+
next_char = remaining[0]
|
|
286
|
+
|
|
287
|
+
# УЛУЧШЕНИЕ: Различаем HEAD и TAIL аббревиатуры
|
|
288
|
+
is_head = preceding in self.HEAD_ABBREVIATIONS
|
|
289
|
+
is_tail = preceding in self.TAIL_ABBREVIATIONS
|
|
290
|
+
|
|
291
|
+
# Если следующий символ - заглавная буква (не цифра)
|
|
292
|
+
if next_char.isupper() and next_char.isalpha():
|
|
293
|
+
# HEAD аббревиатуры (ул., г., проф.) могут идти перед заглавной буквой
|
|
294
|
+
# Например: "ул. Тверская", "г. Москва", "проф. Иванов"
|
|
295
|
+
if is_head:
|
|
296
|
+
return True # Не разбиваем
|
|
297
|
+
|
|
298
|
+
# TAIL аббревиатуры (г., в., д.) НЕ должны идти перед заглавной буквой
|
|
299
|
+
# Исключение: инициалы (А. С. Пушкин)
|
|
300
|
+
if is_tail:
|
|
301
|
+
# Проверяем инициалы: один символ + точка
|
|
302
|
+
if len(remaining) > 2 and remaining[1] == ".":
|
|
303
|
+
return True # Часть последовательности инициалов
|
|
304
|
+
# Иначе это начало нового предложения
|
|
305
|
+
return False
|
|
306
|
+
|
|
307
|
+
# Строчная буква, цифра или пунктуация после аббревиатуры - оставляем соединенными
|
|
308
|
+
return True
|
|
247
309
|
|
|
248
310
|
def is_initials_context(self, text: str, pos: int) -> bool:
|
|
249
|
-
"""Проверяет, находится ли точка в контексте инициалов.
|
|
311
|
+
"""Проверяет, находится ли точка непосредственно в контексте инициалов.
|
|
312
|
+
|
|
313
|
+
Улучшено: проверяем только если инициалы находятся РЯДОМ с границей,
|
|
314
|
+
а не в радиусе 20 символов.
|
|
250
315
|
|
|
251
316
|
Args:
|
|
252
|
-
text:
|
|
253
|
-
pos:
|
|
317
|
+
text: Текст для проверки
|
|
318
|
+
pos: Позиция после точки
|
|
254
319
|
|
|
255
320
|
Returns:
|
|
256
|
-
True
|
|
321
|
+
True если в непосредственном контексте инициалов
|
|
257
322
|
"""
|
|
258
|
-
#
|
|
259
|
-
|
|
260
|
-
|
|
323
|
+
# Проверяем небольшой контекст: 5 символов до и 10 после
|
|
324
|
+
# Это достаточно для "А. С. Пушкин" но не захватывает далекие инициалы
|
|
325
|
+
start = max(0, pos - 5)
|
|
326
|
+
end = min(len(text), pos + 10)
|
|
261
327
|
context = text[start:end]
|
|
262
328
|
|
|
263
|
-
|
|
329
|
+
# Дополнительно: точка должна быть ВНУТРИ найденного паттерна инициалов
|
|
330
|
+
match = self.initials_pattern.search(context)
|
|
331
|
+
if not match:
|
|
332
|
+
return False
|
|
333
|
+
|
|
334
|
+
# Проверяем, что граница (pos) находится внутри найденного паттерна инициалов
|
|
335
|
+
# или сразу после него (с учетом смещения start)
|
|
336
|
+
match_start = start + match.start()
|
|
337
|
+
match_end = start + match.end()
|
|
338
|
+
|
|
339
|
+
# Граница должна быть внутри паттерна или максимум на 2 символа после
|
|
340
|
+
if match_start <= pos <= match_end + 2:
|
|
341
|
+
return True
|
|
342
|
+
|
|
343
|
+
return False
|
|
264
344
|
|
|
265
345
|
def find_sentence_boundaries(self, text: str) -> list[int]:
|
|
266
346
|
"""Находит границы предложений в тексте.
|
|
@@ -287,8 +367,9 @@ class SynTagRusPatterns:
|
|
|
287
367
|
# Check if this is a valid boundary
|
|
288
368
|
is_valid_boundary = False
|
|
289
369
|
|
|
290
|
-
# Case 1: Followed by whitespace and capital letter
|
|
291
|
-
|
|
370
|
+
# Case 1: Followed by whitespace and capital letter (русская ИЛИ латинская)
|
|
371
|
+
# УЛУЧШЕНИЕ: добавлена поддержка латинских заглавных (для XXI, IV, и т.д.)
|
|
372
|
+
if re.match(r"\s+[А-ЯЁA-Z«\"\'(]", remaining):
|
|
292
373
|
is_valid_boundary = True
|
|
293
374
|
|
|
294
375
|
# Case 2: Followed by paragraph break
|
|
@@ -313,18 +394,20 @@ class SynTagRusPatterns:
|
|
|
313
394
|
"""Проверяет, блокируется ли граница высокоприоритетным правилом.
|
|
314
395
|
|
|
315
396
|
Args:
|
|
316
|
-
text:
|
|
317
|
-
pos:
|
|
397
|
+
text: Текст
|
|
398
|
+
pos: Позиция границы (после точки/знака)
|
|
318
399
|
|
|
319
400
|
Returns:
|
|
320
|
-
True
|
|
401
|
+
True если граница блокирована
|
|
321
402
|
"""
|
|
322
|
-
#
|
|
403
|
+
# Проверка на аббревиатуру (точка после аббревиатуры)
|
|
404
|
+
# ВАЖНО: is_abbreviation уже проверяет контекст до И после точки
|
|
323
405
|
if pos > 0 and text[pos - 1] == ".":
|
|
324
|
-
|
|
406
|
+
# Передаем позицию ПОСЛЕ точки (pos), а не позицию точки
|
|
407
|
+
if self.is_abbreviation(text, pos):
|
|
325
408
|
return True
|
|
326
409
|
|
|
327
|
-
#
|
|
410
|
+
# Проверка на инициалы (А. С. Пушкин)
|
|
328
411
|
if self.is_initials_context(text, pos):
|
|
329
412
|
return True
|
|
330
413
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
mawo_razdel/__init__.py,sha256=
|
|
2
|
-
mawo_razdel/syntagrus_patterns.py,sha256=
|
|
1
|
+
mawo_razdel/__init__.py,sha256=TDGqj1RnRWYHtTv14a__lwD_ke2l4a2XxprXjE2-QP0,8481
|
|
2
|
+
mawo_razdel/syntagrus_patterns.py,sha256=na90JObwtakS59qjzBJgmFLxh_rlhNok-JgkiVQpeM0,18363
|
|
3
3
|
mawo_razdel/data/corpora_sents.txt.lzma,sha256=9g3tHoVAVWxZRBao3S9jSvDREK88tTHcW_HdIsUqOmo,3558884
|
|
4
4
|
mawo_razdel/data/corpora_tokens.txt.lzma,sha256=32JAHq7qtQgX2EA88DelBDiAuCG8Q8vNVqCRakrcSXY,3785332
|
|
5
5
|
mawo_razdel/data/gicrya_sents.txt.lzma,sha256=puRJ23GkU554Ed81yn8B7B35Zqjeqa4RKEtIEL56d6I,2189240
|
|
@@ -8,8 +8,8 @@ mawo_razdel/data/rnc_sents.txt.lzma,sha256=In5BVwCvotaWA-BZy446qLjhBAht4iLE2lv5v
|
|
|
8
8
|
mawo_razdel/data/rnc_tokens.txt.lzma,sha256=7keKlZaZxHmw7D8ZtFLnCPiCS2hXPtxjt1vBeum2E54,2491824
|
|
9
9
|
mawo_razdel/data/syntag_sents.txt.lzma,sha256=TrdCYsTWu9lG04cUGPDrEaOh4h-yLgAg3pOpMqsRWSk,2190388
|
|
10
10
|
mawo_razdel/data/syntag_tokens.txt.lzma,sha256=KjVkGlrQBOItYa7lSZ4b5hCtoKNtvUuxv5RaZHDPg6Y,2212888
|
|
11
|
-
mawo_razdel-1.0.
|
|
12
|
-
mawo_razdel-1.0.
|
|
13
|
-
mawo_razdel-1.0.
|
|
14
|
-
mawo_razdel-1.0.
|
|
15
|
-
mawo_razdel-1.0.
|
|
11
|
+
mawo_razdel-1.0.3.dist-info/licenses/LICENSE,sha256=HxcBccBgl94zsrO98Iv1FqnG5cp8fSsnxfq3YDSi7Mg,1066
|
|
12
|
+
mawo_razdel-1.0.3.dist-info/METADATA,sha256=1oL9HpjIB1sW8nmYvU2ZX0JGUT6RnxytNulmBJnj4nU,13039
|
|
13
|
+
mawo_razdel-1.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
14
|
+
mawo_razdel-1.0.3.dist-info/top_level.txt,sha256=zjx6jdks6KA3fcXqFLPR_XQeF7-3anYoqlHs9kpiojA,12
|
|
15
|
+
mawo_razdel-1.0.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|