
# coding: utf-8

# #  Определение класса аварийности транспортного средства

# По данным телеметрического мониторинга состояния карьерных самосвалов, полученных на предприятиях Coal Inc. в ноябре 2019 года необходимо построить модель определения класса аварийности транспортного средства.

# **Описание данных:**

# Таблица data:  
# - *temperature* — cредняя температура воздуха за бортом;
# - *velocity* — cредняя скорость движения самосвала;
# - *pressure* — cреднее давление в шинах;
# - *incline* — cреднее значение показаний инклинометра;
# - *class* — класс аварийности
# 
# Предложенный массив данных содержит 135 записей с описанными выше измерениями и классом аварийности самосвала, присвоенным ему ремонтной бригадой.

# In[1]:


import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy import stats as st
import math


# In[2]:


data=pd.read_csv('./data/data.csv')


# In[3]:


data.head()


# Многие алгоритмы машинного обучения требуют на вход численные признаки, поэтому приведем метку класса к целочисленному типу.

# In[4]:


data.loc[:,'class']=data['class'].map({'Class_1': 1, 'Class_2': 2, 'Class_3': 3})


# In[5]:


data.info()


# ## Шаг 1. Статистический анализ

# Посмотрим на распределение имеющихся данных по классам. 

# In[6]:


data['class'].value_counts()


# Распределение практически равномерное.

# Обратимся к совместному распределению классов.

# In[7]:


sns.pairplot(data, hue ='class')


# Построим матрицу корреляции, показывающую линейный коэффициент корреляции между всеми возможными парами переменных.

# In[8]:


data.corr()


# Построим так же сводную таблицу средних значений телеметрических показателей для каждого класса аварийности. 

# In[9]:


data.pivot_table(index='class', aggfunc='mean')


# ## Вывод

# Сильная линейная зависимость (с коэффициентром корреляции 0.96) наблюдается классом давлением в шинах и показаниями инклинометра. При превышении наклоном определенного придела давление в шинах значительно повышается. Можно так же заметить, все самосвалы, относящиеся к 1 классу аварийности имели дваление в шинах в пределе 1-2 атм (соответственно работая на в основном в несложных условиях небольших наклонов). Можно сделать вывод, что большая часть аварий, приходится на самосвалы, работающие в более тяжелых условиях. Средний показатель давления в шинах для самосвала отнесенного ко второму классу аварийности - 4.26, для самосвала третьего класса - 5.55. Это хороший показатель для предсказания итогового класса аварийности. С ростом наклона (и соответвующего ему давления в шинах) повышается и класс аварийности. 
# 
# Некоторая линейная зависимость (с коэффициентром корреляции 0.78) наблюдается между классом аварийности и температурой окружающего воздуха. Чем выше температура, тем выше класс аварийности, однако этот показатель не такой силен, при одинаковой температуре встречаются самосвалы всех трех классов аварийности. Ко всему прочему температура - параметр который практически невозможно контролировать. Несмотря на это она может помочь предсказать класс аварийности.
# 
# Зависимость класса опасности от скорости нелинейна. Однако можно заметить, что самосвалы 1 класса как правило имеют большую среднюю скорость (равную 3.44). Как было выяснено ранее, это может быть связано с тем, что самосвалы 1 класса работают в более легких условиях небольшого наклона и способны достичь более высокой скорости в принципе. Однако при сложных условиях наблюдается зависимость между скоростью и аварийностью. Самосвалы 3 класса двигались со средней скоростью 3, что выше среднего показателя скорости у самосвалов второго класса (составляющей 2.8). В данном случае более высокая скорость коррелирует с повышением класса аварийности. 
# 
# **Практические решения:** Наиболее сильно аварийность самосвалов повышается при работе в тяжелых условиях, характеризующихся высокими показаниями инклинометра, ведущими к повышенному давлению в шинах. Для понижения общего уровня аварийности следует своевременно выполненять вертикальную планировку промплощадки, облегчая тем самым условия для ведения работ. В реальных условиях угольного разреза, изобилующего съездами и имеющего высокие темпы ведения горных работ, зачастую невозможно значительно улучшить производственные условия. В этом случае рекомендуется ограничить допустимую скорость движения самосвалов при работе в тяжелых условиях. Это поможет уменьшить число самосвалов, отнесенных к 3 классу аварийности.   
# Так же некоторое улучшение может наступить, если проводить ротацию техники, работающую в различных условиях. Это позволит распределить загруженность ремонтных бригад по времени. 
# 

