Spaces:
Build error
Build error
| """ | |
| 🔄 نرمالسازی موجودیتها | |
| Entity Normalization for better matching | |
| """ | |
| import re | |
| import logging | |
| logger = logging.getLogger(__name__) | |
| class EntityNormalizer: | |
| """ | |
| کلاس برای نرمالسازی موجودیتهای مختلف | |
| این کلاس موجودیتها را به فرمت استاندارد تبدیل میکند تا: | |
| - تطابق دقیق بهتر انجام شود | |
| - fuzzy matching موثرتر باشد | |
| - از تکرار جلوگیری شود | |
| """ | |
| def __init__(self): | |
| """مقداردهی اولیه normalizer""" | |
| logger.info("✅ EntityNormalizer initialized") | |
| # نقشه تبدیل اعداد فارسی به انگلیسی | |
| self.persian_to_english = str.maketrans('۰۱۲۳۴۵۶۷۸۹', '0123456789') | |
| def normalize(self, text: str, entity_type: str) -> str: | |
| """ | |
| نرمالسازی بر اساس نوع موجودیت | |
| Args: | |
| text: متن اصلی موجودیت | |
| entity_type: نوع موجودیت (person/company/amount/percent) | |
| Returns: | |
| متن نرمالشده | |
| Examples: | |
| >>> normalizer = EntityNormalizer() | |
| >>> normalizer.normalize("آقای علی احمدی", "person") | |
| 'علی احمدی' | |
| >>> normalizer.normalize("۵۰ میلیارد ریال", "amount") | |
| '50 میلیارد ریال' | |
| """ | |
| if not text: | |
| return "" | |
| # انتخاب متد مناسب بر اساس نوع | |
| if entity_type == "person": | |
| return self.normalize_person(text) | |
| elif entity_type == "company": | |
| return self.normalize_company(text) | |
| elif entity_type == "amount": | |
| return self.normalize_amount(text) | |
| elif entity_type == "percent": | |
| return self.normalize_percent(text) | |
| else: | |
| logger.warning(f"⚠️ Unknown entity type: {entity_type}, using basic normalization") | |
| return self._basic_normalize(text) | |
| def _basic_normalize(self, text: str) -> str: | |
| """نرمالسازی پایه برای هر متنی""" | |
| text = text.strip().lower() | |
| text = re.sub(r'\s+', ' ', text) | |
| return text | |
| def normalize_person(self, text: str) -> str: | |
| """ | |
| نرمالسازی اسامی اشخاص | |
| - حذف فضاهای اضافی | |
| - lowercase | |
| - حذف عناوین (آقای، خانم، دکتر، ...) | |
| Args: | |
| text: نام شخص | |
| Returns: | |
| نام نرمالشده | |
| Examples: | |
| >>> normalizer.normalize_person("آقای علی احمدی") | |
| 'علی احمدی' | |
| >>> normalizer.normalize_person("دکتر مریم کریمی") | |
| 'مریم کریمی' | |
| """ | |
| text = text.strip().lower() | |
| # حذف فضاهای اضافی | |
| text = re.sub(r'\s+', ' ', text) | |
| # حذف عناوین رایج | |
| titles = [ | |
| 'آقای', 'خانم', 'خانوم', | |
| 'دکتر', 'دکتور', 'dr.', | |
| 'مهندس', 'استاد', 'اقای', | |
| 'جناب', 'سرکار', | |
| ] | |
| for title in titles: | |
| # حذف عنوان از ابتدای متن | |
| pattern = rf'^{title}\s+' | |
| text = re.sub(pattern, '', text, flags=re.IGNORECASE) | |
| return text.strip() | |
| def normalize_company(self, text: str) -> str: | |
| """ | |
| نرمالسازی نام شرکتها | |
| - حذف فضاهای اضافی | |
| - lowercase | |
| - استانداردسازی اختصارات (ش. → شرکت) | |
| - حذف کلمات اضافی پایانی | |
| Args: | |
| text: نام شرکت | |
| Returns: | |
| نام نرمالشده | |
| Examples: | |
| >>> normalizer.normalize_company("ش. پارس") | |
| 'شرکت پارس' | |
| >>> normalizer.normalize_company("شرکت ملی نفت ایران") | |
| 'شرکت ملی نفت' | |
| """ | |
| text = text.strip().lower() | |
| # حذف فضاهای اضافی | |
| text = re.sub(r'\s+', ' ', text) | |
| # استانداردسازی اختصارات | |
| replacements = { | |
| 'ش.': 'شرکت', | |
| 'ش ': 'شرکت ', | |
| 'شرکت': 'شرکت', | |
| 'بانک': 'بانک', | |
| 'سازمان': 'سازمان', | |
| 'موسسه': 'موسسه', | |
| 'گروه': 'گروه', | |
| } | |
| for old, new in replacements.items(): | |
| text = text.replace(old, new) | |
| # حذف کلمات اضافی پایانی که معمولاً اختیاری هستند | |
| # مثلاً "شرکت ملی نفت ایران" → "شرکت ملی نفت" | |
| optional_suffixes = [ | |
| r'\s+ایران$', | |
| r'\s+تهران$', | |
| r'\s+خصوصی$', | |
| r'\s+عام$', | |
| r'\s+سهامی$', | |
| r'\s+سهامی\s+عام$', | |
| r'\s+سهامی\s+خاص$', | |
| ] | |
| for pattern in optional_suffixes: | |
| text = re.sub(pattern, '', text) | |
| return text.strip() | |
| def normalize_amount(self, text: str) -> str: | |
| """ | |
| نرمالسازی مبالغ مالی | |
| - تبدیل اعداد فارسی به انگلیسی | |
| - حذف فضاهای اضافی | |
| - lowercase | |
| - استانداردسازی واحدها | |
| Args: | |
| text: مبلغ مالی | |
| Returns: | |
| مبلغ نرمالشده | |
| Examples: | |
| >>> normalizer.normalize_amount("۵۰ میلیارد ریال") | |
| '50 میلیارد ریال' | |
| >>> normalizer.normalize_amount("30 میلیارد تومن") | |
| '30 میلیارد تومان' | |
| """ | |
| # تبدیل اعداد فارسی به انگلیسی | |
| text = text.translate(self.persian_to_english) | |
| text = text.strip().lower() | |
| # حذف فضاهای اضافی | |
| text = re.sub(r'\s+', ' ', text) | |
| # استانداردسازی واحدهای پولی | |
| unit_map = { | |
| 'تومن': 'تومان', | |
| 'ریآل': 'ریال', | |
| 'ريال': 'ریال', | |
| 'دالر': 'دلار', | |
| 'يورو': 'یورو', | |
| } | |
| for old, new in unit_map.items(): | |
| text = text.replace(old, new) | |
| # استانداردسازی واحدهای مقیاس | |
| scale_map = { | |
| 'ميليارد': 'میلیارد', | |
| 'ميليون': 'میلیون', | |
| 'هزار': 'هزار', | |
| 'تریلیون': 'تریلیون', | |
| } | |
| for old, new in scale_map.items(): | |
| text = text.replace(old, new) | |
| # حذف صفات اضافی (مثلاً "ریالی" → "ریال") | |
| text = text.replace('ریالی', 'ریال') | |
| text = text.replace('تومانی', 'تومان') | |
| text = text.replace('دلاری', 'دلار') | |
| return text.strip() | |
| def normalize_percent(self, text: str) -> str: | |
| """ | |
| نرمالسازی درصدها | |
| - تبدیل اعداد فارسی به انگلیسی | |
| - یکسانسازی علامت درصد (%, ٪ → درصد) | |
| - فرمت استاندارد: "عدد درصد" | |
| Args: | |
| text: درصد | |
| Returns: | |
| درصد نرمالشده | |
| Examples: | |
| >>> normalizer.normalize_percent("۱۵٪") | |
| '15 درصد' | |
| >>> normalizer.normalize_percent("20%") | |
| '20 درصد' | |
| >>> normalizer.normalize_percent("25 درصدی") | |
| '25 درصد' | |
| """ | |
| # تبدیل اعداد فارسی به انگلیسی | |
| text = text.translate(self.persian_to_english) | |
| text = text.strip() | |
| # حذف فضاهای اضافی | |
| text = re.sub(r'\s+', ' ', text) | |
| # یکسانسازی علامتهای درصد | |
| text = text.replace('٪', '%') | |
| # تبدیل به فرمت استاندارد: "عدد درصد" | |
| # مثال: "15%" → "15 درصد" | |
| text = re.sub(r'(\d+(?:\.\d+)?)\s*[%٪]', r'\1 درصد', text) | |
| # حذف "درصدی" و تبدیل به "درصد" | |
| text = re.sub(r'(\d+(?:\.\d+)?)\s*درصدی', r'\1 درصد', text) | |
| return text.strip() | |
| # ✅ تستهای سریع | |
| if __name__ == "__main__": | |
| print("=" * 60) | |
| print("🧪 Testing Normalizer Module") | |
| print("=" * 60) | |
| normalizer = EntityNormalizer() | |
| # تست 1: Person normalization | |
| print("\n📊 Test 1: Person Normalization") | |
| person_tests = [ | |
| "آقای علی احمدی", | |
| "دکتر مریم کریمی", | |
| "خانم سارا رضایی", | |
| "علی محمدی" | |
| ] | |
| for person in person_tests: | |
| normalized = normalizer.normalize_person(person) | |
| print(f" '{person}' → '{normalized}'") | |
| # تست 2: Company normalization | |
| print("\n📊 Test 2: Company Normalization") | |
| company_tests = [ | |
| "شرکت پارس", | |
| "ش. ملی نفت ایران", | |
| "شرکت صبا سهامی خاص", | |
| "بانک ملی ایران" | |
| ] | |
| for company in company_tests: | |
| normalized = normalizer.normalize_company(company) | |
| print(f" '{company}' → '{normalized}'") | |
| # تست 3: Amount normalization | |
| print("\n📊 Test 3: Amount Normalization") | |
| amount_tests = [ | |
| "۵۰ میلیارد ریال", | |
| "30 میلیارد تومن", | |
| "100 میلیارد ریالی", | |
| "5 ميليون دلار" | |
| ] | |
| for amount in amount_tests: | |
| normalized = normalizer.normalize_amount(amount) | |
| print(f" '{amount}' → '{normalized}'") | |
| # تست 4: Percent normalization | |
| print("\n📊 Test 4: Percent Normalization") | |
| percent_tests = [ | |
| "۱۵٪", | |
| "20%", | |
| "25 درصدی", | |
| "30 درصد" | |
| ] | |
| for percent in percent_tests: | |
| normalized = normalizer.normalize_percent(percent) | |
| print(f" '{percent}' → '{normalized}'") | |
| print("\n" + "=" * 60) | |
| print("✅ All tests completed!") | |
| print("=" * 60) | |