161 KiB
Raw Permalink Blame History

Датасет астероидов

Выведем записи и столбцы

In [1]:
import pandas as pd
df_subset = pd.read_csv("..//..//static//csv//neo.csv")
df = df_subset.head(15000)
print(df.columns)
df.head()
Index(['id', 'name', 'est_diameter_min', 'est_diameter_max',
       'relative_velocity', 'miss_distance', 'orbiting_body', 'sentry_object',
       'absolute_magnitude', 'hazardous'],
      dtype='object')
Out[1]:
id name est_diameter_min est_diameter_max relative_velocity miss_distance orbiting_body sentry_object absolute_magnitude hazardous
0 2162635 162635 (2000 SS164) 1.198271 2.679415 13569.249224 5.483974e+07 Earth False 16.73 False
1 2277475 277475 (2005 WK4) 0.265800 0.594347 73588.726663 6.143813e+07 Earth False 20.00 True
2 2512244 512244 (2015 YE18) 0.722030 1.614507 114258.692129 4.979872e+07 Earth False 17.83 False
3 3596030 (2012 BV13) 0.096506 0.215794 24764.303138 2.543497e+07 Earth False 22.20 False
4 3667127 (2014 GE35) 0.255009 0.570217 42737.733765 4.627557e+07 Earth False 20.09 True

Бизнес-цели:

  1. Повышение безопасности планеты от потенциальных угроз космических объектов.
  2. Оптимизация исследования космических объектов для использования в коммерческих или исследовательских миссиях.

Цели технического проекта:

Для 1-й бизнес-цели:

  • Создать веб-приложение или API, которое принимает параметры объекта и прогнозирует, опасен ли он для Земли.
  • Модель может использоваться в системах мониторинга космических объектов для предоставления оперативных оценок и предупреждений.
  • Включение автоматической системы оповещения для НАСА и других космических агентств с обновлениями по объектам, представляющим угрозу.

Для 2-й бизнес-цели:

  • Разработка модели, которая позволяет астрономам и специалистам по космосу загружать данные о новых объектах и получать предсказания о расстоянии их ближайшего сближения с Землей.
  • Создание системы мониторинга с графическим интерфейсом, отображающим траектории движения объектов и предполагаемые даты и расстояния их ближайших подходов.
  • Реализация системы оповещений на основе пороговых значений расстояний для идентификации особо опасных сближений.

Проверим датасет на пропущенные значения:

In [2]:
for i in df.columns:
    null_rate = df[i].isnull().sum() / len(df) * 100
    print(f'{i} Процент пустых значений: %{null_rate:.2f}')

print(df.isnull().sum())

df.isnull().any()
id Процент пустых значений: %0.00
name Процент пустых значений: %0.00
est_diameter_min Процент пустых значений: %0.00
est_diameter_max Процент пустых значений: %0.00
relative_velocity Процент пустых значений: %0.00
miss_distance Процент пустых значений: %0.00
orbiting_body Процент пустых значений: %0.00
sentry_object Процент пустых значений: %0.00
absolute_magnitude Процент пустых значений: %0.00
hazardous Процент пустых значений: %0.00
id                    0
name                  0
est_diameter_min      0
est_diameter_max      0
relative_velocity     0
miss_distance         0
orbiting_body         0
sentry_object         0
absolute_magnitude    0
hazardous             0
dtype: int64
Out[2]:
id                    False
name                  False
est_diameter_min      False
est_diameter_max      False
relative_velocity     False
miss_distance         False
orbiting_body         False
sentry_object         False
absolute_magnitude    False
hazardous             False
dtype: bool

Нулевых значений нет

Разобьём набор на 3 классических выборки: обучающую, тестовую и контрольную

In [ ]:
from sklearn.model_selection import train_test_split

df = df.dropna()
df = df.drop_duplicates()

X = df.drop(columns=['absolute_magnitude'])
y = df['absolute_magnitude']


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


X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.25, random_state=42)

print("Размер обучающей выборки:", X_train.shape)
print("Размер контрольной выборки:", X_val.shape)
print("Размер тестовой выборки:", X_test.shape)
Размер обучающей выборки: (9000, 9)
Размер контрольной выборки: (3000, 9)
Размер тестовой выборки: (3000, 9)

