Так как на предыдущем майноре вам не успели рассказать про рandas
, matplotlib
, numpy
, то придется вас спасать.
Дисклеймер
Мы очень ограничены во времени и естественно стать гуру pandas
, numpy
, matplotlib
и других модулей за такой краткий срок у вас не получится. Этому может способствовать только постоянная практика: наши домашние задания, онлайн курсы по анализу данных в python
, видео лекции, ваша курсовая работа (?). Я постараюсь изложить основную суть. Для всего, что я не успею - можно спрашивать меня, google или RTFM.
Начнем.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('ggplot')
plt.rcParams['figure.figsize'] = (16,8)
NumPy (short for Numerical Python) - это эффективная библиотека для работы с числовыми массивами и матрицами.
Нам с вами очень важно уметь работать с этими структурами быстро по крайней мере по двум причинам:
Основа numpy
- это array
(массивы). В отличие от списков или кортежей, элементы массива должны быть одного типа. Такая жертва оправдывает скорость и низкие затраты на хранение информации, которую дает numpy. Массивы же, в свою очередь, используются во многих других библиотеках python
для анализа данных.
Массив можно создать из списка.
arr = np.array([1,3,4,7,12])
arr
arr = np.array([1,3,4,7,12.0])
arr
arr = np.array([1,3,4,'7',12.0])
arr
Но наиболее распространены создания "с нуля"
arr = np.arange(0, 12, 0.7) # расширение функции range()
arr
arr = np.linspace(0, 12, 20) # равномерно распределенные 20 чисел от 0 до 12
arr
# Массив из "1"
arr = np.ones(7)
print arr
arr = np.ones(7, dtype=int)
print arr
# Массив из "0"
arr = np.zeros(7)
# Массив из чего хотите
arr = np.full(7, np.exp(1), )
arr
A = np.array([[3, 1, 4], [1, 5, 9], [2, 6, 5], [4, 1, 1]])
print A.shape
print A.size
print A.ndim
Форму массива (shape
) можно менять, но так, чтобы это согласовывалось с его размером (size
)
A = A.reshape((6,2))
A
A = A.flatten()
A
#!!!
# Это не тоже самое, что A = A.reshape((1, 12))
A = A.reshape((3,-1))
A
# -1 как бы означает, "сделай первую размерность равную 3,
# а все остальное запихни во вторую, если получится
A.T # Транспонирование матрицы
Довольно стандартная и интуитивно понятная
arr = np.arange(0, 19, 3)
arr
arr[3]
arr[:3]
arr[3:5]
arr[::3] #?!
arr[-2:] #?!
Задание
Догадайтесь, как вывести массив в обратном порядке?
## Your code here
print arr>10
print arr[arr>10]
arr[[1,3,2]]
На многомерные массивы (матрицы) все распространяется точно также.
A = np.random.randint(0, 20, (5,6))
A
# Небольшое дополнение
print A[:, 2]
print A[2, :]
A[A>5]
Задание
Задайте случайную матрицу размера (5,7)
c числами от 5 до 34, такую что на каждой строчке числа поледовательно возрастают на 1. Первая строчка начинается с 5 и заканчивается 10, вторая начинается с 11 и тп.
## Your code here
a = np.random.randint(0, 10, (2, 5))
b = np.random.randint(0, 10, (2, 5))
print a
print b
A = np.r_[a,b]
A
A = np.concatenate((a,b), axis=0)
A
A = np.c_[a,b]
A
A = np.concatenate((a,b), axis=1)
A
Тут все тоже довольно просто
arr = np.arange(1,6, dtype=float)
arr
1/arr
arr * 2
arr // 2
bar = np.arange(6,1,-1)
bar
arr + bar
arr * bar
arr ** bar
# Матричное умножение (скалярное произведение)
arr.dot(bar)
В numpy
реализовано много математических функций
np.log(arr)
np.sqrt(arr)
Задание
Задайте два случайных массива $a$ и $b$ одинаковой длины.
Вычислите следующие расстояния между массивами:
## Your code here
Задание
Выполните загрузку данных, как указано ниже (может занять время).
Выберите случайную строчку из данных. Найдите другую "ближайшую" (по евклидовому расстоянию) к ней строчку и выведите её.
from sklearn.datasets import fetch_olivetti_faces
faces = fetch_olivetti_faces()
X = faces.data
## Your code here
Массивы можно аггрегировать - считать среднее значение, медиану, моду, максимум, минимум, сумму и тп
arr = np.random.rand(11)
arr
print np.mean(arr)
print arr.mean()
arr.sum()
print 'максимальное значение %.4f находится на %d позиции' % (arr.max(), arr.argmax())
# аналогично argmax, есть argmin и argsort
np.median(arr)
np.percentile(arr, [15, 85])
Задание
Сгенерируйте такой случайный вектор (np.random.rand()
) длины 10, что сумма его элементов равна 2.
## Your code here
Задание
Сгенерируйте случайный вектор (np.random.rand()
) длины 100. Выполните такое преобразование массива, что
## Your code here
Задание
Сгенерируйте случайный вектор длины 20 из целых чисел на интервале [0,50]. Оставьте в нем только те элементы что меньше 5 персентиля и больше 95 персентиля
## Your code here
Что касается матриц - то в них все примерно тоже самое.
A = np.random.rand(3,5)
A
A.mean()
Задание
Сгенерируйте случайную матрицу размера $5 \times 6$ из целых чисел на интервале [0,50]. Выведите столбец с содержащий максимальное значение во всей матрице.
## Your code here
В numpy
есть специальные обозначения для бесконечности и пропущенных значений.
В реальном мире приходится работать с очень "грязными" данными и частенько бывает, что какие-то измерения, значения признаков и тп просто отсутствуют. К этому надо быть готовым
np.log(0)
np.log(-1)
np.nan
arr = np.random.rand(10)
idx = np.random.randint(0, 10, 4)
arr[idx] = np.nan
arr
# проверяем, является ли значение пропущенным
is_nan = np.isnan(arr)
# проверяем, есть ли хотя бы одно пропущенное
np.any(is_nan)
# проверяем, есть ли хотя бы одно пропущенное
np.all(is_nan)
Аггрегация массивов с пропущенными значениями может выполняться без учета np.nan
print np.nanmean(arr)
print np.mean(arr)
Задание
Замените все пропущенные значение средним
## Your code here
Загрузите файл 1 и файл 2 в папку с тетрадкой. С помощью функции loadtxt
в модуле numpy
загрузите табличные данные одного из файлов. Присвойте y = D[:,0] а X = D[:, 1:].
Сейчас мы воспользуемся одной магической формулой и построим модель линейной регрессии. Откуда эта формула берется мы узнаем на следующих занятиях.
Модель линейной регрессии в матричном виде выглядит так: $\hat{y} = X\hat{\beta}$, где
$$ \hat{\beta} = (X^T X)^{-1} X^T y $$Остатки модели рассчитываются как $$ \text{res} = y - \hat{y} $$
Итак, еще раз:
# load data
D = np.loadtxt('tutorial_dataset_1.csv',
skiprows=1,
delimiter=',')
Модуль pandas
существенно упрощает исследование табличных данных в python
. Работа в нем во многом напоминает работу с таблицами в SQL с тем отличием, что в pandas
тебе не хочется рвать волосы на голове это делать гораздо удобнее, и в нем заложены некоторые дополнительные инструменты по работе с данными.
Основными структурами являются Series
и DataFrame
.
Series
– это проиндексированный одномерный массив значений. Он похож на простой словарь типа dict
, где имя элемента будет соответствовать индексу, а значение – значению записи.
Задать Series
можно многими способами, например с помощью массива:
ser = pd.Series(np.random.rand(5))
ser
Колонка слева - это (строчный) индекс - некоторая нумерация записанных значений
ser.index
ser.values
# Достучаться до одного значения можно так
ser[1]
# Можно так - это обычная интервальная индексация в python.
ser[0:2]
Но про то, как улучше находить нужные вам значения - чуть позже.
Индексом может быть что угодно, например:
ser = pd.Series(np.random.rand(5), index=['m', 'i', 'n', 'o', 'r'])
ser
ser['r']
ser['n':'o']
Индексация в pandas
может временами может показаться запутанной
ser = pd.Series(np.random.rand(5), index=[1,3,5,6,9])
ser
ser[3]
ser[3:5] #?!
Поэтому придумали разные операторы для индексирования, чтобы можно было явно указать, когда вы хотите использовать значения индекса, а когда позицию.
ser.loc[3:5, ]
ser.loc[:, ]
idx = ser > 0.5
ser.loc[idx, ]
ser.iloc[3:5, ]
Их же используйте для присваивания!!
idx = ser > 0.5
ser.loc[idx, ] = 0.5
ser
DataFrame
— это проиндексированный многомерный массив значений, соответственно каждый столбец DataFrame
, является структурой Series
. Индексирование в DataFrame
ровно тоже, что и в Series
, с тем отличием, что добавляется второе измерение.
df = pd.DataFrame(np.random.randn(10, 3),
index=range(10),
columns=['A', 'B', 'C'])
df.head() # выводит первые 5 (по-умолчанию) строк таблицы
print df.index
print df.columns
df.loc[1:3, ['A', 'B']]
df.iloc[1:3, 0:2]
DataFrame
тоже можно транспонировать!
df.T
# Краткая описательная статистика
df.describe()
# Кстати, это тоже DataFrame
Аггрегация в DataFrame (по-умолчанию) происходит по стоблцам
df.mean()
df.A.mean()
Перевод данных в нужный тип
df.A = df.A.astype(int)
df.head()
print 'Количество уникальных значений в столбце А = %d' % df.A.nunique()
print 'Самые большие значения в стоблце B :'
print df.B.nlargest(2)
# Гораздо быстрее, чем df.B.sort(ascending=False).iloc[:2]
Важно следить за данными, которые у вас хранятся в DataFrame
df.dtypes
df.loc[0, 'A'] = 'lalaley'
df.head()
df.A
Удаление\добавление строк\столбцов
df.head()
df.drop(0, axis=0)
# Пока df не изменился !
df.drop('A', axis=1)
df.loc[:, 'D'] = np.nan
df.head()
df.loc[10, :] = 0
df.tail()
В 1968 году была опубликована статья под интригующем названием Correlation of Performance Test Scores with Tissue Concentration of Lysergic Acid Diethylamide in Human Subjects.
К статье приложен небольшой набор данных, состоящий из 7 наблюдений
df = pd.read_csv('drugs-and-math.csv',
index_col=0,
sep=',')
df.head()
print df.shape
print df.columns
print df.index
Таблица уже отсортирована по колонке Drugs - отсортируем по колонке Score
df = df.sort_values('Score',
ascending=False)
df.head()
df.describe().T # Иногда так лучше
df.plot(kind='box')
df.Drugs.hist()
# df.plot(x='Drugs', y='Score')
df.plot(x='Drugs', y='Score', kind='scatter')
Мы явно видим тенденцию..
Загрузите датасет с информацией о характеристиках вина и его качестве.
## Your code here
## Your code here
Какие признаки больше всего влияют на целевую переменную?
## Your code here
Создайте новый столбец quality_cat
, которая будет иметь значение "good"
если quality > 5
и "bad"
- иначе.
## Your code here
Нарисуйте гистрограммы признака alcohol в группах с quality_cat == "good"
и quality_cat == "bad"
.
## Your code here
Можете ли вы придумать правило для классификации вина на хорошее и плохое по рисунку выше? Пусть это будет нашей первой моделью)
Напишите функцию brute_clf_train()
которая бы перебирала пороговое значение по признаку alcohol
и находило бы "оптимальное" (кстати, что значит оптимальное?)
## Your code here
Напишите функцию brute_clf_predict()
которая бы по значению признака alcohol
и найденному выше порогу говорила какое качество у вина.
А заодно выводила бы количество "ошибок" на текущем наборе данных
Проверим, как обобщается наша модель на другие данные.
## Your code here