Repository: JohnSnowLabs/langtest (language: python)

Issue:
Feature/dyslexia swap feature
For issue [344](https://github.com/JohnSnowLabs/nlptest/issues/344)

-I've added Google style docstrings to my code. [here](https://github.com/ArkajyotiChakraborty/nlptest/blob/29b9871a269b61628e2d4e38abd46d7cd1888fe7/nlptest/transform/robustness.py#L700)
-I have added tests to cover my changes. [here](https://github.com/ArkajyotiChakraborty/nlptest/blob/29b9871a269b61628e2d4e38abd46d7cd1888fe7/tests/test_robustness.py#L139)



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/requirements.txt b/.github/workflows/requirements.txt
index 969592473..4a6a8f5bf 100644
--- a/.github/workflows/requirements.txt
+++ b/.github/workflows/requirements.txt
@@ -14,4 +14,4 @@ openai
 langchain
 evaluate
 rouge_score
-typing-extensions < 4.6.0
\ No newline at end of file
+typing-extensions < 4.6.0


Test patch:
diff --git a/nlptest/transform/robustness.py b/nlptest/transform/robustness.py
index b1c96115e..6dfc19a5b 100644
--- a/nlptest/transform/robustness.py
+++ b/nlptest/transform/robustness.py
@@ -1,10 +1,11 @@
+import spacy
 import asyncio
 import random
 import re
 from abc import ABC, abstractmethod
 from typing import Dict, List, Optional
 from nlptest.modelhandler.modelhandler import ModelFactory
-from .utils import (CONTRACTION_MAP, TYPO_FREQUENCY, default_user_prompt ,ocr_typo_dict,abbreviation_dict,Slang_Nouns, Slang_Adverbs, Slang_Adjectives)
+from .utils import (CONTRACTION_MAP, TYPO_FREQUENCY, default_user_prompt ,ocr_typo_dict,abbreviation_dict,Slang_Nouns, Slang_Adverbs, Slang_Adjectives, dyslexia_map)
 from ..utils.custom_types import Sample, Span, Transformation
 from ..utils.number_to_word import engine
 from typing import List
@@ -597,6 +598,51 @@ def search_contraction(text):
         return sample_list
 
 
+class DyslexiaWordSwap(BaseRobustness):
+    alias_name = "dyslexia_word_swap"
+    
+    @staticmethod
+    def transform(sample_list: List[Sample]) -> List[Sample]:
+        """Converts the string by changing some similar words from the dictonary(dyslexia_map) and outputs a string. 
+           Args: sample_list: List of sentences to process.
+
+           Returns: Returns a converted string like words like write will get converted to right. 
+        """
+        def dyslexia_swap(text):
+            perturbed_text = text
+            transformations = [] 
+
+            for orig, perturbed in dyslexia_map.items():
+                pattern = r"(?i)\b" + re.escape(orig) + r"\b"
+                perturbed_text = re.sub(pattern, "__TEMP__" + perturbed, perturbed_text)
+                matches = re.finditer(pattern, text)
+                for match in matches:
+                    start = match.start()
+                    end = match.end()
+                    token = text[start:end]
+                    if perturbed != token:
+                        transformations.append(
+                            Transformation(
+                                original_span=Span(start=start, end=end, word=token),
+                                new_span=Span(start=start, end=start + len(perturbed), word=perturbed),
+                                ignore=False
+                            )
+                        ) 
+            perturbed_text = perturbed_text.replace( "__TEMP__", "")
+            return perturbed_text, transformations
+
+        for idx, sample in enumerate(sample_list):
+            if isinstance(sample, str):
+                sample_list[idx],_= dyslexia_swap(sample)
+            else:
+                sample.test_case, transformations = dyslexia_swap(sample.original)
+                if sample.task in ("ner", "text-classification"):
+                    sample.transformations = transformations
+                sample.category = "robustness"
+        
+        return sample_list
+
+
 class NumberToWord(BaseRobustness):
     alias_name = "number_to_word"
     infEng = engine()
@@ -919,6 +965,4 @@ def slangify_typo(text):
                     sample.transformations = transformations
                 sample.category = "robustness"
 
-        return sample_list
-    
-
+        return sample_list
\ No newline at end of file
diff --git a/nlptest/transform/utils.py b/nlptest/transform/utils.py
index 642e6cb6d..2e1895e77 100644
--- a/nlptest/transform/utils.py
+++ b/nlptest/transform/utils.py
@@ -6949,6 +6949,75 @@
                             "Kosovo", "South Africa"]}
 
 
