Welcome to Melusine’s documentation !

_images/melusine.png

This package aims to be the reference in the processing and modelization of French email data. The project has been developed by MAIF.

Company

GitHub Account

Website

_images/logoMAIFnew.png

MAIF GitHub

MAIF

Melusine

_images/melusine.png https://img.shields.io/pypi/v/melusine.svg https://travis-ci.org/MAIF/melusine.svg?branch=master Documentation Status

Overview

Melusine is a high-level Python library for emails classification and feature extraction, written in Python and capable of running on top of Scikit-Learn, Keras or Tensorflow. It was developed with a focus on emails written in french.

Use Melusine if you need a library which :
  • Supports both convolutional networks and recurrent networks, as well as combinations of the two.

  • Runs seamlessly on CPU and GPU.

Melusine is compatible with Python 3.6 (<=2.3.2), Python 3.7 and Python 3.8.

Release Notes

Release 2.3.5

Bug fix:
  • PR139: Fix config search with a recursive paramater.

Release 2.3.4

New features:
Updates:
Bug fix:
  • PR 124: fixing purge of dict_attr keys while saving bert models (train.py)

  • Issue 126: fixing initialisation of bert_tokenizer for cross validation (train.py)

Release 2.3.2

Updates:
  • Compatibility with python 3.7 and 3.8

  • Optional dependencies (viz, transformers, all)

  • Specify custom configurations with environment variable MELUSINE_CONFIG_DIR

  • Use any number of JSON and YAML files for configurations

(instead of just one config file)

Bug fix:
  • Fixed bug when training transformers model without meta features

Release 2.3

New features:
  • Added a class ExchangeConnector to interact with an Exchange Mailbox

  • Added new tutorial tutorial14_exchange_connector to demonstrate the usage of the ExchangeConnector class

Updates:

Release 2.0

New features:
  • Attentive Neural Networks are now available. :tada: We propose you an original Transformer architecture as well as pre-trained BERT models (Camembert and Flaubert)

  • Tutorial 13 will explain you how to get started with these models and attempt to compare them.

  • Validation data can now be used to train models (See fit function from NeuralModel for usage)

  • The activation function can now be modified to adapt to your needs (See NeuralModel init for usage)

Release 1.10.0

Updates:
  • Melusine is now running with Tensorflow 2.2

Release 1.9.6

New features:
  • Flashtext library is now used to flag names instead of regex. It allows a faster computation.

Release 1.9.5

New features:
  • An Ethics Guide is now available to evaluate AI projects, with guidelines and questionnaire. The questionnaire is based on criteria derived in particular from the work of the European Commission and grouped by categories.

  • Melusine also offers an easy and nice dashboard app with StreamLit. The App contains exploratory dashboard on the email dataset and a more specific study on discrimination between the dataset and a neural model classification.

The Melusine package

_images/schema_1.png

This package is designed for the preprocessing, classification and automatic summarization of emails written in french. 3 main subpackages are offered :

  • prepare_email : to preprocess and clean the emails.

  • summarizer : to extract keywords from an email.

  • models : to classify e-mails according to categories pre-defined defined by the user.

2 other subpackages are offered as building blocks :

  • nlp_tools : to provide classic NLP tools such as tokenizer, phraser and embeddings.

  • utils : to provide a TransformerScheduler class to build your own transformer and integrate it into a scikit-learn Pipeline.

An other subpackage is also provided to manage, modify or add parameters such as : regular expressions, keywords, stopwords, etc.

  • config : contains ConfigJsonReader class to setup and handle a conf.json file. This JSON file is the core of this package since it’s used by different submodules to preprocess the data.

Getting started : 30 seconds to Melusine

Importing Melusine

To use Melusine in a project:

import melusine

Input data : Email DataFrame

The basic requirement to use Melusine is to have an input e-mail DataFrame with the following columns:

  • body : Body of an email (single message or conversation history)

  • header : Header of an email

  • date : Reception date of an email

  • from : Email address of the sender

  • to (optional): Email address of the recipient

  • label (optional): Label of the email for a classification task (examples: Business, Spam, Finance or Family)

body

header

date

from

to

label

Thank you.\nBye,\nJohn

Re: Your order

jeudi 24 mai 2018 11 h 49 CEST

anonymous.sender@unknown.com

anonymous.recipient@unknown.fr

??

To import the test dataset:

from melusine.data.data_loader import load_email_data

df_email = load_email_data()

Pre-processing pipeline

A working pre-processing pipeline is given below:

from sklearn.pipeline import Pipeline
from melusine.utils.transformer_scheduler import TransformerScheduler
from melusine.prepare_email.manage_transfer_reply import check_mail_begin_by_transfer, update_info_for_transfer_mail, add_boolean_answer, add_boolean_transfer
from melusine.prepare_email.build_historic import build_historic
from melusine.prepare_email.mail_segmenting import structure_email
from melusine.prepare_email.body_header_extraction import extract_last_body
from melusine.prepare_email.cleaning import clean_body

ManageTransferReply = TransformerScheduler(
functions_scheduler=[
    (check_mail_begin_by_transfer, None, ['is_begin_by_transfer']),
    (update_info_for_transfer_mail, None, None),
    (add_boolean_answer, None, ['is_answer']),
    (add_boolean_transfer, None, ['is_transfer'])
])

EmailSegmenting = TransformerScheduler(
functions_scheduler=[
    (build_historic, None, ['structured_historic']),
    (structure_email, None, ['structured_body'])
])

Cleaning = TransformerScheduler(
functions_scheduler=[
    (extract_last_body, None, ['last_body']),
    (clean_body, None, ['clean_body'])
])

prepare_data_pipeline = Pipeline([
  ('ManageTransferReply', ManageTransferReply),
  ('EmailSegmenting', EmailSegmenting),
  ('Cleaning', Cleaning),
])

df_email = prepare_data_pipeline.fit_transform(df_email)

Phraser and Tokenizer pipeline

A pipeline to train and apply the phraser end tokenizer is given below:

from melusine.nlp_tools.phraser import Phraser
from melusine.nlp_tools.tokenizer import Tokenizer

tokenizer = Tokenizer (input_column='clean_body', output_column="tokens")
df_email = tokenizer.fit_transform(df_email)

phraser = Phraser(
    input_column='tokens',
    output_column='phrased_tokens',
    threshold=5,
    min_count=2
)
_ = phraser.fit(df_email)
df_email = phraser.transform(df_email)

Embeddings training

An example of embedding training is given below:

from melusine.nlp_tools.embedding import Embedding

embedding = Embedding(
    tokens_column='tokens',
    size=300,
    workers=4,
    min_count=3
)
embedding.train(df_email)

Metadata pipeline

A pipeline to prepare the metadata is given below:

from melusine.prepare_email.metadata_engineering import MetaExtension, MetaDate, Dummifier

metadata_pipeline = Pipeline([
  ('MetaExtension', MetaExtension()),
  ('MetaDate', MetaDate()),
  ('Dummifier', Dummifier())
])

df_meta = metadata_pipeline.fit_transform(df_email)

Keywords extraction

An example of keywords extraction is given below:

from melusine.summarizer.keywords_generator import KeywordsGenerator

keywords_generator = KeywordsGenerator()

df_email = keywords_generator.fit_transform(df_email)

Classification

The package includes multiple neural network architectures including CNN, RNN, Attentive and pre-trained BERT Networks. An example of classification is given below:

from sklearn.preprocessing import LabelEncoder
from melusine.models.neural_architectures import cnn_model
from melusine.models.train import NeuralModel

X = df_email.drop(['label'], axis=1)
y = df_email.label

le = LabelEncoder()
y = le.fit_transform(y)

pretrained_embedding = embedding

nn_model = NeuralModel(architecture_function=cnn_model,
                       pretrained_embedding=pretrained_embedding,
                       text_input_column='clean_body')

nn_model.fit(X, y)
y_res = nn_model.predict(X)

Glossary

Pandas dataframes columns

Because Melusine manipulates pandas dataframes, the naming of the columns is imposed. Here is a basic glossary to provide an understanding of each columns manipulated. Initial columns of the dataframe:

  • body : the body of the email. It can be composed of a unique message, a history of messages, a transfer of messages or a combination of history and transfers.

  • header : the subject of the email.

  • date : the date the email has been sent. It corresponds to the date of the last message of the email has been written.

  • from : the email address of the author of the last message of the email.

  • to : the email address of the recipient of the last message.

Columns added by Melusine:

  • is_begin_by_transfer : boolean, indicates if the email is a direct transfer. In that case it is recommended to update the value of the initial columns with the informations of the message transferred.

  • is_answer : boolean, indicates if the email contains a history of messages

  • is_transfer : boolean, indicates if the email is a transfer (in that case it does not have to be a direct transfer).

  • structured_historic : list of dictionaries, each dictionary corresponds to a message of the email. The first dictionary corresponds to the last message (the one that has been written) while the last dictionary corresponds to the first message of the history. Each dictionary has two keys :

    • meta : to access the metadata of the message as a string.

    • text : to access the message itself as a string.

  • structured_body : list of dictionaries, each dictionary corresponds to a message of the email. The first dictionary corresponds to the last message (the one that has been written) while the last dictionary corresponds to the first message of the history. Each dictionary has two keys :

    • meta : to access the metadata of the message as a dictionary. The dictionary has three keys:

      • date : the date of the message.

      • from : the email address of the author of the message.

      • to : the email address of the recipient of the message.

    • text : to access the message itself as a dictionary. The dictionary has two keys:

      • header : the subject of the message.

      • structured_text : the different parts of the message segmented and tagged as a list of dictionaries. Each dictionary has two keys:

        • part : to access the part of the message as a string.

        • tags : to access the tag of the part of the message.

  • last_body : string, corresponds to the part of the last message of the email that has been tagged as “BODY”.

  • clean_body : string, corresponds a cleaned last_body.

  • clean_header : string, corresponds to a cleaned header.

  • clean_text : string, concatenation of clean_header and clean_body.

  • tokens : list of strings, corresponds to a tokenized column, by default clean_text.

  • keywords : list of strings, corresponds to the keywords of extracted from the tokens column.

  • stemmed_tokens : list of strings, corresponds to a stemmed column, by default stemmed_tokens.

  • lemma_spacy_sm : string, corresponds to a lemmatized column.

  • lemma_lefff : string, corresponds to a lemmatized column.

Tags

Each messages of an email are segmented the in the structured_body columns and each parts are assigned a tag:

  • “RE/TR” : any metadata such as date, from, to etc.

  • “DISCLAIMER” : any disclaimer such as “L’émetteur décline toute responsabilité…”.

  • “GREETINGS” : any greetings such as “Salutations”.

  • “PJ” : any indication of an attached document such as “See attached file…”.

  • “FOOTER” : any footer such as “Provenance : Courrier pour Windows”.

  • “HELLO” : any salutations such as “Bonjour,”.

  • “THANKS” : any thanks such as “Avec mes remerciements”

  • “BODY” : the core of the the message which contains the valuable information.

Motivation & history

Origin of the project

MAIF, being one of the leading mutual insurance companies in France, receives daily a large volume of emails from its clients and is under pressure to reply to their requests as efficiently as possible. As such an efficient routing system is of the upmost importance to assign each emails to its right entity. However the previously outdated routing system put the company under ever increasing difficulties to fulfill its pledge. In order to face up to this challenge, MAIF has implemented a new routing system based on state-of-the-art NLP and Deep Learning techniques that would classify each email under the right label according to its content and extract the relevant information to help the MAIF counsellors processing the emails.

Ambitions of the project

Melusine is the first Open Source and free-of-use solution dedicated specifically to the qualification of e-mails written in french. The ambition of this Python package is to become a reference, but also to live in the French NLP community by federating users and contributors. Initially developed to answer the problem of routing e-mails received by the MAIF, the solution was implemented using state-of-the-art techniques in Deep Learning and NLP. Melusine can be interfaced with Scikit-Learn: it offers the user the possibility to train his own classification and automatic summarization model according to the constraints of his problem.

Why Melusine ?

Following MAIF’s tradition to name its open source packages after deities, it was chosen to release this package under the name of Melusine as an homage to a legend from the local folklore in the Poitou region in France where MAIF is historically based.

Credits

This package was created with Cookiecutter and the audreyr/cookiecutter-pypackage project template.

Installation

Stable release

To install melusine, run this command in your terminal:

$ pip install melusine

This is the preferred method to install melusine, as it will always install the most recent stable release.

If you don’t have pip installed, this Python installation guide can guide you through the process.

Optional dependencies

When running Melusine in production, users may want to limit the number of packages installed. For this purpose, Melusine makes use of optional dependencies. The command pip install melusine installs only the mandatory dependencies. Optional dependencies can be install as follows:

  • pip install melusine[viz] : Installs plotly and streamlit for visualization purposes

  • pip install melusine[exchange] : Installs exchangelib to connect Melusine with an Outlook Exchange mailbox

  • pip install melusine[transformers] : Installs transformers to train BERT-like models

  • pip install melusine[all] : Installs all the dependencies

From sources

The sources for melusine can be downloaded from the Github repo.

You can either clone the public repository:

$ git clone https://github.com/MAIF/melusine

Or download the tarball:

$ curl  -OL https://github.com/MAIF/melusine

Once you have a copy of the source, you can install it with:

$ python setup.py install

Usage

To use melusine in a project:

import melusine

Melusine input data : Email DataFrames

The basic requirement to use Melusine is to have an input e-mail DataFrame with the following columns:

  • body : Body of an email (single message or conversation historic)

  • header: Header of an email

  • date : Reception date of an email

  • from : Email address of the sender

  • to : Email address of the recipient

  • label (optional) : Label of the email for a classification task (examples: Business, Spam, Finance or Family)

body

header

date

from

to

label

Thank you.\nBye,\nJohn

Re: Your order

jeudi 24 mai 2018 11 h 49 CEST

anonymous.sender@unknown.com

anonymous.recipient@unknown.fr

A

In the examples presented below, a toy email DataFrame containing anonymized emails is used. The toy DataFrame can be loaded as follows:

from melusine.melusine.utils.data_loader import load_email_data

df_emails = load_email_data()
df_emails.head()

Prepare email subpackage : Basic usage

A common pre-processing step is to check whether an e-mail is an answer or not. This can be done in Melusine with the function add_boolean_answer:

from melusine.prepare_email.manage_transfer_reply import add_boolean_answer

df_emails['is_answer'] = df_emails.apply(add_boolean_answer, axis=1)

A new column is_answer is created containing a boolean variable:

  • True if the message is an answer

  • False if the message is not an answer

body

header

is_answer

Happy Birthday Bill!!

Birthday

False

Thank you

Re: Birthday

True

Create an email pre-processing pipeline

An email pre-processing pipeline takes an email DataFrame as input and executes a sequence of Transformers on every email in the DataFrame. The recommended way to create a pre-processing pipeline with Melusine is to:

  1. Wrap pre-processing functions in TransformerScheduler objects.

  2. Use a Scikit-Learn Pipeline object to chain transformers

Once the pipeline has been set-up, the pre-processing of an email DataFrame is straightforward:

>>> df_emails_preprocessed = pipeline.fit_transform(df_emails)

TransformerScheduler class

Functions can be wrapped in a TransformerScheduler object that can be integrated into an execution Pipeline. TransformerScheduler objects are compatible with the scikit-learn API (they have fit and transform methods).

A TransformerScheduler object is initialized with a functions_scheduler argument. The functions_scheduler argument is a list of tuples containing information about the desired pre-processing functions. Each tuple describe an individual function and should contain the following elements:

  1. A function

  2. A tuple with the function’s arguments (if no arguments are required, use None or an empty tuple)

  3. A column(s) name list returned by the function (if no arguments are required, use None or an empty list)

The code below describes the definition of a transformer:

from melusine.utils.transformer_scheduler import TransformerScheduler

melusine_transformer = TransformerScheduler(
functions_scheduler=[
    (my_function_1, (argument1, argument2), ['return_col_A']),
    (my_function_2, None, ['return_col_B', 'return_col_C'])
    (my_function_3, (argument1, ), None),
mode='apply_by_multiprocessing',
n_jobs=4)
])

The other parameters of the TransformerScheduler class are:

  • mode (optional): Define mode to apply function along a row axis (axis=1) If set to ‘apply_by_multiprocessing’, it uses multiprocessing tool to parallelize computation. Possible values are ‘apply’ (default) and ‘apply_by_multiprocessing’

  • n_jobs (optional): Number of cores used for computation. Default value, 1. Possible values are integers ranging from 1 (default) to the number of cores available for computation

A TransformerScheduler can be used independently or included in a scikit pipeline (recommended):

>>> # Used independently
>>> df_emails = melusine_transformer.fit_transform(df_emails)
>>> # Used in a scikit pipeline
>>> from sklearn.pipeline import Pipeline
>>> pipeline = Pipeline([('MelusineTransformer', melusine_transformer)])
>>> df_emails = pipeline.fit_transform(df_emails)

The fit_transform method returns a DataFrame with new features (new columns)

body

header

return_col_A

return_col_B

return_col_C

return_col_D

Happy Birthday Bill!!

Birthday

new_feature_A

new_feature_B

new_feature_C

new_feature_D

Thank you

Re: Birthday

new_feature_A

new_feature_B

new_feature_C

new_feature_D

Chaining transformers in a scikit-learn pipeline

Once all the desired functions and transformers have been defined, transformers can be chained in a Scikit-Learn Pipeline. The code below describes the definition of a pipeline:

from sklearn.pipeline import Pipeline

pipeline = Pipeline([
    ('TransformerName1', TransformerObject1),
    ('TransformerName2', TransformerObject2),
    ('TransformerName3', TransformerObject3),
])

Example of a working pipeline

A working pre-processing pipeline is given below:

from sklearn.pipeline import Pipeline
from melusine.utils.transformer_scheduler import TransformerScheduler
from melusine.prepare_email.manage_transfer_reply import add_boolean_answer, add_boolean_transfer
from melusine.prepare_email.build_historic import build_historic
from melusine.prepare_email.mail_segmenting import structure_email

ManageTransferReply = TransformerScheduler(
    functions_scheduler=[
        (add_boolean_answer, None, ['is_answer']),
        (add_boolean_transfer, None, ['is_transfer'])
])

HistoricBuilder = TransformerScheduler(
    functions_scheduler=[
        (build_historic, None, ['structured_historic']),
])

Segmenting = TransformerScheduler(
    functions_scheduler=[
        (structure_email, None, ['structured_body'])
])

prepare_data_pipeline = Pipeline([
    ('ManageTransferReply', ManageTransferReply),
    ('HistoricBuilder', HistoricBuilder),
    ('Segmenting', Segmenting),
])

df_emails = prepare_data_pipeline.fit_transform(df_emails)

In this example, the pre-processing functions applied are:

Create a custom email pre-processing function

Creating a custom pre-processing function and adding it to a pre-processing pipeline can be done easily with Melusine. Two main requirements are:

  1. Make the function compatible with the pandas apply method

    • First argument should be ‘row’ (Row of an email DataFrame)
      >>> def my_function(row, arg1, arg2):
      
    • Example: row[‘header’] will contain the header of a message

  2. Make sure to call existing columns of the DataFrame

    • Don’t call row[‘is_answer’] before the ‘is_answer’ column has been created

The following example creates a custom function to count the occurrence of a word in the body of an email:

import pandas as pd
from sklearn.pipeline import Pipeline
from melusine.utils.transformer_scheduler import TransformerScheduler
from melusine.prepare_email.manage_transfer_reply import add_boolean_answer, add_boolean_transfer

# Create a fake email Dataframe
df_duck = pd.DataFrame({
    "body" : ["Lion Duck Pony", "Duck Pony Pony", "Duck Duck Pony"],
    "header" : ["zoo report", "Tr : zoo report", "Re : zoo report"]
})

# Define a custom function
def count_word_occurrence_in_body(row, word):
    all_word_list = row["body"].lower().split()
    word_occurence = all_word_list.count(word)
    return word_occurence

# Wrap function in a transformer
CountWordOccurrence = TransformerScheduler(
functions_scheduler=[
    (count_word_occurrence_in_body, ("duck",), ['duck_count']),
    (count_word_occurrence_in_body, ("pony",), ['pony_count']),
])

# Create a second transformer with regular Melusine functions
ManageTransferReply = TransformerScheduler(
functions_scheduler=[
    (add_boolean_answer, None, ['is_answer']),
    (add_boolean_transfer, None, ['is_transfer'])
])

# Chain transformers in a pipeline
prepare_data_pipeline = Pipeline([
    ('CountWordOccurrence', CountWordOccurrence), # Transformer with custom functions
    ('ManageTransferReply', ManageTransferReply), # Transformer with regular Melusine functions
])

# Pre-process input DataFrame
df_duck_prep = prepare_data_pipeline.fit_transform(df_duck)

body

header

duck_count

pony_count

is_answer

is_transfer

Lion Duck Pony

zoo report

1

1

False

False

Duck Duck Pony

Re : zoo report

2

1

True

False

Duck Pony Pony

Tr : zoo report

1

2

False

False

Note : It is totally fine to mix regular and custom functions in a transformer.

Testing a function on a single email

Since all pre-processing functions are made compatible with pandas apply function, a function can be tested on a single email. In the example below, the function add_boolean_answer is tested on a single email:

from melusine.prepare_email.manage_transfer_reply import add_boolean_answer

email_index = 2
email_is_answer = add_boolean_answer(df_emails.iloc[email_index])
print("Message %d is an answer: %r" %(email_index, email_is_answer))

Output:

"Message 2 is an answer: True"

NLP tools subpackage

The different classes of the NLP tools subpackage are described in this section.

Phraser

The Melusine Phraser class transforms common multi-word expressions into single elements:

>>> new york -> new_york

To train a Melusine Phraser (which is based on a Gensim Phraser), the input email DataFrame should contain a ‘clean_body’ column which can be created with the clean_body function.

In the example below, a Phraser is trained on a toy DataFrame:

import pandas as pd
from melusine.nlp_tools.phraser import Phraser
from melusine.nlp_tools.phraser import phraser_on_text

phraser = Phraser()
df_new_york = pd.DataFrame({
    'clean_body' : ["new york is so cool", "i love new york", "new york city"]
})

phraser.train(df_new_york)

df_new_york['clean_body'] = df_new_york['clean_body'].apply(phraser_on_text, args=(phraser,))

# Save the Phraser instance to disk
phraser.save('./pretrained_phraser.pickle')
# Load the Phraser
pretrained_phraser = Phraser().load('./pretrained_phraser.pickle')

In reality, a training set with only 3 emails is too small to train a Phraser. For illustrative purpose, the table below shows the expected output.

clean_body

clean_body_new

new york is so cool

new_york is so cool

i love new york

i love new_york

new york city

new_york city

The specific parameters of the Phraser class are:

  • common_terms : list of stopwords to be ignored (default value = stopword list from NLTK)

  • threshold : threshold to select collocations

  • min_count : minimum count of word to be selected as collocation

Tokenizer

A tokenizer splits a sentence-like string into a list of sub-strings (tokens). The Melusine Tokenizer class is based on a NLTK regular expression tokenizer which uses a regular expression (regex) pattern to tokenize the text:

import pandas as pd
from melusine.nlp_tools.tokenizer import Tokenizer

df_tok = pd.DataFrame({
    'clean_body' : ["hello, i'm here to tokenize text. bye"],
    'clean_header' : ["re: hello"],
})

tokenizer = Tokenizer(input_column='clean_body')
df_tok = tokenizer.fit_transform(df_tok)

A new column tokens is created with a list of the tokens extracted from the text data.

clean_body

clean_header

tokens

hello, i’m here to tokenize text. bye

re: hello

[‘hello’, ‘i’, ‘here’, ‘tokenize’, ‘text’, ‘bye’]

The specific parameters of the Tokenizer class are:

  • stopwords : list of keywords to be ignored (this list can be defined in the conf file)

  • stop_removal : True if stopwords should be removed, else False

Embeddings

With a regular representation of words, there is one dimension for each word in the vocabulary (set of all the words in a text corpus). The computational cost of NLP tasks, such as training a neural network model, based on such a high dimensional space can be prohibitive. Word embeddings are abstract representations of words in a lower dimensional vector space. One of the advantages of word embeddings is thus to save computational cost.