# ___

# ## Шаг 2. Построение аналитических моделей. 

# Исходные данные содержат небольшое число записей, каждая из которых состоит из 4 численных параметров и 1 метки класса. Классы хорошо различимы даже визуально (например, хорошо видно, что к 1 классу относятся только машины, уровень давления в шинах которых не превышает 2 атм). Для таких данных хорошо подходят простые алгоритмы классификации, вроде k - ближайшах соседей или деревья решений. 

# В работе использована библиотека scikit-learn.

# Для выполнения контроля за переобучением разобъем имеющийся датасет на тренировочную и тестовую часть. 

# In[10]:


from sklearn.model_selection import train_test_split, GridSearchCV


# Разделим датасет на две части X, содержащий телеметрические показатели и y, содержащий метки классов. 

# In[11]:


X = data.iloc[:, 0:4].values
y = data.iloc[:, 4].values


# Случайным образом разделим данные на тренировочную и тестовую часть. Размер тестовой части ввиду небольного количества исходных данных примем равным 20% от общего размера датасета.

# In[12]:


X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)


# - Первый исползуемый алгоритм - используемый алгоритм классификации - решающее дерево.  
# 
# Оно предоставляет наглядные и легко интерпретируемые человеком результаты. 

# In[13]:


from sklearn.tree import DecisionTreeClassifier


# Произведем настройку параметров дерева. Максимальная глубина и минимальное число элементов в листе настраивается на 5-кратной кросс-валидации.

# In[14]:


tree_params = {'max_depth': list(range(1, 5)), 
               'min_samples_leaf': list(range(1, 5))}


# In[15]:


tree_new = DecisionTreeClassifier(max_depth=5, random_state=42)


# In[16]:


tree_grid = GridSearchCV(tree_new, tree_params,
cv=5, n_jobs=-1,
verbose=True)


# In[17]:


tree_grid.fit(X_train, y_train)


# In[18]:


tree_grid.best_params_, tree_grid.best_score_


# Выбраны самые оптимальные параметры решающего дерева - максимальная глубина - 2, минимальное число элементов в листе - 1

# Выполним отрисовку дерева для наглядного представления полученного классификатора

# In[19]:


from sklearn import tree


# In[20]:


tree_new = DecisionTreeClassifier(max_depth=2, min_samples_leaf=1, random_state=42)


# In[21]:


tree.plot_tree(tree_new.fit(X_train, y_train), 
               feature_names = ('temperature', 'velocity', 'pressure', 'incline', 'class'));


# Видно, что модель относит к первому классу аварийности те записи, телеметрия которых показывает значение давления меньше или равное 2,45. Из оставшихся записей, если показатель инклинометра меньше или равен 1,65 - запись относится ко второму классу аварийности, если выше - к третьему. 

# Проверим полученную модель на имеющихся тестовых данных.

# In[22]:


tree_grid_pred = tree_grid.predict(X_test)


# In[23]:


from sklearn.metrics import confusion_matrix, accuracy_score


# In[24]:


confusion_matrix(y_test, tree_grid_pred)


# In[25]:


accuracy_score(y_test, tree_grid_pred)


# Как видно, даже такая модель предоставляет крайне высокую процент верного определения класса, что значит что её целесообразно использовать для определения класса аварийности по телеметрическим показателям.

# ___

# Выполним так же построение простой нейронной сети с помощью библиотеки машинного обучения PyTorch.  
# 
# В данном случае использование нейронной сети является избыточным, ведь, как мы видели, эту задачу можно решить гораздо более простыми методами. Ко всему прочему нейронные сети функционируют по принципу черного ящика, и неприминимы в случаях, когда необходимо объяснить причину принятия решения. 
# 
# Однако в целом нейросети имеют множество преимуществ перед остальными методами (например устойчивость к шумам, адаптивность, высокое быстроействие и отказоустойчивость), поэтому спектр задач, в которых их применение оправдано достаточно широк.

