class DFPCA(BaseEstimator, TransformerMixin): # NOTE: # - DFPCA(n_components=df.shape[1]) to remain every dimensions # - DFPCA(rescale_with_mean=False, rescale_with_std=False) to avoid using built-in StandardScaler() def __init__(self, columns=None, prefix='pca_', **kwargs): self.columns = columns self.prefix = prefix self.model = PCA(**kwargs) self.transform_cols = None self.stat_df = None def fit(self, X, y=None): self.columns = X.columns if self.columns is None else self.columns self.transform_cols = [x for x in X.columns if x in self.columns] self.model.fit(X[self.transform_cols]) # Reference: Reference: https://www.appliedaicourse.com/lecture/11/applied-machine-learning-online-course/2896/pca-for-dimensionality-reduction-not-visualization/0/free-videos self.stat_df = pd.DataFrame({ 'dimension': [x+1 for x in range(len(self.model.eigenvalues_))], 'eigenvalues': self.model.eigenvalues_, 'explained_inertia': self.model.explained_inertia_, 'cumsum_explained_inertia': np.cumsum(self.model.explained_inertia_) }) return self def transform(self, X): if self.transform_cols is None: raise NotFittedError(f"This {self.__class__.__name__} instance is not fitted yet. Call 'fit' with appropriate arguments before using this estimator.") new_X = self.model.transform(X[self.transform_cols]) new_X.rename(columns=dict(zip(new_X.columns, [f'{self.prefix}{x}' for x in new_X.columns])), inplace=True) new_X = pd.concat([X.drop(columns=self.transform_cols), new_X], axis=1) return new_X def fit_transform(self, X, y=None): return self.fit(X).transform(X)
def model_interpretation(self, patient_id, patient_preprocessed, pred, prob, model): ''' Fazer gráficos avaliativos do modelo. Argumentos: patient_id = string referente a identificação do paciente patient_preprocessed = dicionario contendo dados do exame do paciente pred = classe predita pelo modelo prob = probabilidade referente a classe predita pelo modelo model = objeto do modelo ''' #### Pegar variaveis necessárias para o plot (import csv) #### Nome dos plots plot_1_name = 'app/ai_models/temp/probacurve-' + str( patient_id) + '.png' plot_2_name = 'app/ai_models/temp/shap-' + str(patient_id) + '.png' plot_3_name = 'app/ai_models/temp/dist-' + str(patient_id) + '.png' plot_4_name = 'app/ai_models/temp/mapa-' + str(patient_id) + '.png' #URL API PLOTS plot_1_api = "http://" + self.IP + ":" + self.API_PORT + "/api/media/probacurve-" + str( patient_id) + ".png" plot_2_api = "http://" + self.IP + ":" + self.API_PORT + "/api/media/shap-" + str( patient_id) + ".png" plot_3_api = "http://" + self.IP + ":" + self.API_PORT + "/api/media/dist-" + str( patient_id) + ".png" plot_4_api = "http://" + self.IP + ":" + self.API_PORT + "/api/media/mapa-" + str( patient_id) + ".png" #### Configurações gerais do plt DPI_IMAGES = 100 FONT_SIZE = 8 FONT_NAME = 'sans-serif' plt.rc('font', family=FONT_NAME, size=FONT_SIZE) plt.rc('axes', titlesize=FONT_SIZE, labelsize=FONT_SIZE) plt.rc('xtick', labelsize=FONT_SIZE) plt.rc('ytick', labelsize=FONT_SIZE) plt.rc('legend', fontsize=FONT_SIZE) #### PLOT 1 - Distribuição da probabilidade dada pelo modelo para pacientes positivos # Itens Necessário: self.probs_df(csv importado) e pred exame_resp = pred exame_prob = prob # Plot fig, axis = plt.subplots(nrows=1, ncols=1, figsize=(5, 5)) sns.kdeplot(self.probs_df['prob_neg'], shade=True, color='#386796', ax=axis, linestyle="--", label='Casos Negativos') sns.kdeplot(self.probs_df['prob_pos'], shade=True, color='#F06C61', ax=axis, label='Casos positivos') # Pegar eixo XY do Plt object para fazer a interpolação if exame_resp == 0: xi = 1 - exame_prob data_x, data_y = axis.lines[0].get_data() elif exame_resp == 1: xi = exame_prob data_x, data_y = axis.lines[1].get_data() # Fazer a interpolação e plot yi = np.interp(xi, data_x, data_y) axis.plot([xi], [yi], linestyle='None', marker="*", color='black', markersize=10, label='Paciente') # Outras configuracoes do plot axis.legend(loc="upper right") #axis.set_title('Probabilidade de ser COVID Positivo pelo modelo', fontweight='bold') axis.set_xlim([0, 1]) axis.set_ylim([0, axis.get_ylim()[1]]) plt.tight_layout() # Salvar plot 1 plt.savefig(plot_1_name, dpi=DPI_IMAGES, bbox_inches='tight', pad_inches=0.1) plt.close() #### PLOT 2 - SHAP # Necessário: patient_preprocessed, pred e model features = np.array(list(patient_preprocessed.keys())) sample_x = np.array(list(patient_preprocessed.values())) # Calcular SHAP Value explainer = TreeExplainer(model=model) # Faz o objeto SHAP shap_values_sample = explainer.shap_values(sample_x) # Calculo do SHAP expected_value = explainer.expected_value[ exame_resp] # Pega o baseline para a classe predita pelo modelo shap_values_sample = explainer.shap_values( sample_x) # Calcular os SHAP values # Plot #plt.title('Valores SHAP', fontweight='bold') waterfall_plot(expected_value, shap_values_sample[exame_resp], sample_x, feature_names=features, max_display=20, show=False) # Salvar imagem plt.tight_layout() plt.savefig(plot_2_name, dpi=DPI_IMAGES, bbox_inches='tight', pad_inches=0) plt.close() #### PLOT 3 - Distribuição das variáveis mais importantes para o modelo # Necessário: self.train_df(csv importado), patient_preprocessed, pred important_features = [ 'Leucócitos', 'Plaquetas', 'Hemácias', 'Eosinófilos' ] target_0 = self.train_df[self.train_df['target'] == 0][[ 'Leucócitos', 'Plaquetas', 'Hemácias', 'Eosinófilos' ]] target_1 = self.train_df[self.train_df['target'] == 1][[ 'Leucócitos', 'Plaquetas', 'Hemácias', 'Eosinófilos' ]] # Plot fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(10, 5)) # Plot settings #sns.set_color_codes() #st = fig.suptitle("Distribuição das variáveis importantes para o modelo", fontweight='bold') #st.set_y (1.05) # Index col/row r = 0 c = 0 # Loop to plot for feat in important_features: # Plot distribuição sns.kdeplot(list(target_0[feat]), shade=True, color='#386796', ax=axes[r][c], label='Casos Negativos', linestyle="--") sns.kdeplot(list(target_1[feat]), shade=True, color='#F06C61', ax=axes[r][c], label='Casos positivos') # Pegar a curva de densidade a partir do resultado do modelo if pred == 0: data_x, data_y = axes[r][c].lines[0].get_data() elif pred == 1: data_x, data_y = axes[r][c].lines[1].get_data() # Pegar a informação (valor) daquela variável importante xi = patient_preprocessed[feat] yi = np.interp(xi, data_x, data_y) ## Plot ponto na curva axes[r][c].plot([xi], [yi], linestyle='None', marker="*", color='black', markersize=10, label='Paciente') axes[r][c].set_title(feat) axes[r][c].legend(loc="upper right") axes[r][c].set_ylim([0, axes[r][c].get_ylim()[1]]) # Mudar onde sera plotado if c == 0: c += 1 else: r += 1 c = 0 # Ajeitar o plot plt.tight_layout() # Salvar imagem plt.savefig(plot_3_name, dpi=DPI_IMAGES, bbox_inches='tight', pad_inches=0.1) plt.close() #### PLOT 4 - Mapa com SVD para os pacientes # Necessário: train_df(csv importado), patient_preprocessed amostra = pd.DataFrame(patient_preprocessed, index=[ 0, ]).drop(axis=1, columns=['Outra gripe']) # Fazer PCA com SVD via prince package y_train = self.train_df['target'] # Salvar coluna target dados = self.train_df.drop( axis=1, columns=['Outra gripe', 'target']).copy() # Dataset para criar o mapa pca_obj = PCA(n_components=2, random_state=42) # Objeto do PCA pca_obj.fit(dados) # Fit no conjunto de dados componentes = pca_obj.transform( dados) # Criar os componentes principais dos dados transf = pca_obj.transform(amostra) # Transformar paciente para PCA xi = transf.loc[0, 0] # Eixo X do paciente para plot yi = transf.loc[0, 1] # Eixo Y do paciente para plot comp = pd.DataFrame() # Dataframe para conter os componentes comp['C1'] = componentes[0] # Componente Principal 1 comp['C2'] = componentes[1] # Componente Principal 2 comp['TG'] = y_train # Variável target para a mascara comp_0 = comp[comp['TG'] == 0][['C1', 'C2' ]] # Dataframe de CP para negativos comp_1 = comp[comp['TG'] == 1][['C1', 'C2' ]] # Dataframe de CP para positivos # Plot fig, ax = plt.subplots(figsize=(8, 8)) plt.margins(0, 0) sns.scatterplot(ax=ax, data=comp_0, x='C1', y='C2', color='#386796', label='Casos Negativos') sns.scatterplot(ax=ax, data=comp_1, x='C1', y='C2', color='#F06C61', label='Casos Positivos') x_mean, y_mean, width, height, angle = self.build_ellipse( comp_0['C1'], comp_0['C2']) ax.add_patch( Ellipse((x_mean, y_mean), width, height, angle=angle, linewidth=2, color='#386796', fill=True, alpha=0.2)) x_mean, y_mean, width, height, angle = self.build_ellipse( comp_1['C1'], comp_1['C2']) ax.add_patch( Ellipse((x_mean, y_mean), width, height, angle=angle, linewidth=2, color='#F06C61', fill=True, alpha=0.2)) ax.plot([xi], [yi], linestyle='None', marker="*", color='black', markersize=10, label='Paciente') # Configurações do plot #ax.set_title('Similaridade entre pacientes',fontweight='bold') ax.set_xticks([]) ax.set_yticks([]) ax.set_ylabel('') ax.set_xlabel('') handles, labels = ax.get_legend_handles_labels() labels, handles = zip( *sorted(zip(labels, handles), key=lambda t: t[0])) ax.legend(handles, labels, loc="upper right") # Salvar imagem plt.axis('off') plt.savefig(plot_4_name, dpi=DPI_IMAGES, bbox_inches='tight', pad_inches=0) plt.close() # Retornar model_result = { 'prediction': pred, 'probability': str(round(prob * 100, 2)), 'probacurve': plot_1_api, 'shap_img': plot_2_api, 'dist_img': plot_3_api, 'mapa_img': plot_4_api } return model_result """