Архив за месяц: Апрель 2018

DataFrame pandas базовые манипуляции с данными

Pandas — очень мощный инструмент для работы и манипуляций над данными с помощью python. Можно сказать, что pandas предоставляет возможности SQL базы данных, дополненные мощью python. Однако, работа с данной библиотекой иногда вызывает некоторые трудности, так как работать приходится по сути с матричными данными и объектами pandas да numpy, а переход к стандартным python типам и циклам неминуемо грозит кратной потерей функциональности. Поэтому для себя создал шпаргалку по работе с дата-фреймами:

Работа начинается с загрузки данных в DataFrame, поэтому для начала считаем данные:

df = pd.read_csv('some.csv')
# если Вам заранее известны типы, то чрезвычайно полезно задать типы колонок сразу - это сэкономит оперативную память.
# также можно использоть лишь несколько колонок, а не все колонки в файле
df = pd.read_csv('some.csv', usecols=["user_id", "id3"], dtype={"user_id": np.int64, "id3": np.uint16})

Возможно и самостоятельно создать DataFrame и добавить строки

# пример создания объекта дата-фрейм
pd.DataFrame(columns=['A', 'B'])
# пример добавления строки в дата-фрейм
df.append({'A':1, 'B':2}, ignore_index=True)

После создания или загрузки DF полезно посмотреть чтоже там за данные, для этого можно воспользоваться следующими операциями:

df.head()       # первые строки
df.tail()       # последние строки
df.sample(5)    # случайно выбранное кол-ва строк, полезно использовать для уменьшения матрицы для прогонки тестов
df.shape        # по аналогии с numpy - размерность матрицы
df.describe()   # математические данные
df.info()       # использование памяти

Возможно далее вам захочется получить точечное значение по координатам из дата фрема, тогда используйте ix

some_value = df.ix[2, 'som_col']  # похожие функции loc и iloc

Для фильтрации по колонкам пандас использует булевую логику, проще показать на примере:

filtered_data = df[df.some_col == 'apple']
filtered_data = df[(df.price > 10.0) & (df.some_col == 'apple')]

Далее приведём несколько примеров манипуляции с данными:

# пример удаления колонки:
df = df.drop("A", axis=1)
# удалит дубликаты
df = df.drop_duplicates()
# Создание новой колонки со значением по умолчанию
df['new_col'] = 1
# а вот так можно добавлять колонку с условием
df['new_col2'] = np.where(df['score']>=10.0, True, False)

Применение произвольной функции ко всей колонке

def get_score(s):
    return s * 0.21

df['score'] = df.some_col.apply(get_score)

Переименование колонки, сортировка

# переименовываем колонку A на колонку C
df = df.rename(columns={'A': 'C'})
# сортировка по одной колонке
df = df.sort_values('price', axis=0, ascending=False)
# сортировка сразу по нескольким колонкам
df = df.sort_values(['price', 'score'], ascending=[1, 0])

пример на соединение или конкатенацию дата-фреймов

df = pd.concat([df1, df2], ignore_index=True, axis=0)

группировка в дата-фрейме без мультииндекса

df.groupby(['A'], as_index=False).agg({'B': lambda series: series.iloc[0]})

После всех манипуляций, вас скорее всего надо будет данные куда-то выгрузить в другом формате или сохранить до следующего раза новый фрагмент таблицы дата-фрейма:

# в numpy массив
df.values
# в numpy массив, но сразу всю матрицу
df.as_matrix
# сохранить в файл
df.to_csv('submission.csv', index=False)

Примеры использования Random Forest из scikit-learn для классификации

Один из классических алгоритмов классификации является, алгоритм использования ансамбля решающих деревьев Random Forest (далее просто RF). Его выбирают по следующим причинам:

  • Random Forest хорош для не нормализованных данных, многие алгоритмы машинного обучения (ML) дают плохие результаты и данные предварительно надо обрабатывать. Для RF этот шаг можно попробовать опустить и быстро получить работающей прототип
  • Быстрота обучения классификатора, поэтому для обучения можно использовать большое количество данных не беспокоясь в времени (в отличии от SVM). Распараллеливание алгоритма.
  • Можно получить важность / веса параметров из пространства признаков а также вероятность предсказания

Из некоторых минусув можно выделить то, что обучившись на одних и тех-же данных несколько раз — подход будет предсказывать немного разные значения с разными вероятностями. Т.е. детерминированности вы не получите. От этого не уйдешь, т.к. выборка ансамбля решающих деревьев происходит случайным образом. Для заморозки случайности используйте random_seed.