# In[26]:


import torch
from torch import nn
from sklearn.preprocessing import StandardScaler


# Отмасштабируем данные и приведем их к виду, применимому в нейросетевой модели 

# In[27]:


scaler = StandardScaler()
X_nn = scaler.fit_transform(X_train)
X_nn_test = scaler.fit_transform(X_test)


# In[28]:


X_nn = torch.autograd.Variable(torch.FloatTensor(X_train))
y_nn = torch.autograd.Variable(torch.LongTensor(y_train.astype(np.int64))) - 1


# In[29]:


X_nn_test = torch.autograd.Variable(torch.FloatTensor(X_test))
y_nn_test = torch.autograd.Variable(torch.LongTensor(y_test.astype(np.int64))) - 1


# Зададим параметры математической модели. Сеть состоит из одного входного, двух скрытых  (в каждом по 50 нейронов) и одного выходного слоя. После каждого линейного слоя выполняется дропаут (это необходимо для избежания переобучения). Функция активации для каждого линейного слоя - ReLU.  
# Функция потерь - CrossEntropyLoss, наиболее часто применяемая для задач многоклассовой классификации.  
# Оптимизатор - Adam.

# In[30]:


torch.manual_seed(42) 
np.random.seed(42)
D_in, H1, H2, D_out = 4, 50, 50, 3
model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H1),
    torch.nn.Dropout(),
    torch.nn.ReLU(),
    torch.nn.Linear(H1, H2),
    torch.nn.Dropout(),
    torch.nn.ReLU(),
    torch.nn.Linear(H2, D_out),
)
    
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)


# Определим функцию обучениия. 

# In[31]:


def train(num_epoch):
    train_losses = []
    for i in range(num_epoch):
        model.train(True)
        y_pred = model(X_nn)
        loss = loss_fn(y_pred, y_nn)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        train_losses.append(loss.item())
    return train_losses


# Обучим модель на обучающей выборке. Выведем при этом значение функции потерь.

# In[32]:


train_losses = train(100)
plt.scatter(range(len(train_losses)), train_losses)


# In[33]:


model.train(False)


# Выполним предсказания на тестовых данных. 

# In[34]:


y_pred = model(X_nn_test)


# Модель выдает вероятность принадлежности объекта к каждому классу. Получим класс с наибольшей вероятностью для каждого объекта. 

# In[35]:


_, index = torch.max(y_pred,1)


# In[36]:


accuracy_score(y_nn_test, index)


# Как видно процент верно предсказаных значений на тестовых данных превышает такой процент для решающего дерева (и равен 100%). Это хорошо характеризует полученную модель. 

# Код для проверки на внешних тестовых данных. Необходимо поместить csv файл с данными с названием 'test.csv' в папку и запустить ячейки.

# In[ ]:


test=pd.read_csv('./data/test.csv')


# In[ ]:


test.loc[:,'class']=test['class'].map({'Class_1': 1, 'Class_2': 2, 'Class_3': 3})


# In[ ]:


X_val = data.iloc[:, 0:4].values
y_val = data.iloc[:, 4].values


# - Проверка дерева 

# In[ ]:


tree_test_pred = tree_grid.predict(X_val)


# In[ ]:


accuracy_score(y_val, tree_test_pred)


# - Проверка нейросети.

# In[ ]:


X_nn_val = scaler.fit_transform(X_val)


# In[ ]:


X_nn_val = torch.autograd.Variable(torch.FloatTensor(X_nn_val))
y_nn_val = torch.autograd.Variable(torch.LongTensor(y_val.astype(np.int64))) - 1


# In[ ]:


y_val_pred = model(X_nn_val)


# In[ ]:


_, index = torch.max(y_pred,1)


# In[ ]:


accuracy_score(y_nn_test, index)


7930da588baa8cb8761d3a5f2ae9b9127a4df627
<mask token>


def train(num_epoch):
    train_losses = []
    for i in range(num_epoch):
        model.train(True)
        y_pred = model(X_nn)
        loss = loss_fn(y_pred, y_nn)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        train_losses.append(loss.item())
    return train_losses