Построим несколько столбчатых диаграмм для визуализации распределения:

In [4]:
import matplotlib.pyplot as plt
import seaborn as sns

train_data = pd.DataFrame({'absolute_magnitude': y_train})
val_data = pd.DataFrame({'absolute_magnitude': y_val})
test_data = pd.DataFrame({'absolute_magnitude': y_test})

sns.histplot(train_data['absolute_magnitude'], kde=True)
plt.title('Распределение absolute_magnitude в обучающей выборке')
plt.show()

sns.histplot(val_data['absolute_magnitude'], kde=True)
plt.title('Распределение absolute_magnitude в контрольной выборке')
plt.show()

sns.histplot(test_data['absolute_magnitude'], kde=True)
plt.title('Распределение absolute_magnitude в тестовой выборке')
plt.show()
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image

Конструирование признаков

Унитарное кодирование

Унитарное кодирование категориальных признаков (one-hot encoding). Преобразуем категориальные признаки в бинарные векторы.

In [6]:
from sklearn.preprocessing import OneHotEncoder

df = pd.read_csv("..//..//static//csv//neo.csv")

categorical_columns = ['orbiting_body', 'sentry_object']

df_encoded = pd.get_dummies(df, columns=categorical_columns)

print(df_encoded.head())
        id                 name  est_diameter_min  est_diameter_max  \
0  2162635  162635 (2000 SS164)          1.198271          2.679415   
1  2277475    277475 (2005 WK4)          0.265800          0.594347   
2  2512244   512244 (2015 YE18)          0.722030          1.614507   
3  3596030          (2012 BV13)          0.096506          0.215794   
4  3667127          (2014 GE35)          0.255009          0.570217   

   relative_velocity  miss_distance  absolute_magnitude  hazardous  \
0       13569.249224   5.483974e+07               16.73      False   
1       73588.726663   6.143813e+07               20.00       True   
2      114258.692129   4.979872e+07               17.83      False   
3       24764.303138   2.543497e+07               22.20      False   
4       42737.733765   4.627557e+07               20.09       True   

   orbiting_body_Earth  sentry_object_False  
0                 True                 True  
1                 True                 True  
2                 True                 True  
3                 True                 True  
4                 True                 True  

Дискретизация числовых признаков

Процесс преобразования непрерывных числовых значений в дискретные категории или интервалы (бины).

In [ ]:
import pandas as pd

df['miss_distance_binned'] = pd.cut(df['miss_distance'], bins=5)

df['absolute_magnitude_binned'] = pd.qcut(df['absolute_magnitude'], q=4)

print(df[['miss_distance', 'miss_distance_binned']].head())
print(df[['absolute_magnitude', 'absolute_magnitude_binned']].head())
   miss_distance          miss_distance_binned
0   5.483974e+07  (44881889.084, 59840270.268]
1   6.143813e+07  (59840270.268, 74798651.452]
2   4.979872e+07  (44881889.084, 59840270.268]
3   2.543497e+07    (14965126.716, 29923507.9]
4   4.627557e+07  (44881889.084, 59840270.268]
   absolute_magnitude   absolute_magnitude_binned
0               16.73  (9.229000000000001, 21.34]
1               20.00  (9.229000000000001, 21.34]
2               17.83  (9.229000000000001, 21.34]
3               22.20               (21.34, 23.7]
4               20.09  (9.229000000000001, 21.34]

Ручной синтез

Создание новых признаков на основе экспертных знаний и логики предметной области. В нашем случае можно задействовать расстояния объекта от Земли и скорость движения объекта, синтезировав новый признак - "скорость в сравнении с расстоянием". Этот признак показывает, что объект может быть более опасным, если его скорость велика, а расстояние до Земли — маленькое.

In [ ]:
# Создание нового признака 'Speed VS Distance'
df['high_risk'] = ((df['miss_distance'] < threshold_distance) & (df['relative_velocity'] > threshold_velocity)).astype(int)

Масштабирование признаков

Процесс преобразования числовых признаков таким образом, чтобы они имели одинаковый масштаб. Это важно для многих алгоритмов машинного обучения, которые чувствительны к масштабу признаков, таких как линейная регрессия, метод опорных векторов (SVM) и нейронные сети.