The Melusine Embedding class uses the Gensim Word2Vec module to train a word2vec model. The trained Embedding object will be used in the Models subpackage to train a Neural Network to classify emails.

The code below illustrates how the Embedding class works. It should be noted that, in practice, to train a word embedding model, a lot of emails are required:

import pandas as pd
from melusine.nlp_tools.embedding import Embedding

df_embeddings = pd.DataFrame({
    'clean_body' : ["word text word text data word text"],
    'clean_header' : ["re: hello"],
})

embedding = Embedding(input_column='clean_body', min_count=3)
embedding.train(df_embeddings)

# Save the trained Embedding instance to disk
embedding.save('./pretrained_embedding.pickle')

# Load the trained Embedding instance
pretrained_embedding = Embedding().load('./pretrained_embedding.pickle')

# Use trained Embedding to initialise the Neural Network Model
# The definition of a neural network model is not discussed in this section
# nn_model = NeuralModel("...", pretrained_embedding=pretrained_embedding, "...")

Summarizer subpackage

The main item of the Summarizer subpackage is the KeywordGenerator class. The KeywordGenerator class extracts relevant keywords in the text data based on a tf-idf score.

Requirements on the input DataFrame to use a KeywordGenerator:

  • KeywordGenerator requires a ‘tokens’ column which can be generated with a Tokenizer

Keywords can then be extracted as follows:

import pandas as pd
from melusine.summarizer.keywords_generator import KeywordsGenerator
from melusine.nlp_tools.tokenizer import Tokenizer


df_zoo = pd.DataFrame({
    'clean_body' : ["i like lions and ponys and gorillas", "i like ponys and tigers", "i like tigers and lions", "i like raccoons and unicorns"],
    'clean_header' : ["things i like", "things i like", "things i like", "things i like"]
})

tokenizer = Tokenizer(input_column='clean_body')
# Create the 'tokens' column
df_zoo = tokenizer.fit_transform(df_zoo)

keywords_generator = KeywordsGenerator(n_max_keywords=2, stopwords=['like'])
# Fit keyword generator on the text data corpus (using the tokens column)
keywords_generator.fit(df_zoo)
# Extract relevant keywords
keywords_generator.transform(df_zoo)

In the text data of the example, some words are very common such as “i”, “like” or “things”, whereas other words are rare, such as “raccoons”. The keyword generator gives priority to rare words in the keyword extraction process:

clean_body

clean_header

tokens

keywords

i like lions and ponies and gorillas

things i like

[things, i, i, lions, and, ponies, and, gorillas]

[lions, ponys]

i like ponies and tigers

things i like

[things, i, i, ponies, and, tigers]

[ponies, tigers]

i like tigers and lions

things i like

[things, i, i, tigers, and, lions]

[tigers, lions]

i like raccoons and unicorns

things i like

[things, i, i, raccoons, and, unicorns]

[raccoons, unicorns]

The specific parameters of the KeywordGenerator class are:

  • max_tfidf_features : size of vocabulary for tfidf

  • keywords : list of keyword to be extracted in priority (this list can be defined in the conf file)

  • stopwords : list of keywords to be ignored (this list can be defined in the conf file)

  • resample : when DataFrame contains a ‘label’ column, balance the dataset by resampling

  • n_max_keywords : maximum number of keywords to be returned for each email

  • n_min_keywords : minimum number of keywords to be returned for each email

  • threshold_keywords : minimum tf-idf score for a word to be selected as keyword

Models subpackage

The main item of the Models subpackage is the NeuralModel class. The NeuralModel creates a Neural Network that can be trained and used to classify emails.

The minimum input features required by the NeuralModel class are the following:

  • An email DataFrame with:

    • an integer ‘label’ column (a label encoder can be used to convert class names into integers)

    • a ‘clean_text’ column with text data

  • An instance of the Embedding class (Trained word embedding model)

The code below shows a minimal working example for Email Classification using a NeuralModel instance (a much larger training set is required to obtain meaningful results):

import pandas as pd

# Prepare email
from melusine.utils.transformer_scheduler import TransformerScheduler
from melusine.prepare_email.manage_transfer_reply import \
    check_mail_begin_by_transfer, update_info_for_transfer_mail, add_boolean_answer, add_boolean_transfer
from melusine.prepare_email.build_historic import build_historic
from melusine.prepare_email.mail_segmenting import structure_email
from melusine.prepare_email.body_header_extraction import extract_last_body, extract_header
from melusine.prepare_email.cleaning import clean_body, clean_header
from melusine.prepare_email.metadata_engineering import MetaDate, MetaExtension, Dummifier

# Scikit-Learn API
from sklearn.pipeline import Pipeline

# NLP tools
from melusine.nlp_tools.phraser import Phraser
from melusine.nlp_tools.phraser import phraser_on_body
from melusine.nlp_tools.phraser import phraser_on_header
from melusine.nlp_tools.tokenizer import Tokenizer
from melusine.nlp_tools.embedding import Embedding

# Summarizer
from melusine.summarizer.keywords_generator import KeywordsGenerator

# Models
from melusine.models.train import NeuralModel
from melusine.models.neural_architectures import cnn_model

# Load toy email data
from utils.data_loader import load_email_data
df_emails = load_email_data()

# Transformer object to manage transfers and replies
ManageTransferReply = TransformerScheduler(
    functions_scheduler=[
        (check_mail_begin_by_transfer, None, ['is_begin_by_transfer']),
        (update_info_for_transfer_mail, None, None),
        (add_boolean_answer, None, ['is_answer']),
        (add_boolean_transfer, None, ['is_transfer'])
    ]
)

# Transformer object to segment the different messages in the email, parse their metadata and
# tag the different part of the messages
Segmenting = TransformerScheduler(
    functions_scheduler=[
        (build_historic, None, ['structured_historic']),
        (structure_email, None, ['structured_body'])
    ]
)

# Transformer object to extract the body of the last message of the email and clean it as
# well as the header
LastBodyHeaderCleaning = TransformerScheduler(
    functions_scheduler=[
        (extract_last_body, None, ['last_body']),
        (clean_body, None, ['clean_body']),
        (clean_header, None, ['clean_header'])
    ]
)

# Transformer object to apply the phraser on the texts
phraser = Phraser().load('./phraser.pickle')
PhraserTransformer = TransformerScheduler(
    functions_scheduler=[
        (phraser_on_body, (phraser,), ['clean_body']),
        (phraser_on_header, (phraser,), ['clean_header'])
    ]
)

# Tokenizer object
tokenizer = Tokenizer(input_column="clean_body")

# Full preprocessing pipeline
PreprocessingPipeline = Pipeline([
    ('ManageTransferReply', ManageTransferReply),
    ('Segmenting', Segmenting),
    ('LastBodyHeaderCleaning', LastBodyHeaderCleaning),
    ('PhraserTransformer', PhraserTransformer),
    ('tokenizer', tokenizer)
])

# Apply preprocessing pipeline to DataFrame
df_emails = PreprocessingPipeline.fit_transform(df_emails)

# Pipeline to extract dummified metadata
MetadataPipeline = Pipeline([
    ('MetaExtension', MetaExtension()),
    ('MetaDate', MetaDate()),
    ('Dummifier', Dummifier())
])

# Apply MetaData processing pipeline to DataFrame
df_meta = MetadataPipeline.fit_transform(df_emails)

# Keywords extraction
keywords_generator = KeywordsGenerator(n_max_keywords=3)
df_emails = keywords_generator.fit_transform(df_emails)

# Train an embedding with the 'clean_body' data
pretrained_embedding = Embedding(input_column='clean_body', min_count=3)
pretrained_embedding.train(df_emails)

# Create a 'clean_text' column from the 'clean_header' and 'clean_body' columns
df_emails['clean_text'] = df_emails['clean_header']+'. '+df_emails['clean_body']

# Create a training set DataFrame with MetaData + the 'clean_text' columns
X = pd.concat([df_emails['clean_text'],df_meta],axis=1)

# The 'label' column contains target labels for email classification
# Labels should be encoded ('Family', 'Work', 'Sport' => 1, 2, 3)
from sklearn.preprocessing import LabelEncoder
y = df_emails['label']
le = LabelEncoder()
y = le.fit_transform(y)

# CNN model with 'clean_text' as text_input and 'extension', 'dayofweek', 'hour', 'min'
# as metadata input
nn_model = NeuralModel(neural_architecture_function=cnn_model,
                       pretrained_embedding=pretrained_embedding,
                       text_input_column="clean_text",
                       meta_input_list=['extension', 'dayofweek', 'hour', 'min'],
                       n_epochs=10)

# Train the Neural Network model
nn_model.fit(X,y)

# Predict labels on the training dataset
y_res = nn_model.predict(X)

# Decode prediction results (1, 2, 3 => 'Family', 'Work', 'Sport')
y_res = le.inverse_transform(y_res)

# Print results
print(y_res)

The specific parameters of the NeuralModel class are:

  • neural_architecture_function : function which returns a Neural Network Model instance from Keras

  • pretrained_embedding : pretrained embedding (Embedding class object)

  • text_input_column : input text column to consider for the model (Example: ‘clean_body’)

  • meta_input_list : list of the names of the columns containing the metadata If empty list or None the model is used without metadata Default value, [‘extension’, ‘dayofweek’, ‘hour’, ‘min’]

  • vocab_size : size of vocabulary for neural network model

  • seq_size : maximum size of input for neural model

  • loss : loss function for training (Default ‘categorical_crossentropy’)

  • batch_size : size of batches for the training of the neural network model

  • n_epochs : number of epochs for the training of the neural network model

We integrate 3 main classifier neural networks, respectively recurrent, convolutional and attentive. Each of the proposed architecture employs a distinct mathematical operation.

Recurrent Neural Network Classifier (RNN)

RNN are traditionally used with textual data as they are specifically designed to handle sequentially structured data. Inputs are sequentially computed given a cell operation, generally a LSTM or GRU cell. at each step, the current input as well as the output from the previous step are used to compute the next hidden state. The proposed architecture includes a 2-layers bidirectional GRU networks. The network last hidden state is used as the final sentence embedding.

_images/rnn-model.png

Convolutional Neural Network Classifier (CNN)

CNN uses multiple filters to distinguish patterns in data. Such filters are assembled across the hidden layers to build more complex patterns and structures. The last layer should therefor capture a global and generic representation of the data. In our architecture, we use a two hidden layers CNN with respectively 200 filters for each hidden layer. The last hidden states are aggregated using a max pooling operation.

_images/cnn-model.png

Attentive Neural Network Classifier

Attentive-based neural networks are fairly new in the NLP community but results are extremely promising. They rely on the self-attention operation which computes hidden states as a weighted sum from the inputs. As the multiple filters in the CNN architecture, the multi-branch attention aggregate multiple attention operation to capture various properties from the input. Such operation is easily perform on GPU infrastructure. We propose an architecture inspired from previously introduced RNN and CNN architecture with a two layers multi-branch attention module follow by a max pooling operation.

_images/transformer-model.png

BERT Neural Network Classifier

We also propose a wrap-up for the popular pre-trained bert architecture. Bidirectional Encoder Representations from Transformers (BERT) take into account the context for each occurrence of a given word and will provide a contextualized embedding that will be different according to the sentence. However, we only use the first word embedding, usually called the classification token in our classifier model. We made available the two trending French models Camembert and Flaubert.

_images/bert-model.png

Use a custom config file

To optimize Melusine for your needs, a custom configuration file may be used. Through a custom configuration file, the user can specify parameters such as:

  • Custom keywordsList of keywords that Melusine should focus on
    • Example: extract keywords with the KeywordExtractor

  • Custom stopwordsList of stopwords that Melusine should ignore
    • Example: tokenize sentences with a Tokenizer

  • Custom regular expressions (regex)regex used in the melusine functions
    • Example: Regex to detect that a message is a reply

The following code shows how to specify a custom configuration file:

from melusine.config.config import ConfigJsonReader

conf = ConfigJsonReader()

# Print the path to the current configuration file
with open(conf.path_ini_file_, 'r') as ini_file:
    print(ini_file.read())

# Print the content of the current configuration file
conf_dict = conf.get_config_file()
print(conf_dict)  # will print the json

# Set a new path to the configuration file
conf.set_config_path(file_path='my/path/conf.json')

# Print the new path to the configuration file
with open(conf.path_ini_file_, 'r') as ini_file:
    print(ini_file.read())

# Print the content of the new configuration file
conf_dict = conf.get_config_file()
print(conf_dict)  # will print the json

It is also possible to go back to the original configuration file:

from melusine.config.config import ConfigJsonReader

conf = ConfigJsonReader()
conf.reset_config_path()

Warning : the configuration file is loaded by the different modules (Tokenizer, KeywordExtractor, etc) during the import, therefore, for the new configuration file to be effective, the code / kernel should be restarted after each modification of the configuration file.

Use a custom name file

While working with text data, names might undergo specific processing:

  • stopword processing : if names don’t need to be identified, they may be discarded during the text processing

  • flagging : if names need to be identified but not specifically, names may be replaced with a name_flag (bob -> flag_name)

By default, Melusine identifies names using an explicit list of names available in a file (‘melusine/config/names.csv’). The default name list was adapted from a name dataset publicly available on the french government website. This list contains first names given to children (french or not) born in France between 1900 and 2018.

Melusine users may specify a custom name list using a custom ‘names.csv’ file.

The following code shows how to specify a custom configuration file:

import os
import pandas as pd
from melusine.config.config import ConfigJsonReader

conf = ConfigJsonReader()

# Print the path to the current name file
with open(conf.path_ini_file_, 'r') as ini_file:
        print(ini_file.read())

# Print the current name list
conf_dict = conf.get_config_file()
print(conf_dict['words_list']['names'][:5])

### Use a custom name file
# 1. Create a new (custom) name file
#    - The file should be a csv file with a column called `Name`
# 2. Set the new file as the current Melusine name file    with open(conf.path_ini_file_, 'r') as ini_file:
# Set a new path to the configuration file

# Create a name DataFrame
df_names = pd.DataFrame({'Name' : ['Daenerys', 'Tyrion', 'Jon', 'Raegar']})

# Path to the new name.csv file
new_path = os.path.join(os.getcwd(), 'data', 'names.csv')

# Save new name.csv file
df_names.to_csv(new_path, encoding="latin-1", sep=";", index=False)

# Set a new path to the name file in Melusine
conf.set_name_file_path(file_path=new_path)

# Print the new path to the name file
with open(conf.path_ini_file_, 'r') as ini_file:
    print(ini_file.read())

# Print the new file list
conf_dict = conf.get_config_file()
print(conf_dict['words_list']['names'][:5])

It is possible to go back to the original name file:

from melusine.config.config import ConfigJsonReader

conf = ConfigJsonReader()
conf.reset_name_file_path()

conf_dict = conf.get_config_file()
print(conf_dict['words_list']['names'][:5])

Warning : the name file is loaded by the different modules (Tokenizer, KeywordExtractor, etc) during the import, therefore, for the new name file to be effective, the code / kernel should be restarted after each modification of the name file.

Package dependencies

Melusine works with Python versions from 3.6 to 3.8. It builds on top of following packages:

  • pandas

  • scikit-learn

  • flashtext

  • gensim

  • tensorflow

  • joblib

  • unidecode

Melusine API

Utils subpackage melusine.utils

TODO : ADD DESCRIPTION OF THE SUBPACKAGE

List of submodules

TransformerScheduler melusine.utils.transformer_scheduler

Useful class to define its own transformer using specific functions in a specific order to apply along a row of DataFrame (axis=1).

It is compatible with scikit-learn API (i.e. contains fit, transform methods).

class melusine.utils.transformer_scheduler.TransformerScheduler(functions_scheduler, mode='apply', n_jobs=1, progress_bar=True, copy=True, verbose=0)[source]

Bases: sklearn.base.BaseEstimator, sklearn.base.TransformerMixin

This class aims to provide a good way to define its own transformer. It takes a list of function defined in a specific order to apply along a row of DataFrame (axis=1). Transformer returned is compatible with scikit-learn API (i.e. contains fit, transform methods).

Parameters
functions_schedulerlist of tuples, (function, tuple, list)

List of function to be applied in a specific order. Each element of the list has to be defined as follow: (function, argument(s) used by the function (optional), colname(s) returned (optional))

modestr {‘apply’, ‘apply_by_multiprocessing’}, optional

Define mode to apply function along a row axis (axis=1). Default value, ‘apply’. If set to ‘apply_by_multiprocessing’, it uses multiprocessing tool to parallelize computation.

n_jobsint, optional

Number of cores used for computation. Default value, 1.

progress_barboolean, optional

Whether to print a progress bar from tqdm package. Default value, True. Works only when mode is set to ‘apply_by_multiprocessing’.

copyboolean, optional

Make a copy of DataFrame. Default value, True.

verboseint, optional

Verosity mode, print loggers. Default value, 0.

Examples

>>> from melusine.utils.transformer_scheduler import TransformerScheduler
>>> MelusineTransformer = TransformerScheduler(
>>>     functions_scheduler=[
>>>         (my_function_1, (argument1, argument2), ['return_col_A']),
>>>         (my_function_2, None, ['return_col_B', 'return_col_C'])
>>>         (my_function_3, (), ['return_col_D'])
>>>     ])
Attributes
function_scheduler, mode, n_jobs, progress_bar
static apply_dict(X_, func_, args_=None, cols_=None, **kwargs)[source]

Apply a function on a dictionary.

Parameters
X_dict,

Data on which transformations are applied.

args_list or tuple

List of arguments of the function to apply

cols_list or tuple

List of columns created by the transformation

func_func

Function to apply

Returns
dict
static apply_pandas(X_, func_, args_=None, cols_=None, **kwargs)[source]

Apply a function on a pandas DataFrame.

Parameters
X_pandas.DataFrame,

Data on which transformations are applied.

args_list or tuple

List of arguments of the function to apply

cols_list or tuple

List of columns created by the transformation

func_func

Function to apply

Returns
pandas.DataFrame
static apply_pandas_multiprocessing(X_, func_, args_=None, cols_=None, n_jobs=1, progress_bar=False, **kwargs)[source]
fit(X, y=None)[source]

Unused method. Defined only for compatibility with scikit-learn API.

transform(X)[source]

Apply functions defined in the function_scheduler parameter.

Parameters
Xpandas.DataFrame,

Data on which transformations are applied.

Returns
pandas.DataFrame
Multiprocessing melusine.utils.multiprocessing
melusine.utils.multiprocessing.apply_by_multiprocessing(df, func, **kwargs)[source]

Apply a function along an axis of the DataFrame using multiprocessing.

Parameters
dfpd.DataFrame

DataFrame where the function is applied

funcfunction to apply
Returns
pd.DataFrame

Returns the DataFrame with the function applied.

melusine.utils.multiprocessing.apply_df(input_args)[source]
Streamer melusine.utils.streamer
PrintParts melusine.utils.printer
melusine.utils.printer.print_color(text, part=None)[source]

Select according to the tag the right color to use when printing.

melusine.utils.printer.print_color_mail(structured_body)[source]

Highlight the tagged sentences.

Parameters
structured_bodya structured body from process_sent_tag,
Returns
Print the mail by sentence.

Prepare_email subpackage melusine.prepare_email

_images/schema_2.png

List of submodules

Transfer & Reply melusine.prepare_email.manage_transfer_reply
melusine.prepare_email.manage_transfer_reply.add_boolean_answer(row)[source]

Compute boolean Series which return True if the “header” starts with given regex ‘transfer_subject’, False if not.

To be used with methods such as: apply(func, axis=1) or apply_by_multiprocessing(func, axis=1, **kwargs).

Parameters
rowrow of pd.Dataframe, columns [‘header’]
Returns
pd.Series

Examples

>>> import pandas as pd
>>> data = pd.read_pickle('./tutorial/data/emails_anonymized.pickle')
>>> # data contains a 'header' column
>>> from melusine.prepare_email.manage_transfer_reply import add_boolean_answer
>>> add_boolean_answer(data.iloc[0])  # apply for 1 sample
>>> data.apply(add_boolean_answer, axis=1)  # apply to all samples
melusine.prepare_email.manage_transfer_reply.add_boolean_transfer(row)[source]

Compute boolean Series which return True if the “header” starts with given regex ‘answer_subject’, False if not.

To be used with methods such as: apply(func, axis=1) or apply_by_multiprocessing(func, axis=1, **kwargs).

Parameters
rowrow of pd.Dataframe, columns [‘header’]
Returns
pd.Series

Examples

>>> import pandas as pd
>>> data = pd.read_pickle('./tutorial/data/emails_anonymized.pickle')
>>> # data contains a 'header' column
>>> from melusine.prepare_email.manage_transfer_reply import add_boolean_transfer
>>> add_boolean_transfer(data.iloc[0])  # apply for 1 sample
>>> data.apply(add_boolean_transfer, axis=1)  # apply to all samples
melusine.prepare_email.manage_transfer_reply.check_mail_begin_by_transfer(row)[source]

Compute boolean Series which return True if the “body” starts with given regex ‘begin_transfer’, False if not.

To be used with methods such as: apply(func, axis=1) or apply_by_multiprocessing(func, axis=1, **kwargs).

Parameters
rowrow of pd.Dataframe, columns [‘body’]
Returns
pd.Series

Examples

>>> import pandas as pd
>>> data = pd.read_pickle('./tutorial/data/emails_anonymized.pickle')
>>> # data contains a 'body' column
>>> from melusine.prepare_email.manage_transfer_reply import check_mail_begin_by_transfer
>>> check_mail_begin_by_transfer(data.iloc[0])  # apply for 1 sample
>>> data.apply(check_mail_begin_by_transfer, axis=1)  # apply to all samples
melusine.prepare_email.manage_transfer_reply.update_info_for_transfer_mail(row)[source]

Extracts and updates informations from forwarded mails, such as: body, from, to, header, date. - It changes the header by the initial subject (extracted from forward email). - It removes the header from emails’ body.

To be used with methods such as: apply(func, axis=1) or apply_by_multiprocessing(func, axis=1, **kwargs).

Parameters
rowrow of pd.Dataframe,
columns [‘body’, ‘header’, ‘from’, ‘to’, ‘date’, ‘is_begin_by_transfer’]
Returns
pd.DataFrame

Examples

>>> import pandas as pd
>>> from melusine.prepare_email.manage_transfer_reply import check_mail_begin_by_transfer
>>> data = pd.read_pickle('./tutorial/data/emails_anonymized.pickle')
>>> data['is_begin_by_transfer'] = data.apply(check_mail_begin_by_transfer, axis=1)
>>> # data contains columns ['from', 'to', 'date', 'header', 'body', 'is_begin_by_transfer']
>>> from melusine.prepare_email.manage_transfer_reply import update_info_for_transfer_mail
>>> update_info_for_transfer_mail(data.iloc[0])  # apply for 1 sample
>>> data.apply(update_info_for_transfer_mail, axis=1)  # apply to all samples
Cleaning melusine.prepare_email.cleaning

Cleaning of the body and the header

melusine.prepare_email.cleaning.clean_body(row, flags=True)[source]
Clean body column. The cleaning involves the following operations:
  • Cleaning the text

  • Removing the multiple spaces

  • Flagging specific items (postal code, phone number, date…)

Parameters
rowrow of pandas.Dataframe object,

Data contains ‘last_body’ column.

flagsboolean, optional

True if you want to flag relevant info, False if not. Default value, True.

Returns
row of pandas.DataFrame object or pandas.Series if apply to all DF.
melusine.prepare_email.cleaning.clean_header(row, flags=True)[source]
Clean the header column. The cleaning involves the following operations:
  • Removing the transfers and answers indicators

  • Cleaning the text

  • Flagging specific items (postal code, phone number, date…)

Parameters
rowrow of pandas.Dataframe object,

Data contains ‘header’ column.

flagsboolean, optional

True if you want to flag relevant info, False if not. Default value, True.

