Книга: Простой Python. Современный стиль программирования. 2-е изд.
Назад: Глава 21. За работой
Дальше: Приложения

Глава 22. Python в науке

Во времена ее владения сила пара

Стала величайшей на суше и воде,

И теперь все полагаются

На последние достижения науки.

Джеймс Макинтайр. Ода к юбилею королевы, 1887

В последние годы в основном из-за ПО, показанного в этой главе, Python стал очень популярен среди ученых. Если вы и сами ученый или студент, то, возможно, пользовались такими инструментами, как MatLab и R, или традиционными языками, например Java, C или C++. Сейчас вы увидите, что Python стал отличной платформой для научного анализа и публикации результатов.

Математика и статистика в стандартной библиотеке

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

Математические функции

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

Она содержит такие константы, как pi и e:

>>> import math

>>> math.pi

>>> 3.141592653589793

>>> math.e

2.718281828459045

В основном код состоит из функций, поэтому рассмотрим наиболее полезные из них.

Функция fabs() возвращает абсолютное значение своего аргумента:

>>> math.fabs(98.6)

98.6

>>> math.fabs(-271.1)

271.1

Получаем округление вниз (floor()) и вверх (ceil()) некоторого числа:

>>> math.floor(98.6)

98

>>> math.floor(-271.1)

-272

>>> math.ceil(98.6)

99

>>> math.ceil(-271.1)

-271

Высчитываем факториал (в математике это выглядит как n!) с помощью функции factorial():

>>> math.factorial(0)

1

>>> math.factorial(1)

1

>>> math.factorial(2)

2

>>> math.factorial(3)

6

>>> math.factorial(10)

3628800

Получаем натуральный логарифм аргумента на основании e с помощью функции log():

>>> math.log(1.0)

0.0

>>> math.log(math.e)

1.0

Если вы хотите задать другое основание логарифма, то передайте его как второй аргумент:

>>> math.log(8, 2)

3.0

Функция pow() возвращает противоположный результат, возводя число в степень:

>>> math.pow(2, 3)

8.0

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

>>> 2**3

8

>>> 2.0**3

8.0

Получаем квадратный корень с помощью функции sqrt():

>>> math.sqrt(100.0)

10.0

Не пытайтесь обмануть эту функцию, она уже все это видела:

>>> math.sqrt(-100.0)

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

ValueError: math domain error

Здесь имеются также обычные тригонометрические функции, я просто приведу их названия: sin(), cos(), tan(), asin(), acos(), atan() и atan2(). Если вы помните теорему Пифагора (или хотя бы можете повторить ее быстро три раза, не начав плеваться), то библиотека math вдобавок предоставит вам функцию hypot(), которая рассчитывает гипотенузу на основании длины двух катетов:

>>> x = 3.0

>>> y = 4.0

>>> math.hypot(x, y)

5.0

Если вы не доверяете таким функциям, то можете сделать все самостоятельно:

>>> math.sqrt(x*x + y*y)

5.0

>>> math.sqrt(x**2 + y**2)

5.0

Последний набор функций преобразует угловые координаты:

>>> math.radians(180.0)

3.141592653589793

>>> math.degrees(math.pi)

180.0

Работа с комплексными числами

Комплексные числа полностью поддерживаются основой языка Python в традиционной нотации с мнимой и действительной частями:

>>> # действительное число

... 5

5

>>> # мнимое число

... 8j

8j

>>> # мнимое число

... 3 + 2j

(3+2j)

Поскольку мнимое число i (в Python оно записывается как 1j) определено как квадратный корень из –1, мы можем выполнить следующее:

>>> 1j * 1j

(-1+0j)

>>> (7 + 1j) * 1j

(-1+7j)

Некоторые математические функции для комплексных чисел содержатся в стандартном модуле cmath ().

Рассчитываем точное значение чисел с плавающей точкой с помощью модуля decimal

Числа с плавающей точкой в вычислительной технике не похожи на настоящие числа, которые мы изучали в школе:

>>> x = 10.0 / 3.0

>>> x

3.3333333333333335

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

