Нескучный Pandas — PYTHON https://chel-center.ru/python-yfc курс молодого бойца Wed, 21 Apr 2021 06:12:56 +0000 ru-RU hourly 1 https://wordpress.org/?v=5.9.3 https://chel-center.ru/python-yfc/wp-content/uploads/sites/15/2020/06/cropped-kmb-icon-2-1-32x32.png Нескучный Pandas — PYTHON https://chel-center.ru/python-yfc 32 32 Как оформлять код https://chel-center.ru/python-yfc/2019/10/01/kak-oformlyat-kod/ https://chel-center.ru/python-yfc/2019/10/01/kak-oformlyat-kod/#respond Tue, 01 Oct 2019 01:35:18 +0000 http://chel-center.ru/python-yfc/?p=502 Читать далее «Как оформлять код»

]]>
Этот документ описывает соглашение о том, как писать код для языка python, включая стандартную библиотеку, входящую в состав python.

PEP 8 (Python Enhancement Proposals, англ. или предложения по улучшению Python, рус.) создан на основе рекомендаций Гуидо ван Россума с добавлениями от Барри. Если где-то возникал конфликт, мы выбирали стиль Гуидо. И, конечно, этот PEP может быть неполным (фактически, он, наверное, никогда не будет закончен).

Ключевая идея Гуидо такова: код читается намного больше раз, чем пишется. Собственно, рекомендации о стиле написания кода направлены на то, чтобы улучшить читаемость кода и сделать его согласованным между большим числом проектов. В идеале, весь код будет написан в едином стиле, и любой сможет легко его прочесть.

Это руководство о согласованности и единстве. Согласованность с этим руководством очень важна. Согласованность внутри одного проекта еще важнее. А согласованность внутри модуля или функции — самое важное. Но важно помнить, что иногда это руководство неприменимо, и понимать, когда можно отойти от рекомендаций. Когда вы сомневаетесь, просто посмотрите на другие примеры и решите, какой выглядит лучше.

Две причины для того, чтобы нарушить данные правила:

  1. Когда применение правила сделает код менее читаемым даже для того, кто привык читать код, который следует правилам.
  2. Чтобы писать в едином стиле с кодом, который уже есть в проекте и который нарушает правила (возможно, в силу исторических причин) — впрочем, это возможность переписать чужой код.

Внешний вид кода

Отступы

Используйте 4 пробела на каждый уровень отступа.

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

Правильно:

# Выровнено по открывающему разделителю
foo = long_function_name(var_one, var_two,
                         var_three, var_four)

# Больше отступов включено для отличения его от остальных
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)

Неправильно:

# Аргументы на первой линии запрещены, если не используется вертикальное выравнивание
foo = long_function_name(var_one, var_two,
    var_three, var_four)

# Больше отступов требуется, для отличения его от остальных
def long_function_name(
    var_one, var_two, var_three,
    var_four):
    print(var_one)

Опционально:

# Нет необходимости в большем количестве отступов.
foo = long_function_name(
  var_one, var_two,
  var_three, var_four)

Закрывающие круглые/квадратные/фигурные скобки в многострочных конструкциях могут находиться под первым непробельным символом последней строки списка, например:

my_list = [
    1, 2, 3,
    4, 5, 6,
    ]
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
    )

либо быть под первым символом строки, начинающей многострочную конструкцию:

my_list = [
    1, 2, 3,
    4, 5, 6,
]
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
)

Табуляция или пробелы?

Пробелы — самый предпочтительный метод отступов.

Табуляция должна использоваться только для поддержки кода, написанного с отступами с помощью табуляции.

Python 3 запрещает смешивание табуляции и пробелов в отступах.

Python 2 пытается преобразовать табуляцию в пробелы.

Когда вы вызываете интерпретатор Python 2 в командной строке с параметром -t, он выдает предупреждения (warnings) при использовании смешанного стиля в отступах, а запустив интерпретатор с параметром -tt, вы получите в этих местах ошибки (errors). Эти параметры очень рекомендуются!

Максимальная длина строки

Ограничьте длину строки максимум 79 символами.

Для более длинных блоков текста с меньшими структурными ограничениями (строки документации или комментарии), длину строки следует ограничить 72 символами.

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

Некоторые команды предпочитают большую длину строки. Для кода, поддерживающегося исключительно или преимущественно этой группой, в которой могут прийти к согласию по этому вопросу, нормально увеличение длины строки с 80 до 100 символов (фактически увеличивая максимальную длину до 99 символов), при условии, что комментарии и строки документации все еще будут 72 символа.

Стандартная библиотека Python консервативна и требует ограничения длины строки в 79 символов (а строк документации/комментариев в 72).

Предпочтительный способ переноса длинных строк является использование подразумеваемых продолжений строк Python внутри круглых, квадратных и фигурных скобок. Длинные строки могут быть разбиты на несколько строк, обернутые в скобки. Это предпочтительнее использования обратной косой черты для продолжения строки.

Обратная косая черта все еще может быть использована время от времени. Например, длинная конструкция with не может использовать неявные продолжения, так что обратная косая черта является приемлемой:

with open('/path/to/some/file/you/want/to/read') as file_1, \
        open('/path/to/some/file/being/written', 'w') as file_2:
    file_2.write(file_1.read())

Ещё один случай — assert.

Сделайте правильные отступы для перенесённой строки. Предпочтительнее вставить перенос строки после логического оператора, но не перед ним. Например:

class Rectangle(Blob):

    def __init__(self, width, height,
                 color='black', emphasis=None, highlight=0):
        if (width == 0 and height == 0 and
                color == 'red' and emphasis == 'strong' or
                highlight > 100):
            raise ValueError("sorry, you lose")
        if width == 0 and height == 0 and (color == 'red' or
                                           emphasis is None):
            raise ValueError("I don't think so -- values are %s, %s" %
                             (width, height))
        Blob.__init__(self, width, height,
                      color, emphasis, highlight)

Пустые строки

Отделяйте функции верхнего уровня и определения классов двумя пустыми строками.

Определения методов внутри класса разделяются одной пустой строкой.

Дополнительные пустые строки возможно использовать для разделения различных групп похожих функций. Пустые строки могут быть опущены между несколькими связанными однострочниками (например, набор фиктивных реализаций).

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

Python расценивает символ control+L как незначащий (whitespace), и вы можете использовать его, потому что многие редакторы обрабатывают его как разрыв страницы — таким образом логические части в файле будут на разных страницах. Однако, не все редакторы распознают control+L и могут на его месте отображать другой символ.

Кодировка исходного файла

Кодировка Python должна быть UTF-8 (ASCII в Python 2).

Файлы в ASCII (Python 2) или UTF-8 (Python 3) не должны иметь объявления кодировки.

В стандартной библиотеке, нестандартные кодировки должны использоваться только для целей тестирования, либо когда комментарий или строка документации требует упомянуть имя автора, содержащего не ASCII символы; в остальных случаях использование \x, \u, \U или \N — наиболее предпочтительный способ включить не ASCII символы в строковых литералах.

Начиная с версии python 3.0 в стандартной библиотеке действует следующее соглашение: все идентификаторы обязаны содержать только ASCII символы, и означать английские слова везде, где это возможно (во многих случаях используются сокращения или неанглийские технические термины). Кроме того, строки и комментарии тоже должны содержать лишь ASCII символы. Исключения составляют: (а) test case, тестирующий не-ASCII особенности программы, и (б) имена авторов. Авторы, чьи имена основаны не на латинском алфавите, должны транслитерировать свои имена в латиницу.

Проектам с открытым кодом для широкой аудитории также рекомендуется использовать это соглашение.