<mask token>

<mask token>
data.head()
<mask token>
data.info()
data['class'].value_counts()
sns.pairplot(data, hue='class')
data.corr()
data.pivot_table(index='class', aggfunc='mean')
<mask token>
tree_grid.fit(X_train, y_train)
tree_grid.best_params_, tree_grid.best_score_
<mask token>
tree.plot_tree(tree_new.fit(X_train, y_train), feature_names=('temperature',
    'velocity', 'pressure', 'incline', 'class'))
<mask token>
confusion_matrix(y_test, tree_grid_pred)
accuracy_score(y_test, tree_grid_pred)
<mask token>
torch.manual_seed(42)
np.random.seed(42)
<mask token>


def train(num_epoch):
    train_losses = []
    for i in range(num_epoch):
        model.train(True)
        y_pred = model(X_nn)
        loss = loss_fn(y_pred, y_nn)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        train_losses.append(loss.item())
    return train_losses


<mask token>
plt.scatter(range(len(train_losses)), train_losses)
model.train(False)
<mask token>
accuracy_score(y_nn_test, index)
<mask token>
accuracy_score(y_val, tree_test_pred)
<mask token>
accuracy_score(y_nn_test, index)

<mask token>
data = pd.read_csv('./data/data.csv')
data.head()
data.loc[:, 'class'] = data['class'].map({'Class_1': 1, 'Class_2': 2,
    'Class_3': 3})
data.info()
data['class'].value_counts()
sns.pairplot(data, hue='class')
data.corr()
data.pivot_table(index='class', aggfunc='mean')
<mask token>
X = data.iloc[:, 0:4].values
y = data.iloc[:, 4].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,
    random_state=42)
<mask token>
tree_params = {'max_depth': list(range(1, 5)), 'min_samples_leaf': list(
    range(1, 5))}
tree_new = DecisionTreeClassifier(max_depth=5, random_state=42)
tree_grid = GridSearchCV(tree_new, tree_params, cv=5, n_jobs=-1, verbose=True)
tree_grid.fit(X_train, y_train)
tree_grid.best_params_, tree_grid.best_score_
<mask token>
tree_new = DecisionTreeClassifier(max_depth=2, min_samples_leaf=1,
    random_state=42)
tree.plot_tree(tree_new.fit(X_train, y_train), feature_names=('temperature',
    'velocity', 'pressure', 'incline', 'class'))
tree_grid_pred = tree_grid.predict(X_test)
<mask token>
confusion_matrix(y_test, tree_grid_pred)
accuracy_score(y_test, tree_grid_pred)
<mask token>
scaler = StandardScaler()
X_nn = scaler.fit_transform(X_train)
X_nn_test = scaler.fit_transform(X_test)
X_nn = torch.autograd.Variable(torch.FloatTensor(X_train))
y_nn = torch.autograd.Variable(torch.LongTensor(y_train.astype(np.int64))) - 1
X_nn_test = torch.autograd.Variable(torch.FloatTensor(X_test))
y_nn_test = torch.autograd.Variable(torch.LongTensor(y_test.astype(np.int64))
    ) - 1
torch.manual_seed(42)
np.random.seed(42)
D_in, H1, H2, D_out = 4, 50, 50, 3
model = torch.nn.Sequential(torch.nn.Linear(D_in, H1), torch.nn.Dropout(),
    torch.nn.ReLU(), torch.nn.Linear(H1, H2), torch.nn.Dropout(), torch.nn.
    ReLU(), torch.nn.Linear(H2, D_out))
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)


def train(num_epoch):
    train_losses = []
    for i in range(num_epoch):
        model.train(True)
        y_pred = model(X_nn)
        loss = loss_fn(y_pred, y_nn)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        train_losses.append(loss.item())
    return train_losses


train_losses = train(100)
plt.scatter(range(len(train_losses)), train_losses)
model.train(False)
y_pred = model(X_nn_test)
_, index = torch.max(y_pred, 1)
accuracy_score(y_nn_test, index)
test = pd.read_csv('./data/test.csv')
test.loc[:, 'class'] = test['class'].map({'Class_1': 1, 'Class_2': 2,
    'Class_3': 3})