С помощью модуля decimal () вы можете записывать числа с желаемым уровнем точности. Это особенно важно для расчетов денежных сумм. Сумма в валюте Соединенных Штатов не может быть меньше одного цента (сотая часть доллара), поэтому при подсчете количества долларов и центов нам нужно быть предельно точными. Если мы попробуем представить доллары и центы как значения с плавающей точкой, такие как 19,99 или 0,06, то потеряем некоторую часть точности в последних битах еще до начала вычислений. Как с этим справиться? Легко. Нужно использовать модуль decimal:

>>> from decimal import Decimal

>>> price = Decimal('19.99')

>>> tax = Decimal('0.06')

>>> total = price + (price * tax)

>>> total

Decimal('21.1894')

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

>>> penny = Decimal('0.01')

>>> total.quantize(penny)

Decimal('21.19')

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

Выполняем вычисления для рациональных чисел с помощью модуля fractions

Вы можете представлять числа как числитель, разделенный на знаменатель, с помощью модуля стандартной библиотеки Python fractions (). Рассмотрим пример простой операции умножения 1/3 на 2/3:

>>> from fractions import Fraction

>>> Fraction(1,  3) * Fraction(2, 3)

Fraction(2, 9)

Аргументы с плавающей точкой могут быть неточными, поэтому вы можете использовать модуль Decimal вместе с модулем Fraction:

>>> Fraction(1.0/3.0)

Fraction(6004799503160661, 18014398509481984)

>>> Fraction(Decimal('1.0')/Decimal('3.0'))

Fraction(3333333333333333333333333333, 10000000000000000000000000000)

Получим наибольший общий делитель для двух чисел с помощью функции gcd:

>>> import fractions

>>> fractions.gcd(24, 16)

8

Используем Packed Sequences с помощью модуля array

В Python список больше похож на связанный список, а не на массив. Если вы хотите получить одномерную последовательность элементов одинакового типа, то применяйте тип array (). Переменные этого типа используют меньше места и поддерживают многие методы работы со списками. Создайте массив с помощью команды вида array(кодтипа,инициализатор). Код типа указывает на тип данных (такой как int или float), а опциональный инициализатор содержит исходные значения, которые можно передать как список, строку или итерируемое значение.

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

Обрабатываем простую статистику с помощью модуля statistics

Начиная с версии Python 3.4, statistics () — стандартный модуль. Он содержит традиционные функции: среднее, медиану, режим, стандартное отклонение, распределение и т.д. Входными аргументами являются последовательности (списки или кортежи) или итераторы любого числового типа данных: int, float, decimal и fraction. Одна из функций, mode, также принимает в качестве аргументов строки. Для Python существует множество других статистических функций, доступных в пакетах, таких как SciPy и Pandas, которые мы рассмотрим далее в этой главе.

Перемножение матриц

Начиная с Python 3.5, вы увидите символ @, который делает необычные вещи. Он все еще может использоваться для декораторов, но будет применяться также и для перемножения матриц (). Если вы работаете со старой версией Python, то вам следует задействовать NumPy.

Python для науки

В оставшейся части этой главы рассматриваются сторонние пакеты Python для науки и математики. Несмотря на то что вы можете установить их индивидуально, следует рассмотреть их одновременное скачивание в качестве части научного дистрибутива Python. Рассмотрим основные варианты.

Anaconda (/) — это бесплатный пакет, имеющий множество самых свежих возможностей. Он поддерживает Python 2 и 3 и не вредит установленной у вас версии Python.

• Enthought Canopy () — доступна как бесплатная, так и коммерческая версии.

• Python(x, y) (/) — этот релиз подходит только для Windows.

Pyzo (/) — этот пакет основан на некоторых инструментах пакета Anaconda, а также других.

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

NumPy

NumPy (/) — это одна из основных причин популярности Python среди ученых (/). Вы слышали, что динамические языки, такие как Python, зачастую медленнее компилирующих языков наподобие С или даже других интерпретируемых языков, например Java. NumPy был написан для предоставления доступа к быстрым многомерным массивам по аналогии с научными языками, такими как FORTRAN. Вы получаете скорость С и дружелюбную к разработчикам природу Python.

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