In [6]:
from sklearn.preprocessing import StandardScaler, MinMaxScaler

# Пример масштабирования числовых признаков
numerical_features = ['miss_distance', 'absolute_magnitude']

scaler = StandardScaler()
df_encoded[numerical_features] = scaler.fit_transform(df_encoded[numerical_features])
df_encoded[numerical_features] = scaler.transform(df_encoded[numerical_features])
df_encoded[numerical_features] = scaler.transform(df_encoded[numerical_features])
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[6], line 7
      4 numerical_features = ['miss_distance', 'absolute_magnitude']
      6 scaler = StandardScaler()
----> 7 df_encoded[numerical_features] = scaler.fit_transform(df_encoded[numerical_features])
      8 df_encoded[numerical_features] = scaler.transform(df_encoded[numerical_features])
      9 df_encoded[numerical_features] = scaler.transform(df_encoded[numerical_features])

NameError: name 'df_encoded' is not defined
In [9]:
import pandas as pd
import featuretools as ft
from sklearn.model_selection import train_test_split

df = pd.read_csv("..//..//static//csv//neo.csv")

df['id'] = range(1, len(df) + 1)

df = df.drop_duplicates()

es = ft.EntitySet(id='objects_data')

es = es.add_dataframe(
    dataframe_name='objects',
    dataframe=df,
    index='id'
)

feature_matrix, feature_defs = ft.dfs(entityset=es, target_dataframe_name='objects', max_depth=1)

print(feature_matrix.head())

train_data, test_data = train_test_split(df, test_size=0.3, random_state=42)

val_data, test_data = train_test_split(test_data, test_size=0.5, random_state=42)

val_feature_matrix = ft.calculate_feature_matrix(features=feature_defs, entityset=es, instance_ids=val_data['id'])
test_feature_matrix = ft.calculate_feature_matrix(features=feature_defs, entityset=es, instance_ids=test_data['id'])

print(val_feature_matrix.head())
print(test_feature_matrix.head())
e:\Aim\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
e:\Aim\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
    est_diameter_min  est_diameter_max  relative_velocity  miss_distance  \
id                                                                         
1           1.198271          2.679415       13569.249224   5.483974e+07   
2           0.265800          0.594347       73588.726663   6.143813e+07   
3           0.722030          1.614507      114258.692129   4.979872e+07   
4           0.096506          0.215794       24764.303138   2.543497e+07   
5           0.255009          0.570217       42737.733765   4.627557e+07   

   orbiting_body  sentry_object  absolute_magnitude  hazardous  
id                                                              
1          Earth          False               16.73      False  
2          Earth          False               20.00       True  
3          Earth          False               17.83      False  
4          Earth          False               22.20      False  
5          Earth          False               20.09       True  
       est_diameter_min  est_diameter_max  relative_velocity  miss_distance  \
id                                                                            
17465          0.265800          0.594347        6639.199305   7.248720e+07   
10057          0.023150          0.051765       66065.475247   2.182677e+07   
6905           0.148784          0.332690       35092.567329   6.261058e+07   
40989          0.007321          0.016370       24301.494107   2.765938e+06   
23499          0.044112          0.098637       33502.608133   7.025798e+07   

      orbiting_body  sentry_object  absolute_magnitude  hazardous  
id                                                                 
17465         Earth          False               20.00      False  
10057         Earth          False               25.30      False  
6905          Earth          False               21.26      False  
40989         Earth          False               27.80      False  
23499         Earth          False               23.90      False  
       est_diameter_min  est_diameter_max  relative_velocity  miss_distance  \
id                                                                            
66148          0.020163          0.045086       24899.946486   7.427192e+06   
68694          0.175612          0.392681       67322.863166   3.526971e+07   
17013          0.031809          0.071128       20216.336390   5.832689e+07   
69199          0.007321          0.016370       40616.528788   2.591562e+07   
45632          0.199781          0.446725       86281.198262   6.763452e+07   

      orbiting_body  sentry_object  absolute_magnitude  hazardous  
id                                                                 
66148         Earth          False               25.60      False  
68694         Earth          False               20.90       True  
17013         Earth          False               24.61      False  
69199         Earth          False               27.80      False  
45632         Earth          False               20.62       True  