Returns
row of pd.DataFrame object or pandas.Series if apply to all DF.
melusine.prepare_email.cleaning.clean_text(text)[source]
Clean a string. The cleaning involves the following operations:
  • Putting all letters to lowercase

  • Removing all the accents

  • Removing all line breaks

  • Removing all symbols and punctuations

  • Removing the multiple spaces

Parameters
textstr
Returns
str
melusine.prepare_email.cleaning.flag_items(text, flags=True)[source]
Flag relevant information

ex : amount, phone number, email address, postal code (5 digits)..

Parameters
textstr,

Body content.

flagsboolean, optional

True if you want to flag relevant info, False if not. Default value, True.

Returns
str
melusine.prepare_email.cleaning.remove_accents(text, use_unidecode=False)[source]

Remove accents from text Using unidecode is more powerful but much more time consuming Exemple: the joined ‘ae’ character is converted to ‘a’ + ‘e’ by unidecode while it is suppressed by unicodedata.

melusine.prepare_email.cleaning.remove_apostrophe(text)[source]

Remove apostrophes from text

melusine.prepare_email.cleaning.remove_line_break(text)[source]

Remove line breaks from text

melusine.prepare_email.cleaning.remove_multiple_spaces_and_strip_text(text)[source]

Remove multiple spaces, strip text, and remove ‘-’, ‘*’ characters.

Parameters
textstr,

Header content.

Returns
str
melusine.prepare_email.cleaning.remove_superior_symbol(text)[source]

Remove superior and inferior symbols from text

melusine.prepare_email.cleaning.remove_transfer_answer_header(text)[source]

Remove historic and transfers indicators in the header. Ex: “Tr:”, “Re:”, “Fwd”, etc.

Parameters
textstr,

Header content.

Returns
str
melusine.prepare_email.cleaning.text_to_lowercase(text)[source]

Set all letters to lowercase

Build Email Historic melusine.prepare_email.build_historic
melusine.prepare_email.build_historic.build_historic(row)[source]

Rebuilds and structures historic of emails from the whole contents. Function has to be applied with apply method of a DataFrame along an axis=1. For each email of the historic, it segments the body into 2 different parts (2 keys of dict):

{‘text’: extract raw text without metadata,

‘meta’: get transition from the ‘transition_list’ defined in the conf.json }.

Parameters
rowrow,

A pandas.DataFrame row object with ‘body’ column.

Returns
list

Examples

>>> import pandas as pd
>>> data = pd.read_pickle('./tutorial/data/emails_anonymized.pickle')
>>> # data contains a 'body' column
>>> from melusine.prepare_email.build_historic import build_historic
>>> build_historic(data.iloc[0])  # apply for 1 sample
>>> data.apply(build_historic, axis=1)  # apply to all samples
melusine.prepare_email.build_historic.is_only_typo(text)[source]

check if the string contains any word character

Email Segmenting melusine.prepare_email.mail_segmenting
melusine.prepare_email.mail_segmenting.split_message_to_sentences(text, sep_='(.*?[;.,?!])')[source]

Split each sentences in a text

melusine.prepare_email.mail_segmenting.structure_email(row)[source]

1. Splits parts of each messages in historic and tags them. For example a tag can be hello, body, greetings etc 2. Extracts the meta informations of each messages

To be used with methods such as: apply(func, axis=1) or apply_by_multiprocessing(func, axis=1, **kwargs).

Parameters
rowrow of pd.Dataframe, apply on column [‘structured_historic’]
Returns
list of dictsone dict per message

Examples

>>> import pandas as pd
>>> from melusine.prepare_email.build_historic import build_historic
>>> data = pd.read_pickle('./tutorial/data/emails_anonymized.pickle')
>>> data['structured_historic'] = data.apply(build_historic, axis=1)
>>> # data contains column ['structured_historic']
>>> from melusine.prepare_email.mail_segmenting import structure_email
>>> structure_email(data.iloc[0])  # apply for 1 sample
>>> data.apply(structure_email, axis=1)  # apply to all samples
melusine.prepare_email.mail_segmenting.structure_message(message)[source]

Splits parts of a message and tags them. For example a tag can be hello, body, greetings etc Extracts the meta informations of the message

Parameters
messagedict
Returns
dict
melusine.prepare_email.mail_segmenting.structure_meta(meta)[source]

Extract meta informations (date, from, to, header) from string meta

Parameters
metastr
Returns
tuple(dict, string)
melusine.prepare_email.mail_segmenting.tag(string)[source]

Tags a string.

Parameters
stringstr,
Returns
tupleslist of tuples and boolean
melusine.prepare_email.mail_segmenting.tag_parts_message(text)[source]

Splits message into sentences, tags them and merges two sentences in a row having the same tag.

Parameters
textstr,
Returns
list of tuples
melusine.prepare_email.mail_segmenting.tag_sentence(sentence, default='BODY')[source]

Tag a sentence. If the sentence cannot be tagged it will tag the subsentences

Parameters
sentencestr,
Returns
list of tuplessentence, tag
melusine.prepare_email.mail_segmenting.tag_signature(row, token_threshold=5)[source]

Function to be called after the mail_segmenting function as it requires a “structured_body” column. This function detects parts of a message that qualify as “signature”. Exemples of parts qualifying as signature are sender name, company name, phone number, etc.

The methodology to detect a signature is the following: - Look for a THANKS or GREETINGS part indicating that the message is approaching the end - Check the length of the following message parts currently tagged as “BODY” - (The maximum number of words is specified through the variable “signature_token_threshold”) - If ALL the “ending parts” contain few words => tag them as “SIGNATURE” parts - Otherwise : cancel the signature tagging

Parameters
rowpd.Series

Row of an email DataFrame

Returns
structured_bodyUpdated structured body
Process Email Metadata melusine.prepare_email.metadata_engineering
class melusine.prepare_email.metadata_engineering.Dummifier(columns_to_dummify=['extension', 'dayofweek', 'hour', 'min', 'attachment_type'], copy=True)[source]

Bases: sklearn.base.BaseEstimator, sklearn.base.TransformerMixin

Transformer to dummifies categorial features and list of . Compatible with scikit-learn API.

fit(X, y=None)[source]

Store dummified features to avoid inconsistance of new data which could contain new labels (unknown from train data).

transform(X, y=None)[source]

Dummify features and keep only common labels with pretrained data.

class melusine.prepare_email.metadata_engineering.MetaAttachmentType(topn_extension=100)[source]

Bases: sklearn.base.BaseEstimator, sklearn.base.TransformerMixin

Transformer which creates ‘type’ feature extracted from regex in metadata. It extracts types of attached files.

Compatible with scikit-learn API.

static encode_type(row, top_ext)[source]
fit(X, y=None)[source]
static get_attachment_type(row)[source]

Gets type from attachment.

static get_top_attachment_type(X, n=100)[source]

Returns list of most common types of attachment.

transform(X)[source]

Encode extensions

class melusine.prepare_email.metadata_engineering.MetaDate(regex_date_format='\\w+ (\\d+) (\\w+) (\\d{4}) (\\d{2}) h (\\d{2})', date_format='%d/%m/%Y %H:%M')[source]

Bases: sklearn.base.BaseEstimator, sklearn.base.TransformerMixin

Transformer which creates new features from dates such as:
  • hour

  • minute

  • dayofweek

Compatible with scikit-learn API.

Parameters
date_formatstr, optional

Regex to extract date from text.

date_formatstr, optional

A date format.

date_formatting(row, regex_format)[source]

Set a date in the right format

fit(X, y=None)[source]

Unused method. Defined only for compatibility with scikit-learn API.

static get_dayofweek(row)[source]

Get day of the week from date

static get_hour(row)[source]

Get hour from date

static get_min(row)[source]
transform(X)[source]
class melusine.prepare_email.metadata_engineering.MetaExtension(topn_extension=100)[source]

Bases: sklearn.base.BaseEstimator, sklearn.base.TransformerMixin

Transformer which creates ‘extension’ feature extracted from regex in metadata. It extracts extension of mail adresses.

Compatible with scikit-learn API.

static encode_extension(row, top_ext)[source]
fit(X, y=None)[source]
static get_extension(row)[source]

Gets extension from email address.

static get_top_extension(X, n=100)[source]

Returns list of most common extensions.

transform(X)[source]

Encode extensions

Extract Email Body & Header melusine.prepare_email.body_header_extraction
melusine.prepare_email.body_header_extraction.extract_body(message_dict)[source]

Extracts the body from a message dictionary.

Parameters
message_dictdict
Returns
str
melusine.prepare_email.body_header_extraction.extract_header(message_dict)[source]

Extracts the header from a message dictionary.

Parameters
message_dictdict
Returns
str
melusine.prepare_email.body_header_extraction.extract_last_body(row)[source]

Extracts the body from the last message of the conversation. The conversation is structured as a dictionary.

To be used with methods such as: apply(func, axis=1) or apply_by_multiprocessing(func, axis=1, **kwargs).

Parameters
message_dictdict
Returns
str

Nlp_tools subpackage melusine.nlp_tools

TODO : ADD DESCRIPTION OF THE SUBPACKAGE

List of submodules

Tokenizer melusine.nlp_tools.tokenizer
class melusine.nlp_tools.tokenizer.RegexTokenizer(tokenizer_regex: str = '\\w+(?:[\\?\\-\\"_]\\w+)*', stopwords: Optional[List[str]] = None)[source]

Bases: object

FILENAME = 'tokenizer.json'

Tokenize text using a regex split pattern.

tokenize(text: str) Sequence[str][source]

Apply the full tokenization pipeline on a text. Parameters ———- text: str

Input text to be tokenized

Returns
tokens: Sequence[str]

List of tokens

class melusine.nlp_tools.tokenizer.Tokenizer(input_column='text', output_column='tokens', stop_removal=True)[source]

Bases: sklearn.base.BaseEstimator, sklearn.base.TransformerMixin

Tokenizer class to split text into tokens.

fit(X, y=None)[source]

Unused method. Defined only for compatibility with scikit-learn API.

transform(data)[source]

Applies tokenize method on pd.Dataframe.

Parameters
datapandas.DataFrame,

Data on which transformations are applied.

Returns
pandas.DataFrame
Phraser melusine.nlp_tools.phraser
class melusine.nlp_tools.phraser.Phraser(input_column='tokens', output_column='tokens', **phraser_args)[source]

Bases: object

Train and use a Gensim Phraser.

FILENAME = 'gensim_phraser_meta.pkl'
PHRASER_FILENAME = 'gensim_phraser'
fit(df, y=None)[source]
transform(df)[source]
Embedding melusine.nlp_tools.embedding
class melusine.nlp_tools.embedding.Embedding(tokens_column=None, workers=40, random_seed=42, iter=15, size=300, method='word2vec_cbow', min_count=100)[source]

Bases: object

Class to train embeddings with Word2Vec algorithm. Attributes ———- word2id: dict,