Чтобы начать работу с NumPy, вы должны понять устройство основной структуры данных, многомерного массива ndarray (от N-dimensional array — «N-мерный массив») или просто array. В отличие от списков и кортежей в Python, все элементы должны иметь одинаковый тип. NumPy называет количество измерений массива его рангом. Одномерный массив похож на ряд значений, двумерный — на таблицу со строками и столбцами, а трехмерный — на кубик Рубика. Длина измерений может не быть одинаковой.

109985.png

Массивы array в NumPy и array в Python — не одно и то же. Далее в этой главе я буду работать только с массивами NumPy.

Но зачем нам нужны массивы?

Научные данные зачастую представляют собой большие последовательности.

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

NumPy обрабатывает массивы гораздо быстрее, чем стандартные списки или кортежи Python.

Существует множество способов создать массив NumPy.

Создаем массив с помощью функции array()

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

>>> b = np.array( [2, 4, 6, 8] )

>>> b

array([2, 4, 6, 8])

Атрибут ndim возвращает ранг массива:

>>> b.ndim

1

Общее число значений можно получить с помощью атрибута size:

>>> b.size

4

Количество значений каждого ранга возвращает атрибут shape:

>>> b.shape

(4,)

Создаем массив с помощью функции arange()

Метод arange()NumPy похож на стандартный метод range() Python. Если вы вызовете метод arange(), передав ему один целочисленный аргумент num, то он вернет ndarray от 0 до num-1:

>>> import numpy as np

>>> a = np.arange(10)

>>> a

array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

>>> a.ndim

1

>>> a.shape

(10,)

>>> a.size

10

С помощью двух значений он создаст массив от первого элемента до последнего минус один:

>>> a = np.arange(7, 11)

>>> a

array([  7, 8,  9, 10])

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

>>> a = np.arange(7, 11, 2)

>>> a

array([7, 9])

До сих пор мы показывали примеры лишь с целыми числами, но метод arrange() работает и с числами с плавающей точкой:

>>> f = np.arange(2.0, 9.8, 0.3)

>>> f

array([ 2. ,  2.3,  2.6,  2.9,  3.2,  3.5,  3.8,  4.1,  4.4,  4.7,  5. ,

        5.3,  5.6,  5.9,  6.2,  6.5,  6.8,  7.1,  7.4,  7.7,  8. ,  8.3,

        8.6,  8.9,  9.2,  9.5,  9.8])

И последний прием: аргумент dtype диктует функции arrange(), какой тип значения следует создать:

>>> g = np.arange(10, 4, -1.5, dtype=np.float)

>>> g

array([ 10. ,   8.5,   7. ,   5.5])

Создаем массив с помощью функций zeros(), ones() и random()

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

>>> a = np.zeros((3,))

>>> a

array([ 0.,  0.,  0.])

>>> a.ndim

1

>>> a.shape

(3,)

>>> a.size

3

Этот массив имеет ранг 2:

>>> b = np.zeros((2, 4))

>>> b

array([[ 0.,  0.,  0.,  0.],

       [ 0.,  0.,  0.,  0.]])

>>> b.ndim

2

>>> b.shape

(2, 4)

>>> b.size

8

Другой особой функцией, заполняющей массив одинаковыми значениями, является ones():

>>> import numpy as np

>>> k = np.ones((3, 5))

>>> k

array([[ 1.,  1.,  1.,  1.,  1.],

       [ 1.,  1.,  1.,  1.,  1.],

       [ 1.,  1.,  1.,  1.,  1.]])

Последняя функция создает массив и заполняет его случайными значениями из промежутка от 0,0 до 1,0:

>>> m = np.random.random((3, 5))

>>> m

 

array([[  1.92415699e-01,   4.43131404e-01,   7.99226773e-01,

          1.14301942e-01,   2.85383430e-04],

       [  6.53705749e-01,   7.48034559e-01,   4.49463241e-01,

          4.87906915e-01,   9.34341118e-01],

       [  9.47575562e-01,   2.21152583e-01,   2.49031209e-01,

          3.46190961e-01,   8.94842676e-01]])