Первый пример c RF: обучаем модель и предсказываем один элемент

from sklearn.ensemble import RandomForestClassifier

x_train = [
    [1, 2],
    [3, 4],
    [-1, 2],
    [-3, 4]
]
y_train = [1, 1, 0, 0]
clf_rf = RandomForestClassifier()
clf_rf.fit(x_train, y_train)

print(clf_rf.predict([[2, 2]]))  # [1] это предсказанный класс
print(clf_rf.predict_proba([[2, 2]]))  # [[0.2 0.8]] вероятности по классам

Пример второй: кросс-валидация или разбиение оценка точности с помощью разбиения на выборки для теста и для тренировки модели RF

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import train_test_split


x_train = [
    [1, 2], [5, 6],
    [3, 4], [7, 8],
    [-1, 2], [-5, 6],
    [-3, 4], [-7, 8], [0, 0]
]
y_train = [1, 1, 1, 1, 0, 0, 0, 0, 1]
clf_rf = RandomForestClassifier()

# разбиваем на два подмножества / фолда, параметр cv и получаем точность для каждого
scores = cross_val_score(clf_rf, x_train, y_train, cv=2)
print(scores)  # [0.8  0.75]

# кстати рабить на обучающую выборку и тестовую можно функцией
X_train, X_test, Y_train, Y_test = train_test_split(x_train, y_train, test_size=0.2)
print(X_test, Y_test)  # [[5, 6], [-3, 4]] [1, 0]

Возможно вы уже задались вопросом, какие параметры подкрутить, чтобы как-то влиять на точность и адаптировать алгоритм для входных данных и для железа.
n_estimators — параметр, напрямую влияющий на качество, при увеличении пространства признаков, следует рассмотреть увеличение этого параметра
max_depth — поможет если модель получилась сильно переобученной
criterion — entropy или gini . Можно с ними поиграться)
n_jobs — когда можно использовать несколько ядер
Пример третий: использование параметров для настройки RF
Здесь показано как с помощью GridSearchCV найти оптимальные параметры для классификатора.

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV


x_train = [
    [1, 2], [5, 6],
    [3, 4], [7, 8],
    [-1, 2], [-5, 6],
    [-3, 4], [-7, 8], [0, 0]
]
y_train = [1, 1, 1, 1, 0, 0, 0, 0, 1]

parameter_grid = {
            'criterion': ['entropy', 'gini'],
            'max_depth': [10, 20, 100],
            'n_estimators': [10, 20, 100]
        }
clf = RandomForestClassifier()
grid_searcher = GridSearchCV(clf, parameter_grid, verbose=2)
grid_searcher.fit(x_train, y_train)
clf_best = grid_searcher.best_estimator_

print('Best params = ', clf_best.get_params())

Пример четвертый — сохранение и распаковка обученной модели:

from sklearn.externals import joblib
# предполагаем что clf - обучен выше
joblib.dump(clf, path_to_pkl, compress=1)
clf = joblib.load(self.path_to_pkl)

Здесь параметр compress — влияет на то на сколько сильно будет сжат файл классификатора, чем больше тем сильнее, однако не советую увлекаться, т.к. это обратнопропорционально вляет на время распаковки.

Пример пятый — рисуем график важности параметров. Вполне может пригодиться для презентации результатов.

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
import matplotlib.pyplot as plt
import numpy as np



x_train = [
    [1, 2], [5, 6],
    [3, 4], [7, 8],
    [-1, 2], [-5, 6],
    [-3, 4], [-7, 8], [0, 0]
]
y_train = [1, 1, 1, 1, 0, 0, 0, 0, 1]

parameter_grid = {
            'criterion': ['entropy', 'gini'],
            'max_depth': [10, 20, 100],
            'n_estimators': [10, 20, 100]
        }
clf_rf = RandomForestClassifier()
clf_rf.fit(x_train, y_train)

importances = clf_rf.feature_importances_
print(importances)
std = np.std([tree.feature_importances_ for tree in clf_rf.estimators_], axis=0)

indices = np.argsort(importances)[::-1]
names_indices = ['x_coor', 'y_coor']

# Plot the feature importances of the forest
plt.figure()
plt.title("Feature importances")

plt.bar(range(len(importances)), importances[indices], color="r")
plt.xticks(range(len(importances)), names_indices, rotation=90)

plt.tight_layout()
plt.xlim([-1, len(importances)])
plt.show()

Как видно из нарисованного графика, первая координа является более ключевой, что действительно так по выборке. важность параметров в random forest