Математический практикум по Питону.
Pandas таблицы. Столбцы. Series. Серии.
Это предварительная версия! Любые замечания приветсвуются.
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as models
Загрузка через csv
import 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) ) # Здесь мы должны знать, что -2 это то что нужно.
plt.plot( close )
plt.ylabel( 'Цена рубли')
plt.xlabel( 'День начиная с 2018 г')
plt.legend(['Цена ВТБ']) # Соответсвенно помнить название жмитента.
Можно было создать класс, который все это помнит. Но зачем, если уже за нас это сделали?
Для более аккуратной работы с данными есть модуль pandas.
Подключаем pandas
import pandas as pd # Загружаем модуль pandas под псевдонимом pd.
Начнем с загрузки простого файла, т.е. небольшого развера.
mtlr_df = pd.read_csv('MTLR_190101_190110.txt', sep = ';') # Считываем данные из файла.
mtlr_df
Хм.. Как мы видим у колонок появились названия. Но они какие-то не те.
# headerr = None указывает, что заголовка в файле нет.
mtlr_df = pd.read_csv('MTLR_190101_190110.txt', sep = ';', header = None)
mtlr_df
Теперь колонки получили иемя, правда цифровое, т.е. они пронумированы.
Загрузим данные заново (например с сайта finam), но теперь убедися что стоит флажок у "Добавить заголовок файла"
mtlr_df = pd.read_csv('MTLR_190101_190110_header.txt', sep = ';') # Считываем данные из файла включая заголовок.
mtlr_df
Как мы видми у колонок появились правильные названия.
Изменение именования колонок
В любом случае мы возможно хотим переименовать название колонок. Либо пронумерованных, либо англоязычных.
mtlr_df.columns # Колонки хранятся в данной переменной таблицы пандас.
len(mtlr_df.columns) # Количество колнок.
mtlr_df.columns = ['Эмитент', 'Процент', 'Дата', 'Время', 'Открытие', 'Максимум', 'Минимум', 'Закрытие', 'Обьем']
mtlr_df.columns # Не список уже!
mtlr_df # Появились нормальные названия колонок.
# Разумеется нужно следить за количеством колонок.
mtlr_df.columns = ['Открытие', 'Максимум', 'Минимум', 'Закрытие', 'Обьем']
Таблица
Вообще, что за объект, который мы загрузили?
type(mtlr_df) # Объект типа DataFrame
my_close = mtlr_df['Закрытие'] # Можно из таблицы извлесь колонку.
my_close
type(my_close) # Тип у колонки Series, т.е. серия.
И вот сначала мы обсудим тип Series, т.е. что можно делать с колонкой.
Перейдем к обсуждению объекта Серия (Series) модуля Pandas.
Создание, удаление
new_ser = pd.Series([50, 100, 75, 23]) # Создаем Серию по списку.
new_ser
type( new_ser )
Пандас достаточно мудрен, чтобы выбрать наилучший тип для данных. Так если все данные целые, то и итоговые тип будет целым.
new_ser.dtype # Обращу внимание, что тип целочисленный. В этом смысле Пандас умничает.
Если хотя бы одно число действительное, то и тоговая таблица будет такой.
new_serf = pd.Series([50, -100, -75., 23])
new_serf # В данном случае тип float64, т.е. действительные.
new_sers = pd.Series(['Москва', 'Питер', 'Нижний', 'Новосибирск']) # Можно хранить и произвольные объекты.
new_sers # Например, строчки, но тогда тип будет object
В предыдущих примерах первая колонка добавлялась автоматически. Она называется индексом. Поумолчанию она цифровая, т.е. индекс целочисленный. Но можно идекс задать и в явном виде.
cptls = pd.Series([1,4,7,8], index=['Москва', 'Питер', 'Нижний', 'Новосибирск'])
cptls # Индекс к тому же и не числовой.
Просмотр
cptls.values
cptls.index
list( cptls.iteritems() )
cptls.keys()
Операции как над массивами
my_close # Еще раз его выведем.
my_close*2 # Можно оперировать как с массивом из Numpy.
new_serf.abs() # Взяли абсолютную величину от всех значений.
my_open = mtlr_df['Открытие'] # Извлечем ещё и цену открытия.
my_open
avg = 0.5*(my_close + my_open) # Нашли среднее двух колонок.
avg
type( avg ) # Мы по прежнему остаемся в рамка Серии.
my_open
my_close.corr(my_open) # cov
Интегральные операции
my_close.mean(), my_open.max()
Как над списками
my_app = my_open.append( my_close ) # Напомню что в array нельзя добавлять новые элименты.
my_app # Но что произошло с индексом? Он тоже добавился.
cptls
del cptls['Нижний']
cptls
Значения можети не быть
nums = [5, 6, None, 10] # Список значений и пустота.
nums
a = np.array( nums )
a # Тип object!
a*2 # Такое не прокатит.
nums_ser = pd.Series( nums )
nums_ser # Тип всеравно float64!
nums_ser * 2 # Nan проигнорирован.
nums_ser.count() # Количество реальных данных.
nums_ser.mean() # Правильно считает среднее. 5+6+10=21 => 21/3 = 7.0
nums_ser.describe()
nums_ser.dropna() # Оставляем строки со значением.
nums_ser.fillna(-1)
Принадлежность
cptls
'Москва' in cptls, 'Британия' in cptls
Обновить значение
cptls['Екатеринбург'] = 55
cptls
cptls['Нижний'] = 11
cptls
cptls.get('Владивосток', 'не известно')
#cptls.
Как у массива
new_ser[2] # Для Серии действуют теже правила как и для массива. Важно что это число!
new_ser[1:-1] # Извлечь с первого по невключительно последний элемент. Важно что это Серия!
Последние два примеры важны. Если не учитывать, то код может сломаться.
my_app # Помним про данную Серию?
my_app[2] # Раз индекс продублирован, то два элемента, т.е. опять Серия.
Не числовой индекс
cptls
cptls['Москва']
Педантизм
tt = pd.Series(['a','b','c','d'], index=[2,1,3,0])
tt
tt[0] # Что будет? Что хотели?
tt.iloc[0] # Является номером строки.
tt.iloc[2]
tt.loc[2] # Всегда соответствует названию элемента индекса.
#new_cptls = pd.Series(['Москва','Париж','Рим','Берлин'], index=['Россия','Франция','Италия','Германия'])
cptls[1] # Вернет как номер строчки.
cptls.iloc[1] # Тоже самое.
cptls.loc['Москва']
cptls.loc[1] # Так нельзя. Нужно обазательно элемент индекса, т.е. название.
cptls.loc['Питер':]
dd = pd.Series( [1, 2, 1, 4], index=[11, 15, 20, 25])
dd.iat[2]
Атрибут
cptls.Москва
Булевские
a = pd.Series([55, 33, 88, 11])
b = pd.Series([44, 22, 100, 22])
a<b # По аналогии с другими операциями на сериями.
a[a<b] # Используем Серию в качестве фильтра.
Изучим что есть
my_app
my_app.index
a.index
Новый
rr = my_app.reset_index() # Для создания нового индекса.
rr # Теперь выглядит как таблица. Старый индекс превращен в колонку.
rr.index.is_unique
Нужен новый индекс. По хорошему индекс должен однозначно определять строчку.
type(rr) # И действительно это DataFrame. Иногда это нужно, но не сейчас.
rr = my_app.reset_index( drop = True) # Для затирания старого "индекса"
rr
type(rr) # Да это Серия.
rr[2] # Теперь однозначность опять есть.
a
a.index = ['a', 'b', 'c', 'd'] # Можно и так.
a
Сортировка
cptls
cptls.sort_index()
cptls.sort_values()
cptls.rank()
Произвольные вычисления
a
a.map( lambda x: x*x )
a.map( {55:'a', 33:'b', 88:'d'} )
a
#a.apply( {55:'a', 33:'b', 88:'d'} )
aa = pd.Series([1,5,-2])
bb = pd.Series([-2,7,3, 4])
aa
bb
aa.combine(bb, lambda x, y: x*y)
aa = pd.Series([1,5,-2], index=[2,3,4])
aa
aa.combine(bb, lambda x, y: x*y)
Группы
grades = pd.Series([30,20,25, 55, 60, 40, 30, 40, 45],
index=['Максим','Максим','Максим',
'Аня', 'Аня', 'Аня',
'Дима', 'Дима', 'Дима'])
grades
grades.groupby(by=grades.index).mean()
Поиск
my_open.where( lambda x: x>74 )
#my_open.asfreq(0.5)
my_open.c
my_open.groupby( lambda x: int(x) )
my_open
my_open.reindex(index=['день один', 'день два', 'день три', 'день четыре','dd','aa','aaqq'])
tmp_col = my_open.copy()