+#Dyslexia Map Words Dictionary
+dyslexia_map = {
+    'of': 'off',
+    'lose': 'loose',
+    'than': 'then',
+    'though': 'through',
+    'well': 'will',
+    'air': 'heir',
+    'would': 'might',
+    'might': 'would',
+    'heir': 'air',
+    'your': "you're",
+    'write': 'right',
+    'four': 'for',
+    'here': 'hear',
+    'hi': 'high',
+    'hour': 'our',
+    'no': 'know',
+    'height': 'knight',
+    'knot': 'not',
+    'see': 'sea',
+    'since': 'cents',
+    'some': 'sum',
+    'ate': 'eight',
+    'mate': 'malt',
+    'merry': 'marry',
+    'meet': 'meat',
+    'son': 'sun',
+    'sight': 'site',
+    'one': 'won',
+    'wait': 'weight',
+    'wear': 'where',
+    'bye': 'by',
+    'by': 'bye',
+    'to': 'too',
+    'there': 'their',
+    'off': 'of',
+    'loose': 'lose',
+    'then': 'than',
+    'through': 'though',
+    'will': 'well',
+    'hair': 'air',
+    'could': 'would',
+    "you're": 'your',
+    'right': 'write',
+    'for': 'four',
+    'hear': 'here',
+    'high': 'hi',
+    'our': 'hour',
+    'know': 'no',
+    'knight': 'height',
+    'not': 'knot',
+    'sea': 'see',
+    'cents': 'since',
+    'sum': 'some',
+    'eight': 'ate',
+    'malt': 'mate',
+    'marry': 'merry',
+    'meat': 'meet',
+    'sun': 'son',
+    'site': 'sight',
+    'won': 'one',
+    'weight': 'wait',
+    'where': 'wear',
+    'buy': 'by',
+    'too': 'to',
+    'their': 'there'
+}
+
 def get_substitution_names(values_list: List[List[str]]) -> List[str]:
     """ Helper function to get list of substitution names
 
diff --git a/tests/test_robustness.py b/tests/test_robustness.py
index a4f978ab6..1c80f37f1 100644
--- a/tests/test_robustness.py
+++ b/tests/test_robustness.py
@@ -35,17 +35,21 @@ def setUp(self) -> None:
             SequenceClassificationSample(original="He lives in the USA."),
             SequenceClassificationSample(original="He lives in the USA and his cat is black.")
         ]
+        self.dyslexia_sentences = [
+            SequenceClassificationSample(original="I live in London of United Kingdom."),
+            SequenceClassificationSample(original="I would like that.")
+        ]
         self.ocr_sentences = [
-            SequenceClassificationSample(original="This organization's art can win tough acts."),    
-            SequenceClassificationSample(original="Anyone can join our community garden.")                                                                                        
+            SequenceClassificationSample(original="This organization's art can win tough acts."),
+            SequenceClassificationSample(original="Anyone can join our community garden.")
         ]
         self.speech_to_text_sentences = [
-            SequenceClassificationSample(original="I picked up a stone and attempted to skim it across the water."),    
-            SequenceClassificationSample(original="This organization's art can win tough acts.")                                                                                        
+            SequenceClassificationSample(original="I picked up a stone and attempted to skim it across the water."),
+            SequenceClassificationSample(original="This organization's art can win tough acts.")
         ]
         self.add_slangify = [
-            SequenceClassificationSample(original="I picked up a stone and attempted to skim it across the water."),    
-            SequenceClassificationSample(original="It was totally excellent but useless bet.")                                                                                        
+            SequenceClassificationSample(original="I picked up a stone and attempted to skim it across the water."),
+            SequenceClassificationSample(original="It was totally excellent but useless bet.")
         ]
         self.labels = [
             ["O", "O", "O", "B-LOC", "B-COUN", "I-COUN", "O", "B-DATE"],
@@ -150,6 +154,13 @@ def test_add_contraction(self) -> None:
             [len(sample.transformations) for sample in transformed_samples],
             [0, 1]
         )
+
+    def test_dyslexia_swap(self) -> None:
+        """"""
+        transformed_samples = DyslexiaWordSwap.transform(self.dyslexia_sentences)
+        self.assertIsInstance(transformed_samples, list)
+        for sample in transformed_samples:
+            self.assertTrue(sample.test_case != sample.original or sample.test_case)
         
     def test_number_to_word(self) -> None:
         """"""
@@ -188,4 +199,4 @@ def test_add_slangify_typo(self) -> None:
         for sample in transformed_samples:
             self.assertNotEqual(sample.test_case, sample.original)
 
-            
\ No newline at end of file
+