Генерация фиктивных данных с Mimesis: Часть I

image

Mimesis — это библиотека для языка программирования Python, которая помогает генерировать фиктивные данные для различных целей. Библиотека написана с использованием средств, включенных в стандартную библиотеку языка Python, потому не имеет никаких сторонних зависимостей. На данный момент библиотека поддерживает 30 языковых стандартов (в числе которых и русский) и более 20 классов-провайдеров, предоставляющих разного рода данные.


Возможность генерировать фиктивные, но в то же время валидные данные бывает очень полезна при разработке приложений, которые подразумевают работу с базой данных. Ручное заполнение базы данных представляется довольно затратным по времени и трудоемким процессом, который выполняется как минимум в 3 этапа — это:


  1. Сбор необходимой информации.
  2. Постобработка собранных данных.
  3. Программирования генераторов данных.

Эта непростая задача по-настоящему усложняется в тот момент, когда требуется сгенерировать не 10-15 пользователей, а 100-150 тысяч пользователей (или иного рода данные). В этой и двух последующих статьях мы постараемся обратить ваше внимание на инструмент, который в разы упрощает процесс генерации тестовых данных, начальной загрузки базы данных и тестирования в целом.


Общая информация


Поддерживаемые языковые стандарты:


Код Название
1 cs Чешский
2 da Датский
3 de Немецкий
4 de-at Австрийский немецкий
5 de-ch Швейцарский немецкий
6 en Английский
7 en-au Австралийский английский
8 en-ca Канадский английский
9 en-gb Британский английский
10 es Испанский
11 es-mx Мексиканский испанский
12 fa Персидский (Фарси)
13 fi Финский
14 fr Французский
15 hu Венгерский
16 is Исландский
17 it Итальянский
18 ja Японский
19 ko Корейский
20 nl Нидерландский
21 nl-be Бельгийский нидерландский
22 no Норвежский
23 pl Польский
24 pt Португальский
25 pt-br Бразильский португальский
26 ru Русский
27 sv Шведский
28 tr Турецкий
29 uk Украинский
30 zh Китайский

Список поддерживаемых классов-провайдеров постоянно расширяется. Все поддерживаемы поставщики данных перечислены тут.


Помимо перечисленных выше, поддерживаются так же специфичные для конкретных стран данные, которые можно импортировать из подпакета builtins:


Провайдер Методы
1 USASpecProvider tracking_number(), ssn(), personality()
2 JapanSpecProvider full_to_half(), half_to_full()
2 RussiaSpecProvider patronymic(), passport_series(), passport_number(), snils()
2 BrazilSpecProvider cpf(), cnpj()

Установка


Установка Mimesis производится как обычно, т.е посредством пакетного менеджера pip. Чтобы установить последнюю свежую версию библиотеки выполните следующую команду:


➜  ~ pip install mimesis

Если по каким-то причинам у вас не получается установить пакет с помощью pip, то попробуйте установить его вручную, как показано ниже:


(venv) ➜  git clone https://github.com/lk-geimfari/mimesis.git
(venv) ➜  cd mimesis/
(venv) ➜  python3 setup.py install
# или
(venv) ➜ make install

Обращаем ваше внимание, что библиотека работает только на Python 3.5 +. Никаких планов по добавлению поддержки Python 2.7 у разработчиков нет.


Генерация


Изначально мы планировали показать генерацию данных на примере небольшого веб-приложения на Flask, но решили отказаться от этой идеи, по той причине, что не все знакомы с Flask и не все горят желанием это исправлять. Потому мы будем показывать все на чистом Python. В случае, если вы захотите перенести все в свой проект на Flask или Django, то вам нужно всего лишь определить статический метод, выполняющий все манипуляции связанные с текущей моделью и вызывать его в тот момент, когда нужно выполнить начальную загрузку БД, как показано в примере ниже.


Модель для Flask (Flask-SQLAlchemy) будет выглядеть как-то так:


class Patient(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(120), unique=True)
    phone_number = db.Column(db.String(25))
    full_name = db.Column(db.String(100))
    weight = db.Column(db.String(64))
    height = db.Column(db.String(64))
    blood_type = db.Column(db.String(64))

    def __init__(self, **kwargs):
        super(Patient, self).__init__(**kwargs)

    @staticmethod
    def _bootstrap(count=2000, locale='en'):
        from mimesis.providers import Personal

        person = Personal(locale)

        for _ in range(count):
            patient = Patient(
                email=person.email(),
                phone_number=person.telephone(),
                full_name=person.full_name(gender='female'),
                weight=person.weight(),
                height=person.height(),
                blood_type=person.blood_type()
            )

            db.session.add(patient)
            try:
                db.session.commit()
            except Exception:
                db.session.rollback()