Изменяем форму массива с помощью метода reshape()

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

>>> a = np.arange(10)

>>> a

array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

>>> a = a.reshape(2, 5)

>>> a

array([[0, 1, 2, 3, 4],

       [5, 6, 7, 8, 9]])

>>> a.ndim

2

>>> a.shape

(2, 5)

>>> a.size

10

Вы можете изменять форму массива разными способами:

>>> a = a.reshape(5, 2)

>>> a

array([[0, 1],

       [2, 3],

       [4, 5],

       [6, 7],

       [8, 9]])

>>> a.ndim

2

>>> a.shape

(5, 2)

>>> a.size

10

Присваиваем кортеж, указывающий параметры формы, атрибуту shape:

>>> a.shape = (2, 5)

>>> a

array([[0, 1, 2, 3, 4],

      [5, 6, 7, 8, 9]])

Единственное ограничение — произведение рангов должно быть равным количеству значений (в нашем случае 10):

>>> a = a.reshape(3, 4)

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

ValueError: total size of new array must be unchanged

Получаем элемент с помощью конструкции []

Одномерный массив работает как список:

>>> a = np.arange(10)

>>> a[7]

7

>>> a[-1]

9

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

>>> a.shape = (2, 5)

>>> a

array([[0, 1, 2, 3, 4],

       [5, 6, 7, 8, 9]])

>>> a[1,2]

7

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

>>> l = [ [0, 1, 2, 3, 4], [5, 6, 7, 8, 9] ]

>>> l

[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]

>>> l[1,2]

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

TypeError: list indices must be integers, not tuple

>>> l[1][2]

7

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

>>> a = np.arange(10)

>>> a = a.reshape(2, 5)

>>> a

array([[0, 1, 2, 3, 4],

       [5, 6, 7, 8, 9]])

Используйте разбиение, чтобы получить первый ряд — элементы, начиная со смещения 2, до конца:

>>> a[0, 2:]

array([2, 3, 4])

Теперь получим последний ряд — все элементы вплоть до третьего с конца:

>>> a[-1, :3]

array([5, 6, 7])

Вы также можете присвоить значение более чем одному элементу с помощью разбиения. Следующее выражение присваивает значение 1000 столбцам (смещениям) 2 и 3 каждого ряда:

>>> a[:, 2:4] = 1000

>>> a

array([[   0,    1,  1000, 1000,    4],

       [   5,    6,  1000,  1000,    9]])

Математика массивов

Создание и изменение формы массивов так нас увлекли, что мы почти забыли сделать с ними нечто более полезное. Для начала мы задействуем переопределенный в NumPy оператор умножения (*), чтобы умножить все значения массива за раз:

>>> from numpy import *

>>> a = arange(4)

>>> a

array([0, 1, 2, 3])

>>> a *= 3

>>> a

array([0, 3, 6, 9])

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

>>> plain_list = list(range(4))

>>> plain_list

[0, 1, 2, 3]

>>> plain_list = [num * 3 for num in plain_list]

>>> plain_list

[0, 3, 6, 9]

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

>>> from numpy import *

>>> a = zeros((2, 5)) + 17.0

>>> a

array([[ 17.,  17.,  17.,  17.,  17.],

       [ 17.,  17.,  17.,  17.,  17.]])

Линейная алгебра

NumPy содержит множество функций линейной алгебры. Например, определим такую систему линейных уравнений:

4x + 5y = 20

x + 2y = 13

Как найти х и у? Создадим два массива:

коэффициенты (множители для х и у);

зависимые переменные (правая часть уравнения).

>>> import numpy as np

>>> coefficients = np.array([ [4, 5], [1, 2] ])

>>> dependents = np.array( [20, 13] )

Теперь используем функцию solve() модуля linalg:

>>> answers = np.linalg.solve(coefficients, dependents)

>>> answers

array([ -8.33333333,   10.66666667])

В результате получим, что x примерно равен –8.3, а у примерно равен 10.6. Являются ли эти числа решениями уравнения?

>>> 4 * answers[0] + 5 * answers[1]

