### Setup development environment Source: https://scrubadub.readthedocs.io/en/stable/contributing.html Commands to create a virtual environment and install necessary development dependencies. ```bash mkvirtualenv scrubadub pip install -r requirements/python-dev ``` -------------------------------- ### Install libpostal on Linux Source: https://scrubadub.readthedocs.io/en/stable/addresses.html Follow these commands to install the libpostal library on a Linux system. Ensure you have curl, autoconf, automake, libtool, and pkg-config installed. ```bash $ sudo apt-get install curl autoconf automake libtool pkg-config $ git clone https://github.com/openvenues/libpostal $ cd libpostal $ ./bootstrap.sh $ ./configure --prefix=/usr/local/ $ make -j4 $ sudo make install ``` -------------------------------- ### Run the test suite Source: https://scrubadub.readthedocs.io/en/stable/contributing.html Execute the project's test suite to verify the local installation. ```bash ./tests/run.py ``` -------------------------------- ### Install Scrubadub Base Package Source: https://scrubadub.readthedocs.io/en/stable/names.html Install the base scrubadub package, which includes the TextBlob detector. ```bash $ pip install scrubadub ``` -------------------------------- ### Install Scrubadub Stanford Detector Source: https://scrubadub.readthedocs.io/en/stable/names.html Install the necessary Python package for the Stanford NER detector. ```bash $ pip install scrubadub_stanford ``` -------------------------------- ### Registering a custom PostProcessor Source: https://scrubadub.readthedocs.io/en/stable/api_scrubadub_post.html Provides an example of how to register a custom PostProcessor with the Scrubber class, allowing it to be automatically loaded. ```python scrubadub.post_processors.register_post_processor(_post_processor : Type[PostProcessor]_, _autoload : Optional[bool] = None_, _index : Optional[int] = None_) -> None: """ Register a PostProcessor for use with the `Scrubber` class. You can use `register_post_processor(NewPostProcessor)` after your post-processor definition to automatically register it with the `Scrubber` class so that it can be used to process Filth. The argument `autoload` sets if a new `Scrubber()` instance should load this `PostProcessor` by default. Parameters * **post_processor** (_PostProcessor class_) – The `PostProcessor` to register with the scrubadub post-processor configuration. * **autoload** (_bool_) – Whether to automatically load this `Detector` on `Scrubber` initialisation. * **index** (_int_) – The location/index in which this `PostProcessor` should be added. """ ``` -------------------------------- ### Install scrubadub_address Python Package Source: https://scrubadub.readthedocs.io/en/stable/addresses.html Install the scrubadub_address package using pip after libpostal has been successfully installed. This will also install pypostal and pyap as dependencies. ```bash $ pip install scrubadub_address ``` -------------------------------- ### Install scrubadub via pip Source: https://scrubadub.readthedocs.io/en/stable Command to install the scrubadub package using the pip package manager. ```bash pip install scrubadub ``` -------------------------------- ### Install Java for Stanford NER Source: https://scrubadub.readthedocs.io/en/stable/names.html Install Java 14 JRE on Debian Linux systems to use the Stanford NER detector. ```bash $ apt-get install openjdk-14-jre ``` -------------------------------- ### Install Scrubadub Spacy Detector Source: https://scrubadub.readthedocs.io/en/stable/names.html Install the Python package for the Spacy detector. Spacy v3 requires Python 3.6 to 3.8. ```bash $ pip install scrubadub_spacy ``` -------------------------------- ### UserSuppliedFilthDetector Example Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/detectors/user_supplied.html Demonstrates how to use the UserSuppliedFilthDetector to replace known names in a text. This detector requires a list of dictionaries, each specifying a 'match' string and a 'filth_type'. ```python >>> import scrubadub >>> scrubber = scrubadub.Scrubber(detector_list=[ ... scrubadub.detectors.UserSuppliedFilthDetector([ ... {'match': 'Anika', 'filth_type': 'name'}, ... {'match': 'Larry', 'filth_type': 'name'}, ... ]), ... ]) >>> scrubber.clean("Anika is my favourite employee.") '{{NAME}} is my favourite employee.' ``` -------------------------------- ### Create a Custom Filth Detector Source: https://scrubadub.readthedocs.io/en/stable/usage.html Extend Scrubadub by creating custom Filth types and Detector classes. The example shows how to define a new Filth type 'mine' and a detector 'MyDetector' that yields this custom filth. ```python import scrubadub class MyFilth(scrubadub.filth.Filth): type = 'mine' class MyDetector(scrubadub.detectors.Detector): name = 'my_detector' def iter_filth(self, text, document_name=None): # This detector always returns this same Filth no matter the input. # You should implement something better here. yield MyFilth(beg=0, end=8, text='My stuff', document_name=document_name, detector_name=self.name) scrubber = scrubadub.Scrubber() scrubber.add_detector(MyDetector) text = "My stuff can be found there." scrubber.clean(text) ``` -------------------------------- ### Implement a Custom Post-Processor Source: https://scrubadub.readthedocs.io/en/stable/usage.html Add custom post-processing logic to Scrubadub by creating a PostProcessor subclass. The PIIReplacer example modifies the 'replacement_string' for detected filth to 'PII'. ```python import scrubadub class PIIReplacer(scrubadub.post_processors.PostProcessor): name = 'pii_replacer' def process_filth(self, filth_list): for filth in filth_list: # replacement_string is what the Filth will be replaced by filth.replacement_string = 'PII' return filth_list scrubber = scrubadub.Scrubber(post_processor_list=[ PIIReplacer(), scrubadub.post_processors.PrefixSuffixReplacer(), ]) text = "contact me on (478)345-1309 or joe@example.com" scrubber.clean(text) ``` -------------------------------- ### Use Stanford NER Detector Source: https://scrubadub.readthedocs.io/en/stable/names.html Add and use the StanfordEntityDetector with a Scrubber instance. Requires `scrubadub_stanford` to be installed. ```python >>> import scrubadub, scrubadub_stanford >>> scrubber = scrubadub.Scrubber() >>> scrubber.add_detector(scrubadub_stanford.detectors.StanfordEntityDetector) >>> scrubber.clean("My name is John") 'My name is {{NAME}}' ``` -------------------------------- ### Define a Custom Detector Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/detectors/base.html Example of creating a new detector by subclassing Detector and implementing iter_filth. Requires defining a custom Filth class. ```python >>> import scrubadub >>> class MyFilth(scrubadub.filth.Filth): ... type = 'mine' >>> class MyDetector(scrubadub.detectors.Detector): ... name = 'my_fr_detector' ... def iter_filth(self, text, document_name=None): ... # This detector always returns this same Filth no matter the input. ... # You should implement something better here. ... yield MyFilth(beg=0, end=8, text='My stuff', document_name=document_name, detector_name=self.name) >>> scrubber = scrubadub.Scrubber() >>> scrubber.add_detector(MyDetector) >>> text = "My stuff can be found there." >>> scrubber.clean(text) '{{MINE}} can be found there.' ``` -------------------------------- ### Configure and Use SpacyNameDetector for German Source: https://scrubadub.readthedocs.io/en/stable/api_scrubadub_detectors.html This example demonstrates how to configure the SpacyNameDetector for the German language by setting custom noun tags and name prefixes. It then uses the configured detector with a Scrubber to clean a German text. ```python import scrubadub, scrubadub_spacy scrubadub_spacy.detectors.spacy_name_title.SpacyNameDetector.NOUN_TAGS['de'] = ['NN', 'NE', 'NNE'] scrubadub_spacy.detectors.spacy_name_title.SpacyNameDetector.NAME_PREFIXES['de'] = ['frau', 'herr'] detector = scrubadub_spacy.detectors.spacy_name_title.SpacyNameDetector(locale='de_DE', model='de_core_news_sm', include_spacy=False) scrubber = scrubadub.Scrubber(detector_list=[detector], locale='de_DE') scrubber.clean("bleib dort Frau Schmidt") ``` -------------------------------- ### Customize filth markers with hash and HTML Source: https://scrubadub.readthedocs.io/en/stable/usage.html Customize filth markers to include a hash of the filth and use custom HTML tags for replacement. This example uses `FilthReplacer` with hash options and `PrefixSuffixReplacer` for bold HTML tags. ```python >>> import scrubadub >>> scrubber = scrubadub.Scrubber(post_processor_list=[ ... scrubadub.post_processors.FilthReplacer(include_hash=True, hash_salt='example', hash_length=5), ... scrubadub.post_processors.PrefixSuffixReplacer(prefix='', suffix=''), ... ]) >>> scrubber.clean("contact me on (478)345-1309 or joe@example.com") 'contact me on PHONE-DB92D or EMAIL-028CC' ``` -------------------------------- ### Filth Class Initialization Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/filth/base.html Initializes a Filth object with optional parameters for its position, text, detector, and document. Raises a ValueError if the start position is greater than or equal to the end position. ```python def __init__(self, beg: Optional[int] = None, end: Optional[int] = None, text: Optional[str] = None, match: Optional[Match] = None, detector_name: Optional[str] = None, document_name: Optional[str] = None, replacement_string: Optional[str] = None, locale: Optional[str] = None, **kwargs): self.beg = 0 # type: int self.end = 0 # type: int self.text = '' # type: str self.match = None # type: Optional[Match] if match is not None and isinstance(match, Match): self.beg = match.start() self.end = match.end() self.text = match.string[match.start():match.end()] self.match = match if beg is not None: self.beg = beg if end is not None: self.end = end if text is not None: self.text = text self.detector_name = detector_name # type: Optional[str] self.document_name = document_name # type: Optional[str] self.replacement_string = replacement_string # type: Optional[str] self.locale = locale # type: Optional[str] if self.beg >= self.end: raise ValueError( f"Creating invalid filth (self.beg >= self.end): {self}" ) ``` -------------------------------- ### Check and Download spaCy Model Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub_spacy/detectors/spacy.html Verifies if a specified spaCy model is installed. If not, it logs a message and attempts to download it using `spacy.cli.download`. Returns true if the model is available. ```python spacy_info = spacy.info() if isinstance(spacy_info, str): raise ValueError('Unable to detect spacy models.') models = list(spacy_info.get('pipelines', spacy_info.get('models', None)).keys()) if models is None: raise ValueError('Unable to detect spacy models.') if model not in models: logger = logging.getLogger('scrubadub.detectors.spacy.SpacyEntityDetector') logger.info("Downloading spacy model {}".format(model)) spacy.cli.download(model) importlib.import_module(model) # spacy.info() doesnt update after a spacy.cli.download, so theres no point checking it models.append(model) # Always returns true, if it fails to download, spacy sys.exit()s return model in models ``` -------------------------------- ### Configure SpacyEntityDetector with a specific model Source: https://scrubadub.readthedocs.io/en/stable/usage.html Use the `Scrubber` and `SpacyEntityDetector` to identify named entities in a specific language. Ensure the `scrubadub_spacy` library is installed and the specified model is available. ```python >>> import scrubadub, scrubadub_spacy >>> scrubber = scrubadub.Scrubber(locale='fr_FR') >>> detector = scrubadub_spacy.detectors.SpacyEntityDetector(model='fr_core_news_lg') >>> scrubber.add_detector(detector) >>> text = "contacter Emmanuel Pereira au 01 81 36 78 86" >>> scrubber.clean(text) 'contacter {{NAME}} au {{PHONE}}' ``` -------------------------------- ### Registering a detector via entry points Source: https://scrubadub.readthedocs.io/en/stable/creating_detectors.html Use entry points in setup.cfg to register detectors from separate packages that are not yet imported. ```ini [options.entry_points] scrubadub_detectors = orange = scrubadub_fruit.detectors:OrangeDetector ``` -------------------------------- ### Clone the repository Source: https://scrubadub.readthedocs.io/en/stable/contributing.html Initial step to obtain a local copy of the project source code. ```bash git clone https://github.com/YOUR-USERNAME/scrubadub.git ``` -------------------------------- ### Scrubber with FilthReplacer (Basic) Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/post_processors/filth_replacer.html Demonstrates using the FilthReplacer post-processor with default settings to replace detected filth with their types (e.g., PHONE, EMAIL). ```python >>> import scrubadub >>> scrubber = scrubadub.Scrubber(post_processor_list=[ ... scrubadub.post_processors.FilthReplacer(), ... ]) >>> scrubber.clean("Contact me at 522-368-8530 or hernandezjenna@example.com") 'Contact me at PHONE or EMAIL' ``` -------------------------------- ### Configure and use DateOfBirthDetector Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/detectors/date_of_birth.html Demonstrates how to customize the minimum age for detection and initialize the scrubber with the detector. ```python >>> import scrubadub, scrubadub.detectors.date_of_birth >>> DateOfBirthFilth.min_age_years = 12 >>> scrubber = scrubadub.Scrubber(detector_list=[ ... scrubadub.detectors.date_of_birth.DateOfBirthDetector(), ... ]) >>> scrubber.clean("I was born on 10-Nov-2008.") 'I was born {{DATE_OF_BIRTH}}.' ``` -------------------------------- ### Configure and use SpacyEntityDetector Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub_spacy/detectors/spacy.html Demonstrates how to define a custom Filth class, map it to a spaCy entity label, and use the detector within a Scrubber instance. ```python >>> import scrubadub, scrubadub_spacy >>> class MoneyFilth(scrubadub.filth.Filth): ... type = 'money' >>> scrubadub_spacy.detectors.spacy.SpacyEntityDetector.filth_cls_map['MONEY'] = MoneyFilth >>> detector = scrubadub_spacy.detectors.spacy.SpacyEntityDetector(named_entities=['MONEY']) >>> scrubber = scrubadub.Scrubber(detector_list=[detector]) >>> scrubber.clean("You owe me 12 dollars man!") 'You owe me {{MONEY}} man!' ``` -------------------------------- ### GET /detectors/date_of_birth/supported_locale Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/detectors/date_of_birth.html Checks if the DateOfBirthDetector supports a specific locale. ```APIDOC ## GET /detectors/date_of_birth/supported_locale ### Description Returns true if the DateOfBirthDetector supports the given locale. ### Method GET ### Endpoint /detectors/date_of_birth/supported_locale ### Parameters #### Query Parameters - **locale** (string) - Required - The locale of the documents in the format: 2 letter lower-case language code eg "en", "es". ### Response #### Success Response (200) - **supported** (boolean) - True if the locale is supported, otherwise False. ``` -------------------------------- ### Add Prefix and Suffix to Filth Replacement Source: https://scrubadub.readthedocs.io/en/stable/api_scrubadub_post.html Demonstrates initializing a Scrubber with a FilthReplacer, implying that subsequent replacements might be affected by default prefix/suffix settings if PrefixSuffixReplacer were also included or if FilthReplacer had such options. ```python >>> import scrubadub >>> scrubber = scrubadub.Scrubber(post_processor_list=[ ... scrubadub.post_processors.FilthReplacer(), ... ]) ``` -------------------------------- ### Use Spacy NER Detector Source: https://scrubadub.readthedocs.io/en/stable/names.html Add and use the SpacyEntityDetector with a Scrubber instance. Requires `scrubadub_spacy` to be installed. ```python >>> import scrubadub, scrubadub_spacy >>> scrubber = scrubadub.Scrubber() >>> scrubber.add_detector(scrubadub_spacy.detectors.SpacyEntityDetector) >>> scrubber.clean("My name is John") 'My name is {{NAME}}' ``` -------------------------------- ### FilthReplacer Initialization Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/post_processors/filth_replacer.html Initializes the FilthReplacer post-processor with configuration options for how filth should be replaced in the text. ```APIDOC ## FilthReplacer.__init__ ### Description Initializes the FilthReplacer instance with specific formatting rules for replacement tokens. ### Parameters #### Request Body - **include_type** (bool) - Optional - Whether to include the filth type in the replacement label. Default: True. - **include_count** (bool) - Optional - Whether to include a unique count for each piece of filth. Default: False. - **include_hash** (bool) - Optional - Whether to include a hash of the filth content. Default: False. - **uppercase** (bool) - Optional - Whether to make the label uppercase. Default: True. - **separator** (str) - Optional - Separator used for merged filth labels. Default: '+'. - **hash_length** (int) - Optional - Length of the hexadecimal hash. Default: 16. - **hash_salt** (str/bytes) - Optional - Salt used for the hashing process. ``` -------------------------------- ### Scrubber with FilthReplacer (Include Hash) Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/post_processors/filth_replacer.html Demonstrates using the FilthReplacer post-processor with hashing enabled. It includes the filth type and an 8-character hash of the filth, using a specified salt. ```python >>> scrubber = scrubadub.Scrubber(post_processor_list=[ ... scrubadub.post_processors.FilthReplacer(include_hash=True, hash_salt='example', hash_length=8), ... ]) >>> scrubber.clean("Contact me at 522-368-8530 or hernandezjenna@example.com") 'Contact me at PHONE-7358BF44 or EMAIL-AC0B8AC3' ``` -------------------------------- ### Check spaCy Version Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub_spacy/detectors/spacy.html Ensures that the installed spaCy version is v3. Raises an ImportError if the version is incorrect or cannot be detected. ```python spacy_version = spacy.__version__ # spacy_info.get('spaCy version', spacy_info.get('spacy_version', None)) if spacy_version is None: raise ImportError('Spacy v3 needs to be installed. Unable to detect spacy version.') try: spacy_major = int(spacy_version.split('.')[0]) except Exception: raise ImportError('Spacy v3 needs to be installed. Spacy version {} is unknown.'.format(spacy_version)) if spacy_major != 3: raise ImportError('Spacy v3 needs to be installed. Detected version {}.'.format(spacy_version)) return True ``` -------------------------------- ### Scrub Document and Generate Classification Report Source: https://scrubadub.readthedocs.io/en/stable/accuracy.html Initializes a Scrubber, adds a TaggedEvaluationFilthDetector with known PII, iterates through the document to find filth, and then generates a classification report. This demonstrates how to measure detector efficiency. ```python scrubber = scrubadub.Scrubber() scrubber.add_detector(scrubadub.detectors.TaggedEvaluationFilthDetector(known_filth_items=tagged_pii)) filth_list = list(scrubber.iter_filth(document)) print(scrubadub.comparison.get_filth_classification_report(filth_list)) ``` -------------------------------- ### Get Counts from FilthTypePositions Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/comparison.html Merges positions and then calculates the counts of detected and tagged filth items, returning the results as a pandas DataFrame. ```python def get_counts(self) -> pd.DataFrame: self.merge_positions() data_list = [] # type: List[Dict[Tuple[str, ...], int]] for position in self.positions: row = { detected_name: 1 for detected_name in position.detected } row.update({ detected_name: 1 for detected_name in position.tagged }) data_list.append(row) dataframe = pd.DataFrame(data_list).fillna(0).astype(int) dataframe.columns = pd.MultiIndex.from_tuples( dataframe.columns.values.tolist(), names=self.column_names, ) return dataframe ``` -------------------------------- ### Add prefix and suffix to replacements Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/post_processors/prefix_suffix.html Use PrefixSuffixReplacer to wrap replacement strings with specified prefixes and suffixes. This example demonstrates wrapping with '{{' and '}}'. ```python import scrubadub scrubber = scrubadub.Scrubber(post_processor_list=[ scrubadub.post_processors.FilthReplacer(), scrubadub.post_processors.PrefixSuffixReplacer(prefix='{{', suffix='}}'), ]) scrubber.clean("Contact me at 522-368-8530 or hernandezjenna@example.com") ``` -------------------------------- ### Initialise FilthReplacer Post-Processor Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/post_processors/filth_replacer.html Initialises the FilthReplacer with options to include type, count, hash, case, separator, and hashing parameters. The hash salt is encoded to bytes if provided as a string, otherwise a random salt is generated. ```python def __init__(self, include_type: bool = True, include_count: bool = False, include_hash: bool = False, uppercase: bool = True, separator: Optional[str] = None, hash_length: Optional[int] = None, hash_salt: Optional[Union[str, bytes]] = None, **kwargs): """Initialise the FilthReplacer. :param include_type: :type include_type: bool, default True :param include_count: :type include_count: bool, default False :param include_hash: :type include_hash: bool, default False :param uppercase: Make the label uppercase :type uppercase: bool, default True :param separator: Used to separate labels if a merged filth is being replaced :type separator: Optional[str], default None :param hash_length: The length of the hexadecimal hash :type hash_length: Optional[int], default None :param hash_salt: The salt used in the hashing process :type hash_salt: Optional[Union[str, bytes]], default None """ super(FilthReplacer, self).__init__(**kwargs) self.include_type = include_type self.include_count = include_count self.include_hash = include_hash self.uppercase = uppercase self.separator = separator or '+' self.hash_length = hash_length or 16 if isinstance(hash_salt, str): self.hash_salt = hash_salt.encode('utf8') # type: bytes else: self.hash_salt = os.urandom(128) ``` -------------------------------- ### Scrubber with FilthReplacer (Include Count) Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/post_processors/filth_replacer.html Demonstrates using the FilthReplacer post-processor with the count option enabled. This replaces identical filth instances with the same sequential number. ```python >>> scrubber = scrubadub.Scrubber(post_processor_list=[ ... scrubadub.post_processors.FilthReplacer(include_count=True), ... ]) >>> scrubber.clean("Contact me at taylordaniel@example.com or hernandezjenna@example.com, " ... "but taylordaniel@example.com is probably better.") 'Contact me at EMAIL-0 or EMAIL-1, but EMAIL-0 is probably better.' ``` -------------------------------- ### Sort Filth Objects Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/scrubbers.html Sorts a sequence of Filth objects by document name, start position, and length. This is a prerequisite for merging and concatenation operations. ```python @staticmethod def _sort_filths(filth_list: Sequence[Filth]) -> List[Filth]: """Sorts a list of filths, needed before merging and concatenating""" # Sort by start position. If two filths start in the same place then # return the longer one first filth_list = list(filth_list) filth_list.sort(key=lambda f: ( str(getattr(f, 'document_name', None) if hasattr(f, 'document_name') else ''), f.beg, -f.end )) return filth_list ``` -------------------------------- ### Initialize Scrubber with Custom Detectors and Post-Processors Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/scrubbers.html Initializes a Scrubber instance, optionally specifying custom lists of detectors and post-processors. If not provided, it defaults to loading all autoloading detectors and post-processors that support the specified locale. ```python import warnings from typing import Optional, Sequence, Generator, Dict, Type, Union, List from . import detectors from . import post_processors from .detectors import Detector from .post_processors import PostProcessor from .filth import Filth [docs]class Scrubber: """The Scrubber class is used to clean personal information out of dirty dirty text. It manages a set of ``Detector``'s that are each responsible for identifying ``Filth``. ``PostProcessor`` objects are used to alter the found Filth. This could be to replace the Filth with a hash or token. """ [docs] def __init__(self, detector_list: Optional[Sequence[Union[Type[Detector], Detector, str]]] = None, post_processor_list: Optional[Sequence[Union[Type[PostProcessor], PostProcessor, str]]] = None, locale: Optional[str] = None): """Create a ``Scrubber`` object. :param detector_list: The list of detectors to use in this scrubber. :type detector_list: Optional[Sequence[Union[Type[Detector], Detector, str]]] :param post_processor_list: The locale that the phone number should adhere to. :type post_processor_list: Optional[Sequence[Union[Type[Detector], Detector, str]]] :param locale: The locale of the documents in the format: 2 letter lower-case language code followed by an underscore and the two letter upper-case country code, eg "en_GB" or "de_CH". :type locale: str, optional """ super().__init__() # instantiate all of the detectors which, by default, uses all of the # detectors that are in the detectors.types dictionary self._detectors = {} # type: Dict[str, Detector] self._post_processors = [] # type: List[PostProcessor] if locale is None: locale = 'en_US' self._locale = locale # type: str if detector_list is None: # First we gather all detectors that should automatically load detector_list = [ detector for detector in detectors.catalogue.detector_catalogue.get_all().values() if detector.autoload and ( # Then we filter out ones that don't support the current locale not hasattr(detector, 'supported_locale') or ( hasattr(detector, 'supported_locale') and detector.supported_locale(locale) # type: ignore ) ) ] for detector in detector_list: self.add_detector(detector, warn=True) if post_processor_list is None: post_processor_list = [ post_processor for post_processor in sorted( post_processors.catalogue.post_processor_catalogue.get_all().values(), key=lambda pp: pp.index, ) if post_processor.autoload ] for post_processor in post_processor_list: self.add_post_processor(post_processor) ``` -------------------------------- ### Iterate Filth Documents with Scrubber Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub.html Initialises a Scrubber with a specified locale and iterates over documents to find filth. Requires a list of documents and optional keyword arguments. ```python scrubber = Scrubber(locale=locale) return list(scrubber.iter_filth_documents(documents, **kwargs)) ``` -------------------------------- ### Find text between delimiters Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/detectors/tagged.html Yields filth found between start and end substrings, supporting case-insensitivity, whitespace normalization, and word boundary constraints. ```python ) -> Generator[Filth, None, None]: """Yield filth for text between (and including) substr_start and substr_end, but only if the text between the two is less than limit characters. """ text_orig = copy.copy(text) if ignore_case: text = text.lower() substr_start = substr_start.lower() substr_end = substr_end.lower() if ignore_whitespace: # We change any white space in the original with "\s+" that has to match one or more whitespace chars substr_start = '\\s+'.join([re.escape(token) for token in substr_start.split()]) substr_end = '\\s+'.join([re.escape(token) for token in substr_end.split()]) else: substr_start = re.escape(substr_start) substr_end = re.escape(substr_end) if ignore_partial_word_matches: substr_start = f"\\b{substr_start}\\b" substr_end = f"\\b{substr_end}\\b" matches = re.finditer(f"({substr_start})(.{{0,{limit}}})({substr_end})", text, re.MULTILINE | re.DOTALL) for match in matches: yield self.create_filth( match.span()[0], match.span()[1], text_orig[match.span()[0]:match.span()[1]], comparison_type=comparison_type, detector_name=self.name, document_name=document_name, locale=self.locale, ) ``` -------------------------------- ### Manage detectors by instance, class, or name Source: https://scrubadub.readthedocs.io/en/stable/usage.html Illustrates adding and removing detectors from a `Scrubber` instance using various methods: detector object, detector class, or detector name string. It also shows how to handle duplicate detector names. ```python >>> import scrubadub >>> # Create a detector with a name 'example_email' >>> detector = scrubadub.detectors.EmailDetector(name='example_email') >>> # Detectors can be added on Scrubber initialisation >>> scrubber = scrubadub.Scrubber(detector_list=[detector, ]) >>> # add/remove a detector with a Detector instance >>> scrubber.remove_detector(detector) >>> # adds/removes detector with the default name 'email' using the class >>> scrubber.add_detector(scrubadub.detectors.EmailDetector) >>> scrubber.remove_detector(scrubadub.detectors.EmailDetector) >>> # Adds/removes the scrubadub.detectors.EmailDetector detector, see the `name` attribute of the detector >>> scrubber.add_detector('email') >>> scrubber.remove_detector('email') >>> # Add back the original instance >>> scrubber.add_detector(detector) >>> # KeyError is thrown if two detectors have the same name >>> scrubber.add_detector(detector) Traceback (most recent call last): ... KeyError: 'can not add Detector "example_email" to this Scrubber, this name is already in use. Try removing it first.' ``` -------------------------------- ### Define a Regex Detector Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/detectors/base.html Example of creating a new detector using RegexDetector for pattern matching. Requires setting filth_cls and defining a regex attribute. ```python >>> import re, scrubadub >>> class NewUrlDetector(scrubadub.detectors.RegexDetector): ... name = 'new_url_detector' ... filth_cls = scrubadub.filth.url.UrlFilth ... regex = re.compile(r'https.*$', re.IGNORECASE) >>> scrubber = scrubadub.Scrubber(detector_list=[NewUrlDetector()]) >>> text = u"This url will be found https://example.com" >>> scrubber.clean(text) 'This url will be found {{URL}}' ``` -------------------------------- ### Initialize Scrubber with UserSuppliedFilthDetector Source: https://scrubadub.readthedocs.io/en/stable/api_scrubadub_detectors.html Use this detector to find known filth in text by providing a list of dictionaries. Each dictionary must contain 'match' and 'filth_type'. This detector is not enabled by default and must be added to the Scrubber. ```python import scrubadub scrubber = scrubadub.Scrubber(detector_list=[ scrubadub.detectors.UserSuppliedFilthDetector([ {'match': 'Anika', 'filth_type': 'name'}, {'match': 'Larry', 'filth_type': 'name'}, ]), ]) scrubber.clean("Anika is my favourite employee.") ``` -------------------------------- ### PrefixSuffixReplacer Initialization Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/post_processors/prefix_suffix.html Initializes the PrefixSuffixReplacer with optional prefix and suffix strings to be applied to filth replacements. ```APIDOC ## Class: PrefixSuffixReplacer ### Description Initializes a post-processor that adds a prefix and/or suffix to the Filth's replacement string. ### Parameters #### Constructor Parameters - **prefix** (str) - Optional - The string to prepend to the replacement. Defaults to '{{'. - **suffix** (str) - Optional - The string to append to the replacement. Defaults to '}}'. - **name** (str) - Optional - A custom name for the post-processor. ### Usage Example ```python from scrubadub.post_processors import PrefixSuffixReplacer processor = PrefixSuffixReplacer(prefix='', suffix='') ``` ``` -------------------------------- ### Import necessary modules for scrubadub.comparison Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/comparison.html Imports required modules for the scrubadub.comparison functionality, including regular expressions, copying, random data generation, itertools, Faker for synthetic data, and specific scrubadub modules like filth and detectors. ```python import re import copy import random import itertools from faker import Faker from . import filth as filth_module from .filth import Filth from .detectors.tagged import KnownFilthItem from typing import List, Dict, Union, Optional, Tuple, Callable, Iterable, Type, Set import numpy as np import pandas as pd import sklearn.metrics ``` -------------------------------- ### Example Tagged PII Data Format Source: https://scrubadub.readthedocs.io/en/stable/accuracy.html This is the expected format for tagged PII data when using the TaggedEvaluationFilthDetector. Each item is a dictionary specifying the text to match and its filth type. ```json [ {"match": "wwashington@reed-ryan.org", "filth_type": "email"}, {"match": "https://www.wong.com/", "filth_type": "url"} ] ``` -------------------------------- ### Initialize Detector with Known Filth Items Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/detectors/tagged.html Initializes a detector with a list of known filth items. Each item must be a dictionary containing 'match' and 'filth_type' keys. It validates input types and formats, and removes duplicate entries. ```python super().__init__(**kwargs) for item in known_filth_items: if 'match' not in item or 'filth_type' not in item: raise KeyError("Each known filth item (dict) needs both keys 'match' and 'filth_type'.") if not isinstance(item['match'], str): raise ValueError("The value of 'match' in each KnownItem should be a string. " "Current value: " + item['match'].__repr__()) if not isinstance(item['filth_type'], str): raise ValueError("The value of 'filth_type' in each KnownItem should be a string. " "Current value: " + item['filth_type'].__repr__()) item['match'] = item['match'].strip() item['filth_type'] = item['filth_type'].strip() if 'match_end' in item: if not isinstance(item['match_end'], str): raise ValueError("The value of 'match_end' in each KnownItem should be a string. " "Current value: " + item['match_end'].__repr__()) item['match_end'] = item['match_end'].strip() for key in item.keys(): if key not in ['match', 'match_end', 'limit', 'filth_type', 'ignore_case', 'ignore_whitespace', 'ignore_partial_word_matches']: raise KeyError("Unexpected key '{}' in the known filth item.".format(key)) self._known_filth_items = self.dedup_dicts(known_filth_items) ``` -------------------------------- ### PrefixSuffixReplacer Initialization Source: https://scrubadub.readthedocs.io/en/stable/api_scrubadub_post.html Initializes the PrefixSuffixReplacer to wrap Filth replacement strings with specific prefixes and suffixes. ```APIDOC ## PrefixSuffixReplacer.__init__ ### Description Adds a prefix and/or suffix to the Filth’s replacement string. ### Parameters #### Request Body - **prefix** (str) - Optional - The string to prepend to the replacement. - **suffix** (str) - Optional - The string to append to the replacement. - **name** (str) - Optional - The name of the post-processor. ``` -------------------------------- ### Create a Custom RegexDetector Source: https://scrubadub.readthedocs.io/en/stable/api_scrubadub_detectors.html Extend RegexDetector to create a new detector for specific patterns like URLs. Ensure to set the filth_cls and regex attributes. This example demonstrates detecting URLs with a case-insensitive regex. ```python import re, scrubadub >>> class NewUrlDetector(scrubadub.detectors.RegexDetector): ... name = 'new_url_detector' ... filth_cls = scrubadub.filth.url.UrlFilth ... regex = re.compile(r'https.*$', re.IGNORECASE) >>> scrubber = scrubadub.Scrubber(detector_list=[NewUrlDetector()]) >>> text = u"This url will be found https://example.com" >>> scrubber.clean(text) 'This url will be found {{URL}}' ``` -------------------------------- ### UserSuppliedFilthDetector Configuration Options Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/detectors/user_supplied.html Illustrates different ways to configure the UserSuppliedFilthDetector with various options. These options control how the detector searches for and identifies filth in the text. ```python >>> {'match': 'aaa', 'filth_type': 'name'} ``` ```python >>> {'match': 'aaa', 'match_end': 'zzz', 'filth_type': 'name'} ``` ```python >>> {'match': '012345', 'filth_type': 'phone', 'ignore_partial_word_matches': True} ``` -------------------------------- ### Add an optional detector Source: https://scrubadub.readthedocs.io/en/stable/usage.html Demonstrates how to instantiate a Scrubber and register an optional detector to clean specific text patterns. ```python >>> import scrubadub >>> scrubber = scrubadub.Scrubber() >>> scrubber.add_detector(scrubadub.detectors.DateOfBirthDetector) >>> scrubber.clean("I was born on 5th December 1983") 'I was born {{DATE_OF_BIRTH}}' ``` -------------------------------- ### Custom Filth Validation with RegexDetector Source: https://scrubadub.readthedocs.io/en/stable/creating_detectors.html Use Filth.is_valid() for complex validation beyond simple regex matching. This example defines a VisaCardNumberFilth class with Luhn algorithm validation and a VisaCardNumberDetector to find potential Visa card numbers. ```python import scrubadub, string, stdnum.luhn, re class VisaCardNumberFilth(scrubadub.filth.Filth): type = 'visa_card_number' def is_valid(self) -> bool: return stdnum.luhn.is_valid(self.text) class VisaCardNumberDetector(scrubadub.detectors.RegexDetector): name = 'visa' filth_cls = VisaCardNumberFilth autoload = False regex = re.compile(r"\b4[0-9]{12}(?:[0-9]{3})?\b") scrubber = scrubadub.Scrubber(detector_list=[VisaCardNumberDetector]) scrubber.clean('My reservation ID is 4343457563243982, please charge my room to the card 4829675671235307') ``` -------------------------------- ### Get Filth Classification Report Function Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/comparison.html This function generates a filth classification report from a list of Filth objects. It accepts parameters to control detector combination and document grouping. This is a high-level function for generating reports based on detected filth. ```python [docs] def get_filth_classification_report( filth_list: List[Filth], combine_detectors: bool = False, groupby_documents: bool = False, ``` -------------------------------- ### Get Counts Method with Missing Expansion Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/comparison.html This method calculates and returns the counts of filth items, optionally expanding the DataFrame to include all possible combinations of filth types and detectors. Use `expand_missing=True` to ensure a consistent DataFrame structure, which is useful for comparisons or further analysis. ```python def get_counts(self, expand_missing: bool = False) -> pd.DataFrame: if len(self.types) == 0: return pd.DataFrame() df_list = [] # type: List[pd.DataFrame] running_rows = 0 for positioniser in self.types.values(): pos_df = positioniser.get_counts() if expand_missing: pos_df = self.expand_missing(pos_df) df_list.append(pos_df) df_list[-1].index += running_rows running_rows += max(df_list[-1].index) + 1 return pd.concat(df_list).fillna(0).astype(int) ``` -------------------------------- ### Generate a filth classification report Source: https://scrubadub.readthedocs.io/en/stable/accuracy.html Demonstrates creating a fake document, running a detector, and generating a classification report to evaluate performance. ```python >>> import scrubadub, scrubadub.comparison, scrubadub_spacy >>> document, known_filth_items = scrubadub.comparison.make_fake_document(paragraphs=1, seed=3, filth_types=['name']) >>> scrubber = scrubadub.Scrubber(detector_list=[scrubadub_spacy.detectors.SpacyNameDetector()]) >>> scrubber.add_detector(scrubadub.detectors.TaggedEvaluationFilthDetector(known_filth_items=known_filth_items)) >>> filth_list = list(scrubber.iter_filth(document)) >>> for filth in filth_list: print(filth) , ]> , ]> , ]> , ]> >>> print(scrubadub.comparison.get_filth_classification_report(filth_list)) filth detector locale precision recall f1-score support name spacy_name en_US 0.80 1.00 0.89 4 micro avg 0.80 1.00 0.89 4 macro avg 0.80 1.00 0.89 4 weighted avg 0.80 1.00 0.89 4 ``` -------------------------------- ### Configure Locale for Scrubadub Source: https://scrubadub.readthedocs.io/en/stable/localization.html Demonstrates setting the locale parameter in clean functions and the Scrubber class to handle region-specific phone number formats. ```python >>> import scrubadub >>> scrubadub.clean('My US number is 731-938-1630', locale='en_US') 'My US number is {{PHONE}}' >>> scrubadub.clean('My US number is 731-938-1630', locale='en_GB') 'My US number is 731-938-1630' >>> scrubadub.clean('My GB number is 0121 496 0112', locale='en_GB') 'My GB number is {{PHONE}}' >>> scrubadub.clean('My GB number is 0121 496 0112', locale='en_US') 'My GB number is 0121 496 0112' >>> scrubber = scrubadub.Scrubber(locale='de_DE') >>> scrubber.clean('Meine Telefonnummer ist 05086 63680') 'Meine Telefonnummer ist {{PHONE}}' ``` -------------------------------- ### Initialize Detector with Custom Name Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/detectors/base.html Demonstrates how the Detector class is initialized, including handling custom names and locales. A warning is issued if the detector name is not explicitly set and derived from filth_cls.type. ```python if getattr(self, 'name', 'detector') == 'detector' and getattr(self, 'filth_cls', None) is not None: if getattr(self.filth_cls, 'type', None) is not None and type(self) != Detector: self.name = self.filth_cls.type warnings.warn( "Setting the detector name from the filth_cls.type is depreciated, please declare an explicit name" "attribute on the class.", DeprecationWarning ) if name is not None: self.name = name self.locale = locale self.language, self.region = self.locale_split(locale) if hasattr(self, 'supported_locale'): if not self.supported_locale(locale=locale): # type: ignore warnings.warn("Detector {} does not support the locale '{}'வுகளை.".format(self.name, locale)) ``` -------------------------------- ### Add an external detector Source: https://scrubadub.readthedocs.io/en/stable/usage.html Shows the process of importing an external detector package and adding it to the Scrubber instance. ```python >>> import scrubadub, scrubadub_spacy >>> scrubber = scrubadub.Scrubber() >>> scrubber.add_detector(scrubadub_spacy.detectors.SpacyEntityDetector) >>> scrubber.clean("My name is John") 'My name is {{NAME}}' ``` -------------------------------- ### Add Detectors to Scrubber Source: https://scrubadub.readthedocs.io/en/stable/api_scrubadub.html Demonstrates adding detectors to a Scrubber instance using class types, detector names, or pre-initialized detector instances. ```python >>> import scrubadub >>> scrubber = scrubadub.Scrubber(detector_list=[]) >>> scrubber.add_detector(scrubadub.detectors.CreditCardDetector) >>> scrubber.add_detector('skype') >>> detector = scrubadub.detectors.DateOfBirthDetector(require_context=False) >>> scrubber.add_detector(detector) ``` -------------------------------- ### Detector Class Initialization Source: https://scrubadub.readthedocs.io/en/stable/_modules/scrubadub/detectors/base.html Initializes a new detector instance with a specific name and locale. ```APIDOC ## __init__ ### Description Initializes the Detector instance with an optional name and locale. ### Parameters #### Path Parameters - **name** (str) - Optional - Overrides the default name of the Detector. - **locale** (str) - Optional - The locale of the documents (e.g., 'en_US', 'en_GB'). ### Request Example { "name": "my_custom_detector", "locale": "en_US" } ```