Вопрос или проблема
Я пытаюсь рассчитать значения SHAP для моих данных в модели RandomForest. Проблема в том, что мои данные – это временной ряд, и мне нужно получить среднее значение для всех значений SHAP в этих интервалах и нормализовать их. Вот мои воспроизводимые данные и код:
import pandas as pd
from pandas.tseries.offsets import MonthBegin, MonthEnd
from sklearn.preprocessing import StandardScaler
import shap
import numpy as np
from sklearn.ensemble import RandomForestRegressor
def adjust_dates_by_month(_dax_df: pd.DataFrame, _all_df: pd.DataFrame, _standardize: bool, _tone_WA_LM_ML_Hedo: int, base_year: int, base_month: int, interval: int):
# Рассчитываем новую базовую дату
base_date = pd.Timestamp(year=base_year, month=base_month, day=1) + pd.DateOffset(months=interval)
# Определяем даты начала и конца учебного периода
train_start = base_date
train_end = train_start + pd.DateOffset(months=2)
# Определяем период оценки
evaluate_start = train_end + MonthBegin(1)
evaluate_end = evaluate_start + pd.DateOffset(months=1) - pd.DateOffset(days=1)
# Определяем период для прогноза
to_be_predicted_start = evaluate_end + MonthBegin(1)
to_be_predicted_end = to_be_predicted_start + pd.DateOffset(years=1) - pd.DateOffset(days=1)
# Присваиваем даты датафрейму
_all_df['date'] = pd.to_datetime(_all_df[['year', 'month']].assign(day=1))
# Фильтруем строки для обучающего набора данных
train_df = _all_df[(_all_df['date'] >= train_start) & (_all_df['date'] <= train_end)]
# Фильтруем строки для набора данных для оценки
evaluate_df = _all_df[(_all_df['date'] >= evaluate_start) & (_all_df['date'] <= evaluate_end)]
# Фильтруем строки для набора данных, который нужно предсказать
to_be_predicted_df = _all_df[(_all_df['date'] >= to_be_predicted_start) & (_all_df['date'] <= to_be_predicted_end)]
if _standardize:
scaler = StandardScaler()
if _tone_WA_LM_ML_Hedo == 0: # Пример условия для тональных корректировок
additional_columns = ["Tone", "Positive_Score", "Negative_Score", "Polarity", "Activity_Reference_Density", "Self_Group_Reference_Density"]
_all_df[additional_columns] = scaler.fit_transform(_all_df[additional_columns])
train_df=train_df[['Code','Date', 'year', 'month','M','T','D','C','P','O','TE','Tone','Positive_Score','Negative_Score','Polarity','Activity_Reference_Density','Self_Group_Reference_Density']]
evaluate_df=evaluate_df[['Code','Date', 'year', 'month','M','T','D','C','P','O','TE','Tone','Positive_Score','Negative_Score','Polarity','Activity_Reference_Density','Self_Group_Reference_Density']]
to_be_predicted_df=to_be_predicted_df[['Code','Date', 'year', 'month','M','T','D','C','P','O','TE','Tone','Positive_Score','Negative_Score','Polarity','Activity_Reference_Density','Self_Group_Reference_Density']]
return train_df.reset_index(drop=True),evaluate_df.reset_index(drop=True),to_be_predicted_df.reset_index(drop=True)
# Пример использования
data = {
'Code': [3711, 3711, 3711, 3711, 3711, 3711, 3711, 3711, 3711, 3711],
'Date': pd.to_datetime(['2015-03-01', '2015-04-01', '2015-05-01', '2015-06-01', '2015-07-01', '2015-08-01', '2015-09-01', '2015-10-01', '2015-11-01', '2015-12-01']),
'M': [70042064, 63624232, 61003728, 62000000, 63000000, 64000000, 65000000, 66000000, 67000000, 68000000],
'T': [1.63, 1.63, 1.63, 1.60, 1.65, 1.70, 1.75, 1.80, 1.85, 1.90],
'D': [2.13, 2.13, 2.13, 2.10, 2.12, 2.14, 2.16, 2.18, 2.20, 2.22],
'C': [1.77, 1.77, 1.77, 1.75, 1.73, 1.71, 1.69, 1.67, 1.65, 1.63],
'P': [2.06, 2.06, 2.06, 2.04, 2.02, 2.00, 1.98, 1.96, 1.94, 1.92],
'O': [1.05, 1.05, 1.05, 1.07, 1.09, 1.11, 1.13, 1.15, 1.17, 1.19],
'TE': [1.12, 1.04, 1.31, 1.33, 1.35, 1.37, 1.39, 1.41, 1.43, 1.45],
'Tone': [0.5, 0.6, 0.55, 0.57, 0.59, 0.61, 0.63, 0.65, 0.67, 0.69],
'Positive_Score': [0.1, 0.1, 0.1, 0.11, 0.11, 0.12, 0.12, 0.13, 0.13, 0.14],
'Negative_Score': [0.05, 0.07, 0.06, 0.06, 0.06, 0.07, 0.07, 0.08, 0.08, 0.09],
'Polarity': [0.5, 0.6, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62],
'Activity_Reference_Density': [0.07, 0.07, 0.07, 0.08, 0.08, 0.09, 0.09, 0.10, 0.10, 0.11],
'Self_Group_Reference_Density': [0.03, 0.03, 0.03, 0.04, 0.04, 0.05, 0.05, 0.06, 0.06, 0.07]
}
df = pd.DataFrame(data)
# Добавление столбцов Год и Месяц
df['year'] = df['Date'].dt.year
df['month'] = df['Date'].dt.month
я
shap_values_list = []
for _interval in range(0, 4):
train,evaluate,to_be_predicted_df = adjust_dates_by_month(_dax_df=df, _all_df=df, _standardize=True, _tone_WA_LM_ML_Hedo=0, base_year=2015, base_month=3, interval=_interval)
model=RandomForestRegressor()
model.fit(train.iloc[:, 5:], train['M'])
eval = to_be_predicted_df.iloc[:, 5:]
explainer = shap.Explainer(model.predict, eval)
shap_values_list.append(explainer(eval).values)
На данный момент мой код вычисляет значения SHAP, однако у меня есть, как уже упоминалось, 3 проблемы
- Я не вижу названия столбцов в графике, как мне передать названия столбцов в график?
- Как мне рассчитать среднее значение из этих чисел, чтобы у меня было одно значение для каждого столбца?
- Как мне нормализовать данные так, чтобы сумма равнялась 1?
Этот код как-то работает, когда интервал равен 1, но если больше 1, тогда не работает.
all_shap_values = np.array(shap_values_list)
mean_shap_values = np.mean(all_shap_values, axis=0)
normalized_shap_values = mean_shap_values / np.sum(mean_shap_values)
shap.summary_plot(normalized_shap_values, plot_type="bar", show=False)
bars = plt.gca().get_children()
plt.xlabel('')
plt.show()
Ответ или решение
Ответ на вопрос по SHAP и Mean в NumPy
Ваш запрос включает несколько вопросов относительно расчета SHAP-значений и их обработки в контексте временных рядов. Давайте разберем каждый из ваших вопросов подробно.
1. Как передать названия колонок в график SHAP?
Для того чтобы отобразить названия колонок на графике SHAP, вы можете использовать параметр feature_names
в функции shap.summary_plot
. Вам нужно будет передать список названий колонок, соответствующих вашим данным, например:
feature_names = ['M', 'T', 'D', 'C', 'P', 'O', 'TE', 'Tone', 'Positive_Score', 'Negative_Score', 'Polarity', 'Activity_Reference_Density', 'Self_Group_Reference_Density']
shap.summary_plot(normalized_shap_values, feature_names=feature_names, plot_type="bar", show=False)
2. Как рассчитать средние значения из SHAP?
Средние значения SHAP-значений для каждого признака можно получить с помощью функции np.mean
, как вы уже начали делать. Однако следует убедиться, что каждая подгруппа SHAP-значений имеет одинаковую форму. После того как все значения будут собраны в один массив, можно будет выполнить следующую операцию:
all_shap_values = np.array(shap_values_list)
mean_shap_values = np.mean(all_shap_values, axis=0)
3. Как нормализовать данные так, чтобы их сумма равнялась 1?
Нормализацию можно провести, разделив каждое значение на сумму всех значений. Вы правильно подходите к этой задаче:
normalized_shap_values = mean_shap_values / np.sum(mean_shap_values)
Это приведет к тому, что сумма нормализованных SHAP-значений всегда будет равна 1.
Пример кода
Рассмотрим пример кода, который объединяет все вышеперечисленные шаги:
import matplotlib.pyplot as plt
# Собираем SHAP-значения
shap_values_list = []
for _interval in range(0, 4):
train, evaluate, to_be_predicted_df = adjust_dates_by_month(_dax_df=df, _all_df=df, _standardize=True, _tone_WA_LM_ML_Hedo=0, base_year=2015, base_month=3, interval=_interval)
model = RandomForestRegressor()
model.fit(train.iloc[:, 5:], train['M'])
eval = to_be_predicted_df.iloc[:, 5:]
explainer = shap.Explainer(model.predict, eval)
shap_values_list.append(explainer(eval).values)
# Рассчитываем средние значения
all_shap_values = np.array(shap_values_list)
mean_shap_values = np.mean(all_shap_values, axis=0)
# Нормализация
normalized_shap_values = mean_shap_values / np.sum(mean_shap_values)
# Названия признаков
feature_names = ['M', 'T', 'D', 'C', 'P', 'O', 'TE', 'Tone', 'Positive_Score', 'Negative_Score', 'Polarity', 'Activity_Reference_Density', 'Self_Group_Reference_Density']
# Построение графика
shap.summary_plot(normalized_shap_values, feature_names=feature_names, plot_type="bar", show=False)
plt.xlabel('')
plt.show()
Заключение
Теперь вы можете визуализировать ваши SHAP-значения с помощью корректных названий колонок, а также правильно вычислить их средние и нормализованные значения. Обратите внимание, если ваш код не работает при интервалах больше 1, это может быть связано со структурой данных или с самим процессом предсказания. Убедитесь, что ваши данные не теряются при фильтрации в функции adjust_dates_by_month
.
Если у вас есть дополнительные вопросы или вам нужна помощь, не стесняйтесь обращаться!