Переходим в shell-mode:


(venv) ➜ python3 manage.py shell

И генерируем данные, предварительно убедившись в том, что база данных и подопытная модель доступны.


>>> db
<SQLAlchemy engine='sqlite:///db.sqlite'>

>>> Patient
<class 'app.models.Patient'>

>>> Patient()._bootstrap(count=4000, locale='ru') # Сгенерировать 4к записей на русском языке.

Введение


Хотелось бы отметить, что мы будем приводить в примерах только базовые возможности библиотеки и, в основном, обойдемся несколькими наиболее часто встречающимися классами-провайдерами, ибо их слишком много, чтобы рассказывать обо всех в деталях. Если статья возбудит в вас интерес к библиотеке, то вы сможете найти полезные ссылки в самом конце статьи и самостоятельно все изучить.


Библиотека устроена довольно просто и все, что вам необходимо для того, чтобы начать работать с данным — это создать экземпляр класса-провайдера. Наиболее часто встречающиеся данные в веб-приложениях — это личные данные пользователя, такие как имя пользователя, имя, фамилия, возраст, кредитные данные и т.п. Для генерации таких данных существует специальный класс-провайдер — Personal(), который принимает код языкового стандарта в виде строки, как показано ниже:


>>> from mimesis import Personal

# Создаем экземпляр класса-провайдера с данными для исландского языка.
>>> person = Personal('is')

# Выводим исландские мужские имена.
>>> for _ in range(0, 3):
...     person.full_name(gender='male')

`Karl Brynjúlfsson`
`Rögnvald Eiðsson`
`Vésteinn Ríkharðsson`

Практически каждое веб-приложение требует ввода e-mail адреса при регистрации. Библиотека, разумеется поддерживает возможность генерировать e-mail адреса и делается это с помощью метода email() класса Personal(), как показано ниже:


# Женский:
>>> person.email(gender='female')
>>> 'lvana6108@gmail.com'

# Мужской:
>>> person.email(gender='male')
'john2454@yandex.com'

В способе, что был приведен выше кроется небольшая проблема, которая может несколько загрязнять код, если в приложении используется не один единственный класс-провайдер, а несколько. В таких случаях следует использовать объект Generic(), который дает доступ ко всем провайдерам из одного единственного объекта, как показано ниже:


>>> from mimesis import Generic

>>> # Согласно стандарту ISO 639-1, pl - это код Польши.
>>> g = Generic('pl')

>>> g.personal.full_name()
'Lonisława Podsiadło'

>>> g.datetime.birthday(readable=True)
'Listopad 11, 1997'

>>> g.personal.blood_type()
'A−'

Комбинирование данных дает большой простор. К примеру можно создать фиктивных держателей (женского пола) карты Visa (или MasterCard, Maestro):


>>> user = Personal('en')

>>> def get_card(sex='female'):
...     owner = {
...       'owner': user.full_name(sex),
...       'exp_date': user.credit_card_expiration_date(maximum=21),
...       'number': user.credit_card_number(card_type='visa')
...       }
...     return owner

>>> for _ in range(0, 3):
...     get_card()

Вывод:


{'exp_date': '02/20', 'owner': 'Laverna Morrison', 'card_number': '4920 3598 2121 3328'}
{'exp_date': '11/19', 'owner': 'Melany Martinez', 'card_number': '4980 9423 5464 1201'}
{'exp_date': '01/19', 'owner': 'Cleora Mcfarland', 'card_number': '4085 8037 5801 9703'}

Как уже говорилось выше, библиотека поддерживает более 20 классов-провайдеров, которые содержат данные на все случаи жизни (если нет, то ждем PR с исправлением этого ужасного недоразумения). К примеру, если вы разрабатываете приложение ориентированное на грузоперевозки или на иную деятельность, связанную с транспортом и вам необходимо сгенерировать модели транспорта, то вы с легкостью сможете сделать это, воспользовавшись классом-провайдером Transport(), который содержит данные о транспорте:


>>> from mimesis import Transport
>>> trans = Transport()

>>> for _ in range(0, 5):
...     trans.truck()

'Seddon-2537 IM'
'Karrier-7799 UN'
'Minerva-5567 YC'
'Hyundai-2808 XR'
'LIAZ-7174 RM'

Ну или можно указать маску модели транспорта:


>>> for _ in range(0, 5):
...     trans.truck(model_mask="##@") # # - числа, @ - буквы

Henschel-16G
Bean-44D
Unic-82S
Ford-05Q
Kalmar-58C

