Математический практикум по Питону.
Создание функций (определение и объявление). Показана важность ранее пройденных объектов: словарь (dict) и кортеж (couple).
Это предварительная версия! Любые замечания приветсвуются.
#%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as models
Напомню:
import csv # Для рабом с csv файлами.
data = []
with open( 'MTLR_180101_190110.txt' ) as f:
data_rows = csv.reader( f, delimiter = ';' )
data = list( data_rows )
close = list( map( lambda x: float(x[-2]), data) ) # Или сразу.
#close Не будем его выводить. Там много данных.
plt.plot( close )
plt.ylabel( 'Цена рубли')
plt.xlabel( 'День начиная с 2018 г')
Как выполнить данное считывания и для других эмитентов, т.е. для других файлов? Можно создать функцию, которая обособит код.
Мы уже сталкивались с ламбда функциями. Напомню.
# Задаем ламбда функцию, которая удваивает число (объект).
dbl = lambda x : 2*x
# Применим функцию путем её вызова.
dbl( 2 ), dbl( -1.1 ), dbl( 3.5 )
# На всякий случай приведем и такой пример.
dbl( 'раз ' ) # Мы все это уже знаем...
Ламбда функция не позволяет задать больше одного действия. Фактически это обособленное выражение. Дабы можно было отделить несколько последовательных операций необходимо использовать функции. Покажем их сначала как вариант ламбда функций.
Функция задаяется (объявляется и определяется) ключевым словом def в начале строки. Далее, через пробел, идет имя, в скобках список её аргументов. Строка звершается двоеточием.
# В данном случае, функция имеет название dblF и один аргумент x.
def dblF( x ):
# Сдесь могут идти действия. Но у нас простая функция. Поэтому их нет.
# Для возврата значения из функции ипользется оператор return.
return 2*x # Перед возвратом значения вычисляется выражение.
dblF( 2 ), dblF( -1.3 ), dblF( 'ещё раз ' ) # Вызов полностью совпадает.
# Ламбда функция от двух аргументов. Возводит в степень.
mypow = lambda x, y: x**y
# Аналогичная функция mypowW имеет два аргумента.
def mypowW( x, y):
return x**y
# Вызвали функции от двух аргументов.
mypow( 2, 3), mypowW( 2, 3), mypowW( 25, 0.5)
Конечно можно строить композицию.
# Напомню, что вызовы делаются по значению,
# т.е. сначала вычисляется значение аргумента,
mypow( dbl(1), 4), mypowW( dblF(1), 4), dbl( mypowW(3, 2) )
# а потом это значение передается дальше.
именные аргументы
# В качестве примера возьмем другую функцию.
def mysum( a, b): # Теперь у функции два аргумента
return a + b * 2; # Возвращаем значение выражения.
mysum( 3, 2) # 3 + 2 * 2
В предыдущем вызове функции mysum число 3 будет присвоено первому аргументу функции, т.е. переменной a, а число 2 второму аргументу, т.е. переменной b. Таким образом, значения присваиваются аргументам исходя из того порядка в котором они идут при вызове функции. В Питоне значения аргументов можно передавать не только по номеру аргумента в списке (по позиции), но и по его названию. В Питоне есть возможность этот порядок изменить и выполнить вызов функции явно указав какому аргументу какое значение присовить. Для этого при вызове используется оператор присвоения.
# Для явного указания какому аргументу
# какое значение присовить используется оператор присвоения.
mysum( b = 3, a = 2) # аргументу b присвоено значение 3,
# а a число 2. 2 + 3 * 2
Естественно что существует ряд ограничений.
mysum( c = 5 ) # Имя аргумента должно сущестовать.
Можно комбинировать режимы. Но при двух аргументов мало что скомбинируешь. Тем не менее...
# При таком вызове система запутается.
mysum( 3, a = 2) # Точнее будет повторное присвоение первому аргументу.
# А так нельзя потомучто после присвоения переменной значения по имени
mysum( b = 3, 2) # позиции не учитываются.
mysum(0, b=-1)
Но можно так.
# Функция от трех аргументов.
def mymac(a, m, s): # mac -- Multiply–accumulate operation.
return a + m*s
mymac(10, s=3, m=2) # Так можно. 10 + 2*3
Значения по умолчанию
# при вызове функции необходио указать значение всех аргументов.
mysum( 5 )
Упр. Напиши функцию вычисляющую корень квадратного уравнения. Например, а) больший корень. б) оба корня ввиде набора.
# Можно сделать значения по умолчанию
def mysum2( a, b = 5): # У аргумента b задано значение по умолчанию,
return a + b * 2; # которое используется в случае,
# если оно не задано явно.
mysum2( 3 ) # Так можно. 3 + 5 * 2
mysum2( 3, 2) # А можно и указать значение. 3 + 2 * 2
# Обычные переменные не могут следовать после переменных
# с заданным значением по умолчанию.
def mysum3( a, b = 5, c):
return a + b + c
Упр. Напиши функцию вычисляющую логарифм. По умолчанию основание пусть будет натуральным. Иначе, оно должно быть указано.
Тело
# Тело функции ествественно может быть сложным
def doCnt( a, c=0 ): # Вычисляем .
eq = list(map( lambda x: x==c, a))
return np.mean( eq )
doCnt( [1, 2, 3, 0, 5, 4, 0, 8], 2 ), 1/8 # 8 элементов.
doCnt( [1, 2, 3, 0, 5, 4, 0, 8] )
Упр. Напиши функцию вычисляющую среднее и среднеквадратичное отклонение массива.
def txt2data( name ): # Функция принимает на вход название файла.
with open( name ) as f:
data_rows = csv.reader( f, delimiter = ';' )
data = list( data_rows )
# close = list( map( lambda x: float(x[-2]), data) ) Или сразу
return data
mechel = txt2data( 'MTLR_180101_190110.txt' )
mtl_cl = np.array( list( map( lambda x: float(x[-2]), mechel) )) # -2 это цена закрытия.
vtb = txt2data( 'VTBR_180101_190110.txt' )
vtb_cl = np.array( list( map( lambda x: float(x[-2]), vtb) ))
plt.plot( mtl_cl )
plt.plot( vtb_cl )
plt.ylabel( 'Цена рубли')
plt.xlabel( 'День начиная с 2018 г')
Чего-то с графиком цены не то... они разного масштаба.
Пусть будет график процентного изменения.
plt.plot( mtl_cl/mtl_cl[0] ) # Поделим все элементы массива на самое первое значение.
plt.plot( vtb_cl/vtb_cl[0] ) # Аналогично для второго эмитента.
plt.ylabel( 'Процентое изменение') # Отображаем не цену, а процентное изменение.
plt.xlabel( 'День начиная с 2018 г')
plt.legend( ["Мечел", "ВТБ"] )
gaz = txt2data( 'GAZP_180101_190110.txt' )
gaz_cl = np.array( list( map( lambda x: float(x[-2]), gaz) ))
Как во всех этих элементах не запутаться? Напомню, есть такая вещь как словарь.
Каталог эмитентов
ticket = dict()
ticket['MTLR'] = mtl_cl
ticket['VTBR'] = vtb_cl
ticket['GAZP'] = gaz_cl
i = list(ticket.items())
i;
# Первый элемент кортежа имя (ключ),
i[0][0], type( i[0][1] ) # второе сами данные (котировки).
def doPlt( i, lab ):
k, t = i
plt.plot( t/t[0] )
lab.append( k )
doPlt(i[0], [])
lab = []
list( map( lambda x : doPlt(x, lab), ticket.items() ))
plt.ylabel( 'Процентое изменение')
plt.xlabel( 'День начиная с 2018 г')
plt.legend( lab )
Упражнение. Как сделать так чтобы подписи в легенде были не сокращенные названия, а полноценные имена (как в предыдущем графике).
Обратно к обработке строки
translit = str.maketrans({'п':'p', 'р':'r', 'о':'o', 'е':'e', 'к':'k', 'а':'a', 'в':'v'})
translit # Словарь соотетствия: по одному символу дается другой.
'проверка'.translate( translit ) # Используем для посимвольного преобразования.
# Иногда (когда буква соответсвует букве) можно обойтись и строчкой.
traslit = str.maketrans( 'проверка', 'proverka') # Строится словарь.
traslit
# Когда букв нет в словаре трансляции, они не обрабатываются.
'проверь'.translate( translit )
Да и сам список
stud = ['Алексей',"Дима", "Аня", "Саша"]
stud
stud.sort()
stud
Как получить табельный номер студента?
stud_num = enumerate( stud ) # Нумирует список.
stud_num # Как обычно пока это просто генератор.
stud_num = list( stud_num ) # Формируем из него список.
stud_num # Список состоит из кортежей
# (индекс и, соответствующее, значение элемента списка).
stud_num[2] # Кортеж из номера и значения списка (имени).
# Теперь можно сделать аналог списка через словарь.
studd = dict(stud_num)
studd
type(studd), studd[2], type(stud), stud[2],
stud_name = list( map( lambda x: (x[1], x[0]), stud_num ))
stud_name
# А теперь словарь, который по имени возвращает номер.
studn = dict(stud_name)
studn
studn['Дима']
На самом деле ранее используемое понятие сложного индекса является объектом питона: slice.
# Как и ранее, указывается начальный индекс,
a = slice( 1, -1, 1) # последний не включительно и шаг.
a, type(a)
# Имея объект, который работает с понятием сложного индекса,
# можно вместо индекса подать объект срез.
b = [1, 2, 3, 4, 5]
b[ a ] # Применяем как обычно при использовании сложного индекса.
# Результат совпадает с применением сложного индекса напрямую.
b[ 1: -1: 1]
# Раз срез -- объект, то его можно, например,
def subsum( a, s): # передавать как значение функции.
return sum( a[s] )
# Четный и нечетный поддиапазон индексов.
l = [ slice(0, -1, 2), slice(1, -1, 2) ]
subsum( b, l[0]), subsum( b, l[1])
# Как и объект touple
a.start = 3 # объект срез является константным.
# Можно использовать его метод indices для получения сложного индекса без отрцательных первых двух чисел.
c = a.indices(10) # Для 10го объекта. Нумерация с 0.
c # Раз индексов бесконечно много (-1), то получим значение.
slice(-1, -5, -1).indices(10)
# Если же взять ограниченный срез,
a = slice(1,5,1) # то поведение будет иное.
a.indices(2) # Указываем сколько фактически элементов.
a.indices(10) # Мы упремся в "последний" элемент.
# Так как речь идет о настоящем индексе,
a.indices( -2 ) # то отрицательным он быть не может.
a = slice(1, -1, -1)
a.indices(5)