Импорты

  • Каждый импорт, как правило, должен быть на отдельной строке.

    Правильно:

    import os
    import sys

    Неправильно:

    import sys, os

    В то же время, можно писать так:

    from subprocess import Popen, PIPE
  • Импорты всегда помещаются в начале файла, сразу после комментариев к модулю и строк документации, и перед объявлением констант.

    Импорты должны быть сгруппированы в следующем порядке:

    1. импорты из стандартной библиотеки
    2. импорты сторонних библиотек
    3. импорты модулей текущего проекта

    Вставляйте пустую строку между каждой группой импортов.

    Указывайте спецификации __all__ после импортов.

  • Рекомендуется абсолютное импортирование, так как оно обычно более читаемо и ведет себя лучше (или, по крайней мере, даёт понятные сообщения об ошибках) если импортируемая система настроена неправильно (например, когда каталог внутри пакета заканчивается на sys.path):

    import mypkg.sibling
    from mypkg import sibling
    from mypkg.sibling import example

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

    from . import sibling
    from .sibling import example

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

    Неявные относительно импорты никогда не должны быть использованы, и были удалены в Python 3.

  • Когда вы импортируете класс из модуля, вполне можно писать вот так:

    from myclass import MyClass
    from foo.bar.yourclass import YourClass

    Если такое написание вызывает конфликт имен, тогда пишите:

    import myclass
    import foo.bar.yourclass

    И используйте «myclass.MyClass» и «foo.bar.yourclass.YourClass».

  • Шаблоны импортов (from import *) следует избегать, так как они делают неясным то, какие имена присутствуют в глобальном пространстве имён, что вводит в заблуждение как читателей, так и многие автоматизированные средства. Существует один оправданный пример использования шаблона импорта, который заключается в опубликовании внутреннего интерфейса как часть общественного API (например, переписав реализацию на чистом Python в модуле акселератора (и не будет заранее известно, какие именно функции будут перезаписаны).

Пробелы в выражениях и инструкциях

Избегайте использования пробелов в следующих ситуациях:

  • Непосредственно внутри круглых, квадратных или фигурных скобок.

    Правильно:

    spam(ham[1], {eggs: 2})

    Неправильно:

    spam( ham[ 1 ], { eggs: 2 } )
  • Непосредственно перед запятой, точкой с запятой или двоеточием:

    Правильно:

    if x == 4: print(x, y); x, y = y, x

    Неправильно:

    if x == 4 : print(x , y) ; x , y = y , x
  • Сразу перед открывающей скобкой, после которой начинается список аргументов при вызове функции:

    Правильно:

    spam(1)

    Неправильно:

    spam (1)
  • Сразу перед открывающей скобкой, после которой следует индекс или срез:

    Правильно:

    dict['key'] = list[index]

    Неправильно:

    dict ['key'] = list [index]
  • Использование более одного пробела вокруг оператора присваивания (или любого другого) для того, чтобы выровнять его с другим:

    Правильно:

    x = 1
    y = 2
    long_variable = 3

    Неправильно:

    x             = 1
    y             = 2
    long_variable = 3

Другие рекомендации

  • Всегда окружайте эти бинарные операторы одним пробелом с каждой стороны: присваивания (=, +=, -= и другие), сравнения (==, <, >, !=, <>, <=, >=, in, not in, is, is not), логические (and, or, not).
  • Если используются операторы с разными приоритетами, попробуйте добавить пробелы вокруг операторов с самым низким приоритетом. Используйте свои собственные суждения, однако, никогда не используйте более одного пробела, и всегда используйте одинаковое количество пробелов по обе стороны бинарного оператора.

    Правильно:

    i = i + 1
    submitted += 1
    x = x*2 - 1
    hypot2 = x*x + y*y
    c = (a+b) * (a-b)

    Неправильно:

    i=i+1
    submitted +=1
    x = x * 2 - 1
    hypot2 = x * x + y * y
    c = (a + b) * (a - b)
  • Не используйте пробелы вокруг знака =, если он используется для обозначения именованного аргумента или значения параметров по умолчанию.

    Правильно:

    def complex(real, imag=0.0):
        return magic(r=real, i=imag)

    Неправильно:

    def complex(real, imag = 0.0):
        return magic(r = real, i = imag)
  • Не используйте составные инструкции (несколько команд в одной строке).

    Правильно:

    if foo == 'blah':
        do_blah_thing()
    do_one()
    do_two()
    do_three()

    Неправильно:

    if foo == 'blah': do_blah_thing()
    do_one(); do_two(); do_three()
  • Иногда можно писать тело циклов while, for или ветку if в той же строке, если команда короткая, но если команд несколько, никогда так не пишите. А также избегайте длинных строк!

    Точно неправильно:

    if foo == 'blah': do_blah_thing()
    for x in lst: total += x
    while t < 10: t = delay()

    Вероятно, неправильно:

    if foo == 'blah': do_blah_thing()
    else: do_non_blah_thing()
    
    try: something()
    finally: cleanup()
    
    do_one(); do_two(); do_three(long, argument,
                                 list, like, this)
    
    if foo == 'blah': one(); two(); three()

Комментарии

Комментарии, противоречащие коду, хуже, чем отсутствие комментариев. Всегда исправляйте комментарии, если меняете код!

Комментарии должны являться законченными предложениями. Если комментарий — фраза или предложение, первое слово должно быть написано с большой буквы, если только это не имя переменной, которая начинается с маленькой буквы (никогда не изменяйте регистр переменной!).

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

Ставьте два пробела после точки в конце предложения.

Программисты, которые не говорят на английском языке, пожалуйста, пишите комментарии на английском, если только вы не уверены на 120%, что ваш код никогда не будут читать люди, не знающие вашего родного языка.

Блоки комментариев

Блок комментариев обычно объясняет код (весь, или только некоторую часть), идущий после блока, и должен иметь тот же отступ, что и сам код. Каждая строчка такого блока должна начинаться с символа # и одного пробела после него (если только сам текст комментария не имеет отступа).

Абзацы внутри блока комментариев разделяются строкой, состоящей из одного символа #.

«Встрочные» комментарии

Старайтесь реже использовать подобные комментарии.

Такой комментарий находится в той же строке, что и инструкция. «Встрочные» комментарии должны отделяться по крайней мере двумя пробелами от инструкции. Они должны начинаться с символа # и одного пробела.

Комментарии в строке с кодом не нужны и только отвлекают от чтения, если они объясняют очевидное. Не пишите вот так:

x = x + 1                 # Increment x

Впрочем, такие комментарии иногда полезны:

x = x + 1                 # Компенсация границы

Строки документации

  • Пишите документацию для всех публичных модулей, функций, классов, методов. Строки документации необязательны для приватных методов, но лучше написать, что делает метод. Комментарий нужно писать после строки с def.
  • PEP 257 объясняет, как правильно и хорошо документировать. Заметьте, очень важно, чтобы закрывающие кавычки стояли на отдельной строке. А еще лучше, если перед ними будет ещё и пустая строка, например:

    """Return a foobang
    
    Optional plotz says to frobnicate the bizbaz first.
    
    """
  • Для однострочной документации можно оставить закрывающие кавычки на той же строке.

Контроль версий

Если вам нужно использовать Subversion, CVS или RCS в ваших исходных кодах, делайте вот так:

__version__ = "$Revision: 1a40d4eaa00b $"
# $Source$

Вставляйте эти строки после документации модуля перед любым другим кодом и отделяйте их пустыми строками по одной до и после.

Соглашения по именованию

Соглашения по именованию переменных в python немного туманны, поэтому их список никогда не будет полным — тем не менее, ниже мы приводим список рекомендаций, действующих на данный момент. Новые модули и пакеты должны быть написаны согласно этим стандартам, но если в какой-либо уже существующей библиотеке эти правила нарушаются, предпочтительнее писать в едином с ней стиле.

Главный принцип

Имена, которые видны пользователю как часть общественного API должны следовать конвенциям, которые отражают использование, а не реализацию.

Описание: Стили имен

Существует много разных стилей. Поможем вам распознать, какой стиль именования используется, независимо от того, для чего он используется.

Обычно различают следующие стили:

  • b (одиночная маленькая буква)
  • B (одиночная заглавная буква)
  • lowercase (слово в нижнем регистре)
  • lower_case_with_underscores (слова из маленьких букв с подчеркиваниями)
  • UPPERCASE (заглавные буквы)
  • UPPERCASE_WITH_UNDERSCORES (слова из заглавных букв с подчеркиваниями)
  • CapitalizedWords (слова с заглавными буквами, или CapWords, или CamelCase). Замечание: когда вы используете аббревиатуры в таком стиле, пишите все буквы аббревиатуры заглавными — HTTPServerError лучше, чем HttpServerError.
  • mixedCase (отличается от CapitalizedWords тем, что первое слово начинается с маленькой буквы)
  • Capitalized_Words_With_Underscores (слова с заглавными буквами и подчеркиваниями — уродливо!)

Ещё существует стиль, в котором имена, принадлежащие одной логической группе, имеют один короткий префикс. Этот стиль редко используется в python, но мы упоминаем его для полноты. Например, функция os.stat() возвращает кортеж, имена в котором традиционно имеют вид st_mode, st_size, st_mtime и так далее. (Так сделано, чтобы подчеркнуть соответствие этих полей структуре системных вызовов POSIX, что помогает знакомым с ней программистам).

В библиотеке X11 используется префикс Х для всех public-функций. В python этот стиль считается излишним, потому что перед полями и именами методов стоит имя объекта, а перед именами функций стоит имя модуля.

В дополнение к этому, используются следующие специальные формы записи имен с добавлением символа подчеркивания в начало или конец имени:

  • _single_leading_underscore: слабый индикатор того, что имя используется для внутренних нужд. Например, from M import * не будет импортировать объекты, чьи имена начинаются с символа подчеркивания.
  • single_trailing_underscore_: используется по соглашению для избежания конфликтов с ключевыми словами языка python, например:

    Tkinter.Toplevel(master, class_='ClassName')
  • __double_leading_underscore: изменяет имя атрибута класса, то есть в классе FooBar поле __boo становится _FooBar__boo.
  • __double_leading_and_trailing_underscore__ (двойное подчеркивание в начале и в конце имени): магические методы или атрибуты, которые находятся в пространствах имен, управляемых пользователем. Например, __init__, __import__ или __file__. Не изобретайте такие имена, используйте их только так, как написано в документации.

Предписания: соглашения по именованию

Имена, которых следует избегать

Никогда не используйте символы l (маленькая латинская буква «эль»), O (заглавная латинская буква «о») или I (заглавная латинская буква «ай») как однобуквенные идентификаторы.

В некоторых шрифтах эти символы неотличимы от цифры один и нуля. Если очень нужно l, пишите вместо неё заглавную L.

Имена модулей и пакетов

Модули должны иметь короткие имена, состоящие из маленьких букв. Можно использовать символы подчеркивания, если это улучшает читабельность. То же самое относится и к именам пакетов, однако в именах пакетов не рекомендуется использовать символ подчёркивания.

Так как имена модулей отображаются в имена файлов, а некоторые файловые системы являются нечувствительными к регистру символов и обрезают длинные имена, очень важно использовать достаточно короткие имена модулей — это не проблема в Unix, но, возможно, код окажется непереносимым в старые версии Windows, Mac, или DOS.

Когда модуль расширения, написанный на С или C++, имеет сопутствующий python-модуль (содержащий интерфейс высокого уровня), С/С++ модуль начинается с символа подчеркивания, например, _socket.

Имена классов

Имена классов должны обычно следовать соглашению CapWords.

Вместо этого могут использоваться соглашения для именования функций, если интерфейс документирован и используется в основном как функции.

Обратите внимание, что существуют отдельные соглашения о встроенных именах: большинство встроенных имен — одно слово (либо два слитно написанных слова), а соглашение CapWords используется только для именования исключений и встроенных констант.

Имена исключений

Так как исключения являются классами, к исключениями применяется стиль именования классов. Однако вы можете добавить Error в конце имени (если, конечно, исключение действительно является ошибкой).

Имена глобальных переменных

Будем надеяться, что глобальные переменные используются только внутри одного модуля. Руководствуйтесь теми же соглашениями, что и для имен функций.

Добавляйте в модули, которые написаны так, чтобы их использовали с помощью from M import *, механизм __all__, чтобы предотвратить экспортирование глобальных переменных. Или же, используйте старое соглашение, добавляя перед именами таких глобальных переменных один символ подчеркивания (которым вы можете обозначить те глобальные переменные, которые используются только внутри модуля).

Имена функций

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

Стиль mixedCase допускается в тех местах, где уже преобладает такой стиль, для сохранения обратной совместимости.

Аргументы функций и методов

Всегда используйте self в качестве первого аргумента метода экземпляра объекта.

Всегда используйте cls в качестве первого аргумента метода класса.

Если имя аргумента конфликтует с зарезервированным ключевым словом python, обычно лучше добавить в конец имени символ подчеркивания, чем исказить написание слова или использовать аббревиатуру. Таким образом, class_ лучше, чем clss. (Возможно, хорошим вариантом будет подобрать синоним).

Имена методов и переменных экземпляров классов

Используйте тот же стиль, что и для имен функций: имена должны состоять из маленьких букв, а слова разделяться символами подчеркивания.

Используйте один символ подчёркивания перед именем для непубличных методов и атрибутов.

Чтобы избежать конфликтов имен с подклассами, используйте два ведущих подчеркивания.

Python искажает эти имена: если класс Foo имеет атрибут с именем __a, он не может быть доступен как Foo.__a. (Настойчивый пользователь все еще может получить доступ, вызвав Foo._Foo__a.) Вообще, два ведущих подчеркивания должны использоваться только для того, чтобы избежать конфликтов имен с атрибутами классов, предназначенных для наследования.

Примечание: есть некоторые разногласия по поводу использования __ имена (см. ниже).

Константы

Константы обычно объявляются на уровне модуля и записываются только заглавными буквами, а слова разделяются символами подчеркивания. Например: MAX_OVERFLOW, TOTAL.

Проектирование наследования

Обязательно решите, каким должен быть метод класса или экземпляра класса (далее — атрибут) — публичный или непубличный. Если вы сомневаетесь, выберите непубличный атрибут. Потом будет проще сделать его публичным, чем наоборот.

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

Мы не используем термин «приватный атрибут», потому что на самом деле в python таких не бывает.

Другой тип атрибутов классов принадлежит так называемому API подклассов (в других языках они часто называются protected). Некоторые классы проектируются так, чтобы от них наследовали другие классы, которые расширяют или модифицируют поведение базового класса. Когда вы проектируете такой класс, решите и явно укажите, какие атрибуты являются публичными, какие принадлежат API подклассов, а какие используются только базовым классом.

Теперь сформулируем рекомендации:

  • Открытые атрибуты не должны иметь в начале имени символа подчеркивания.
  • Если имя открытого атрибута конфликтует с ключевым словом языка, добавьте в конец имени один символ подчеркивания. Это более предпочтительно, чем аббревиатура или искажение написания (однако, у этого правила есть исключение — аргумента, который означает класс, и особенно первый аргумент метода класса (class method) должен иметь имя cls).
  • Назовите простые публичные атрибуты понятными именами и не пишите сложные методы доступа и изменения (accessor/mutator, get/set, — прим. перев.) Помните, что в python очень легко добавить их потом, если потребуется. В этом случае используйте свойства (properties), чтобы скрыть функциональную реализацию за синтаксисом доступа к атрибутам.

    Примечание 1: Свойства (properties) работают только в классах нового стиля (в Python 3 все классы являются таковыми).

    Примечание 2: Постарайтесь избавиться от побочных эффектов, связанным с функциональным поведением; впрочем, такие вещи, как кэширование, вполне допустимы.

    Примечание 3: Избегайте использования вычислительно затратных операций, потому что из-за записи с помощью атрибутов создается впечатление, что доступ происходит (относительно) быстро.

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

    Примечание 1: Будьте внимательны: если подкласс будет иметь то же имя класса и имя атрибута, то вновь возникнет конфликт имен.

    Примечание 2: Механизм изменения имен может затруднить отладку или работу с __getattr__(), однако он хорошо документирован и легко реализуется вручную.

    Примечание 3: Не всем нравится этот механизм, поэтому старайтесь достичь компромисса между необходимостью избежать конфликта имен и возможностью доступа к этим атрибутам.

Общие рекомендации

  • Код должен быть написан так, чтобы не зависеть от разных реализаций языка (PyPy, Jython, IronPython, Pyrex, Psyco и пр.).

    Например, не полагайтесь на эффективную реализацию в CPython конкатенации строк в выражениях типа a+=b или a=a+b. Такие инструкции выполняются значительно медленнее в Jython. В критичных к времени выполнения частях программы используйте ».join() — таким образом склеивание строк будет выполнено за линейное время независимо от реализации python.

  • Сравнения с None должны обязательно выполняться с использованием операторов is или is not, а не с помощью операторов сравнения. Кроме того, не пишите if x, если имеете в виду if x is not None — если, к примеру, при тестировании такая переменная может принять значение другого типа, отличного от None, но при приведении типов может получиться False!
  • При реализации методов сравнения, лучше всего реализовать все 6 операций сравнения (__eq__, __ne__, __lt__, __le__, __gt__, __ge__), чем полагаться на то, что другие программисты будут использовать только конкретный вид сравнения.

    Для минимизации усилий можно воспользоваться декоратором functools.total_ordering() для реализации недостающих методов.

    PEP 207 указывает, что интерпретатор может поменять y > х на х < y, y >= х на х <= y, и может поменять местами аргументы х == y и х != y. Гарантируется, что операции sort() и min() используют оператор <, а max() использует оператор >. Однако, лучше всего осуществить все шесть операций, чтобы не возникало путаницы в других местах.

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

    Правильно:

    def f(x): return 2*x

    Неправильно:

    f = lambda x: 2*x
  • Наследуйте свой класс исключения от Exception, а не от BaseException. Прямое наследование от BaseException зарезервировано для исключений, которые не следует перехватывать.
  • Используйте цепочки исключений соответствующим образом. В Python 3, «raise X from Y» следует использовать для указания явной замены без потери отладочной информации.

    Когда намеренно заменяется исключение (использование «raise X» в Python 2 или «raise X from None» в Python 3.3+), проследите, чтобы соответствующая информация передалась в новое исключение (такие, как сохранение имени атрибута при преобразовании KeyError в AttributeError или вложение текста исходного исключения в новом).

  • Когда вы генерируете исключение, пишите raise ValueError(‘message’) вместо старого синтаксиса raise ValueError, message.

    Старая форма записи запрещена в python 3.

    Такое использование предпочтительнее, потому что из-за скобок не нужно использовать символы для продолжения перенесенных строк, если эти строки длинные или если используется форматирование.

  • Когда код перехватывает исключения, перехватывайте конкретные ошибки вместо простого выражения except:.

    К примеру, пишите вот так:

    try:
        import platform_specific_module
    except ImportError:
        platform_specific_module = None

    Простое написание «except:» также перехватит и SystemExit, и KeyboardInterrupt, что породит проблемы, например, сложнее будет завершить программу нажатием control+C. Если вы действительно собираетесь перехватить все исключения, пишите «except Exception:».

    Хорошим правилом является ограничение использования «except:», кроме двух случаев:

    1. Если обработчик выводит пользователю всё о случившейся ошибке; по крайней мере, пользователь будет знать, что произошла ошибка.
    2. Если нужно выполнить некоторый код после перехвата исключения, а потом вновь «бросить» его для обработки где-то в другом месте. Обычно же лучше пользоваться конструкцией «try…finally».
  • При связывании перехваченных исключений с именем, предпочитайте явный синтаксис привязки, добавленный в Python 2.6:

    try:
        process_data()
    except Exception as exc:
        raise DataProcessingFailedError(str(exc))

    Это единственный синтаксис, поддерживающийся в Python 3, который позволяет избежать проблем неоднозначности, связанных с более старым синтаксисом на основе запятой.

  • При перехвате ошибок операционной системы, предпочитайте использовать явную иерархию исключений, введенную в Python 3.3, вместо анализа значений errno.
  • Постарайтесь заключать в каждую конструкцию try…except минимум кода, чтобы легче отлавливать ошибки. Опять же, это позволяет избежать замаскированных ошибок.

    Правильно:

    try:
        value = collection[key]
    except KeyError:
        return key_not_found(key)
    else:
        return handle_value(value)

    Неправильно:

    try:
        # Здесь много действий!
        return handle_value(collection[key])
    except KeyError:
        # Здесь также перехватится KeyError, который может быть сгенерирован handle_value()
        return key_not_found(key)
  • Когда ресурс является локальным на участке кода, используйте выражение with для того, чтобы после выполнения он был очищен оперативно и надёжно.
  • Менеджеры контекста следует вызывать с помощью отдельной функции или метода, всякий раз, когда они делают что-то другое, чем получение и освобождение ресурсов. Например:

    Правильно:

    with conn.begin_transaction():
        do_stuff_in_transaction(conn)

    Неправильно:

    with conn:
        do_stuff_in_transaction(conn)

    Последний пример не дает никакой информации, указывающей на то, что __enter__ и __exit__ делают что-то кроме закрытия соединения после транзакции. Быть явным важно в данном случае.

  • Используйте строковые методы вместо модуля string — они всегда быстрее и имеют тот же API для unicode-строк. Можно отказаться от этого правила, если необходима совместимость с версиями python младше 2.0.

    В Python 3 остались только строковые методы.

  • Пользуйтесь ».startswith() и ».endswith() вместо обработки срезов строк для проверки суффиксов или префиксов.

    startswith() и endswith() выглядят чище и порождают меньше ошибок. Например:

    Правильно:

    if foo.startswith('bar'):

    Неправильно:

    if foo[:3] == 'bar':
  • Сравнение типов объектов нужно делать с помощью isinstance(), а не прямым сравнением типов:

    Правильно:

    if isinstance(obj, int):

    Неправильно:

    if type(obj) is type(1):

    Когда вы проверяете, является ли объект строкой, обратите внимание на то, что строка может быть unicode-строкой. В python 2 у str и unicode есть общий базовый класс, поэтому вы можете написать:

    if isinstance(obj, basestring):

    Отметим, что в Python 3, unicode и basestring больше не существуют (есть только str) и bytes больше не является своего рода строкой (это последовательность целых чисел).

  • Для последовательностей (строк, списков, кортежей) используйте тот факт, что пустая последовательность есть false:

    Правильно:

    if not seq:
    if seq:

    Неправильно:

    if len(seq)
    if not len(seq)
  • Не пользуйтесь строковыми константами, которые имеют важные пробелы в конце — они невидимы, а многие редакторы (а теперь и reindent.py) обрезают их.
  • Не сравнивайте логические типы с True и False с помощью ==:

    Правильно:

    if greeting:

    Неправильно:

    if greeting == True:

    Совсем неправильно:

    if greeting is True:
  • Оригинал

    Специальные требования для оформления текстов для моих курсантов

    В самом начале скрипта с кодом Python необходимо указывать следующие атрибуты:

    #имя проекта: numpy-example
    #номер версии: 1.0
    #имя файла: example_2.py
    #автор и его учебная группа: Е. Волков, ЭУ-142
    #дата создания: 20.03.2019
    #дата последней модификации: 25.03.2019
    #связанные файлы: пакеты numpy, matplotlib
    #описание: простейшие статистические вычисления
    #версия Python: 3.6
    ]]> https://chel-center.ru/python-yfc/2019/10/01/kak-oformlyat-kod/feed/ 0 Нескучный Pandas: объекты Series и DataFrame. Построение Index https://chel-center.ru/python-yfc/2020/01/06/neskuchnyj-pandas-obekty-series-i-dataframe-postroenie-index/ https://chel-center.ru/python-yfc/2020/01/06/neskuchnyj-pandas-obekty-series-i-dataframe-postroenie-index/#respond Mon, 06 Jan 2020 13:14:20 +0000 http://chel-center.ru/python-yfc/?p=29145 Читать далее «Нескучный Pandas: объекты Series и DataFrame. Построение Index»

    ]]>
    Содержание

    Что такое Pandas DataFrame?

    Pandas — сравнительно новый пакет, надстройка над библиотекой NumPy, обеспечивающий эффективную реализацию класса DataFrame.

    Объекты DataFrame — многомерные массивы с метками для строк и столбцов, а также зачастую с неоднородным типом данных и/или пропущенными данными.

    Помимо удобного интерфейса для хранения маркированных данных, библиотека Pandas реализует множество операций для работы с данными хорошо знакомых пользователям фреймворков баз данных и электронных таблиц.

    Импорт библиотек NumPy и Pandas

    На самом примитивном уровне объекты библиотеки Pandas можно считать расширенной версией структурированных массивов библиотеки NumPy, в которых строки и столбцы идентифицируются метками, а не простыми числовыми индексами. Библиотека Pandas предоставляет множество полезных утилит, методов и функциональности в дополнение к базовым структурам данных, но все последующее изложение потребует понимания этих базовых структур. Позвольте познакомить вас с тремя фундаментальными структурами данных библиотеки Pandas: классами Series, DataFrame и Index.

    Начнем наш сеанс программирования с обычных импортов библиотек NumPy и Pandas:

    Объект Series библиотеки Pandas

    Объект Series библиотеки Pandas — одномерный массив индексированных данных. Его можно создать из списка или массива следующим образом:

    Результат:

    Как мы видели из предыдущего результата, объект Series служит адаптером как для последовательности значений, так и последовательности индексов, к которым можно получить доступ посредством атрибутов values и index. Атрибут values представляет собой массив NumPy:

    Index — массивоподобный объект типа pd.Index:

    Результат:

    Аналогично массивам библиотеки NumPy, к данным можно обращаться по соответствующему им индексу посредством нотации с использованием квадратных скобок языка Python:

    Результат:

    Однако объект Series библиотеки Pandas намного универсальнее и гибче, чем эмулируемый им одномерный массив библиотеки NumPy.

    Объект Series как обобщенный массив NumPy

    Может показаться, что объект Series и одномерный массив библиотеки NumPy взаимозаменяемы. Основное различие между ними — индекс. В то время как индекс массива NumPy, используемый для доступа к значениям, — целочисленный и описывается неявно, индекс объекта Series библиотеки Pandas описывается явно и связывается со значениями.

    Явное описание индекса расширяет возможности объекта Series. Такой индекс не должен быть целым числом, а может состоять из значений любого нужного типа. Например, при желании мы можем использовать в качестве индекса строковые значения:

    Результат:

    При этом доступ к элементам работает обычным образом:

    Результат:

    Объект Series как специализированный словарь

    Объект Series библиотеки Pandas можно рассматривать как специализированную разновидность словаря языка Python. Словарь — структура, задающая соответствие произвольных ключей набору произвольных значений, а объект Series — структура, задающая соответствие типизированных ключей набору типизированных значений.

    Типизация важна: точно так же, как соответствующий типу специализированный код для массива библиотеки NumPy при выполнении определенных операций делает его эффективнее, чем стандартный список Python, информация о типе в объекте Series библиотеки Pandas делает его намного более эффективным для определенных операций, чем словари Python.

    Можно сделать аналогию «объект Series — словарь» еще более наглядной, сконструировав объект Series непосредственно из словаря Python.

    По умолчанию при этом будет создан объект Series с полученным из отсортированных ключей индексом. Следовательно, для него возможен обычный доступ к элементам, такой же, как для словаря. Объект Series поддерживает операции «срезы».

    Результат:

    Создание объектов Series

    Мы уже изучили несколько способов создания объектов Series библиотеки Pandas с нуля.

    Все они представляют собой различные варианты следующего синтаксиса Pandas Series (общий вид синтаксиса):

    где index — необязательный аргумент, а data может быть одной из множества сущностей.

    Например, аргумент data может быть списком или массивом NumPy. В этом случае index по умолчанию будет целочисленной последовательностью:

    Результат:

    Аргумент data может быть скалярным значением, которое будет повторено нужное количество раз для заполнения заданного индекса:

    Результат:

    Аргумент data может быть словарем, в котором index по умолчанию является отсортированными ключами этого словаря:

    Результат:

    В каждом случае индекс можно указать вручную, если необходимо получить другой результат:

    Результат:

    Обратите внимание, что объект Series заполняется только заданными явным образом ключами.

    Объект DataFrame библиотеки Pandas

    Следующая базовая структура библиотеки Pandas — объект DataFrame. Как и объект Series, объект DataFrame можно рассматривать или как обобщение массива NumPy, или как специализированную версию словаря Python. Изучим оба варианта.

    DataFrame как обобщенный массив NumPy

    Если объект Series — аналог одномерного массива с гибкими индексами, объект DataFrame — аналог двумерного массива с гибкими индексами строк и гибкими именами столбцов. Аналогично тому, что двумерный массив можно рассматривать как упорядоченную последовательность выровненных столбцов, объект DataFrame можно рассматривать как упорядоченную последовательность выровненных объектов Series. Под «выровненными» имеется в виду то, что они используют один и тот же индекс.

    Чтобы продемонстрировать это, сначала создадим новый объект Series, содержащий площадь каждого из пяти упомянутых в предыдущем разделе штатов:

    Результат:

    Воспользовавшись объектом population класса Series, сконструируем на основе словаря единый двумерный объект, содержащий всю эту информацию:

    Результат:

    Аналогично объекту Series у объекта DataFrame имеется атрибут index, обеспечивающий доступ к меткам индекса. Еще у объекта DataFrame есть атрибут columns, представляющий собой содержащий метки столбцов объект Index.

    Результат:

    Таким образом, объект DataFrame можно рассматривать как обобщение двумерного массива NumPy, где как у строк, так и у столбцов есть обобщенные индексы для доступа к данным.

    Объект DataFrame как специализированный словарь

    DataFrame можно рассматривать как специализированный словарь. Если словарь задает соответствие ключей значениям, то DataFrame задает соответствие имени столбца объекту Series с данными этого столбца. Например, запрос данных по атрибуту ‘area’ приведет к тому, что будет возвращен объект Series, содержащий площади штатов:

    Результат:

    Создание объектов DataFrame

    Существует множество способов создания объектов DataFrame библиотеки Pandas. Вот несколько примеров.

    Из одного объекта Series

    Объект DataFrame — набор объектов Series.

    DataFrame, состоящий из одного столбца, можно создать на основе одного объекта Series:

    Результат:

    Из списка словарей

    Любой список словарей можно преобразовать в объект DataFrame. Мы воспользуемся простым списковым включением для создания данных:

    Результат:

    Результат:

    Из словаря объектов Series

    Объект DataFrame также можно создать на основе словаря объектов Series (этот пример был приведен ранее):

    Результат:

    Из двумерного массива NumPy

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

    Результат:

    Из структурированного массива NumPy

    Объект DataFrame библиотеки Pandas ведет себя во многом аналогично структурированному массиву библиотеки NumPy и может быть создан непосредственно из него:

    Результат:

    Объект Index библиотеки Pandas

    Как объект Series, так и объект DataFrame содержат явный индекс, обеспечивающий возможность ссылаться на данные и модифицировать их.

    Объект Index можно рассматривать или как неизменяемый массив (immutable array), или как упорядоченное множество (ordered set) (формально мультимножество, так как объекты Index могут содержать повторяющиеся значения). Из этих способов его представления следуют некоторые интересные возможности операций над объектами Index. В качестве простого примера создадим Index из списка целых чисел:

    Результат:

    Объект Index как неизменяемый массив

    Объект Index во многом ведет себя аналогично массиву. Например, для извлечения из него значений или срезов можно использовать стандартную нотацию индексации языка Python. У объектов Index есть много атрибутов.

    Результат:

    Одно из различий между объектами Index и массивами NumPy — неизменяемость индексов, то есть их нельзя модифицировать стандартными средствами:

    Результат:

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

    Основа текста

    ]]>
    https://chel-center.ru/python-yfc/2020/01/06/neskuchnyj-pandas-obekty-series-i-dataframe-postroenie-index/feed/ 0
    10 трюков Python Pandas, которые сделают вашу работу более эффективной https://chel-center.ru/python-yfc/2020/01/06/10-trjukov-python-pandas-kotorye-sdelajut-vashu-rabotu-bolee-effektivnoj/ https://chel-center.ru/python-yfc/2020/01/06/10-trjukov-python-pandas-kotorye-sdelajut-vashu-rabotu-bolee-effektivnoj/#respond Mon, 06 Jan 2020 13:34:43 +0000 http://chel-center.ru/python-yfc/?p=29202 Читать далее «10 трюков Python Pandas, которые сделают вашу работу более эффективной»

    ]]>
    Здесь про это

    Pandas — это широко используемый пакет Python для структурированных данных.

    read_csv

    Все знают эту команду. Но данные, которые вы пытаетесь прочитать, велики, попробуйте добавить этот аргумент: nrows = 5, чтобы загружать только часть данных. Тогда вы можете избежать ошибки, выбрав неправильный разделитель (он не всегда может быть разделен запятой).

    (Или вы можете использовать команду ‘head’ в linux, чтобы проверить первые 5 строк (скажем) в любом текстовом файле: head -n 5 data.txt)

    Затем вы можете извлечь список столбцов, используя df.columns.tolist() для извлечения всех столбцов, а затем добавить аргумент usecols = [‘c1’, ‘c2’,…], чтобы загрузить нужные вам столбцы. 

    Кроме того, если вы знаете типы данных нескольких определенных столбцов, вы можете добавить аргумент dtype = {‘c1’: str, ‘c2’: int,…}, чтобы он загружался быстрее. 

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

    select_dtypes

    Если предварительная обработка данных должна выполняться в Python, эта команда сэкономит вам время. После чтения в таблице типами данных по-умолчанию для каждого столбца могут быть bool, int64, float64, object, category, timedelta64 или datetime64. Вы можете сначала проверить распределение по

    чтобы узнать все возможные типы данных вашего DataFrame

    выбрать sub-dataframe только с числовыми характеристиками.

    copy()

    Это важная команда, если вы еще не слышали о ней. Если вы выполните следующие команды:

    Вы обнаружите, что df1 изменен. Это потому, что df2 = df1 не делает копию df1 и присваивает ее df2, но устанавливает указатель, указывающий на df1. Таким образом, любые изменения в df2 приведут к изменениям в df1. Чтобы это исправить, вы можете сделать либо

    или же

    map()

    Это классная команда для простого преобразования данных. Сначала вы определяете словарь, в котором «ключами» являются старые значения, а «значениями» являются новые значения.

    Некоторые примеры:

    True, False до 1, 0 (для моделирования); определение уровней; определяемые пользователем лексические кодировки.

    apply or not apply?

    Если мы хотим создать новый столбец с несколькими другими столбцами в качестве входных данных, функция apply иногда будет весьма полезна.

    В приведенных выше кодах мы определяем функцию с двумя входными переменными и используем функцию apply, чтобы применить ее к столбцам ‘c1’ и ‘c2’.

    Но проблема «apply» в том , что иногда он слишком медленный . Скажем, если вы хотите вычислить максимум из двух столбцов «c1» и «c2», конечно, вы можете сделать

    но вы найдете это намного медленнее, чем эта команда:

    Вывод: не используйте apply, если вы можете выполнить ту же работу с другими встроенными функциями (они часто быстрее). Например, если вы хотите округлить колонку «с» целыми числами, делать

    или

    Вместо использования функции применяются: 

    value counts

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

    Есть несколько полезных трюков / аргументов:

    1. normalize = True: если вы хотите проверить частоту вместо количества.
    2. dropna = False: если вы также хотите включить пропущенные значения в статистику.
    3. df['c'].value_counts().reset_index().: если вы хотите преобразовать таблицу статистики в кадр данных pandas и манипулировать ею
    4. df['c'].value_counts().sort_index(): показать статистику, отсортированную по разным значениям, в столбце «c» вместо счетчиков.

    number of missing values — количество пустых значений

    При построении моделей может потребоваться исключить строку со слишком большим количеством пропущенных значений / строки со всеми пропущенными значениями. Вы можете использовать .isnull() и .sum() для подсчета количества пропущенных значений в указанных столбцах.

    выбрать строки с конкретными идентификаторами (select rows with specific IDs)

    В SQL мы можем сделать это, используя SELECT * FROM … WHERE ID in (‘A001’, ‘C022’, …), чтобы получить записи с конкретными идентификаторами. Если вы хотите сделать то же самое с Pandas, вы можете сделать

    Процентильные группы (Percentile groups)

    У вас есть числовой столбец, и вы хотите классифицировать значения в этом столбце по группам, скажем, верхние 5% в группе 1, 5–20% в группе 2, 20–50% в группе 3, нижние 50% в группе 4 Конечно, вы можете сделать это с помощью pandas.cut, но я бы хотел предоставить здесь другую опцию:

    который быстро запускается (не применяется функция apply).

    to_csv

    Опять же, это команда, которую все будут использовать. Я хотел бы указать на две уловки здесь. Первый

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

    Еще один трюк — это смешанные целые числа и пропущенные значения. Если столбец содержит как пропущенные значения, так и целые числа, тип данных по-прежнему будет плавающим, а не целым. Когда вы экспортируете таблицу, вы можете добавить float_format=‘%.0f’, чтобы округлить все числа с плавающей точкой до целых чисел. Используйте этот трюк, если вам нужны только целочисленные выходные данные для всех столбцов — вы избавитесь от всех назойливых ‘.0’s.

    Основа текста

    ]]>
    https://chel-center.ru/python-yfc/2020/01/06/10-trjukov-python-pandas-kotorye-sdelajut-vashu-rabotu-bolee-effektivnoj/feed/ 0
    Нескучный Pandas: выбор строк и столбцов одновременно в .iloc https://chel-center.ru/python-yfc/2020/01/06/vybor-strok-i-stolbcov-odnovremenno-v-iloc/ https://chel-center.ru/python-yfc/2020/01/06/vybor-strok-i-stolbcov-odnovremenno-v-iloc/#respond Mon, 06 Jan 2020 15:21:39 +0000 http://chel-center.ru/python-yfc/?p=29210 Читать далее «Нескучный Pandas: выбор строк и столбцов одновременно в .iloc»

    ]]>
    Здесь про это:

    Рассмотрим несколько примеров, чтобы понять, чем отличается .iloc от .loc.

    Выберем две строки и два столбца:

    Результат:

    Осуществим выборку строк и столбцов с помощью среза:

    Результат:

    Выберем 1 значение из столбца и указанной колонки:

    Результат:

    Резюме по .loc и .iloc

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

    • .loc — используется для доступа по строковой метке — т.е. фактически по значению индекса и по названию столбца
    • .iloc — используется для доступа по числовому значению (начиная от 0) — т.е. по номеру строки и номеру столбца

    Как выбрать строки из Pandas DataFrame по условию

    Собираем тестовый набор данных для иллюстрации работы выборки по условию

    Color Shape Price
    Green Rectangle 10
    Green Rectangle 15
    Green Square 5
    Blue Rectangle 5
    Blue Square 10
    Red Square 15
    Red Square 15
    Red Rectangle 5

    Пишем скрипт:

    Результат выполнения:

    Синтаксис выборки строк из Pandas DataFrame по условию

    Вы можете использовать следующую логику для выбора строк в Pandas DataFrame по условию:

    Например, если вы хотите получить строки с зеленым цветом , вам нужно применить:

    Где:

    • Color — это название столбца
    • Green — это условие (значение колонки)

    А вот полный код Python для нашего примера:

    Результат:

    Выберем строки, где цена равна или больше 10

    Чтобы получить все строки, где цена равна или больше 10, Вам нужно применить следующее условие:

    Полный код Python:

    Результат:

    Выберем строки, в которых цвет зеленый, а форма — прямоугольник

    Теперь цель состоит в том, чтобы выбрать строки на основе двух условий:

    • Color зеленый; а также
    • Shape = прямоугольник

    Мы будем использовать символ & для применения нескольких условий. В нашем примере код будет выглядеть так:

    Полный код примера Python для выборки Pandas DataFrame:

    Результат:

    Выберем строки, где цвет зеленый ИЛИ форма прямоугольная

    Для достижения этой цели будем использовать символ | следующим образом:

    Полный код Python 3:

    Результат:

    Выберем строки, где цена не равна 15

    Мы будем использовать комбинацию символов !=, чтобы выбрать строки, цена которых не равна 15:

    Полный код Pandas DF на питоне:

    Результат работы скрипта Python:

    Основа текста

    ]]>
    https://chel-center.ru/python-yfc/2020/01/06/vybor-strok-i-stolbcov-odnovremenno-v-iloc/feed/ 0
    Обработка данных с помощью Pandas https://chel-center.ru/python-yfc/2020/01/06/obrabotka-dannyh-s-pomoshhju-pandas/ https://chel-center.ru/python-yfc/2020/01/06/obrabotka-dannyh-s-pomoshhju-pandas/#respond Mon, 06 Jan 2020 15:44:51 +0000 http://chel-center.ru/python-yfc/?p=29222 Читать далее «Обработка данных с помощью Pandas»

    ]]>
    Обработка данных является одной из важнейших задач в data science и анализе данных, которая включает такие типовые операции, как:

    • Dealing with missing values (Работа с пропущенными значениями) — количественная оценка пропущенных значений для каждого столбца, заполнение и удаление пропущенных значений.
    • Reshaping data (Изменение формы данных) — единая кодировка данных, сводные таблицы, объединения, группировка и агрегирование.
    • Data Sorting (Сортировка данных): упорядочивание значений в порядке возрастания или убывания.
    • Data Filtration (Фильтрация данных): создание подмножества данных согласно тем или иным условиям.
    • Data deduplication (Дедупликация данных) — это технология поиска и устранения дубликатов в хранилищах данных. Применяется для снижения накладные расходов на хранение информации.
    • Data Reduction (Понижение размерности/Сокращение данных): Уменьшение объема данных, сокращение количества используемых признаков и разнообразия их значений. Применяется в случае, когда данные избыточны. Избыточность возникает тогда, когда задачу анализа можно решить с тем же уровнем эффективности и точности, но используя меньшую размерность данных. Это позволяет сократить время и вычислительные затраты на решение задачи, сделать данные и результаты их анализа более интерпретируемыми и понятными для пользователя.
    • Data Access (Доступ к данным): для чтения или записи файлов данных.
    • Data Handling/Data Processing (Обработка данных) или Data Transformation (преобразование данных): выполнение агрегации, статистических и подобных операций над конкретными значениями.
    • Другое: Создание описательных столбцов, поэлементные условные операции.

    Data Wrangling With Pandas (подробнее англ.)

    Data_Wrangling_Pandas_Cheat_Sheet

    Скачать файл

    По мотивам: Using Pandas and Python to Explore Your Dataset

    Code snippets supplementing the Using Pandas and Python to Explore Your Dataset article on Real Python

    ]]>
    https://chel-center.ru/python-yfc/2020/01/06/obrabotka-dannyh-s-pomoshhju-pandas/feed/ 0
    Вкусные рецепты Pandas https://chel-center.ru/python-yfc/2020/01/29/vkusnye-recepty-pandas/ https://chel-center.ru/python-yfc/2020/01/29/vkusnye-recepty-pandas/#respond Wed, 29 Jan 2020 08:49:46 +0000 http://chel-center.ru/python-yfc/?p=29250 Читать далее «Вкусные рецепты Pandas»

    ]]>
    Здесь хранилище коротких сладких примеров и ссылок на полезные рецепты Pandas. Настоятельно рекомендую освоить для эффективного решения реальных задач, а в комментарии делиться своим опытом python-нинга и добавлять свои «фишки».

    Простые, компактные, предназначенные специально для новичков примеры Jupytor Notebook‑кода, отобранные по популярности на Stack Overflow и GitHub. Многие ссылки приводят к полной и исчерпывающей информации по теме примеров.

    Для выполнения приведённых скриптов используются только 2 (два) пакета — Pandas(pd) и Numpy (np). Смело загружайте IDLE Python и записывайте код, приведенный после In [ХХХ]:

    Всё сделано на Python 3.6.8, а в других версиях могут потребоваться незначительные настройки.

    Идиомы   

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

    Здесь несколько аккуратных идиом pandas.

    if-then / if-then-else для одного столбца и переназначение его другому или нескольким столбцам:

    In [1]: df = pd.DataFrame({'AAA': [4, 5, 6, 7],
       ...:                    'BBB': [10, 20, 30, 40],
       ...:                    'CCC': [100, 50, -30, -50]})
       ...: 
    
    In [2]: df
    Out[2]: 
       AAA  BBB  CCC
    0    4   10  100
    1    5   20   50
    2    6   30  -30
    3    7   40  -50
    

    if-then…

    if-then для одной колонки

    In [3]: df.loc[df.AAA >= 5, 'BBB'] = -1
    
    In [4]: df
    Out[4]: 
       AAA  BBB  CCC
    0    4   10  100
    1    5   -1   50
    2    6   -1  -30
    3    7   -1  -50
    

    If-then с присвоением 2 столбцам:

    In [5]: df.loc[df.AAA >= 5, ['BBB', 'CCC']] = 555
    
    In [6]: df
    Out[6]: 
       AAA  BBB  CCC
    0    4   10  100
    1    5  555  555
    2    6  555  555
    3    7  555  555
    

    … добавьте еще одну строку с описанием логики для реализации -else

    In [7]: df.loc[df.AAA < 5, ['BBB', 'CCC']] = 2000
    
    In [8]: df
    Out[8]: 
       AAA   BBB   CCC
    0    4  2000  2000
    1    5   555   555
    2    6   555   555
    3    7   555   555
    

    … или используйте pandas после создании маски

    In [9]: df_mask = pd.DataFrame({'AAA': [True] * 4,
       ...:                         'BBB': [False] * 4,
       ...:                         'CCC': [True, False] * 2})
       ...: 
    
    In [10]: df.where(df_mask, -1000)
    Out[10]: 
       AAA   BBB   CCC
    0    4 -1000  2000
    1    5 -1000 -1000
    2    6 -1000   555
    3    7 -1000 -1000
    

    if-then-else, используя where() из numpy

    In [11]: df = pd.DataFrame({'AAA': [4, 5, 6, 7],
       ....:                    'BBB': [10, 20, 30, 40],
       ....:                    'CCC': [100, 50, -30, -50]})
       ....: 
    
    In [12]: df
    Out[12]: 
       AAA  BBB  CCC
    0    4   10  100
    1    5   20   50
    2    6   30  -30
    3    7   40  -50
    
    In [13]: df['logic'] = np.where(df['AAA'] > 5, 'high', 'low')
    
    In [14]: df
    Out[14]: 
       AAA  BBB  CCC logic
    0    4   10  100   low
    1    5   20   50   low
    2    6   30  -30  high
    3    7   40  -50  high
    

    Разделение   

    Разделение фрейма по логическим критериям

    In [15]: df = pd.DataFrame({'AAA': [4, 5, 6, 7],
       ....:                    'BBB': [10, 20, 30, 40],
       ....:                    'CCC': [100, 50, -30, -50]})
       ....: 
    
    In [16]: df
    Out[16]: 
       AAA  BBB  CCC
    0    4   10  100
    1    5   20   50
    2    6   30  -30
    3    7   40  -50
    
    In [17]: df[df.AAA <= 5]
    Out[17]: 
       AAA  BBB  CCC
    0    4   10  100
    1    5   20   50
    
    In [18]: df[df.AAA > 5]
    Out[18]: 
       AAA  BBB  CCC
    2    6   30  -30
    3    7   40  -50
    

    Конструкции сложных критериев   

    Отбор данных из нескольких столбцов по комплексным критериям

    In [19]: df = pd.DataFrame({'AAA': [4, 5, 6, 7],
       ....:                    'BBB': [10, 20, 30, 40],
       ....:                    'CCC': [100, 50, -30, -50]})
       ....: 
    
    In [20]: df
    Out[20]: 
       AAA  BBB  CCC
    0    4   10  100
    1    5   20   50
    2    6   30  -30
    3    7   40  -50
    

    …и (без использования объекта Series в качестве возвращаемого значения)

    In [21]: df.loc[(df['BBB'] < 25) & (df['CCC'] >= -40), 'AAA']
    Out[21]: 
    0    4
    1    5
    Name: AAA, dtype: int64
    

    …или так (без использования объекта Series в качестве возвращаемого значения)

    In [22]: df.loc[(df['BBB'] > 25) | (df['CCC'] >= -40), 'AAA']
    Out[22]: 
    0    4
    1    5
    2    6
    3    7
    Name: AAA, dtype: int64
    

    …и (с модификацией объекта DataFrame)

    In [23]: df.loc[(df['BBB'] > 25) | (df['CCC'] >= 75), 'AAA'] = 0.1
    
    In [24]: df
    Out[24]: 
       AAA  BBB  CCC
    0  0.1   10  100
    1  5.0   20   50
    2  0.1   30  -30
    3  0.1   40  -50
    

    Выберите строки с данными, ближайшими к определенному значению с использованием argsort

    In [25]: df = pd.DataFrame({'AAA': [4, 5, 6, 7],
       ....:                    'BBB': [10, 20, 30, 40],
       ....:                    'CCC': [100, 50, -30, -50]})
       ....: 
    
    In [26]: df
    Out[26]: 
       AAA  BBB  CCC
    0    4   10  100
    1    5   20   50
    2    6   30  -30
    3    7   40  -50
    
    In [27]: aValue = 43.0
    
    In [28]: df.loc[(df.CCC - aValue).abs().argsort()]
    Out[28]: 
       AAA  BBB  CCC
    1    5   20   50
    0    4   10  100
    2    6   30  -30
    3    7   40  -50
    

    Динамическое сокращение списка критериев с помощью бинарных операторов

    In [29]: df = pd.DataFrame({'AAA': [4, 5, 6, 7],
       ....:                    'BBB': [10, 20, 30, 40],
       ....:                    'CCC': [100, 50, -30, -50]})
       ....: 
    
    In [30]: df
    Out[30]: 
       AAA  BBB  CCC
    0    4   10  100
    1    5   20   50
    2    6   30  -30
    3    7   40  -50
    
    In [31]: Crit1 = df.AAA <= 5.5
    
    In [32]: Crit2 = df.BBB == 10.0
    
    In [33]: Crit3 = df.CCC > -40.0
    

    Можно использовать составной код:

    In [34]: AllCrit = Crit1 & Crit2 & Crit3
    

    … или использовать динамически построенные списки критериев

    In [35]: import functools
    
    In [36]: CritList = [Crit1, Crit2, Crit3]
    
    In [37]: AllCrit = functools.reduce(lambda x, y: x & y, CritList)
    
    In [38]: df[AllCrit]
    Out[38]: 
       AAA  BBB  CCC
    0    4   10  100
    

    Отбор   

    Фреймы данных   

    Документация по индексированию находится здесь.

    Использование меток строк и условных значений

    In [39]: df = pd.DataFrame({'AAA': [4, 5, 6, 7],
       ....:                    'BBB': [10, 20, 30, 40],
       ....:                    'CCC': [100, 50, -30, -50]})
       ....: 
    
    In [40]: df
    Out[40]: 
       AAA  BBB  CCC
    0    4   10  100
    1    5   20   50
    2    6   30  -30
    3    7   40  -50
    
    In [41]: df[(df.AAA <= 6) & (df.index.isin([0, 2, 4]))]
    Out[41]: 
       AAA  BBB  CCC
    0    4   10  100
    2    6   30  -30
    

    Используйте loc для нарезки по меткам и iloc для позиционной нарезки

    In [42]: df = pd.DataFrame({'AAA': [4, 5, 6, 7],
       ....:                    'BBB': [10, 20, 30, 40],
       ....:                    'CCC': [100, 50, -30, -50]},
       ....:                   index=['foo', 'bar', 'boo', 'kar'])
       ....: 
    

    Есть два метода нарезки и третий общий вариант.

    1. Позиционно-ориентированный (стиль нарезки Python: исключая end).
    2. Ориентированный на метки (стиль нарезки Python: включая end).
    3. Общие (любой стиль нарезки: зависит от того, содержит ли срез метки или позиции).
    In [43]: df.loc['bar':'kar']  # Label
    Out[43]: 
         AAA  BBB  CCC
    bar    5   20   50
    boo    6   30  -30
    kar    7   40  -50
    
    # Generic
    In [44]: df.iloc[0:3]
    Out[44]: 
         AAA  BBB  CCC
    foo    4   10  100
    bar    5   20   50
    boo    6   30  -30
    
    In [45]: df.loc['bar':'kar']
    Out[45]: 
         AAA  BBB  CCC
    bar    5   20   50
    boo    6   30  -30
    kar    7   40  -50
    

    Если индекс целое число, то при начальном значением индекса, отличным от 0, или его приращением, отличным от 1, возникает неопределенность.

    In [46]: data = {'AAA': [4, 5, 6, 7],
       ....:         'BBB': [10, 20, 30, 40],
       ....:         'CCC': [100, 50, -30, -50]}
       ....: 
    
    In [47]: df2 = pd.DataFrame(data=data, index=[1, 2, 3, 4])  # Note index starts at 1.
    
    In [48]: df2.iloc[1:3]  # Position-oriented
    Out[48]: 
       AAA  BBB  CCC
    2    5   20   50
    3    6   30  -30
    
    In [49]: df2.loc[1:3]  # Label-oriented
    Out[49]: 
       AAA  BBB  CCC
    1    4   10  100
    2    5   20   50
    3    6   30  -30
    

    Использование оператора инверсии (~) для дополнения маски

    In [50]: df = pd.DataFrame({'AAA': [4, 5, 6, 7],
       ....:                    'BBB': [10, 20, 30, 40],
       ....:                    'CCC': [100, 50, -30, -50]})
       ....: 
    
    In [51]: df
    Out[51]: 
       AAA  BBB  CCC
    0    4   10  100
    1    5   20   50
    2    6   30  -30
    3    7   40  -50
    
    In [52]: df[~((df.AAA <= 6) & (df.index.isin([0, 2, 4])))]
    Out[52]: 
       AAA  BBB  CCC
    1    5   20   50
    3    7   40  -50
    

    Новые колонки   

    Эффективное и динамическое создание новых столбцов с использованием applymap

    In [53]: df = pd.DataFrame({'AAA': [1, 2, 1, 3],
       ....:                    'BBB': [1, 1, 2, 2],
       ....:                    'CCC': [2, 1, 3, 1]})
       ....: 
    
    In [54]: df
    Out[54]: 
       AAA  BBB  CCC
    0    1    1    2
    1    2    1    1
    2    1    2    3
    3    3    2    1
    
    In [55]: source_cols = df.columns   # Or some subset would work too
    
    In [56]: new_cols = [str(x) + "_cat" for x in source_cols]
    
    In [57]: categories = {1: 'Alpha', 2: 'Beta', 3: 'Charlie'}
    
    In [58]: df[new_cols] = df[source_cols].applymap(categories.get)
    
    In [59]: df
    Out[59]: 
       AAA  BBB  CCC  AAA_cat BBB_cat  CCC_cat
    0    1    1    2    Alpha   Alpha     Beta
    1    2    1    1     Beta   Alpha    Alpha
    2    1    2    3    Alpha    Beta  Charlie
    3    3    2    1  Charlie    Beta    Alpha
    

    Сохранение столбцов, используя функцию min() с group by

    In [60]: df = pd.DataFrame({'AAA': [1, 1, 1, 2, 2, 2, 3, 3],
       ....:                    'BBB': [2, 1, 3, 4, 5, 1, 2, 3]})
       ....: 
    
    In [61]: df
    Out[61]: 
       AAA  BBB
    0    1    2
    1    1    1
    2    1    3
    3    2    4
    4    2    5
    5    2    1
    6    3    2
    7    3    3
    

    Способ 1: idxmin() для получения индексов минимумов

    In [62]: df.loc[df.groupby("AAA")["BBB"].idxmin()]
    Out[62]: 
       AAA  BBB
    1    1    1
    5    2    1
    6    3    2
    

    Способ 2: сортировка, а затем получение первого

    In [63]: df.sort_values(by="BBB").groupby("AAA", as_index=False).first()
    Out[63]: 
       AAA  BBB
    0    1    1
    1    2    1
    2    3    2
    

    Обратите внимание на одинаковые результаты, за исключением индекса.

    Мульти-индексация   

    Полная документация по мульти-индексации находится здесь.

    Создание мульти-индекса по меткам фрейма

    In [64]: df = pd.DataFrame({'row': [0, 1, 2],
       ....:                    'One_X': [1.1, 1.1, 1.1],
       ....:                    'One_Y': [1.2, 1.2, 1.2],
       ....:                    'Two_X': [1.11, 1.11, 1.11],
       ....:                    'Two_Y': [1.22, 1.22, 1.22]})
       ....: 
    
    In [65]: df
    Out[65]: 
       row  One_X  One_Y  Two_X  Two_Y
    0    0    1.1    1.2   1.11   1.22
    1    1    1.1    1.2   1.11   1.22
    2    2    1.1    1.2   1.11   1.22
    
    # As Labelled Index
    In [66]: df = df.set_index('row')
    
    In [67]: df
    Out[67]: 
         One_X  One_Y  Two_X  Two_Y
    row                            
    0      1.1    1.2   1.11   1.22
    1      1.1    1.2   1.11   1.22
    2      1.1    1.2   1.11   1.22
    
    # With Hierarchical Columns
    In [68]: df.columns = pd.MultiIndex.from_tuples([tuple(c.split('_'))
       ....:                                         for c in df.columns])
       ....: 
    
    In [69]: df
    Out[69]: 
         One        Two      
           X    Y     X     Y
    row                      
    0    1.1  1.2  1.11  1.22
    1    1.1  1.2  1.11  1.22
    2    1.1  1.2  1.11  1.22
    
    # Now stack & Reset
    In [70]: df = df.stack(0).reset_index(1)
    
    In [71]: df
    Out[71]: 
        level_1     X     Y
    row                    
    0       One  1.10  1.20
    0       Two  1.11  1.22
    1       One  1.10  1.20
    1       Two  1.11  1.22
    2       One  1.10  1.20
    2       Two  1.11  1.22
    
    # And fix the labels (Notice the label 'level_1' got added automatically)
    In [72]: df.columns = ['Sample', 'All_X', 'All_Y']
    
    In [73]: df
    Out[73]: 
        Sample  All_X  All_Y
    row                     
    0      One   1.10   1.20
    0      Two   1.11   1.22
    1      One   1.10   1.20
    1      Two   1.11   1.22
    2      One   1.10   1.20
    2      Two   1.11   1.22
    

    Арифметика   

    Выполнение арифметики с мульти-индексом для поиска

    In [74]: cols = pd.MultiIndex.from_tuples([(x, y) for x in ['A', 'B', 'C']
       ....:                                   for y in ['O', 'I']])
       ....: 
    
    In [75]: df = pd.DataFrame(np.random.randn(2, 6), index=['n', 'm'], columns=cols)
    
    In [76]: df
    Out[76]: 
              A                   B                   C          
              O         I         O         I         O         I
    n  0.469112 -0.282863 -1.509059 -1.135632  1.212112 -0.173215
    m  0.119209 -1.044236 -0.861849 -2.104569 -0.494929  1.071804
    
    In [77]: df = df.div(df['C'], level=1)
    
    In [78]: df
    Out[78]: 
              A                   B              C     
              O         I         O         I    O    I
    n  0.387021  1.633022 -1.244983  6.556214  1.0  1.0
    m -0.240860 -0.974279  1.741358 -1.963577  1.0  1.0
    

    Нарезка   

    Нарезка мульти-индекса с xs

    In [79]: coords = [('AA', 'one'), ('AA', 'six'), ('BB', 'one'), ('BB', 'two'),
       ....:           ('BB', 'six')]
       ....: 
    
    In [80]: index = pd.MultiIndex.from_tuples(coords)
    
    In [81]: df = pd.DataFrame([11, 22, 33, 44, 55], index, ['MyData'])
    
    In [82]: df
    Out[82]: 
            MyData
    AA one      11
       six      22
    BB one      33
       two      44
       six      55
    

    Для вычисления сечения 1-го уровня и 1-й оси указателя:

    # Примечание: уровень и ось являются необязательными и по умолчанию равны нулю
    In [83]: df.xs('BB', level=0, axis=0)
    Out[83]: 
         MyData
    one      33
    two      44
    six      55
    

    … и теперь 2-й уровень 1-й оси.

    In [84]: df.xs('six', level=1, axis=0)
    Out[84]: 
        MyData
    AA      22
    BB      55
    

    Нарезка мульти-индекса с помощью xs, метод №2

    In [85]: import itertools
    
    In [86]: index = list(itertools.product(['Ada', 'Quinn', 'Violet'],
       ....:                                ['Comp', 'Math', 'Sci']))
       ....: 
    
    In [87]: headr = list(itertools.product(['Exams', 'Labs'], ['I', 'II']))
    
    In [88]: indx = pd.MultiIndex.from_tuples(index, names=['Student', 'Course'])
    
    In [89]: cols = pd.MultiIndex.from_tuples(headr)   # Notice these are un-named
    
    In [90]: data =  df = pd.DataFrame(data, indx, cols)
    
    In [92].transform(sum).sort_values(by='data')
    
    In [123]: sorted_df = df.loc[agg_n_sort_order.index]
    
    In [124]: sorted_df
    Out[124]: 
      code  data   flag
    1  bar -0.21   True
    4  bar -0.59  False
    0  foo  0.16  False
    3  foo  0.45   True
    2  baz  0.33  False
    5  baz  0.62   True
    

    Создание нескольких агрегированных столбцов

    In [125]: rng = pd.date_range(start="2014-10-07", periods=10, freq='2min')
    
    In [126]: ts = pd.Series(data=list(range(10)), index=rng)
    
    In [127]: def MyCust(x):
       .....:     if len(x) > 2:
       .....:         return x[1] * 1.234
       .....:     return pd.NaT
       .....: 
    
    In [128]: mhc = {'Mean': np.mean, 'Max': np.max, 'Custom': MyCust}
    
    In [129]: ts.resample("5min").apply(mhc)
    Out[129]: 
    Mean    2014-10-07 00:00:00        1
            2014-10-07 00:05:00      3.5
            2014-10-07 00:10:00        6
            2014-10-07 00:15:00      8.5
    Max     2014-10-07 00:00:00        2
            2014-10-07 00:05:00        4
            2014-10-07 00:10:00        7
            2014-10-07 00:15:00        9
    Custom  2014-10-07 00:00:00    1.234
            2014-10-07 00:05:00      NaT
            2014-10-07 00:10:00    7.404
            2014-10-07 00:15:00      NaT
    dtype: object
    
    In [130]: ts
    Out[130]: 
    2014-10-07 00:00:00    0
    2014-10-07 00:02:00    1
    2014-10-07 00:04:00    2
    2014-10-07 00:06:00    3
    2014-10-07 00:08:00    4
    2014-10-07 00:10:00    5
    2014-10-07 00:12:00    6
    2014-10-07 00:14:00    7
    2014-10-07 00:16:00    8
    2014-10-07 00:18:00    9
    Freq: 2T, dtype: int64
    

    Создание столбца и переназначение его во фрейме данных.

    In [131]: df = pd.DataFrame({'Color': 'Red Red Red Blue'.split(),
       .....:                    'Value': [100, 150, 50, 50]})
       .....: 
    
    In [132]: df
    Out[132]: 
      Color  Value
    0   Red    100
    1   Red    150
    2   Red     50
    3  Blue     50
    
    In [133]: df['Counts'] = df.groupby(['Color']).transform(len)
    
    In [134]: df
    Out[134]: 
      Color  Value  Counts
    0   Red    100       3
    1   Red    150       3
    2   Red     50       3
    3  Blue     50       1
    

    Сдвиг групп значений в столбце на основе индекса

    In [135]: df = pd.DataFrame({'line_race': [10, 10, 8, 10, 10, 8],
       .....:                    'beyer': [99, 102, 103, 103, 88, 100]},
       .....:                   index=['Last Gunfighter', 'Last Gunfighter',
       .....:                          'Last Gunfighter', 'Paynter', 'Paynter',
       .....:                          'Paynter'])
       .....: 
    
    In [136]: df
    Out[136]: 
                     line_race  beyer
    Last Gunfighter         10     99
    Last Gunfighter         10    102
    Last Gunfighter          8    103
    Paynter                 10    103
    Paynter                 10     88
    Paynter                  8    100
    
    In [137]: df['beyer_shifted'] = df.groupby(level=0)['beyer'].shift(1)
    
    In [138]: df
    Out[138]: 
                     line_race  beyer  beyer_shifted
    Last Gunfighter         10     99            NaN
    Last Gunfighter         10    102           99.0
    Last Gunfighter          8    103          102.0
    Paynter                 10    103            NaN
    Paynter                 10     88          103.0
    Paynter                  8    100           88.0
    

    Выбор строки с максимальным значением из каждой группы

    In [139]: df = pd.DataFrame({'host': ['other', 'other', 'that', 'this', 'this'],
       .....:                    'service': ['mail', 'web', 'mail', 'mail', 'web'],
       .....:                    'no': [1, 2, 1, 2, 1]}).set_index(['host', 'service'])
       .....: 
    
    In [140]: mask = df.groupby(level=0).agg('idxmax')
    
    In [141]: df_count = df.loc[mask['no']].reset_index()
    
    In [142]: df_count
    Out[142]: 
        host service  no
    0  other     web   2
    1   that    mail   1
    2   this    mail   2
    

    Группировка как в Python itertools.groupby

    In [143]: df = pd.DataFrame([0, 1, 0, 1, 1, 1, 0, 1, 1], columns=['A'])
    
    In [144]: df.A.groupby((df.A != df.A.shift()).cumsum()).groups
    Out[144]: 
    {1: Int64Index([0], dtype='int64'),
     2: Int64Index([1], dtype='int64'),
     3: Int64Index([2], dtype='int64'),
     4: Int64Index([3, 4, 5], dtype='int64'),
     5: Int64Index([6], dtype='int64'),
     6: Int64Index([7, 8], dtype='int64')}
    
    In [145]: df.A.groupby((df.A != df.A.shift()).cumsum()).cumsum()
    Out[145]: 
    0    0
    1    1
    2    0
    3    1
    4    2
    5    3
    6    0
    7    1
    8    2
    Name: A, dtype: int64
    

    Расщепление   

    Расщепление фрейма

    Создание списка фреймов при разделении на основе логики, включенной в строки.

    In [146]: df = pd.DataFrame(data={'Case': ['A', 'A', 'A', 'B', 'A', 'A', 'B', 'A',
       .....:                                  'A'],
       .....:                         'Data': np.random.randn(9)})
       .....: 
    
    In [147]: dfs = list(zip(*df.groupby((1 * (df['Case'] == 'B')).cumsum()
       .....:                .rolling(window=3, min_periods=1).median())))[-1]
       .....: 
    
    In [148]: dfs[0]
    Out[148]: 
      Case      Data
    0    A  0.276232
    1    A -1.087401
    2    A -0.673690
    3    B  0.113648
    
    In [149]: dfs[1]
    Out[149]: 
      Case      Data
    4    A -1.478427
    5    A  0.524988
    6    B  0.404705
    
    In [150]: dfs[2]
    Out[150]: 
      Case      Data
    7    A  0.577046
    8    A -1.715002
    

    Вращение

    Полная документация по вращению находится здесь.

    Частичные суммы и промежуточные итоги

    In [151]: df = pd.DataFrame(data={'Province': ['ON', 'QC', 'BC', 'AL', 'AL', 'MN', 'ON'],
       .....:                         'City': ['Toronto', 'Montreal', 'Vancouver',
       .....:                                  'Calgary', 'Edmonton', 'Winnipeg',
       .....:                                  'Windsor'],
       .....:                         'Sales': [13, 6, 16, 8, 4, 3, 1]})
       .....: 
    
    In [152]: table = pd.pivot_table(df, values=['Sales'], index=['Province'],
       .....:                        columns=['City'], aggfunc=np.sum, margins=True)
       .....: 
    
    In [153]: table.stack('City')
    Out[153]: 
                        Sales
    Province City            
    AL       All         12.0
             Calgary      8.0
             Edmonton     4.0
    BC       All         16.0
             Vancouver   16.0
    ...                   ...
    All      Montreal     6.0
             Toronto     13.0
             Vancouver   16.0
             Windsor      1.0
             Winnipeg     3.0
    
    [20 rows x 1 columns]
    

    Таблица частот типа plyr в R

    In [154]: grades = [48, 99, 75, 80, 42, 80, 72, 68, 36, 78]
    
    In [155]: df = pd.DataFrame({'ID': ["x%d" % r for r in range(10)],
       .....:                    'Gender': ['F', 'M', 'F', 'M', 'F',
       .....:                               'M', 'F', 'M', 'M', 'M'],
       .....:                    'ExamYear': ['2007', '2007', '2007', '2008', '2008',
       .....:                                 '2008', '2008', '2009', '2009', '2009'],
       .....:                    'Class': ['algebra', 'stats', 'bio', 'algebra',
       .....:                              'algebra', 'stats', 'stats', 'algebra',
       .....:                              'bio', 'bio'],
       .....:                    'Participated': ['yes', 'yes', 'yes', 'yes', 'no',
       .....:                                     'yes', 'yes', 'yes', 'yes', 'yes'],
       .....:                    'Passed': ['yes' if x > 50 else 'no' for x in grades],
       .....:                    'Employed': [True, True, True, False,
       .....:                                 False, False, False, True, True, False],
       .....:                    'Grade': grades})
       .....: 
    
    In [156]: df.groupby('ExamYear').agg({'Participated': lambda x: x.value_counts()['yes'],
       .....:                             'Passed': lambda x: sum(x == 'yes'),
       .....:                             'Employed': lambda x: sum(x),
       .....:                             'Grade': lambda x: sum(x) / len(x)})
       .....: 
    Out[156]: 
              Participated  Passed  Employed      Grade
    ExamYear                                           
    2007                 3       2         3  74.000000
    2008                 3       3         0  68.500000
    2009                 3       2         2  60.666667
    

    Отображение фрейма pandas с данными за год

    Для создания перекрестных таблиц по годам и месяцам:

    In [157]: df = pd.DataFrame({'value': np.random.randn(36)},
       .....:                   index=pd.date_range('2011-01-01', freq='M', periods=36))
       .....: 
    
    In [158]: pd.pivot_table(df, index=df.index.month, columns=df.index.year,
       .....:                values='value', aggfunc='sum')
       .....: 
    Out[158]: 
            2011      2012      2013
    1  -1.039268 -0.968914  2.565646
    2  -0.370647 -1.294524  1.431256
    3  -1.157892  0.413738  1.340309
    4  -1.344312  0.276662 -1.170299
    5   0.844885 -0.472035 -0.226169
    6   1.075770 -0.013960  0.410835
    7  -0.109050 -0.362543  0.813850
    8   1.643563 -0.006154  0.132003
    9  -1.469388 -0.923061 -0.827317
    10  0.357021  0.895717 -0.076467
    11 -0.674600  0.805244 -1.187678
    12 -1.776904 -1.206412  1.130127
    

    Применение   

    Скользящее применение, чтобы организовать — Превращение встроенных списков во фрейм MultiIndex

    In [159]: df = pd.DataFrame(data={'A': 2, 4, 8, 16], [100, 200], [10, 20, 30,
       .....:                         'B': 'a', 'b', 'c'], ['jj', 'kk'], ['ccc'},
       .....:                   index=['I', 'II', 'III'])
       .....: 
    
    In [160]: def SeriesFromSubList(aList):
       .....:     return pd.Series(aList)
       .....: 
    
    In [161]: df_orgz = pd.concat({ind: row.apply(SeriesFromSubList)
       .....:                      for ind, row in df.iterrows()})
       .....: 
    
    In [162]: df_orgz
    Out[162]: 
             0    1    2     3
    I   A    2    4    8  16.0
        B    a    b    c   NaN
    II  A  100  200  NaN   NaN
        B   jj   kk  NaN   NaN
    III A   10   20   30   NaN
        B  ccc  NaN  NaN   NaN
    

    Применение прокрутки фрейма и возврат Series

    Rolling Apply to multiple columns where function calculates a Series before a Scalar from the Series is returned

    In [163]: df = pd.DataFrame(data=np.random.randn(2000, 2) / 10000,
       .....:                   index=pd.date_range('2001-01-01', periods=2000),
       .....:                   columns=['A', 'B'])
       .....: 
    
    In [164]: df
    Out[164]: 
                       A         B
    2001-01-01 -0.000144 -0.000141
    2001-01-02  0.000161  0.000102
    2001-01-03  0.000057  0.000088
    2001-01-04 -0.000221  0.000097
    2001-01-05 -0.000201 -0.000041
    ...              ...       ...
    2006-06-19  0.000040 -0.000235
    2006-06-20 -0.000123 -0.000021
    2006-06-21 -0.000113  0.000114
    2006-06-22  0.000136  0.000109
    2006-06-23  0.000027  0.000030
    
    [2000 rows x 2 columns]
    
    In [165]: def gm(df, const):
       .....:     v = ((((df.A + df.B) + 1).cumprod()) - 1) * const
       .....:     return v.iloc[-1]
       .....: 
    
    In [166]: s = pd.Series({df.index[i]: gm(df.iloc[i:min(i + 51, len(df) - 1)], 5)
       .....:                for i in range(len(df) - 50)})
       .....: 
    
    In [167]: s
    Out[167]: 
    2001-01-01    0.000930
    2001-01-02    0.002615
    2001-01-03    0.001281
    2001-01-04    0.001117
    2001-01-05    0.002772
                    ...   
    2006-04-30    0.003296
    2006-05-01    0.002629
    2006-05-02    0.002081
    2006-05-03    0.004247
    2006-05-04    0.003928
    Length: 1950, dtype: float64
    

    Роллинг применяется с DataFrame, возвращающим скаляр

    Скользящий Применить к нескольким столбцам, где функция возвращает скаляр (средневзвешенная цена)

    In [168]: rng = pd.date_range(start='2014-01-01', periods=100)
    
    In [169]: df = pd.DataFrame({'Open': np.random.randn(len(rng)),
       .....:                    'Close': np.random.randn(len(rng)),
       .....:                    'Volume': np.random.randint(100, 2000, len(rng))},
       .....:                   index=rng)
       .....: 
    
    In [170]: df
    Out[170]: 
                    Open     Close  Volume
    2014-01-01 -1.611353 -0.492885    1219
    2014-01-02 -3.000951  0.445794    1054
    2014-01-03 -0.138359 -0.076081    1381
    2014-01-04  0.301568  1.198259    1253
    2014-01-05  0.276381 -0.669831    1728
    ...              ...       ...     ...
    2014-04-06 -0.040338  0.937843    1188
    2014-04-07  0.359661 -0.285908    1864
    2014-04-08  0.060978  1.714814     941
    2014-04-09  1.759055 -0.455942    1065
    2014-04-10  0.138185 -1.147008    1453
    
    [100 rows x 3 columns]
    
    In [171]: def vwap(bars):
       .....:     return ((bars.Close * bars.Volume).sum() / bars.Volume.sum())
       .....: 
    
    In [172]: window = 5
    
    In [173]: s = pd.concat([(pd.Series(vwap(df.iloc[i:i + window]),
       .....:                 index=[df.index[i + window]]))
       .....:                for i in range(len(df) - window)])
       .....: 
    
    In [174]: s.round(2)
    Out[174]: 
    2014-01-06    0.02
    2014-01-07    0.11
    2014-01-08    0.10
    2014-01-09    0.07
    2014-01-10   -0.29
                  ... 
    2014-04-06   -0.63
    2014-04-07   -0.02
    2014-04-08   -0.03
    2014-04-09    0.34
    2014-04-10    0.29
    Length: 95, dtype: float64
    

    Временные последовательности   

    Между временами

    Использование индекса между временем

    Построение диапазона дат и времени, исключающего выходные и включающего только определенное время

    Векторизованный поиск

    Агрегирование и построение временных рядов

    Превратите матрицу с часами в столбцах и днями в строках в непрерывную последовательность строк в виде временного ряда.
    Как переставить в Python фрейм данных pandas?

    Работа с дубликатами при переиндексации временного ряда к определенной частоте

    Рассчитать первый день месяца для каждой записи в DatetimeIndex

    In [175]: dates = pd.date_range('2000-01-01', periods=5)
    
    In [176]: dates.to_period(freq='M').to_timestamp()
    Out[176]: 
    DatetimeIndex(['2000-01-01', '2000-01-01', '2000-01-01', '2000-01-01',
                   '2000-01-01'],
                  dtype='datetime64[ns]', freq=None)
    

    Слияние   

    Документация по слиянию и объединению находится здесь, а по присоединению здесь.

    Добавить два кадра данных с перекрывающимся индексом (эмулировать R rbind)

    In [177]: rng = pd.date_range('2000-01-01', periods=6)
    
    In [178]: df1 = pd.DataFrame(np.random.randn(6, 3), index=rng, columns=['A', 'B', 'C'])
    
    In [179]: df2 = df1.copy()
    

    В зависимости от конструкции df может потребоваться ignore_index

    In [180]: df = df1.append(df2, ignore_index=True)
    
    In [181]: df
    Out[181]: 
               A         B         C
    0  -0.870117 -0.479265 -0.790855
    1   0.144817  1.726395 -0.464535
    2  -0.821906  1.597605  0.187307
    3  -0.128342 -1.511638 -0.289858
    4   0.399194 -1.430030 -0.639760
    5   1.115116 -2.012600  1.810662
    6  -0.870117 -0.479265 -0.790855
    7   0.144817  1.726395 -0.464535
    8  -0.821906  1.597605  0.187307
    9  -0.128342 -1.511638 -0.289858
    10  0.399194 -1.430030 -0.639760
    11  1.115116 -2.012600  1.810662
    

    Self Join of a DataFrame

    In [182]: df = pd.DataFrame(data={'Area': ['A'] * 5 + ['C'] * 2,
       .....:                         'Bins': [110] * 2 + [160] * 3 + [40] * 2,
       .....:                         'Test_0': [0, 1, 0, 1, 2, 0, 1],
       .....:                         'Data': np.random.randn(7)})
       .....: 
    
    In [183]: df
    Out[183]: 
      Area  Bins  Test_0      Data
    0    A   110       0 -0.433937
    1    A   110       1 -0.160552
    2    A   160       0  0.744434
    3    A   160       1  1.754213
    4    A   160       2  0.000850
    5    C    40       0  0.342243
    6    C    40       1  1.070599
    
    In [184]: df['Test_1'] = df['Test_0'] - 1
    
    In [185]: pd.merge(df, df, left_on=['Bins', 'Area', 'Test_0'],
       .....:          right_on=['Bins', 'Area', 'Test_1'],
       .....:          suffixes=('_L', '_R'))
       .....: 
    Out[185]: 
      Area  Bins  Test_0_L    Data_L  Test_1_L  Test_0_R    Data_R  Test_1_R
    0    A   110         0 -0.433937        -1         1 -0.160552         0
    1    A   160         0  0.744434        -1         1  1.754213         0
    2    A   160         1  1.754213         0         2  0.000850         1
    3    C    40         0  0.342243        -1         1  1.070599         0
    

    Как установить индекс и присоединиться

    KDB like asof join

    Присоединяйтесь с критериями, основанными на значениях

    Использование поиска сортируется для слияния на основе значений внутри диапазона

    Визуализация   

    Познакомтесь с документацией по визуализации.

    Make Matplotlib look like R

    Setting x-axis major and minor labels

    Plotting multiple charts in an ipython notebook

    Creating a multi-line plot

    Plotting a heatmap

    Annotate a time-series plot

    Annotate a time-series plot #2

    Generate Embedded plots in excel files using Pandas, Vincent and xlsxwriter

    Boxplot for each quartile of a stratifying variable

    In [186]: df = pd.DataFrame(
       .....:     {'stratifying_var': np.random.uniform(0, 100, 20),
       .....:      'price': np.random.normal(100, 5, 20)})
       .....: 
    
    In [187]: df['quartiles'] = pd.qcut(
       .....:     df['stratifying_var'],
       .....:     4,
       .....:     labels=['0-25%', '25-50%', '50-75%', '75-100%'])
       .....: 
    
    In [188]: df.boxplot(column='price', by='quartiles')
    Out[188]: <matplotlib.axes._subplots.AxesSubplot at 0x7f4529608e90>
    

    Ввод / Вывод данных   

    Сравнение производительности SQL vs HDF5

    CSV   

    The CSV docs

    read_csv in action

    appending to a csv

    Чтение CSV-фрагмента

    Чтение только определенных строк CSV-фрагмента.

    Чтение первых нескольких строк фрейма

    Чтение сжатого файла, но не с помощью gzip/bz2 (собственные сжатые форматы, которые понимает read_csv).
    В этом примере показан файл WinZipped, но это общее применение для открытия и чтения файла в диспетчере контекста.
    Смотри здесь

    Вывод dtypes из файла

    Работа с ошибочными строками

    Работа с ошибочными строками II

    Чтение CSV с метками времени Unix и преобразование в местный часовой пояс

    Запись многострочного индекса CSV без дубликатов записей

    Чтение нескольких файлов в один DataFrame

    Лучший способ объединить несколько файлов в один DataFrame — это прочитать отдельные кадры один за другим, поместить все в отдельные фреймы в список, а затем объединить фреймы в списке, используя pd.concat():

    In [189]: for i in range(3):
       .....:     data = pd.DataFrame(np.random.randn(10, 4))
       .....:     data.to_csv('file_{}.csv'.format(i))
       .....: 
    
    In [190]: files = ['file_0.csv', 'file_1.csv', 'file_2.csv']
    
    In [191]: result = pd.concat([pd.read_csv(f) for f in files], ignore_index=True)
    

    Вы можете использовать тот же подход для чтения всех файлы, соответствующих шаблону. Вот пример использования glob:

    In [192]: import glob
    
    In [193]: import os
    
    In [194]: files = glob.glob('file_*.csv')
    
    In [195]: result = pd.concat([pd.read_csv(f) for f in files], ignore_index=True)
    

    Наконец, эта стратегия будет работать и с другими функциями pd.read_*(...), описанными в io docs.

    Разбор компонентов даты в несколько столбцов

    Разбор компонентов даты в нескольких столбцах выполняется быстрее, если описан формат:

    In [196]: i = pd.date_range('20000101', periods=10000)
    
    In [197]: df = pd.DataFrame({'year': i.year, 'month': i.month, 'day': i.day})
    
    In [198]: df.head()
    Out[198]: 
       year  month  day
    0  2000      1    1
    1  2000      1    2
    2  2000      1    3
    3  2000      1    4
    4  2000      1    5
    
    In [199]: %timeit pd.to_datetime(df.year * 10000 + df.month * 100 + df.day, format='%Y%m%d')
       .....: ds = df.apply(lambda x: "%04d%02d%02d" % (x['year'],
       .....:                                           x['month'], x['day']), axis=1)
       .....: ds.head()
       .....: %timeit pd.to_datetime(ds)
       .....: 
    9.41 ms +- 596 us per loop (mean +- std. dev. of 7 runs, 100 loops each)
    2.76 ms +- 60.8 us per loop (mean +- std. dev. of 7 runs, 100 loops each)
    

    Пропустить строку между заголовком и данными

    In [200]: data = """;;;;
       .....:  ;;;;
       .....:  ;;;;
       .....:  ;;;;
       .....:  ;;;;
       .....:  ;;;;
       .....: ;;;;
       .....:  ;;;;
       .....:  ;;;;
       .....: ;;;;
       .....: date;Param1;Param2;Param4;Param5
       .....:     ;m²;°C;m²;m
       .....: ;;;;
       .....: 01.01.1990 00:00;1;1;2;3
       .....: 01.01.1990 01:00;5;3;4;5
       .....: 01.01.1990 02:00;9;5;6;7
       .....: 01.01.1990 03:00;13;7;8;9
       .....: 01.01.1990 04:00;17;9;10;11
       .....: 01.01.1990 05:00;21;11;12;13
       .....: """
       .....: 
    
    Вариант 1: для пропуска передать строки явно
    In [201]: from io import StringIO
    
    In [202]: pd.read_csv(StringIO(data), sep=';', skiprows=[11, 12],
       .....:             index_col=0, parse_dates=True, header=10)
       .....: 
    Out[202]: 
                         Param1  Param2  Param4  Param5
    date                                               
    1990-01-01 00:00:00       1       1       2       3
    1990-01-01 01:00:00       5       3       4       5
    1990-01-01 02:00:00       9       5       6       7
    1990-01-01 03:00:00      13       7       8       9
    1990-01-01 04:00:00      17       9      10      11
    1990-01-01 05:00:00      21      11      12      13
    
    Вариант 2: прочитать имена столбцов, а затем данные
    In [203]: pd.read_csv(StringIO(data), sep=';', header=10, nrows=10).columns
    Out[203]: Index(['date', 'Param1', 'Param2', 'Param4', 'Param5'], dtype='object')
    
    In [204]: columns = pd.read_csv(StringIO(data), sep=';', header=10, nrows=10).columns
    
    In [205]: pd.read_csv(StringIO(data), sep=';', index_col=0,
       .....:             header=12, parse_dates=True, names=columns)
       .....: 
    Out[205]: 
                         Param1  Param2  Param4  Param5
    date                                               
    1990-01-01 00:00:00       1       1       2       3
    1990-01-01 01:00:00       5       3       4       5
    1990-01-01 02:00:00       9       5       6       7
    1990-01-01 03:00:00      13       7       8       9
    1990-01-01 04:00:00      17       9      10      11
    1990-01-01 05:00:00      21      11      12      13
    

    SQL   

    Ищите документацию по SQL здесь.

    Чтение из баз данных с SQL

    HDFStore   

    Докментация по HDFStores здесь.

    Простые запросы с индексированием по времени

    Управление разнородными данными с использованием связанной иерархии нескольких таблиц

    Объединение на диске таблиц с миллионами строк

    Как избежать несоответствий при записи в хранилище из нескольких процессов / потоков

    Дедупликация большого хранилища кусками, по сути, рекурсивная операция сокращения. Показывает функцию для получения данных из CSV-файл и создание набора по частям с разбором по дате.
    Смотри здесь

    Создание фрагмента набора из файла CSV

    Присоединение к набору с созданием уникального индекса

    Работа с большими данными

    Чтение нескольких файлов и формирование во время добавлении глобального уникального индекса

    Группировка в наборе HDFS с низкой плотностью групп

    Группировка в наборе HDFS с высокой плотностью групп

    Иерархические запросы в хранилище HDFS

    Подсчет с HDFStore

    Обработка исключений HDFStore

    Установка min_itemsize для строк

    Использование ptrepack для создания полностью отсортированного индекса в наборе

    Хранение атрибутов для группового узла:

    In [206]: df = pd.DataFrame(np.random.randn(8, 3))
    
    In [207]: store = pd.HDFStore('test.h5')
    
    In [208]: store.put('df', df)
    
    # you can store an arbitrary Python object via pickle
    In [209]: store.get_storer('df').attrs.my_attribute = {'A': 10}
    
    In [210]: store.get_storer('df').attrs.my_attribute
    Out[210]: {'A': 10}
    

    Двичные файлы   

    Pandas принимает массивы записей NumPy, если вам нужно читать в двоичном виде файл, состоящий из массива структур Си. Например, учитывая эту программу C в файле с именем main.c, скомпилированным с помощью gcc main.c -std = gnu99 на 64-битный компьютере.

    #include <stdio.h>
    #include <stdint.h>
    
    typedef struct _Data
    {
        int32_t count;
        double avg;
        float scale;
    } Data;
    
    int main(int argc, const char *argv[])
    {
        size_t n = 10;
        Data d[n];
    
        for (int i = 0; i < n; ++i)
        {
            d[i].count = i;
            d[i].avg = i + 1.0;
            d[i].scale = (float) i + 2.0f;
        }
    
        FILE *file = fopen("binary.dat", "wb");
        fwrite(&d, sizeof(Data), n, file);
        fclose(file);
    
        return 0;
    }
    

    Следующий скрипт Python будет читать двоичный файл 'binary.dat' в DataFrame pandas, при этом каждый элемент структуры соответствует столбцу фрейма:

    names = 'count', 'avg', 'scale'
    
    # note that the offsets are larger than the size of the type because of
    # struct padding
    offsets = 0, 8, 16
    formats = 'i4', 'f8', 'f4'
    dt = np.dtype({'names': names, 'offsets': offsets, 'formats': formats},
                  align=True)
    df = pd.DataFrame(np.fromfile('binary.dat', dt))
    

    Note

    В зависимости от архитектура машины, на которой был создан файл, смещения элементов структуры могут отличаться. Для общего хранения данных файл с «сырыми» данными в двоичном формате испольховать не рекомендуется, так как кроссплатформенность в таком случае отсутствует. Мы рекомендуем либо HDF5, либо msgpack. Оба поддерживаются средствами ввода / вывода Pandas.

    Вычисления   

    Численное интегрирование временного ряда (на основе выборки)

    Correlation   

    Часто бывает полезно получить нижнюю (или верхнюю) треугольную форму корреляционной матрицы, рассчитанной DataFrame.corr(). Это может быть достигнуто путем передачи логической маски where следующим образом:

    In [211]: df = pd.DataFrame(np.random.random(size=(100, 5)))
    
    In [212]: corr_mat = df.corr()
    
    In [213]: mask = np.tril(np.ones_like(corr_mat, dtype=np.bool), k=-1)
    
    In [214]: corr_mat.where(mask)
    Out[214]: 
              0         1         2         3   4
    0       NaN       NaN       NaN       NaN NaN
    1 -0.018923       NaN       NaN       NaN NaN
    2 -0.076296 -0.012464       NaN       NaN NaN
    3 -0.169941 -0.289416  0.076462       NaN NaN
    4  0.064326  0.018759 -0.084140 -0.079859 NaN
    

    Аргумент method в DataFrame.corr позволяет дополнительно определить способ вычисления корреляции. Здесь вычисляется корреляуионная матрица расстояний для объекта DataFrame.

    In [215]: def distcorr(x, y):
       .....:     n = len(x)
       .....:     a = np.zeros(shape=(n, n))
       .....:     b = np.zeros(shape=(n, n))
       .....:     for i in range(n):
       .....:         for j in range(i + 1, n):
       .....:             a[i, j] = abs(x[i] - x[j])
       .....:             b[i, j] = abs(y[i] - y[j])
       .....:     a += a.T
       .....:     b += b.T
       .....:     a_bar = np.vstack([np.nanmean(a, axis=0)] * n)
       .....:     b_bar = np.vstack([np.nanmean(b, axis=0)] * n)
       .....:     A = a - a_bar - a_bar.T + np.full(shape=(n, n), fill_value=a_bar.mean())
       .....:     B = b - b_bar - b_bar.T + np.full(shape=(n, n), fill_value=b_bar.mean())
       .....:     cov_ab = np.sqrt(np.nansum(A * B)) / n
       .....:     std_a = np.sqrt(np.sqrt(np.nansum(A**2)) / n)
       .....:     std_b = np.sqrt(np.sqrt(np.nansum(B**2)) / n)
       .....:     return cov_ab / std_a / std_b
       .....: 
    
    In [216]: df = pd.DataFrame(np.random.normal(size=(100, 3)))
    
    In [217]: df.corr(method=distcorr)
    Out[217]: 
              0         1         2
    0  1.000000  0.199653  0.214871
    1  0.199653  1.000000  0.195116
    2  0.214871  0.195116  1.000000
    

    Timedeltas   

    Timedeltas — интервалы времени, например, день, час, минута, секунда. Они могут быть как положительными, так и отрицательными.

    Документация по Timedeltas находится здесь.

    Использование timedeltas

    In [218]: import datetime
    
    In [219]: s = pd.Series(pd.date_range('2012-1-1', periods=3, freq='D'))
    
    In [220]: s - s.max()
    Out[220]: 
    0   -2 days
    1   -1 days
    2    0 days
    dtype: timedelta64[ns]
    
    In [221]: s.max() - s
    Out[221]: 
    0   2 days
    1   1 days
    2   0 days
    dtype: timedelta64[ns]
    
    In [222]: s - datetime.datetime(2011, 1, 1, 3, 5)
    Out[222]: 
    0   364 days 20:55:00
    1   365 days 20:55:00
    2   366 days 20:55:00
    dtype: timedelta64[ns]
    
    In [223]: s + datetime.timedelta(minutes=5)
    Out[223]: 
    0   2012-01-01 00:05:00
    1   2012-01-02 00:05:00
    2   2012-01-03 00:05:00
    dtype: datetime64[ns]
    
    In [224]: datetime.datetime(2011, 1, 1, 3, 5) - s
    Out[224]: 
    0   -365 days +03:05:00
    1   -366 days +03:05:00
    2   -367 days +03:05:00
    dtype: timedelta64[ns]
    
    In [225]: datetime.timedelta(minutes=5) + s
    Out[225]: 
    0   2012-01-01 00:05:00
    1   2012-01-02 00:05:00
    2   2012-01-03 00:05:00
    dtype: datetime64[ns]
    

    Сложение и вычитание интервалов времени и дат

    In [226]: deltas = pd.Series([datetime.timedelta(days=i) for i in range(3)])
    
    In [227]: df = pd.DataFrame({'A': s, 'B': deltas})
    
    In [228]: df
    Out[228]: 
               A      B
    0 2012-01-01 0 days
    1 2012-01-02 1 days
    2 2012-01-03 2 days
    
    In [229]: df['New Dates'] = df['A'] + df['B']
    
    In [230]: df['Delta'] = df['A'] - df['New Dates']
    
    In [231]: df
    Out[231]: 
               A      B  New Dates   Delta
    0 2012-01-01 0 days 2012-01-01  0 days
    1 2012-01-02 1 days 2012-01-03 -1 days
    2 2012-01-03 2 days 2012-01-05 -2 days
    
    In [232]: df.dtypes
    Out[232]: 
    A             datetime64[ns]
    B            timedelta64[ns]
    New Dates     datetime64[ns]
    Delta        timedelta64[ns]
    dtype: object
    

    Ещё один пример

    Значения могут быть установлены в NaT, используя np.nan, аналогично datetime

    In [233]: y = s - s.shift()
    
    In [234]: y
    Out[234]: 
    0      NaT
    1   1 days
    2   1 days
    dtype: timedelta64[ns]
    
    In [235]: y[1] = np.nan
    
    In [236]: y
    Out[236]: 
    0      NaT
    1      NaT
    2   1 days
    dtype: timedelta64[ns]
    

    Псевдонимы имен осей   

    Для глобального определения псевдонимов имен осей можно использовать эти две функции:

    In [237]: def set_axis_alias(cls, axis, alias):
       .....:     if axis not in cls._AXIS_NUMBERS:
       .....:         raise Exception("invalid axis [%s] for alias [%s]" % (axis, alias))
       .....:     cls._AXIS_ALIASES[alias] = axis
       .....: 
    
    In [238]: def clear_axis_alias(cls, axis, alias):
       .....:     if axis not in cls._AXIS_NUMBERS:
       .....:         raise Exception("invalid axis [%s] for alias [%s]" % (axis, alias))
       .....:     cls._AXIS_ALIASES.pop(alias, None)
       .....: 
    
    In [239]: set_axis_alias(pd.DataFrame, 'columns', 'myaxis2')
    
    In [240]: df2 = pd.DataFrame(np.random.randn(3, 2), columns=['c1', 'c2'],
       .....:                    index=['i1', 'i2', 'i3'])
       .....: 
    
    In [241]: df2.sum(axis='myaxis2')
    Out[241]: 
    i1   -0.461013
    i2    2.040016
    i3    0.904681
    dtype: float64
    
    In [242]: clear_axis_alias(pd.DataFrame, 'columns', 'myaxis2')
    

    Creating example data   

    Для создания фрейма данных, комбинируя некоторые заданные значения, например, как функция expand.grid() в R, можно создать dict<.code>, где ключи — это имена столбцов, а значения — списки данных:

    In [243]: def expand_grid(data_dict):
       .....:     rows = itertools.product(*data_dict.values())
       .....:     return pd.DataFrame.from_records(rows, columns=data_dict.keys())
       .....: 
    
    In [244]: df = expand_grid({'height': [60, 70],
       .....:                   'weight': [100, 140, 180],
       .....:                   'sex': ['Male', 'Female']})
       .....: 
    
    In [245]: df
    Out[245]: 
        height  weight     sex
    0       60     100    Male
    1       60     100  Female
    2       60     140    Male
    3       60     140  Female
    4       60     180    Male
    5       60     180  Female
    6       70     100    Male
    7       70     100  Female
    8       70     140    Male
    9       70     140  Female
    10      70     180    Male
    11      70     180  Female
    

    Оригинал: Cookbook

    ]]>
    https://chel-center.ru/python-yfc/2020/01/29/vkusnye-recepty-pandas/feed/ 0
    Описательная статистика на Python (часть 1) https://chel-center.ru/python-yfc/2020/02/11/opisatelnaya-statistika-na-python-chast-1/ https://chel-center.ru/python-yfc/2020/02/11/opisatelnaya-statistika-na-python-chast-1/#respond Tue, 11 Feb 2020 16:28:37 +0000 http://chel-center.ru/python-yfc/?p=29243 Читать далее «Описательная статистика на Python (часть 1)»

    ]]>
    В эпоху больших данных и искусственного интеллекта Data Science и Machine Learning стали обязательным элементом многих областей науки и техники. При этом одним из важнейших аспектом работы с данными являются возможности их описания, обобщения и визуального представления. Библиотеки статистики Python — это комплексные, популярные и широко используемые инструменты, которые помогают работать с цифрами.

    В этой статье вы узнаете:

    • какие числовые величины можно использовать для описания и обобщения ваших наборов данных;
    • как вычислить описательную статистику на Python;
    • с помощью каких доступных библиотек Python можно получить описательную статистику;
    • как визуализировать ваши наборы данных.

    Понятие описательной статистики

    Описательная статистика — это описание и интегральные параметры наборов данных. Она использует два основных подхода:

    1. Количественный подход, который описывает общие численные характеристики данных.
    2. Визуальный подход, который иллюстрирует данные с помощью диаграмм, графиков, гистограмм и прочих графических образов.

    Описательную статистику можно применять к одному или нескольким наборам данных или переменных. Когда вы описываете и вычисляете характеристики одной переменной, то выполняете одномерный анализ. Когда вы ищете статистические связи между парой переменных, то вы делаете двухмерный анализ. Аналогичным образом, многомерный анализ связан с несколькими переменными одновременно.

    Виды метрик

    Здесь вы узнаете о следующих метриках описательной статистики:

    1. Центральные метрики, которые говорят вам о центрах концентрации данных, таких как среднее, медиана и мода.
    2. Метрики оценки вариативности данных, которые говорят о разбросе значений, такие как дисперсия и стандартное отклонение.
    3. Метрики оценки корреляции или взаимозависимости, которые указывает на связь между парой переменных в наборе данных, такие как ковариация и коэффициент корреляции.

    Вы узнаете, как понимать и вычислять эти меры с помощью Python.

    Наблюдения и выборки

    Наблюдения или генеральная совокупность в статистике — это набор всех элементов, относительно которых предполагается делать выводы при изучении конкретной задачи. Зачастую генеральная совокупность очень велика, что делает её непригодной для сбора и анализа. Вот почему в статистике обычно пытаются сделать некоторые выводы о популяции, выбирая и исследуя репрезентативную подгруппу этой совокупности.

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

    Выбросы

    Выброс — это такая точка, которая существенно отличается от большинства значений, взятых из выборки или совокупности. Есть множество возможных причин появления выбросов и вот для начала только несколько:

    1. Естественные выбросы данных.
    2. Изменение поведения наблюдаемой системы.
    3. Ошибки при сборе данных.

    Наиболее частой причиной появления выбросов являются Ошибки сбора данных. Например, ограничения измерительных приборов или самих процедур сбора информации и это означает, что правильные данные просто не могут быть получены. Другие ошибки могут быть вызваны просчетами, зашумлением данных, человеческой ошибкой и многое-многое другое.

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

    Выбор библиотек статистики Python

    Существует множество библиотек статистики Python, с которыми можно работать, но здесь мы рассмотрим только некоторых из них, самые популярные и широко используемые:

    1. Python statistics — встроенная библиотека Python для описательной статистики. Вы можете использовать её, если ваши наборы данных не слишком велики или если вы не можете полагаться на импорт других библиотек.
    2. NumPy — сторонняя библиотека для численных вычислений, оптимизированная для работы с одномерными и многомерными массивами. Его основным типом является тип массива называется ndarray. Эта библиотека содержит множество подпрограмм для статистического анализа.
    3. SciPy — сторонняя библиотека для научных вычислений, основанная на NumPy. Она предлагает дополнительную функциональность по сравнению с NumPy, в том числе scipy.stats для статистического анализа.
    4. Pandas — сторонняя библиотека для численных вычислений, основанная на NumPy. Она отлично справляется с обработкой помеченных одномерных (1D) данных с объектами Series и двумерных (2D) данными объектами DataFrame.
    5. Matplotlib — сторонняя библиотека для визуализации данных. Он хорошо работает в сочетании с NumPy, SciPy и Pandas.
    6. plotly — сторонняя библиотека для визуализации данных, самая продвинутая на сегодняшний день. Она позволяет создавать интерактивные динамические истории для глубокого погружения в данные и хорошо работает в сочетании с NumPy, SciPy и Pandas.

    Обратите внимание, что во многих случаях Series DataFrame вместо массивов NumPy можно использоваться и объекты. Часто вы можете просто передать их в статистическую функцию NumPy или SciPy. Кроме того, из объектом Series или DataFrame вы можете получить немаркированные данные такие, как объект np.ndarray, вызвав методы .values или .to_numpy().

    Начало работы с библиотеками статистики Python

    Встроенная библиотека Python statistics имеет относительно небольшое количество наиболее важных статистических функций. Официальная документация является ценным ресурсом для уточнения нюансов. Если вы ограничены чистым Python, то statistics Python может быть правильным выбором.

    Хорошее место для начала изучения numpy — это официальное руководство пользователя, особенно разделы быстрый старт и основы. Справочное руководство освежит вашу память по конкретным вопросам NumPy. Убедиться в мощности и полезности Numpy можно прочитав статью «Нескучный Numpy». Во время чтения полезно будет подглядывать официальную справку scipy.stats.

    Примечание:

    При изучении NumPy ознакомьтесь с этими ресурсами:

    Если вы хотите изучать Pandas, то официальная страница «Getting Started» — то самое место, с которого надо начать. Введение в структуры данных позволит вам узнать об основных типах данных Series и DataFrame. Кроме того, отличная официальная 10‑минутка 10 minutes to pandas придаст импульс для эффективного использования Pandas на практике.

    Примечание:

    При изучении Pandas ознакомьтесь с этими ресурсами:

    Для matplotlib есть полное официальное руководство пользователя, где есть все детали использования библиотеки. Анатомия Matplotlib является отличным ресурсом для начинающих, которые хотят начать работать с этой библиотекой.

    Примечание:

    Для более полного знакомства с визуализацией ознакомьтесь с этими ресурсами:

    Расчет описательной статистики

    Далее при чтении рекомендую использовать простейший инструмент для работы IDLE, который устанавливается автоматом при скачивании Python c python.org позволяет работать в режиме интерактивного интерпретатора. Только не забудьте установить требуемые пакеты. Эти строчки необходимо набрать в командной строке cmd:

    # для начала перейдите в папку, где у вас установлен Python
    cd python path 
    
    # а теперь в командной строке можно выполнить:
    pip install numpy
    pip install scipy
    pip install pandas
    pip install matplotlib
    

    Начните с импорта в свои скрипты всех пакетов, которые нам понадобятся:

    import math
    import statistics
    import numpy as np
    import scipy.stats
    import pandas as pd
    

    Это все пакеты Python, которые вам пока потребуются для расчетов описательной статистики. Обычно вы не будете использовать math, встроенный в Python, но здесь это будет полезно. Позже вы будете импортировать matplotlib.pyplot для визуализации данных.

    Давайте создадим некоторые исходные данные. Мы начнём со списков Python, содержащих произвольные числовые данные:

    >>> x = [8.0, 1, 2.5, 4, 28.0]
    >>> x_with_nan = [8.0, 1, 2.5, math.nan, 4, 28.0]
    >>> x
    [8.0, 1, 2.5, 4, 28.0]
    >>> x_with_nan
    [8.0, 1, 2.5, nan, 4, 28.0]
    

    Теперь у вас есть списки x и x_with_nan. Они почти одинаковы, с той разницей, что x_with_nan содержат nan значение. Важно понимать поведение процедур статистики Python, когда они сталкиваются с не-числовым значением (nan). В науке о данных пропущенные значения являются общими и вы часто будете заменять их на nan.

    Примечание:

    Как получить значение nan?

    В Python можно использовать:

    >>> math.isnan(np.nan), np.isnan(math.nan)
    (True, True)
    >>> math.isnan(y_with_nan[3]), np.isnan(y_with_nan[3])
    (True, True)
    

    Практически, всё это одно и то же. Однако, надо помнить, что результатом сравнения двух значений nan будет False. Значение выражения math.nan == math.nan есть False!

    Теперь создайте объекты np.ndarray и pd.Series, соответствующие x и x_with_nan:

    >>> y, y_with_nan = np.array(x), np.array(x_with_nan)
    >>> z, z_with_nan = pd.Series(x), pd.Series(x_with_nan)
    >>> y
    array([ 8. ,  1. ,  2.5, 4. , 28. ])
    >>> y_with_nan
    array([ 8. ,  1. ,  2.5,  nan,  4. , 28. ])
    >>> z
    0     8.0
    1     1.0
    2     2.5
    3     4.0
    4    28.0
    dtype: float64
    >>> z_with_nan
    0     8.0
    1     1.0
    2     2.5
    3     NaN
    4     4.0
    5    28.0
    dtype: float64
    

    Теперь у вас есть два массива NumPy (y и y_with_nan) и два объекта Series Pandas (z и z_with_nan). Все это — 1D‑последовательности значений.

    Примечание:

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

    При необходимости можно указать метку для каждого значения в z и z_with_nan.

    Центральные метрики

    Центральные метрики показывают центр или средние значения наборов данных. Существует несколько определений того, что считается центром набора данных. Здесь вы узнаете, как определить и рассчитать эти метрики, используя Python:

    • Среднеарифметическое;
    • Средневзвешенное;
    • Среднегеометрическое;
    • Гармоническое среднее;
    • Медиана;
    • Мода.

    Среднее значение набора данных, среднеарифметическое, знакомо всем со школьной скамьи:

    X_{average} = \frac{\sum_{i=1}^{N}(X_i)}{N}

    Другими словами, это сумма всех элементов X_i, деленная на количество элементов в наборе данных.

    На этом рисунке показано среднее значение выборки, которую мы определили, с пятью точками данных:

    Зеленые точки представляют собой точки данных 1, 2.5, 4, 8 и 28. Красная пунктирная линия — это их среднее значение, или (1 + 2.5 + 4 + 8 + 28) / 5 = 8.7.

    Вы можете вычислить среднее значение на чистом Python, используя sum() и len(), без импорта библиотек:

    >>> mean_ = sum(x) / len(x)
    >>> mean_
    8.7
    

    Хотя это чисто и элегантно, вы также можете применить встроенные функции статистики Python:

    >>> mean_ = statistics.mean(x)
    >>> mean_
    8.7
    >>> mean_ = statistics.fmean(x)
    >>> mean_
    8.7
    

    Вы вызвали функции mean() и fmean() из встроенной библиотеки statistics Python и получили тот же результат. fmean() вводится в Python 3.8 как более быстрая альтернатива mean(). Она всегда возвращает число с плавающей запятой.

    Однако если среди ваших данных есть значения nan, то statistics.mean() и statistics.fmean() вернутся nan в качестве результата:

    >>> mean_ = statistics.mean(x_with_nan)
    >>> mean_
    nan
    >>> mean_ = statistics.fmean(x_with_nan)
    >>> mean_
    nan
    

    Этот результат связан с поведением sum(). Ведь sum(x_with_nan) также возвращает nan.

    Если вы используете NumPy, то вы можете получить среднее значение с помощью np.mean():

    >>> mean_ = np.mean(y)
    >>> mean_
    8.7
    

    В приведенном выше примере, mean() это функция, но можно использовать соответствующий метод .mean():

    >>> y.mean()
    8.7
    

    И функция, и метод ведут себя аналогично при наличии значений nan среди ваших данных:

    >>> np.mean(y_with_nan)
    nan
    >>> y_with_nan.mean()
    nan
    

    Вам часто не нужно получать учитывать nan в результате. Если вы предпочитаете игнорировать nan, то вы можете использовать np.nanmean():

    >>> np.nanmean(y_with_nan)
    8.7
    

    nanmean() просто игнорирует все значения nan и возвращает то же самое значение, что mean() если из набора данных удалить все значения nan.

    Объекты pd.Series также имеют метод .mean():

    >>> mean_ = z.mean()
    >>> mean_
    8.7
    

    Как вы можете видеть, он используется так же, как и в случае NumPy. Однако .mean() Pandas игнорирует значения nan по умолчанию:

    >>> z_with_nan.mean()
    8.7
    

    Это поведение является результатом назначения необязательного параметра skipna по умолчанию. Этот параметре можно изменить.

    Средневзвешенное

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

    Вы определяете один вес w_i для каждой точки данных x_i набора данных x, где = 1, 2,…, n и n — количество элементов в x. Затем вы умножаете каждую точку данных на соответствующий вес, суммируете все произведения и делите полученную сумму на сумму весов:

    \frac{\sum_{i=1}^{N}(w_i * x_i)}{\sum_{i=1}^{N}w_i}

    Примечание

    Обычно, все веса неотрицательны, w_i >= 0 и их подбирают так, что сумма равна единице или {\sum_{i=1}^{N}w_i} = 1.

    Средневзвешенное значение очень удобно, когда вам нужно среднее значение набора данных, содержащего элементы, которые встречаются с заданными относительными частотами. Например, допустим, что у вас есть набор, в котором 20% всех элементов равны 2, 50% элементов равны 4, а оставшиеся 30% элементов равны 8. Вы можете вычислить среднее значение такой набор, как это:

    >>> 0.2 * 2 + 0.5 * 4 + 0.3 * 8
    4.8
    

    Здесь вы учитываете частоты с весами. При использовании этого метода вам не нужно знать общее количество элементов.

    В чистом Python средневзвешенное можно реализовать комбинацией sum() с range() или zip():

    >>> x = [8.0, 1, 2.5, 4, 28.0]
    >>> w = [0.1, 0.2, 0.3, 0.25, 0.15]
    >>> wmean = sum(w[i] * x[i] for i in range(len(x))) / sum(w)
    >>> wmean
    6.95
    >>> wmean = sum(x_ * w_ for (x_, w_) in zip(x, w)) / sum(w)
    >>> wmean
    6.95
    

    Опять же, это чистая и элегантная реализация, в которой вам не нужно импортировать какие-либо библиотеки.

    Однако, если у вас большие наборы данных, то NumPy, вероятно, будет лучшим решением. Вы можете использовать np.average() для массивов NumPy или серии Pandas:

    >>> x = [8.0, 1, 2.5, 4, 28.0]
    >>> y, z, w = np.array(x), pd.Series(x), np.array(w)
    >>> wmean = np.average(y, weights=w)
    >>> wmean
    6.95
    >>> wmean = np.average(z, weights=w)
    >>> wmean
    6.95

    Результат такой же, как и в случае чистой реализации Python. Этот метод можно использовать для обычных списков и кортежей.

    Кроме того, можно использовать поэлементное умножение w * y с np.sum() или .sum():

    >>> (w * y).sum() / w.sum()
    6.95
    

    Это оно! Вы рассчитали средневзвешенное значение.

    Однако, будьте осторожны, если ваш набор данных содержит значения nan:

    >>> w = np.array([0.1, 0.2, 0.3, 0.0, 0.2, 0.1])
    >>> (w * y_with_nan).sum() / w.sum()
    nan
    >>> np.average(y_with_nan, weights=w)
    nan
    >>> np.average(z_with_nan, weights=w)
    nan
    

    В этом случае average() возвращает nan в связи с алгоритмом np.mean().

    Гармоническое среднее

    Гармоническое среднее есть обратная величина от среднего значения обратных величин всех элементов в наборе данных:

    \frac{n}{\sum_{i=1}^{n}(\frac{1}{x_i})},

    где i = 1, 2,…, n и n — количество элементов в наборе данных . Один из вариантов реализации гармонического среднего на чистом Python:

    >>> hmean = len(x) / sum(1 / item for item in x)
    >>> hmean
    2.7613412228796843
    

    Он сильно отличается от значения среднего арифметического для тех же данных x, которые мы рассчитали ранее 8,7.

    Вы также можете рассчитать эту меру с помощью statistics.harmonic_mean():

    >>> hmean = statistics.harmonic_mean(x)
    >>> hmean
    2.7613412228796843
    

    В приведенном выше примере показана одна из реализаций statistics.harmonic_mean(). Если в наборе данных есть значение nan, оно вернет nan. Если хотя бы один 0, то он вернет 0. Если вы укажете хотя бы одно отрицательное число, вы получите статистику StatisticsError:

    >>> statistics.harmonic_mean(x_with_nan)
    nan
    >>> statistics.harmonic_mean([1, 0, 2])
    0
    >>> statistics.harmonic_mean([1, 2, -2])  # Raises StatisticsError
    

    Помните об этом при использовании этого метода!

    Третий способ вычисления среднего значения гармоники — использовать scipy.stats.hmean():

    >>> scipy.stats.hmean(y)
    2.7613412228796843
    >>> scipy.stats.hmean(z)
    2.7613412228796843
    

    Это, Опять же, довольно простая реализация. Однако, если ваш набор данных содержит nan, 0, отрицательное число или что-то кроме положительных чисел, то вы получите ValueError!

    Среднее геометрическое

    Среднее геометрическое является корнем n-степени произведения всех элементов x_i в наборе данных x:

    \sqrt[n]{(\prod_{i}x_i)},

    где i = 1, 2,…, n. На следующем рисунке показаны средние арифметические, гармонические и геометрические значения набора данных:

    Снова, зеленые точки — точки данных 1, 2.5, 4, 8 и 28. Красная пунктирная линия — среднее арифметическое. Синяя пунктирная линия — гармоническое среднее, а желтая пунктирная линия — геометрическое среднее.

    На чистом Python вычисление геометрического среднего можно реализовать следующим образом:

    >>> gmean = 1
    >>> for item in x:
    ...     gmean *= item
    ...
    >>> gmean **= 1 / len(x)
    >>> gmean
    4.677885674856041
    

    Как видите, для одного и того же набора данных x значение геометрического среднего значительно отличается от значений среднего арифметического (8,7) и гармонического (2,76)

    Начиная с Python 3.8 введен statistics.geometric_mean(), который преобразует все значения в числа с плавающей точкой и возвращает их среднее геометрическое значение:

    >>> gmean = statistics.geometric_mean(x)
    >>> gmean
    4.67788567485604
    

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

    Если вы передадите данные со значениями nan, то statistics.geometric_mean() будет вести себя как большинство похожих функций и вернет nan:

    >>> gmean = statistics.geometric_mean(x_with_nan)
    >>> gmean
    nan
    

    Действительно, это согласуется с поведением statistics.mean(), statistics.fmean() и statistics.harmonic_mean(). Если среди ваших данных есть ноль или отрицательное число, то statistics.geometric_mean() вызовет statistics.StatisticsError.

    Вы также можете получить среднее геометрическое с помощью scipy.stats.gmean():

    >>> scipy.stats.gmean(y)
    4.67788567485604
    >>> scipy.stats.gmean(z)
    4.67788567485604
    

    Вы получили тот же результат, что и в реализации на чистом Python.

    Если у вас есть значения nan в наборе данных, то gmean() вернет nan. Если хотя бы один 0, он вернет 0.0 и выдаст предупреждение. Если вы укажете хотя бы одно отрицательное число, вы получите Nan и предупреждение.

    Медиана

    Медиана — это средний элемент отсортированного набора данных. Набор данных может быть отсортирован по возрастанию или убыванию. Если число элементов набора данных нечетное, то медиана является значением в средней позиции: 0,5( + 1). Если чётно, то медиана — это среднее арифметическое двух значений в середине, то есть элементов в позициях 0.5 и 0.5 + 1.

    Например, если у вас есть точки данных 2, 4, 1, 8 и 9, то медианное значение равно 4, что находится в середине отсортированного набора данных (1, 2, 4, 8, 9). Если точками данных являются 2, 4, 1 и 8, то медиана равна 3, что является средним значением двух средних элементов отсортированной последовательности (2 и 4). Посмотрите на рисунок ниже:

    Точками данных являются зеленые точки, а фиолетовые линии показывают медиану для каждого набора данных. Медианное значение для верхнего набора данных (1, 2,5, 4, 8 и 28) равно 4. Если вы удалите выброс 28 из нижнего набора данных, медиана станет средним арифметическим между 2,5 и 4, что составляет 3,25.

    На рисунке ниже показано среднее и среднее значение точек данных 1, 2.5, 4, 8 и 28:

    Опять же, среднее значение — это красная пунктирная линия, а медиана — фиолетовая линия.

    Основное различие между поведением среднего значения и медианы связано с выбросами или экстремальными значениями. Среднее значение сильно зависит от выбросов, а медиана либо незначительно зависит от выбросов, либо вообще не зависит. Посмотрим следующий рисунок:

    В верхнем наборе данных снова есть элементы 1, 2.5, 4, 8 и 28. Его среднее значение равно 8,7, а медиана равна 5, как вы видели ранее. Нижний набор данных показывает, что происходит, когда вы перемещаете крайнюю правую точку со значением 28:

    • Если вы увеличите его значение (переместите его вправо), то среднее значение возрастет, но медианное значение никогда не изменится.
    • Если вы уменьшите его значение (переместите его влево), то среднее значение уменьшится, но медиана останется неизменной, пока значение движущейся точки не станет больше или равно 4.

    Вы можете сравнить среднее значение и медиану как один из способов обнаружения выбросов и асимметрии в ваших данных. Является ли среднее значение или среднее значение более полезным для вас, зависит от контекста вашей конкретной проблемы.

    Вот одна из многих возможных реализаций чистого медиана Python:

    >>> n = len(x)
    >>> if n % 2:
    ...     median_ = sorted(x)[round(0.5*(n-1))]
    ... else:
    ...     x_ord, index = sorted(x), round(0.5 * n)
    ...     median_ = 0.5 * (x_ord[index-1] + x_ord[index])
    ...
    >>> median_
    4
    

    Два наиболее важных шага этой реализации заключаются в следующем:

    1. Сортировка элементов набора данных;
    2. Нахождение средних элементов в отсортированном наборе данных.

    Вы можете получить медиану с помощью statistics.median():

    >>> median_ = statistics.median(x)
    >>> median_
    4
    >>> median_ = statistics.median(x[:-1])
    >>> median_
    3.25
    

    Сортированный датасет x имеет вид [1, 2.5, 4, 8.0, 28.0], поэтому элемент в середине равен 4. Сортированный датасет x[: - 1], то есть x без последнего элемента 28.0, равна [1 2,5, 4, 8,0]. Теперь есть два средних элемента, 2,5 и 4. Их среднее значение составляет 3,25.

    median_low() и median_high() — еще две функции, связанные с медианой в статистической библиотеке Python. Они всегда возвращают элемент из набора данных:

    • Если число элементов нечетное, то имеется одно среднее значение, поэтому эти функции ведут себя как median().
    • Если число элементов четное, то есть два средних значения. В этом случае median_low() возвращает меньшее, а median_high() — большее среднее значение.

    Вы можете использовать эти функции так же, как и median():

    >>> statistics.median_low(x[:-1])
    2.5
    >>> statistics.median_high(x[:-1])
    4
    

    Опять же, отсортированный x[:-1] это [1, 2.5, 4, 8.0]. Два элемента в середине: 2,5 (низкий) и 4 (высокий).

    В отличие от большинства других функций из библиотеки статистики Python, median(), median_low() и median_high() не возвращают nan при наличии значений nan среди точек данных:

    >>> statistics.median(x_with_nan)
    6.0
    >>> statistics.median_low(x_with_nan)
    4
    >>> statistics.median_high(x_with_nan)
    8.0
    

    Остерегайтесь этого, может быть это не то, что вы хотите!

    Вы также можете получить медиану с помощью np.median():

    >>> median_ = np.median(y)
    >>> median_
    4.0
    >>> median_ = np.median(y[:-1])
    >>> median_
    3.25
    

    Вы получили те же значения с помощью statistics.median() и np.median().

    Однако, если в вашем наборе данных есть значение nan, тогда np.median() выдает RuntimeWarning и возвращает nan. Если это не то, что нужно, то можно использовать nanmedian(), чтобы игнорировать все значения nan:

    >>> np.nanmedian(y_with_nan)
    4.0
    >>> np.nanmedian(y_with_nan[:-1])
    3.25
    

    Полученные результаты такие же, как и для statistics.median() и np.median(), примененных к наборам данных x и y.

    Объекты серии Pandas имеют метод .median(), который по умолчанию игнорирует значения nan:

    >>> z.median()
    4.0
    >>> z_with_nan.median()
    4.0
    

    Поведение .median() аналогично .mean() в Pandas. Вы можете изменить это поведение с помощью необязательного параметра skipna.

    Мода

    Мода — это значение в наборе данных, которое встречается чаще всего. Если такого значения не существует, набор является мультимодальным, поскольку он имеет несколько модальных значений. Например, в наборе, который содержит точки 2, 3, 2, 8 и 12, число 2 является модой, потому что встречаеся дважды, в отличие от других элементов, которые встречаются только один раз.

    Вот как вы можете получить режим с чистым Python:

    >>> u = [2, 3, 2, 8, 12]
    >>> mode_ = max((u.count(item), item) for item in set(u))[1]
    >>> mode_
    2
    

    Вы используете u.count(), чтобы получить количество вхождений каждого элемента в u. Элемент с максимальным количеством вхождений — это мода. Обратите внимание, что вам не нужно использовать set(u). Вместо этого вы можете заменить его просто на u и повторить весь список.

    Примечание:

    set(u) возвращает набор Python только с уникальными элементами в u. Вы можете использовать этот прием для оптимизации работы с большими данными, особенно если вы ожидаете увидеть много дубликатов.

    Вы можете вычислить моду с помощью statistics.mode() и statistics.multimode():

    >>> mode_ = statistics.mode(u)
    >>> mode_
    >>> mode_ = statistics.multimode(u)
    >>> mode_
    [2]
    

    Обратите внимание, mode() вернула одно значение, а multimode() в результате вернула список. Однако, это не единственное различие между двумя функциями. Если существует более одного модального значения, то mode() вызывает StatisticsError, а multimode() возвращает список со всеми режимами:

    >>> v = [12, 15, 12, 15, 21, 15, 12]
    >>> statistics.mode(v)  # Raises StatisticsError
    >>> statistics.multimode(v)
    [12, 15]
    

    Нужно обратить особое внимание и быть осторожным при выборе между этими двумя функциями так, как statistics.mode() и statistics.multimode() обрабатывают значения nan как обычные значения и могут возвращать nan как модальное значение:

    >>> statistics.mode([2, math.nan, 2])
    2
    >>> statistics.multimode([2, math.nan, 2])
    [2]
    >>> statistics.mode([2, math.nan, 0, math.nan, 5])
    nan
    >>> statistics.multimode([2, math.nan, 0, math.nan, 5])
    [nan]
    

    В первом примере число 2 встречается дважды, т.е является модальным значением. Во втором примере nan — это модальное значение, поскольку оно встречается дважды, тогда как другие значения встречаются только один раз.

    Примечание:

    statistics.multimode() появилась только в Python 3.8.

    Моду, кроме того, можно получить с помощью scipy.stats.mode():

    >>> u, v = np.array(u), np.array(v)
    >>> mode_ = scipy.stats.mode(u)
    >>> mode_
    ModeResult(mode=array([2]), count=array([2]))
    >>> mode_ = scipy.stats.mode(v)
    >>> mode_
    ModeResult(mode=array([12]), count=array([3]))
    

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

    Вы можете получить моду и количество её вхождений в виде массивов NumPy с точечной нотацией:

    >>> mode_.mode
    array([12])
    >>> mode_.count
    array([3])
    

    Этот код использует .mode, чтобы вернуть наименьшую моду (12) в массиве v, и .count для повторений (3). scipy.stats.mode() также гибок со значениями nan. Это позволяет вам определить желаемое поведение с помощью необязательного параметра nan_policy. Этот параметр может принимать значения ‘propagate’, ‘raise’ (an error) или ‘omit’..

    Для объектов серии Pandas имеется метод .mode(), который хорошо обрабатывает мультимодальные значения и по умолчанию игнорирует значения nan:

    >>> u, v, w = pd.Series(u), pd.Series(v), pd.Series([2, 2, math.nan])
    >>> u.mode()
    0    2
    dtype: int64
    >>> v.mode()
    0    12
    1    15
    dtype: int64
    >>> w.mode()
    0    2.0
    dtype: float64
    

    Посмотрите, .mode() возвращает новый объект pd.Series, который содержит все модальные значения. Если вы хотите, чтобы .mode() учитывал значения nan, просто передайте необязательный аргумент dropna = False.

    Метрики оценки вариативности данных

    Центральных метрик недостаточно для описания данных. Практически всегда необходимы метрики оценки вариативности данных, которые количественно определяют разброс точек данных. В этом разделе вы узнаете, как определить и рассчитать следующие показатели:

    • дисперсия;
    • среднеквадратичное отклонение;
    • смещение;
    • процентили;
    • диапазон;

    Дисперсия

    Дисперсия количественно определяет разброс данных. Численно показывает, как далеко точки данных от среднего значения. Вы можете вычислить дисперсию набора данных, как

    \sigma^2 =\frac{\sum_{i}(x_i - mean())^2}{(n - 1)},

    где i = 1, 2,…, n и среднее значение mean() равно среднему. Если вы хотите глубже понять, почему вы делите сумму на n — 1 вместо n, то можете поглубже погрузиться в поправку Бесселя.

    На следующем рисунке показано, почему важно учитывать разницу при описании наборов данных:
    Вариативность

    На этом рисунке представлены два набора данных:

    • Зеленые точки — этот набор данных с небольшой дисперсией или небольшим отклонением от среднего. У него меньший диапазон или меньшая разница между самым большим и самым маленьким значениями.
    • Белые точки — этот набор данных с большой дисперсией или большим отклонением от среднего. У него больший диапазон или большая разница между самым большим и самым маленьким значениями.

    Обратите внимание, что эти два набора имеют одно и то же среднее значение и медиану, даже если они значительно различаются. Ни среднее, ни медиана не могут показать эту разницу. Вот почему нужны метрики вариативности.

    Вот как вы можете рассчитать оценку дисперсии на чистом Python:

    >>> n = len(x)
    >>> mean_ = sum(x) / n
    >>> var_ = sum((item - mean_)**2 for item in x) / (n - 1)
    >>> var_
    123.19999999999999
    

    Этого достаточно и можно правильно дать оценку дисперсии. Однако, более короткое и элегантное решение — использовать функцию statistics.variance():

    >>> var_ = statistics.variance(x)
    >>> var_
    123.2
    

    Вы получили тот же результат для дисперсии, что и выше. Функция variance() может избежать вычисления среднего значения, если вы явно укажете среднее значение в качестве второго аргумента: statistics.variance(x, mean_).

    Если среди ваших данных есть значения nan, то statistics.variance() вернет nan:

    >>> statistics.variance(x_with_nan)
    nan
    

    Это связано с работой mean() и большинством других функций из библиотеки статистики Python.

    Оценку дисперсии можно рассчитать, используя NumPy. Для этого необходимо использовать функцию np.var() или соответствующий метод .var():

    >>> var_ = np.var(y, ddof=1)
    >>> var_
    123.19999999999999
    >>> var_ = y.var(ddof=1)
    >>> var_
    123.19999999999999
    

    Очень важно указать параметр ddof = 1, ограничивающий количество степеней свободы равными 1. Этот параметр позволяет правильно вычислять σ2, с (n - 1) в знаменателе вместо n.

    Если в наборе данных есть значения nan, то np.var() и .var() вернут nan:

    >>> np.var(y_with_nan, ddof=1)
    nan
    >>> y_with_nan.var(ddof=1)
    nan
    

    Это связано с работой функций np.mean() и np.average(). Если вы хотите пропустить значения nan, следует использовать np.nanvar ():

    >>> np.nanvar(y_with_nan, ddof=1)
    123.19999999999999
    

    np.nanvar() игнорирует значения nan. Необходимо также указать ddof = 1.

    Объекты pd.Series имеют метод .var(), который по умолчанию пропускает значения nan:

    >>> z.var(ddof=1)
    123.19999999999999
    >>> z_with_nan.var(ddof=1)
    123.19999999999999
    

    Метод также имеет параметр ddof, но его значение по умолчанию равно 1, так что вы можете его опустить. Если вы хотите другое поведение, связанное со значениями nan, используйте необязательный параметр skipna.

    Расчёт дисперсии генеральной совокупности производится аналогично расчёту оценки дисперсии. Однако, вы должны использовать n в знаменателе вместо n — 1:

    \sigma^2 =\frac{\sum_{i}(x_i - mean())^2}{n}

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

    • Замените (n — 1) на n в чистой реализации Python.
    • Используйте statistics.pvariance () вместо statistics.variance().
    • Укажите параметр ddof = 0, если вы используете NumPy или Pandas. В NumPy вы можете опустить ddof, потому что его значение по умолчанию равно 0.

    Обратите внимание, что вы всегда должны знать, работаете ли вы с выборкой или со всей совокупностью при вычислении дисперсии!

    Среднеквадратичное отклонение

    Стандартное отклонение выборки является еще одним показателем разброса данных. Он связан с оценкой дисперсией, поскольку стандартное отклонение есть положительным квадратный корень из оценки дисперсии. Стандартное отклонение часто более удобно, чем дисперсия, потому что имеет ту же размерность, что и данные. Получив дисперсию, вы можете рассчитать стандартное отклонение с помощью чистого Python:

    >>> std_ = var_ ** 0.5
    >>> std_
    11.099549540409285
    

    Хотя это работает, но также можно использовать statistics.stdev():

    >>> std_ = statistics.stdev(x)
    >>> std_
    11.099549540409287
    

    Конечно, результат такой же, как и раньше. Как и variance(), stdev() не вычисляет среднее значение, если вы явно укажете его в качестве второго аргумента: statistics.stdev(x, mean_).

    Вы можете получить стандартное отклонение с помощью NumPy практически таким же образом. Вы можете использовать функцию std() и соответствующий метод .std() для вычисления стандартного отклонения. Если в наборе данных есть значения nan, они вернут nan. Чтобы игнорировать значения nan, вы должны использовать np.nanstd(). Вы используете std(), .std() и nanstd() из NumPy точно также, как если бы вы использовали var(), .var() и nanvar():

    >>> np.std(y, ddof=1)
    11.099549540409285
    >>> y.std(ddof=1)
    11.099549540409285
    >>> np.std(y_with_nan, ddof=1)
    nan
    >>> y_with_nan.std(ddof=1)
    nan
    >>> np.nanstd(y_with_nan, ddof=1)
    11.099549540409285
    

    Не забудьте установить дельта степеней свободы на 1!

    Объекты pd.Series также имеют метод .std(), который по умолчанию пропускает nan:

    >>> z.std(ddof=1)
    11.099549540409285
    >>> z_with_nan.std(ddof=1)
    11.099549540409285
    

    Параметр ddof по умолчанию равен 1, поэтому вы можете его опустить. Опять же, если вы хотите по-разному относиться к значениям nan, примените параметр skipna.

    Стандартное отклонение совокупности относится ко всей совокупности. Это положительный квадратный корень дисперсии. Вы можете рассчитать его так же, как стандартное отклонение выборки, со следующими различиями:

    • Найдите квадратный корень дисперсии совокупности в чистом Python.
    • Используйте statistics.pstdev() вместо statistics.stdev().
    • Укажите параметр ddof = 0, если вы используете NumPy или Pandas. В NumPy вы можете опустить ddof, потому что его значение по умолчанию равно 0.

    Как видите, стандартное отклонение в Python, NumPy и Pandas находится практически так же, как и дисперсия. Вы используете различные, но аналогичные функции и методы с одинаковыми аргументами.

    Смещение

    Отклонение показывает асимметрию выборки данных.

    Существует несколько математических определений асимметрии. Одним общим выражением для вычисления асимметрии набора данных x с nэлементами является

    (\frac{n^2}{((n - 1) * (n - 2))}) * (\frac{\sum_i{(x_i - mean())^3}}{(n*s^3)}).

    Более простым выражением является

    \frac{\sum_i{(x_i - mean())^3 * n}}{(n - 1)*(n - 2) * s^3},

    где n = 1, 2,…, n, а mean() — это среднее по выборке. Определенная таким образом асимметрия называется скорректированным коэффициентом стандартизированного момента Фишера-Пирсона.

    На предыдущем рисунке были показаны два набора данных, которые были довольно симметричными. Другими словами, их точки были на одинаковом расстоянии от среднего. Напротив, следующее изображение иллюстрирует два асимметричных набора:
    Смещение

    Первый набор представлен зелеными точками, а второй — белыми. Обычно отрицательные значения асимметрии указывают на то, что с левой стороны есть доминанта, которую вы видите в первом наборе. Положительные значения асимметрии соответствуют более длинному или толстому хвосту справа, который вы видите во втором наборе. Если асимметрия близка к 0 (например, между -0,5 и 0,5), то набор данных считается относительно симметричным.

    После того как вы рассчитали размер набора данных n, mean_ и стандартное отклонение std_, вы можете получить асимметрию выборки с помощью чистого Python:

    >>> x = [8.0, 1, 2.5, 4, 28.0]
    >>> n = len(x)
    >>> mean_ = sum(x) / n
    >>> var_ = sum((item - mean_)**2 for item in x) / (n - 1)
    >>> std_ = var_ ** 0.5
    >>> skew_ = (sum((item - mean_)**3 for item in x)
    ...          * n / ((n - 1) * (n - 2) * std_**3))
    >>> skew_
    1.9470432273905929
    

    Асимметрия положительна, поэтому у x хвост с правой стороны.

    Вы также можете рассчитать асимметрию с помощью scipy.stats.skew():

    >>> y, y_with_nan = np.array(x), np.array(x_with_nan)
    >>> scipy.stats.skew(y, bias=False)
    1.9470432273905927
    >>> scipy.stats.skew(y_with_nan, bias=False)
    nan
    

    Полученный результат совпадает с реализацией на чистом Python. Для параметра «bias» установлено значение «False», чтобы включить поправки для статистического смещения. Необязательный параметр nan_policy может принимать значения 'propagate', 'raise', или 'omit'. Это позволяет вам контролировать, как вы будете обрабатывать значения Nan.

    Объекты серии Pandas имеют метод .skew (), который также возвращает асимметрию набора данных:

    >>> z, z_with_nan = pd.Series(x), pd.Series(x_with_nan)
    >>> z.skew()м
    1.9470432273905924
    >>> z_with_nan.skew()
    1.9470432273905924
    

    Как и другие методы, .skew() игнорирует значения nan.

    Процентили

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

    • Первый квартиль — это образец 25-го процентиля. Он разделяет примерно 25% самых маленьких элементов от остальной части набора данных.
    • Второй квартиль — это образец 50-го процентиля или медиана. Приблизительно 25% элементов находятся между первым и вторым квартилями и еще 25% между вторым и третьим квартилями.
    • Третий квартиль — это образец 75-го процентиля. Он разделяет примерно 25% самых больших элементов от остальной части набора данных.

    Каждая часть имеет примерно одинаковое количество элементов. Если вы хотите разделить ваши данные на несколько интервалов, то можно использовать statistics.quantiles():

    >>> x = [-5.0, -1.1, 0.1, 2.0, 8.0, 12.8, 21.0, 25.8, 41.0]
    >>> statistics.quantiles(x, n=2)
    [8.0]
    >>> statistics.quantiles(x, n=4, method='inclusive')
    [0.1, 8.0, 21.0]
    

    В этом примере 8,0 — медиана x, а 0,1 и 21,0 — это 25-й и 75-й процентили выборки соответственно. Параметр n определяет количество результирующих процентилей с равной вероятностью, а метод определяет, как их вычислять.

    Вы также можете использовать np.percentile() для определения любого процентиля в наборе данных. Например, вот так вы можете найти 5-й и 95-й процентили:

    >>> y = np.array(x)
    >>> np.percentile(y, 5)
    -3.44
    >>> np.percentile(y, 95)
    34.919999999999995
    

    percentile() принимает несколько аргументов. Вы должны предоставить набор данных в качестве первого аргумента и значение процентиля в качестве второго. Набор данных может быть в виде массива, списка, кортежа или подобной структуры данных NumPy. Перцентиль может быть числом от 0 до 100, как в примере выше, но также может быть последовательностью чисел:

    >>> np.percentile(y, [25, 50, 75])
    array([ 0.1,  8. , 21. ])
    >>> np.median(y)
    8.0
    

    Этот код вычисляет 25-й, 50-й и 75-й процентили одновременно. Если значение процентиля является последовательностью, то percentile() возвращает массив NumPy с результатами. Первое утверждение возвращает массив квартилей. Второе утверждение возвращает медиану, поэтому вы можете подтвердить, что она равна 50-му процентилю, то есть 8.0.

    Если вы хотите игнорировать значения nan, используйте вместо этого np.nanpercentile():

    >>> y_with_nan = np.insert(y, 2, np.nan)
    >>> y_with_nan
    array([-5. , -1.1,  nan,  0.1,  2. ,  8. , 12.8, 21. , 25.8, 41. ])
    >>> np.nanpercentile(y_with_nan, [25, 50, 75])
    array([ 0.1,  8. , 21. ])
    

    Вот как можно избежать значений Nan.

    NumPy также предлагает вам очень похожую функциональность в quantile () и nanquantile (). Если вы используете их, вам нужно будет указать квантильные значения в виде чисел от 0 до 1 вместо процентилей:

    >>> np.quantile(y, 0.05)
    -3.44
    >>> np.quantile(y, 0.95)
    34.919999999999995
    >>> np.quantile(y, [0.25, 0.5, 0.75])
    array([ 0.1,  8. , 21. ])
    >>> np.nanquantile(y_with_nan, [0.25, 0.5, 0.75])
    array([ 0.1,  8. , 21. ])
    

    Результаты такие же, как в предыдущих примерах, но здесь ваши аргументы находятся между 0 и 1. Другими словами, вы передали 0.05 вместо 5 и 0.95 вместо 95.

    Объекты pd.Series имеют метод .quantile():

    >>> z, z_with_nan = pd.Series(y), pd.Series(y_with_nan)
    >>> z.quantile(0.05)
    -3.44
    >>> z.quantile(0.95)
    34.919999999999995
    >>> z.quantile([0.25, 0.5, 0.75])
    0.25     0.1
    0.50     8.0
    0.75    21.0
    dtype: float64
    >>> z_with_nan.quantile([0.25, 0.5, 0.75])
    0.25     0.1
    0.50     8.0
    0.75    21.0
    dtype: float64
    

    Для .quantile() также необходимо указать значение квантиля в качестве аргумента. Это значение может быть числом от 0 до 1 или последовательностью чисел. В первом случае .quantile() возвращает скаляр. Во втором случае он возвращает новую серию, содержащую результаты.

    Диапазон

    Диапазон данных — это разница между максимальным и минимальным элементом в наборе данных. Вы можете получить его с помощью функции np.ptp():

    >>> np.ptp(y)
    46.0
    >>> np.ptp(z)
    46.0
    >>> np.ptp(y_with_nan)
    nan
    >>> np.ptp(z_with_nan)
    46.0
    

    Эта функция возвращает nan, если в вашем массиве NumPy есть значения nan. Если вы используете объект серии Pandas, он вернет число.

    В качестве альтернативы вы можете использовать встроенные функции и методы Python, NumPy или Pandas для вычисления максимумов и минимумов последовательностей:

    • max() и min() из стандартной библиотеки Python;
    • amax() и amin() из NumPy;
    • nanmax() и nanmin() из NumPy, чтобы игнорировать значения nan;
    • .max() и .min() от NumPy;
    • .max() и .min() из Pandas, чтобы игнорировать значения nan по умолчанию.

    Вот несколько примеров того, как вы будете использовать эти процедуры:

    >>> np.amax(y) - np.amin(y)
    46.0
    >>> np.nanmax(y_with_nan) - np.nanmin(y_with_nan)
    46.0
    >>> y.max() - y.min()
    46.0
    >>> z.max() - z.min()
    46.0
    >>> z_with_nan.max() - z_with_nan.min()
    46.0
    

    Вот как вы получаете диапазон данных.

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

    >>> quartiles = np.quantile(y, [0.25, 0.75])
    >>> quartiles[1] - quartiles[0]
    20.9
    >>> quartiles = z.quantile([0.25, 0.75])
    >>> quartiles[0.75] - quartiles[0.25]
    20.9
    

    Обратите внимание, что вы получаете доступ к значениям в объекте серии Pandas с метками 0,75 и 0,25.

    Сводка описательной статистики

    SciPy и Pandas предлагают полезные процедуры для быстрого получения описательной статистики с помощью одного вызова функции или метода. Вы можете использовать scipy.stats.describe() следующим образом:

    >>> result = scipy.stats.describe(y, ddof=1, bias=False)
    >>> result
    DescribeResult(nobs=9, minmax=(-5.0, 41.0), mean=11.622222222222222, variance=228.75194444444446, skewness=0.9249043136685094, kurtosis=0.14770623629658886)
    

    В качестве первого аргумента необходимо передать набор данных, который может быть представлен массивом NumPy, списком, кортежем или любой другой подобной структурой данных. Можно опустить ddof = 1, так как это значение по умолчанию и имеет значение только при расчете дисперсии. Указано bias = False для принудительного исправления асимметрии и эксцесса статистического смещения.

    description() возвращает объект, который содержит следующую описательную статистику:

    • nobs — количество наблюдений или элементов в вашем наборе данных;
    • minmax — кортеж с минимальными и максимальными значениями;
    • mean — среднее значение;
    • variance — дисперсия;
    • skewness — асимметрия;
    • kurtosis — эксцесс вашего набора данных.

    Эти значения можно получить по отдельности:

    >>> result.nobs
    9
    >>> result.minmax[0]  # Min
    -5.0
    >>> result.minmax[1]  # Max
    41.0
    >>> result.mean
    11.622222222222222
    >>> result.variance
    228.75194444444446
    >>> result.skewness
    0.9249043136685094
    >>> result.kurtosis
    0.14770623629658886
    

    В SciPy функция от описательной сводной статистики для вашего набора данных единственная.

    В Pandas похожая, если не лучшая, функциональность. Объекты Series имеют метод .describe():

    >>> result = z.describe()
    >>> result
    count     9.000000
    mean     11.622222
    std      15.124548
    min      -5.000000
    25%       0.100000
    50%       8.000000
    75%      21.000000
    max      41.000000
    dtype: float64
    

    Если вы хотите, чтобы результирующий объект Series содержал другие процентили, то следует указать значение дополнительного параметра процентили. Вы можете получить доступ к каждому элементу результата с его меткой:

    >>> result['mean']
    11.622222222222222
    >>> result['std']
    15.12454774346805
    >>> result['min']
    -5.0
    >>> result['max']
    41.0
    >>> result['25%']
    0.1
    >>> result['50%']
    8.0
    >>> result['75%']
    21.0
    

    Вот так можно получить описательную статистику объекта Series с помощью одного вызова метода с использованием Pandas.

    Использованы материалы: Python Statistics Fundamentals: How to Describe Your Data

    ]]>
    https://chel-center.ru/python-yfc/2020/02/11/opisatelnaya-statistika-na-python-chast-1/feed/ 0
    Описательная статистика на Python (часть 2) https://chel-center.ru/python-yfc/2020/02/13/opisatelnaya-statistika-na-python-chast-2/ https://chel-center.ru/python-yfc/2020/02/13/opisatelnaya-statistika-na-python-chast-2/#respond Thu, 13 Feb 2020 14:09:32 +0000 http://chel-center.ru/python-yfc/?p=29507 Читать далее «Описательная статистика на Python (часть 2)»

    ]]>
    ←   Описательная статистика на Python (Часть I)

    Корреляции между парами данных

    Часто нужно исследовать отношения между соответствующими элементами двух переменных в наборе данных. Скажем, есть две переменные, x и y, с равным количеством элементов, n. Пусть x_1 из x соответствуют y_1 из y, x_2 из x соответствуют y_2 из y т.д. Затем можно сказать, что существует n пар соответствующих элементов: (x_1, y_1), (x_2, y_2) и т.д.

    Вы увидите следующие показатели корреляции между парами данных:

    • Положительная корреляция существует, когда большие значения x соответствуют большим значениям y и наоборот.
    • Отрицательная корреляция существует, когда большие значения x соответствуют меньшим значениям y и наоборот.
    • Слабая или отсутствует корреляция, если нет такой очевидной связи.

    На следующем рисунке показаны примеры отрицательной, слабой и положительной корреляции:

    Положительная, отрицательная и слабая корреляции
    Положительная, отрицательная и слабая корреляции

    График слева, с красными точками, показывает отрицательную корреляцию. Посередине, с зелеными точками, показывает слабую корреляцию. Наконец, график справа, с синими точками показывает положительную корреляцию.

    Примечание:

    Одна важная вещь, которую вы всегда должны иметь в виду при работе с корреляцией между парой переменных, и это то, что корреляция не является мерой или показателем причинности, а только ассоциации!

    Есть две статистические меры, которые характеризуют корреляцию между наборами данных — ковариация и коэффициент корреляции. Давайте определимся для работы с этими мерами. Создадим два списка Python и будете использовать их для получения соответствующих массивов NumPy и серии Pandas:

    >>> x = list(range(-10, 11))
    >>> y = [0, 2, 2, 2, 2, 3, 3, 6, 7, 4, 7, 6, 6, 9, 4, 5, 5, 10, 11, 12, 14]
    >>> x_, y_ = np.array(x), np.array(y)
    >>> x__, y__ = pd.Series(x_), pd.Series(y_)
    

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

    Ковариации

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

    • Если корреляция положительная, то и ковариация тоже положительная. Более сильное отношение соответствует более высокой ценности ковариации.
    • Если корреляция отрицательна, то и ковариация также отрицательна. Более сильное отношение соответствует более низкому (или более высокому абсолютному) значению ковариации.
    • Если корреляция слабая, то ковариация близка к нулю.

    Ковариация двух переменных x и y математически записывается следующим образом:

    s_{xy} = \frac{\sum_{i}{(x_i - mean(x))(y_i - mean(y))}}{(n - 1)},

    где i = 1, 2, …, ?, mean(x) среднеарифметическое x и mean(y) среднеарифметическое y. Откуда следует, что ковариация двух одинаковых переменных на самом деле является дисперсией:

    s_{xx} = \frac{\sum_{i}{(x_i - mean(x))^2}}{(n - 1)} = \sigma_x^2 и s_{yy} = \frac{\sum_{i}{(y_i - mean(y))^2}}{(n - 1)} = \sigma_y^2.

    Вот как вы можете рассчитать ковариацию в чистом Python:

    >>> mean_x, mean_y = sum(x) / n, sum(y) / n
    >>> cov_xy = (sum((x[k] - mean_x) * (y[k] - mean_y) for k in range(n))
    ...           / (n - 1))
    >>> cov_xy
    19.95
    

    Во-первых, необходимо найти среднее значение х и у, затем применить математическую формулу для ковариации.

    NumPy имеет функцию cov(), которая возвращает ковариационную матрицу:

    >>> cov_matrix = np.cov(x_, y_)
    >>> cov_matrix
    array(38.5       , 19.95      ],
           [19.95      , 13.91428571)
    

    Обратите внимание, cov() имеет необязательные параметры bias (по умолчанию False) и ddof (по умолчанию None). Их значения по умолчанию подходят для получения образца ковариационной матрицы. Верхний левый элемент ковариационной матрицы — это ковариация x и x или дисперсия x. Точно так же нижний правый элемент — y и y или дисперсия y. Вы можете проверить, что это правда:

    >>> x_.var(ddof=1)
    38.5
    >>> y_.var(ddof=1)
    13.914285714285711
    

    Как видите, дисперсии x и y равны cov_matrix[0, 0] и cov_matrix[1, 1] соответственно.

    Два других элемента ковариационной матрицы равны и представляют фактическую ковариацию между x и y:

    >>> cov_xy = cov_matrix[0, 1]
    >>> cov_xy
    19.95
    >>> cov_xy = cov_matrix[1, 0]
    >>> cov_xy
    19.95
    

    Вы получили то же значение ковариации с np.cov(), что и с чистым Python.
    Объект Series Pandas имеет метод .cov(), который вы можете использовать для вычисления ковариации:

    >>> cov_xy = x__.cov(y__)
    >>> cov_xy
    19.95
    >>> cov_xy = y__.cov(x__)
    >>> cov_xy
    19.95
    

    Здесь для одного объекта Series вызывается .cov() и передаете другой объект в качестве первого аргумента.

    Коэффициент корреляции

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

    • ? > 0 указывает на положительную корреляцию.
    • ? < 0 указывает на отрицательную корреляцию.
    • r = 1 является максимально возможным значением ?. Это свидетельство полной линейной зависимости между переменными.
    • r = −1 является минимально возможным значением ?. Это свидетельство полного отсутствия линейной зависимости между переменными.
    • r ≈ 0 или когда around около нуля, означает, что корреляция между переменными отсутствует.

    Математическая формула для коэффициента корреляции:

    r = \frac{s_{xy}}{\sigma_x\sigma_y}.

    где \sigma_x и \sigma_y — стандартные отклонения x и y соответственно. Если у вас есть средние значения (mean_x и mean_y) и стандартные отклонения (std_x, std_y) для наборов данных x и y, а также их ковариация cov_xy, то вы можете рассчитать коэффициент корреляции с помощью чистого Python:

    >>> var_x = sum((item - mean_x)**2 for item in x) / (n - 1)
    >>> var_y = sum((item - mean_y)**2 for item in y) / (n - 1)
    >>> std_x, std_y = var_x ** 0.5, var_y ** 0.5
    >>> r = cov_xy / (std_x * std_y)
    >>> r
    0.861950005631606

    Есть переменная r, которая представляет коэффициент корреляции.

    В scipy.stats есть функция pearsonr(), которая вычисляет коэффициент корреляции и p-value:

    >>> r, p = scipy.stats.pearsonr(x_, y_)
    >>> r
    0.861950005631606
    >>> p
    5.122760847201171e-07
    

    Верхний левый элемент — это коэффициент корреляции между x_ и x_. Нижний правый элемент — это коэффициент корреляции между y_ и y_. Их значения равны 1,0. Два других элемента равны и представляют фактический коэффициент корреляции между x_ и y_:

    >>> r = corr_matrix[0, 1]
    >>> r
    0.8619500056316061
    >>> r = corr_matrix[1, 0]
    >>> r
    0.861950005631606
    

    Конечно, результат такой же, как с чистым Python и pearsonr().

    Вы можете получить коэффициент корреляции с помощью scipy.stats.linregress():

    >>> scipy.stats.linregress(x_, y_)
    LinregressResult(slope=0.5181818181818181, intercept=5.714285714285714, rvalue=0.861950005631606, pvalue=5.122760847201164e-07, stderr=0.06992387660074979)
    

    linregress() принимает x_ и y_, вычисляет линейную регрессию и возвращает результаты — наклон и точка пересечения определяют уравнение прямой регрессии, а rvalue — коэффициент корреляции. Чтобы получить доступ к определенным значениям из результата linregress(), включая коэффициент корреляции, используйте точечную запись:

    >>> result = scipy.stats.linregress(x_, y_)
    >>> r = result.rvalue
    >>> r
    0.861950005631606
    

    Вот так можно сделать линейную регрессию и получить коэффициент корреляции.

    Объект Series Pandas имеет метод .corr() для расчета коэффициента корреляции:

    >>> r = x__.corr(y__)
    >>> r
    0.8619500056316061
    >>> r = y__.corr(x__)
    >>> r
    0.861950005631606
    

    Необходимо вызвать метод .corr() для одного объекта Series и передать другой объект в качестве первого аргумента.

    Работа с данными 2D (таблицы)

    В статистике очень часто работают с 2D данными. Вот несколько примеров форматов данных 2D:

    • Таблицы базы данных
    • CSV файлы
    • Таблицы Excel, Calc и Google

    NumPy и SciPy предоставляют комплексные средства для работы с 2D-данными. Pandas имеет класс DataFrame специально для обработки 2D данных.

    Axes

    Начните с создания 2D массива NumPy:

    >>> a = np.array(1, 1, 1],
    ...               [2, 3, 1],
    ...               [4, 9, 2],
    ...               [8, 27, 4],
    ...               [16, 1, 1)
    >>> a
    array( 1,  1,  1],
           [ 2,  3,  1],
           [ 4,  9,  2],
           [ 8, 27,  4],
           [16,  1,  1)
    

    Теперь у вас есть набор 2D данных, который мы будем использовать в этом разделе. Можно применять к нему статистические функции и методы Python так же, как к данным 1D (последовательности):

    >>> np.mean(a)
    5.4
    >>> a.mean()
    5.4
    >>> np.median(a)
    2.0
    >>> a.var(ddof=1)
    53.40000000000001
    

    Как видите, здесь получены статистики (например, среднее значение, медиана или дисперсия) по всем данным в массиве a. Иногда, нужно именно это, а часто эти величины надо рассчитать для каждой строки или столбца вашего 2D-массива.

    Функции и методы, которые вы использовали до сих пор, имеют один необязательный параметр, называемый осью, который необходим для обработки 2D-данных. Ось может принимать любое из следующих значений:

    • axis = None — расчет статистики по всем данным в массиве, как в приведенном выше примере. Такое поведение часто используется по умолчанию в NumPy.
    • axis = 0 — расчет статистики для каждого столбца массива. Такое поведение часто используется по умолчанию для статистических функций SciPy.
    • axis = 1 — расчет статистики для каждой строки массива.

    Давайте посмотрим ось = 0 в действии с np.mean ():

    >>> np.mean(a, axis=0)
    array([6.2, 8.2, 1.8])
    >>> a.mean(axis=0)
    array([6.2, 8.2, 1.8])
    

    Два приведенных выше оператора возвращают новые массивы NumPy со средним значением для каждого столбца a. В этом примере среднее значение первого столбца равно 6,2. Второй столбец имеет среднее значение 8,2, а третий столбец имеет 1,8.

    Если вы укажете axis = 1, то функция mean() выдаст результаты для каждой строки:

    >>> np.mean(a, axis=1)
    array([ 1.,  2.,  5., 13.,  6.])
    >>> a.mean(axis=1)
    array([ 1.,  2.,  5., 13.,  6.])
    

    Как видите, первая строка имеет среднее значение 1.0, вторая 2.0 и так далее.

    Примечание:

    Эти правила можно распространить на многомерные массивы, но это выходит за рамки данного руководства. Не стесняйтесь погрузиться в эту тему самостоятельно!

    Параметр axis работает аналогично c другими функциям и методами NumPy:

    >>> np.median(a, axis=0)
    array([4., 3., 1.])
    >>> np.median(a, axis=1)
    array([1., 2., 4., 8., 1.])
    >>> a.var(axis=0, ddof=1)
    array([ 37.2, 121.2,   1.7])
    >>> a.var(axis=1, ddof=1)
    array([  0.,   1.,  13., 151.,  75.])
    

    Здесь получены медианы и вариация для всех столбцов (axis = 0) и строк (axis = 1) массива a.

    Это очень похоже на работу со статистическими функциями SciPy. Но помните, что в этом случае значение по умолчанию для axis равно 0:

    >>> scipy.stats.gmean(a)  # Default: axis=0
    array([4.        , 3.73719282, 1.51571657])
    >>> scipy.stats.gmean(a, axis=0)
    array([4.        , 3.73719282, 1.51571657])
    

    Если вы опустите axis или зададите axis = 0, то расчёт делается для каждого столбца. Например, первый столбец матрицы a имеет среднее геометрическое значение 4,0 и т. Д.

    Если вы укажете axis = 1, то получите результат для каждой строки:

    >>> scipy.stats.gmean(a, axis=1)
    array([1.        , 1.81712059, 4.16016765, 9.52440631, 2.5198421 ])
    

    В этом примере среднее геометрическое для первой строки матрицы a равно 1.0. Для второго ряда это примерно 1,82 и так далее.

    Если вы хотите получить статистику для всего набора данных, то вы должны указать axis = None:

    >>> scipy.stats.gmean(a, axis=None)
    2.829705017016332
    

    Среднее геометрическое значение всех элементов в массиве a составляет приблизительно 2,83.

    Можно получить сводную статистику по Python за один вызов функции для двумерных данных с помощью scipy.stats.describe(). Он работает аналогично 1D массивам, но нужно быть осторожным с параметром axis:

    >>> scipy.stats.describe(a, axis=None, ddof=1, bias=False)
    DescribeResult(nobs=15, minmax=(1, 27), mean=5.4, variance=53.40000000000001, skewness=2.264965290423389, kurtosis=5.212690982795767)
    >>> scipy.stats.describe(a, ddof=1, bias=False)  # Default: axis=0
    DescribeResult(nobs=5, minmax=(array([1, 1, 1]), array([16, 27,  4])), mean=array([6.2, 8.2, 1.8]), variance=array([ 37.2, 121.2,   1.7]), skewness=array([1.32531471, 1.79809454, 1.71439233]), kurtosis=array([1.30376344, 3.14969121, 2.66435986]))
    >>> scipy.stats.describe(a, axis=1, ddof=1, bias=False)
    DescribeResult(nobs=3, minmax=(array([1, 1, 2, 4, 1]), array([ 1,  3,  9, 27, 16])), mean=array([ 1.,  2.,  5., 13.,  6.]), variance=array([  0.,   1.,  13., 151.,  75.]), skewness=array([0.        , 0.        , 1.15206964, 1.52787436, 1.73205081]), kurtosis=array([-3. , -1.5, -1.5, -1.5, -1.5]))
    

    При axis = None получаем сводку по всем данным. Большинство результатов являются скалярами. Если вы установите axis = 0 или вообще не укажете, то возвращаемое значение является сводкой для каждой строки. Таким образом, большинство результатов — это массивы с тем же количеством элементов, что и количество столбцов. Если вы установите axis = 1, тогда метод description() возвращает сводку для всех столбцов и количество элементов равно количеству строк.

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

    >>> result = scipy.stats.describe(a, axis=1, ddof=1, bias=False)
    >>> result.mean
    array([ 1.,  2.,  5., 13.,  6.])
    

    Вот так вы можете увидеть статистическую сводку для двумерного массива с помощью одного вызова функции.

    DataFrames

    Класс DataFrame является одним из основных типов данных Pandas. С ним очень удобно работать, потому что в нем есть метки для строк и столбцов. Используйте массив a и создайте DataFrame:

    >>> row_names = ['first', 'second', 'third', 'fourth', 'fifth']
    >>> col_names = ['A', 'B', 'C']
    >>> df = pd.DataFrame(a, index=row_names, columns=col_names)
    >>> df
             A   B  C
    first    1   1  1
    second   2   3  1
    third    4   9  2
    fourth   8  27  4
    fifth   16   1  1
    

    На практике имена столбцов имеют значение и должны быть описательными. Имена строк иногда указываются автоматически как 0, 1 и т. Д. Вы можете указать их явно с помощью параметра index, хотя вы можете свободно опускать index, если хотите.

    Методы DataFrame очень похожи на методы Series, хотя их поведение отличается. Если вы вызываете методы статистики Python без аргументов, то DataFrame будет возвращать результаты для каждого столбца:

    >>> df.mean()
    A    6.2
    B    8.2
    C    1.8
    dtype: float64
    >>> df.var()
    A     37.2
    B    121.2
    C      1.7
    dtype: float64
    

    То, что вы получаете, это новый объект Series, который содержит результаты. В этом случае ряд содержит среднее значение и дисперсию для каждого столбца. Если вы хотите получить результаты для каждой строки, просто укажите параметр axis = 1:

    >>> df.mean(axis=1)
    first      1.0
    second     2.0
    third      5.0
    fourth    13.0
    fifth      6.0
    dtype: float64
    >>> df.var(axis=1)
    first       0.0
    second      1.0
    third      13.0
    fourth    151.0
    fifth      75.0
    dtype: float64
    

    Результатом является объект Series с желаемым количеством для каждой строки. Метки «первый», «второй» и т.д. относятся к разным строкам.

    Вы можете изолировать каждый столбец DataFrame следующим образом:

    >>> df['A']
    first      1
    second     2
    third      4
    fourth     8
    fifth     16
    Name: A, dtype: int64
    

    Теперь у вас есть столбец «A» в форме объекта Series, и вы можете применить соответствующие методы:

    >>> df['A'].mean()
    6.2
    >>> df['A'].var()
    37.20000000000001
    

    Вот как вы можете получить статистику для одного столбца.

    Иногда вам может понадобиться использовать DataFrame в качестве массива NumPy и применить к нему некоторую функцию. Все данные можно получить из DataFrame с помощью .values или .to_numpy():

    >>> df.values
    array( 1,  1,  1],
           [ 2,  3,  1],
           [ 4,  9,  2],
           [ 8, 27,  4],
           [16,  1,  1)
    >>> df.to_numpy()
    array( 1,  1,  1],
           [ 2,  3,  1],
           [ 4,  9,  2],
           [ 8, 27,  4],
           [16,  1,  1)
    

    df.values и df.to_numpy() дают вам массив NumPy со всеми элементами из DataFrame без меток строк и столбцов. Обратите внимание, что df.to_numpy() более гибок, потому что вы можете указать тип данных элементов и хотите ли вы использовать существующие данные или скопировать их.

    Как и Series, объекты DataFrame имеют метод .describe(), который возвращает другой DataFrame со сводкой статистики для всех столбцов:

    >>> df.describe()
                  A          B        C
    count   5.00000   5.000000  5.00000
    mean    6.20000   8.200000  1.80000
    std     6.09918  11.009087  1.30384
    min     1.00000   1.000000  1.00000
    25%     2.00000   1.000000  1.00000
    50%     4.00000   3.000000  1.00000
    75%     8.00000   9.000000  2.00000
    max    16.00000  27.000000  4.00000
    

    В итоге получаем следующие результаты:

    • count: количество элементов в каждом столбце;
    • mean: среднее значение каждого столбца;
    • std: стандартное отклонение;
    • min и max: минимальное и максимальное значения;
    • 25%, 50% и 75%: процентили.

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

    Вы можете получить доступ к каждому элементу описательной статистики следующим образом:

    >>> df.describe().at['mean', 'A']
    6.2
    >>> df.describe().at['50%', 'B']
    3.0
    

    Вот так можно получить описательную статистику Python в одном объекте Series с помощью одного вызова метода Pandas.

    Визуализация данных

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

    matplotlib.pyplot — очень удобная и широко используемая библиотека, хотя это не единственная библиотека Python, доступная для этой цели. Вы можете импортировать это так:

    >>> import matplotlib.pyplot as plt
    >>> plt.style.use('ggplot')
    

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

    Примечание:

    В этом разделе основное внимание уделяется представлению данных и минимальным стилевым параметрам. Вы увидите ссылки на официальную документацию по используемым подпрограммам из matplotlib.pyplot, так что вы можете изучить варианты, которых вы не увидите здесь.

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

    В NumPy 1.17 появился еще один модуль для генерации псевдослучайных чисел. Чтобы узнать об этом больше, посмсотрите официальную документацию.

    Box Plots

    Ящик с усами является отличным инструментом для визуального представления описательной статистики данного набора данных. Он может показывать диапазон, межквартильный диапазон, медиану, моду, выбросы и все квартили. Во-первых, создайте некоторые данные для представления в виде графика:

    >>> np.random.seed(seed=0)
    >>> x = np.random.randn(1000)
    >>> y = np.random.randn(100)
    >>> z = np.random.randn(10)
    

    Первый оператор инициализирует генератора случайных чисел NumPy с помощью seed(). Поэтому при каждом запуске скрипта могут получаться одинаковые результаты. Не нужно устанавливать начальное значение и, если его не указывать, то каждый раз будут получаться разные результаты.

    Другие операторы создают три массива NumPy с нормально распределенными псевдослучайными числами. x относится к массиву с 1000 элементами, y имеет 100, а z содержит 10 элементов. Теперь, когда у вас есть данные для работы, вы можете применить .boxplot() для отрисовки ящика с усами:

    fig, ax = plt.subplots()
    ax.boxplot((x, y, z), vert=False, showmeans=True, meanline=True,
               labels=('x', 'y', 'z'), patch_artist=True,
               medianprops={'linewidth': 2, 'color': 'purple'},
               meanprops={'linewidth': 2, 'color': 'red'})
    plt.show()
    

    Параметры .boxplot():

    • х ваши данные.
    • vert устанавливает горизонтальную ориентацию графика, когда False. Ориентация по умолчанию - вертикальная.
    • showmeans показывает среднее значение ваших данных, когда True.
    • meanline представляет среднее в виде линии, когда истина. Представлением по умолчанию является точка.
    • labels: метки ваших данных.
    • patch_artist определяет, как рисовать график.
    • medianprops обозначает свойства линии, представляющей медиану.
    • meanprops указывает свойства линии или точки, представляющей среднее значение.

    Есть и другие параметры, но их анализ выходит за рамки данного руководства.

    Приведенный выше код создает изображение, подобное этому:

    Box plots
    Box plots

    Вы можете увидеть три сюжета. Каждый из них соответствует одному набору данных (x, y или z) и показывает следующее:

    • Среднее значение — это красная пунктирная линия.
    • Медиана — это фиолетовая линия.
    • Первый квартиль — левый край синего прямоугольника.
    • Третий квартиль — это правый край синего прямоугольника.
    • Межквартильный диапазон — это длина синего прямоугольника.
    • Диапазон — всё слева направо.
    • Выбросы — точки слева и справа.

    Сюжетная диаграмма может показать столько информации на одном рисунке!

    Гистограммы

    Гистограмма особенно полезна, когда в наборе данных содержится большое количество уникальных значений. Гистограмма делит значения из отсортированного набора данных на интервалы, также называемые ячейками. Часто все лотки имеют одинаковую ширину, но это не обязательно так. Значения нижней и верхней границ ячейки называются ребрами ячейки.

    Частота представляет собой одно значение, которое соответствует каждому бину. Это количество элементов набора данных со значениями между краями корзины. По договоренности, все корзины, кроме самой правой, наполовину открыты. Они включают значения, равные нижним границам, но исключают значения, равные верхним границам. Крайняя правая корзина закрыта, так как включает обе границы. Если вы разделите набор данных с ребрами 0, 5, 10 и 15, то есть три элемента:

    • Первый и самый левый столбец содержит значения, большие или равные 0 и меньшие 5.
    • Второй контейнер содержит значения, большие или равные 5 и меньшие 10.
    • Третий и самый правый контейнер содержит значения, большие или равные 10 и меньшие или равные 15.

    Функция np.histogram() — это удобный способ получить данные для гистограмм:

    >>> hist, bin_edges = np.histogram(x, bins=10)
    >>> hist
    array([  9,  20,  70, 146, 217, 239, 160,  86,  38,  15])
    >>> bin_edges
    array([-3.04614305, -2.46559324, -1.88504342, -1.3044936 , -0.72394379,
           -0.14339397,  0.43715585,  1.01770566,  1.59825548,  2.1788053 ,
            2.75935511])
    

    Он берет массив с вашими данными и количеством (или ребрами) бинов и возвращает два массива NumPy:

    • hist содержит частоту или количество элементов, соответствующих каждому бину.
    • bin_edges содержит ребра или границы корзины.

    Что рассчитывает histogram(), граф .hist() может показать графически:

    fig, ax = plt.subplots()
    ax.hist(x, bin_edges, cumulative=False)
    ax.set_xlabel('x')
    ax.set_ylabel('Frequency')
    plt.show()
    

    Первый аргумент .hist() — это последовательность с вашими данными. Второй аргумент определяет края бинов. Третий отключает возможность создания гистограммы с накопленными значениями. Приведенный выше код создает такую фигуру:

    Гистограмма
    Гистограмма

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

    При значении аргумента cumulative = True в .hist() можно получить гистограмму с совокупным количеством элементов:

    fig, ax = plt.subplots()
    ax.hist(x, bin_edges, cumulative=True)
    ax.set_xlabel('x')
    ax.set_ylabel('Frequency')
    plt.show()
    

    Результат выполнения кода посмотрите на рисунке:

    Гистограмма с накоплением
    Гистограмма с накоплением

    Частота первого и самого левого лотка — это количество элементов в этом лотке. Частота второго бина — это сумма количества элементов в первом и втором бинах. Другие контейнеры отрисовываются аналогично. Наконец, частота последнего и самого правого бина — это общее количество элементов в наборе данных (в данном случае 1000). Вы также можете напрямую нарисовать гистограмму с помощью pd.Series.hist(), используя matplotlib в фоновом режиме.

    Pie Charts круговые диаграммы

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

    Давайте определим данные, связанные с тремя метками:

    >>> x, y, z = 128, 256, 1024
    

    Теперь создайте круговую диаграмму с помощью .pie():

    fig, ax = plt.subplots()
    ax.pie((x, y, z), labels=('x', 'y', 'z'), autopct='%1.1f%%')
    plt.show()
    

    Первый аргумент .pie() — данные, а второй — последовательность соответствующих меток. autopct определяет формат относительных частот, показанных на рисунке. Вы получите фигуру, которая выглядит следующим образом:

    Круговая диаграмма
    Круговая диаграмма

    Круговая диаграмма показывает x как наименьшую часть круга, y как следующую наибольшую, а затем z как самую большую часть. Проценты обозначают относительный размер каждого значения по сравнению с их суммой.

    Bar Charts

    Гистограммы также иллюстрируют данные, которые соответствуют заданным меткам или дискретным числовым значениям. Они могут показывать пары данных из двух наборов данных. Элементы одного набора - это метки, а соответствующие элементы другого - их частоты. При желании они также могут отображать ошибки, связанные с частотами.

    Гистограмма показывает параллельные прямоугольники, называемые барами. Каждая полоса соответствует одной метке и имеет высоту, пропорциональную частоте или относительной частоте ее метки. Давайте создадим три набора данных, каждый из которых содержит 21 элемент:

    >>> x = np.arange(21)
    >>> y = np.random.randint(21, size=21)
    >>> err = np.random.randn(21)
    

    Вы используете np.arange(), чтобы получить x или массив последовательных целых чисел от 0 до 20. Вы будете использовать это для представления меток. y — это массив равномерно распределенных случайных целых чисел, также от 0 до 20. Этот массив будет представлять частоты. err содержит нормально распределенные числа с плавающей точкой, которые являются ошибками. Эти значения не являются обязательными.

    Вы можете создать гистограмму с помощью .bar(), если вам нужны вертикальные столбцы или .barh(), если вам нужны горизонтальные столбцы:

    fig, ax = plt.subplots())
    ax.bar(x, y, yerr=err)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    plt.show()
    

    Результат вы видите на рисунке:

    Bar Charts
    Bar Charts

    Высоты красных полос соответствуют частотам y, а длины черных линий показывают ошибки err. Если вы не хотите включать ошибки, пропустите параметр yerr в .bar().

    X-Y участки

    Диаграмма x-y или диаграмма рассеяния представляет пары данных из двух наборов данных. Горизонтальная ось x показывает значения из набора x, а вертикальная ось y показывает соответствующие значения из набора y. При желании вы можете включить линию регрессии и коэффициент корреляции. Давайте сгенерируем два набора данных и выполним линейную регрессию с помощью scipy.stats.linregress():

    >>> x = np.arange(21)
    >>> y = 5 + 2 * x + 2 * np.random.randn(21)
    >>> slope, intercept, r, *__ = scipy.stats.linregress(x, y)
    >>> line = f'Regression line: y={intercept:.2f}+{slope:.2f}x, r={r:.2f}'
    

    Набор данных x снова является массивом с целыми числами от 0 до 20. y вычисляется как линейная функция x, искаженная некоторым случайным шумом.

    linregress возвращает несколько значений. Вам понадобится наклон и точка пересечения линии регрессии, а также коэффициент корреляции r. Затем вы можете применить .plot(), чтобы получить график x-y:

    fig, ax = plt.subplots()
    ax.plot(x, y, linewidth=0, marker='s', label='Data points')
    ax.plot(x, intercept + slope * x, label=line)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.legend(facecolor='white')
    plt.show()
    

    Вот результат:

    Диаграмма рассеивания
    Диаграмма рассеивания

    Вы можете видеть точки данных (пары x-y) в виде красных квадратов, а также синюю линию регрессии.

    Схемы Зоны активности

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

    matrix = np.cov(x, y).round(decimals=2)
    fig, ax = plt.subplots()
    ax.imshow(matrix)
    ax.grid(False)
    ax.xaxis.set(ticks=(0, 1), ticklabels=('x', 'y'))
    ax.yaxis.set(ticks=(0, 1), ticklabels=('x', 'y'))
    ax.set_ylim(1.5, -0.5)
    for i in range(2):
        for j in range(2):
            ax.text(j, i, matrix[i, j], ha='center', va='center', color='w')
    plt.show()
    

    Здесь тепловая карта содержит метки «x» и «y», а также числа из ковариационной матрицы. Вы получите такую фигуру:

    Тепловая карта
    Тепловая карта

    Желтое поле представляет самый большой элемент из матрицы 130.34, а фиолетовое соответствует наименьшему элементу 38.5. Синие квадраты между ними связаны со значением 69,9.

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

    matrix = np.corrcoef(x, y).round(decimals=2)
    fig, ax = plt.subplots()
    ax.imshow(matrix)
    ax.grid(False)
    ax.xaxis.set(ticks=(0, 1), ticklabels=('x', 'y'))
    ax.yaxis.set(ticks=(0, 1), ticklabels=('x', 'y'))
    ax.set_ylim(1.5, -0.5)
    for i in range(2):
        for j in range(2):
            ax.text(j, i, matrix[i, j], ha='center', va='center', color='w')
    plt.show()
    

    Посмотрите на результат:

    Ещё одна тепловая карта
    Ещё одна тепловая карта

    Желтый цвет представляет значение 1,0, а фиолетовый показывает 0,99.

    Заключение

    Теперь вы знаете величины, характеризующие и обобщают наборы данных, и как их вычислять в Python. Можно получить описательную статистику с помощью чистого кода Python, но делают это крайне редко. Обычно вы используете некоторые библиотеки, созданные специально для этой цели:

    • Используйте статистику Python для наиболее важных статистических функций Python.
    • Используйте NumPy для эффективной обработки массивов.
    • Используйте SciPy для дополнительных подпрограмм статистики Python для массивов NumPy.
    • Используйте Pandas для работы с помеченными наборами данных.
    • Для визуализации данных с помощью графиков, диаграмм и гистограмм рекомендую использовать:

    В эпоху больших данных и искусственного интеллекта вы должны знать, как рассчитывать описательную статистику. Теперь вы готовы глубже погрузиться в мир науки о данных и машинного обучения! Если у вас есть вопросы или комментарии, пожалуйста, оставьте их в комментариях ниже.

    Использованы материалы: Python Statistics Fundamentals: How to Describe Your Data

    ]]>
    https://chel-center.ru/python-yfc/2020/02/13/opisatelnaya-statistika-na-python-chast-2/feed/ 0
    Лучшие датасеты для машинного обучения и анализа данных https://chel-center.ru/python-yfc/2021/01/15/luchshie-datasety-dlja-mashinnogo-obuchenija-i-analiza-dannyh/ https://chel-center.ru/python-yfc/2021/01/15/luchshie-datasety-dlja-mashinnogo-obuchenija-i-analiza-dannyh/#respond Fri, 15 Jan 2021 13:56:11 +0000 http://waksoft.susu.ru/?p=27078 Читать далее «Лучшие датасеты для машинного обучения и анализа данных»

    ]]>
    Для анализа данных и машинного обучения требуется много данных. Можно было бы собрать их самостоятельно, но это утомительно. Здесь нам на помощь приходят готовые датасеты в самых разных категориях:

    Где искать датасеты

    • Google Dataset Search. Dataset Search позволяет по ключевому слову искать датасеты по всей Сети.
    • Kaggle. Площадка для соревнований по машинному обучению с множеством интересных датасетов. В списке датасетов можно найти разные нишевые экземпляры — от оценок рамена до баскетбольных данных NCAA  и базы лицензий на домашних животных в Сиэтле.
    • UCI Machine Learning Repository. Один из старейших источников датасетов в Сети и первое место, куда стоит заглянуть в поиске интересных датасетов. Хотя они добавляются пользователями и потому имеют различную степень «чистоты», большинство из них очищены. Данные можно скачивать сразу, без регистрации.
    • VisualData. Датасеты для компьютерного зрения, разбитые по категориям. Доступен поиск.
    • Find Datasets | CMU Libraries. Коллекция датасетов, предоставленная университетом Карнеги Меллон.

    Датасеты общего назначения

    Государственные датасеты

    • Data.gov. Здесь можно найти данные от разных государственных учреждений США. Они варьируются от государственных бюджетов до школьных оценок.
    • Food Environment Atlas. Содержит данные о том, как различные факторы (близость магазинов/ресторанов, цены на продукты и тому подобное) влияют на выбор продуктов и качество питания в США.
    • School system finances. Данные о финансах школьных систем в США.
    • Chronic disease data. Данные о показателях хронических заболеваний на территории США.
    • The US National Center for Education Statistics. Данные об образовательных учреждениях и образовательной демографии в США и во всём мире.
    • The UK Data Service. Крупнейшая в Великобритании коллекция социальных, экономических и демографических данных.
    • Data USA. Исчерпывающая визуализация общедоступных данных США.

    Данные о жилье

    • Boston Housing Dataset. Содержит информацию о жилье в Бостоне, собранную бюро переписи населения США. Она была получена из архива StatLib и широко использовалась в литературе для оценки алгоритмов.

    Экономика и финансы

    • Quandl. Хороший источник экономических и финансовых данных — полезен при построении моделей для прогнозирования экономических показателей или цен на акции.
    • World Bank Open Data. Наборы данных, охватывающих демографическую ситуацию, огромное количество экономических показателей и индикаторов развития со всего мира.
    • IMF Data. Международный валютный фонд публикует данные о международных финансах, показателях долга, валютных резервах, инвестициях и ценах на сырьевые товары.
    • Financial Times Market Data. Актуальная информация о финансовых рынках со всего мира, которая включает индексы цен на акции, товары и валюту.
    • Google Trends. Изучайте и анализируйте данные о поисковой активности в Интернете и трендах по всему миру.
    • American Economic Association (AEA). Хороший источник данных о макроэкономике США.

    Датасеты для машинного обучения

    Компьютерное зрение

    • xView. Один из самых больших общедоступных наборов воздушных снимков земли. Он содержит изображения различных сцен со всего мира, аннотированных с помощью ограничительных рамок.
    • Labelme. Большой датасет аннотированных изображений.
    • ImageNet. Датасет изображений для новых алгоритмов, организованный в соответствии с иерархией WordNet, в которой сотни и тысячи изображений представляют каждый узел иерархии.
    • LSUN. Датасет изображений, разбитых по сценам и категориям с частичной разметкой данных.
    • MS COCO. Крупномасштабный датасет для обнаружения и сегментации объектов.
    • COIL100. 100 разных объектов, изображённых под каждым углом в круговом обороте.
    • Visual Genome. Датасет с ~100 тыс. подробно аннотированных изображений.
    • Google’s Open Images. Коллекция из 9 миллионов URL-адресов к изображениям, «которые были помечены метками, охватывающими более 6000 категорий» под лицензией Creative Commons.
    • Labelled Faces in the Wild. Набор из 13 000 размеченных изображений лиц людей для использования приложений, которые предполагают использование технологии распознавания лиц.
    • Stanford Dogs Dataset. Содержит 20 580 изображений из 120 пород собак.
    • Indoor Scene Recognition. Датасет для распознавания интерьера зданий. Содержит 15 620 изображений и 67 категорий.

    Анализ отзывов

    • Multidomain sentiment analysis dataset. Немного устаревший датасет, который содержит отзывы на товары с Amazon.
    • IMDB reviews. Староватый, относительной небольшой (25 000 отзывов к фильмам) датасет для бинарного анализа отзывов.
    • Stanford Sentiment Treebank. Стэнфордский датасет для анализа отзывов.
    • Sentiment140. Популярный датасет с 160 000 твитов с удалёнными смайликами.
    • Twitter US Airline Sentiment. Набор данных из Twitter об авиакомпаниях США, датируемый февралём 2015 года, разделённый на положительные, негативные и нейтральные твиты.

    Обработка естественного языка

    • HotspotQA Dataset. Датасет с вопросами-ответами, позволяющий создавать системы для ответов на вопросы более понятным способом.
    • Enron Dataset. Данные электронной почты от высшего руководства Enron.
    • Amazon Reviews. Содержит около 35 млн отзывов с Amazon за 18 лет. Данные включают информацию о продукте и пользователе, оценки и сам текст отзыва.
    • Google Books Ngrams. Коллекция слов из Google Книги.
    • Blogger Corpus. Коллекция из 681 288 постов с Blogger. Каждый блог содержит как минимум 200 вхождений часто используемых английских слов.
    • Wikipedia Links data. Датасет, состоящий из веб-страниц, которые удовлетворяют следующим двум условиям: каждая из них содержит хотя бы одну ссылку на Википедию и текст её якоря совпадает или похож на заголовок целевой страницы.
    • Gutenberg eBooks List. Аннотированный список электронных книг проекта «Гутенберг».
    • Hansards text chunks of Canadian Parliament. Датасет с 1.3 миллионами пар текстовых файлов, записанных с дебатов 36-го Канадского Парламента.
    • Jeopardy. Архив с более чем 200 000 вопросов с телевикторины Jeopardy.
    • Rotten Tomatoes Reviews. Архив из более чем 480 000 рецензий с Rotten Tomatoes.
    • SMS Spam Collection in English. Датасет, состоящий из 5574 спам-смс на английском.
    • Yelp Reviews. Датасет от Yelp, содержащий более 5 млн отзывов.
    • UCI’s Spambase. Большой датасет спам-писем.

    Автопилоты

    • Berkeley DeepDrive BDD100k. На данный момент это самый большой датасет для автопилотов. Он содержит более 100 000 видео с более чем 1100 часами записей вождения в разное время дня и в различных погодных условиях.
    • Baidu Apolloscapes. Большой датасет для распознавания 26 семантически разных объектов вроде машин, велосипедов, пешеходов, зданий, уличных фонарей и т. д.
    • Comma.ai. Более семи часов езды по шоссе. Датасет включает информацию о скорости машины, ускорении, угле поворота руля и GPS-координатах.
    • Oxford’s Robotic Car. Более ста повторений одного маршрута по Оксфорду, заснятого в течение года. В датасет попали разные комбинации погодных условий, трафика и пешеходов, а также более длительные изменения вроде дорожных работ.
    • Cityscape Dataset. Большой датасет, содержащий записи ста уличных сцен в 50 городах.
    • KUL Belgium Traffic Sign Dataset. Более 10 000 аннотаций тысяч разных светофоров в Бельгии.
    • LISA. Laboratory for Intelligent & Safe Automobiles, UC San Diego Datasets. Датасет с дорожными знаками, светофорами, распознанными средствами передвижения и траекториями движения.
    • Bosch Small Traffic Light Dataset. Датасет с 24 000 аннотированных светофоров.
    • LaRa Traffic Light Recognition. Ещё один датасет для распознавания светофоров.
    • WPI datasets. Датасет для распознавания светофоров, пешеходов и дорожной разметки.

    Медицинские данные

    • MIMIC-III. Датасет с обезличенными данными о состоянии здоровья ~40 000 пациентов, находящихся на интенсивной терапии. Он включает демографические данные, показатели жизнедеятельности, лабораторные анализы, лекарства и многое другое.

    Оригинал: Tproger

    Перевод статьи «The Best Public Datasets for Machine Learning and Data Science»

    P.S. Поскромнее, но зато отечественные на все случаи данных

    ]]>
    https://chel-center.ru/python-yfc/2021/01/15/luchshie-datasety-dlja-mashinnogo-obuchenija-i-analiza-dannyh/feed/ 0
    Учебное пособие по NumPy: ваши первые шаги в науке о данных на Python https://chel-center.ru/python-yfc/2021/04/19/uchebnoe-posobie-po-numpy-vashi-pervye-shagi-v-nauke-o-dannyh-na-python/ https://chel-center.ru/python-yfc/2021/04/19/uchebnoe-posobie-po-numpy-vashi-pervye-shagi-v-nauke-o-dannyh-na-python/#respond Mon, 19 Apr 2021 04:56:01 +0000 http://chel-center.ru/python-yfc/?p=32129 Читать далее «Учебное пособие по NumPy: ваши первые шаги в науке о данных на Python»

    ]]>
     

    NumPy — это библиотека Python, которая предоставляет простую, но мощную структуру данных: n-мерный массив. Это фундамент, на котором построена почти вся мощь инструментария Python для обработки данных, а изучение NumPy — это первый шаг на пути любого ученого-специалиста по Python. Этот урок предоставит вам знания, необходимые для использования NumPy и библиотек более высокого уровня, которые на него полагаются.

    В этом руководстве вы узнаете:

    • Какие основные концепции науки о данных стали возможны с помощью NumPy.
    • Как создавать массивы NumPy разными методами.
    • Как манипулировать массивами NumPy для выполнения полезных вычислений.
    • Как применить эти новые навыки к реальным проблемам.

    Чтобы получить максимальную отдачу от урока по NumPy, вы должны быть знакомы с написанием кода Python. «Курс молодого бойца — Python» — отличный способ убедиться, что у вас есть базовые навыки. Знания матричной математики так-же будут полезно. Однако, ничего не нужно знать о науке о данных. Вы узнаете это здесь.

    Содержание

    Есть репозиторий примеров кода NumPy, которые вы увидите в этом руководстве. Вы можете использовать его для справки и поэкспериментировать с примерами, чтобы увидеть, как изменение кода меняет результат. Чтобы скачать код, щелкните ссылку ниже:

    Выбор NumPy: преимущества

    Поскольку вы уже знаете Python, вы можете спросить себя, действительно ли вам нужно изучать совершенно новую парадигму, чтобы заниматься наукой о данных. Циклы for в Python потрясающие! Чтение и запись файлов CSV можно выполнять с помощью традиционного кода. Однако есть несколько убедительных аргументов в пользу изучения новой парадигмы.

    Вот четыре основных преимущества, которые NumPy может дать вашему коду:

    1. Больше скорости: NumPy использует алгоритмы, написанные на C, которые выполняются за наносекунды, а не за секунды.
    2. Меньше циклов: NumPy помогает сократить количество циклов и не запутаться в индексах итераций.
    3. Более четкий код: без циклов,ваш код будет больше похож на уравнения, которые вы пытаетесь вычислить.
    4. Лучшее качество: тысячи участников работают над тем, чтобы NumPy оставался быстрым, дружелюбным и свободным от ошибок.

    Благодаря этим преимуществам NumPy является стандартом де-факто для многомерных массивов в науке о данных Python, и многие из самых популярных библиотек построены на его основе. Изучение NumPy — отличный способ заложить прочную основу, поскольку вы расширяете свои знания в более конкретные области науки о данных.

    Установка NumPy

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

    Вы также будете устанавливать Matplotlib. Вы будете использовать его в одном из следующих примеров, чтобы изучить, как другие библиотеки используют NumPy.

    Использование Repl.it в качестве онлайн-редактора

    Если вы просто хотите начать с некоторых примеров, следуйте инструкциям по этому руководству и начните наращивать мышечную память с помощью NumPy, тогда Repl.it — ​​отличный вариант для редактирования в браузере. Вы можете зарегистрироваться и запустить среду Python за считанные минуты. Слева есть вкладка для пакетов. Вы можете добавить столько, сколько хотите.В этом руководстве по NumPy используйте текущие версии NumPy и Matplotlib.

    Установка NumPy с Anaconda

    Дистрибутив Anaconda — это набор стандартных инструментов Python для обработки данных, связанных с менеджером пакетов, который помогает управлять вашими виртуальными средами и зависимостями проекта. Он построен на основе conda, которая фактически является менеджером пакетов. Это метод, рекомендованный проектом NumPy,особенно если вы начинаете заниматься наукой о данных в Python, еще не настроив сложную среду разработки.

    Если у вас уже есть рабочий процесс, который вам нравится, в котором используются pip, Pipenv, Poetry или какой-либо другой набор инструментов, тогда, возможно, лучше не добавлять conda в микс. Репозиторий пакетов conda отделен от PyPI, а сама conda создает отдельный маленький островок пакетов на вашем компьютере, поэтому управление путями и запоминание того, какой пакет живет где, может быть кошмаром.

    После того, как вы установили conda, вы можете запустить команду установки необходимых вам библиотек:

    $ conda install numpy matplotlib
    

    Это установит все, что вам нужно для этого руководства по NumPy, и все будет готово.

    Установка NumPy с помощью pip

    Хотя проект NumPy рекомендует использовать conda, если вы начинаете с нуля, нет ничего плохого в том, чтобы самостоятельно управлять своей средой и просто использовать старый добрый pip, Pipenv, Poetry или любую другую альтернативу pip, которая вам больше всего нравится.

    Вот команды для настройки с помощью pip:

    $ mkdir numpy-tutorial
    $ cd numpy-tutorial
    $ python3 -m venv .numpy-tutorial-venv
    $ source .numpy-tutorial-venv/bin/activate
    
    (.numpy-tutorial-venv)
    $ pip install numpy matplotlib
    Collecting numpy
      Downloading numpy-1.19.1-cp38-cp38-macosx_10_9_x86_64.whl (15.3 MB)
         |████████████████████████████████| 15.3 MB 2.7 MB/s
    Collecting matplotlib
      Downloading matplotlib-3.3.0-1-cp38-cp38-macosx_10_9_x86_64.whl (11.4 MB)
         |████████████████████████████████| 11.4 MB 16.8 MB/s
    ...
    

    После этого убедитесь, что ваша виртуальная среда активирована, и весь ваш код должен работать должным образом.

    Использование IPython, Notebooks или JupyterLab

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

    IPython — это обновленный цикл чтения-оценки-печати Python (REPL), который делает редактирование кода в сеансе реального интерпретатора более простым и красивым. Вот как выглядит сеанс REPL IPython:

    
    In [2]: digits = np.array([
       ...:     [1, 2, 3],
       ...:     [4, 5, 6],
       ...:     [6, 7, 9],
       ...: ])
    
    In [3]: digits
    Out[3]:
    array([ [1, 2, 3],
           [4, 5, 6],
           [6, 7, 9] ])
    

    Он имеет несколько отличий от базового REPL Python, включая номера строк, использование цветов и качество визуализации массивов. Также есть много бонусов для пользовательского опыта, которые делают более приятным ввод, повторный ввод и редактирование кода.

    Вы можете установить IPython отдельно:

    $ pip install ipython
    

    В качестве альтернативы, если вы подождете и установите любой из последующих инструментов, они будут включать копию IPython.

    Чуть более функциональная альтернатива REPL — это ноутбук. Однако записные книжки — это немного другой стиль написания Python, чем стандартные сценарии. Вместо традиционного файла Python они предоставляют вам серию мини-файлов. Сценарии, называемые ячейками, которые вы можете запускать и повторно запускать в любом порядке, в том же сеансе Python.

    Одна из замечательных особенностей записных книжек заключается в том, что вы можете включать графики и отображать абзацы Markdown между ячейками, так что они действительно удобны для написания анализа данных прямо внутри кода!

    Вот как это выглядит:

    Самым популярным предложением ноутбуков, вероятно, является Jupyter Notebook, но nteract — это еще один вариант, который объединяет функциональность Jupyter и пытается сделать его более доступным и мощным.

    Однако, если вы смотрите на Jupyter Notebook и думаете, что ему нужно больше возможностей, подобных IDE, то другой вариант — JupyterLab. Вы можете настраивать текстовые редакторы, записные книжки, терминалы и пользовательские компоненты в интерфейсе на основе браузера. Вероятно, это будет удобнее для людей, пришедших из MatLab. Это самое молодое из предложений, но его версия 1.0 была выпущена еще в 2019 году, поэтому она должна быть стабильной и полнофункциональной.

    Вот как выглядит интерфейс:

    Какой бы вариант вы ни выбрали, после его установки вы будете готовы запустить свои первые строки кода NumPy. Пришло время для первого примера.

    Здравствуйте, NumPy: руководство по выставлению оценок за тест на изгиб

    В первом примере представлены несколько основных концепций NumPy, которые вы будете использовать в оставшейся части руководства:

    • Создание массивов с использованием numpy.array()
    • Обработка полных массивов как отдельных значений, чтобы сделать векторизованные вычисления более удобочитаемыми
    • Использование встроенных функций NumPy для изменения и агрегирования данных

    Эти концепции составляют основу эффективного использования NumPy.

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

    Однако это будет относительно элементарное исправление. Вы возьмете любой средний балл и объявите, что это C. Кроме того, вы убедитесь, что кривая случайно не повредит успеваемость ваших учеников и не поможет настолько, что ученик успеет лучше, чем 100%.

    Введите этот код в свой REPL:

    >>> import numpy as np
    >>> CURVE_CENTER = 80
    >>> grades = np.array([72, 35, 64, 88, 51, 90, 74, 12])
    >>> def curve(grades):
    ...     average = grades.mean()
    ...     change = CURVE_CENTER - average
    ...     new_grades = grades + change
    ...     return np.clip(new_grades, grades, 100)
    ...
    >>> curve(grades)
    array([ 91.25,  54.25,  83.25, 100.  ,  70.25, 100.  ,  93.25,  31.25])
    

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

    Вот важные моменты:

    • Строка 1 импортирует NumPy с использованием псевдонима np, что является общепринятым соглашением, позволяющим сэкономить несколько нажатий клавиш.
    • Строка 3 создает ваш первый массив NumPy, который является одномерным и имеет форму (8,) и тип данных int64. Пока не стоит особо беспокоиться об этих деталях. Вы изучите их более подробно позже в этом руководстве.
    • В строке 5 берется среднее значение всех оценок с использованием .mean(). У массивов много методов.

    В строке 7 вы сразу пользуетесь двумя важными концепциями:

    1. Векторизация
    2. Вещание или Broadcasting

    Векторизация — это процесс выполнения одной и той же операции одинаковым образом для каждого элемента в массиве. Это удаляет циклы for из вашего кода, но дает тот же результат.

    Broadcasting — это процесс расширения двух массивов разной формы и выяснения того, как выполнять векторизованные вычисления между ними. Помните, Grades — это массив чисел формы (8,), а изменение — это скаляр или одно число, по существу, имеющее форму (1,). В этом случае NumPy добавляет скаляр к каждому элементу в массиве и возвращает новый массив с результатами.

    Наконец, в строке 8 вы ограничиваете или обрезаете значения набором минимумов и максимумов. Помимо методов массива, NumPy также имеет большое количество встроенных функций. Не нужно их все запоминать — для этого и нужна документация. Каждый раз, когда вы застреваете или чувствуете, что должен быть более простой способ что-то сделать, загляните в документацию и посмотрите, нет ли уже процедуры, которая делает именно то, что вам нужно.

    В этом случае, вам нужна функция, которая принимает массив и следит за тем, чтобы значения не превышали заданный минимум или максимум. clip() делает именно это.

    Строка 8 также представляет собой еще один пример широковещательной передачи. Для второго аргумента clip() вы передаете оценки, гарантируя, что каждая новая оценка не будет ниже исходной. Но для третьего аргументавы передаете единственное значение: 100. NumPy принимает это значение и передает его каждому элементу в new_grades, гарантируя, что ни одна из недавно изогнутых оценок не превышает идеального результата.

    Переход к форме: формы и оси массивов

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

    Векторы, представляющие собой одномерные массивы чисел, легче всего отслеживать. Два измерения тоже не так уж и плохи, потому что они похожи на электронные таблицы. Но все начинает усложняться в трех измерениях, а визуализировать четыре? Забудь об этом.

    Освоение формы

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

    Все массивы имеют свойство с именем .shape, которое возвращает кортеж размера в каждом измерении. Менее важно, какое измерение является каким, но очень важно, чтобы массивы, которые вы передаете функциям, имели форму, которую ожидают функции. Обычный способ убедиться, что ваши данные имеют правильную форму, — это распечатать данные и их форму до тех пор, пока вы не убедитесь, что все работает так, как вы ожидаете.

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

    In [1]: import numpy as np
    
    In [2]: temperatures = np.array([
       ...:     29.3, 42.1, 18.8, 16.1, 38.0, 12.5,
       ...:     12.6, 49.9, 38.6, 31.3, 9.2, 22.2
       ...: ]).reshape(2, 2, 3)
    
    In [3]: temperatures.shape
    Out[3]: (2, 2, 3)
    
    In [4]: temperatures
    Out[4]:
    array([ [ [29.3, 42.1, 18.8],
            [16.1, 38. , 12.5] ],
    
           [ [12.6, 49.9, 38.6],
            [31.3,  9.2, 22.2] ] ])
    
    In [5]: np.swapaxes(temperatures, 1, 2)
    Out[5]:
    array([ [ [29.3, 16.1],
            [42.1, 38. ],
            [18.8, 12.5] ],
    
           [ [12.6, 31.3],
            [49.9,  9.2],
            [38.6, 22.2] ] ])
    

    Здесь вы используете метод numpy.ndarray с именем .reshape() для формирования блока данных размером 2 × 2 × 3. Когда вы проверяете форму вашего массива во входных данных 3, это именно то, что вы ему сказали. Однако вы можете видеть, как быстро становится трудно визуализировать напечатанные массивы в трех или более измерениях. После того, как вы поменяете оси с помощью .swapaxes (), становится немного яснее, какое измерение есть какое. Подробнее об осях вы узнаете в следующем разделе.

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

    Что такое топоры

    Приведенный выше пример показывает, насколько важно знать не только, в какой форме находятся ваши данные, но и какие данные находятся на какой оси. В массивах NumPy оси имеют нулевой индекс и определяют, какое измерение является каким. Например, двумерный массив имеет вертикальную ось (ось 0) и горизонтальную ось (ось 1). Многие функции и команды в NumPy изменяют свое поведение в зависимости от того, какую ось вы им приказываете обрабатывать.

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

    In [1]: import numpy as np
    
    In [2]: table = np.array([
       ...:     [5, 3, 7, 1],
       ...:     [2, 6, 7 ,9],
       ...:     [1, 1, 1, 1],
       ...:     [4, 3, 2, 0],
       ...: ])
    
    In [3]: table.max()
    Out[3]: 9
    
    In [4]: table.max(axis=0)
    Out[4]: array([5, 6, 7, 9])
    
    In [5]: table.max(axis=1)
    Out[5]: array([7, 9, 1, 4])
    

    По умолчанию .max() возвращает наибольшее значение во всем массиве, независимо от количества измерений. Однако, как только вы укажете ось, он выполняет это вычисление для каждого набора значений вдоль этой конкретной оси. Например, с аргументом axis=0, .max() выбирает максимальное значение в каждом из четырех вертикальных наборов значений в таблице и возвращает массив, который был сведен или агрегирован в одномерный массив.

    Фактически, многие функции NumPy ведут себя следующим образом: если ось не указана, они выполняют операцию со всем набором данных. Иначе, они выполняют операцию по оси.

    Broadcasting

    До сих пор вы видели несколько небольших примеров Broadcasting, но чем больше примеров вы увидите, тем яснее станет эта тема. По сути, он работает вокруг одного правила: массивы могут транслироваться друг против друга, если их размеры совпадают или если один из массивов имеет размер 1.

    Если массивы совпадают по размеру вдоль оси,тогда элементы будут обрабатываться поэлементно, подобно тому, как работает встроенная функция Python zip(). Если один из массивов имеет размер 1 по оси, то это значение будет транслироваться по этой оси или дублироваться столько раз, сколько необходимо, чтобы соответствовать количеству элементов вдоль этой оси в другом массиве.

    Вот небольшой пример. Массив A имеет форму (4, 1, 8), а массив B имеет форму (1, 6, 8). Основываясь на приведенных выше правилах, вы можете работать с этими массивами вместе:

    • На оси 0 у A есть 4, а у B — 1, поэтому B может транслироваться по этой оси.
    • На оси 1 у A есть 1, а у B — 6, поэтому A может транслироваться по этой оси.
    • На оси 2 два массива имеют совпадающие размеры,чтобы они могли успешно работать.

    Все три оси успешно следуют правилу.

    Вы можете настроить массивы следующим образом:

    In [1]: import numpy as np
    
    In [2]: A = np.arange(32).reshape(4, 1, 8)
    
    In [3]: A
    Out[3]:
    array([ [ [ 0,  1,  2,  3,  4,  5,  6,  7] ],
    
           [ [ 8,  9, 10, 11, 12, 13, 14, 15] ],
    
           [ [16, 17, 18, 19, 20, 21, 22, 23] ],
    
           [ [24, 25, 26, 27, 28, 29, 30, 31] ] ])
    
    In [4]: B = np.arange(48).reshape(1, 6, 8)
    
    In [5]: B
    Out[5]:
    array([ [ [ 0,  1,  2,  3,  4,  5,  6,  7],
            [ 8,  9, 10, 11, 12, 13, 14, 15],
            [16, 17, 18, 19, 20, 21, 22, 23],
            [24, 25, 26, 27, 28, 29, 30, 31],
            [32, 33, 34, 35, 36, 37, 38, 39],
            [40, 41, 42, 43, 44, 45, 46, 47] ] ])
    

    У A 4 плоскости, каждая с 1 строкой и 8 столбцами. B имеет только 1 плоскость с 6 рядами и 8 столбцами. Посмотрите, что NumPy делает для вас, когда вы пытаетесь произвести расчет между ними!

    Сложите два массива вместе:

    In [7]: A + B
    Out[7]:
    array([ [ [ 0,  2,  4,  6,  8, 10, 12, 14],
            [ 8, 10, 12, 14, 16, 18, 20, 22],
            [16, 18, 20, 22, 24, 26, 28, 30],
            [24, 26, 28, 30, 32, 34, 36, 38],
            [32, 34, 36, 38, 40, 42, 44, 46],
            [40, 42, 44, 46, 48, 50, 52, 54] ],
    
           [ [ 8, 10, 12, 14, 16, 18, 20, 22],
            [16, 18, 20, 22, 24, 26, 28, 30],
            [24, 26, 28, 30, 32, 34, 36, 38],
            [32, 34, 36, 38, 40, 42, 44, 46],
            [40, 42, 44, 46, 48, 50, 52, 54],
            [48, 50, 52, 54, 56, 58, 60, 62] ],
    
           [ [16, 18, 20, 22, 24, 26, 28, 30],
            [24, 26, 28, 30, 32, 34, 36, 38],
            [32, 34, 36, 38, 40, 42, 44, 46],
            [40, 42, 44, 46, 48, 50, 52, 54],
            [48, 50, 52, 54, 56, 58, 60, 62],
            [56, 58, 60, 62, 64, 66, 68, 70] ],
    
           [ [24, 26, 28, 30, 32, 34, 36, 38],
            [32, 34, 36, 38, 40, 42, 44, 46],
            [40, 42, 44, 46, 48, 50, 52, 54],
            [48, 50, 52, 54, 56, 58, 60, 62],
            [56, 58, 60, 62, 64, 66, 68, 70],
            [64, 66, 68, 70, 72, 74, 76, 78] ] ])
    

    Принцип работы broadcasting заключается в том, что NumPy дублирует плоскость в B три раза, так что у вас всего четыре, что соответствует количеству плоскостей в A. Он также дублирует одну строку в A пять раз, всего шесть, что соответствует числу. строк в B. Затем он добавляет каждый элемент во вновь развернутом массиве A к его аналогу в том же месте в B. Результат каждого вычисления отображается в соответствующем месте вывода.

    Примечание. Это хороший способ создать массив из диапазона с помощью arange()!

    Еще раз, даже если вы можете использовать такие слова, как «плоскость», «строка» и «столбец», чтобы описать, как фигуры в этом примере транслируются для создания совпадающих трехмерных фигур, все становится сложнее в более высоких измерениях. Часто вам придется просто соблюдать правила вещания и много печатать, чтобы убедиться, что все работает по плану.

    Понимание broadcasting — важная часть освоения векторизованных вычислений, а векторизованные вычисления — это способ написать чистый идиоматический код NumPy.

    Операции обработки данных: фильтр, порядок, агрегирование

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

    Индексирование

    Индексирование использует многие из тех идиом, которые использует обычный код Python. Вы можете использовать положительные или отрицательные индексы для индексации спереди или сзади массива. Вы можете использовать двоеточие (:), чтобы указать «остальное» или «все», и вы даже можете использовать два двоеточия для пропуска элементов, как в обычных списках Python.

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

    Числовой квадрат ниже обладает некоторыми удивительными свойствами. Если сложить строки, столбцы или диагонали, получится то же число — 34. То же самое вы получите, если сложите каждый из четырех квадрантов, четыре центральных квадрата, четыре угловых квадрата или четыре угловых квадрата любой из имеющихся сеток 3 × 3. Вы собираетесь это доказать!

    Интересный факт: в нижнем ряду числа 15 и 14 находятся посередине, представляя год, когда Дюрер создал этот квадрат. Цифры 1 и 4 также находятся в этом ряду, представляя первую и четвертую буквы алфавита, A и D, которые являются инициалами создателя квадрата, Альбрехта Дюрера!

    Введите в REPL следующее:

    In [1]: import numpy as np
    
    In [2]: square = np.array([
       ...:     [16, 3, 2, 13],
       ...:     [5, 10, 11, 8],
       ...:     [9, 6, 7, 12],
       ...:     [4, 15, 14, 1]
       ...: ])
    
    In [3]: for i in range(4):
       ...:     assert square[:, i].sum() == 34
       ...:     assert square[i, :].sum() == 34
       ...:
    
    In [4]: assert square[:2, :2].sum() == 34
    
    In [5]: assert square[2:, :2].sum() == 34
    
    In [6]: assert square[:2, 2:].sum() == 34
    
    In [7]: assert square[2:, 2:].sum() == 34
    

    Внутри цикла for вы проверяете, что сумма всех строк и всех столбцов равна 34. После этого, используя выборочную индексацию, вы проверяете, что каждый из квадрантов также составляет 34.

    И последнее, что следует отметить, это то, что вы можете взять сумму любого массива, чтобы сложить все его элементы глобально с помощью square.sum(). Этот метод также может принимать аргумент оси, чтобы вместо этого выполнять суммирование по осям.

    Маскирование и фильтрация

    Выбор на основе индекса — это хорошо, но что, если вы хотите отфильтровать данные на основе более сложных, неоднородных или непоследовательных критериев? Здесь в игру вступает концепция маски.

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

    Вот пример, показывающий процесс сначала в замедленной съемке, а затем, как это обычно делается, в одной строке:

    In [1]: import numpy as np
    
    In [2]: numbers = np.linspace(5, 50, 24, dtype=int).reshape(4, -1)
    
    In [3]: numbers
    Out[3]:
    array([ [ 5, 6,  8, 10, 12, 14],
           [16, 18, 20, 22, 24, 26],
           [28, 30, 32, 34, 36, 38],
           [40, 42, 44, 46, 48, 50] ])
    
    In [4]: mask = numbers % 4 == 0
    
    In [5]: mask
    Out[5]:
    array([ [False, False,  True, False,  True, False],
           [ True, False,  True, False,  True, False],
           [ True, False,  True, False,  True, False],
           [ True, False,  True, False,  True, False] ])
    
    In [6]: numbers[mask]
    Out[6]: array([ 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48])
    
    In [7]: by_four = numbers[numbers % 4 == 0]
    
    In [8]: by_four
    Out[8]: array([ 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48])
    

    Вы увидите объяснение новых приемов создания массива в In [2], а пока сосредоточимся на сути примера. Это важные части:

    • In [4] создает маску, выполняя векторизованное логическое вычисление, беря каждый элемент и проверяя, делится ли на четыре без остатка. Возвращается массив масок той же формы с поэлементными результатами вычислений.
    • In [6] использует эту маску для индексации исходного массива чисел. Это приводит к тому, что массив теряет свою исходную форму, уменьшая его до одного измерения, но вы по-прежнему получаете данные, которые ищете.
    • In [7] обеспечивает более традиционный, идиоматический замаскированный выбор, который вы можете увидеть в природе, с анонимным фильтрующим массивом, созданным встроенным в скобки выбора. Этот синтаксис аналогичен использованию в языке программирования R.

    Возвращаясь ко In [2], вы сталкиваетесь с тремя новыми концепциями:

    • Использование np.linspace() для создания равномерно распределенного массива
    • Установка dtype вывода
    • Изменение формы массива с -1

    np.linspace() генерирует n чисел, равномерно распределенных между минимумом и максимумом, что полезно для равномерно распределенной выборки при научном построении графиков.

    Благодаря конкретному вычислению в этом примере, наличие целых чисел в массиве чисел упрощает жизнь. Но поскольку пространство между 5 и 50 не делится равномерно на 24, результирующие числа будут числами с плавающей запятой. Вы указываете dtype типа int, чтобы функция округлялась в меньшую сторону и выдавала целые числа. Позже вы увидите более подробное обсуждение типов данных.

    Наконец, array.reshape() может принимать -1 в качестве одного из размеров своих измерений. Это означает, что NumPy должен просто выяснить, насколько большой должна быть эта конкретная ось, исходя из размера других осей. В этом случае с 24 значениями и размером 4 по оси 0 ось 1 заканчивается размером 6.

    Вот еще один пример, демонстрирующий возможности маскированной фильтрации.Нормальное распределение — это распределение вероятностей, в котором примерно 95,45% значений находятся в пределах двух стандартных отклонений от среднего.

    Вы можете проверить это с небольшой помощью модуля NumPy random для генерации случайных значений:

    In [1]: import numpy as np
    
    In [2]: from numpy.random import default_rng
    
    In [3]: rng = default_rng()
    
    In [4]: values = rng.standard_normal(10000)
    
    In [5]: values[:5]
    Out[5]: array([ .9779210858,  1.8361585253,  -.3641365235,
                   -.1311344527, 1.286542056 ])
    
    In [6]: std = values.std()
    
    In [7]: std
    Out[7]: .9940375551073492
    
    In [8]: filtered = values[(values > -2 * std) & (values < 2 * std)]
    
    In [9]: filtered.size
    Out[9]: 9565
    
    In [10]: values.size
    Out[10]: 10000
    
    In [11]: filtered.size / values.size
    Out[11]: 0.9565
    

    Здесь вы используете потенциально странно выглядящий синтаксис для объединения условий фильтрации: бинарный оператор &. Почему так? Это потому, что NumPy обозначает & и | как векторизованные поэлементные операторы для объединения логических значений. Если вы попытаетесь выполнить A и B, то получите предупреждение о том, насколько странно истинное значение для массива, потому что и работает с истинным значением всего массива, а не поэлементно.

    Транспонирование, сортировка и объединение

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

    Вот перенос массива:

    In [1]: import numpy as np
    
    In [2]: a = np.array([
       ...:     [1, 2],
       ...:     [3, 4],
       ...:     [5, 6],
       ...: ])
    
    In [3]: a.T
    Out[3]:
    array(1, 3, 5],
           [2, 4, 6)
    
    In [4]: a.transpose()
    Out[4]:
    array(1, 3, 5],
           [2, 4, 6)
    

    Когда вы вычисляете транспонирование массива, индексы строки и столбца каждого элемента меняются местами. Например, элемент [0, 2] становится элементом [2, 0]. Вы также можете использовать a.T как псевдоним для a.transpose().

    Следующий блок кода показывает сортировку, но вы также увидите более мощный метод сортировки в следующем разделе, посвященном структурированным данным:

    In [1]: import numpy as np
    
    In [2]: data = np.array([
       ...:     [7, 1, 4],
       ...:     [8, 6, 5],
       ...:     [1, 2, 3]
       ...: ])
    
    In [3]: np.sort(data)
    Out[3]:
    array([ [1, 4, 7],
           [5, 6, 8],
           [1, 2, 3] ])
    
    In [4]: np.sort(data, axis=None)
    Out[4]: array([1, 1, 2, 3, 4, 5, 6, 7, 8])
    
    In [5]: np.sort(data, axis=0)
    Out[5]:
    array([ [1, 1, 3],
           [7, 2, 4],
           [8, 6, 5] ])
    

    Отсутствие аргумента оси автоматически выбирает последнее и самое внутреннее измерение, которым в этом примере являются строки. Использование None сглаживает массив и выполняет глобальную сортировку. В противном случае вы можете указать, какую ось хотите. В Out[5] каждый столбец массива по-прежнему содержит все его элементы, но они отсортированы по возрастанию внутри этого столбца..

    Наконец, вот пример конкатенации. Хотя существует функция np.concatenate(), есть также ряд вспомогательных функций, которые иногда легче читать.

    Вот некоторые примеры:

    In [1]: import numpy as np
    
    In [2]: a = np.array([
       ...:     [4, 8],
       ...:     [6, 1]
       ...: ])
    
    In [3]: b = np.array([
       ...:     [3, 5],
       ...:     [7, 2],
       ...: ])
    
    In [4]: np.hstack((a, b))
    Out[4]:
    array([ [4, 8, 3, 5],
           [6, 1, 7, 2] ])
    
    In [5]: np.vstack((b, a))
    Out[5]:
    array([ [3, 5],
           [7, 2],
           [4, 8],
           [6, 1] ])
    
    In [6]: np.concatenate((a, b))
    Out[6]:
    array([ [4, 8],
           [6, 1],
           [3, 5],
           [7, 2] ])
    
    In [7]: np.concatenate((a, b), axis=None)
    Out[7]: array([4, 8, 6, 1, 3, 5, 7, 2])
    

    In [4] и In [5] показывают чуть более интуитивно понятные функции hstack() и vstack(). In [6] и In [7] показывают более общий метод concatenate(), сначала без аргумента оси, а затем с axis=None. Это поведение сглаживания похоже по форме на то, что вы только что видели с помощью sort(). Следует отметить один важный камень преткновения: все эти функции в качестве первого аргумента принимают кортеж массивов, а не переменное количество аргументов, как вы могли ожидать. Вы можете это сказать, потому что здесь есть лишняя пара круглых скобок.

    Агрегирование

    Ваша последняя остановка в этом туре по функциональности перед тем, как погрузиться в более сложные темы и примеры, - это агрегирование. Вы уже видели довольно много методов агрегирования, включая .sum(), .max (), .mean () и .std (). Вы можете ссылаться на более обширную библиотеку функций NumPy, чтобы увидеть больше. Многие математические, финансовые,а статистические функции используют агрегирование, чтобы помочь вам уменьшить количество измерений в ваших данных.

    Практический пример 1. Реализация серии Маклорена

    Пришло время увидеть реалистичный пример использования навыков, представленных в разделах выше: реализация уравнения.

    Одна из самых сложных вещей при преобразовании математических уравнений в код без NumPy заключается в том, что многие визуальные сходства отсутствуют, из-за чего трудно определить, на какую часть уравнения вы смотрите, читая код. Суммирования преобразуются в более подробные циклы for, а оптимизация пределов в конечном итоге выглядит как циклы while.

    Использование NumPy позволяет вам приблизиться к однозначному представлению от уравнения к коду.

    В следующем примере, например, закодируйте серию Маклорена для e^x. Ряды Маклорена - это способ приближения более сложных функций с помощью бесконечного ряда суммированных членов с центром около нуля.

    Например, ряд Маклорена представляет собой следующее суммирование:

    e^x = \sum_{n=0}^{\infty}{\dfrac{x^n}{n!}} = 1 + x + \dfrac{x^2}{2} + \dfrac{x^3}{3} \dots

    Вы складываете члены, начиная с нуля и теоретически до бесконечности. Каждый n-й член будет возведен в x до n и разделен на n!, что является обозначением факториальной операции.

    Теперь пришло время поместить это в код NumPy. Создайте файл с именем maclaurin.py:

    from math import e, factorial
    
    import numpy as np
    
    fac = np.vectorize(factorial)
    
    def e_x(x, terms=10):
        """Approximates e^x using a given number of terms of
        the Maclaurin series
        """
        n = np.arange(terms)
        return np.sum((x ** n) / fac(n))
    
    if __name__ == "__main__":
        print("Значение:", e ** 3)  # Using e from the standard library
    
        print("N (ряд)\t\tПриближение\tПогрешность")
    
        for n in range(1, 14):
            maclaurin = e_x(3, terms=n)
            print(f"{n}\t\t{maclaurin:.03f}\t\t{e**3 - maclaurin:.03f}")
    

    Когда вы запустите скрипт, то должны увидеть следующий результат:

    Значение: 20.085536923187664
    N (ряд)		Приближение	Погрешность
    1		1.000		19.086
    2		4.000		16.086
    3		8.500		11.586
    4		13.000		7.086
    5		16.375		3.711
    6		18.400		1.686
    7		19.412		0.673
    8		19.846		0.239
    9		20.009		0.076
    10		20.063		0.022
    11		20.080		0.006
    12		20.084		0.001
    13		20.085		0.000
    

    По мере увеличения количества членов ряда ваше значение Маклорена становится все ближе и ближе к фактическому значению, а ошибка становится все меньше и меньше.

    Вычисление каждого члена включает в себя взятие x в степени n и деление на n!, или факториал числа n. Сложение, суммирование и возведение в степень - это все операции, которые NumPy может векторизовать автоматически и быстро, но не для factorial().

    Чтобы использовать factorial() в векторизованных вычислениях, вы должны использовать np.vectorize() для создания векторизованной версии. Документация для np.vectorize() утверждает, что это не более чем тонкая оболочка, которая применяет цикл for к заданной функции. Использование этого кода вместо обычного кода Python не дает реальных преимуществ в производительности и потенциально может привести к некоторым штрафам за накладные расходы. Однако, как вы сейчас увидите, удобство чтения огромно. Как только ваш векторизованный факториал готов, фактический код для вычисления всего ряда Маклорена становится шокирующе коротким. Это также читабельно. Что наиболее важно, это почти точно соответствует тому, как выглядит математическое уравнение:

    n = np.arange(terms)
    return np.sum((x ** n) / fac(n))
    

    Это настолько важная идея, что ее следует повторить. За исключением дополнительной строки для инициализации n, код читается почти так же, как исходное математическое уравнение. Нет циклов for, временных переменных i, j, k. Просто математика.

    Точно так же вы используете NumPy для математического программирования! Для дополнительной практики попробуйте выбрать одну из других серий Maclaurin и реализовать ее аналогичным образом.

    Оптимизация хранилища: типы данных

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

    Однако в NumPy есть еще кое-что, что нужно осветить. NumPy использует код C под капотом для оптимизации производительности, и он не может этого сделать, если все элементы в массиве не относятся к одному типу. Это не означает только тот же тип Python. Они должны быть одного и того же базового типа C, с одинаковой формой и размером в битах!

    Числовые типы: int, bool, float и complex

    Поскольку большая часть вашей науки о данных и численных расчетов будет, как правило, включать числа, они кажутся лучшим местом для начала. По сути, в коде NumPy есть четыре числовых типа, и каждый может принимать несколько разных размеров.

    В таблице ниже приведены подробные сведения об этих типах:

    Название Кол-во бит Тип Python Тип NumPy
    Integer 64 int np.int_
    Booleans 8 bool np.bool_
    Float 64 float np.float_
    Complex 128 complex np.complex_

    Это просто типы, которые соответствуют существующим типам Python. В NumPy также есть типы для версий каждого меньшего размера, такие как 8-, 16- и 32-битные целые числа, 32-битные числа с плавающей запятой одинарной точности и 64-битные комплексные числа одинарной точности. В документации они перечислены полностью.

    Чтобы указать тип при создании массива, можно указать аргумент dtype:

    In [1]: import numpy as np
    
    In [2]: a = np.array([1, 3, 5.5, 7.7, 9.2], dtype=np.single)
    
    In [3]: a
    Out[3]: array([1. , 3. , 5.5, 7.7, 9.2], dtype=float32)
    
    In [4]: b = np.array([1, 3, 5.5, 7.7, 9.2], dtype=np.uint8)
    
    In [5]: b
    Out[5]: array([1, 3, 5, 7, 9], dtype=uint8)
    

    NumPy автоматически преобразует ваш независимый от платформы тип np.single в любой тип фиксированного размера, поддерживаемый вашей платформой для этого размера. В этом случае он использует np.float32. Если предоставленные вами значения не соответствуют форме указанного вами dtype, NumPy либо исправит это за вас, либо выдаст ошибку.

    Типы строк: Unicode с измененным размером

    Строки ведут себя немного странно в коде NumPy, потому что NumPy нужно знать, сколько байтов следует ожидать, что обычно не является фактором программирования на Python. К счастью, NumPy неплохо справляется с менее сложными случаями:

    In [1]: import numpy as np
    
    In [2]: names = np.array(["bob", "amy", "han"], dtype=str)
    
    In [3]: names
    Out[3]: array(['bob', 'amy', 'han'], dtype='<U')
    
    In [4]: names.itemsize
    Out[4]: 12
    
    In [5]: names = np.array( ["bob", "amy", "han"] )
    
    In [6]: names
    Out[6]: array( ['bob', 'amy', 'han'], dtype='<U' )
    
    In [7]: more_names = np.array( ["bobo", "jehosephat"] )
    
    In [8]: 
     

    NumPy - это библиотека Python, которая предоставляет простую, но мощную структуру данных: n-мерный массив. Это фундамент, на котором построена почти вся мощь инструментария Python для обработки данных, а изучение NumPy - это первый шаг на пути любого ученого-специалиста по Python.Это руководство предоставит вам знания, необходимые для использования NumPy и библиотек более высокого уровня, которые на него полагаются.

    В этом руководстве вы узнаете:
    • Какие основные концепции науки о данных стали возможны с помощью NumPy
    • Как создавать массивы NumPy разными методами
    • Как манипулировать массивами NumPy для выполнения полезных вычислений
    • Как применить эти новые навыки к реальным проблемам
    Чтобы получить максимальную отдачу от этого руководства по NumPy, вы должны быть знакомы с написанием кода Python. Прохождение курса «Введение в Python» - отличный способ убедиться, что у вас есть базовые навыки. Если вы знакомы с матричной математикой,тогда это тоже будет полезно. Однако вам не нужно ничего знать о науке о данных. Вы узнаете это здесь.

    Содержание

    Есть репозиторий примеров кода NumPy, которые вы увидите в этом руководстве. Вы можете использовать его для справки и поэкспериментировать с примерами, чтобы увидеть, как изменение кода меняет результат. Чтобы скачать код, щелкните ссылку ниже:

    Выбор NumPy: преимущества

    Поскольку вы уже знаете Python, вы можете спросить себя, действительно ли вам нужно изучать совершенно новую парадигму, чтобы заниматься наукой о данных. Циклы for в Python потрясающие! Чтение и запись файлов CSV можно выполнять с помощью традиционного кода. Однако есть несколько убедительных аргументов в пользу изучения новой парадигмы. Вот четыре основных преимущества, которые NumPy может дать вашему коду:
    1. Больше скорости: NumPy использует алгоритмы, написанные на C, которые выполняются за наносекунды, а не за секунды.
    2. Меньше циклов: NumPy помогает сократить количество циклов и не запутаться в индексах итераций.
    3. Более четкий код: без циклов,ваш код будет больше похож на уравнения, которые вы пытаетесь вычислить.
    4. Лучшее качество: тысячи участников работают над тем, чтобы NumPy оставался быстрым, дружелюбным и свободным от ошибок.
    Благодаря этим преимуществам NumPy является стандартом де-факто для многомерных массивов в науке о данных Python, и многие из самых популярных библиотек построены на его основе. Изучение NumPy - отличный способ заложить прочную основу, поскольку вы расширяете свои знания в более конкретные области науки о данных.

    Установка NumPy

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

    Использование Repl.it в качестве онлайн-редактора

    Если вы просто хотите начать с некоторых примеров, следуйте инструкциям по этому руководству и начните наращивать мышечную память с помощью NumPy, тогда Repl.it - ​​отличный вариант для редактирования в браузере. Вы можете зарегистрироваться и запустить среду Python за считанные минуты. Слева есть вкладка для пакетов. Вы можете добавить столько, сколько хотите.В этом руководстве по NumPy используйте текущие версии NumPy и Matplotlib.

    Установка NumPy с Anaconda

    Дистрибутив Anaconda - это набор стандартных инструментов Python для обработки данных, связанных с менеджером пакетов, который помогает управлять вашими виртуальными средами и зависимостями проекта. Он построен на основе conda, которая фактически является менеджером пакетов. Это метод, рекомендованный проектом NumPy,особенно если вы начинаете заниматься наукой о данных в Python, еще не настроив сложную среду разработки. Если у вас уже есть рабочий процесс, который вам нравится, в котором используются pip, Pipenv, Poetry или какой-либо другой набор инструментов, тогда, возможно, лучше не добавлять conda в микс. Репозиторий пакетов conda отделен от PyPI, а сама conda создает отдельный маленький островок пакетов на вашем компьютере, поэтому управление путями и запоминание того, какой пакет живет где, может быть кошмаром. После того, как вы установили conda, вы можете запустить команду установки необходимых вам библиотек:
    $ conda install numpy matplotlib
    

    Это установит все, что вам нужно для этого руководства по NumPy, и все будет готово.

    Установка NumPy с помощью pip

    Хотя проект NumPy рекомендует использовать conda, если вы начинаете с нуля, нет ничего плохого в том, чтобы самостоятельно управлять своей средой и просто использовать старый добрый pip, Pipenv, Poetry или любую другую альтернативу pip, которая вам больше всего нравится.

    Вот команды для настройки с помощью pip:

    $ mkdir numpy-tutorial
    $ cd numpy-tutorial
    $ python3 -m venv .numpy-tutorial-venv
    $ source .numpy-tutorial-venv/bin/activate
    
    (.numpy-tutorial-venv)
    $ pip install numpy matplotlib
    Collecting numpy
      Downloading numpy-1.19.1-cp38-cp38-macosx_10_9_x86_64.whl (15.3 MB)
         |████████████████████████████████| 15.3 MB 2.7 MB/s
    Collecting matplotlib
      Downloading matplotlib-3.3.0-1-cp38-cp38-macosx_10_9_x86_64.whl (11.4 MB)
         |████████████████████████████████| 11.4 MB 16.8 MB/s
    ...
    

    После этого убедитесь, что ваша виртуальная среда активирована, и весь ваш код должен работать должным образом.

    Использование IPython, Notebooks или JupyterLab

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

    IPython - это обновленный цикл чтения-оценки-печати Python (REPL), который делает редактирование кода в сеансе реального интерпретатора более простым и красивым. Вот как выглядит сеанс REPL IPython:

    
    In [2]: digits = np.array([
       ...:     [1, 2, 3],
       ...:     [4, 5, 6],
       ...:     [6, 7, 9],
       ...: ])
    
    In [3]: digits
    Out[3]:
    array([ [1, 2, 3],
           [4, 5, 6],
           [6, 7, 9] ])
    

    Он имеет несколько отличий от базового REPL Python, включая номера строк, использование цветов и качество визуализации массивов. Также есть много бонусов для пользовательского опыта, которые делают более приятным ввод, повторный ввод и редактирование кода.

    Вы можете установить IPython отдельно:

    $ pip install ipython
    

    В качестве альтернативы, если вы подождете и установите любой из последующих инструментов, они будут включать копию IPython.

    Чуть более функциональная альтернатива REPL - это ноутбук. Однако записные книжки - это немного другой стиль написания Python, чем стандартные сценарии. Вместо традиционного файла Python они предоставляют вам серию мини-файлов. Сценарии, называемые ячейками, которые вы можете запускать и повторно запускать в любом порядке, в том же сеансе Python.

    Одна из замечательных особенностей записных книжек заключается в том, что вы можете включать графики и отображать абзацы Markdown между ячейками, так что они действительно удобны для написания анализа данных прямо внутри кода!

    Вот как это выглядит:

    Самым популярным предложением ноутбуков, вероятно, является Jupyter Notebook, но nteract - это еще один вариант, который объединяет функциональность Jupyter и пытается сделать его более доступным и мощным.

    Однако, если вы смотрите на Jupyter Notebook и думаете, что ему нужно больше возможностей, подобных IDE, то другой вариант - JupyterLab. Вы можете настраивать текстовые редакторы, записные книжки, терминалы и пользовательские компоненты в интерфейсе на основе браузера. Вероятно, это будет удобнее для людей, пришедших из MatLab. Это самое молодое из предложений, но его версия 1.0 была выпущена еще в 2019 году, поэтому она должна быть стабильной и полнофункциональной.

    Вот как выглядит интерфейс:

    Какой бы вариант вы ни выбрали, после его установки вы будете готовы запустить свои первые строки кода NumPy. Пришло время для первого примера.

    Здравствуйте, NumPy: руководство по выставлению оценок за тест на изгиб

    В первом примере представлены несколько основных концепций NumPy, которые вы будете использовать в оставшейся части руководства:

    • Создание массивов с использованием numpy.array()
    • Обработка полных массивов как отдельных значений, чтобы сделать векторизованные вычисления более удобочитаемыми
    • Использование встроенных функций NumPy для изменения и агрегирования данных

    Эти концепции составляют основу эффективного использования NumPy.

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

    Однако это будет относительно элементарное исправление. Вы возьмете любой средний балл и объявите, что это C. Кроме того, вы убедитесь, что кривая случайно не повредит успеваемость ваших учеников и не поможет настолько, что ученик успеет лучше, чем 100%.

    Введите этот код в свой REPL:

    >>> import numpy as np
    >>> CURVE_CENTER = 80
    >>> grades = np.array([72, 35, 64, 88, 51, 90, 74, 12])
    >>> def curve(grades):
    ...     average = grades.mean()
    ...     change = CURVE_CENTER - average
    ...     new_grades = grades + change
    ...     return np.clip(new_grades, grades, 100)
    ...
    >>> curve(grades)
    array([ 91.25,  54.25,  83.25, 100.  ,  70.25, 100.  ,  93.25,  31.25])
    

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

    Вот важные моменты:

    • Строка 1 импортирует NumPy с использованием псевдонима np, что является общепринятым соглашением, позволяющим сэкономить несколько нажатий клавиш.
    • Строка 3 создает ваш первый массив NumPy, который является одномерным и имеет форму (8,) и тип данных int64. Пока не стоит особо беспокоиться об этих деталях. Вы изучите их более подробно позже в этом руководстве.
    • В строке 5 берется среднее значение всех оценок с использованием .mean(). У массивов много методов.

    В строке 7 вы сразу пользуетесь двумя важными концепциями:

    1. Векторизация
    2. Вещание или Broadcasting

    Векторизация - это процесс выполнения одной и той же операции одинаковым образом для каждого элемента в массиве. Это удаляет циклы for из вашего кода, но дает тот же результат.

    Broadcasting - это процесс расширения двух массивов разной формы и выяснения того, как выполнять векторизованные вычисления между ними. Помните, Grades - это массив чисел формы (8,), а изменение - это скаляр или одно число, по существу, имеющее форму (1,). В этом случае NumPy добавляет скаляр к каждому элементу в массиве и возвращает новый массив с результатами.

    Наконец, в строке 8 вы ограничиваете или обрезаете значения набором минимумов и максимумов. Помимо методов массива, NumPy также имеет большое количество встроенных функций. Не нужно их все запоминать - для этого и нужна документация. Каждый раз, когда вы застреваете или чувствуете, что должен быть более простой способ что-то сделать, загляните в документацию и посмотрите, нет ли уже процедуры, которая делает именно то, что вам нужно.

    В этом случае, вам нужна функция, которая принимает массив и следит за тем, чтобы значения не превышали заданный минимум или максимум. clip() делает именно это.

    Строка 8 также представляет собой еще один пример широковещательной передачи. Для второго аргумента clip() вы передаете оценки, гарантируя, что каждая новая оценка не будет ниже исходной. Но для третьего аргументавы передаете единственное значение: 100. NumPy принимает это значение и передает его каждому элементу в new_grades, гарантируя, что ни одна из недавно изогнутых оценок не превышает идеального результата.

    Переход к форме: формы и оси массивов

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

    Векторы, представляющие собой одномерные массивы чисел, легче всего отслеживать. Два измерения тоже не так уж и плохи, потому что они похожи на электронные таблицы. Но все начинает усложняться в трех измерениях, а визуализировать четыре? Забудь об этом.

    Освоение формы

    Форма - это ключевая концепция при использовании многомерных массивов. В какой-то момент легче забыть о визуализации формы ваших данных и вместо этого следовать некоторым мысленным правилам и доверять NumPy, чтобы сказать вам правильную форму.

    Все массивы имеют свойство с именем .shape, которое возвращает кортеж размера в каждом измерении. Менее важно, какое измерение является каким, но очень важно, чтобы массивы, которые вы передаете функциям, имели форму, которую ожидают функции. Обычный способ убедиться, что ваши данные имеют правильную форму, - это распечатать данные и их форму до тех пор, пока вы не убедитесь, что все работает так, как вы ожидаете.

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

    In [1]: import numpy as np
    
    In [2]: temperatures = np.array([
       ...:     29.3, 42.1, 18.8, 16.1, 38.0, 12.5,
       ...:     12.6, 49.9, 38.6, 31.3, 9.2, 22.2
       ...: ]).reshape(2, 2, 3)
    
    In [3]: temperatures.shape
    Out[3]: (2, 2, 3)
    
    In [4]: temperatures
    Out[4]:
    array([ [ [29.3, 42.1, 18.8],
            [16.1, 38. , 12.5] ],
    
           [ [12.6, 49.9, 38.6],
            [31.3,  9.2, 22.2] ] ])
    
    In [5]: np.swapaxes(temperatures, 1, 2)
    Out[5]:
    array([ [ [29.3, 16.1],
            [42.1, 38. ],
            [18.8, 12.5] ],
    
           [ [12.6, 31.3],
            [49.9,  9.2],
            [38.6, 22.2] ] ])
    

    Здесь вы используете метод numpy.ndarray с именем .reshape() для формирования блока данных размером 2 × 2 × 3. Когда вы проверяете форму вашего массива во входных данных 3, это именно то, что вы ему сказали. Однако вы можете видеть, как быстро становится трудно визуализировать напечатанные массивы в трех или более измерениях. После того, как вы поменяете оси с помощью .swapaxes (), становится немного яснее, какое измерение есть какое. Подробнее об осях вы узнаете в следующем разделе.

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

    Что такое топоры

    Приведенный выше пример показывает, насколько важно знать не только, в какой форме находятся ваши данные, но и какие данные находятся на какой оси. В массивах NumPy оси имеют нулевой индекс и определяют, какое измерение является каким. Например, двумерный массив имеет вертикальную ось (ось 0) и горизонтальную ось (ось 1). Многие функции и команды в NumPy изменяют свое поведение в зависимости от того, какую ось вы им приказываете обрабатывать.

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

    In [1]: import numpy as np
    
    In [2]: table = np.array([
       ...:     [5, 3, 7, 1],
       ...:     [2, 6, 7 ,9],
       ...:     [1, 1, 1, 1],
       ...:     [4, 3, 2, 0],
       ...: ])
    
    In [3]: table.max()
    Out[3]: 9
    
    In [4]: table.max(axis=0)
    Out[4]: array([5, 6, 7, 9])
    
    In [5]: table.max(axis=1)
    Out[5]: array([7, 9, 1, 4])
    

    По умолчанию .max() возвращает наибольшее значение во всем массиве, независимо от количества измерений. Однако, как только вы укажете ось, он выполняет это вычисление для каждого набора значений вдоль этой конкретной оси. Например, с аргументом axis=0, .max() выбирает максимальное значение в каждом из четырех вертикальных наборов значений в таблице и возвращает массив, который был сведен или агрегирован в одномерный массив.

    Фактически, многие функции NumPy ведут себя следующим образом: если ось не указана, они выполняют операцию со всем набором данных. Иначе, они выполняют операцию по оси.

    Broadcasting

    До сих пор вы видели несколько небольших примеров Broadcasting, но чем больше примеров вы увидите, тем яснее станет эта тема. По сути, он работает вокруг одного правила: массивы могут транслироваться друг против друга, если их размеры совпадают или если один из массивов имеет размер 1.

    Если массивы совпадают по размеру вдоль оси,тогда элементы будут обрабатываться поэлементно, подобно тому, как работает встроенная функция Python zip(). Если один из массивов имеет размер 1 по оси, то это значение будет транслироваться по этой оси или дублироваться столько раз, сколько необходимо, чтобы соответствовать количеству элементов вдоль этой оси в другом массиве.

    Вот небольшой пример. Массив A имеет форму (4, 1, 8), а массив B имеет форму (1, 6, 8). Основываясь на приведенных выше правилах, вы можете работать с этими массивами вместе:

    • На оси 0 у A есть 4, а у B - 1, поэтому B может транслироваться по этой оси.
    • На оси 1 у A есть 1, а у B - 6, поэтому A может транслироваться по этой оси.
    • На оси 2 два массива имеют совпадающие размеры,чтобы они могли успешно работать.

    Все три оси успешно следуют правилу.

    Вы можете настроить массивы следующим образом:

    In [1]: import numpy as np
    
    In [2]: A = np.arange(32).reshape(4, 1, 8)
    
    In [3]: A
    Out[3]:
    array([ [ [ 0,  1,  2,  3,  4,  5,  6,  7] ],
    
           [ [ 8,  9, 10, 11, 12, 13, 14, 15] ],
    
           [ [16, 17, 18, 19, 20, 21, 22, 23] ],
    
           [ [24, 25, 26, 27, 28, 29, 30, 31] ] ])
    
    In [4]: B = np.arange(48).reshape(1, 6, 8)
    
    In [5]: B
    Out[5]:
    array([ [ [ 0,  1,  2,  3,  4,  5,  6,  7],
            [ 8,  9, 10, 11, 12, 13, 14, 15],
            [16, 17, 18, 19, 20, 21, 22, 23],
            [24, 25, 26, 27, 28, 29, 30, 31],
            [32, 33, 34, 35, 36, 37, 38, 39],
            [40, 41, 42, 43, 44, 45, 46, 47] ] ])
    

    У A 4 плоскости, каждая с 1 строкой и 8 столбцами. B имеет только 1 плоскость с 6 рядами и 8 столбцами. Посмотрите, что NumPy делает для вас, когда вы пытаетесь произвести расчет между ними!

    Сложите два массива вместе:

    In [7]: A + B
    Out[7]:
    array([ [ [ 0,  2,  4,  6,  8, 10, 12, 14],
            [ 8, 10, 12, 14, 16, 18, 20, 22],
            [16, 18, 20, 22, 24, 26, 28, 30],
            [24, 26, 28, 30, 32, 34, 36, 38],
            [32, 34, 36, 38, 40, 42, 44, 46],
            [40, 42, 44, 46, 48, 50, 52, 54] ],
    
           [ [ 8, 10, 12, 14, 16, 18, 20, 22],
            [16, 18, 20, 22, 24, 26, 28, 30],
            [24, 26, 28, 30, 32, 34, 36, 38],
            [32, 34, 36, 38, 40, 42, 44, 46],
            [40, 42, 44, 46, 48, 50, 52, 54],
            [48, 50, 52, 54, 56, 58, 60, 62] ],
    
           [ [16, 18, 20, 22, 24, 26, 28, 30],
            [24, 26, 28, 30, 32, 34, 36, 38],
            [32, 34, 36, 38, 40, 42, 44, 46],
            [40, 42, 44, 46, 48, 50, 52, 54],
            [48, 50, 52, 54, 56, 58, 60, 62],
            [56, 58, 60, 62, 64, 66, 68, 70] ],
    
           [ [24, 26, 28, 30, 32, 34, 36, 38],
            [32, 34, 36, 38, 40, 42, 44, 46],
            [40, 42, 44, 46, 48, 50, 52, 54],
            [48, 50, 52, 54, 56, 58, 60, 62],
            [56, 58, 60, 62, 64, 66, 68, 70],
            [64, 66, 68, 70, 72, 74, 76, 78] ] ])
    

    Принцип работы broadcasting заключается в том, что NumPy дублирует плоскость в B три раза, так что у вас всего четыре, что соответствует количеству плоскостей в A. Он также дублирует одну строку в A пять раз, всего шесть, что соответствует числу. строк в B. Затем он добавляет каждый элемент во вновь развернутом массиве A к его аналогу в том же месте в B. Результат каждого вычисления отображается в соответствующем месте вывода.

    Примечание. Это хороший способ создать массив из диапазона с помощью arange()!

    Еще раз, даже если вы можете использовать такие слова, как «плоскость», «строка» и «столбец», чтобы описать, как фигуры в этом примере транслируются для создания совпадающих трехмерных фигур, все становится сложнее в более высоких измерениях. Часто вам придется просто соблюдать правила вещания и много печатать, чтобы убедиться, что все работает по плану.

    Понимание broadcasting - важная часть освоения векторизованных вычислений, а векторизованные вычисления - это способ написать чистый идиоматический код NumPy.

    Операции обработки данных: фильтр, порядок, агрегирование

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

    Индексирование

    Индексирование использует многие из тех идиом, которые использует обычный код Python. Вы можете использовать положительные или отрицательные индексы для индексации спереди или сзади массива. Вы можете использовать двоеточие (:), чтобы указать «остальное» или «все», и вы даже можете использовать два двоеточия для пропуска элементов, как в обычных списках Python.

    В чем разница: в массивах NumPy между осями используются запятые, поэтому вы можете индексировать несколько осей в одном наборе квадратных скобок. Пример - самый простой способ показать это. Пора подтвердить магический квадрат Дюрера!

    Числовой квадрат ниже обладает некоторыми удивительными свойствами. Если сложить строки, столбцы или диагонали, получится то же число - 34. То же самое вы получите, если сложите каждый из четырех квадрантов, четыре центральных квадрата, четыре угловых квадрата или четыре угловых квадрата любой из имеющихся сеток 3 × 3. Вы собираетесь это доказать!

    Интересный факт: в нижнем ряду числа 15 и 14 находятся посередине, представляя год, когда Дюрер создал этот квадрат. Цифры 1 и 4 также находятся в этом ряду, представляя первую и четвертую буквы алфавита, A и D, которые являются инициалами создателя квадрата, Альбрехта Дюрера!

    Введите в REPL следующее:

    In [1]: import numpy as np
    
    In [2]: square = np.array([
       ...:     [16, 3, 2, 13],
       ...:     [5, 10, 11, 8],
       ...:     [9, 6, 7, 12],
       ...:     [4, 15, 14, 1]
       ...: ])
    
    In [3]: for i in range(4):
       ...:     assert square[:, i].sum() == 34
       ...:     assert square[i, :].sum() == 34
       ...:
    
    In [4]: assert square[:2, :2].sum() == 34
    
    In [5]: assert square[2:, :2].sum() == 34
    
    In [6]: assert square[:2, 2:].sum() == 34
    
    In [7]: assert square[2:, 2:].sum() == 34
    

    Внутри цикла for вы проверяете, что сумма всех строк и всех столбцов равна 34. После этого, используя выборочную индексацию, вы проверяете, что каждый из квадрантов также составляет 34.

    И последнее, что следует отметить, это то, что вы можете взять сумму любого массива, чтобы сложить все его элементы глобально с помощью square.sum(). Этот метод также может принимать аргумент оси, чтобы вместо этого выполнять суммирование по осям.

    Маскирование и фильтрация

    Выбор на основе индекса - это хорошо, но что, если вы хотите отфильтровать данные на основе более сложных, неоднородных или непоследовательных критериев? Здесь в игру вступает концепция маски.

    Маска - это массив, который имеет ту же форму, что и ваши данные, но вместо ваших значений он содержит логические значения: True или False. Вы можете использовать этот массив масок для индексации в массиве данных нелинейными и сложными способами. Он вернет все элементы, в которых логический массив имеет значение True.

    Вот пример, показывающий процесс сначала в замедленной съемке, а затем, как это обычно делается, в одной строке:

    In [1]: import numpy as np
    
    In [2]: numbers = np.linspace(5, 50, 24, dtype=int).reshape(4, -1)
    
    In [3]: numbers
    Out[3]:
    array([ [ 5, 6,  8, 10, 12, 14],
           [16, 18, 20, 22, 24, 26],
           [28, 30, 32, 34, 36, 38],
           [40, 42, 44, 46, 48, 50] ])
    
    In [4]: mask = numbers % 4 == 0
    
    In [5]: mask
    Out[5]:
    array([ [False, False,  True, False,  True, False],
           [ True, False,  True, False,  True, False],
           [ True, False,  True, False,  True, False],
           [ True, False,  True, False,  True, False] ])
    
    In [6]: numbers[mask]
    Out[6]: array([ 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48])
    
    In [7]: by_four = numbers[numbers % 4 == 0]
    
    In [8]: by_four
    Out[8]: array([ 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48])
    

    Вы увидите объяснение новых приемов создания массива в In [2], а пока сосредоточимся на сути примера. Это важные части:

    • In [4] создает маску, выполняя векторизованное логическое вычисление, беря каждый элемент и проверяя, делится ли на четыре без остатка. Возвращается массив масок той же формы с поэлементными результатами вычислений.
    • In [6] использует эту маску для индексации исходного массива чисел. Это приводит к тому, что массив теряет свою исходную форму, уменьшая его до одного измерения, но вы по-прежнему получаете данные, которые ищете.
    • In [7] обеспечивает более традиционный, идиоматический замаскированный выбор, который вы можете увидеть в природе, с анонимным фильтрующим массивом, созданным встроенным в скобки выбора. Этот синтаксис аналогичен использованию в языке программирования R.

    Возвращаясь ко In [2], вы сталкиваетесь с тремя новыми концепциями:

    • Использование np.linspace() для создания равномерно распределенного массива
    • Установка dtype вывода
    • Изменение формы массива с -1

    np.linspace() генерирует n чисел, равномерно распределенных между минимумом и максимумом, что полезно для равномерно распределенной выборки при научном построении графиков.

    Благодаря конкретному вычислению в этом примере, наличие целых чисел в массиве чисел упрощает жизнь. Но поскольку пространство между 5 и 50 не делится равномерно на 24, результирующие числа будут числами с плавающей запятой. Вы указываете dtype типа int, чтобы функция округлялась в меньшую сторону и выдавала целые числа. Позже вы увидите более подробное обсуждение типов данных.

    Наконец, array.reshape() может принимать -1 в качестве одного из размеров своих измерений. Это означает, что NumPy должен просто выяснить, насколько большой должна быть эта конкретная ось, исходя из размера других осей. В этом случае с 24 значениями и размером 4 по оси 0 ось 1 заканчивается размером 6.

    Вот еще один пример, демонстрирующий возможности маскированной фильтрации.Нормальное распределение - это распределение вероятностей, в котором примерно 95,45% значений находятся в пределах двух стандартных отклонений от среднего.

    Вы можете проверить это с небольшой помощью модуля NumPy random для генерации случайных значений:

    In [1]: import numpy as np
    
    In [2]: from numpy.random import default_rng
    
    In [3]: rng = default_rng()
    
    In [4]: values = rng.standard_normal(10000)
    
    In [5]: values[:5]
    Out[5]: array([ .9779210858,  1.8361585253,  -.3641365235,
                   -.1311344527, 1.286542056 ])
    
    In [6]: std = values.std()
    
    In [7]: std
    Out[7]: .9940375551073492
    
    In [8]: filtered = values[(values > -2 * std) & (values < 2 * std)]
    
    In [9]: filtered.size
    Out[9]: 9565
    
    In [10]: values.size
    Out[10]: 10000
    
    In [11]: filtered.size / values.size
    Out[11]: 0.9565
    

    Здесь вы используете потенциально странно выглядящий синтаксис для объединения условий фильтрации: бинарный оператор &. Почему так? Это потому, что NumPy обозначает & и | как векторизованные поэлементные операторы для объединения логических значений. Если вы попытаетесь выполнить A и B, то получите предупреждение о том, насколько странно истинное значение для массива, потому что и работает с истинным значением всего массива, а не поэлементно.

    In [1]: import numpy as np In [2]: a = np.array([ ...: [1, 2], ...: [3, 4], ...: [5, 6], ...: ]) In [3]: a.T Out[3]: array(1, 3, 5], [2, 4, 6) In [4]: a.transpose() Out[4]: array(1, 3, 5], [2, 4, 6)

    Когда вы вычисляете транспонирование массива, индексы строки и столбца каждого элемента меняются местами. Например, элемент [0, 2] становится элементом [2, 0]. Вы также можете использовать a.T как псевдоним для a.transpose().

    Следующий блок кода показывает сортировку, но вы также увидите более мощный метод сортировки в следующем разделе, посвященном структурированным данным:

    In [1]: import numpy as np
    
    In [2]: data = np.array([
       ...:     [7, 1, 4],
       ...:     [8, 6, 5],
       ...:     [1, 2, 3]
       ...: ])
    
    In [3]: np.sort(data)
    Out[3]:
    array([ [1, 4, 7],
           [5, 6, 8],
           [1, 2, 3] ])
    
    In [4]: np.sort(data, axis=None)
    Out[4]: array([1, 1, 2, 3, 4, 5, 6, 7, 8])
    
    In [5]: np.sort(data, axis=0)
    Out[5]:
    array([ [1, 1, 3],
           [7, 2, 4],
           [8, 6, 5] ])
    

    Отсутствие аргумента оси автоматически выбирает последнее и самое внутреннее измерение, которым в этом примере являются строки. Использование None сглаживает массив и выполняет глобальную сортировку. В противном случае вы можете указать, какую ось хотите. В Out[5] каждый столбец массива по-прежнему содержит все его элементы, но они отсортированы по возрастанию внутри этого столбца..

    Наконец, вот пример конкатенации. Хотя существует функция np.concatenate(), есть также ряд вспомогательных функций, которые иногда легче читать.

    Вот некоторые примеры:

    In [1]: import numpy as np
    
    In [2]: a = np.array([
       ...:     [4, 8],
       ...:     [6, 1]
       ...: ])
    
    In [3]: b = np.array([
       ...:     [3, 5],
       ...:     [7, 2],
       ...: ])
    
    In [4]: np.hstack((a, b))
    Out[4]:
    array([ [4, 8, 3, 5],
           [6, 1, 7, 2] ])
    
    In [5]: np.vstack((b, a))
    Out[5]:
    array([ [3, 5],
           [7, 2],
           [4, 8],
           [6, 1] ])
    
    In [6]: np.concatenate((a, b))
    Out[6]:
    array([ [4, 8],
           [6, 1],
           [3, 5],
           [7, 2] ])
    
    In [7]: np.concatenate((a, b), axis=None)
    Out[7]: array([4, 8, 6, 1, 3, 5, 7, 2])
    

    In [4] и In [5] показывают чуть более интуитивно понятные функции hstack() и vstack(). In [6] и In [7] показывают более общий метод concatenate(), сначала без аргумента оси, а затем с axis=None. Это поведение сглаживания похоже по форме на то, что вы только что видели с помощью sort(). Следует отметить один важный камень преткновения: все эти функции в качестве первого аргумента принимают кортеж массивов, а не переменное количество аргументов, как вы могли ожидать. Вы можете это сказать, потому что здесь есть лишняя пара круглых скобок.

    Агрегирование

    Ваша последняя остановка в этом туре по функциональности перед тем, как погрузиться в более сложные темы и примеры, - это агрегирование. Вы уже видели довольно много методов агрегирования, включая .sum(), .max (), .mean () и .std (). Вы можете ссылаться на более обширную библиотеку функций NumPy, чтобы увидеть больше. Многие математические, финансовые,а статистические функции используют агрегирование, чтобы помочь вам уменьшить количество измерений в ваших данных.

    Практический пример 1. Реализация серии Маклорена

    Пришло время увидеть реалистичный пример использования навыков, представленных в разделах выше: реализация уравнения.

    Одна из самых сложных вещей при преобразовании математических уравнений в код без NumPy заключается в том, что многие визуальные сходства отсутствуют, из-за чего трудно определить, на какую часть уравнения вы смотрите, читая код. Суммирования преобразуются в более подробные циклы for, а оптимизация пределов в конечном итоге выглядит как циклы while.

    Использование NumPy позволяет вам приблизиться к однозначному представлению от уравнения к коду.

    В следующем примере, например, закодируйте серию Маклорена для e^x. Ряды Маклорена - это способ приближения более сложных функций с помощью бесконечного ряда суммированных членов с центром около нуля.

    Например, ряд Маклорена представляет собой следующее суммирование:

    e^x = \sum_{n=0}^{\infty}{\dfrac{x^n}{n!}} = 1 + x + \dfrac{x^2}{2} + \dfrac{x^3}{3} \dots

    Вы складываете члены, начиная с нуля и теоретически до бесконечности. Каждый n-й член будет возведен в x до n и разделен на n!, что является обозначением факториальной операции.

    Теперь пришло время поместить это в код NumPy. Создайте файл с именем maclaurin.py:

    from math import e, factorial
    
    import numpy as np
    
    fac = np.vectorize(factorial)
    
    def e_x(x, terms=10):
        """Approximates e^x using a given number of terms of
        the Maclaurin series
        """
        n = np.arange(terms)
        return np.sum((x ** n) / fac(n))
    
    if __name__ == "__main__":
        print("Значение:", e ** 3)  # Using e from the standard library
    
        print("N (ряд)\t\tПриближение\tПогрешность")
    
        for n in range(1, 14):
            maclaurin = e_x(3, terms=n)
            print(f"{n}\t\t{maclaurin:.03f}\t\t{e**3 - maclaurin:.03f}")
    

    Когда вы запустите скрипт, то должны увидеть следующий результат:

    Значение: 20.085536923187664
    N (ряд)		Приближение	Погрешность
    1		1.000		19.086
    2		4.000		16.086
    3		8.500		11.586
    4		13.000		7.086
    5		16.375		3.711
    6		18.400		1.686
    7		19.412		0.673
    8		19.846		0.239
    9		20.009		0.076
    10		20.063		0.022
    11		20.080		0.006
    12		20.084		0.001
    13		20.085		0.000
    

    По мере увеличения количества членов ряда ваше значение Маклорена становится все ближе и ближе к фактическому значению, а ошибка становится все меньше и меньше.

    Вычисление каждого члена включает в себя взятие x в степени n и деление на n!, или факториал числа n. Сложение, суммирование и возведение в степень - это все операции, которые NumPy может векторизовать автоматически и быстро, но не для factorial().

    Чтобы использовать factorial() в векторизованных вычислениях, вы должны использовать np.vectorize() для создания векторизованной версии. Документация для np.vectorize() утверждает, что это не более чем тонкая оболочка, которая применяет цикл for к заданной функции. Использование этого кода вместо обычного кода Python не дает реальных преимуществ в производительности и потенциально может привести к некоторым штрафам за накладные расходы. Однако, как вы сейчас увидите, удобство чтения огромно. Как только ваш векторизованный факториал готов, фактический код для вычисления всего ряда Маклорена становится шокирующе коротким. Это также читабельно. Что наиболее важно, это почти точно соответствует тому, как выглядит математическое уравнение:

    n = np.arange(terms)
    return np.sum((x ** n) / fac(n))
    

    Это настолько важная идея, что ее следует повторить. За исключением дополнительной строки для инициализации n, код читается почти так же, как исходное математическое уравнение. Нет циклов for, временных переменных i, j, k. Просто математика.

    Точно так же вы используете NumPy для математического программирования! Для дополнительной практики попробуйте выбрать одну из других серий Maclaurin и реализовать ее аналогичным образом.

    Оптимизация хранилища: типы данных

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

    Однако в NumPy есть еще кое-что, что нужно осветить. NumPy использует код C под капотом для оптимизации производительности, и он не может этого сделать, если все элементы в массиве не относятся к одному типу. Это не означает только тот же тип Python. Они должны быть одного и того же базового типа C, с одинаковой формой и размером в битах!

    Числовые типы: int, bool, float и complex

    Поскольку большая часть вашей науки о данных и численных расчетов будет, как правило, включать числа, они кажутся лучшим местом для начала. По сути, в коде NumPy есть четыре числовых типа, и каждый может принимать несколько разных размеров.

    В таблице ниже приведены подробные сведения об этих типах:

    Название Кол-во бит Тип Python Тип NumPy
    Integer 64 int np.int_
    Booleans 8 bool np.bool_
    Float 64 float np.float_
    Complex 128 complex np.complex_

    Это просто типы, которые соответствуют существующим типам Python. В NumPy также есть типы для версий каждого меньшего размера, такие как 8-, 16- и 32-битные целые числа, 32-битные числа с плавающей запятой одинарной точности и 64-битные комплексные числа одинарной точности. В документации они перечислены полностью.

    Чтобы указать тип при создании массива, можно указать аргумент dtype:

    In [1]: import numpy as np
    
    In [2]: a = np.array([1, 3, 5.5, 7.7, 9.2], dtype=np.single)
    
    In [3]: a
    Out[3]: array([1. , 3. , 5.5, 7.7, 9.2], dtype=float32)
    
    In [4]: b = np.array([1, 3, 5.5, 7.7, 9.2], dtype=np.uint8)
    
    In [5]: b
    Out[5]: array([1, 3, 5, 7, 9], dtype=uint8)
    

    NumPy автоматически преобразует ваш независимый от платформы тип np.single в любой тип фиксированного размера, поддерживаемый вашей платформой для этого размера. В этом случае он использует np.float32. Если предоставленные вами значения не соответствуют форме указанного вами dtype, NumPy либо исправит это за вас, либо выдаст ошибку.

    Типы строк: Unicode с измененным размером

    Строки ведут себя немного странно в коде NumPy, потому что NumPy нужно знать, сколько байтов следует ожидать, что обычно не является фактором программирования на Python. К счастью, NumPy неплохо справляется с менее сложными случаями:

    >>> import numpy as np
    >>> names = np.array(["bob", "amy", "han"], dtype=str)
    
    >>> names
    array(['bob', 'amy', 'han'], dtype='<U')
    
    >>> names.itemsize
    12
    
    >>> names = np.array( ["bob", "amy", "han"] )
    
    >>> names
    array(['bob', 'amy', 'han'], dtype='<U')
    
    >>> more_names = np.array( ["bobo", "jehosephat"] )
    >>> np.concatenate((names, more_names))
    array(['bob', 'amy', 'han', 'bobo', 'jehosephat'], dtype='< U10')
    

    В строке 2 вы предоставляете dtype встроенного в Python типа str, но в Out[3] он был преобразован в строку Unicode с прямым порядком байтов размером 3. Когда вы проверяете размер данного элемента в In [4], то видите что каждый из них 12 байтов: три 4-байтовых символа Unicode.

    Примечание. Имея дело с типами данных NumPy, вы должны думать о таких вещах, как порядок байтов ваших значений. В этом случае dtype '<U' означает, что каждое значение имеет размер трех символов Unicode, причем младший байт хранится первым в памяти, а старший значащий байт сохраняется последним. Тип dtype '>U3' будет означать обратное.

    Например, NumPy представляет символ Unicode "🐍" с байтами 0xF4 0x01 0x00 с dtype ">U1" и 0x00 0x01 0xF4 с dtype ">U1". Попробуйте это, создав массив, полный эмодзи, установив для dtype одно или другое значение, а затем вызвав .tobytes() в своем массиве!

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

    Когда вы объединяете это с массивом, который имеет больший элемент, чтобы создать новый массив в In [8], NumPy помогает выяснить, насколько большими должны быть элементы нового массива, и увеличивает их все до размера <U10.

    Но вот что происходит, когда вы пытаетесь изменить один из слотов со значением, превышающим емкость dtype:

    >>>names[2] = "jamima"
    
    >>> names
    array(['bob', 'amy', 'jam'], dtype='<U3')
    

    Он не работает должным образом и вместо этого усекает ваше значение. Если у вас уже есть массив, автоматическое определение размера NumPy вам не подойдет. У вас есть три персонажа и все. Остальные теряются в пустоте.

    Это все, чтобы сказать, что в целом NumPy помогает вам, когда вы работаете со строками, но всегда должно следить за размером ваших элементов и следить за тем, чтобы у вас было достаточно места при модификации или изменении массивов на месте.

    Структурированные массивы

    Первоначально вы узнали, что все элементы массива должны иметь один и тот же тип данных, но это было не совсем правильно. NumPy имеет особый вид массива, называемый массивом записей или структурированным массивом, с помощью которого вы можете указать тип и, при желании, имя для каждого столбца. Это делает сортировку и фильтрацию еще более мощной, что может быть похоже на работу с данными в Excel, CSV или реляционных базах данных.

    Вот небольшой пример, чтобы немного их продемонстрировать:

    >>> import numpy as np
    
    >>> data = np.array([
       ...:     ("joe", 32, 6),
       ...:     ("mary", 15, 20),
       ...:     ("felipe", 80, 100),
       ...:     ("beyonce", 38, 9001),
       ...: ], dtype=[("name", str, 10), ("age", int), ("power", int)])
    
    >>> data[0]
    ('joe', 32, 6)
    
    >>> data["name"]
    array(['joe', 'mary', 'felipe', 'beyonce'], dtype='<U10')
    
    >>> data[data["power"] > 9000]["name"]
    array(['beyonce'], dtype='<U10')
    

    В In [2] вы создаете массив, за исключением того, что каждый элемент представляет собой кортеж с именем, возрастом и уровнем мощности. Для dtype вы фактически предоставляете список кортежей с информацией о каждом поле: name - это 10-символьное поле Unicode, а age и power - стандартные 4-байтовые или 8-байтовые целые числа.

    В In [3] вы можете видеть, что строки, известные как записи, по-прежнему доступны с помощью index.

    В In [4] вы видите новый синтаксис для доступа ко всему столбцу или полю.

    Наконец, В In [5] вы видите сверхмощную комбинацию фильтрации на основе маски на основе поля и выбора на основе поля. Обратите внимание на то, что чтение следующего запроса SQL не сильно отличается:

    SELECT name FROM data
    WHERE power > 9000;
    

    В обоих случаях результатом является список имен с уровнем мощности более 9000.

    Вы даже можете добавить функциональность ORDER BY, используя np.sort():

    In [6]: np.sort(data[data["age"] > 20], order="power")["name"]
    Out[6]: array(['joe', 'felipe', 'beyonce'], dtype='<U10')
    

    Перед извлечением данные сортируются по значению, и таким образом мы завершаем разговор о выборе инструментов NumPy для выбора, фильтрации и сортировки элементов, в том числе и с помощью SQL!

    Подробнее о типах данных

    Этот раздел руководства был разработан, чтобы дать вам достаточно знаний, чтобы продуктивно работать с типами данных NumPy, немного понять, как все работает под капотом, и распознать некоторые распространенные ошибки. Это, конечно, не исчерпывающее руководство. В документации NumPy на ndarrays гораздо больше ресурсов. Также есть намного больше информации об объектах dtype, в том числе о различных способах их создания, настройки и оптимизации, а также о том, как сделать их более надежными для всех ваших потребностей в обработке данных. Если у вас возникнут проблемы и ваши данные загружаются в массивы не так, как вы ожидали, то это хорошее место для начала.

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

    Взгляд в будущее: более мощные библиотеки

    В следующем разделе вы перейдете к мощным инструментам, созданным на основе фундаментальных строительных блоков, которые вы видели выше. Вот несколько библиотек, на которые вы захотите взглянуть в качестве следующих шагов на пути к полному овладению наукой о данных Python.

    pandas

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

    В документации pandas есть краткое руководство с конкретными примерами под названием 10 Minutes to pandas. Это отличный ресурс, который можно использовать для быстрой практической практики.

    scikit-learn

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

    Если вы уже знакомы с математикой, то в документации scikit-learn есть отличный список руководств, которые помогут вам начать работу с Python.

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

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

    Matplotlib

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

    Практический пример 2: манипулирование изображениями с помощью Matplotlib

    Всегда удобно, когда вы работаете с библиотекой Python, и она дает вам что-то, что оказывается базовым массивом NumPy. В этом примере вы испытаете это во всей красе.

    Вы собираетесь загрузить изображение с помощью Matplotlib, поймете, что изображения RGB на самом деле представляют собой массивы целых чисел int8 шириной × высотой × 3, манипулируйте этими байтами, и снова используйте Matplotlib, чтобы сохранить измененное изображение, когда закончите.

    Загрузите эту картинку для своих упражнений:

    
    
    
    
    
    
    
    

    Заключение

    Независимо от того, в скольких измерениях находятся ваши данные, NumPy дает вам инструменты для работы с ними. Вы можете хранить его, изменять его форму, комбинировать, фильтровать и сортировать, и ваш код будет выглядеть так, как будто вы работаете только с одним числом за раз, а не с сотнями или тысячами.

    В этом уроке вы узнали:

    • Основные концепции науки о данных, которые стали возможными благодаря NumPy
    • Как создавать массивы NumPy разными методами
    • Как манипулировать массивами NumPy для выполнения полезных вычислений
    • Как применить эти новые навыки к реальным проблемам

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

    Теперь вы готовы к следующим шагам на пути к науке о данных. Независимо от того, очищаете ли вы данные, обучаете нейронные сети, общаетесь с помощью мощных графиков или собираете данные из Интернета вещей, все эти действия начинаются с одного места: скромного массива NumPy.

    
    

    NumPy Tutorial: Your First Steps Into Data Science in Python
    Out[8]: array(['bob', 'amy', 'han', 'bobo', 'jehosephat'], dtype='<U10')

    В In [2] вы предоставляете dtype встроенного в Python типа str, но в Out[3] он был преобразован в строку Unicode с прямым порядком байтов размером 3. Когда вы проверяете размер данного элемента в In [4], то видите что каждый из них 12 байтов: три 4-байтовых символа Unicode.

    Примечание. Имея дело с типами данных NumPy, вы должны думать о таких вещах, как порядок байтов ваших значений. В этом случае dtype '<U' означает, что каждое значение имеет размер трех символов Unicode, причем младший байт хранится первым в памяти, а старший значащий байт сохраняется последним. Тип dtype '>U3' будет означать обратное.

    Например, NumPy представляет символ Unicode «🐍» с байтами 0xF4 0x01 0x00 с dtype «<U1» и 0x00 0x01 0xF4 с dtype «>U1». Попробуйте это, создав массив, полный эмодзи, установив для dtype одно или другое значение, а затем вызвав .tobytes() в своем массиве!

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

    Когда вы объединяете это с массивом, который имеет больший элемент, чтобы создать новый массив в In [8], NumPy помогает выяснить, насколько большими должны быть элементы нового массива, и увеличивает их все до размера <U10.

    Но вот что происходит, когда вы пытаетесь изменить один из слотов со значением, превышающим емкость dtype:

    In [9]: names[2] = "jamima"
    
    In [10]: names
    Out[10]: array(['bob', 'amy', 'jam'], dtype='<U')
    

    Он не работает должным образом и вместо этого усекает ваше значение. Если у вас уже есть массив, автоматическое определение размера NumPy вам не подойдет. У вас есть три персонажа и все. Остальные теряются в пустоте.

    Это все, чтобы сказать, что в целом NumPy помогает вам, когда вы работаете со строками, но всегда должно следить за размером ваших элементов и следить за тем, чтобы у вас было достаточно места при модификации или изменении массивов на месте.

    Структурированные массивы

    Первоначально вы узнали, что все элементы массива должны иметь один и тот же тип данных, но это было не совсем правильно. NumPy имеет особый вид массива, называемый массивом записей или структурированным массивом, с помощью которого вы можете указать тип и, при желании, имя для каждого столбца. Это делает сортировку и фильтрацию еще более мощной, что может быть похоже на работу с данными в Excel, CSV или реляционных базах данных.

    Вот небольшой пример, чтобы немного их продемонстрировать:

    In [1]: import numpy as np
    
    In [2]: data = np.array([
       ...:     ("joe", 32, 6),
       ...:     ("mary", 15, 20),
       ...:     ("felipe", 80, 100),
       ...:     ("beyonce", 38, 9001),
       ...: ], dtype=[("name", str, 10), ("age", int), ("power", int)])
    
    In [3]: data[0]
    Out[3]: ('joe', 32, 6)
    
    In [4]: data["name"]
    Out[4]: array(['joe', 'mary', 'felipe', 'beyonce'], dtype='<U10')
    
    In [5]: data[data["power"] > 9000]["name"]
    Out[5]: array(['beyonce'], dtype='<U10')
    

    В In [2] вы создаете массив, за исключением того, что каждый элемент представляет собой кортеж с именем, возрастом и уровнем мощности. Для dtype вы фактически предоставляете список кортежей с информацией о каждом поле: name - это 10-символьное поле Unicode, а age и power - стандартные 4-байтовые или 8-байтовые целые числа.

    В In [3] вы можете видеть, что строки, известные как записи, по-прежнему доступны с помощью index.

    В In [4] вы видите новый синтаксис для доступа ко всему столбцу или полю.

    Наконец, В In [5] вы видите сверхмощную комбинацию фильтрации на основе маски на основе поля и выбора на основе поля. Обратите внимание на то, что чтение следующего запроса SQL не сильно отличается:

    SELECT name FROM data
    WHERE power > 9000;
    

    В обоих случаях результатом является список имен с уровнем мощности более 9000.

    Вы даже можете добавить функциональность ORDER BY, используя np.sort():

    In [6]: np.sort(data[data["age"] > 20], order="power")["name"]
    Out[6]: array(['joe', 'felipe', 'beyonce'], dtype='<U10')
    

    Перед извлечением данные сортируются по значению, и таким образом мы завершаем разговор о выборе инструментов NumPy для выбора, фильтрации и сортировки элементов, в том числе и с помощью SQL!

    Подробнее о типах данных

    Этот раздел руководства был разработан, чтобы дать вам достаточно знаний, чтобы продуктивно работать с типами данных NumPy, немного понять, как все работает под капотом, и распознать некоторые распространенные ошибки. Это, конечно, не исчерпывающее руководство. В документации NumPy на ndarrays гораздо больше ресурсов. Также есть намного больше информации об объектах dtype, в том числе о различных способах их создания, настройки и оптимизации, а также о том, как сделать их более надежными для всех ваших потребностей в обработке данных. Если у вас возникнут проблемы и ваши данные загружаются в массивы не так, как вы ожидали, то это хорошее место для начала.

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

    Взгляд в будущее: более мощные библиотеки

    В следующем разделе вы перейдете к мощным инструментам, созданным на основе фундаментальных строительных блоков, которые вы видели выше. Вот несколько библиотек, на которые вы захотите взглянуть в качестве следующих шагов на пути к полному овладению наукой о данных Python.

    pandas

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

    В документации pandas есть краткое руководство с конкретными примерами под названием 10 Minutes to pandas. Это отличный ресурс, который можно использовать для быстрой практической практики.

    scikit-learn

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

    Если вы уже знакомы с математикой, то в документации scikit-learn есть отличный список руководств, которые помогут вам начать работу с Python.

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

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

    Matplotlib

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

    Практический пример 2: манипулирование изображениями с помощью Matplotlib

    Всегда удобно, когда вы работаете с библиотекой Python, и она дает вам что-то, что оказывается базовым массивом NumPy. В этом примере вы испытаете это во всей красе.

    Вы собираетесь загрузить изображение с помощью Matplotlib, поймете, что изображения RGB на самом деле представляют собой массивы целых чисел int8 шириной × высотой × 3, манипулируйте этими байтами, и снова используйте Matplotlib, чтобы сохранить измененное изображение, когда закончите.

    Загрузите эту картинку для своих упражнений:

    Это изображение очаровательного котенка размером 1920 на 1299 пикселей. Вы собираетесь изменить цвета этих пикселей.

    Создайте файл Python с именем image_mod.py, затем настройте импорт и загрузите изображение:

    import numpy as np
    import matplotlib.image as mpimg
    
    img = mpimg.imread("kitty.jpg")
    print(type(img))
    print(img.shape)
    

    Это хорошее начало. Matplotlib имеет собственный модуль для обработки изображений, и вы собираетесь опираться на него, потому что он упрощает чтение и запись форматов изображений.

    Если вы запустите этот код, то ваш друг массив NumPy появится на выходе:

    $ python3 image_mod.py
    
    (1299, 1920, 3)
    

    Это изображение высотой 1299 пикселей, шириной 1920 пикселей и тремя каналами: по одному для уровней красного, зеленого и синего цветов (RGB).

    Хотите увидеть, что произойдет, если вы отключите каналы R и G? Добавьте это в свой скрипт:

    output = img.copy()  # Исходное изображение только для чтения!
    output[:, :, :2] = 0
    mpimg.imsave("blue.jpg", output)
    

    Запустите его еще раз и проверьте папку. Должен появиться новый образ:

    Ваш мозг еще не взорван? Вы чувствуете силу? Изображения - это просто причудливые массивы! Пиксели - это просто числа!

    Но теперь пора заняться чем-нибудь более полезным. Вы собираетесь преобразовать это изображение в оттенки серого. Однако преобразование в оттенки серого сложнее. Усредняя R, G и B, и если их объединить, получится изображение в оттенках серого. Но человеческий мозг странный, и это преобразование, похоже, не совсем правильно обрабатывает яркость цветов.

    На самом деле, лучше увидеть это самому. Вы можете использовать тот факт, что если вы выводите массив только с одним каналом вместо трех, затем вы можете указать цветовую карту, известную как cmap в мире Matplotlib. Если вы укажете cmap, Matplotlib выполнит вычисления линейного градиента за вас.

    Избавьтесь от последних трех строк в вашем скрипте и замените их следующим:

    averages = img.mean(axis=2)  # Take the average of each R, G, and B
    mpimg.imsave("bad-gray.jpg", averages, cmap="gray")
    

    Эти новые строки создают новый массив, называемый средними значениями, который является копией массива img, который вы выровняли по оси 2, взяв среднее значение по всем трем каналам. Вы усреднили все три канала и вывели что-то со значениями R, G и B, равными этому среднему. Когда R, G и B одинаковы, результирующий цвет находится в градациях серого. То, что в итоге дает, не страшно:

    Но вы можете добиться большего, используя метод яркости. Этот метод вычисляет средневзвешенное значение трех каналов, исходя из того, что зеленый цвет определяет, насколько ярким будет изображение, а синий цвет может сделать его темнее. Вы будете использовать оператор @, который является оператором NumPy для вычисления традиционного двумерного массива точечного произведения. Снова замените эти две последние строки в своем скрипте:

    weights = np.array([0.3, 0.59, 0.11])
    grayscale = img @ weights
    mpimg.imsave("good-gray.jpg", grayscale, cmap="gray")
    

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

    Вот результат:

    Первое изображение немного темнее, а края и тени более жирные. Второе изображение светлее и ярче, а темные линии не такие жирные. Вот и все - вы использовали массивы Matplotlib и NumPy для управления изображением!

    Заключение

    Независимо от того, в скольких измерениях находятся ваши данные, NumPy дает вам инструменты для работы с ними. Вы можете хранить его, изменять его форму, комбинировать, фильтровать и сортировать, и ваш код будет выглядеть так, как будто вы работаете только с одним числом за раз, а не с сотнями или тысячами.

    В этом уроке вы узнали:

    • Основные концепции науки о данных, которые стали возможными благодаря NumPy.
    • Как создавать массивы NumPy разными методами.
    • Как манипулировать массивами NumPy для выполнения полезных вычислений.
    • Как применить эти новые навыки к реальным проблемам.

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

    Теперь вы готовы к следующим шагам на пути к науке о данных. Независимо от того, очищаете ли вы данные, обучаете нейронные сети, общаетесь с помощью мощных графиков или собираете данные из Интернета вещей, все эти действия начинаются с одного места: скромного массива NumPy.

    По мотивам NumPy Tutorial: Your First Steps Into Data Science in Python

    ]]>
    https://chel-center.ru/python-yfc/2021/04/19/uchebnoe-posobie-po-numpy-vashi-pervye-shagi-v-nauke-o-dannyh-na-python/feed/ 0