Нередко при тестировании веб-приложений (тестирование блога — яркий пример) возникает необходимость сгенерировать текстовые данные (текст, предложение, тег и.т.п.). Вбивать вручную текст при тестировании — это долго и скучно и Mimesis позволяет этого избежать, благодаря классу-провайдеру Text():


>>> from mimesis import Text
>>> text = Text('ru')
>>> text.text(quantity=3) # quantity - показатель количества предложений.

'Язык включает в себя средства порождения параллельных легковесных процессов и их взаимодействия через обмен асинхронными сообщениями в соответствии с моделью акторов. Python поддерживает несколько парадигм программирования, в том числе структурное, объектно-ориентированное, функциональное, императивное и аспектно-ориентированное. Например, определение функции, которое использует сопоставление с образцом, для выбора одного из вариантов вычисления или извлечения элемента данных из составной структуры, напоминает уравнение.'

Можно получить список случайных слов:


>>> text = Text('pt-br')
>>> text.words(quantity=5)
['poder', 'de', 'maior', 'só', 'cima']

Cгенерировать название улицы:


>>> from mimesis import Address
>>> address = Address('ru')
>>> address.address()

'ул. Хабаровская 651'

Получить название субъекта/штата/провинции странны, к которой относится выбранный языковой стандарт. В данном случае — это субъект Российской Федерации:


>>> address.state()
'Кировская область'

Сгенерировать координаты:


>>> address.coordinates()
{'latitude': -28.362892454682246, 'longitude': 11.512065821275826}

В библиотеке так же есть средства для романизации кириллических языков (на момент написания статьи поддерживаются только русский и украинский):


>>> from mimesis.decorators import romanized

>>> @romanized('ru')
... def name_ru():
...     return 'Вероника Денисова'
...

>>> @romanized('uk')
>>> def name_uk():
...     return 'Емілія Акуленко'
...

>>> name_ru()
'Veronika Denisova'

>>> name_uk()
'Emіlіja Akulenko'

На самом деле возможностей очень много и можно придумать огромное количество куда более наглядных примеров, в контексте которых данные будут выглядеть более ценными, чем выше. Мы ждем таких примеров от вас — читателей. Мы будем очень рады прочитать об успешном опыте применении библиотеки в ваших проектах.


Полезные ссылки:


Здесь вы можете найти вторую часть статьи.
Здесь вы сможете прочитать дополненный вариант этой статьи и много других интересных статей на разные темы.


Github: lk-geimfari/mimesis
Read the Docs: mimesis


Спасибо за внимание и удачных вам тестов!

Поделиться публикацией