X_val = data.iloc[:, 0:4].values
y_val = data.iloc[:, 4].values
tree_test_pred = tree_grid.predict(X_val)
accuracy_score(y_val, tree_test_pred)
X_nn_val = scaler.fit_transform(X_val)
X_nn_val = torch.autograd.Variable(torch.FloatTensor(X_nn_val))
y_nn_val = torch.autograd.Variable(torch.LongTensor(y_val.astype(np.int64))
    ) - 1
y_val_pred = model(X_nn_val)
_, index = torch.max(y_pred, 1)
accuracy_score(y_nn_test, index)

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy import stats as st
import math
data = pd.read_csv('./data/data.csv')
data.head()
data.loc[:, 'class'] = data['class'].map({'Class_1': 1, 'Class_2': 2,
    'Class_3': 3})
data.info()
data['class'].value_counts()
sns.pairplot(data, hue='class')
data.corr()
data.pivot_table(index='class', aggfunc='mean')
from sklearn.model_selection import train_test_split, GridSearchCV
X = data.iloc[:, 0:4].values
y = data.iloc[:, 4].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,
    random_state=42)
from sklearn.tree import DecisionTreeClassifier
tree_params = {'max_depth': list(range(1, 5)), 'min_samples_leaf': list(
    range(1, 5))}
tree_new = DecisionTreeClassifier(max_depth=5, random_state=42)
tree_grid = GridSearchCV(tree_new, tree_params, cv=5, n_jobs=-1, verbose=True)
tree_grid.fit(X_train, y_train)
tree_grid.best_params_, tree_grid.best_score_
from sklearn import tree
tree_new = DecisionTreeClassifier(max_depth=2, min_samples_leaf=1,
    random_state=42)
tree.plot_tree(tree_new.fit(X_train, y_train), feature_names=('temperature',
    'velocity', 'pressure', 'incline', 'class'))
tree_grid_pred = tree_grid.predict(X_test)
from sklearn.metrics import confusion_matrix, accuracy_score
confusion_matrix(y_test, tree_grid_pred)
accuracy_score(y_test, tree_grid_pred)
import torch
from torch import nn
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_nn = scaler.fit_transform(X_train)
X_nn_test = scaler.fit_transform(X_test)
X_nn = torch.autograd.Variable(torch.FloatTensor(X_train))
y_nn = torch.autograd.Variable(torch.LongTensor(y_train.astype(np.int64))) - 1
X_nn_test = torch.autograd.Variable(torch.FloatTensor(X_test))
y_nn_test = torch.autograd.Variable(torch.LongTensor(y_test.astype(np.int64))
    ) - 1
torch.manual_seed(42)
np.random.seed(42)
D_in, H1, H2, D_out = 4, 50, 50, 3
model = torch.nn.Sequential(torch.nn.Linear(D_in, H1), torch.nn.Dropout(),
    torch.nn.ReLU(), torch.nn.Linear(H1, H2), torch.nn.Dropout(), torch.nn.
    ReLU(), torch.nn.Linear(H2, D_out))
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)


def train(num_epoch):
    train_losses = []
    for i in range(num_epoch):
        model.train(True)
        y_pred = model(X_nn)
        loss = loss_fn(y_pred, y_nn)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        train_losses.append(loss.item())
    return train_losses


train_losses = train(100)
plt.scatter(range(len(train_losses)), train_losses)
model.train(False)
y_pred = model(X_nn_test)
_, index = torch.max(y_pred, 1)
accuracy_score(y_nn_test, index)
test = pd.read_csv('./data/test.csv')
test.loc[:, 'class'] = test['class'].map({'Class_1': 1, 'Class_2': 2,
    'Class_3': 3})
X_val = data.iloc[:, 0:4].values
y_val = data.iloc[:, 4].values
tree_test_pred = tree_grid.predict(X_val)
accuracy_score(y_val, tree_test_pred)
X_nn_val = scaler.fit_transform(X_val)
X_nn_val = torch.autograd.Variable(torch.FloatTensor(X_nn_val))
y_nn_val = torch.autograd.Variable(torch.LongTensor(y_val.astype(np.int64))
    ) - 1
