Примеры задач
Обозначения
Обучающая выборка В зависимости от задачи $X = (x_i, y_i)_{i=1}^l$, если известный $y_i$
Признаки Описание объекта $x = (x^1, ..., x^d)$ (вектор)
Алгоритм Функция, предсказывающая ответ для любого объекта
Функция потерь Способ измерить корректность ответов алгоритма
Функционал качества Способ измерить качество алгоритма на выборке
Библиотека языка Python, позволяющая [удобно] работать с многомерными массивами и матрицами, содержащая математические функции.
import numpy as np
Массив — тип данных (numpy.array). Способ задания — передать последовательность в качестве первого параметра
print np.array([1,2,3,4,5,6])
print np.array([[1,2,3], [4,5,6]])
[1 2 3 4 5 6] [[1 2 3] [4 5 6]]
Обратите внимание, что следующий код работать не будет:
print np.array(1,2,3,4,5,6)
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-3-f4b008f13bf6> in <module>() ----> 1 print np.array(1,2,3,4,5,6) ValueError: only 2 non-keyword arguments accepted
При необходимости можно явно указать тип хранимых значений (dtype):
print np.array([1,2,3,4,5,6], dtype=float)
[ 1. 2. 3. 4. 5. 6.]
Иногда бывает полезно быстро обратиться к документации функции, чтобы посмотреть параметры, которые она принимает на вход. Для этого в ipython-notebook встроен удобный интерфейс, а именно следующая команда:
?np.array
покажет docstring данной функции ___
Функции numpy обычно возвращают np.array
a = np.arange(0, 10, 0.5)
a
array([ 0. , 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5, 5. , 5.5, 6. , 6.5, 7. , 7.5, 8. , 8.5, 9. , 9.5])
np.arange - аналог range в Python, которому можно передать нецелочисленный шаг
Могут возникнуть более сложные ситуации: когда вам необходимо разделить некоторый отрезок на $n$ частей. Зная функцию arange и вычислив правильный шаг можно это сделать, однако это не всегда удобно. Поэтому в numpy есть функция linspace:
np.linspace(0, 10, num=13)
array([ 0. , 0.83333333, 1.66666667, 2.5 , 3.33333333, 4.16666667, 5. , 5.83333333, 6.66666667, 7.5 , 8.33333333, 9.16666667, 10. ])
?np.linspace
Чтобы посмотреть какой размер имеет наш массив, можно воспользоваться полем shape:
a.shape
(20,)
Обратите внимание на различие в строчках ниже. Если вы хотите задать матрицу размера n x m, то в каждой последовательности должно быть строго m элементов.
np.array([[1,2,3], [4,5,6], [1, 1]]).shape
(3,)
np.array([[1,2,3], [4,5,6], [1, 1, 1]]).shape
(3, 3)
Чтобы узнать размерность вашего массива, можно воспользоваться полем ndim:
a.ndim
1
Если необходимо изменить размеры массива, в numpy есть функция reshape:
b = np.reshape(a, (2, 10))
b
array([[ 0. , 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5], [ 5. , 5.5, 6. , 6.5, 7. , 7.5, 8. , 8.5, 9. , 9.5]])
b = np.reshape(a, (10, 2))
b
array([[ 0. , 0.5], [ 1. , 1.5], [ 2. , 2.5], [ 3. , 3.5], [ 4. , 4.5], [ 5. , 5.5], [ 6. , 6.5], [ 7. , 7.5], [ 8. , 8.5], [ 9. , 9.5]])
При этом всегда можно перейти к "одномерному" представлению массива, а именно создать одномерный массив из всех элементов матрицы с помощью одной функции:
b.flatten()
array([ 0. , 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5, 5. , 5.5, 6. , 6.5, 7. , 7.5, 8. , 8.5, 9. , 9.5])
b.shape
(2, 10)
b.ndim
2
Для того чтобы понять, какого типа элементы элементы в массиве, есть поле dtype
b.dtype.name
'float64'
Также можно создавать array специального вида при помощи функций zeros(), ones(), empty(), identity() (тип данных по умолчанию — float64):
np.zeros((3, 3))
array([[ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.]])
np.ones((3, 3))
array([[ 1., 1., 1.], [ 1., 1., 1.], [ 1., 1., 1.]])
np.empty((3, 3))
array([[ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.]])
Здесь важно помнить что функция empty() создает "пустой" массив, то есть в качестве значений может лежать какой-то мусор.
np.identity(3)
array([[ 1., 0., 0.], [ 0., 1., 0.], [ 0., 0., 1.]])
Базовые операции с матрицами
Все арифметические операции над матрицами производятся поэлементно
A = np.arange(0, 9).reshape(3, 3)
B = np.arange(1, 10).reshape(3, 3)
A
array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
A + B
array([[ 1, 3, 5], [ 7, 9, 11], [13, 15, 17]])
A * B
array([[ 0, 2, 6], [12, 20, 30], [42, 56, 72]])
A - B
array([[-1, -1, -1], [-1, -1, -1], [-1, -1, -1]])
A / B
array([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
A + 1
array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
Если же хочется выполнить привычное матричное умножение, необходимо воспользоваться функцией dot:
np.dot(A, B)
array([[ 18, 21, 24], [ 54, 66, 78], [ 90, 111, 132]])
Еще одно большое преимущество numpy — над матрицами определены многие стандартные операции (нахождение минимума, максимума и пр.) Важно помнить, что все эти функции находятся в пакете numpy:
np.min(A), np.max(A), np.sum(A)
(0, 8, 36)
Когда требуется выполнить аналогичные операции, но по некоторой размерности (например, найти минимальный элемент в каждой строке), можно указать параметр axis:
np.min(A, axis=1), np.max(A, axis=1), np.sum(A, axis=1)
(array([0, 3, 6]), array([2, 5, 8]), array([ 3, 12, 21]))
np.min(A, axis=0), np.max(A, axis=0), np.sum(A, axis=0)
(array([0, 1, 2]), array([6, 7, 8]), array([ 9, 12, 15]))
Помимо привычный функций, в пакете numpy есть много и математических функций, которые можно выполнять над матрицами:
np.sqrt(A)
array([[ 0. , 1. , 1.41421356], [ 1.73205081, 2. , 2.23606798], [ 2.44948974, 2.64575131, 2.82842712]])
Индексация
Одномерные массивы осуществляют операции индексирования, срезов и итераций очень схожим образом с обычными списками и другими последовательностями Python.
a = np.arange(1, 28)
a
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27])
a[0]
1
a[-1]
27
a[1:-1]
array([ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26])
Но в numpy появляется новый тип индексации, а именно индексация с помощью массивов (которая работает по всем размерностям):
a[[0, 2, 3]]
array([1, 3, 4])
b = np.reshape(a, (3, 9))
b
array([[ 1, 2, 3, 4, 5, 6, 7, 8, 9], [10, 11, 12, 13, 14, 15, 16, 17, 18], [19, 20, 21, 22, 23, 24, 25, 26, 27]])
b[0]
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
b[:, 1]
array([ 2, 11, 20])
c = np.reshape(a, (3, 3, 3))
c
array([[[ 1, 2, 3], [ 4, 5, 6], [ 7, 8, 9]], [[10, 11, 12], [13, 14, 15], [16, 17, 18]], [[19, 20, 21], [22, 23, 24], [25, 26, 27]]])
c[0]
array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
c[:,:,1]
array([[ 2, 5, 8], [11, 14, 17], [20, 23, 26]])
c[:, :, [1, 2]]
array([[[ 2, 3], [ 5, 6], [ 8, 9]], [[11, 12], [14, 15], [17, 18]], [[20, 21], [23, 24], [26, 27]]])
А что еще более интересно — можно делать булевую индексацию. То есть вам необходимо передать массив из True/False, соответствующий какие элементы брать:
a = np.arange(10)
good = np.array([True, False, False, True, True, True, False, False, True, False])
a[good]
array([0, 3, 4, 5, 8])
В начале может показаться что это долго (в обычной индексации нам необходимо было передать несколько индексов, а здесь — целый массив), однако отвлечемся немного на numpy. Вспомним, что к массивам можно применять обычные операции. Логические операции не являются исключением:
a % 2 == 0
array([ True, False, True, False, True, False, True, False, True, False], dtype=bool)
Таким образом получаем, что чтобы найти в массиве только четные числа, нужно выполнить следующую строчку:
a[a % 2 == 0]
array([0, 2, 4, 6, 8])
"...fast, flexible, and expressive data structures designed to make working with “relational” or “labeled” data both easy and intuitive"
import pandas as pd
Попробуем начать работать с модельными данными. Для начала посмотрим как можно загрузить данные с помощью Python.
print(open('data.txt').read())
Feature1,Weight,Height,Bla-bla,Size,Class 10.0,12,344,0,23.0,Class1 7.2,12,208,0,18.0,Class2 19.0,11,344,1,21.0,Class4 7.2,13,208,0,20.0,Class2 9.2,20,208,0,17.0,Class1 19.0,11,254,2,11.0,Class3
Можно увидеть, что первая строчка является заголовком, а каждая следующая строчка — описанием объекта. Последний столбец является целевой меткой. Все остальные столбцы являются признаковым описанием объектов или просто признаками.
Считаем данные и сохраним в удобном для нас формате
from itertools import islice
points = []
for line in islice(open('data.txt'), 1, None):
columns = line.strip().split(',')
features = [float(feature) for feature in columns[:5]]
label = columns[5]
points.append({'features': features, 'label': label})
points
[{'features': [10.0, 12.0, 344.0, 0.0, 23.0], 'label': 'Class1'}, {'features': [7.2, 12.0, 208.0, 0.0, 18.0], 'label': 'Class2'}, {'features': [19.0, 11.0, 344.0, 1.0, 21.0], 'label': 'Class4'}, {'features': [7.2, 13.0, 208.0, 0.0, 20.0], 'label': 'Class2'}, {'features': [9.2, 20.0, 208.0, 0.0, 17.0], 'label': 'Class1'}, {'features': [19.0, 11.0, 254.0, 2.0, 11.0], 'label': 'Class3'}]
Попробуем считать данные с помощью Pandas. Данные будут сохранены в специализированный объект класса pandas.DataFrame, который представляет из себя таблицу с проименованными строками и столбцами.
df = pd.read_csv('data.txt')
df
Feature1 | Weight | Height | Bla-bla | Size | Class | |
---|---|---|---|---|---|---|
0 | 10.0 | 12 | 344 | 0 | 23 | Class1 |
1 | 7.2 | 12 | 208 | 0 | 18 | Class2 |
2 | 19.0 | 11 | 344 | 1 | 21 | Class4 |
3 | 7.2 | 13 | 208 | 0 | 20 | Class2 |
4 | 9.2 | 20 | 208 | 0 | 17 | Class1 |
5 | 19.0 | 11 | 254 | 2 | 11 | Class3 |
Можно делать срезы по именам колонки
df['Weight']
0 12 1 12 2 11 3 13 4 20 5 11 Name: Weight, dtype: int64
а также по строкам
df[2:3]
Feature1 | Weight | Height | Bla-bla | Size | Class | |
---|---|---|---|---|---|---|
2 | 19 | 11 | 344 | 1 | 21 | Class4 |
Посчитает какие и сколько раз признак "Bla-bla" принимал значения
df['Bla-bla'].value_counts()
0 4 2 1 1 1 Name: Bla-bla, dtype: int64
Попробуем посмотреть на общую статистику по признакам
df.describe()
Feature1 | Weight | Height | Bla-bla | Size | |
---|---|---|---|---|---|
count | 6.000000 | 6.000000 | 6.000000 | 6.00000 | 6.000000 |
mean | 11.933333 | 13.166667 | 261.000000 | 0.50000 | 18.333333 |
std | 5.583786 | 3.430258 | 66.714316 | 0.83666 | 4.179314 |
min | 7.200000 | 11.000000 | 208.000000 | 0.00000 | 11.000000 |
25% | 7.700000 | 11.250000 | 208.000000 | 0.00000 | 17.250000 |
50% | 9.600000 | 12.000000 | 231.000000 | 0.00000 | 19.000000 |
75% | 16.750000 | 12.750000 | 321.500000 | 0.75000 | 20.750000 |
max | 19.000000 | 20.000000 | 344.000000 | 2.00000 | 23.000000 |
Для удаления какого-либо столбца можно воспользоваться методом drop
df.drop('Height', axis=1)
Feature1 | Weight | Bla-bla | Size | Class | |
---|---|---|---|---|---|
0 | 10.0 | 12 | 0 | 23 | Class1 |
1 | 7.2 | 12 | 0 | 18 | Class2 |
2 | 19.0 | 11 | 1 | 21 | Class4 |
3 | 7.2 | 13 | 0 | 20 | Class2 |
4 | 9.2 | 20 | 0 | 17 | Class1 |
5 | 19.0 | 11 | 2 | 11 | Class3 |
Удаление строк делается похожим образом
df.drop([0, 1])
Feature1 | Weight | Height | Bla-bla | Size | Class | |
---|---|---|---|---|---|---|
2 | 19.0 | 11 | 344 | 1 | 21 | Class4 |
3 | 7.2 | 13 | 208 | 0 | 20 | Class2 |
4 | 9.2 | 20 | 208 | 0 | 17 | Class1 |
5 | 19.0 | 11 | 254 | 2 | 11 | Class3 |
Обратите внимание, что исходный датафрейм остается неизменным
df
Feature1 | Weight | Height | Bla-bla | Size | Class | |
---|---|---|---|---|---|---|
0 | 10.0 | 12 | 344 | 0 | 23 | Class1 |
1 | 7.2 | 12 | 208 | 0 | 18 | Class2 |
2 | 19.0 | 11 | 344 | 1 | 21 | Class4 |
3 | 7.2 | 13 | 208 | 0 | 20 | Class2 |
4 | 9.2 | 20 | 208 | 0 | 17 | Class1 |
5 | 19.0 | 11 | 254 | 2 | 11 | Class3 |
Вспомним про numpy
mat = df.drop('Class', axis=1).values.astype(np.float32)
mat
array([[ 10. , 12. , 344. , 0. , 23. ], [ 7.19999981, 12. , 208. , 0. , 18. ], [ 19. , 11. , 344. , 1. , 21. ], [ 7.19999981, 13. , 208. , 0. , 20. ], [ 9.19999981, 20. , 208. , 0. , 17. ], [ 19. , 11. , 254. , 2. , 11. ]], dtype=float32)
Есть два способа конкатенации матриц:
np.vstack([mat, mat ** 2])
array([[ 1.00000000e+01, 1.20000000e+01, 3.44000000e+02, 0.00000000e+00, 2.30000000e+01], [ 7.19999981e+00, 1.20000000e+01, 2.08000000e+02, 0.00000000e+00, 1.80000000e+01], [ 1.90000000e+01, 1.10000000e+01, 3.44000000e+02, 1.00000000e+00, 2.10000000e+01], [ 7.19999981e+00, 1.30000000e+01, 2.08000000e+02, 0.00000000e+00, 2.00000000e+01], [ 9.19999981e+00, 2.00000000e+01, 2.08000000e+02, 0.00000000e+00, 1.70000000e+01], [ 1.90000000e+01, 1.10000000e+01, 2.54000000e+02, 2.00000000e+00, 1.10000000e+01], [ 1.00000000e+02, 1.44000000e+02, 1.18336000e+05, 0.00000000e+00, 5.29000000e+02], [ 5.18399963e+01, 1.44000000e+02, 4.32640000e+04, 0.00000000e+00, 3.24000000e+02], [ 3.61000000e+02, 1.21000000e+02, 1.18336000e+05, 1.00000000e+00, 4.41000000e+02], [ 5.18399963e+01, 1.69000000e+02, 4.32640000e+04, 0.00000000e+00, 4.00000000e+02], [ 8.46399994e+01, 4.00000000e+02, 4.32640000e+04, 0.00000000e+00, 2.89000000e+02], [ 3.61000000e+02, 1.21000000e+02, 6.45160000e+04, 4.00000000e+00, 1.21000000e+02]], dtype=float32)
np.hstack([mat, mat ** 2])
array([[ 1.00000000e+01, 1.20000000e+01, 3.44000000e+02, 0.00000000e+00, 2.30000000e+01, 1.00000000e+02, 1.44000000e+02, 1.18336000e+05, 0.00000000e+00, 5.29000000e+02], [ 7.19999981e+00, 1.20000000e+01, 2.08000000e+02, 0.00000000e+00, 1.80000000e+01, 5.18399963e+01, 1.44000000e+02, 4.32640000e+04, 0.00000000e+00, 3.24000000e+02], [ 1.90000000e+01, 1.10000000e+01, 3.44000000e+02, 1.00000000e+00, 2.10000000e+01, 3.61000000e+02, 1.21000000e+02, 1.18336000e+05, 1.00000000e+00, 4.41000000e+02], [ 7.19999981e+00, 1.30000000e+01, 2.08000000e+02, 0.00000000e+00, 2.00000000e+01, 5.18399963e+01, 1.69000000e+02, 4.32640000e+04, 0.00000000e+00, 4.00000000e+02], [ 9.19999981e+00, 2.00000000e+01, 2.08000000e+02, 0.00000000e+00, 1.70000000e+01, 8.46399994e+01, 4.00000000e+02, 4.32640000e+04, 0.00000000e+00, 2.89000000e+02], [ 1.90000000e+01, 1.10000000e+01, 2.54000000e+02, 2.00000000e+00, 1.10000000e+01, 3.61000000e+02, 1.21000000e+02, 6.45160000e+04, 4.00000000e+00, 1.21000000e+02]], dtype=float32)
Вернемся к датафреймам:
newdf = pd.DataFrame(np.hstack([mat, mat ** 2]))
newdf
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 10.0 | 12 | 344 | 0 | 23 | 100.000000 | 144 | 118336 | 0 | 529 |
1 | 7.2 | 12 | 208 | 0 | 18 | 51.839996 | 144 | 43264 | 0 | 324 |
2 | 19.0 | 11 | 344 | 1 | 21 | 361.000000 | 121 | 118336 | 1 | 441 |
3 | 7.2 | 13 | 208 | 0 | 20 | 51.839996 | 169 | 43264 | 0 | 400 |
4 | 9.2 | 20 | 208 | 0 | 17 | 84.639999 | 400 | 43264 | 0 | 289 |
5 | 19.0 | 11 | 254 | 2 | 11 | 361.000000 | 121 | 64516 | 4 | 121 |