Word vocabulary (key: word, value: word_index.

embeddingGensim KeyedVector Instance,

Gensim KeyedVector Instance relative to the specific trained or imported embedding.

methodstr,
One of the following :
  • “word2vec_sg” : Trains a Word2Vec Embedding using the Skip-Gram method (usually takes a long time).

  • “word2vec_cbow” : Trains a Word2Vec Embedding using the Continuous Bag-Of-Words method.

  • “lsa_docterm” : Trains an Embedding by using an SVD on a Document-Term Matrix.

  • “lsa_tfidf” : Trains an Embedding by using an SVD on a TF-IDFized Document-Term Matrix.

train_paramsdict,
Additional parameters for the embedding training. Check the following documentation :
  • gensim.models.Word2Vec for Word2Vec Embeddings

  • sklearn.decomposition.TruncatedSVD for LSA Embeddings

If left untouched, the default training values will be kept from the aforementioned packages.

>>> from melusine.nlp_tools.embedding import Embedding
>>> embedding = Embedding()
>>> embedding.train(X)  # noqa
>>> embedding.save(filepath)  # noqa
>>> embedding = Embedding().load(filepath)  # noqa
load(filepath)[source]

Method to load Embedding object.

save(filepath)[source]

Method to save Embedding object.

train(X)[source]

Train embeddings with the desired word embedding algorithm (default is Word2Vec). Parameters ———- X : pd.Dataframe

Containing a clean body column.

train_word2vec()[source]

Fits a Word2Vec Embedding on the given documents, and update the embedding attribute.

Stemmer melusine.nlp_tools.stemmer
Lemmatizer melusine.nlp_tools.lemmatizer
Emoji Flagger melusine.nlp_tools.emoji_flagger
class melusine.nlp_tools.emoji_flagger.DeterministicEmojiFlagger(input_column: str = 'last_body', output_column: str = 'last_body', flag_emoji: str = ' flag_emoji_ ')[source]

Bases: object

FILENAME = 'emoji_flagger.json'
fit(df, y=None)[source]
transform(df)[source]

Summarizer subpackage melusine.summarizer

TODO : ADD DESCRIPTION OF THE SUBPACKAGE

List of submodules

KeywordsGenerator melusine.summarizer.keywords_generator
class melusine.summarizer.keywords_generator.KeywordsGenerator(max_tfidf_features=10000, keywords=['keyword'], stopwords=['au', 'aux', 'avec', 'ce', 'ces', 'dans', 'de', 'des', 'du', 'elle', 'en', 'et', 'eux', 'il', 'je', 'la', 'le', 'leur', 'lui', 'ma', 'mais', 'me', 'même', 'mes', 'moi', 'mon', 'ne', 'nos', 'notre', 'nous', 'on', 'ou', 'par', 'pas', 'pour', 'qu', 'que', 'qui', 'sa', 'se', 'ses', 'son', 'sur', 'ta', 'te', 'tes', 'toi', 'ton', 'tu', 'un', 'une', 'vos', 'votre', 'vous', 'c', 'd', 'j', 'l', 'à', 'm', 'n', 's', 't', 'y', 'été', 'étée', 'étées', 'étés', 'étant', 'étante', 'étants', 'étantes', 'suis', 'es', 'est', 'sommes', 'êtes', 'sont', 'serai', 'seras', 'sera', 'serons', 'serez', 'seront', 'serais', 'serait', 'serions', 'seriez', 'seraient', 'étais', 'était', 'étions', 'étiez', 'étaient', 'fus', 'fut', 'fûmes', 'fûtes', 'furent', 'sois', 'soit', 'soyons', 'soyez', 'soient', 'fusse', 'fusses', 'fût', 'fussions', 'fussiez', 'fussent', 'ayant', 'ayante', 'ayantes', 'ayants', 'eu', 'eue', 'eues', 'eus', 'ai', 'as', 'avons', 'avez', 'ont', 'aurai', 'auras', 'aura', 'aurons', 'aurez', 'auront', 'aurais', 'aurait', 'aurions', 'auriez', 'auraient', 'avais', 'avait', 'avions', 'aviez', 'avaient', 'eut', 'eûmes', 'eûtes', 'eurent', 'aie', 'aies', 'ait', 'ayons', 'ayez', 'aient', 'eusse', 'eusses', 'eût', 'eussions', 'eussiez', 'eussent', 'nan', 'aaliyah', 'aalyah', 'aaron', 'abbas', 'abbes', 'abby', 'abbygaelle', 'abbygaelle', 'abd', 'abd-el', 'abdallah', 'abdel', 'abdel-aziz', 'abdel-hakim', 'abdel-kader', 'abdel-karim', 'abdel-malik', 'abdelali', 'abdelatif', 'abdelaziz', 'abdelghani', 'abdelhadi', 'abdelhafid', 'abdelhak', 'abdelhakim', 'abdelhalim', 'abdelhamid', 'abdelilah', 'abdeljalil', 'abdelkader', 'abdelkarim', 'abdelkrim', 'abdellah', 'abdellatif', 'abdelmadjid', 'abdelmajid', 'abdelmalek', 'abdelmalik', 'abdelnour', 'abdelrahim', 'abdelrahman', 'abdelrahmane', 'abdelrani', 'abdelwahab', 'abdenour', 'abderahim', 'abderahman', 'abderahmane', 'abderrahim', 'abderrahman', 'abderrahmane', 'abderrazak', 'abdeslam', 'abdessamad', 'abdon', 'abdou', 'abdoul', 'abdoulaye', 'abdoullah', 'abdourahmane', 'abdul', 'abdullah', 'abdurrahman', 'abed', 'abel', 'abigael', 'abigaelle', 'abigail', 'abigael', 'abigaelle', 'abigail', 'abilio', 'abir', 'abla', 'abou', 'aboubacar', 'aboubakar', 'aboubakr', 'abraham', 'aby', 'abygael', 'abygaelle', 'abygael', 'abygaelle', 'acelya', 'achille', 'achour', 'achraf', 'ada', 'adalbert', 'adaline', 'adam', 'adama', 'adame', 'adan', 'adda', 'adel', 'adela', 'adelaide', 'adelaide', 'adele', 'adelia', 'adelie', 'adelin', 'adelina', 'adeline', 'adelino', 'adelphine', 'adelyne', 'adem', 'aden', 'adham', 'adib', 'adil', 'adile', 'adja', 'adnan', 'adnane', 'adolphe', 'adolphine', 'adonis', 'adrian', 'adriana', 'adriane', 'adrianna', 'adriano', 'adriel', 'adrien', 'adrienne', 'adele', 'adelaide', 'adelia', 'adelie', 'aedan', 'ael', 'aela', 'aelia', 'aelig', 'aelys', 'afaf', 'afif', 'afonso', 'agate', 'agatha', 'agathe', 'aglae', 'aglaee', 'aglae', 'agnan', 'agnes', 'agostinho', 'agostino', 'aharon', 'ahcene', 'ahlam', 'ahlame', 'ahlem', 'ahmad', 'ahmadou', 'ahmed', 'ahmet', 'aicha', 'aida', 'aidan', 'aiden', 'aileen', 'aima', 'aimad', 'aiman', 'aimane', 'aimen', 'aimeric', 'aimery', 'aimie', 'aimy', 'aina', 'ainhoa', 'ainoa', 'aisha', 'aissa', 'aissam', 'aissata', 'aissatou', 'aisse', 'aissetou', 'aiya', 'aiyana', 'akif', 'akil', 'akila', 'akim', 'akima', 'akin', 'akli', 'akram', 'aksel', 'aksil', 'alaa', 'alae', 'alaia', 'alain', 'alais', 'alan', 'alana', 'alane', 'alanis', 'alann', 'alaric', 'alassane', 'alais', 'alba', 'alban', 'albane', 'albanie', 'alberic', 'albert', 'alberta', 'alberte', 'albertine', 'alberto', 'albin', 'albina', 'albine', 'albino', 'alcide', 'alcime', 'alda', 'aldo', 'aldric', 'alec', 'alejandro', 'alek', 'aleksandar', 'aleksandra', 'alen', 'alena', 'alenzo', 'alesia', 'alessandra', 'alessandro', 'alessia', 'alessio', 'aleth', 'alex', 'alexa', 'alexander', 'alexandra', 'alexandre', 'alexandrine', 'alexandro', 'alexandru', 'alexane', 'alexanne', 'alexi', 'alexia', 'alexian', 'alexiane', 'alexie', 'alexina', 'alexine', 'alexis', 'alexy', 'alexya', 'alexys', 'aleyna', 'alfonso', 'alfred', 'alfreda', 'alfredine', 'alfredo', 'ali', 'alia', 'alice', 'alicia', 'alicya', 'alida', 'alienor', 'aliette', 'alim', 'alima', 'alin', 'alina', 'aline', 'aliocha', 'aliou', 'alioune', 'alisa', 'alise', 'alisee', 'alisha', 'alison', 'alisone', 'alissa', 'alissia', 'alisson', 'alistair', 'alix', 'alixe', 'alixia', 'aliya', 'aliyah', 'aliza', 'alize', 'alizea', 'alizee', 'alize', 'alizee', 'alienor', 'allain', 'allan', 'allen', 'allison', 'allisson', 'allya', 'allyah', 'allyson', 'alma', 'almire', 'alois', 'aloise', 'alon', 'alonso', 'alonzo', 'aloys', 'aloyse', 'alois', 'aloise', 'alper', 'alperen', 'alpha', 'alphonse', 'alphonsine', 'alric', 'alrick', 'althea', 'alvaro', 'alvin', 'alvina', 'alvine', 'alvyn', 'alwena', 'alwenna', 'alwin', 'aly', 'alya', 'alyah', 'alyana', 'alycia', 'alyette', 'alys', 'alysee', 'alyson', 'alyssa', 'alyssia', 'alysson', 'alyzee', 'alena', 'amable', 'amadeo', 'amadou', 'amael', 'amaelle', 'amaia', 'amal', 'amale', 'amalia', 'amalya', 'amance', 'amand', 'amanda', 'amande', 'amandine', 'amane', 'amani', 'amar', 'amara', 'amaria', 'amaryllis', 'amaury', 'amaya', 'amayas', 'amael', 'amaia', 'amber', 'ambre', 'ambrine', 'ambroise', 'ambroisine', 'amed', 'amede', 'amedee', 'amel', 'amele', 'amelia', 'amelie', 'amelien', 'amelina', 'ameline', 'amelle', 'amely', 'amelya', 'americo', 'amicie', 'amin', 'amina', 'aminata', 'amine', 'amir', 'amira', 'amjad', 'ammar', 'amna', 'amor', 'amory', 'amos', 'amour', 'amparo', 'amy', 'amelia', 'amelie', 'amelya', 'ana', 'ana-maria', 'anabel', 'anabela', 'anabella', 'anabelle', 'anae', 'anael', 'anaele', 'anaelle', 'anais', 'anaise', 'anakin', 'anas', 'anass', 'anasse', 'anastase', 'anastasia', 'anastasie', 'anastassia', 'anasthasia', 'anasthasie', 'anatole', 'anaya', 'anae', 'anae', 'anael', 'anaelle', 'anaia', 'anais', 'ancelin', 'anderson', 'andgel', 'andoche', 'andoni', 'andrea', 'andreane', 'andreas', 'andreea', 'andrei', 'andreia', 'andres', 'andrew', 'andria', 'andry', 'andre', 'andrea', 'andreas', 'andree', 'andy', 'anes', 'aness', 'anette', 'anfel', 'ange', 'ange-marie', 'angel', 'angela', 'angele', 'angelica', 'angelie', 'angelika', 'angelin', 'angelina', 'angeline', 'angelino', 'angelique', 'angelita', 'angella', 'angelle', 'angelo', 'angie', 'angus', 'angy', 'angele', 'angela', 'angelina', 'angeline', 'angelique', 'angelo', 'anh', 'ani', 'ania', 'anic', 'anice', 'anicet', 'anicette', 'anicia', 'anick', 'anie', 'aniela', 'anik', 'anil', 'anir', 'anis', 'anisa', 'anise', 'aniss', 'anissa', 'anisse', 'anissia', 'anita', 'anja', 'anjali', 'ann', 'anna', 'anna-maria', 'anna-rose', 'annabel', 'annabella', 'annabelle', 'annael', 'annaelle', 'annah', 'annaick', 'annaig', 'annaik', 'annaelle', 'anne', 'anne-carole', 'anne-caroline', 'anne-catherine', 'anne-charlotte', 'anne-christine', 'anne-claire', 'anne-claude', 'anne-cecile', 'anne-celine', 'anne-elisabeth', 'anne-elise', 'anne-emmanuelle', 'anne-fleur', 'anne-flore', 'anne-florence', 'anne-france', 'anne-francoise', 'anne-frederique', 'anne-gaelle', 'anne-gaelle', 'anne-helene', 'anne-isabelle', 'anne-julie', 'anne-laure', 'anne-laurence', 'anne-line', 'anne-lise', 'anne-louise', 'anne-lucie', 'anne-lyse', 'anne-marie', 'anne-rose', 'anne-sarah', 'anne-solene', 'anne-sophie', 'anne-sylvie', 'anne-therese', 'anne-valerie', 'anne-yvonne', 'anneliese', 'annelise', 'annelyse', 'annet', 'annette', 'annic', 'annick', 'annie', 'annie-claude', 'annie-france', 'annik', 'annissa', 'annita', 'annoncia', 'annonciade', 'anny', 'anonyme', 'anouar', 'anouchka', 'anouck', 'anouk', 'anselme', 'anta', 'anthea', 'anthelme', 'anthime', 'anthonin', 'anthony', 'anthyme', 'anthea', 'antoine', 'antoinette', 'anton', 'antone', 'antonella', 'antoni', 'antonia', 'antonie', 'antonietta', 'antonin', 'antonina', 'antonine', 'antonino', 'antonio', 'antonn', 'antony', 'antton', 'anwar', 'any', 'anya', 'anys', 'anyssa', 'aodren', 'apoline', 'apollinaire', 'apolline', 'apollonie', 'appolinaire', 'appoline', 'appolonie', 'april', 'aram', 'aramis', 'arcade', 'archange', 'archibald', 'arda', 'areski', 'arezki', 'argan', 'ari', 'aria', 'ariana', 'ariane', 'arianne', 'arie', 'ariel', 'arielle', 'arif', 'arij', 'aris', 'ariste', 'aristide', 'arlene', 'arlette', 'arman', 'armance', 'armand', 'armande', 'armandine', 'armando', 'armantine', 'armel', 'armele', 'armelle', 'armonie', 'armony', 'arnaud', 'arnauld', 'arnault', 'arno', 'arnold', 'arnould', 'aron', 'arone', 'aronn', 'arsene', 'arsene', 'arthemise', 'arthur', 'arthus', 'artur', 'arturo', 'artus', 'arwa', 'arwen', 'arwenn', 'ary', 'arya', 'arzu', 'ascension', 'ashley', 'ashton', 'asia', 'asiya', 'aslan', 'asli', 'asma', 'asmaa', 'asmae', 'asmin', 'assa', 'assad', 'assane', 'assetou', 'assia', 'assil', 'assile', 'assim', 'assiya', 'assma', 'assunta', 'assya', 'aston', 'astou', 'astrid', 'astride', 'asya', 'atef', 'athanase', 'athena', 'athenais', 'athina', 'athena', 'athenais', 'atika', 'atilla', 'attilio', 'aubane', 'aubert', 'aubin', 'aubry', 'aude', 'aude-marie', 'audeline', 'audran', 'audray', 'audren', 'audrey', 'audric', 'audrina', 'audry', 'augusta', 'auguste', 'augustin', 'augustina', 'augustine', 'augusto', 'aurane', 'aure', 'aurea', 'aurel', 'aurele', 'aurelia', 'aurelie', 'aurelien', 'aureline', 'aurelio', 'aurely', 'auria', 'aurian', 'auriana', 'auriane', 'aurianne', 'aurora', 'aurore', 'aurele', 'aurelia', 'aurelie', 'aurelien', 'austin', 'auxane', 'auxence', 'ava', 'aveline', 'avi', 'avit', 'awa', 'awen', 'awena', 'axel', 'axele', 'axelle', 'aya', 'ayaan', 'ayah', 'ayan', 'ayana', 'ayanna', 'ayat', 'ayaz', 'aydan', 'ayden', 'aydin', 'ayhan', 'ayla', 'aylan', 'aylane', 'ayleen', 'aylin', 'ayline', 'ayman', 'aymane', 'aymar', 'aymeline', 'aymen', 'aymene', 'aymeric', 'aymerick', 'aymerik', 'ayna', 'aynur', 'ayoub', 'ayoube', 'ayron', 'ayrton', 'ayse', 'aysegul', 'aysel', 'aysenur', 'aysun', 'ayyoub', 'azad', 'azdine', 'azeddine', 'azedine', 'azelie', 'azeline', 'azilis', 'aziliz', 'azilys', 'aziz', 'aziza', 'azize', 'azra', 'azzeddine', 'azzedine', 'azziz', 'azelie', 'aelia', 'aelys', 'ael', 'aelys', 'aicha', 'aida', 'aidan', 'aiden', 'aina', 'ainhoa', 'aisha', 'aissa', 'aissata', 'aissatou', 'baba', 'babacar', 'babette', 'babylas', 'bachir', 'bader', 'badia', 'badis', 'badr', 'badra', 'badre', 'badreddine', 'badredine', 'bafode', 'bahar', 'bahia', 'bahija', 'bakari', 'bakary', 'bakhta', 'balkis', 'balla', 'balthazar', 'bandiougou', 'bao', 'baptiste', 'baptistin', 'baptistine', 'baran', 'barbara', 'barbe', 'baris', 'barnabe', 'barnabe', 'barthelemy', 'barthelemy', 'basil', 'basile', 'basma', 'bassem', 'bassma', 'bastian', 'bastien', 'bathilde', 'batiste', 'batuhan', 'baudoin', 'baudouin', 'baya', 'bayane', 'bayram', 'bayron', 'bazile', 'beatrix', 'beatriz', 'bechir', 'bekir', 'belaid', 'belgacem', 'belinay', 'belinda', 'belkacem', 'bella', 'ben', 'benamar', 'benedict', 'benedicte', 'benito', 'benjamin', 'benjamine', 'benji', 'benjy', 'benoist', 'benoit', 'benoite', 'benoni', 'benoit', 'beranger', 'berangere', 'berat', 'berenger', 'berengere', 'berenice', 'berfin', 'berivan', 'berkan', 'berkay', 'bernadette', 'bernadin', 'bernadine', 'bernard', 'bernardin', 'bernardine', 'bernardo', 'bertha', 'berthe', 'berthilde', 'bertille', 'bertin', 'bertrand', 'bertrande', 'beryl', 'besma', 'betina', 'betsy', 'bettina', 'betty', 'betul', 'beverley', 'beverly', 'beya', 'beyza', 'bianca', 'bibi', 'bienaime', 'bilal', 'bilel', 'billal', 'billel', 'billie', 'billy', 'bineta', 'binta', 'bintou', 'bixente', 'blaise', 'blanche', 'blandine', 'blessing', 'bleuenn', 'bleuette', 'blondine', 'bluette', 'bob', 'bocar', 'bochra', 'bogdan', 'boleslaw', 'bonaventure', 'boniface', 'bonnie', 'boris', 'boualem', 'boubacar', 'boubakar', 'boubaker', 'boubou', 'bouchra', 'boumediene', 'boumedienne', 'bouziane', 'bouzid', 'brad', 'bradley', 'brady', 'brahim', 'brahima', 'brandon', 'brandy', 'brayan', 'brayton', 'brenda', 'brendan', 'brendon', 'brewen', 'briac', 'brian', 'briana', 'brianna', 'brice', 'brieg', 'brieuc', 'brigitte', 'britany', 'britney', 'brittany', 'bronislaw', 'bronislawa', 'brooklyn', 'bruce', 'bruna', 'brune', 'brunehilde', 'brunella', 'brunette', 'bruno', 'bryan', 'bryana', 'bryanna', 'bryce', 'btissam', 'bunyamin', 'burak', 'busra', 'byron', 'beatrice', 'belinda', 'benedicte', 'berangere', 'berengere', 'beryl', 'berenice', 'caitlin', 'caleb', 'cali', 'calie', 'calista', 'calixte', 'callie', 'callista', 'calliste', 'cally', 'calogera', 'calogero', 'calvin', 'caly', 'calypso', 'calysta', 'camel', 'camelia', 'camellia', 'cameron', 'camerone', 'camil', 'camila', 'camilia', 'camilla', 'camille', 'camillia', 'camilo', 'camron', 'camelia', 'can', 'canan', 'candice', 'candida', 'candide', 'candido', 'candie', 'candy', 'candyce', 'candys', 'canelle', 'cannelle', 'cansu', 'cantin', 'capucine', 'cara', 'carene', 'carina', 'carine', 'carinne', 'carl', 'carla', 'carla-marie', 'carline', 'carlo', 'carlos', 'carlotta', 'carly', 'carlyne', 'carmel', 'carmela', 'carmele', 'carmelina', 'carmelle', 'carmelo', 'carmen', 'carmine', 'carol', 'carol-anne', 'carolane', 'carole', 'carole-anne', 'carolina', 'caroline', 'caroll', 'carolle', 'carolyn', 'carolyne', 'casey', 'casimir', 'casimira', 'cassandra', 'cassandre', 'cassidy', 'cassie', 'cassiopee', 'cassiopee', 'cassy', 'castille', 'cataldo', 'cataleya', 'catalina', 'catarina', 'caterina', 'catharina', 'catheline', 'catherina', 'catherine', 'cathia', 'cathie', 'cathleen', 'cathy', 'catia', 'caty', 'cecilia', 'cecilie', 'cecilien', 'cedric', 'cedrick', 'cedrik', 'cedrine', 'celena', 'celeste', 'celestin', 'celestine', 'celia', 'celian', 'celiane', 'celie', 'celien', 'celina', 'celine', 'celinie', 'celio', 'cellia', 'celya', 'celyan', 'cem', 'cendrine', 'cengiz', 'ceren', 'cerena', 'cerine', 'cerise', 'cesaire', 'cesar', 'cesarine', 'ceyda', 'ceylan', 'ceylin', 'chabane', 'chad', 'chaden', 'chadi', 'chadia', 'chafia', 'chafik', 'chafika', 'chahd', 'chahid', 'chahin', 'chahinaz', 'chahine', 'chahinez', 'chahineze', 'chahrazad', 'chahrazed', 'chaima', 'chaimaa', 'chaimae', 'chaina', 'chainez', 'chaineze', 'chakib', 'chakir', 'chams', 'chamseddine', 'chana', 'chanel', 'chanelle', 'chanez', 'chantal', 'chantale', 'chaouki', 'charif', 'charlaine', 'charleen', 'charlelie', 'charlemagne', 'charlene', 'charles', 'charles-alexandre', 'charles-andre', 'charles-antoine', 'charles-edouard', 'charles-elie', 'charles-emmanuel', 'charles-henri', 'charles-henry', 'charlesia', 'charlette', 'charley', 'charli', 'charlie', 'charline', 'charlise', 'charlize', 'charlot', 'charlotte', 'charly', 'charlyne', 'charlene', 'chayma', 'chaymaa', 'chaymae', 'chaima', 'cheick', 'cheikh', 'cheima', 'chelsea', 'chelsy', 'chems', 'chems-eddine', 'chemseddine', 'cherazade', 'cherif', 'cherifa', 'cherine', 'cheryl', 'cheryne', 'cheyenne', 'cheyma', 'chiara', 'chimene', 'chiraz', 'chirine', 'chjara', 'chlea', 'chloe', 'chloee', 'chloe', 'chloe', 'chlea', 'chokri', 'chouaib', 'choukri', 'chretien', 'chris', 'chrislaine', 'christ', 'christa', 'christal', 'christel', 'christelle', 'christian', 'christiana', 'christiane', 'christianne', 'christiano', 'christie', 'christina', 'christine', 'christofer', 'christophe', 'christopher', 'christy', 'christele', 'chrys', 'chrystal', 'chrystel', 'chrystele', 'chrystelle', 'chrystele', 'cherine', 'ciana', 'ciara', 'cidalia', 'cigdem', 'cihan', 'cilia', 'cindie', 'cindy', 'cinthia', 'cinthya', 'circe', 'cisse', 'cladie', 'clair', 'claire', 'claire-lise', 'claire-marie', 'clairette', 'clara', 'clarence', 'clarice', 'clarissa', 'clarisse', 'clarys', 'clarysse', 'claude', 'claudette', 'claudia', 'claudie', 'claudine', 'claudio', 'claudius', 'claudy', 'clayton', 'clea', 'clelia', 'clelie', 'clemence', 'clement', 'clementine', 'cleo', 'cleophee', 'clervie', 'clet', 'cliff', 'clio', 'clodomir', 'cloe', 'clorinde', 'clotaire', 'clothilde', 'clotilde', 'clovis', 'cloe', 'clyde', 'clea', 'clelia', 'clelie', 'clemence', 'clement', 'clementine', 'cleo', 'cleophee', 'cody', 'colas', 'colbert', 'coleen', 'colette', 'colin', 'coline', 'colleen', 'collette', 'colline', 'collyne', 'colombe', 'colombine', 'colyne', 'come', 'concepcion', 'conception', 'concetta', 'conchita', 'conrad', 'constance', 'constantin', 'constantine', 'consuelo', 'cora', 'coralie', 'coraline', 'coraly', 'coralyne', 'corantin', 'corenthin', 'corentin', 'corentine', 'corine', 'corinne', 'corneille', 'cornelia', 'cornelie', 'corrine', 'corto', 'cory', 'cosette', 'cosimo', 'cosme', 'coumba', 'crepin', 'crescent', 'cristal', 'cristel', 'cristelle', 'cristian', 'cristiano', 'cristina', 'cristine', 'cristobal', 'crystal', 'crystale', 'crystel', 'crystelle', 'curt', 'curtis', 'cyana', 'cyane', 'cyann', 'cybelia', 'cylia', 'cylian', 'cyndel', 'cyndelle', 'cyndi', 'cyndia', 'cyndie', 'cynthia', 'cyntia', 'cyprien', 'cyprienne', 'cyr', 'cyriac', 'cyrian', 'cyriane', 'cyriaque', 'cyriel', 'cyrielle', 'cyril', 'cyrill', 'cyrille', 'cyrine', 'cyrus', 'czeslaw', 'czeslawa', 'cecile', 'cecilia', 'cedric', 'cedrick', 'celeste', 'celestin', 'celestine', 'celia', 'celian', 'celie', 'celina', 'celine', 'celya', 'celyan', 'celena', 'cesar', 'come', 'dado', 'dahbia', 'dahlia', 'daina', 'daisy', 'dalia', 'dalida', 'dalil', 'dalila', 'dalla', 'dalya', 'damaris', 'damian', 'damien', 'damienne', 'damiens', 'damla', 'damon', 'dan', 'dana', 'danae', 'danae', 'dani', 'dania', 'danie', 'daniel', 'daniela', 'daniele', 'daniella', 'danielle', 'danila', 'danilo', 'daniele', 'dann', 'danny', 'dante', 'danton', 'dany', 'danya', 'danyl', 'daoud', 'daouda', 'daphne', 'daphnee', 'daphne', 'daphnee', 'dara', 'darell', 'daren', 'daria', 'darina', 'darine', 'dario', 'daris', 'darius', 'darko', 'darlene', 'darren', 'darryl', 'daryl', 'dastan', 'dave', 'david', 'david-alexandre', 'davide', 'davina', 'davis', 'davy', 'dawson', 'dayan', 'dayana', 'dayane', 'dayann', 'dayna', 'dayron', 'dayvon', 'dean', 'debora', 'deborah', 'debra', 'declan', 'defne', 'dejan', 'delhia', 'delia', 'delinda', 'delphin', 'delphine', 'delya', 'demba', 'denia', 'denis', 'denisa', 'denise', 'deniz', 'denize', 'dennis', 'denovan', 'denys', 'denyse', 'denzel', 'derek', 'derya', 'desir', 'desiree', 'destiny', 'deva', 'deven', 'devon', 'devran', 'devy', 'dewi', 'deyan', 'diaba', 'diadie', 'diana', 'diane', 'diarra', 'didier', 'diego', 'dienaba', 'dieneba', 'dieudonne', 'dieynaba', 'dikra', 'dila', 'dilan', 'dilara', 'dilek', 'dilhan', 'dimitri', 'dimitry', 'dina', 'dinah', 'dinis', 'dino', 'diogo', 'divine', 'diyar', 'diego', 'djalil', 'djamal', 'djamel', 'djamil', 'djamila', 'djamilla', 'django', 'djason', 'djawad', 'djayan', 'djebril', 'djelloul', 'djemal', 'djemel', 'djemila', 'djena', 'djenaba', 'djeneba', 'djenna', 'djessim', 'djessy', 'djibril', 'djiby', 'djilali', 'djillali', 'djimmy', 'djina', 'djino', 'djulian', 'dogan', 'doha', 'dolly', 'dolores', 'dolores', 'domenica', 'domenico', 'domingo', 'domingos', 'dominic', 'dominica', 'dominique', 'domitille', 'don', 'dona', 'donald', 'donat', 'donatella', 'donatien', 'donatienne', 'donato', 'donavan', 'donia', 'donna', 'donovan', 'donya', 'dora', 'dorcas', 'doreen', 'doria', 'dorian', 'doriana', 'doriane', 'doriann', 'dorianne', 'dorina', 'dorine', 'doris', 'dorothee', 'dorothy', 'dorothee', 'dorra', 'dorsaf', 'doryan', 'doua', 'douaa', 'douglas', 'dounia', 'dounya', 'dov', 'dragan', 'dragana', 'dramane', 'driss', 'duarte', 'duncan', 'dune', 'duygu', 'dwayne', 'dyclan', 'dylan', 'dylane', 'dylann', 'dyna', 'debora', 'deborah', 'delia', 'desire', 'desiree', 'ebru', 'ecrin', 'eda', 'edan', 'edanur', 'eddie', 'eddine', 'eddy', 'edeline', 'edem', 'eden', 'edene', 'edenn', 'edern', 'edgar', 'edgard', 'edin', 'edith', 'edithe', 'ediz', 'edme', 'edmee', 'edmond', 'edmonde', 'edna', 'edouard', 'edouardo', 'edson', 'eduard', 'eduardo', 'edvard', 'edvin', 'edward', 'edwige', 'edwin', 'edwina', 'edy', 'edene', 'efe', 'efflam', 'eglantine', 'egon', 'eileen', 'eitan', 'ekrem', 'ela', 'elaia', 'elaine', 'elana', 'elanur', 'elaura', 'elaia', 'elda', 'elea', 'eleana', 'eleane', 'eleanor', 'eleanore', 'elen', 'elena', 'eleonor', 'eleonora', 'eleonore', 'elfie', 'elfriede', 'elfy', 'elhadj', 'elhadji', 'elham', 'eli', 'elia', 'eliakim', 'eliam', 'elian', 'eliana', 'eliane', 'elianne', 'elias', 'eliaz', 'elicia', 'elida', 'elidjah', 'elie', 'eliel', 'elien', 'elies', 'eliette', 'eliezer', 'elif', 'elijah', 'elikya', 'elin', 'elina', 'eline', 'elinor', 'elio', 'elior', 'eliora', 'eliot', 'elioth', 'eliott', 'elisa', 'elisabete', 'elisabeth', 'elise', 'elisee', 'elisha', 'elissa', 'eliya', 'eliz', 'eliza', 'elizabete', 'elizabeth', 'elizio', 'ella', 'ellen', 'ellia', 'ellie', 'elliot', 'elliott', 'elly', 'ellyn', 'ellyne', 'elma', 'elmire', 'eloa', 'eloan', 'eloane', 'eloann', 'elodie', 'elody', 'elohim', 'eloi', 'eloine', 'elois', 'eloise', 'elona', 'elora', 'elorri', 'elouan', 'elouane', 'elouann', 'elouen', 'elowan', 'eloy', 'eloise', 'elric', 'elsa', 'else', 'elsie', 'elsy', 'elvan', 'elvin', 'elvina', 'elvira', 'elvire', 'elvis', 'elwan', 'elwenn', 'ely', 'elya', 'elyah', 'elyan', 'elyana', 'elyane', 'elyas', 'elyass', 'elyes', 'elyess', 'elyette', 'elyn', 'elyna', 'elyne', 'elynn', 'elyo', 'elyot', 'elyott', 'elysa', 'elyse', 'elysee', 'elyssa', 'elza', 'elea', 'eleana', 'eleanor', 'eleanore', 'elena', 'eleonore', 'ema', 'emanuel', 'emanuela', 'emanuelle', 'emel', 'emelia', 'emelie', 'emeline', 'emelyne', 'emerance', 'emeraude', 'emeric', 'emerick', 'emerik', 'emerson', 'emery', 'emi', 'emie', 'emil', 'emile', 'emilia', 'emiliano', 'emilie', 'emilien', 'emilienne', 'emilio', 'emily', 'emin', 'emina', 'emine', 'emir', 'emira', 'emirhan', 'emma', 'emmanuel', 'emmanuela', 'emmanuele', 'emmanuella', 'emmanuelle', 'emmeline', 'emmie', 'emmy', 'emna', 'emrah', 'emre', 'emrick', 'emrys', 'emy', 'ena', 'enael', 'enael', 'enaelle', 'encarnacion', 'endy', 'enea', 'eneko', 'enes', 'engin', 'enguerran', 'enguerrand', 'enis', 'enki', 'ennio', 'enoa', 'enogat', 'enoha', 'enola', 'enora', 'enorah', 'enric', 'enrick', 'enrico', 'enrique', 'ensar', 'enya', 'enza', 'enzo', 'enea', 'eole', 'eolia', 'ephraim', 'ephrem', 'epiphane', 'epiphanie', 'eponine', 'eray', 'ercan', 'erdem', 'erell', 'eren', 'erhan', 'eric', 'erica', 'erich', 'erick', 'ericka', 'erik', 'erika', 'erin', 'erina', 'erine', 'erkan', 'erna', 'ernest', 'ernestine', 'ernesto', 'erol', 'eros', 'ersin', 'erva', 'ervin', 'erwan', 'erwann', 'erwin', 'eryn', 'eryne', 'erynn', 'esaie', 'esila', 'esin', 'eslem', 'esma', 'esmanur', 'esmeralda', 'esmee', 'esperance', 'esperanza', 'esra', 'essia', 'esteban', 'estebane', 'estebann', 'estel', 'estella', 'estelle', 'ester', 'estevan', 'esteve', 'esther', 'estrella', 'esteban', 'etan', 'etann', 'ethan', 'ethane', 'ethann', 'ethel', 'etienne', 'etiennette', 'etiennise', 'eudes', 'eudoxie', 'eugenia', 'eugenie', 'eugenio', 'eugene', 'eugenie', 'eulalie', 'euloge', 'eunice', 'euphemie', 'euphrasie', 'eurydice', 'eusebe', 'eusebio', 'eustache', 'eustase', 'eva', 'evaelle', 'evan', 'evana', 'evane', 'evangeline', 'evann', 'evans', 'evariste', 'eve', 'eve-marie', 'evelina', 'eveline', 'evelyn', 'evelyne', 'evelyse', 'even', 'evenor', 'evens', 'evie', 'evin', 'evodie', 'evrard', 'evy', 'ewa', 'ewald', 'ewan', 'ewann', 'ewen', 'ewenn', 'expedit', 'expedite', 'eya', 'eyal', 'eyden', 'eylem', 'eymen', 'eymeric', 'eytan', 'eythan', 'eyup', 'ezechiel', 'ezekiel', 'ezel', 'ezequiel', 'ezgi', 'ezilda', 'ezio', 'ezra', 'ezzio', 'ezechiel', 'fabian', 'fabien', 'fabienne', 'fabio', 'fabiola', 'fabrice', 'fabrizio', 'fadel', 'fadela', 'fadi', 'fadia', 'fadil', 'fadila', 'fadime', 'fadoua', 'fadwa', 'fady', 'fael', 'fahad', 'fahd', 'fahed', 'fahim', 'fahima', 'faical', 'fairouz', 'faisal', 'faissal', 'faith', 'faiz', 'faiza', 'fallon', 'fallone', 'falone', 'fanch', 'fanchon', 'fanelie', 'fanette', 'fanie', 'fannie', 'fanny', 'fanta', 'fantin', 'fantine', 'fany', 'faouzi', 'faouzia', 'farah', 'farel', 'farell', 'fares', 'farha', 'farid', 'farida', 'faride', 'faris', 'farouk', 'farrah', 'fares', 'fatah', 'faten', 'fathi', 'fathia', 'fatia', 'fatih', 'fatiha', 'fatim', 'fatima', 'fatima-zahra', 'fatima-zohra', 'fatimata', 'fatine', 'fatma', 'fatna', 'fatou', 'fatouma', 'fatoumata', 'faty', 'faustin', 'faustine', 'fauve', 'fawzi', 'faycal', 'faycel', 'fayrouz', 'faysal', 'fayssal', 'fayza', 'faycal', 'fazia', 'faiza', 'federico', 'felice', 'felicia', 'felicie', 'felicien', 'felicienne', 'felicite', 'felipe', 'felix', 'fella', 'fenda', 'ferdi', 'ferdinand', 'ferdinande', 'ferhat', 'feriel', 'fernand', 'fernanda', 'fernande', 'fernando', 'ferreol', 'feryel', 'fethi', 'feyza', 'fidele', 'fideline', 'fidji', 'filip', 'filipe', 'filippo', 'filiz', 'filomena', 'fily', 'fiona', 'fiorella', 'firas', 'firat', 'firdaous', 'firdaws', 'firmin', 'firmine', 'flavia', 'flavian', 'flavie', 'flavien', 'flavienne', 'flavio', 'flavy', 'fleurine', 'fleury', 'flora', 'floran', 'florane', 'flore', 'floreal', 'florelle', 'florence', 'florent', 'florentin', 'florentine', 'florestan', 'florestine', 'florette', 'floria', 'florian', 'floriana', 'floriane', 'florianne', 'floriant', 'florida', 'florie', 'florient', 'florimond', 'florin', 'florina', 'florinda', 'florine', 'floris', 'florise', 'floryan', 'fode', 'fodie', 'fortuna', 'fortunee', 'fouad', 'foucauld', 'foued', 'foune', 'fouzi', 'fouzia', 'franc', 'franca', 'france-lise', 'franceline', 'francelise', 'francesca', 'francesco', 'francette', 'francia', 'franciane', 'francine', 'francis', 'francisca', 'francisco', 'francise', 'francisque', 'franck', 'franckie', 'francky', 'franco', 'francois', 'frank', 'frankie', 'franklin', 'franky', 'frantz', 'franz', 'francois', 'francois-joseph', 'francois-marie', 'francois-regis', 'francois-xavier', 'francoise', 'fred', 'freddy', 'frederick', 'frederico', 'frederik', 'fredy', 'frida', 'fridolin', 'frieda', 'fritz', 'frederic', 'frederick', 'frederique', 'fulbert', 'funda', 'furkan', 'felicia', 'felicie', 'felicien', 'felix', 'feriel', 'gabi', 'gabie', 'gabin', 'gabriel', 'gabriela', 'gabriele', 'gabriella', 'gabrielle', 'gabryel', 'gaby', 'gad', 'gael', 'gaele', 'gaelle', 'gaetan', 'gaetane', 'gaetano', 'gaia', 'galaad', 'gamze', 'gaoussou', 'garance', 'garry', 'gary', 'gaspar', 'gaspard', 'gaston', 'gatien', 'gatienne', 'gaultier', 'gauthier', 'gautier', 'gauvain', 'gavin', 'gaya', 'gaye', 'gaylor', 'gaylord', 'gaetan', 'gael', 'gaelle', 'gaetan', 'gaetane', 'gaia', 'gebril', 'gedeon', 'gemma', 'gena', 'genevieve', 'genna', 'gennaro', 'gentil', 'geoffray', 'geoffrey', 'geoffroy', 'george', 'georges', 'georges-marie', 'georget', 'georgette', 'georgia', 'georgina', 'georgine', 'geraldine', 'gerardine', 'geraud', 'gerda', 'geremy', 'germain', 'germaine', 'germinal', 'gerome', 'gersende', 'gertrude', 'gerty', 'gervais', 'gervaise', 'gery', 'ghania', 'ghislain', 'ghislaine', 'ghizlane', 'ghjulia', 'ghyslain', 'ghyslaine', 'giacomo', 'gian', 'giani', 'gianna', 'gianni', 'gianny', 'gibril', 'gil', 'gilbert', 'gilberte', 'gilberto', 'gilda', 'gildas', 'gilette', 'giliane', 'gille', 'gilles', 'gillette', 'gillian', 'gilliane', 'gina', 'gines', 'ginette', 'ginger', 'gino', 'gioia', 'giorgia', 'giorgio', 'giovani', 'giovanna', 'giovanni', 'giovanny', 'giovany', 'gipsy', 'girard', 'giselaine', 'giselle', 'gishlaine', 'gislain', 'gislaine', 'gislene', 'gislhaine', 'gismonde', 'gisele', 'giulia', 'giulian', 'giuliana', 'giuliano', 'giulio', 'giuseppe', 'giuseppina', 'gizem', 'gladys', 'glawdys', 'glen', 'glenn', 'gloire', 'gloria', 'glwadys', 'godefroy', 'godeleine', 'godfroy', 'gokhan', 'gontran', 'gontrand', 'gonzague', 'goran', 'gordana', 'gordon', 'goulven', 'goulwen', 'goundo', 'goundoba', 'gracia', 'gracianne', 'gracie', 'gratianne', 'gratien', 'gratienne', 'grazia', 'graziella', 'greg', 'gregoire', 'gregor', 'gregori', 'gregorio', 'gregory', 'greta', 'gregoire', 'gregory', 'gualbert', 'guenael', 'guenaelle', 'guenhael', 'guenola', 'guenole', 'guerric', 'guewen', 'gui', 'guido', 'guilain', 'guilaine', 'guilene', 'guilhem', 'guilherme', 'guilian', 'guillaume', 'guillaumette', 'guillaumine', 'guillem', 'guillemette', 'guillermo', 'guillian', 'guirec', 'guiseppe', 'guiseppina', 'guislain', 'guislaine', 'gul', 'gulay', 'gulcan', 'gunther', 'gurvan', 'gurwan', 'gustave', 'gustavie', 'gustavo', 'guy', 'guylain', 'guylaine', 'guylene', 'guylene', 'gweltaz', 'gwen', 'gwenael', 'gwenaele', 'gwenaelle', 'gwenael', 'gwenaelle', 'gwendal', 'gwendolina', 'gwendoline', 'gwendolyn', 'gwendolyne', 'gwenegan', 'gwenn', 'gwennael', 'gwennaelle', 'gwennaelle', 'gwenola', 'gwenole', 'gwenvael', 'gwladys', 'gwenael', 'gwenaelle', 'gwenola', 'gysele', 'gyslaine', 'gerald', 'geraldine', 'gerard', 'geraud', 'habib', 'habiba', 'habibatou', 'haby', 'hacen', 'hacene', 'hacer', 'hachim', 'haci', 'hacina', 'hada', 'hadassa', 'hadda', 'hadi', 'hadia', 'hadidja', 'hadil', 'hadj', 'hadja', 'hadjar', 'hadjer', 'hadjira', 'hadrien', 'hafid', 'hafida', 'hafsa', 'hafssa', 'hager', 'haifa', 'haikel', 'hailey', 'hajar', 'hajare', 'hajer', 'hakan', 'hakim', 'hakima', 'hala', 'haley', 'halil', 'halim', 'halima', 'halimatou', 'halime', 'halina', 'hamadi', 'hamady', 'hamdi', 'hamed', 'hamel', 'hamid', 'hamida', 'hamidou', 'hamza', 'hana', 'hanaa', 'hanae', 'hanan', 'hanane', 'hanae', 'hanae', 'handy', 'hanen', 'hanene', 'hani', 'hania', 'hanifa', 'hanine', 'hanna', 'hannah', 'hannelore', 'hans', 'hanya', 'harald', 'haris', 'harmonie', 'harmony', 'harold', 'haron', 'harone', 'haroun', 'harouna', 'haroune', 'harris', 'harrison', 'harry', 'harun', 'hasan', 'hasna', 'hassan', 'hassane', 'hassen', 'hassiba', 'hassina', 'hassna', 'hatem', 'hatice', 'hatim', 'hatouma', 'hatoumata', 'haude', 'hava', 'havin', 'havva', 'hawa', 'haya', 'hayat', 'hayate', 'haydan', 'hayden', 'hayet', 'hayette', 'haykel', 'hayley', 'haytam', 'haytem', 'haytham', 'haythem', 'hazal', 'hazel', 'heather', 'heaven', 'hector', 'heddy', 'hedi', 'hedia', 'hedwig', 'hedwige', 'hedy', 'heidi', 'heinz', 'hela', 'helder', 'helea', 'helen', 'helena', 'helene', 'helga', 'helia', 'heliane', 'helie', 'helier', 'heliette', 'helin', 'heline', 'helio', 'helios', 'hella', 'helmut', 'helmuth', 'heloise', 'helyett', 'helyette', 'henda', 'hendrick', 'henri', 'henri-claude', 'henri-pierre', 'henria', 'henrick', 'henriette', 'henrik', 'henrique', 'henry', 'henryk', 'henzo', 'herbert', 'herman', 'hermance', 'hermann', 'hermes', 'hermine', 'herminie', 'hermione', 'herveline', 'herve', 'heyden', 'hiba', 'hicham', 'hichame', 'hichem', 'hidaya', 'hilaire', 'hilal', 'hilarion', 'hilary', 'hilda', 'hilde', 'hildegard', 'hildegarde', 'hildevert', 'hilel', 'hillary', 'hillel', 'hina', 'hinata', 'hinatea', 'hind', 'hinda', 'hinde', 'hippolyte', 'hira', 'hiranur', 'hoang', 'hoche', 'hocine', 'hoda', 'hoel', 'homere', 'honorat', 'honore', 'honoree', 'honorine', 'honore', 'hope', 'horace', 'horacio', 'horia', 'horst', 'hortense', 'hortensia', 'hosni', 'houari', 'houcine', 'houda', 'houleye', 'houria', 'houssam', 'houssem', 'houssine', 'hubert', 'huberte', 'hubertine', 'hugo', 'hugues', 'huguette', 'hulya', 'humbert', 'humberto', 'huseyin', 'hussein', 'hyacinthe', 'hylan', 'hyppolite', 'hedi', 'helia', 'helio', 'helios', 'heloise', 'helena', 'helene', 'helena', 'ian', 'ianis', 'iannis', 'iban', 'ibrahim', 'ibrahima', 'ibtissam', 'ibtissame', 'ibtissem', 'icham', 'ichem', 'ida', 'idalina', 'idir', 'idoia', 'idris', 'idriss', 'idrissa', 'ignace', 'ignacio', 'igor', 'ihab', 'iheb', 'ihsan', 'ihsane', 'ihssane', 'ikram', 'ikrame', 'ilan', 'ilana', 'ilane', 'ilann', 'ilario', 'ilayda', 'ilda', 'ildevert', 'ileana', 'ilef', 'ilena', 'ilham', 'ilhame', 'ilhan', 'ilhem', 'ilian', 'iliana', 'iliane', 'ilias', 'ilies', 'ilina', 'ilies', 'ilker', 'ilknur', 'illan', 'illana', 'illian', 'illiana', 'illies', 'illona', 'illyana', 'illyes', 'ilona', 'ilse', 'ilyam', 'ilyan', 'ilyana', 'ilyane', 'ilyann', 'ilyanna', 'ilyas', 'ilyass', 'ilyasse', 'ilyes', 'ilyess', 'ilyesse', 'ilyes', 'ileana', 'ilena', 'imad', 'imade', 'iman', 'imane', 'imani', 'imanol', 'imany', 'imed', 'imelda', 'imen', 'imene', 'imran', 'imrane', 'imran', 'imene', 'ina', 'inaki', 'inas', 'inaya', 'inayah', 'inaia', 'incarnation', 'india', 'indiana', 'indira', 'indra', 'indy', 'inel', 'ines', 'iness', 'inesse', 'ingrid', 'inna', 'innaya', 'insaf', 'inssaf', 'intissar', 'ines', 'ines', 'ines', 'ioan', 'ioana', 'iona', 'ipek', 'irem', 'irena', 'irenee', 'irfan', 'irina', 'iris', 'irma', 'irmgard', 'irmine', 'irvin', 'irwin', 'irys', 'irene', 'isa', 'isaac', 'isaak', 'isabel', 'isabella', 'isabelle', 'isac', 'isadora', 'isaia', 'isaiah', 'isaie', 'isak', 'isaline', 'isalyne', 'isalys', 'isam', 'isao', 'isaura', 'isaure', 'isaiah', 'iseult', 'ishak', 'ishaq', 'isia', 'isidore', 'isis', 'islam', 'islem', 'isleym', 'isma', 'ismael', 'ismahan', 'ismahane', 'ismail', 'ismaila', 'ismael', 'ismail', 'ismerie', 'isoline', 'isra', 'israa', 'israe', 'israel', 'issa', 'issam', 'issra', 'italo', 'ivan', 'ivana', 'ivane', 'ivann', 'ivanna', 'ivette', 'ivo', 'ivonne', 'ivy', 'iwan', 'iwen', 'iyad', 'iyed', 'izak', 'izia', 'izzie', 'izia', 'jacinthe', 'jack', 'jacki', 'jackie', 'jackson', 'jacky', 'jacob', 'jacquelin', 'jacqueline', 'jacques', 'jacques-olivier', 'jacqui', 'jacquie', 'jacquy', 'jad', 'jade', 'jaden', 'jadwiga', 'jael', 'jahid', 'jahyan', 'jahyann', 'jakie', 'jaky', 'jalal', 'jalane', 'jalil', 'jalila', 'jalis', 'jamal', 'jamel', 'james', 'jamie', 'jamil', 'jamila', 'jamy', 'jan', 'jana', 'jane', 'janel', 'janelle', 'janet', 'janette', 'janice', 'janick', 'janie', 'janik', 'janina', 'janine', 'janique', 'janis', 'janna', 'jannah', 'jannat', 'jannick', 'jannie', 'jannine', 'janny', 'jany', 'janyce', 'jaouad', 'jaoued', 'jaouen', 'jared', 'jarod', 'jasmin', 'jasmina', 'jasmine', 'jason', 'jassem', 'jassim', 'jauffrey', 'javier', 'jawad', 'jawed', 'jay', 'jaya', 'jayan', 'jayden', 'jayson', 'jean', 'jean-alain', 'jean-albert', 'jean-alexandre', 'jean-andre', 'jean-antoine', 'jean-baptiste', 'jean-benoit', 'jean-bernard', 'jean-brice', 'jean-bruno', 'jean-camille', 'jean-charles', 'jean-christian', 'jean-christophe', 'jean-claude', 'jean-damien', 'jean-daniel', 'jean-david', 'jean-denis', 'jean-didier', 'jean-dominique', 'jean-edouard', 'jean-elie', 'jean-emile', 'jean-emmanuel', 'jean-eric', 'jean-etienne', 'jean-eudes', 'jean-fabrice', 'jean-francis', 'jean-francois', 'jean-fred', 'jean-frederic', 'jean-felix', 'jean-gabriel', 'jean-georges', 'jean-gilles', 'jean-guillaume', 'jean-guy', 'jean-gerard', 'jean-henri', 'jean-herve', 'jean-hubert', 'jean-hugues', 'jean-jack', 'jean-jacques', 'jean-joseph', 'jean-jose', 'jean-joel', 'jean-julien', 'jean-laurent', 'jean-lou', 'jean-louis', 'jean-loup', 'jean-loic', 'jean-luc', 'jean-lucien', 'jean-manuel', 'jean-marc', 'jean-marcel', 'jean-marie', 'jean-mary', 'jean-mathieu', 'jean-matthieu', 'jean-maurice', 'jean-max', 'jean-maxime', 'jean-michel', 'jean-nicolas', 'jean-noel', 'jean-olivier', 'jean-pascal', 'jean-patrice', 'jean-patrick', 'jean-paul', 'jean-philippe', 'jean-pierre', 'jean-pol', 'jean-raphael', 'jean-raymond', 'jean-rene', 'jean-richard', 'jean-robert', 'jean-roch', 'jean-roger', 'jean-romain', 'jean-remi', 'jean-remy', 'jean-stephane', 'jean-sebastien', 'jean-thierry', 'jean-thomas', 'jean-victor', 'jean-vincent', 'jean-yann', 'jean-yves', 'jeane', 'jeanette', 'jeanick', 'jeanine', 'jeaninne', 'jeanne', 'jeanne-marie', 'jeannette', 'jeannick', 'jeannie', 'jeannina', 'jeannine', 'jeannot', 'jeanny', 'jebril', 'jed', 'jeff', 'jefferson', 'jeffrey', 'jehan', 'jehane', 'jehanne', 'jelena', 'jemima', 'jena', 'jenifer', 'jenna', 'jennah', 'jennie', 'jennifer', 'jenny', 'jennyfer', 'jeoffrey', 'jeremi', 'jeremie', 'jeremy', 'jerome', 'jeromine', 'jerry', 'jersey', 'jerome', 'jeson', 'jess', 'jesse', 'jessica', 'jessie', 'jessika', 'jessim', 'jessy', 'jessyca', 'jessym', 'jesus', 'jeyson', 'jezabel', 'jhonny', 'jibril', 'jihad', 'jihan', 'jihane', 'jihed', 'jihene', 'jill', 'jillian', 'jim', 'jimi', 'jimmy', 'jimy', 'jinane', 'jiyan', 'jo', 'joachim', 'joachin', 'joackim', 'joakim', 'joan', 'joana', 'joane', 'joanes', 'joanie', 'joann', 'joanna', 'joanne', 'joannes', 'joannie', 'joanny', 'joany', 'joao', 'joaquim', 'joaquin', 'joaquina', 'jocelin', 'joceline', 'jocelyn', 'jocelyne', 'jocya', 'jodie', 'jody', 'joe', 'joel', 'joele', 'joevin', 'joey', 'joffray', 'joffre', 'joffrette', 'joffrey', 'johan', 'johana', 'johane', 'johann', 'johanna', 'johanne', 'johannes', 'johannie', 'johanny', 'john', 'johnatan', 'johnathan', 'johnny', 'johny', 'jolan', 'jon', 'jonah', 'jonas', 'jonatan', 'jonathan', 'jonathann', 'jonny', 'joran', 'jordan', 'jordane', 'jordann', 'jordi', 'jordy', 'jorge', 'jorick', 'joris', 'jorys', 'joscelyne', 'josee', 'josef', 'josefa', 'joseline', 'joselito', 'joselyne', 'joseph', 'josepha', 'josephe', 'josephine', 'josephte', 'josette', 'josh', 'joshua', 'josian', 'josiane', 'josianne', 'josias', 'josie', 'joss', 'josse', 'josselin', 'josseline', 'josselyn', 'josselyne', 'jossua', 'josua', 'josue', 'josue', 'josy', 'josyane', 'jose', 'jose-antonio', 'jose-luis', 'jose-manuel', 'jose-maria', 'jose-marie', 'josee', 'josephine', 'joud', 'joudia', 'joulia', 'joumana', 'jounayd', 'jovan', 'jovanny', 'jovany', 'joy', 'joyce', 'joye', 'joe', 'joel', 'joelle', 'juan', 'juan-carlos', 'juana', 'juanita', 'juanito', 'juba', 'jude', 'judes', 'judicael', 'judicaelle', 'judicael', 'judikael', 'judith', 'judy', 'jugurtha', 'jule', 'julen', 'jules', 'julia', 'julian', 'juliana', 'juliane', 'juliann', 'julianna', 'julianne', 'juliano', 'julie', 'julie-anne', 'julien', 'julienne', 'juliet', 'juliette', 'juline', 'julio', 'julius', 'jullian', 'julot', 'july', 'julya', 'julyan', 'jun', 'junayd', 'june', 'junior', 'jurgen', 'just', 'juste', 'justin', 'justina', 'justine', 'justinien', 'jerome', 'jeremie', 'jeremy', 'jerome', 'kaan', 'kacem', 'kaci', 'kacy', 'kaddour', 'kader', 'kadia', 'kadiatou', 'kadidia', 'kadidiatou', 'kadidja', 'kadija', 'kadir', 'kady', 'kael', 'kaelig', 'kaena', 'kahil', 'kahina', 'kahyna', 'kahys', 'kaila', 'kaina', 'kais', 'kaiss', 'kaissy', 'kaled', 'kali', 'kalid', 'kalidou', 'kalie', 'kalifa', 'kalil', 'kalilou', 'kalista', 'kally', 'kaltoum', 'kalvin', 'kalvyn', 'kaly', 'kalya', 'kalyssa', 'kamal', 'kamel', 'kamelia', 'kamelya', 'kameron', 'kamil', 'kamila', 'kamilia', 'kamille', 'kamilya', 'kamron', 'kamelia', 'kani', 'kany', 'kaoutar', 'kaouthar', 'kara', 'karam', 'karamba', 'kardiatou', 'kareen', 'karel', 'karell', 'karelle', 'karen', 'karene', 'karim', 'karima', 'karime', 'karin', 'karina', 'karine', 'karinne', 'karl', 'karla', 'karol', 'karolina', 'karyne', 'kassandra', 'kassandre', 'kassidy', 'kassie', 'kassim', 'kassy', 'kataleya', 'katalina', 'katarina', 'kate', 'katel', 'kateline', 'katell', 'katharina', 'katherine', 'kathia', 'kathleen', 'kathy', 'katia', 'katiana', 'katie', 'katleen', 'katty', 'katy', 'katya', 'kawtar', 'kawthar', 'kaya', 'kayden', 'kayla', 'kaylan', 'kaylee', 'kaylia', 'kayliah', 'kaylie', 'kayline', 'kayna', 'kayron', 'kays', 'kayss', 'kael', 'kaila', 'kaily', 'kaina', 'kais', 'kaiss', 'keenan', 'keila', 'keira', 'keissy', 'kelia', 'kelian', 'keliane', 'kelig', 'kellia', 'kellian', 'kellie', 'kelly', 'kellya', 'kellyan', 'kelsy', 'keltoum', 'kelvin', 'kelvyn', 'kelya', 'kelyah', 'kelyan', 'kelyane', 'kelyann', 'kelyssa', 'kemal', 'kemil', 'ken', 'kenan', 'kendall', 'kendji', 'kendjy', 'kendra', 'kendrick', 'kendy', 'kenji', 'kenjy', 'kenneth', 'kenny', 'kentin', 'keny', 'kenya', 'kenza', 'kenzi', 'kenzo', 'kenzy', 'keo', 'keran', 'kerem', 'keren', 'kerian', 'kerim', 'kerrian', 'kerry', 'kerwan', 'keryan', 'keryann', 'kesia', 'kessie', 'kessy', 'ketsia', 'ketty', 'kevan', 'keven', 'kevin', 'kevser', 'kevyn', 'kewan', 'kewin', 'keyan', 'keyla', 'keylan', 'keylia', 'keyliah', 'keylian', 'keyna', 'keyran', 'keyron', 'keysha', 'keyvan', 'kezia', 'keziah', 'khadidiatou', 'khadidja', 'khadija', 'khadim', 'khadra', 'khady', 'khaled', 'khalid', 'khalida', 'khalifa', 'khalil', 'khalis', 'khalissa', 'khedidja', 'kheira', 'khira', 'khloe', 'kian', 'kiana', 'kiara', 'kiera', 'kieran', 'kilian', 'kiliane', 'kiliann', 'killian', 'killyan', 'kilyan', 'kilyann', 'kim', 'kimany', 'kimberlay', 'kimberley', 'kimberly', 'kimi', 'kimia', 'kimy', 'kimya', 'kinan', 'kingsley', 'kinsley', 'kira', 'kiran', 'kirsten', 'kiyan', 'kiyane', 'klara', 'klea', 'kleber', 'klebert', 'klervi', 'klervie', 'kloe', 'kloe', 'klea', 'koray', 'korentin', 'kouider', 'koumba', 'kris', 'kristel', 'kristell', 'kristelle', 'kristen', 'kristina', 'kristopher', 'kristy', 'krys', 'krystal', 'krystel', 'krystelle', 'kubra', 'kurt', 'kurtis', 'kurtys', 'kyan', 'kyara', 'kylan', 'kyle', 'kylia', 'kylian', 'kyliana', 'kyliane', 'kyliann', 'kylie', 'kyllian', 'kymani', 'kyra', 'kyran', 'kyrian', 'kelia', 'kelian', 'kelio', 'kelya', 'kelyan', 'keo', 'keran', 'kevin', 'keziah', 'ladislas', 'ladji', 'lael', 'laeticia', 'laetitia', 'lahcen', 'lahcene', 'lahna', 'lahouari', 'lahoucine', 'laia', 'laid', 'laila', 'laina', 'lais', 'lakdar', 'lakhdar', 'lala', 'lali', 'lalia', 'lalie', 'lalla', 'lallie', 'lalou', 'laly', 'lalya', 'lambert', 'lamia', 'lamine', 'lamis', 'lamya', 'lana', 'lancelot', 'landry', 'lanna', 'laora', 'lara', 'larbi', 'larissa', 'larry', 'lassana', 'laszlo', 'latifa', 'laura', 'lauralee', 'lauralie', 'lauraline', 'laurana', 'laurane', 'lauranne', 'laure', 'laure-anne', 'laure-helene', 'laureen', 'laureline', 'laurelyne', 'lauren', 'laurena', 'laurence', 'laurencia', 'laurencie', 'laurene', 'laurent', 'laurentine', 'laurette', 'lauriana', 'lauriane', 'laurianne', 'lauric', 'laurie', 'laurie-anne', 'laurina', 'laurinda', 'laurine', 'lauris', 'laury', 'lauryane', 'lauryn', 'lauryne', 'laurene', 'laureline', 'lavinia', 'lawrence', 'laya', 'layan', 'layana', 'layanah', 'layane', 'layann', 'layanna', 'layannah', 'layina', 'layla', 'layna', 'layvin', 'lazar', 'lazare', 'lazarine', 'lazhar', 'laetitia', 'laetitia', 'laia', 'laila', 'laina', 'lea', 'leah', 'leana', 'leandra', 'leandre', 'leandro', 'leane', 'leann', 'leanna', 'leanne', 'lee', 'lee-lou', 'leeloo', 'leelou', 'leena', 'leeroy', 'leger', 'lehna', 'lehyan', 'leia', 'leila', 'leilou', 'leina', 'lelia', 'lelio', 'lemmy', 'lena', 'lenaelle', 'lenaic', 'lenaick', 'lenaig', 'lenaik', 'leni', 'lenna', 'lenni', 'lennie', 'lenny', 'leny', 'lenzo', 'leo', 'leocadie', 'leon', 'leona', 'leonard', 'leonardo', 'leonce', 'leoncia', 'leoncie', 'leonel', 'leonide', 'leonie', 'leonne', 'leonor', 'leonora', 'leonore', 'leonus', 'leony', 'leopold', 'leopoldine', 'lesia', 'leslie', 'lesly', 'leticia', 'letitia', 'letizia', 'letty', 'levana', 'levi', 'levy', 'lewis', 'lexane', 'lexie', 'lexy', 'leya', 'leyan', 'leyana', 'leyla', 'leyna', 'leia', 'leila', 'leina', 'lia', 'liah', 'liam', 'liana', 'liane', 'licia', 'lida', 'lidia', 'lidwine', 'lidy', 'lie', 'lies', 'liham', 'lila', 'lila-rose', 'lilah', 'lilas', 'lilette', 'lili', 'lili-rose', 'lilia', 'lilian', 'liliana', 'liliane', 'lilianne', 'lilie', 'lilio', 'lilirose', 'lilith', 'lilla', 'lilli', 'lillian', 'lilly', 'lilly-rose', 'lilo', 'liloo', 'lilou', 'lilwen', 'lilwenn', 'lily', 'lily-rose', 'lilya', 'lilyan', 'lin', 'lina', 'linaya', 'linda', 'lindsay', 'lindsey', 'lindy', 'line', 'linette', 'lino', 'linoa', 'linsay', 'linsey', 'lio', 'lionel', 'lionelle', 'lionnel', 'lior', 'liora', 'lisa', 'lisa-marie', 'lisandra', 'lisandre', 'lisandro', 'lisandru', 'lisbeth', 'lise', 'lise-marie', 'lisea', 'liselotte', 'lisette', 'lisiane', 'lisie', 'lison', 'lissandre', 'lissandro', 'lisy', 'lisea', 'liv', 'livia', 'livie', 'livio', 'liya', 'liyah', 'liyam', 'liyana', 'liz', 'liza', 'lizea', 'lizie', 'lizon', 'lizy', 'lizzie', 'lizzy', 'lizea', 'lloyd', 'loan', 'loana', 'loane', 'loann', 'loanne', 'lobna', 'loeiz', 'loeiza', 'loelia', 'loetitia', 'loeva', 'loevan', 'logan', 'logane', 'logann', 'loghan', 'lohan', 'lohane', 'lohann', 'loic', 'loicia', 'loick', 'loig', 'loik', 'lois', 'loise', 'lokman', 'lola', 'loli', 'lolie', 'lolita', 'loly', 'lona', 'loni', 'lonny', 'lony', 'loona', 'loqman', 'lora', 'loraine', 'lorane', 'lore', 'lorea', 'loredana', 'loreen', 'loreena', 'lorelei', 'lorelei', 'loreline', 'lorella', 'loren', 'lorena', 'lorene', 'lorenz', 'lorenza', 'lorenzo', 'loretta', 'lorette', 'lori', 'loriana', 'loriane', 'lorianne', 'loric', 'lorick', 'lorie', 'lorik', 'lorin', 'lorina', 'lorine', 'loris', 'lorna', 'lorraine', 'lorrie', 'lorris', 'lorry', 'lory', 'loryne', 'lorys', 'lorene', 'lorena', 'lotfi', 'lothaire', 'lou', 'lou-ann', 'lou-anna', 'lou-anne', 'louan', 'louana', 'louane', 'louann', 'louanne', 'louay', 'loubna', 'louca', 'loucas', 'loucia', 'loucka', 'louen', 'louenn', 'louhane', 'louis', 'louis-alexandre', 'louis-gabriel', 'louis-marie', 'louis-philippe', 'louisa', 'louise', 'louise-marie', 'louisette', 'louisia', 'louisiane', 'louison', 'louiza', 'loujayne', 'louka', 'loukas', 'loula', 'louna', 'lounes', 'lounis', 'lounna', 'lounes', 'loup', 'lourdes', 'loutfi', 'louve', 'lovely', 'lowan', 'lowen', 'loys', 'loe', 'loelia', 'loelie', 'loevan', 'loevan', 'loic', 'loick', 'lois', 'loise', 'luan', 'luana', 'luane', 'lubin', 'luc', 'luca', 'lucas', 'lucay', 'lucca', 'luce', 'lucenzo', 'lucette', 'lucia', 'lucian', 'luciana', 'luciane', 'luciano', 'lucie', 'lucien', 'lucienne', 'lucile', 'lucilia', 'lucille', 'lucinda', 'lucine', 'lucio', 'lucka', 'luckas', 'lucrece', 'lucy', 'lucyle', 'ludger', 'ludivine', 'ludiwine', 'ludmila', 'ludmilla', 'ludovic', 'ludovick', 'ludwig', 'ludyvine', 'lugdivine', 'luidgi', 'luidgy', 'luidji', 'luigi', 'luis', 'luisa', 'luiz', 'luiza', 'luka', 'lukas', 'luke', 'luna', 'lune', 'luqman', 'luther', 'luz', 'ly', 'lya', 'lyah', 'lyam', 'lyana', 'lyanna', 'lycia', 'lyderic', 'lydia', 'lydiane', 'lydie', 'lyes', 'lyess', 'lyha', 'lyham', 'lyhana', 'lyla', 'lylia', 'lylian', 'lyliane', 'lylie', 'lylio', 'lyloo', 'lylou', 'lylwenn', 'lyna', 'lynda', 'lyndsay', 'lyne', 'lynn', 'lynna', 'lyonel', 'lyra', 'lys', 'lysa', 'lysandre', 'lysandro', 'lyse', 'lysea', 'lysia', 'lysiane', 'lysianne', 'lysie', 'lyson', 'lyssandre', 'lyssia', 'lysea', 'lyvan', 'lyvann', 'lyvia', 'lyvio', 'lea', 'lea-marie', 'leah', 'leana', 'leandre', 'leandro', 'leane', 'leanna', 'leanne', 'lelia', 'lelio', 'lena', 'lenaelle', 'lenaic', 'lenais', 'leni', 'leny', 'leo', 'leo-paul', 'leon', 'leona', 'leonard', 'leonce', 'leone', 'leonie', 'leonor', 'leonore', 'leontine', 'leony', 'leopold', 'leopoldine', 'levi', 'levy', 'luna', "m'hamed", 'maamar', 'mabrouk', 'mabrouka', 'maceo', 'macha', 'mackenzie', 'maceo', 'madani', 'maddie', 'maddly', 'maddy', 'madelaine', 'madeleine', 'madeline', 'madelyne', 'maden', 'madenn', 'madi', 'madiana', 'madie', 'madina', 'madison', 'madisson', 'madjid', 'madleen', 'madly', 'mado', 'mady', 'madyson', 'madysson', 'mae', 'mael', 'maela', 'maelan', 'maelane', 'maelann', 'maele', 'maelenn', 'maeli', 'maelia', 'maelie', 'maelig', 'maeline', 'maelis', 'maeliss', 'maelisse', 'maella', 'maelle', 'maellie', 'maelly', 'maellys', 'maelwenn', 'maely', 'maelya', 'maelyne', 'maelys', 'maelyse', 'maelyss', 'maelysse', 'maena', 'maeva', 'maeve', 'maewenn', 'mafalda', 'magali', 'magalie', 'magaly', 'magda', 'magdalena', 'magdelaine', 'magdeleine', 'maggie', 'magguy', 'maggy', 'magid', 'magloire', 'magnolia', 'maguelone', 'maguelonne', 'maguy', 'maha', 'mahamadou', 'mahault', 'mahaut', 'mahawa', 'mahdi', 'mahdy', 'mahe', 'mahee', 'mahel', 'maher', 'mahera', 'maheva', 'mahfoud', 'mahina', 'mahir', 'mahira', 'mahmoud', 'mahmut', 'maho', 'mahe', 'mahee', 'maia', 'maialen', 'maiana', 'maider', 'maika', 'maili', 'mailie', 'mailis', 'mailly', 'maily', 'mailys', 'mailyss', 'maimouna', 'maina', 'maira', 'maissa', 'maissam', 'maissane', 'maite', 'maitena', 'maiwen', 'maiwenn', 'maixent', 'maja', 'majda', 'majdi', 'majdouline', 'majid', 'makan', 'makram', 'maksen', 'maksim', 'malaika', 'malak', 'malaurie', 'malaury', 'malcolm', 'malcom', 'malek', 'malena', 'malia', 'malicia', 'malick', 'malik', 'malika', 'mallaurie', 'mallaury', 'mallorie', 'mallory', 'malo', 'maloe', 'malon', 'malone', 'malonn', 'malorie', 'malory', 'malou', 'maloe', 'malvin', 'malvina', 'malya', 'mama', 'mamadi', 'mamadou', 'mamady', 'mame', 'mamou', 'mamoudou', 'manal', 'manale', 'manar', 'mandy', 'manech', 'manel', 'manele', 'manelle', 'manfred', 'mani', 'manil', 'manny', 'mano', 'manoa', 'manoah', 'manoe', 'manoel', 'manola', 'manolita', 'manolo', 'manon', 'manoe', 'manoe', 'mansour', 'manu', 'manuel', 'manuela', 'manuella', 'manuelle', 'many', 'mao', 'mara', 'maram', 'marc', 'marc-alexandre', 'marc-andre', 'marc-antoine', 'marc-henri', 'marc-olivier', 'marceau', 'marcel', 'marcelin', 'marceline', 'marcelino', 'marcella', 'marcelle', 'marcellin', 'marcelline', 'marcello', 'marcelo', 'marcia', 'marcienne', 'marco', 'marcos', 'marcus', 'marek', 'mareva', 'margaret', 'margareth', 'margarida', 'margarita', 'margault', 'margaux', 'margo', 'margot', 'marguerite', 'marguerite-marie', 'margueritte', 'mari', 'maria', 'maria-carmen', 'maria-christina', 'maria-dolores', 'maria-fatima', 'maria-helena', 'maria-isabel', 'maria-jose', 'maria-luisa', 'maria-rosa', 'maria-teresa', 'mariam', 'mariama', 'mariame', 'marian', 'mariana', 'mariane', 'marianick', 'marianna', 'marianne', 'mariannick', 'mariano', 'maribel', 'marie', 'marie-adeline', 'marie-agnes', 'marie-aimee', 'marie-alice', 'marie-aline', 'marie-alix', 'marie-amandine', 'marie-amelie', 'marie-anais', 'marie-andree', 'marie-ange', 'marie-angelique', 'marie-angele', 'marie-anick', 'marie-anna', 'marie-anne', 'marie-annick', 'marie-annie', 'marie-antoinette', 'marie-armelle', 'marie-astrid', 'marie-aude', 'marie-aurore', 'marie-benedicte', 'marie-bernadette', 'marie-bernard', 'marie-berthe', 'marie-blanche', 'marie-brigitte', 'marie-beatrice', 'marie-camille', 'marie-carmen', 'marie-caroline', 'marie-catherine', 'marie-chantal', 'marie-charlotte', 'marie-christelle', 'marie-christiane', 'marie-christine', 'marie-claire', 'marie-claude', 'marie-claudine', 'marie-clotilde', 'marie-clemence', 'marie-colette', 'marie-cecile', 'marie-celine', 'marie-daniele', 'marie-danielle', 'marie-denise', 'marie-dolores', 'marie-dominique', 'marie-edith', 'marie-elisabeth', 'marie-elise', 'marie-elodie', 'marie-emilie', 'marie-emmanuelle', 'marie-estelle', 'marie-eugenie', 'marie-eve', 'marie-evelyne', 'marie-flore', 'marie-florence', 'marie-france', 'marie-francoise', 'marie-frederique', 'marie-gabrielle', 'marie-gaelle', 'marie-genevieve', 'marie-george', 'marie-georges', 'marie-germaine', 'marie-henriette', 'marie-helene', 'marie-isabelle', 'marie-jacqueline', 'marie-jeanne', 'marie-josee', 'marie-joseph', 'marie-josephe', 'marie-josephine', 'marie-josette', 'marie-josiane', 'marie-josephe', 'marie-jose', 'marie-josee', 'marie-joelle', 'marie-julie', 'marie-laetitia', 'marie-laure', 'marie-laurence', 'marie-liesse', 'marie-line', 'marie-lise', 'marie-lorraine', 'marie-lou', 'marie-louise', 'marie-lourdes', 'marie-luce', 'marie-lucie', 'marie-lyne', 'marie-lys', 'marie-lyse', 'marie-lea', 'marie-madeleine', 'marie-magdeleine', 'marie-marguerite', 'marie-marthe', 'marie-martine', 'marie-michelle', 'marie-michele', 'marie-monique', 'marie-nathalie', 'marie-neige', 'marie-nelly', 'marie-nicole', 'marie-noele', 'marie-noel', 'marie-noelle', 'marie-oceane', 'marie-odette', 'marie-odile', 'marie-pascale', 'marie-paule', 'marie-pauline', 'marie-pierre', 'marie-reine', 'marie-renee', 'marie-rose', 'marie-sarah', 'marie-solange', 'marie-sophie', 'marie-stephanie', 'marie-suzanne', 'marie-sylvie', 'marie-thereze', 'marie-therese', 'marie-victoire', 'marie-virginie', 'marie-veronique', 'marie-yvonne', 'marieke', 'mariella', 'marielle', 'mariem', 'marieme', 'marien', 'marietou', 'marietta', 'mariette', 'marika', 'marilene', 'mariline', 'marilou', 'marilyn', 'marilyne', 'marilys', 'marilyse', 'marin', 'marina', 'marine', 'marinella', 'marinette', 'marino', 'mario', 'marion', 'marisa', 'marise', 'marisol', 'marissa', 'maritchu', 'marius', 'marivonne', 'mariya', 'marjan', 'marjane', 'marjolaine', 'marjorie', 'marjory', 'mark', 'marko', 'markus', 'marla', 'marleine', 'marley', 'marline', 'marlise', 'marlon', 'marlone', 'marly', 'marlyse', 'marlene', 'marnie', 'maroine', 'maroua', 'marouan', 'marouane', 'maroussia', 'marta', 'martha', 'marthe', 'martial', 'martim', 'martin', 'martina', 'martine', 'marty', 'marveen', 'marvin', 'marvyn', 'marwa', 'marwan', 'marwane', 'marwen', 'marwin', 'mary', 'mary-line', 'mary-lou', 'marya', 'maryam', 'maryama', 'maryame', 'maryan', 'maryannick', 'maryem', 'marylaine', 'marylene', 'marylin', 'maryline', 'marylise', 'marylou', 'marylene', 'maryna', 'maryne', 'maryon', 'maryse', 'maryssa', 'maryvonne', 'mason', 'massi', 'massil', 'massilia', 'massilya', 'massimo', 'massinissa', 'massyl', 'matea', 'matei', 'mateo', 'mathea', 'matheis', 'matheo', 'mathew', 'mathias', 'mathieu', 'mathilda', 'mathilde', 'mathis', 'mathurin', 'mathurine', 'mathyas', 'mathylde', 'mathys', 'matheis', 'matheo', 'matheis', 'matias', 'matilda', 'matilde', 'matis', 'matiss', 'matisse', 'matt', 'mattea', 'matteo', 'mattew', 'mattheo', 'matthew', 'matthias', 'matthieu', 'matthis', 'matthys', 'mattheo', 'matti', 'mattia', 'mattias', 'mattieu', 'mattin', 'mattis', 'mattys', 'matteo', 'maty', 'matyas', 'matys', 'mateo', 'maud', 'maude', 'maurane', 'mauranne', 'maureen', 'maurice', 'mauricette', 'mauricia', 'maurille', 'maurin', 'maurine', 'maurizio', 'mauro', 'maverick', 'mavrick', 'max', 'maxance', 'maxandre', 'maxence', 'maxens', 'maxim', 'maxime', 'maximilian', 'maximilien', 'maximilienne', 'maximin', 'maxine', 'maxym', 'may', 'may-lee', 'may-line', 'maya', 'mayalen', 'mayana', 'mayane', 'mayar', 'mayeul', 'mayla', 'maylan', 'maylane', 'maylee', 'mayleen', 'mayli', 'maylie', 'mayline', 'maylis', 'mayliss', 'maylisse', 'maymouna', 'mayna', 'mayra', 'mayron', 'mayronn', 'maysa', 'maysan', 'mayson', 'mayssa', 'mayssam', 'mayssane', 'mazarine', 'mazen', 'mae', 'maelia', 'maelie', 'maeline', 'maely', 'maelya', 'maelyne', 'maelys', 'maeva', 'mae', 'mael', 'maela', 'maelan', 'maelane', 'maelann', 'maelia', 'maelie', 'maeline', 'maelis', 'maella', 'maelle', 'maelly', 'maellys', 'maely', 'maelya', 'maelyne', 'maelynn', 'maelys', 'maelyss', 'maelysse', 'maena', 'maeva', 'maia', 'mailane', 'maily', 'mailys', 'maimouna', 'maina', 'maira', 'maissa', 'maissane', 'maite', 'maiwen', 'maiwenn', 'medard', 'meddy', 'mederic', 'mederick', 'medhi', 'medhy', 'medi', 'medina', 'medine', 'medy', 'meg', 'megan', 'megane', 'meganne', 'meggane', 'meggie', 'meggy', 'meghan', 'meghane', 'meghann', 'meguy', 'mehdi', 'mehdy', 'mehmet', 'mei', 'meije', 'meir', 'meissa', 'meissane', 'mejdi', 'mekki', 'mel', 'melaine', 'melanie', 'melany', 'melchior', 'melda', 'melek', 'melia', 'meliana', 'meliane', 'melie', 'melih', 'meliha', 'melik', 'melika', 'melike', 'melina', 'melinda', 'meline', 'melis', 'melisa', 'melisande', 'melissa', 'melissande', 'melissandre', 'melisse', 'mellie', 'mellina', 'melodie', 'melody', 'meloe', 'meltem', 'melusine', 'melvil', 'melvin', 'melvina', 'melvine', 'melvyn', 'melwan', 'melya', 'melyna', 'melynda', 'melyne', 'melyssa', 'menahem', 'mendel', 'mendy', 'menel', 'menzo', 'meral', 'mercedes', 'meredith', 'meriam', 'meriem', 'merieme', 'merine', 'merlin', 'merouane', 'merry', 'merryl', 'merve', 'merveille', 'merwan', 'merwane', 'mery', 'meryam', 'meryem', 'meryl', 'meryll', 'mesmin', 'messaline', 'messaoud', 'messaouda', 'messi', 'messon', 'mesut', 'metehan', 'metin', 'meva', 'meven', 'mevlut', 'mewen', 'mewenn', 'meyline', 'meyssa', 'meziane', 'meissa', 'mia', 'micael', 'micaela', 'michael', 'michaela', 'michaele', 'michaella', 'michaelle', 'michael', 'michel', 'michel-ange', 'micheline', 'michelle', 'michele', 'mick', 'mickael', 'mickaela', 'mickaella', 'mickaelle', 'mickael', 'micky', 'mieczyslaw', 'miguel', 'mika', 'mikael', 'mikaela', 'mikail', 'mikael', 'mikail', 'mike', 'mikel', 'mikhael', 'mila', 'milan', 'milana', 'milane', 'milann', 'mildred', 'milena', 'milene', 'miley', 'milhan', 'milhane', 'milia', 'milian', 'miliana', 'milla', 'millie', 'milly', 'milo', 'milos', 'miloud', 'mily', 'milan', 'milene', 'milena', 'mimose', 'mimoun', 'mimouna', 'mina', 'mindy', 'minh', 'minna', 'mira', 'mirabelle', 'mirac', 'miral', 'miran', 'miranda', 'miray', 'mirac', 'mireille', 'mirella', 'miren', 'mirette', 'miriam', 'miriame', 'mirko', 'mirna', 'miryam', 'misha', 'miya', 'moana', 'moderan', 'modestine', 'modibo', 'mody', 'mohamad', 'mohamadou', 'mohamed', 'mohamed-ali', 'mohamed-amin', 'mohamed-amine', 'mohamed-lamine', 'mohamed-yacine', 'mohamed-yassine', 'mohammad', 'mohammed', 'mohammed-amine', 'mohand', 'moira', 'moise', 'moisette', 'mokhtar', 'mokrane', 'moktar', 'molly', 'mona', 'moncef', 'monette', 'monia', 'monica', 'monika', 'monique', 'monir', 'montaine', 'montserrat', 'morad', 'morade', 'moran', 'morane', 'morgan', 'morgane', 'morgann', 'morganne', 'morgiane', 'morine', 'morjane', 'morvan', 'mory', 'mostafa', 'mostapha', 'mouad', 'mouadh', 'moufida', 'mouhamad', 'mouhamadou', 'mouhamed', 'mouhammad', 'moulay', 'mouloud', 'mouna', 'mounia', 'mounir', 'mounira', 'mourad', 'mourade', 'moussa', 'moustafa', 'moustapha', 'moira', 'moise', 'muguette', 'muhamed', 'muhammad', 'muhammed', 'muhammed-ali', 'muhammet', 'murat', 'muriel', 'muriele', 'murielle', 'musa', 'mustafa', 'mustapha', 'my', 'mya', 'myah', 'myla', 'mylan', 'mylana', 'mylane', 'mylann', 'mylena', 'mylie', 'mylla', 'mylo', 'mylene', 'mylena', 'myriam', 'myriame', 'myriane', 'myrianne', 'myriem', 'myrna', 'myron', 'myrtille', 'medina', 'medine', 'mederic', 'megane', 'melaine', 'melanie', 'melany', 'melia', 'melie', 'melina', 'melinda', 'meline', 'melisande', 'melissa', 'melissandre', 'mellina', 'melodie', 'melody', 'meloe', 'melusine', 'melya', 'melyna', 'melyne', 'melyssa', 'meryl', 'mia', "n'deye", 'nabil', 'nabila', 'nabile', 'nabintou', 'nacer', 'nacera', 'nacim', 'nacima', 'nacira', 'nada', 'nadege', 'nadeige', 'nader', 'nadera', 'nadhir', 'nadia', 'nadiege', 'nadim', 'nadine', 'nadir', 'nadira', 'nadja', 'nadjet', 'nadji', 'nadjia', 'nadjib', 'nadjim', 'nady', 'nadya', 'nadege', 'nael', 'naela', 'naella', 'naelle', 'naema', 'nafissa', 'nafissatou', 'naguib', 'nahel', 'nahia', 'nahida', 'nahil', 'nahila', 'nahim', 'nahima', 'nahla', 'nahyl', 'nahel', 'naia', 'naida', 'nail', 'naila', 'naim', 'naima', 'nais', 'naissa', 'najah', 'najat', 'najate', 'najet', 'najette', 'naji', 'najia', 'najib', 'najim', 'najla', 'najma', 'najoua', 'najwa', 'nala', 'nalya', 'nana', 'nancy', 'nans', 'nao', 'naomi', 'naomie', 'naomy', 'naoual', 'naouel', 'naoufal', 'naoufel', 'napoleon', 'narcisse', 'narimane', 'narjes', 'narjess', 'narjis', 'narjisse', 'nasim', 'nasira', 'nasma', 'nasri', 'nasrine', 'nasser', 'nassera', 'nasserdine', 'nassim', 'nassima', 'nassime', 'nassira', 'nastasia', 'nastassia', 'natacha', 'natael', 'natale', 'natali', 'natalia', 'natalie', 'natalina', 'natan', 'natanael', 'natasha', 'nateo', 'nathael', 'nathalia', 'nathalie', 'nathaly', 'nathan', 'nathanael', 'nathanaelle', 'nathanael', 'nathanaelle', 'nathane', 'nathaniel', 'nathael', 'natheo', 'nathys', 'natheo', 'nattan', 'natty', 'nateo', 'nausicaa', 'nawal', 'nawale', 'nawel', 'nawell', 'nawelle', 'nawfal', 'nawfel', 'naya', 'nayah', 'nayan', 'nayel', 'nayeli', 'nayla', 'nazaire', 'nazha', 'naziha', 'nazim', 'nazli', 'nae', 'nael', 'naelle', 'naelya', 'naelys', 'naia', 'nail', 'naila', 'naim', 'naima', 'nais', 'naissa', 'ndeye', 'neal', 'nedjma', 'neela', 'nehemie', 'nehla', 'neige', 'neil', 'neila', 'nejla', 'nejma', 'nel', 'nelia', 'nelie', 'nelio', 'nell', 'nella', 'nellie', 'nello', 'nelly', 'nellya', 'nelson', 'nelya', 'nemo', 'nenad', 'nene', 'neo', 'nermine', 'neslihan', 'nesma', 'nesrin', 'nesrine', 'ness', 'nessa', 'nessim', 'nessrine', 'nesta', 'nestor', 'neva', 'neven', 'neyla', 'nezha', 'neila', 'ngoc', 'niame', 'nicaise', 'nicholas', 'nicky', 'nico', 'nicodeme', 'nicola', 'nicolas', 'nicole', 'nicoletta', 'nicolette', 'nicolle', 'nicomede', 'nida', 'nidal', 'nidhal', 'niels', 'nihad', 'nihal', 'nihed', 'nihel', 'nikita', 'nikola', 'nikolas', 'nil', 'nila', 'nilay', 'nilda', 'nils', 'nima', 'nina', 'nine', 'nino', 'ninon', 'niouma', 'nirina', 'nisa', 'nisanur', 'nisrine', 'nissa', 'nissrine', 'nita', 'nizar', 'noa', 'noah', 'noam', 'noan', 'noane', 'noann', 'noe', 'noela', 'noele', 'noelia', 'noelie', 'noeline', 'noelise', 'noella', 'noellie', 'noelline', 'noelly', 'noely', 'noelyne', 'noemi', 'noemie', 'noemy', 'noha', 'noham', 'nohan', 'nohann', 'nohe', 'nohlan', 'nohra', 'nohe', 'nola', 'nolan', 'nolane', 'nolann', 'nolhan', 'nolhann', 'nollan', 'nolwen', 'nolwenn', 'nonce', 'noor', 'nora', 'norah', 'norane', 'norbert', 'nordin', 'nordine', 'noredine', 'noreen', 'norhane', 'noria', 'noriane', 'norine', 'norma', 'norman', 'nouara', 'nouh', 'nouha', 'nouhaila', 'nour', 'nour-eddine', 'noura', 'nourane', 'nourdine', 'noureddine', 'nouredine', 'nourhane', 'nouria', 'nourredine', 'noussayba', 'noe', 'noelia', 'noelie', 'noeline', 'noelyne', 'noemi', 'noemie', 'noemy', 'noe', 'noel', 'noelie', 'noeline', 'noella', 'noelle', 'noellie', 'noelyne', 'noemie', 'numa', 'nuno', 'nunzia', 'nur', 'nuray', 'nurcan', 'nuria', 'nyla', 'nylan', 'nyls', 'nyna', 'nehemie', 'nelia', 'nelio', 'nelya', 'neo', 'ocean', 'oceana', 'oceane', 'oceanne', 'octave', 'octavie', 'oculi', 'oceane', 'oceanne', 'odelia', 'odeline', 'odessa', 'odette', 'odile', 'odille', 'odilon', 'odin', 'odon', 'odyle', 'oguz', 'oguzhan', 'oihan', 'oihana', 'okan', 'oksana', 'oktay', 'olfa', 'olga', 'oliana', 'olinda', 'oliva', 'olive', 'oliver', 'olivette', 'olivia', 'olivier', 'ollivier', 'olympe', 'omaima', 'omar', 'omayma', 'ombeline', 'ombline', 'omer', 'omerine', 'ondine', 'onesime', 'onur', 'oona', 'opale', 'opaline', 'ophelia', 'ophelie', 'opheline', 'ophely', 'ophelia', 'ophelie', 'orane', 'oren', 'oreste', 'orhan', 'oria', 'orian', 'oriana', 'oriane', 'orianna', 'orianne', 'orion', 'orlando', 'orlane', 'orlanne', 'ornela', 'ornella', 'orphee', 'orso', 'oscar', 'oskar', 'osman', 'osmin', 'ossama', 'oswald', 'othilie', 'othman', 'othmane', 'othon', 'otis', 'otman', 'otmane', 'otto', 'ouafa', 'ouahiba', 'ouahid', 'oualid', 'ouarda', 'ouardia', 'ouassila', 'ouassim', 'ouissem', 'ouiza', 'oulfa', 'oumaima', 'oumar', 'oumarou', 'oumayma', 'oumeyma', 'oumou', 'oumy', 'ourdia', 'ouria', 'ourida', 'ouriel', 'ours', 'ousman', 'ousmane', 'oussama', 'ouways', 'ovide', 'owen', 'owenn', 'oxana', 'ozan', 'ozcan', 'ozge', 'ozgur', 'ozkan', 'ozlem', 'pablo', 'paco', 'pacome', 'pacome', 'palmire', 'palmyre', 'paloma', 'pamela', 'pamela', 'paol', 'paola', 'paolina', 'paoline', 'paolo', 'papa', 'pape', 'paquerette', 'paquita', 'parfait', 'parfaite', 'pascal', 'pascale', 'pascaline', 'pasquale', 'patric', 'patrice', 'patricia', 'patricio', 'patrick', 'patrik', 'patrizia', 'patxi', 'paul', 'paul-adrien', 'paul-alexandre', 'paul-andre', 'paul-antoine', 'paul-arthur', 'paul-edouard', 'paul-emile', 'paul-emmanuel', 'paul-henri', 'paul-henry', 'paul-louis', 'paul-marie', 'paula', 'paule', 'paulette', 'paulin', 'paulina', 'pauline', 'paulo', 'pavel', 'pearl', 'pedro', 'peggy', 'peguy', 'peio', 'pelagie', 'pelin', 'penda', 'penelope', 'pepin', 'pepita', 'perine', 'perla', 'perle', 'perline', 'pernelle', 'peroline', 'perrine', 'pervenche', 'peter', 'petra', 'petronille', 'petrus', 'peyo', 'peyton', 'pharell', 'pharrell', 'philbert', 'phileas', 'philemon', 'philibert', 'philiberte', 'philip', 'philippa', 'philippe', 'philippine', 'philogone', 'philomene', 'philomene', 'phoebe', 'pia', 'pierina', 'piero', 'pierre', 'pierre-adrien', 'pierre-alain', 'pierre-alexandre', 'pierre-alexis', 'pierre-andre', 'pierre-antoine', 'pierre-arnaud', 'pierre-baptiste', 'pierre-charles', 'pierre-edouard', 'pierre-emmanuel', 'pierre-eric', 'pierre-etienne', 'pierre-francois', 'pierre-henri', 'pierre-henry', 'pierre-jean', 'pierre-julien', 'pierre-laurent', 'pierre-louis', 'pierre-loup', 'pierre-luc', 'pierre-marie', 'pierre-michel', 'pierre-nicolas', 'pierre-olivier', 'pierre-paul', 'pierre-philippe', 'pierre-yves', 'pierrette', 'pierric', 'pierrick', 'pierrine', 'pierrot', 'pietro', 'pilar', 'pinar', 'pio', 'placide', 'pol', 'precilia', 'precillia', 'prescilia', 'prescilla', 'prescillia', 'preston', 'priam', 'pricillia', 'primo', 'prince', 'princesse', 'prisca', 'priscilia', 'priscilla', 'priscille', 'priscillia', 'privat', 'priya', 'prosper', 'prune', 'prunelle', 'pulcherie', 'penelope', 'qassim', 'qays', 'quentin', 'quiterie', 'quitterie', 'quoc', 'rabah', 'rabha', 'rabia', 'rabiha', 'racha', 'rached', 'rachel', 'rachele', 'rachelle', 'rachid', 'rachida', 'rachide', 'racim', 'radegonde', 'radhia', 'radia', 'radija', 'radoine', 'radouan', 'radouane', 'rafael', 'rafaela', 'rafaele', 'rafaelle', 'rafael', 'raffael', 'raffaele', 'rafik', 'rafika', 'rahim', 'rahima', 'rahma', 'railey', 'raimond', 'raimonde', 'raissa', 'raja', 'rajaa', 'rajae', 'ralph', 'rama', 'ramata', 'ramatoulaye', 'ramazan', 'ramdane', 'rami', 'ramon', 'ramona', 'ramuntcho', 'ramy', 'ramzi', 'ramzy', 'rana', 'randa', 'randy', 'rani', 'rania', 'ranim', 'ranya', 'raouf', 'raoul', 'raphael', 'raphaela', 'raphaele', 'raphaella', 'raphaelle', 'raphael', 'raphaele', 'raphaelle', 'raquel', 'ratiba', 'raul', 'ravza', 'rawane', 'rayan', 'rayana', 'rayane', 'rayann', 'rayanne', 'rayen', 'rayhan', 'rayhana', 'rayhane', 'raymond', 'raymonde', 'raynal', 'raynald', 'rayyan', 'razane', 'razika', 'raissa', 'rebeca', 'rebecca', 'recep', 'reda', 'redha', 'redoine', 'redouan', 'redouane', 'redwan', 'redwane', 'regina', 'reginald', 'regis', 'rehan', 'reine', 'reine-claude', 'reine-marie', 'reinette', 'rejane', 'rejeanne', 'remi', 'remise', 'remo', 'remond', 'remonde', 'remy', 'renald', 'renaldo', 'renan', 'renata', 'renato', 'renaud', 'renelde', 'renelle', 'renette', 'renzo', 'rene', 'rene-claude', 'renee', 'resul', 'reyan', 'reyhan', 'reymond', 'reymonde', 'reynald', 'reza', 'riad', 'riadh', 'ricardo', 'riccardo', 'richard', 'richarde', 'ricky', 'rico', 'rida', 'ridha', 'ridvan', 'rigobert', 'rihab', 'riham', 'rihana', 'rihanna', 'rihem', 'riley', 'rim', 'rima', 'rime', 'rina', 'rinaldo', 'ringo', 'rino', 'rita', 'ritaj', 'ritchie', 'ritchy', 'ritej', 'rivka', 'riwan', 'riyad', 'rizlaine', 'rizlane', 'rizlene', 'roan', 'robert', 'roberta', 'roberte', 'robertine', 'roberto', 'robin', 'robinson', 'rocco', 'roch', 'rochdi', 'rocky', 'rodney', 'rodolph', 'rodolphe', 'rodrigo', 'rodrigue', 'rogatien', 'roger', 'rogerio', 'rohan', 'rokhaya', 'rokia', 'roland', 'rolande', 'rolf', 'rolland', 'rollande', 'romain', 'romaine', 'romaissa', 'roman', 'romance', 'romane', 'romann', 'romano', 'romaric', 'romayssa', 'romaissa', 'romeo', 'romie', 'romina', 'romuald', 'romy', 'romeo', 'ron', 'ronald', 'ronaldo', 'ronan', 'roni', 'ronnie', 'ronny', 'rony', 'rosa', 'rosa-maria', 'rosaire', 'rosalia', 'rosalie', 'rosaline', 'rosan', 'rosana', 'rosane', 'rosanna', 'rosanne', 'rosaria', 'rosario', 'rose', 'rose-aimee', 'rose-anne', 'rose-helene', 'rose-line', 'rose-marie', 'rose-may', 'roseline', 'rosella', 'roselyne', 'rosemarie', 'rosemary', 'rosemonde', 'rosetta', 'rosette', 'rosiane', 'rosie', 'rosina', 'rosine', 'rosita', 'rosy', 'roumayssa', 'rowan', 'roxana', 'roxane', 'roxanne', 'roy', 'roza', 'rozenn', 'rubben', 'ruben', 'rubens', 'rubis', 'ruby', 'ruddy', 'rudi', 'rudolf', 'rudolph', 'rudolphe', 'rudy', 'ruffin', 'rufin', 'rufine', 'rui', 'rukiye', 'rumeysa', 'ruth', 'ryad', 'ryan', 'ryane', 'ryann', 'rym', 'ryma', 'ryme', 'rebecca', 'reda', 'regine', 'regis', 'rejane', 'remi', 'remy', 'saad', 'saadi', 'saadia', 'saba', 'sabah', 'sabas', 'saber', 'sabiha', 'sabin', 'sabina', 'sabine', 'sabra', 'sabri', 'sabria', 'sabrina', 'sabrine', 'sabry', 'sacha', 'sadek', 'sadi', 'sadia', 'sadio', 'sael', 'safa', 'safaa', 'safae', 'safi', 'safia', 'safiatou', 'safir', 'safiya', 'safouane', 'safwan', 'safwane', 'safya', 'sahar', 'sahel', 'sahin', 'sahra', 'said', 'saida', 'saidou', 'saif', 'saina', 'saint', 'saint-ange', 'saint-jean', 'saint-martin', 'sainte', 'sainte-croix', 'saja', 'sajid', 'sajida', 'sakina', 'salah', 'salah-eddine', 'salaheddine', 'salamata', 'saleha', 'salem', 'salia', 'salif', 'salih', 'saliha', 'salim', 'salima', 'salimata', 'salimatou', 'salime', 'salimou', 'salina', 'saliou', 'sally', 'salma', 'salman', 'salmane', 'salome', 'salomee', 'salomon', 'salome', 'saloua', 'salsabil', 'salvador', 'salvator', 'salvatore', 'salwa', 'sam', 'samah', 'samanta', 'samantha', 'samar', 'samara', 'samba', 'samed', 'samet', 'sami', 'samia', 'samih', 'samiha', 'samir', 'samira', 'sammy', 'samra', 'samson', 'samuel', 'samuelle', 'samy', 'samya', 'sana', 'sanaa', 'sanae', 'sanah', 'sandie', 'sandra', 'sandrina', 'sandrine', 'sandro', 'sandy', 'sania', 'sanjay', 'sanna', 'santa', 'santiago', 'santina', 'santino', 'santo', 'sanya', 'saona', 'saphia', 'saphir', 'sara', 'sarah', 'saran', 'sarha', 'sarina', 'sarra', 'sarrah', 'sasa', 'sascha', 'sasha', 'saskia', 'satine', 'saturnin', 'saturnine', 'satya', 'saul', 'sauveur', 'savana', 'savanah', 'savanna', 'savannah', 'saveria', 'saverio', 'savinien', 'sawsane', 'sawsen', 'sawsene', 'saya', 'sayan', 'sayana', 'sayann', 'said', 'saida', 'saina', 'scarlett', 'scheherazade', 'scholastie', 'scholastique', 'scott', 'scotty', 'sean', 'sebastian', 'sebastien', 'sebastienne', 'seda', 'sedat', 'sefa', 'sefora', 'seher', 'seif', 'sekou', 'selda', 'selen', 'selena', 'selene', 'selenia', 'selia', 'selim', 'selima', 'selin', 'selina', 'selma', 'selman', 'selmen', 'selya', 'selyan', 'sema', 'semi', 'semia', 'semih', 'semra', 'sena', 'seny', 'sephora', 'septime', 'seraphie', 'seraphin', 'seraphine', 'serdar', 'serena', 'serge', 'serges', 'sergine', 'sergio', 'serhat', 'serife', 'serigne', 'serine', 'serkan', 'serpil', 'servais', 'servan', 'servane', 'servanne', 'seth', 'sevan', 'sevda', 'severin', 'severine', 'sevgi', 'sevim', 'sevrine', 'seydina', 'seydou', 'seyma', 'seynabou', 'shade', 'shaden', 'shahid', 'shahin', 'shahine', 'shahinez', 'shai', 'shaima', 'shaina', 'shainez', 'shaineze', 'shakira', 'shams', 'shana', 'shane', 'shanel', 'shanelle', 'shani', 'shania', 'shanice', 'shanna', 'shannen', 'shannon', 'shanon', 'shanone', 'shany', 'shanyce', 'shanys', 'sharleen', 'sharon', 'sharone', 'shaun', 'shauna', 'shawn', 'shayan', 'shayana', 'shayma', 'shayna', 'shayness', 'shaynez', 'shaynna', 'shaima', 'shaina', 'shainez', 'sheherazade', 'sheila', 'sheima', 'shelby', 'sheldon', 'shelly', 'shelsy', 'shems', 'shemsy', 'sherazade', 'sherine', 'sherley', 'sheryl', 'sheryne', 'sheyma', 'shirel', 'shirine', 'shirley', 'shona', 'shun', 'shyrel', 'sherazade', 'sherine', 'siam', 'siana', 'sibel', 'sibylle', 'sid', 'sid-ahmed', 'sidi', 'sidi-mohamed', 'sidney', 'sidoine', 'sidonie', 'sidra', 'sidy', 'siegfried', 'sienna', 'siga', 'sigismond', 'sigrid', 'siham', 'sihame', 'sihem', 'siheme', 'sila', 'silas', 'silia', 'siloe', 'siloe', 'silvain', 'silvana', 'silvano', 'silvere', 'silvia', 'silviane', 'silvie', 'silvio', 'silya', 'simao', 'simeon', 'simon', 'simon-pierre', 'simone', 'simonne', 'simplice', 'simeo', 'simeon', 'sina', 'sinai', 'sinan', 'sinai', 'sinda', 'sindy', 'sinem', 'siobhan', 'sira', 'sirin', 'sirine', 'siryne', 'siwar', 'sixte', 'sixtine', 'skander', 'slim', 'sliman', 'slimane', 'sloan', 'sloane', 'smael', 'smahane', 'smail', 'smain', 'snezana', 'soa', 'soan', 'soana', 'soane', 'soann', 'soanne', 'soazic', 'soazig', 'socrate', 'soen', 'sofia', 'sofian', 'sofiane', 'sofien', 'sofiene', 'sofya', 'sofyan', 'sofyane', 'soha', 'sohaib', 'soham', 'sohan', 'sohane', 'sohann', 'sohanne', 'sohayb', 'soheib', 'soheil', 'sohel', 'soizic', 'soizick', 'sokhna', 'sokona', 'solal', 'solan', 'solane', 'solange', 'solann', 'soleane', 'soledad', 'solen', 'solena', 'solene', 'solenn', 'solenne', 'soline', 'solveig', 'solyne', 'solene', 'soleane', 'somaya', 'somia', 'soner', 'songul', 'sonia', 'sonja', 'sonny', 'sony', 'sonya', 'sophia', 'sophian', 'sophiane', 'sophie', 'sophien', 'soraya', 'soren', 'sorenza', 'soria', 'sory', 'sorya', 'sosthene', 'sosthenes', 'souad', 'souade', 'soufian', 'soufiane', 'soufyane', 'souhaib', 'souhail', 'souhaila', 'souhayl', 'souhayla', 'souheil', 'souheila', 'souheyl', 'souhil', 'souhila', 'soujoud', 'soukaina', 'soukayna', 'soukaina', 'soukeina', 'soukeyna', 'soulaimane', 'soulayman', 'soulaymane', 'soulef', 'souleyman', 'souleymane', 'souleymen', 'soumaya', 'soumayya', 'soumeya', 'soumia', 'soumiya', 'soumya', 'soundous', 'souraya', 'souria', 'sovann', 'soen', 'stacey', 'stacie', 'stacy', 'stan', 'stanis', 'stanislas', 'stanislaw', 'stanislawa', 'stanley', 'stann', 'stany', 'stecy', 'steeve', 'steeven', 'steevens', 'steevy', 'stefan', 'stefania', 'stefanie', 'stefano', 'stefen', 'steffi', 'steffie', 'steffy', 'stella', 'stelly', 'stephan', 'stephane', 'stephania', 'stephano', 'stephany', 'stephen', 'stephie', 'stephy', 'sterenn', 'stessie', 'stessy', 'stevan', 'steve', 'steven', 'stevens', 'stevie', 'stevy', 'stive', 'stiven', 'styven', 'steve', 'stephan', 'stephane', 'stephanie', 'stephen', 'sulayman', 'suleyman', 'sulivan', 'sullivan', 'sully', 'sullyvan', 'sulpice', 'sultan', 'sultana', 'sulyvan', 'sumeyye', 'summer', 'suna', 'sunny', 'susan', 'susana', 'susanne', 'susie', 'suzan', 'suzana', 'suzane', 'suzanna', 'suzanne', 'suzel', 'suzelle', 'suzette', 'suzie', 'suzon', 'suzy', 'sven', 'svetlana', 'swan', 'swane', 'swann', 'swanny', 'swany', 'swen', 'syana', 'sybil', 'sybile', 'sybille', 'sydney', 'sylia', 'sylva', 'sylvain', 'sylvaine', 'sylvana', 'sylvanie', 'sylvano', 'sylver', 'sylvere', 'sylvestre', 'sylvette', 'sylvia', 'sylvian', 'sylviana', 'sylviane', 'sylvianne', 'sylvie', 'sylvina', 'sylvine', 'sylvio', 'symphorien', 'syndie', 'synthia', 'syrielle', 'syrine', 'sebastien', 'segolene', 'selim', 'selene', 'selena', 'selenia', 'sephora', 'seraphin', 'seraphine', 'serine', 'serena', 'sevan', 'severine', 'soren', 'tabatha', 'tacko', 'tadeusz', 'taha', 'tahar', 'tahina', 'tahira', 'tahis', 'taho', 'tahys', 'taieb', 'taina', 'tais', 'tal', 'talha', 'tali', 'talia', 'talina', 'taline', 'talya', 'tamara', 'tamim', 'tanais', 'tancrede', 'tangi', 'tangui', 'tanguy', 'tania', 'tanina', 'tanya', 'tao', 'taoufik', 'taous', 'tara', 'tarak', 'tarek', 'tarik', 'tariq', 'taslim', 'tasnim', 'tasnime', 'tassadit', 'tatiana', 'tatyana', 'tayeb', 'taylan', 'tayler', 'taylor', 'tayna', 'tayron', 'tayson', 'tayssir', 'taina', 'tais', 'tea', 'ted', 'teddy', 'tedy', 'teeyah', 'tehani', 'teiva', 'telio', 'telma', 'tennessee', 'teo', 'teoman', 'terence', 'teresa', 'terrence', 'terry', 'tesnim', 'tesnime', 'tess', 'tessa', 'tessy', 'teva', 'tewfik', 'thaddee', 'thadee', 'thaina', 'thais', 'thalia', 'thalie', 'thalya', 'thanh', 'thanina', 'thao', 'thays', 'thais', 'thea', 'theana', 'theau', 'thelio', 'thelma', 'themis', 'theo', 'theodora', 'theodore', 'theodorine', 'theodose', 'theodule', 'theophane', 'theophile', 'theotime', 'theresa', 'theresia', 'theresien', 'theresine', 'thereze', 'thery', 'thessa', 'thi', 'thia', 'thiago', 'thibaud', 'thibauld', 'thibault', 'thibaut', 'thibo', 'thiebaut', 'thiefaine', 'thierno', 'thierry', 'thiery', 'thifaine', 'thimeo', 'thimote', 'thimothee', 'thimeo', 'thiphaine', 'thom', 'thomas', 'thomassine', 'thony', 'thuy', 'thya', 'thylane', 'thymeo', 'thymeo', 'thea', 'thelio', 'themis', 'theo', 'theodore', 'theophane', 'theophile', 'theotime', 'therese', 'therese-marie', 'tia', 'tiago', 'tiana', 'tibo', 'tiburce', 'tidiane', 'tidjane', 'tifaine', 'tifanie', 'tifanny', 'tifany', 'tifenn', 'tiffaine', 'tiffanie', 'tiffany', 'tiffen', 'tigane', 'tiguidanke', 'tijani', 'tilia', 'tilian', 'tilio', 'till', 'tim', 'timael', 'timao', 'timae', 'timael', 'timea', 'timeo', 'timmy', 'timo', 'timoleon', 'timon', 'timote', 'timotei', 'timothe', 'timothee', 'timothey', 'timothy', 'timothe', 'timothee', 'timoty', 'timote', 'timour', 'timy', 'timea', 'timeo', 'tina', 'tino', 'tiphaine', 'tiphanie', 'tiphany', 'tito', 'titouan', 'tiya', 'tiziana', 'tiziano', 'toan', 'tobias', 'tolga', 'tom', 'toma', 'tomas', 'tommy', 'tomy', 'toni', 'tonia', 'tonin', 'tonino', 'tonio', 'tonny', 'tony', 'tosca', 'toscane', 'toufik', 'touria', 'toussaint', 'toussainte', 'toussine', 'tracy', 'travis', 'trecy', 'tressy', 'trevis', 'trevor', 'tricia', 'trinidad', 'trinite', 'trinity', 'tristan', 'trystan', 'tsipora', 'tuana', 'tuba', 'tugba', 'tugdual', 'tulay', 'turenne', 'txomin', 'tya', 'tyago', 'tyana', 'tyfenn', 'tyffanie', 'tylan', 'tyler', 'tylia', 'tylian', 'tyliann', 'tylio', 'tymeo', 'tymeo', 'tyno', 'typhaine', 'typhanie', 'tyron', 'tyrone', 'tyson', 'tea', 'telio', 'teo', 'terence', 'ufuk', 'ugo', 'ugur', 'ulric', 'ulrich', 'ulrick', 'ulysse', 'uma', 'umberto', 'umit', 'umut', 'unai', 'uranie', 'urbain', 'uriel', 'urielle', 'ursula', 'ursule', 'vadim', 'vaiana', 'valene', 'valentin', 'valentina', 'valentine', 'valentino', 'valere', 'valeria', 'valerian', 'valeriane', 'valerien', 'valerine', 'valerio', 'valery', 'valter', 'valerian', 'valerie', 'valery', 'van', 'vanda', 'vanessa', 'vania', 'vanille', 'vanina', 'vannina', 'vasco', 'vassili', 'vassily', 'veli', 'venant', 'venceslas', 'venise', 'venus', 'vera', 'verane', 'verena', 'veronica', 'veronika', 'vesna', 'vianney', 'vicenta', 'vicente', 'vicenzo', 'vickie', 'vicky', 'victoire', 'victor', 'victoria', 'victorien', 'victorin', 'victorine', 'viggo', 'viktor', 'viktoria', 'vilma', 'vince', 'vincent', 'vincente', 'vincenza', 'vincenzo', 'vinciane', 'violaine', 'violene', 'violeta', 'violetta', 'violette', 'virgil', 'virgile', 'virgina', 'virginia', 'virginie', 'vital', 'vitaline', 'vito', 'vitor', 'vittoria', 'vittorio', 'vivette', 'vivian', 'viviane', 'vivianne', 'vivien', 'vivienne', 'vlad', 'vladimir', 'volkan', 'voltaire', 'veronique', 'wacil', 'wael', 'wafa', 'wafaa', 'wafae', 'wahib', 'wahiba', 'wahid', 'wahil', 'wail', 'wajdi', 'waldemar', 'walid', 'wally', 'walter', 'waly', 'wanda', 'wandrille', 'wanis', 'warda', 'waren', 'warren', 'wassil', 'wassila', 'wassim', 'wassima', 'wassime', 'wayan', 'wayatt', 'wayne', 'wael', 'wail', 'wenceslas', 'wendie', 'wendy', 'werner', 'wesley', 'whitney', 'wiam', 'wiame', 'widad', 'wided', 'wiem', 'wijdane', 'wilfrid', 'wilfried', 'wilhelm', 'wilhelmine', 'wilhem', 'will', 'willem', 'willi', 'william', 'williams', 'willow', 'willy', 'wilma', 'wilson', 'windy', 'winona', 'wissal', 'wissam', 'wissem', 'withney', 'wladimir', 'wladislas', 'wladislaw', 'wladislawa', 'wladyslas', 'wladyslaw', 'wladyslawa', 'wolfgang', 'wyatt', 'wylan', 'xabi', 'xan', 'xavier', 'xaviere', 'yacin', 'yacine', 'yacoub', 'yacouba', 'yael', 'yaelle', 'yagmur', 'yahia', 'yahya', 'yakine', 'yakup', 'yamin', 'yamina', 'yamine', 'yamna', 'yan', 'yana', 'yanael', 'yanael', 'yanel', 'yani', 'yanice', 'yanick', 'yanik', 'yanis', 'yaniss', 'yanisse', 'yaniv', 'yann', 'yanna', 'yanne', 'yanni', 'yannic', 'yannick', 'yannig', 'yannik', 'yannis', 'yara', 'yaren', 'yasar', 'yasemin', 'yasin', 'yasine', 'yasmin', 'yasmina', 'yasmine', 'yasser', 'yassin', 'yassine', 'yassir', 'yassmine', 'yavuz', 'yaya', 'yazid', 'yael', 'yaelle', 'ydriss', 'yelena', 'yeliz', 'yesmine', 'yigit', 'yilmaz', 'ylan', 'ylana', 'ylane', 'ylann', 'ylenzo', 'ylhan', 'ylian', 'yliana', 'ylias', 'ylies', 'yliess', 'yllan', 'ylona', 'ynes', 'yness', 'ynes', 'yoan', 'yoann', 'yoanna', 'yoanne', 'yoel', 'yoen', 'yohan', 'yohann', 'yohanna', 'yola', 'yolaine', 'yolan', 'yoland', 'yolanda', 'yolande', 'yolann', 'yolene', 'yon', 'yona', 'yonathan', 'yoni', 'yonis', 'yonni', 'yoram', 'yoran', 'yorick', 'yoris', 'yosra', 'yossef', 'youcef', 'youen', 'youenn', 'youmna', 'youn', 'youna', 'younes', 'youness', 'younesse', 'younous', 'younes', 'youri', 'yousef', 'yousra', 'yousri', 'youssef', 'youssouf', 'youssra', 'yovan', 'ysaline', 'yse', 'ysee', 'yseult', 'ysia', 'yssa', 'ysee', 'yumi', 'yuna', 'yunus', 'yunus-emre', 'yuri', 'yusra', 'yusuf', 'yvain', 'yvan', 'yvana', 'yvane', 'yvann', 'yvanna', 'yvanne', 'yveline', 'yvelise', 'yvelyne', 'yves', 'yves-marie', 'yvette', 'yvon', 'yvone', 'yvonne', 'yvonnette', 'yvonnick', 'yelena', 'zachari', 'zacharia', 'zacharie', 'zachary', 'zack', 'zackaria', 'zackary', 'zadig', 'zahia', 'zahir', 'zahira', 'zahra', 'zaia', 'zaid', 'zaina', 'zainab', 'zaineb', 'zakari', 'zakaria', 'zakariya', 'zakary', 'zakarya', 'zaki', 'zakia', 'zara', 'zayan', 'zayd', 'zayn', 'zayna', 'zaynab', 'zayneb', 'zaid', 'zaim', 'zaina', 'zdzislaw', 'zehra', 'zeina', 'zeinab', 'zeineb', 'zelal', 'zelda', 'zelia', 'zelie', 'zeliha', 'zenaide', 'zenon', 'zephir', 'zephirin', 'zephyr', 'zeyd', 'zeynab', 'zeyneb', 'zeynep', 'zia', 'ziad', 'zian', 'ziane', 'zidane', 'zied', 'zilan', 'zina', 'zine', 'zineb', 'zineddine', 'zinedine', 'zita', 'ziyad', 'ziyed', 'zoe', 'zoelie', 'zoey', 'zofia', 'zohra', 'zola', 'zora', 'zorah', 'zoran', 'zoubida', 'zoubir', 'zouhair', 'zouhir', 'zouina', 'zoulikha', 'zoya', 'zoe', 'zoelie', 'zoe', 'zulma', 'zya', 'zyad', 'zyed', 'zygmund', 'zygmunt', 'zelia', 'zelie', 'eve', 'eden', 'edouard', 'elena', 'elia', 'eliane', 'elias', 'elie', 'elina', 'eline', 'elio', 'eliot', 'eliott', 'elisa', 'elisabeth', 'elise', 'eloane', 'elodie', 'eloi', 'elouan', 'eloise', 'elya', 'elyna', 'elyne', 'elea', 'eleana', 'eleanore', 'elena', 'eleonore', 'emeline', 'emie', 'emile', 'emilie', 'emilien', 'emy', 'enola', 'enora', 'eric', 'erine', 'ethan', 'etienne', 'eva', 'evan', 'evelyne', 'omer'], resample=False, n_jobs=1, progress_bar=False, copy=True, n_max_keywords=6, n_min_keywords=0, threshold_keywords=0.0, n_docs_in_class=100, keywords_coef=10)[source]

Bases: sklearn.base.BaseEstimator, sklearn.base.TransformerMixin

Class to extract list of keywords from text.

It is compatible with scikit-learn API (i.e. contains fit, transform methods).

Parameters
max_tfidf_featuresint, optional

Size of vocabulary for tfidf. Default value, 10000.

keywordslist, optional

Keywords to extracted as priority. Default value, “keywords” list defined in conf file.

stopwordslist, optional

Stopwords not to be extracted. Default value, “names” and “stopwords” lists defined in conf file.

resamplebool, optional

True if dataset must be resampled according to class distribution, else False. Default value, True.

n_jobsint, optional

Number of cores used for computation. Default value, 20.

copybool, optional

Make a copy of DataFrame. Default value, True.

n_max_keywordsint, optional

Maximum number of keywords to be returned. Default value, 6.

n_min_keywordsint, optional

Minimum number of keywords to be returned. Default value, 0.

threshold_keywordsfloat, optional

Minimum tf-idf score for word to be selected as keyword. Default value, 0.0.

n_docs_in_classint, optional

Number of documents in each classes. Default value, 100.

keywords_coefint, optional

Coefficient multiplied with the tf-idf scores of each keywords. Default value, 10.

Examples

>>> from melusine.summarizer.keywords_generator import KeywordsGenerator
>>> keywords_generator = KeywordsGenerator()
>>> keywords_generator.fit(X, y)
>>> keywords_generator.transform(X)
>>> print(X['keywords'])
Attributes
max_tfidf_features, keywords, stopwords, resample, n_jobs, progress_bar,
copy, n_max_keywords, n_min_keywords, threshold_keywords, n_docs_in_class,
keywords_coef,
tfidf_vectorizerTfidfVectorizer instance from sklearn,
dict_scores_dictionary,

Tf-idf scores for each tokens.

max_score_np.array,
fit(X, y=None)[source]

Fit the weighted tf-idf model with input data.

If resample attribute is True the dataset will be resampled according to class distribution.

Parameters
Xpandas.DataFrame, shape (n_samples, n_features)

X must contain [‘tokens’] column.

yIgnored
Returns
selfobject

Returns the instance itself.

get_keywords(row)[source]

Returns list of keywords in apparition order with the weighted tf-idf already fitted.

Parameters
rowrow of pd.Dataframe, columns [‘tokens’]
Returns
list of strings
resample_docs(X, y=None)[source]

Method for resampling documents according to class distribution.

transform(X)[source]

Returns list of keywords in apparition order for each document with the weighted tf-idf already fitted.

Parameters
Xpandas.DataFrame, shape (n_samples, n_features)

X must contain [‘tokens’] column.

Returns
X_newpandas.DataFrame, shape (n_samples, n_components)

Models subpackage melusine.models

TODO : ADD DESCRIPTION OF THE SUBPACKAGE

List of submodules

NeuralArchitectures melusine.models.neural_architectures
melusine.models.neural_architectures.bert_model(ntargets=18, seq_max=100, nb_meta=134, loss='categorical_crossentropy', activation='softmax', bert_model='jplu/tf-camembert-base')[source]

Pre-defined architecture of a pre-trained Bert model.

Parameters
ntargetsint, optional

Dimension of model output. Default value, 18.

seq_maxint, optional

Maximum input length. Default value, 100.

nb_metaint, optional

Dimension of meta data input. Default value, 252.

lossstr, optional

Loss function for training. Default value, ‘categorical_crossentropy’.

activationstr, optional

Activation function. Default value, ‘softmax’.

bert_modelstr, optional

Model name from HuggingFace library or path to local model Only Camembert and Flaubert supported Default value, ‘camembert-base’

Returns
Model instance
melusine.models.neural_architectures.cnn_model(embedding_matrix_init, ntargets, seq_max, nb_meta, loss='categorical_crossentropy', activation='softmax')[source]

Pre-defined architecture of a CNN model.

Parameters
embedding_matrix_initnp.array,

Pretrained embedding matrix.

ntargetsint, optional

Dimension of model output. Default value, 18.

seq_maxint, optional

Maximum input length. Default value, 100.

nb_metaint, optional

Dimension of meta data input. Default value, 252.

lossstr, optional

Loss function for training. Default value, ‘categorical_crossentropy’.

activationstr, optional

Activation function. Default value, ‘softmax’.

Returns
Model instance
melusine.models.neural_architectures.rnn_model(embedding_matrix_init, ntargets=18, seq_max=100, nb_meta=252, loss='categorical_crossentropy', activation='softmax')[source]

Pre-defined architecture of a RNN model.

Parameters
embedding_matrix_initnp.array,

Pretrained embedding matrix.

ntargetsint, optional

Dimension of model output. Default value, 18.

seq_maxint, optional

Maximum input length. Default value, 100.

nb_metaint, optional

Dimension of meta data input. Default value, 252.

lossstr, optional

Loss function for training. Default value, ‘categorical_crossentropy’.

activationstr, optional

Activation function. Default value, ‘softmax’.

Returns
Model instance
melusine.models.neural_architectures.transformers_model(embedding_matrix_init, ntargets=18, seq_max=100, nb_meta=134, loss='categorical_crossentropy', activation='softmax')[source]

Pre-defined architecture of a Transformer model.

Parameters
embedding_matrix_initnp.array,

Pretrained embedding matrix.

ntargetsint, optional

Dimension of model output. Default value, 18.

seq_maxint, optional

Maximum input length. Default value, 100.

nb_metaint, optional

Dimension of meta data input. Default value, 252.

lossstr, optional

Loss function for training. Default value, ‘categorical_crossentropy’.

activationstr, optional

Activation function. Default value, ‘softmax’.

Returns
Model instance
Train melusine.models.train
class melusine.models.train.NeuralModel[source]

Bases: sklearn.base.BaseEstimator, sklearn.base.ClassifierMixin

Generic class for neural models.

It is compatible with scikit-learn API (i.e. contains fit, transform methods).

Parameters
neural_architecture_functionfunction,

Function which returns a Model instance from Keras. Implemented model functions are: cnn_model, rnn_model, transformers_model, bert_model

pretrained_embeddingnp.array,

Pretrained embedding matrix.

text_input_columnstr,

Input text column to consider for the model.

meta_input_listlist, optional

List of the names of the columns containing the metadata. If empty list or None the model is used without metadata Default value, [‘extension’, ‘dayofweek’, ‘hour’, ‘min’].

vocab_sizeint, optional

Size of vocabulary for neurol network model. Default value, 25000.

seq_sizeint, optional

Maximum size of input for neural model. Default value, 100.

lossstr, optional

Loss function for training. Default value, ‘categorical_crossentropy’.

activationstr, optional

Activation function. Default value, ‘softmax’.

batch_sizeint, optional

Size of batches for the training of the neural network model. Default value, 4096.

n_epochsint, optional

Number of epochs for the training of the neural network model. Default value, 15.

bert_tokenizerstr, optional

Tokenizer name from HuggingFace library or path to local tokenizer Only Camembert and Flaubert supported Default value, ‘camembert-base’

bert_modelstr, optional

Model name from HuggingFace library or path to local model

Only Camembert and Flaubert supported Default value, ‘camembert-base’

Examples

>>> from melusine.models.train import NeuralModel
>>> from melusine.models.neural_architectures import cnn_model
>>> from melusine.nlp_tools.embedding import Embedding
>>> pretrained_embedding = Embedding.load()
>>> list_meta = ['extension', 'dayofweek', 'hour']
>>> nn_model = NeuralModel(cnn_model, pretrained_embedding, list_meta)  #noqa
>>> nn_model.fit(X_train, y_train)  #noqa
>>> y_res = nn_model.predict(X_test)  #noqa
Attributes
architecture_function, pretrained_embedding, text_input_column,
meta_input_list, vocab_size, seq_size, loss, batch_size, n_epochs,
modelModel instance from Keras,
tokenizerTokenizer instance from Melusine,
embedding_matrixnp.array,

Embedding matrix used as input for the neural network model.

fit(X_train, y_train, tensorboard_log_dir=None, validation_data=None, **kwargs)[source]

Fit the neural network model on X and y. If meta_input list is empty list or None the model is used without metadata.

Compatible with scikit-learn API.

Parameters
X_trainpd.DataFrame
y_trainpd.Series
tensorboard_log_dirstr

If not None, will be used as path to write logs for tensorboard Tensordboard callback parameters can be changed in config file

validation_data: tuple

Tuple of validation data Data on which to evaluate the loss and any model metrics at the end of each epoch. The model will not be trained on this data. This could be a tuple (x_val, y_val). validation_data will override validation_split. Default value, None.

Returns
selfobject

Returns the instance

load_nn_model(filepath)[source]

Save model from json and load weights from .h5.

predict(X, **kwargs)[source]

Returns the class predicted.

Parameters
Xpd.DataFrame
Returns
int
predict_proba(X, **kwargs)[source]

Returns the probabilities associated to each classes. If meta_input list is empty list or None the model is used without metadata.

Parameters
Xpd.DataFrame
prediction_intervalfloat, optional

between [0,1], the confidence level of the interval. Only available with tensorflow-probability models.

Returns
——-
scorenp.array

The estimation of probability for each category.

infnp.array, optional

The upper bound of the estimation of probability. Only provided if prediction_interval exists.

supnp.array, optional

The lower bound of the estimation of probability. Only provided if prediction_interval exists.

prepare_email_to_predict(X)[source]

Returns the email as a compatible shape wich depends on the type of neural model

Parameters
Xpd.DataFrame
Returns
list

List of the inputs to the neural model Either [X_seq] if no metadata Or [X_seq, X_meta] if metadata Or [X_seq, X_attention, X_meta] if Bert model

save_nn_model(filepath)[source]

Save model to pickle, json and save weights to .h5.

tokens_to_indices(tokens)[source]

Input : list of tokens [“ma”, “carte_verte”, …] Output : list of indices [46, 359, …]

Config subpackage melusine.config

TODO : ADD DESCRIPTION OF THE SUBPACKAGE

List of submodules

Config melusine.config.config
class melusine.config.config.MelusineConfig[source]

Bases: object

The MelusineConfig class acts as a dict containing configurations. The configurations can be changed dynamically using the switch_config function.

copy()[source]

Copy the config dict

has_key(k)[source]

Checks if given key exists in the config dict

items()[source]

Returns the items of the config dict

keys()[source]

Returns the keys of the config dict

load_melusine_conf() None[source]

Load the melusine configurations. The default configurations are loaded first (the one present in the melusine package). Custom configurations may overwrite the default ones. Custom configuration should be specified in YML and JSON files and placed in a directory. The directory path should be set as the value of the MELUSINE_CONFIG_DIR environment variable. Returns ——- conf: dict

Loaded config dict

values()[source]

Returns the values of the config dict

melusine.config.config.config_deprecation_warnings(config_dict: Dict[str, Any]) None[source]

Raise Deprecation Warning when using deprecated configs

melusine.config.config.load_conf_from_path(config_dir_path: str) Dict[str, Any][source]

Given a directory path Parameters ———- config_dir_path: str

Path to a directory containing YML or JSON conf files

conf: dict

Loaded config dict

melusine.config.config.switch_config(new_config)[source]

Function to change the Melusine configuration using a dict.

Parameters
new_config: dict

Dict containing the new config

melusine.config.config.update_nested_dict(base_dict: dict, update_dict: dict) dict[source]

Update a (possibly) nested dictionary using another (possibly) nested dictionary. Ex:

base_dict = {“A”: {“a”: “0”}} u = {“A”: {“b”: “42”}} update_dict = update_nested_dict(d, u) # Output : {“A”: {“a”: “0”, “b”: “42”}}

Parameters
base_dict: Mapping

Base dict to be updated

update_dict: Mapping

Update dict to merge into d

Returns
base_dict: Mapping

Updated dict

Contributing

Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.

You can contribute in many ways:

Types of Contributions

Report Bugs

Report bugs at https://github.com/MAIF/melusine/issues.

If you are reporting a bug, please include:

  • Your operating system name and version.

  • Any details about your local setup that might be helpful in troubleshooting.

  • Detailed steps to reproduce the bug.

Fix Bugs

Look through the GitHub issues for bugs. Anything tagged with “bug” and “help wanted” is open to whoever wants to implement it.

Implement Features

Look through the GitHub issues for features. Anything tagged with “enhancement” and “help wanted” is open to whoever wants to implement it.

Write Documentation

Amabie could always use more documentation, whether as part of the official melusine docs, in docstrings, or even on the web in blog posts, articles, and such.

Submit Feedback

The best way to send feedback is to file an issue at https://github.com/MAIF/melusine/issues.

If you are proposing a feature:

  • Explain in detail how it would work.

  • Keep the scope as narrow as possible, to make it easier to implement.

  • Remember that this is a volunteer-driven project, and that contributions are welcome :)