y_val_pred = model(X_nn_val)
_, index = torch.max(y_pred, 1)
accuracy_score(y_nn_test, index)


# coding: utf-8

# #  Определение класса аварийности транспортного средства

# По данным телеметрического мониторинга состояния карьерных самосвалов, полученных на предприятиях Coal Inc. в ноябре 2019 года необходимо построить модель определения класса аварийности транспортного средства.

# **Описание данных:**

# Таблица data:  
# - *temperature* — cредняя температура воздуха за бортом;
# - *velocity* — cредняя скорость движения самосвала;
# - *pressure* — cреднее давление в шинах;
# - *incline* — cреднее значение показаний инклинометра;
# - *class* — класс аварийности
# 
# Предложенный массив данных содержит 135 записей с описанными выше измерениями и классом аварийности самосвала, присвоенным ему ремонтной бригадой.

# In[1]:


import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy import stats as st
import math


# In[2]:


data=pd.read_csv('./data/data.csv')


# In[3]:


data.head()


# Многие алгоритмы машинного обучения требуют на вход численные признаки, поэтому приведем метку класса к целочисленному типу.

# In[4]:


data.loc[:,'class']=data['class'].map({'Class_1': 1, 'Class_2': 2, 'Class_3': 3})


# In[5]:


data.info()


# ## Шаг 1. Статистический анализ

# Посмотрим на распределение имеющихся данных по классам. 

# In[6]:


data['class'].value_counts()


# Распределение практически равномерное.

# Обратимся к совместному распределению классов.

# In[7]:


sns.pairplot(data, hue ='class')


# Построим матрицу корреляции, показывающую линейный коэффициент корреляции между всеми возможными парами переменных.

# In[8]:


data.corr()


# Построим так же сводную таблицу средних значений телеметрических показателей для каждого класса аварийности. 

# In[9]:


data.pivot_table(index='class', aggfunc='mean')


# ## Вывод

# Сильная линейная зависимость (с коэффициентром корреляции 0.96) наблюдается классом давлением в шинах и показаниями инклинометра. При превышении наклоном определенного придела давление в шинах значительно повышается. Можно так же заметить, все самосвалы, относящиеся к 1 классу аварийности имели дваление в шинах в пределе 1-2 атм (соответственно работая на в основном в несложных условиях небольших наклонов). Можно сделать вывод, что большая часть аварий, приходится на самосвалы, работающие в более тяжелых условиях. Средний показатель давления в шинах для самосвала отнесенного ко второму классу аварийности - 4.26, для самосвала третьего класса - 5.55. Это хороший показатель для предсказания итогового класса аварийности. С ростом наклона (и соответвующего ему давления в шинах) повышается и класс аварийности. 
# 
# Некоторая линейная зависимость (с коэффициентром корреляции 0.78) наблюдается между классом аварийности и температурой окружающего воздуха. Чем выше температура, тем выше класс аварийности, однако этот показатель не такой силен, при одинаковой температуре встречаются самосвалы всех трех классов аварийности. Ко всему прочему температура - параметр который практически невозможно контролировать. Несмотря на это она может помочь предсказать класс аварийности.
# 
# Зависимость класса опасности от скорости нелинейна. Однако можно заметить, что самосвалы 1 класса как правило имеют большую среднюю скорость (равную 3.44). Как было выяснено ранее, это может быть связано с тем, что самосвалы 1 класса работают в более легких условиях небольшого наклона и способны достичь более высокой скорости в принципе. Однако при сложных условиях наблюдается зависимость между скоростью и аварийностью. Самосвалы 3 класса двигались со средней скоростью 3, что выше среднего показателя скорости у самосвалов второго класса (составляющей 2.8). В данном случае более высокая скорость коррелирует с повышением класса аварийности. 
# 
# **Практические решения:** Наиболее сильно аварийность самосвалов повышается при работе в тяжелых условиях, характеризующихся высокими показаниями инклинометра, ведущими к повышенному давлению в шинах. Для понижения общего уровня аварийности следует своевременно выполненять вертикальную планировку промплощадки, облегчая тем самым условия для ведения работ. В реальных условиях угольного разреза, изобилующего съездами и имеющего высокие темпы ведения горных работ, зачастую невозможно значительно улучшить производственные условия. В этом случае рекомендуется ограничить допустимую скорость движения самосвалов при работе в тяжелых условиях. Это поможет уменьшить число самосвалов, отнесенных к 3 классу аварийности.   
# Так же некоторое улучшение может наступить, если проводить ротацию техники, работающую в различных условиях. Это позволит распределить загруженность ремонтных бригад по времени. 
# 

