Введение в машинное обечение.
Вводятся базовые элементы питона (Python версии 3.xx) на базе ключевых библиотек относящихся к анализу данных. Конкретно речь идет о численных выражениях, переменных, циклах, построение графиков/гистограмм и статистике. Последнее, в частности, используется для ввода ключевых понятий из анализа данных: выборка, плотность распределения и подгонка модели под данные.
Это предварительная версия! Любые замечания приветсвуются.
Вычисления как на калькуляторе
# Замечания, пояснения в коде начинаются с символа решетка и идут до конца строки. Как, например, эта строка.
# Код может быть разбит на блоки. Это например новый блок кода. При выполнеии они нумеруются.
# Можно выолнять вычисления как на калькуляторе. Доступны стандартные операции: +, -, * и /.
# Для этого выражение записывается в блоке
2.0 + 3.5 * ( 5 - 3.)
# и наживается клавиша воспроизведения.
# Отмечу, что последнее значение блока выводится в качестве результата блока.
5/2 # Обычное деление.
# Также доступны вычислеия над целыми числами:
# целочисленное деление
5//2
(-5)//2 # Действует правило округление "вниз" -- берется нижняя граница. При деление было бы -2.5. Внизу -3.
# Можно также вычислить остаток от деления:
5%2
(-5) % 2 # Остаток всегда положительный.
(-5) % (-3) # Ну только, если сам делитель неотрицательный.
5 % (-3) # Иначе остаток будет как раз отрицательный.
# Возведение в степень задается двойной звездочкой, т.е. 3 в степени 2 записывается как:
3 ** 2
# Степень конечно может быть и дробной:
32 ** (1/5) # т.е. корень пятой степени из 32.
# Для расширения возможностей ествественно необходимо применять сторонний код, а именно библиотеки.
# В следующем блоке показано как это делается.
# Одна из самых известных библиотек numpy -- это библиотека для работы с векторами, матрицами.
# Она же применяется для вычисления стандартных функций: тригонометрических, эекспонента, логарифм, степень и др.
import numpy as np # Название numpy длинное. Данная строчка загружает библиотеку numpy и дает ей сокращенное название np.
2.2 * np.sin( 1.1 ) # Применили функцию синус (np.sin).
# Следует отметить то как функция вызывается: не сама по себе, а через префикс "np.". Последнее связано с тем, что функция
# sin из библиотеки numpy, но она была загружена в систему с сокращенным названием np. Поэтому такое обозначение.
# Ествественно в качестве аргумента функции можно исользовать не только число, но и целое выражение
np.sqrt( 4**2 + 3**2 )
# На всякий случай упомину и случай функция от фукнции:
np.arcsin( np.sin(0.5) )
Переменные
# Главная особенность понятия кодинга/программирования это наличие переменных.
# сущность позволяющая сохрнаить предыдущий резльтат, более того, его переприсвоить (сохранить новле значение).
a = 2.1 - 5.8 * np.cos( 3.5 ) # Вычисляем очередное выражение. Но главное в другом. Значение сохранается в переменную.
# Переменая как коробка с именем. В неё можно положить что-то и оно там останется пока не будет положено что-то новое.
# Результат блока поэтому не выводится -- он сохранен в переменную.
a # Можем вывести значение переменной, т.е. фактически значение которое туда было ранее помещено.
# С переменой также связан её тип : целое, вещественное число и тому подобное. Можно узнать какой тип у переменной:
type( a ) # Отмечу, что в других языках сначала объявляется переменная нужного типа, а потом присваивается значение.
# В питоне иначе. Переменная создается (если нужно) в момент присвоения ей значения.
# Последнее имеет как свои преимущества, так и недостатки. Преимущество в том, что не нужно отдельно создавать переменную,
# недостаток в том, что система не будет следить за правильностью присвоения значения переменной, т.е.
# переменной, которая ранее хранила матрицу, можно присовить просто число, а то и вообще строку некого текста.
# Итого, в переменую сохранаяется как значение, так и её тип.
a * 2 - 10 # Главное, что можно выполнять вычисления с переменными. Вданом случае используется переменная a.
# Вместо переменной в выражение будет подставлено её значение и выражение вычислиться как обычно.
a * 3 + 5; # Точка с запятой блокирует вывод вычисления. В данном случае получается, что данной строчка бессмысленна.
# Если попытатся использовать не существующую переменную, то компилятор будет ругаться:
5 + n # Он укажет, что такая переменая не задана.
a = 3 + 1 # Не менее важно и, что переменной можно, ествественно, присвоить новое значение выражения.
# Если переменная ранее не существовала, то она создается.
# Если она уже существовала, то она забывает свое прошлое значение.
a #Это строчка чтобы вывод все-ткаи увидеть значение переменной a.
bb = a * 2 # Переменных ествественно может быть много. Их имена могут быть длинными, но с некими огрничениями.
bb # Но не будем об этом.
a = a + 1 # Одну и туже переменную конечно можно использовать и справа и слево от рвенста. Важно понимать:
# Сначала вычисляется правая часть. Потом присваивается левой, где подразумевается переменная.
a
# , т.е. в левой части должна быть перемена
a + 1 = a + 2 # В левой части не должно быть выражения. Ну разве что какого-то хитрого...
# Иначе будет ошибка.
# Блок можно выполнять нескоко раз, для этого его нужно щелкнуть и заново нажать кнопку проигрывателя.
# Попробуй это сделать с блоком содержащем строчку "a = a + 1"
# При каждом новом выполнении значение блока будет менятся.
# Отмечу, что при присвоении блок забывает не только свое значение, но и свой тип.
# Например, сейчас тип у переменной a целочисленый.
a
# Это определяется отсутвием точки (запятой). Но можно это проверить и явно спросив это у системы:
type( a )
# Но если перейти к вычисления с действительными числами, то тип сразу изменится.
a = 25 ** 0.5 # хотя в даном случае значение будет тем же. Но это чистое совпадение.
a
# Повторим запрос типа у системы.
type( a )
# Итого, в первом случае тип был целочисленым (int), а во втором вещественный (плавающая точка, float)
# Важно понимать, что система как в таких простых случаях, так и в более сложных ругатся не будет.
# Она такое присвоение со сменой типа разрешит (во многих других языках так нельзя).
# Это важно, когдя появятся более сложные выражения.
# Подчеркну ещё раз, что в переменную записывается значение, а не выражение, которое это значение выдало.
# Покажу это на характерном прмере.
a = 5 # Присвоеим переменным значения.
b = a * 2
a, b # Выведем значения переменных.
a = -3 # Зададим новое значение для переменной a.
b # Изменится ли значение перменной b? Конечно же нет!
# Прежде чем переходить к самому главному объекту языка Python покажу как можно присваивать значения в паралель.
a, b = 5 + bb, 6 - np.sin ( 1)
# По аналогии чтобы вывести значения переменных пишем:
b, a # Специально в другом порядке.
# Убедись, что ты понимаешь результат.
Списки -- основной объект питона!
# Вычисления над отдельными числами это хорошо, но нам понадобиться список чисел. Хотябы чтобы хранить наши данные.
# Такой тип объекта подддерживает добавление новых элементов в его конец, а также извлечение и удаление подсписков.
[4, 8, 15, 16, 23, 42] # Создаем список из 4 элементах.
[ 2 + 5, 7 - 9]
# Чтобы были понятны принципы работы с ним, нужно его все-ткаи присвоить переменной.
l = [4, 8, 15, 16, 23, 42] # Присвоили переменной l списк
type( l ) # Выведем его тип. Видно что он отличается от ранее используемых числовых типов.
l[3] # Число в квадратных скобках указывает на индекс списка. Внимание, элементы списка нумеруются с 0.
type( l[3] ) # Но, тип самого элемента списка ествественно свой, числовой.
len( l ) # Можно узнать размер списка.
l[7] = 2 # Индекс должен соответствовать существующему элементу списка. Иначе будет ошибка.
l[3:5] # Можно указывать диапазон индексов: от и до. Причем, "до" невключительно, т.е. в данном случае индексы: 3 и 4.
l[3:100] # При задании интервала индесов диапазон может выходить за пределы списка.
# В таком случае диапазон будет укорочен. Ошибкой это считаться не будет.
l[2:] # Если после двоеточия числа нет, то значит нужно идти до конца списка.
l[:4] # Если числа нет до двоеточия, то значит новый список начинется с начала данного.
# Итого, если не указывать явно одну из границ диапазона индексов, то берутся по масимому.
l[:5], l[3:] # т.е. илибо с начала либо до конца.
l[1:100:2] # В расширенном варианте можно указать шаг увеличения индекса. В данном случае он равен 2.
l[1::2] # Ествественно в расширенном варинте тоже можно опускать числа. Смысл будет прежний.
# Отмечу, что элементы списка можно нумировать и с конца. Для этого используются отрицательные индексы.
l[-1] # -1 соответствует первому элементу с конца, т.е. последнему.
l[-2] # Второй элемент с конца.
# Такие элементы также можно использовать при задании диапазона индексов.
l[:-2] # В данном случае, извлекутся все элементу кроме двух посдних.
l[::-1] # Шаг тоже может быть отрицательный. В даном случае получится список в обратном порядке.
l[2] = 1 # Можно присловить другое значение элементу списка.
l # Выводим значение переменной l, т.е. списк чисел
# Присвоить можно и диапазону значений сразу:
l[1:3] = 3, 7 # Список числе должен соответствовать по количеству.
l
l.append(3) # Список на то и списк, что к нему можно добавить элемент в его конец.
l
len( l )
l.remove(3) # Вырезаем (удаляем) первый элемент из списка равные данному.
l
del l[2:5] # Удаляет элемент с данным индексом (ами)
l
del l[1], l[1] # Удаляем один или более разрозненных элементов. подумайте почему индекс указан ожин и тот же.
l
l.append( -5 )
l.append( 7 )
l
# Списки нужны для хранения данных. Если мы захотим все числа умножить на два, то возможно мы бы написали:
l * 2
# И увидели бы результат, который скоре всего не соответсвует нашим ожиданиям.
# Дело в том, что список является объектом сам по себе. Поэтому он имеет свои смысловые нагрузки на операции.
# Например, + объединяет списки (конкатенация), точнее она ставит один список (второй) в конец другому (первому).
[1, -5] + [-7, 5, 18]
# Операции умножение соответствует многократной конкатенации (т.е. повтору) самого себя:
[2, 5] * 3
Списки: map, reduce, filter
# Возвращаясь к нашему желанию выполнить опредленное действо для всех элементов. Для это используются циклы: for.
# Сама конструкция является генератором списка, т.е. она по списку строит список (того же размера!).
[ x * 2 for x in l] # По умному это назвается map, т.е. применение отображения к каждому из элементов списка.
x * 2 for x in l # Так нельзя. Обрати внимание на квадратные скобки в примере в прошлом блоке.
l
[ x for x in l if x%2 == 1 ]
# Но прежде чем углубляться в эту компактную конструкцию рассмотрим дла начала её более полный вариант.
summa = 0 # Обявили переменную summa для хранения суммы всех элементов списка.
# Конструкция for позволяет взять каждый элемент списка поочередно и совершить некое действие над ним.
# В данном случае элементы берутся их списка l и временно сохраняются в переменную x.
for x in l: #l -- мы ранее определяли. Двоеточее в конце строки обазательно.
summa = summa + x # Каждый элемент добавляется к общей сумме. Операция делается постепенно. Отступ необходим.
summa # По умному это назвается reduce, т.е. редукция, когда список сводится к "числу".
# Список, ествественно, можно задавать разными способами. Например явным. Временная переменная тоже необязательно x.
summa = 0
cnt = 0
# Цикл пробегает по элементам явно заданного списка.
for d in [2, -5, 9, 4]: # В даном примере действо для каждого элемента будет состоять из двух строчек.
summa = summa + d # По аналогии считается сумма чисел.
cnt = cnt + 1 # Но и их общее количество. Это позволит вычислить среднее значение.
summa/cnt # Выводим среднее значение элементов списка.
# Помимо map и reduce важной операцией над списками является операция filter, т.е. фильтрация объектов по некому условию.
[x for x in [1, -4, 7, 2, 9] if x%2 == 0] # Создает список состоящей их четных чисел.
# Сущесвуют ряд встроеных объектов, позволяющих увпростить процесс создания списков.
[i for i in range(20)] # Списки можно создавать циклом. Используется range объект задающий диапазон.
# В данном случае с 0 до 20 (не включительно).
# В заключении обсуждения списков отмечу, что их элементы не обязаны иметь один и тот же тип
ll = [2, "строка текста", 6]
ll
# Элемент с индексом 0 и 2 содержит
ll[0], ll[2]
# А элемент с индексом 1 являтся строчкой:
ll[1]
type( ll[1] )
'строка текста'[4] # Строка текста тоже на самом деле список элементов.
ll[1][5] # Извлекаем отдельную букву в этом списке.
type(ll[0]), type(ll[2])
# Последнее показывает, что списки могут соедржать совершенно разнородные объекты. В этом смысле они полноценные.
# Поэтому, естественно, что списки могут содержать списки.
a = [ 2, 3, [4, 5], 8] # Список из трех элементов.
a
# Нулевой, первый и третьий содержат числа:
a[0], a[1], a[3]
# А вот второй элемент сам является списком:
a[2]
# Один из способов, как вычислить значение от каждого элемента списка, было показано выше.
# Показанный способ (отображение) был явный. Он был реализован на чистом питоне без вспомогательных средств.
# Но можно вычислить функцию от каждого элемента списка инче, использую вспомогательные (библиотечные) функции.
a = np.sin( [1.1, 2.2, -4.4] )
a
# Полученый объект уже не будет списоком (см. слово array). Он преврятится в массив, т.е. array.
# Тип array с точки зрения обычных языков фактически является массивом, т.е. объектом у которого тип элементов постоянен.
# По суте идексируемая переменная (имеет тот же тип).
# Такой тип соответсвует математическим понятиям вектора, матрице. Переменая с индексом.
# Перейдем к описанию массивов.
Вычисления над векторами, матрицами
# Списки хороши, но над ними как мы помним нельзя выполнять нужные нами арфиметических операций.
# В частности, списки нельзя складывать. Точнее эта операция будет иметь иное толкование:
[1.1, 2.2, -4.4] + [2.5, 1.9, 5.1]
# Они совместяться в один список (контакенация)
# Для содания списоков чисел, которые можно скоадывать поэлементно, нужны именно вектора/массивы.
# Они из списка создаются так:
np.array( [1.1, 2.2, -4.4])
# Такие объекты, массивы, можно складывть по элементно.
np.array( [1.1, 2.2, -4.4]) + np.array( [2.5, -1.9, 5.1])
# Размер массивов должен совпадать. Иначе система выдаст соответствующую ошибку.
np.array( [1.1, 2.2, -4.4]) + np.array( [2.5, -1.9, 5.1, 7.1])
# При выводе ошибки система указала, что один массив имеет размер 3, а другой -- 4. Даже была указана их одномерность.
# Размер массива всегда можно запросить у самого объекта.
l = np.array( [1.1, 2.2, -4.4]) # Сохраняем массив в переменную l
l.shape # Запрашиваем у переменной её размер. Он хранится в переменной shape.
bb.shape # Размер имеют объекты array. Другие не обязаны давать значение на это поле.
type( l ) # Запросим и тип. Замечу, что он размер не знает.
a0 = np.array([-1, 2])
a0
a1 = np.array([3, 5])
a1
a0 * a1 # Поэлементное умножение массивов.
# Ну или в явном виде, т.е. без создания дополнительных промежуточных переменных.
np.array([-2, 7]) * np.array([ -2 , 5])
# Расширение данной идеи на матрицы.
a0 = np.array([[-1], [2]])
a0 # Отмечу, если раньше a0 был массивом, то теперь стал матрицей, т.е. поменялась размерность.
# Поэлементное умножение матриц (элемент массива является одноэлементным массивом).
a0 * np.array([ [3] , [5]]) # Не путать с матричным умножением!
# Размеры у матрицы a0 были:
a0.shape # т.е. две строки, в каждой из которых по одному элементу.
a0 = np.array([[-1, 2]]) # Можно сделать матрицу наоборот.
a0
a0.shape # Одна строка, содержащая два элемента.
# Такие матрицы тоже можно умножать по-элементно.
np.array([[-1, 2]]) * np.array([ [3 , 5]])
# Матрицы можно формировать из других матриц.
a0 = np.array( [ 3, -6 ] )
a1 = np.array( [ -1, 2 ] )
a = np.array( [ a0, a1] )
a
l[4] # Как и со списками нельзя обарщатся к эелементу, которого нет в массиве.
# Как было показано выше, массивы можно создавать по спискам, т.е. сначала делается список, а потом массив по нему.
l = [i for i in range(3, 10)] # Создаем список с 3 до 10 (не включительно) с шагом 1.
np.array( l ) # Создаем по списку массив.
np.arange(2, 20, 3) # Их также можно генерировать по готовой формуле. В данному слачае, это последовательность
# с первого числа (2) по последнее (18), но не включая его, с шагом (3).
# Существуют и другие вспомогательные функции для формирования массивов. Например функция равномерного разбиения отрезка.
np.linspace(4, 25, 5) # А эта функция создает числа с первого (4) по последнее (включительно) (25) в количестве (5).
# В отличии от списка, все элементы массива должны иметь один и тот же тип. Если объявимть так:
np.array( [1.0, "aa"] ) # то число тоже станет строчкой.
np.array( [1.0, "aa"] ) + np.array( [2.0, "bb"] ) # Сложить их как раньше не получится.
Отрисовка графика функции
# Будем строить функцию по-точечно, т.е. построем значение в каждой из выбранных точек и соеденим соседнии прямой.
# Для начала сгенерируем точки в которых будет вычислена функция, а именно равномерный на набор точек.
x = np.linspace(-5, 10, 20) # Отрисуем нужную функцию. Используем для этого 100 точек.
y = x*x -2*x +3 # Пишем выражение, которое ествественно будет вычислено в каждой точке, т.е. по-элементно.
# Точки и значения вы сформировали. Теперь будем строить график. Для этого нужна ещё одна вспомогательная библиотека.
import matplotlib.pyplot as plt # Библиотека для вывода графиков.
# Следуюшща строка позволяет выводить данные прям на этой странице в блоках. Иначе будет "всплывать" отдельное окно.
%matplotlib inline
# Отмечу, что последняя строчка относится к система jupyter, а не самому языку python.
# После загрузки библитеки в систему график по сформированным данным стротися так:
plt.plot( y ) # Значений достаточно.
# Но можно конечно добавить отсчеты по оси x и разукрасить график.
plt.plot( x, y, 'g.-') # Строит график по-точечно. g -- green (зеленая), . и - задают стиль точки и прямой.
plt.plot( x, y, 'b*--') # 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 = 10*np.sin( x ) + 25
plt.plot( x, y, 'g.-', x, y2, 'b*--')
# Ествественно, что значения можно считать "налету":
plt.plot( x, np.sin(x), 'g.-', x, x*x/100, 'b*--')
# Ввиду того, что количество точек по которым строилась кривая было небольшим, синусойда получилась угловатой.
Загрузка котировок акций
from pandas_datareader import data
import matplotlib.pyplot as plt
import pandas as pd
#tickers = ['AAPL', 'MSFT'] # Создаем список акций.
# Define which online source one should use
#data_source = 'google'
# We would like all available data from 01/01/2000 until 12/31/2016.
#start_date = '2016-01-01'
#end_date = '2017-12-31'
# User pandas_reader.data.DataReader to load the desired data. As simple as that.
#panel_data = data.DataReader(tickers, data_source, start_date, end_date)
# panel_data
#import datetime as dt
#from pandas_datareader import data, wb
#start_date = dt.datetime(1980, 1, 1)
#dat = data.DataReader('googl', 'yahoo', start_date, dt.datetime.today())
import quandl
import datetime as dt
quandl.ApiConfig.api_key = '8qNu5k13WLNT-6tRKj9M'
today=dt.date.today()
thirty_days = dt.timedelta( days=30 )
fifty_days = dt.timedelta( days=50 )
hundred_days = dt.timedelta( days=100 )
thirty_days_ago = today - thirty_days
fifty_days_ago = today - fifty_days
hundred_days_ago = today - hundred_days
#WIKI/AAPL
fb_df = quandl.get("WIKI/FB", start_date=str( thirty_days_ago ), end_date=str(today),column_index=4)
print ( fb_df ) # Facebook, цена закрытия.
fb_df # fb_df достаточно мощный объект. Он (dataframe) сам может вывести красивую таблицу. Объект из Pandas.
fb_df.plot() # А также сам себя может отрисовать в виде графика.
fb_df.iloc[:,0].values # Переводим данные в массив numpy
# Для дальнейшего возьмем более большой объем.
fb_df = quandl.get("WIKI/FB", start_date=str( hundred_days_ago ), end_date=str(today),column_index=4)
data_fb = fb_df.iloc[:,0].values
data_fb.shape
# Можно вызвать отрисовку графика ничего не указав кроме самих данных.
plt.plot( data_fb ) # Котировки Facebook.
Генерирование случайных чисел.
# Существуют специальные (библиотечные) функции для создания случайных чисел.
# Библиотека Numpy уже загружена и переименована как np. Поэтому возможен код:
# В данном случае формируем числа согласно нормальному распределению.
np.random.randn( 4 ) # В скобках указывается размер массива.
a = np.random.randn( 4, 3) # Можно размеры и по другим осям. В данном случае, будет матрица 4x3.
a
a.shape
# np.random является модулем, т.е. он содержит набор функций со схожим смыслом.
# Равномерное распределение задается просто функцией rand из модуля np.random
np.random.rand(4)
Слуйчайные величины и использование компьютера по назначению
gen_data = np.random.randn( 10000 ) # Вывода нет потому что мы присвоили результат переменой.
gen_data.shape
hist = plt.hist( gen_data, 50); # Строит гистограмму по заданному массиву.
# Изучим переменую hist, которая соответствует гистограмме.
type( hist )
# Она является парой (tuple)...
type( hist[0] ), type( hist[1] )
# состоящей из двух массивов (numpy.ndarray).
hist # Смотрим содержимое переменной hist.
h = np.histogram( gen_data, 50 ) # Вызовем функцию, которая вычисляет только гистограммы, она её не рисует.
h
# Видим, что результат совпадает. Скоре всего код функции plt.hist вызывает функцию np.histogram.
Численная статистика
# Изучим Хи-квадрат численно.
gen_data = np.random.randn( 1000, 5) # 1000 раз по 5 нормальных распределений.
# Убедимся в правильности размера массива (матрицы).
gen_data.shape # В скобках перечислены размер массива вдоль каждого из измерений.
gen_data[0].shape, gen_data[999].shape
a = np.array( [[2, 3], [-3, 4]])
a * a # Исходя из материала выше таким способом можно возвести в квадрат, по-элементно.
# В Numpy есть ряд функций, которые выполняют операцию reduce.
np.sum( a ) # Например, вычисляет сумму массива a: 3 + -6 + -1 + 2.
a = np.array( [[[2, 3], [-3, 4]], [[-5, 7], [-9, 11]] ])
a[:, 1, 0]
# Такой вариант вызова выполняет операцию для всех элементов массива как для единого массива, т.е. без учета размерности.
# Но можно и указать вдолб какой именно размерности делать вычисления.
np.sum( a, axis = 0) # Вдоль нулевой делаем и сохраняем результат в другие размерности ( матрица 2 на 2).
# Например, -3 + -9 равно 12 сохранено в элемент матрицы 1, 0.
a[0, :, 1]
np.sum( a, axis = 1) # Суммируем вдоль первой и сохранаяем результат в оставшуюся размерность.
# Например, 3 + 4 равно 7 сохранено в элемент матрицы 0, 1.
# Теперь продолжим наши изыскания...
sq_data = gen_data * gen_data # Массив состоящий их квадратов нормального распределения.
sq_data.shape
gen_data2 = np.sum( sq_data, axis = 1 ) # сумируем вдоль оси с индекосм 1 (нумирация начинается с 0).
gen_data2.shape # получаем массив состоящий из суммы 5 квадратов номального распределения
# т.е. выборка из Хи-квадрат с параметром 5.
hist = plt.hist( gen_data2, 50); # Сторим гистограмму. Она должна напоминать гистограмму Хи-квадрат.
11 < 5 # Помимо арифметических операций существуют и логические. В частности, операции сравнения.
# Лож (False) с точки зрения чисел равна 0,
# а истина (True) 1.
int( 12 < 15 )
# Тогда можно строить такие гибридные выражения, где суммируются логические выражения.
( 13 < 12 ) + ( 13 < 14 ) + ( 13 < 16 ) # В данном случае мы посчитали скольких чисел строго больше 13.
cnt = sum( x < 7 for x in gen_data2 ) # Данная строчка посчитает сколько числе в выборке меньше 7.
cnt
cnt / gen_data2.shape[0] # Поделив на общее количество чисел, получаем значение распределения.
import scipy.stats as models
#?models.chi2
# Можно воспользоватся явной функцией (chi2), которая использует формулу.
models.chi2.cdf( 7, 5 ) # Первое число указывает точку (7), воторое значение параметра (5) распределения.
# Замечаем, что значение близко к рассчетному.
models.chi2.cdf( 7, 5, 0, 1 ) # Более полный набор параметров включает смещение (0) и масштаб (1).
# models.chi2.cdf.__doc__ # Так можно вызвать документацию по немонятной функции.
models.chi2.fit( gen_data2 ) # Можем найти параметры, которые наилучшим образом соответсвуют выборочным данным.
it = models.chi2.interval( 0.95, 5 ) # Строим доверительный интервал.
it
models.chi2.cdf( it[0], 5 ), 1.0 - models.chi2.cdf( it[1], 5 ) # Проверяем доверительный интервал.
models.chi2.cdf( it[1], 5 ) - models.chi2.cdf( it[0], 5 ) # Проверяем его иначе.
sum( it[0] <= x and x <= it[1] for x in gen_data2 ) / gen_data2.shape[0] # Проверяем численно.
models.chi2.mean( 5 ) # Вычисляем матожидание распределения по формуле.
np.mean( gen_data2 ) # Вычисляем выборочное среднее, т.е. по данным.
ДЗ0) Выбрать ожну из статистик и выполнить сравнение численного анализа с известными формулами из учебника
data_fb.shape
per = data_fb[1:]/data_fb[:-1] -1 # Процентное изменение из о дня в день.
per
per.shape
plt.hist( per, 10); # Строим распределение процентного изменения изо дня в день.
ДЗ1) Выбрать эмитент, некое условие и построить распределение некого другого условия.
models.chi2.fit( per )
models.norm.fit( per )