Оценка качества каждого набора признаков

Представим основные оценки качества наборов признаков:

  • Предсказательная способность Метрики: RMSE, MAE, R²

    Методы: Обучение модели на обучающей выборке и оценка на контрольной и тестовой выборках.

  • Скорость вычисления

    Методы: Измерение времени выполнения генерации признаков и обучения модели.

  • Надежность

    Методы: Кросс-валидация, анализ чувствительности модели к изменениям в данных.

  • Корреляция

    Методы: Анализ корреляционной матрицы признаков, удаление мультиколлинеарных признаков.

  • Цельность

    Методы: Проверка логической связи между признаками и целевой переменной, интерпретация результатов модели.

In [10]:
import time
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

X = feature_matrix.drop('absolute_magnitude', axis=1)
y = feature_matrix['absolute_magnitude']

X = pd.get_dummies(X, drop_first=True)

X.fillna(X.median(), inplace=True)

X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)

model = LinearRegression()

start_time = time.time()
model.fit(X_train, y_train)

train_time = time.time() - start_time

predictions = model.predict(X_val)
mse = mean_squared_error(y_val, predictions)

print(f'Время обучения модели: {train_time:.2f} секунд')
print(f'Среднеквадратичная ошибка: {mse:.2f}')
Время обучения модели: 0.06 секунд
Среднеквадратичная ошибка: 5.08
In [11]:
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import r2_score, mean_absolute_error
from sklearn.model_selection import cross_val_score

feature_matrix = feature_matrix.dropna()
val_feature_matrix = val_feature_matrix.dropna()
test_feature_matrix = test_feature_matrix.dropna()

X_train = feature_matrix.drop('absolute_magnitude', axis=1)
y_train = feature_matrix['absolute_magnitude']
X_val = val_feature_matrix.drop('absolute_magnitude', axis=1)
y_val = val_feature_matrix['absolute_magnitude']
X_test = test_feature_matrix.drop('absolute_magnitude', axis=1)
y_test = test_feature_matrix['absolute_magnitude']

X_test = X_test.reindex(columns=X_train.columns, fill_value=0)  

X = pd.get_dummies(X, drop_first=True)

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

model = RandomForestRegressor(random_state=42)

model.fit(X_train, y_train)

y_pred = model.predict(X_test)

rmse = mean_squared_error(y_test, y_pred, squared=False)
r2 = r2_score(y_test, y_pred)
mae = mean_absolute_error(y_test, y_pred)

print()
print(f"RMSE: {rmse}")
print(f"R²: {r2}")
print(f"MAE: {mae} \n")

scores = cross_val_score(model, X_train, y_train, cv=5, scoring='neg_mean_squared_error')
rmse_cv = (-scores.mean())**0.5
print(f"Кросс-валидация RMSE: {rmse_cv} \n")

feature_importances = model.feature_importances_
feature_names = X_train.columns

y_train_pred = model.predict(X_train)

rmse_train = mean_squared_error(y_train, y_train_pred, squared=False)
r2_train = r2_score(y_train, y_train_pred)
mae_train = mean_absolute_error(y_train, y_train_pred)

print(f"Train RMSE: {rmse_train}")
print(f"Train R²: {r2_train}")
print(f"Train MAE: {mae_train}")
print()
e:\Aim\aimenv\Lib\site-packages\sklearn\metrics\_regression.py:492: FutureWarning: 'squared' is deprecated in version 1.4 and will be removed in 1.6. To calculate the root mean squared error, use the function'root_mean_squared_error'.
  warnings.warn(
RMSE: 0.007747870644321186
R²: 0.9999928256622078
MAE: 0.00013519980189125583 

Кросс-валидация RMSE: 0.010153168491376482 

Train RMSE: 0.004358914935336195
Train R²: 0.999997732046293
Train MAE: 4.508435629289199e-05

e:\Aim\aimenv\Lib\site-packages\sklearn\metrics\_regression.py:492: FutureWarning: 'squared' is deprecated in version 1.4 and will be removed in 1.6. To calculate the root mean squared error, use the function'root_mean_squared_error'.
  warnings.warn(