Get Started!

Ready to contribute? Here’s how to set up melusine for local development.

  1. Fork the melusine repo on GitHub.

  2. Clone your fork locally:

    $ git clone git@github.com:your_name_here/melusine.git
    
  3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:

    $ mkvirtualenv melusine
    $ cd melusine/
    $ python setup.py develop
    
  4. Create a branch for local development:

    $ git checkout -b name-of-your-bugfix-or-feature
    

    Now you can make your changes locally.

  5. When you’re done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:

    $ flake8 melusine tests
    $ python setup.py test or py.test
    $ tox
    

    To get flake8 and tox, just pip install them into your virtualenv.

  6. Commit your changes and push your branch to GitHub:

    $ git add .
    $ git commit -m "Your detailed description of your changes."
    $ git push origin name-of-your-bugfix-or-feature
    
  7. Submit a pull request through the GitHub website.

Pull Request Guidelines

Before you submit a pull request, check that it meets these guidelines:

  1. The pull request should include tests.

  2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst.

  3. The pull request should work for Python 2.7, 3.4, 3.5 and 3.6, and for PyPy. Check https://travis-ci.org/sachasamama/melusine/pull_requests and make sure that the tests pass for all supported Python versions.

Tips

To run a subset of tests:

$ py.test tests.test_melusine

Deploying

A reminder for the maintainers on how to deploy. Make sure all your changes are committed (including an entry in HISTORY.rst). Then run:

$ bumpversion patch # possible: major / minor / patch
$ git push
$ git push --tags

Travis will then deploy to PyPI if tests pass.

Credits

Development Lead (Quantmetry Team support Maif Team)

Contributors

To be continued …

Indices and tables