Комментарии 40

    0
    Полезная штука. Хочу такую же как плагин для sketch3. Ну или самому попереть справочники и портировать на праздниках
      0

      Возникла ошибка при установке библиотеки
      SyntaxError: Non-ASCII character '\xe2' in file elizabeth\core\interdata.py on line 720, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

        0
        Скажите, пожалуйста, какая у вас ОС и какой Python использовали?
          0

          Проблема идентичная как на windows 10, так и на Ubuntu 16.04. На обоих ОС python 2.7.12

            0
            Все верно. Возможно вы не обратили внимания на бейджики в ридми. Библиотека работает только на Python 3.
              0

              Спасибо. Действительно не увидел этого. Жаль, что на 2.7 не поддерживается

                0
                Версия 2.7 обошлась бы загрязнением кода, из-за необходимости поддерживать обратную совместимость, и возникновением зависимостей, потому пришлось пойти на такую жертву.
                  0
                  на 2.7 есть faker
                    0
                    Да, верно.
          0

          Проблема идентичная как на windows 10, так и на Ubuntu 16.04. На обоих ОС python 2.7.12.
          Извиняюсь, ошибся веткой

            0
            Скажите, а в чем профит вашей реализации от того же faker?
              +1
              Какого-то глубокого сравнения я не производил, но могу сказать, что данных больше, провайдеров больше. Данные для русского языка достаточно точны и валидны. Скорость работы выше. Я, конечно, не производил сравнения скорости генерации в в боевых условиях (т.е с бд), но даже в обычной генерации данных `elizabeth` работает в разы быстрее, чем `faker`.

              Небольшой пример:
              Ниже приведен скрины работы кода, который генерирует 250к имен (Ф.И).

              image

                +1
                По ссылке старый пакет. Вот актуальный.
                  +1
                  Да, именно с ним я и сравнивал.
              0

              Привет, спасибо. Как-то создал синтетический мир из нескольких тысяч организаций и сотрудников для тестирования сервиса электронного документооборота. Нужны были ИНН, КПП, ОГРН, СНИЛС, ФИО, наименования, города, улицы, индексы, ...


              Подборку исходных данных по ФИО и наименованиям, частично, вот тут отразил:


                0
                Пожалуйста! Мы по возможности стараемся добавлять только данные, которые годятся для всех языков. А ИНН и все такое можно генерировать другими классами-провайдерами, которые с легкостью с этим справляются.
                +3
                А разные группы крови появляются с теми же вероятностями, что и в реальном мире? :)
                  +2
                  :D Нет, к сожаление нет, но такую особенность прикрутить можно. Хорошая идея!
                  0
                  Win10
                  Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:01:18) [MSC v.1900 32 bit (Intel)] on win32
                  Type "help", "copyright", "credits" or "license" for more information.
                  >>> from elizabeth import Personal
                  >>> user = Personal('is')
                  >>> for _ in range(0, 9):
                  ...     print(user.full_name(gender='male'))
                  ...
                  Traceback (most recent call last):
                    File "<stdin>", line 2, in <module>
                    File "C:\Users\mainj\AppData\Local\Programs\Python\Python35-32\lib\encodings\cp437.py", line 19, in encode
                      return codecs.charmap_encode(input,self.errors,encoding_map)[0]
                  UnicodeEncodeError: 'charmap' codec can't encode character '\xf0' in position 5: character maps to <undefined>
                  

                  Нетекстовые данные (и текстовые на английском) нормально генерируются.
                    0
                    Я подозревал, что на Windows проблемы могут возникнуть. Откройте, пожалуйста, issue, чтобы контрибьюторы, у которых Windows могли это исправить.

                    На Linux:
                    Скрин
                    image

                      +1
                      Все, я открыл: #70
                        +1
                        Посмотрите, пожалуйста. Нашлось решение вашей проблемы: #70
                          +1
                          Да, это помогло. Спасибо
                        0
                        Игнорируя второй питон вы оставляете за бортом заметную часть разработчиков.
                        You know it.
                          0
                          Да, вы правы, но ради чистоты и отсутствия зависимостей приходится идти на такую жертву.
                          +1
                          Интересно…
                          как насчет того, чтобы в текст добавить универсальный генератор речей?
                          _https://dezinfo.net/images2/image/09.2009/ukot/1001.jpg
                            0
                            Да, думали над этим. Пока будет только текст, но когда иностранных контрибьюторов наберется — откажемся от текста в файлах и напишем генераторы.
                              0
                              Добавил универсальный генератор речей. Спасибо вам за идею.

                              >>> from elizabeth.builtins import RussiaSpecProvider
                              
                              >>> rus = RussiaSpecProvider()
                              >>> rus.generate_sentence()
                              
                              "Равным образом рамки, задачи и место обучения кадров требуют определения и уточнения направлений прогрессивного развития и перспектив отрасли."
                              
                              0
                              А как оно с падежами? Сгенерить массив из тысяч имен и фамилий это одно, а просклонять? Иными словами, как превратить ['Иван Петрович', 'Василий Байпассович', 'Феофан Илларионович'] в ['выставка картин Иван Петровича', 'концерт Василия Байпассовича', 'арест Феофана Илларионовича'] и есть ли вообще в Питоне средства для подобных трансформаций?
                                0
                                Библиотека не подразумевает, что имена и фамилии будут использоваться в одном контексте. Ф.И для одних задач, Текст — для других. Обеспечить такого рода тонкости для одного языка — это одно, а для 16 — другое. Каждый язык имеет свои тонкости. Потому проще генерировать текст из готовых Предложений.
                                  +1
                                  Хорошо, а если абстрагироваться от текста и просто просклонять, можно ли получить на выходе ['Ивана Петровича'] вместо ['Иван Петрович']? Или не те задачи она решает?
                                    0
                                    Вы сможете сплитить строку и, в зависимости от окончания (метод endswith()), добавлять то чего, требуют правила русского языка. Другими словами, подобного рода вещи делегированы на пользователя. Задача же библиотеки — это дать вам «Ивана Петровича», а уж как его склонять — это уже дело ваше.
                                      +1
                                      Понял, спасибо. Хороший инструмент, стоит освоить.
                                        0
                                        Спасибо за интерес!
                                0
                                Спасибо за статью.
                                А если в файле models.py находится 100500 классов, и внутри каждого 100500 полей. Есть вариант скормить как-то весь models.py и получит готовую базу данных с фиктивными данными без написания staticmethod в каждом классе?
                                  0
                                  В этой библиотеке такой возможности нет и не будет, но мы начинаем работать над подобным проектом.
                                  Планируется поддержка Django Models и SQLAlchemy.

                                  0
                                  Либа названа в честь Элизабет из Биошока? :)

                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                Самое читаемое