Математический практикум по Питону.
Вводятся базовые элементы питона (Python версии 3.xx): списки и наборы (картежи). Вводятся базовые элементы питона (Python версии 3.xx) на базе ключевых библиотек относящихся к анализу данных: построение графиков/гистограмм и численой статистике. Последнее, в частности, используется для ввода ключевых понятий из анализа данных: выборка, плотность распределения и подгонка модели под данные. Ключевые слова: map, filter, reduce, lambda, matplotlib, pyplot, xlim, xticks, csv и plot.
Это предварительная версия! Любые замечания приветсвуются.
Рассмотрим как можно обрабатывать список элементов. Замечу, что существуют три главных действия на список элементов.
map
l = [4, 25, 9, 16]
import numpy as np
# Применим функцию np.sqrt для всех элиментов списка l.
map( np.sqrt, l) # Первый аргумент функция, второй -- список.
q = map( np.sqrt, l)
q # Посмотрим содержимое.
# Проверяем тип.
type( q ) # Map есть map.
Map это объект действие (генератор). Нужно чтобы числа им были сгенерированы.
# Для этого создадим объект список с его помощью.
list( q )
l
q = map( np.sqrt, l) # Создали генератор по списку l.
l.append(81) # Добавили в список l элемент.
list(q) # Он был учтен,
# т.е. генератор актуальный список.
# Приведем другой пример.
ll = [np.pi/2, np.pi, np.pi/4]
list( map( np.sin, ll ) ) # Взяли синус от каждого элемента
Преобразование
Как задать собственное преобразование. Для простых преобразований в питоне есть лямбда функции.
Данная конструкция задается следующим образом:
lambdaсписок аргументов/параметров : действие/значение.
lambda x: x*2 # Умножаем число на 2.
lambda x, y: x**y # Возводим первое чило в степень второго.
# Тип имеет функции.
# Для их применения нужно сохранить в переменну.
dbl = lambda x: x*2
mypow = lambda x, y: x**y
dbl, mypow
type( dbl ), type( mypow )
# Применим преоьразование к числам.
dbl( 2), dbl( 2.5 ), mypow( 2, 3 )
l # Вспомним содержимое списка.
# Применим к каждому элемента списка преобразование,
q = map( dbl, l) # умножение на два.
q
# Построим список по q.
list( q )
# Можно в явном виде.
q = map( lambda x: x*2, l)
q
list( q )
# Можно так. Выводятся элементы.
list( map( print, l) )
# Список портится.
Частично заданые аргументы
# Все аргументы нужно указывать.
list( map( mypow(,0.5), l) )
# Будет ошибка.
# Нужно так: labmda от labmda.
list( map( lambda x: mypow(x, 0.5), l) )
filter
Используется для фильтрации (как сито) элементов списка. После обработки остаются не все элементы списка. Остаются только те для которые дают истину (для предиката).
# Возвращает истину тогда и только тогдв число нечетное.
odd = lambda x: x%2 == 1
odd(1), odd(4), odd(5.1)
list(map(odd, [1,4,6,5,8,9,11]))
q = filter( odd, [1,4,6,5,8,9,11])
q # Тоже возвращает не сам список, а генератор.
# Формируем числа используя генератор.
list( q ) # От списка останется только те элементы
# н которых была истина: нечетные.
stud = []
stud.append( ['муж', 'Максим'] )
stud.append( ['жен', 'Лена'] )
stud.append( ['жен', 'Катя'] )
stud.append( ['муж', 'Дима'] )
stud.append( ['муж', 'Саша'] )
stud.append( ['жен', 'Яна'] )
stud
stud[2]
print( 'идекс 0:', stud[2][0], 'и индекс 1:', stud[2][1] )
# Тогда можно проверить что на нудевом индексе значение 'муж'
male = lambda x: x[0] == 'муж'
print(stud[2], 'мужчина? Ответ: ', male( stud[2] ) )
print(stud[3], 'мужчина? Ответ: ', male( stud[3] ) )
men = list( filter(male, stud) )
men
men[1] # Такая же пока структура.
list( map( lambda x: x[1], men) ) # Извлекаем имя.
reduce
Другим действием является редукция,т.е. сведение списка к некому значению.
from functools import reduce
mac = lambda acc, x: acc + 2*x # acc аккумулирует значение. Правило ассоциативно.
reduce( mac, [-1,2,3]) # -1 + 2*(2) + 2*(3) есть 9. Ясно что ассоциативно.
mac2 = lambda acc, x: 2*acc + x # неассоциатовно
reduce( mac2, [ -2, 2, 3] ) # 2*(2*(-2) + 2) + 3 есть 3.
2*(2*(-2) + 2) + 3
sum( [-1, 2, 3] )
Будем строить функцию по-точечно, т.е. построем значение в каждой из выбранных точек и соеденим соседнии прямой. Для начала сгенерируем точки в которых будет вычислена функция.
# С range можно создавать списки чисел, где аргументы: начадо, конец и шаг.
x_10 = list( range(0, 10, 1) ) # От 0, до 10 с шагом 1.
x_10 # Разрешены только целые числа.
len( x_10 )
# от, до и шаг должны быть целыми.
x_10_5 = list( range(0, 10.5, 1) )
# Выполним преобразование списка чисел 0 до 9
list( map( lambda x: x/2 - 2, x_10 ) )
# Получим список чисел с шагом в 0.5 от -2 до 2.5.
# Теперь возьмем список побольше
x_30 = list( range(0, 30, 1) )
len( x_30 )
#Сгенерируем именно равномерный на набор точек.
x = list( map( lambda x: x/2 - 5, x_30 ) )
# список от -5 до 9.5 с шагом в 0.5.
# Пишем выражение, которое ествественно будет вычислено в каждой точке,
f = lambda x:x*x - 2*x +3 # т.е. по-элементно для списка x.
y = list( map( f, x) )
len(x), len(y) # Видим что длины совпадают.
# Значение в точке -2 равно 11.
x[6], y[6] # (-2)*(-2) - 2(-2) + 3 = 4 + 4 + 3 = 11
Точки и значения вы сформировали. Теперь будем строить график. Для этого нужна ещё одна вспомогательная библиотека.
Библиотека matplotlib применяется для вывода графиков. Точнее её подмодуль (подбиблиотека) pyplot.
# Длинное название к отмеченной подбиблиотеки (pyplot)
import matplotlib.pyplot as plt # обозначим plt.
#Следуюшща строка позволяет выводить данные прям на данной странице в блоках. Иначе будет "всплывать" отдельное окно.
#%matplotlib inline
Отмечу, что последняя строчка относится к система jupyter, а не самому языку python.
Отрисуем нужную функцию. Используем для этого 100 точек. После загрузки библитеки в систему, график по сформированным ранее данным стротися так:
plt.plot( y ) # Значений достаточно.
Но можно конечно добавить отсчеты по оси x и разукрасить график.
# plot строит график по-точечно.
plt.plot( x, y, 'g.-') # g -- green (зеленая), . и - задают стиль точки и прямой.
# b - синий, * -- звездочка в каждой точке. -- соединение пунктиром.
plt.plot( x, y, 'b*--')
# Цвета есть такие:
# 'b' -- синий | 'c' -- бирюзовый | 'k' -- черный
# 'g' -- зеленый | 'm' -- пурпурный | 'w' -- белый
# 'r' -- красный | 'y' -- желтый |
# стили прямых такие:
# '-' -- сплошная | '-.' -- точка-дефис
# '--' -- пунктирная | ':' -- по-точечно
# А стили точек:
# Различные размеры:
# '.', ',' и 'o' -- точек
# 'v', '^', '<' и '>' -- треугольников
# '1', '2', '3' и '4' -- уголка.
# Специфика:
# 's' -- квадрат | 'h' -- гексагон-1 | 'x' -- знак x | '|' -- вертикальные
# 'p' -- пентагон | 'H' -- гексагон-2 | 'D' -- ромб | '_' -- горизонтальные
# '*' -- звезда | '+' -- знак плюс | 'd' -- ромб утонченный |
# Можно рисовать несколько графиков на одном чертеже:
y2 = list( map( lambda x: 10*np.sin( x ) + 25, x) )
plt.plot( x, y, 'g.-', x, y2, 'b*--')
# Ествественно, что значения можно считать "налету":
plt.plot( x, list( map( lambda x: np.sin(x), x) ), 'g.-')
plt.plot(x, list( map( lambda x: x*x/100, x) ), 'b*--')
plt.legend( ['sin', 'x^2'] ) # Добавим и легенду.
Ввиду того, что количество точек по которым строилась кривая было небольшим, синусойда получилась угловатой.
plt.plot( x, y, 'g.-')
plt.xlabel( "Ось абсцисс" ) # Задаем название для оси абсцисс.
plt.ylabel( "Ось ординат") # Задаем название для оси ординат.
plt.plot( x, y, 'g.-')
plt.xticks( [-2, 0.5, 2, 5] ) # Задаем отсчеты вдоль оси абсцисс.
plt.xlim(-4, 8) # Зданаем границы дипазона для оси абсцисс.
plt.plot( x, list( map( lambda x: np.exp(x), x) ), 'g.-')
plt.plot( x, list( map( lambda x: np.exp(x), x) ), 'g.-')
plt.yscale( 'log' ) # Меняем шкалу на логарифмическую.
Упр. Нарисуй параболу (зеленым цветом) и отметь корни соответствуюшего уравнения точками (красного цвета).
Берем откуда-то простой csv (comma seperated values) файл. Отмечу, что расширение у некго не обязано быть csv, оно может быть и txt.
Например с https://www.finam.ru/profile/moex-akcii/mechel/export/ Параметры можно менять: сменить эмитента (например, выбрать Газпром, Сбербанк и тому подобное), период (например, выбрать день). Промежуток времени пока лучше выбрать поменьше. Внимание, пока скачиваем файл без заголовка (убрать соответствующий флажок)! Символ ; в качестве разделителя.
Для считывания данных файлов потребуется библиотека csv.
import csv # Для считывания csv файлов.
data = []
with open('MTLR_190101_190110.txt') as f:
data_rows = csv.reader( f )
data = list( data_rows )
data
len(data)
data[1]
Видно, что строчки из файла считались целиком, как текстовые строчки (обрати внимание на одинарные кавычки). Хотелось бы чтобы каждый столбец отделился от других столбцов. Для этого можно указать дополнительный параметр функции обработки csv файлов. Параметр delimiter, он указывает какой разделитель используется для отделения стобцов.
data = []
with open('MTLR_190101_190110.txt') as f:
data_rows = csv.reader( f, delimiter = ';' )
data = list( data_rows )
data
Теперь видно, что все стобцы считались по отедльности. Но каждый элемент по прежнему является стройчкой. Нужно для тех стобцов про кторые мы знаем наверняка выполнить явное пробразование из текста в число.
Ранее нам уже встречалось явное преобразование, когда мы преобразовывали истинность в число.
int( 5 < 7 )
По аналогии с этим:
int('34') # Преобразуем текст в число.
Более дотошно:
a = '55'
b = int( a )
type( a ), type( b )
Нам должна быть известна структура входного файла. Точнее должно быть известно назначение столбцов. Для ранее скаченного файла она такая: TICKER, PER, DATE, TIME, OPEN, HIGH, LOW, CLOSE, VOL. Предпоследний столбец соответствует цене закрытия.
# Извлечение цены закрытия
get_close = lambda x: x[-2]
data[2]
get_close(data[2])
# Будет сторокой.
fromStr = lambda x: float(x)
fromStr( get_close(data[2]) )
close_str = list( map( get_close, data) )
close_str
close = list( map( fromStr, close_str) )
close
# В два прохода.
close_str = list( map( lambda x: x[-2], data) )
close = list( map( lambda x: float(x), close_str) )
close
# Или в один, сразу.
close = list( map( lambda x: float(x[-2]), data) )
close
Теперь все сработало и был считан столбец значений.
Теперь возьмем файл побольше и построим график.
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 Не будем его выводить. Там много данных.
len( close ) # А именно столько.
close[10:20] # Посмотрим на кусочек данных.
from matplotlib import pyplot as plt
# %matplotlib inline # Раньше это требовалось. Если что попробуй.
plt.plot( close ) # Рисуем график.
plt.ylabel( 'Цена рубли') # Название для оси y (оси ординат).
plt.xlabel( 'День начиная с 2018 г') # Название для оси x (оси абсцисс).
Упр. Нарисуй график цены и его какое-то скользящее среднее.
Упр. Нарисуй графики процентного изменения цен двух эмитентов с начала периуда. С легендой.
Упр. Нарисуй графики процентного поэлементого изменения цен двух эмитентов. С легендой.