# ___

# ## Шаг 2. Построение аналитических моделей. 

# Исходные данные содержат небольшое число записей, каждая из которых состоит из 4 численных параметров и 1 метки класса. Классы хорошо различимы даже визуально (например, хорошо видно, что к 1 классу относятся только машины, уровень давления в шинах которых не превышает 2 атм). Для таких данных хорошо подходят простые алгоритмы классификации, вроде k - ближайшах соседей или деревья решений. 

# В работе использована библиотека scikit-learn.

# Для выполнения контроля за переобучением разобъем имеющийся датасет на тренировочную и тестовую часть. 

# In[10]:


from sklearn.model_selection import train_test_split, GridSearchCV


# Разделим датасет на две части X, содержащий телеметрические показатели и y, содержащий метки классов. 

# In[11]:


X = data.iloc[:, 0:4].values
y = data.iloc[:, 4].values


# Случайным образом разделим данные на тренировочную и тестовую часть. Размер тестовой части ввиду небольного количества исходных данных примем равным 20% от общего размера датасета.

# In[12]:


X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)


# - Первый исползуемый алгоритм - используемый алгоритм классификации - решающее дерево.  
# 
# Оно предоставляет наглядные и легко интерпретируемые человеком результаты. 

# In[13]:


from sklearn.tree import DecisionTreeClassifier


# Произведем настройку параметров дерева. Максимальная глубина и минимальное число элементов в листе настраивается на 5-кратной кросс-валидации.

# In[14]:


tree_params = {'max_depth': list(range(1, 5)), 
               'min_samples_leaf': list(range(1, 5))}


# In[15]:


tree_new = DecisionTreeClassifier(max_depth=5, random_state=42)


# In[16]:


tree_grid = GridSearchCV(tree_new, tree_params,
cv=5, n_jobs=-1,
verbose=True)


# In[17]:


tree_grid.fit(X_train, y_train)


# In[18]:


tree_grid.best_params_, tree_grid.best_score_


# Выбраны самые оптимальные параметры решающего дерева - максимальная глубина - 2, минимальное число элементов в листе - 1

# Выполним отрисовку дерева для наглядного представления полученного классификатора

# In[19]:


from sklearn import tree


# In[20]:


tree_new = DecisionTreeClassifier(max_depth=2, min_samples_leaf=1, random_state=42)


# In[21]:


tree.plot_tree(tree_new.fit(X_train, y_train), 
               feature_names = ('temperature', 'velocity', 'pressure', 'incline', 'class'));


# Видно, что модель относит к первому классу аварийности те записи, телеметрия которых показывает значение давления меньше или равное 2,45. Из оставшихся записей, если показатель инклинометра меньше или равен 1,65 - запись относится ко второму классу аварийности, если выше - к третьему. 

# Проверим полученную модель на имеющихся тестовых данных.

# In[22]:


tree_grid_pred = tree_grid.predict(X_test)


# In[23]:


from sklearn.metrics import confusion_matrix, accuracy_score


# In[24]:


confusion_matrix(y_test, tree_grid_pred)


# In[25]:


accuracy_score(y_test, tree_grid_pred)


# Как видно, даже такая модель предоставляет крайне высокую процент верного определения класса, что значит что её целесообразно использовать для определения класса аварийности по телеметрическим показателям.

# ___

