File size: 6,410 Bytes
4dd2f1d 0ffefbc 361f7d9 503cd1a 0ffefbc 361f7d9 0ffefbc 361f7d9 0ffefbc 361f7d9 0ffefbc 361f7d9 0ffefbc 09bc630 361f7d9 0ffefbc 09bc630 0ffefbc 09bc630 0ffefbc 09bc630 0ffefbc 09bc630 0ffefbc 09bc630 0ffefbc 09bc630 0ffefbc 4dd2f1d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | import pandas as pd
import re
# Конфиги для парсинга дат
YEARS_ALIASES = {
'O': 1918,
'M': 2000,
'N': 2026
}
def _parse_single_year(year_str: str) -> int:
"""
Args:
year_str: "1962" or alias like "O", "M", "N"
Returns:
int: Год
"""
if year_str in YEARS_ALIASES:
return YEARS_ALIASES[year_str]
else:
try:
return int(year_str)
except ValueError:
raise ValueError(f"Невозможно распарсить год: {year_str}")
def _parse_date_range(date_str: str) -> tuple[int, int]:
"""Парсит строку с датой и возвращает (start_year, end_year).
Поддерживает:
- "1962-2002" -> (1962, 2002)
- "1962" -> (1962, 1962)
Args:
date_str: Строка с датой
Returns:
tuple: (start_year, end_year)
"""
date_str = date_str.strip()
# Если содержит дефис
if '-' in date_str:
parts = date_str.split('-')
start = _parse_single_year(parts[0].strip())
end = _parse_single_year(parts[1].strip())
assert start <= end, f"Год начала {start} должен быть меньше или равен году конца {end}"
return (start, end)
year = _parse_single_year(date_str)
return (year, year)
def parse_metadata_from_document(text: str) -> list[tuple[str, tuple[int, int], str]]:
"""Парсит markdown текст и возвращает список (чанк_текста, (год_начала, год_конца), summary).
Формат разметки ОБЯЗАТЕЛЕН:
- ## Summary text - заголовок summary (двойной хэш + пробел)
- ### 1962-2002 - заголовок с годом (тройной хэш + пробел)
Правила:
- Каждый документ ДОЛЖЕН начинаться с "## {summary}"
- После summary ДОЛЖНЫ быть заголовки "### {годы}" с текстом
- ## распространяется на все абзацы ниже до следующего ## или конца файла
- ### распространяется на абзацы ниже до следующего ### или ##
- Текст БЕЗ предшествующего ### Не добавляется в результат
Args:
text: Полный текст документа
Returns:
list: [(chunk_text, (start_year, end_year), summary), ...]
"""
lines = text.split('\n')
result = []
current_summary = None
current_year_range = None
for line in lines:
strip_line = line.strip()
if not strip_line:
continue
if strip_line.startswith('## '):
current_summary = strip_line[3:].strip() # Пропускаем "## "
# Проверяем, является ли строка "### " (год с пробелом после)
elif strip_line.startswith('### '):
current_year_range = _parse_date_range(strip_line[4:])
else:
# Добавляем текст только если у нас есть год
assert current_year_range and current_summary, breakpoint()
result.append((line, current_year_range, current_summary))
return result
def process_documents(documents) -> tuple[pd.DataFrame, pd.DataFrame]:
"""
Обрабатывает документы с парсингом дат и создает два датафрейма.
Returns:
tuple: (paragraphs_df, chunks_df)
paragraphs_df:
- paragraph_id: уникальный идентификатор абзаца
- summary: название документа/раздела
- start_year: год начала периода
- end_year: год окончания периода
- text: текст абзаца
- document_id: ссылка на исходный документ
chunks_df:
- chunk_id: уникальный идентификатор чанка
- paragraph_id: ссылка на абзац (foreign key)
- text: текст чанка
- lemmatized_text: лемматизированный текст (добавляется позже)
"""
paragraphs_data = []
chunks_data = []
paragraph_id_counter = 0
chunk_id_counter = 0
for doc_id, document in enumerate(documents):
dated_chunks = parse_metadata_from_document(document)
for chunk_text, year_range, summary in dated_chunks:
paragraphs = chunk_text.split('\n')
for paragraph in paragraphs:
paragraph = paragraph.strip()
# Добавляем информацию о параграфе в датафрейм параграфов
paragraphs_data.append({
'paragraph_id': paragraph_id_counter,
'summary': summary,
'start_year': year_range[0],
'end_year': year_range[1],
'text': paragraph,
'document_id': doc_id
})
# Разбиваем параграф на предложения и создаем чанки
sentences = re.split(r'(?<=[.!?])\s+', paragraph)
for sent in sentences:
chunks_data.append({
'chunk_id': chunk_id_counter,
'paragraph_id': paragraph_id_counter,
'text': sent.strip()
})
chunk_id_counter += 1
paragraph_id_counter += 1
# Создаем датафреймы
paragraphs_df = pd.DataFrame(paragraphs_data)
chunks_df = pd.DataFrame(chunks_data)
print(f"Создано {len(chunks_df)} чанков")
print(f"Из {len(paragraphs_df)} абзацев в {len(set(paragraphs_df['document_id']))} документах")
return paragraphs_df, chunks_df
|