20.0

>>> 1 * answers[0] + 2 * answers[1]

13.0

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

>>> product = np.dot(coefficients, answers)

>>> product

array([ 20.,  13.])

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

>>> np.allclose(product, dependents)

True

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

Библиотека SciPy

Библиотека SciPy (/) создана на основе NumPy и имеет даже больше математических и статистических функций. Релиз SciPy (-) содержит NumPy, SciPy, Pandas (ее мы рассмотрим позже в этой главе) и другие библиотеки.

SciPy содержит множество модулей, включая те, которые выполняют следующие задачи:

оптимизацию;

• ведение статистики;

• интерполяцию;

• линейную регрессию;

• интеграцию;

• обработку изображений;

обработку сигналов.

Если вы уже работали с другими научными инструментами для компьютера, то обнаружите, что Python, NumPy и SciPy охватывают области, с которыми работает также коммерческий MATLAB () или приложение с открытым исходным кодом R (/).

Библиотека SciKit

Как и предыдущая библиотека, SciKit (/) — это группа научных пакетов, построенная на основе SciPy. SciKit-Learn (/) — это пакет, который специализируется на машинном обучении: поддерживает моделирование, классификацию, кластеризацию и разнообразные алгоритмы.

Pandas

С недавнего времени распространено употребление словосочетания «наука о данных». Я слышал определения «статистика, собираемая на Mac» или «статистика, собираемая в Сан-Франциско». Как бы вы ни определили науку о данных, инструменты, о которых я говорил ранее, — NumPy, SciPy и собственно Pandas, вынесенный в тему этого подраздела, — компоненты растущего популярного инструментария, работающего с данными. (Mac и Сан-Франциско опциональны.)

Pandas (/) — это новый пакет для интерактивного анализа данных. Он особенно полезен для манипулирования данными реального мира с помощью комбинирования матричной математики NumPy и возможности обработки таблиц и реляционных баз данных. В книге Python for Data Analysis: Data Wrangling with Pandas, NumPy and IPython Уэса Маккини (издательство O’Reilly) рассматривается выпас данных с помощью NumPy, Python и Pandas.

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

Pandas — это ETL-инструмент для реальных данных — с отсутствующими значениями, странными форматами, странными измерениями — всех типов. Вы можете разделить, объединить, заполнить, сконвертировать, изменить форму, разбить данные, а также скачать и сохранить файлы. Он интегрируется с инструментами, которые мы только что обсудили: NumPy, SciPy, iPython — для подсчета статистики, подгонки данных под модель, рисования диаграмм, публикации и т.д.

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

Python и научные области

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

Общие:

вычисления Python в науке и инженерном деле ();

интенсивный курс Python для ученых ().

Физика:

физические вычисления ();

• Astropy (/);

• SunPy (/) (анализ данных, поступающих с Солнца);

• MetPy () (анализ метеорологических данных);

• Py-ART () (погодный радар);

• Community Intercomparison Suite (/) (атмосферные науки);

• Freud (/) (анализ траектории);

• Platon (/) (атмосферы экзопланет);

• PSI4 (/) (квантовая химия);

• OpenQuake Engine ();

yt (/) (волюметрический анализ данных).

Биология и медицина:

Biopython (/);

• Python для биологов (/);

• Введение в прикладную биоинформатику (/);

• Нейровизуализация с помощью Python (/);

• MNE () (визуализация нейрофизиологических данных);

• PyMedPhys (/);

Nengo (/) (симулятор нейронов).

Проводятся следующие международные конференции по Python и научным данным:

PyData (/);

• SciPy (/);

• EuroSciPy (/).

Читайте далее

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

Упражнения

22.1. Установите Pandas. Получите CSV-файл в примере 16.1. Запустите программу из примера 16.2. Поэкспериментируйте с командами Pandas.

Оригинальная цитата: In her reign the power of steam On land and sea became supreme, And all now have strong reliance In fresh victories of science. — Примеч. пер.

Маккини У. Python и анализ данных. — М.: ДМК-Пресс, 2015.

Назад: Глава 21. За работой
Дальше: Приложения

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