# Выполним так же построение простой нейронной сети с помощью библиотеки машинного обучения PyTorch.  
# 
# В данном случае использование нейронной сети является избыточным, ведь, как мы видели, эту задачу можно решить гораздо более простыми методами. Ко всему прочему нейронные сети функционируют по принципу черного ящика, и неприминимы в случаях, когда необходимо объяснить причину принятия решения. 
# 
# Однако в целом нейросети имеют множество преимуществ перед остальными методами (например устойчивость к шумам, адаптивность, высокое быстроействие и отказоустойчивость), поэтому спектр задач, в которых их применение оправдано достаточно широк.

# In[26]:


import torch
from torch import nn
from sklearn.preprocessing import StandardScaler


# Отмасштабируем данные и приведем их к виду, применимому в нейросетевой модели 

# In[27]:


scaler = StandardScaler()
X_nn = scaler.fit_transform(X_train)
X_nn_test = scaler.fit_transform(X_test)


# In[28]:


X_nn = torch.autograd.Variable(torch.FloatTensor(X_train))
y_nn = torch.autograd.Variable(torch.LongTensor(y_train.astype(np.int64))) - 1


# In[29]:


X_nn_test = torch.autograd.Variable(torch.FloatTensor(X_test))
y_nn_test = torch.autograd.Variable(torch.LongTensor(y_test.astype(np.int64))) - 1


# Зададим параметры математической модели. Сеть состоит из одного входного, двух скрытых  (в каждом по 50 нейронов) и одного выходного слоя. После каждого линейного слоя выполняется дропаут (это необходимо для избежания переобучения). Функция активации для каждого линейного слоя - ReLU.  
# Функция потерь - CrossEntropyLoss, наиболее часто применяемая для задач многоклассовой классификации.  
# Оптимизатор - Adam.

# In[30]:


torch.manual_seed(42) 
np.random.seed(42)
D_in, H1, H2, D_out = 4, 50, 50, 3
model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H1),
    torch.nn.Dropout(),
    torch.nn.ReLU(),
    torch.nn.Linear(H1, H2),
    torch.nn.Dropout(),
    torch.nn.ReLU(),
    torch.nn.Linear(H2, D_out),
)
    
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)


# Определим функцию обучениия. 

# In[31]:


def train(num_epoch):
    train_losses = []
    for i in range(num_epoch):
        model.train(True)
        y_pred = model(X_nn)
        loss = loss_fn(y_pred, y_nn)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        train_losses.append(loss.item())
    return train_losses


# Обучим модель на обучающей выборке. Выведем при этом значение функции потерь.

# In[32]:


train_losses = train(100)
plt.scatter(range(len(train_losses)), train_losses)


# In[33]:


model.train(False)


# Выполним предсказания на тестовых данных. 

# In[34]:


y_pred = model(X_nn_test)


# Модель выдает вероятность принадлежности объекта к каждому классу. Получим класс с наибольшей вероятностью для каждого объекта. 

# In[35]:


_, index = torch.max(y_pred,1)


# In[36]:


accuracy_score(y_nn_test, index)


# Как видно процент верно предсказаных значений на тестовых данных превышает такой процент для решающего дерева (и равен 100%). Это хорошо характеризует полученную модель. 

# Код для проверки на внешних тестовых данных. Необходимо поместить csv файл с данными с названием 'test.csv' в папку и запустить ячейки.

# In[ ]:


test=pd.read_csv('./data/test.csv')


# In[ ]:


test.loc[:,'class']=test['class'].map({'Class_1': 1, 'Class_2': 2, 'Class_3': 3})


# In[ ]:


X_val = data.iloc[:, 0:4].values
y_val = data.iloc[:, 4].values


# - Проверка дерева 

# In[ ]:


tree_test_pred = tree_grid.predict(X_val)


# In[ ]:


accuracy_score(y_val, tree_test_pred)


# - Проверка нейросети.

# In[ ]:


X_nn_val = scaler.fit_transform(X_val)


# In[ ]:


X_nn_val = torch.autograd.Variable(torch.FloatTensor(X_nn_val))
y_nn_val = torch.autograd.Variable(torch.LongTensor(y_val.astype(np.int64))) - 1


# In[ ]:


y_val_pred = model(X_nn_val)


# In[ ]:


_, index = torch.max(y_pred,1)


# In[ ]:


accuracy_score(y_nn_test, index)

