step0b_normalization_analysis.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. """
  2. Step 0b: Analyze text patterns and suggest normalizations.
  3. """
  4. from typing import List, Dict, Set, Tuple
  5. from collections import Counter
  6. import re
  7. import pandas as pd
  8. from pipeline.models.base import PipelineStep
  9. class NormalizationAnalyzer(PipelineStep):
  10. """Analyze text patterns and suggest normalizations"""
  11. def __init__(self, output_dir: str = "./pipeline_output"):
  12. super().__init__(output_dir)
  13. def execute(self, df: pd.DataFrame) -> Dict[str, Dict[str, str]]:
  14. """
  15. Analyze text and suggest normalizations.
  16. Args:
  17. df: DataFrame with messages
  18. Returns:
  19. Dictionary of suggested normalizations
  20. """
  21. self.logger.info("Analyzing text patterns for normalization...")
  22. # Find abbreviations
  23. abbreviations = self._find_abbreviations(df)
  24. # Find acronyms
  25. acronyms = self._find_acronyms(df)
  26. # Find common misspellings
  27. misspellings = self._find_misspellings(df)
  28. # Find date/time patterns
  29. datetime_patterns = self._find_datetime_patterns(df)
  30. # Combine suggestions
  31. suggestions = {
  32. "abbreviations": abbreviations,
  33. "acronyms": acronyms,
  34. "misspellings": misspellings,
  35. "datetime_patterns": datetime_patterns,
  36. }
  37. # Save results
  38. self._save_normalization_suggestions(suggestions)
  39. return suggestions
  40. def _find_abbreviations(self, df: pd.DataFrame) -> Dict[str, str]:
  41. """Find common abbreviations"""
  42. self.logger.info("Finding abbreviations...")
  43. # Common medical/legal abbreviations
  44. known_abbrevs = {
  45. "dr.": "doctor",
  46. "dr ": "doctor ",
  47. "appt": "appointment",
  48. "hosp": "hospital",
  49. "med": "medical",
  50. "meds": "medications",
  51. "rx": "prescription",
  52. "pt": "patient",
  53. "pts": "patients",
  54. "pron": "pronoun",
  55. "prns": "pronouns",
  56. "info": "information",
  57. "dept": "department",
  58. "rep": "representative",
  59. "admin": "administration",
  60. "surg": "surgery",
  61. "proc": "procedure",
  62. }
  63. # Find abbreviations in text
  64. found_abbrevs = {}
  65. pattern = r"\b[a-z]{2,5}\.?\b"
  66. for message in df["message"].fillna(""):
  67. text = str(message).lower()
  68. matches = re.findall(pattern, text)
  69. for match in matches:
  70. if match in known_abbrevs:
  71. found_abbrevs[match] = known_abbrevs[match]
  72. self.logger.info(f"Found {len(found_abbrevs)} abbreviations")
  73. return found_abbrevs
  74. def _find_acronyms(self, df: pd.DataFrame) -> Dict[str, str]:
  75. """Find common acronyms"""
  76. self.logger.info("Finding acronyms...")
  77. known_acronyms = {
  78. "msk": "memorial sloan kettering",
  79. "er": "emergency room",
  80. "icu": "intensive care unit",
  81. "ob": "obstetrics",
  82. "gyn": "gynecology",
  83. "obgyn": "obstetrics gynecology",
  84. "pcp": "primary care physician",
  85. "np": "nurse practitioner",
  86. "pa": "physician assistant",
  87. "rn": "registered nurse",
  88. "lpn": "licensed practical nurse",
  89. "emr": "electronic medical record",
  90. "ehr": "electronic health record",
  91. "hipaa": "health insurance portability accountability act",
  92. "lgbtq": "lesbian gay bisexual transgender queer",
  93. "lgbt": "lesbian gay bisexual transgender",
  94. }
  95. found_acronyms = {}
  96. pattern = r"\b[A-Z]{2,6}\b"
  97. for message in df["message"].fillna(""):
  98. text = str(message)
  99. matches = re.findall(pattern, text)
  100. for match in matches:
  101. match_lower = match.lower()
  102. if match_lower in known_acronyms:
  103. found_acronyms[match_lower] = known_acronyms[match_lower]
  104. self.logger.info(f"Found {len(found_acronyms)} acronyms")
  105. return found_acronyms
  106. def _find_misspellings(self, df: pd.DataFrame) -> Dict[str, str]:
  107. """Find common misspellings"""
  108. self.logger.info("Finding common misspellings...")
  109. # Common misspellings in medical/legal context
  110. known_misspellings = {
  111. "recieve": "receive",
  112. "occured": "occurred",
  113. "seperate": "separate",
  114. "definately": "definitely",
  115. "accomodate": "accommodate",
  116. "untill": "until",
  117. "thier": "their",
  118. "recieved": "received",
  119. }
  120. found_misspellings = {}
  121. for message in df["message"].fillna(""):
  122. text = str(message).lower()
  123. words = text.split()
  124. for word in words:
  125. clean_word = re.sub(r"[^a-z]", "", word)
  126. if clean_word in known_misspellings:
  127. found_misspellings[clean_word] = known_misspellings[clean_word]
  128. self.logger.info(f"Found {len(found_misspellings)} misspellings")
  129. return found_misspellings
  130. def _find_datetime_patterns(self, df: pd.DataFrame) -> Dict[str, str]:
  131. """Find date/time patterns"""
  132. self.logger.info("Finding date/time patterns...")
  133. patterns = {}
  134. # Common date patterns
  135. date_patterns = [
  136. (r"\d{1,2}/\d{1,2}/\d{2,4}", "date_slash"),
  137. (r"\d{1,2}-\d{1,2}-\d{2,4}", "date_dash"),
  138. (
  139. r"\b(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\s+\d{1,2}",
  140. "date_month_day",
  141. ),
  142. (
  143. r"\d{1,2}\s+(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)",
  144. "date_day_month",
  145. ),
  146. ]
  147. for message in df["message"].fillna(""):
  148. text = str(message).lower()
  149. for pattern, pattern_name in date_patterns:
  150. if re.search(pattern, text):
  151. patterns[pattern_name] = pattern
  152. self.logger.info(f"Found {len(patterns)} date/time patterns")
  153. return patterns
  154. def _save_normalization_suggestions(self, suggestions: Dict):
  155. """Save normalization suggestions"""
  156. self.save_results(suggestions, "normalization_suggestions.json")
  157. # Create readable text file
  158. text_output = []
  159. text_output.append("TEXT NORMALIZATION SUGGESTIONS")
  160. text_output.append("=" * 80)
  161. text_output.append("")
  162. text_output.append("ABBREVIATIONS TO EXPAND:")
  163. text_output.append("-" * 80)
  164. for abbrev, expansion in sorted(suggestions["abbreviations"].items()):
  165. text_output.append(f" {abbrev:20} -> {expansion}")
  166. text_output.append("")
  167. text_output.append("ACRONYMS TO EXPAND:")
  168. text_output.append("-" * 80)
  169. for acronym, expansion in sorted(suggestions["acronyms"].items()):
  170. text_output.append(f" {acronym:20} -> {expansion}")
  171. text_output.append("")
  172. if suggestions["misspellings"]:
  173. text_output.append("MISSPELLINGS TO CORRECT:")
  174. text_output.append("-" * 80)
  175. for misspell, correct in sorted(suggestions["misspellings"].items()):
  176. text_output.append(f" {misspell:20} -> {correct}")
  177. text_output.append("")
  178. text_output.append("DATE/TIME PATTERNS FOUND:")
  179. text_output.append("-" * 80)
  180. for pattern_name, pattern in suggestions["datetime_patterns"].items():
  181. text_output.append(f" {pattern_name}: {pattern}")
  182. filepath = self.output_dir / "normalization_suggestions.txt"
  183. with open(filepath, "w") as f:
  184. f.write("\n".join(text_output))
  185. self.logger.info(f"Saved normalization suggestions to: {filepath}")
  186. if __name__ == "__main__":
  187. import pandas as pd
  188. df = pd.read_csv("../_sources/signal_messages.csv")
  189. analyzer = NormalizationAnalyzer()
  190. suggestions = analyzer.execute(df)
  191. print("\nNormalization suggestions:")
  192. print(f" Abbreviations: {len(suggestions['abbreviations'])}")
  193. print(f" Acronyms: {len(suggestions['acronyms'])}")
  194. print(f" Misspellings: {len(suggestions['misspellings'])}")
  195. print(f" Date patterns: {len(suggestions['datetime_patterns'])}")