Project: s41467-024-46000-9 | Function: get_sudo_info (python)

system: You are an expert Python programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow Python best practices and maintain consistent code style.
Return **only** the complete Python function code.

user: Complete this Python function:

**DESCRIPTION:** Calculates pseudo-label discrepancy (SUDO) by computing the difference in performance between pseudo negative and pseudo positive label classifiers

**KEY TERMS:** {
  "low_label": "pseudo negative labels",
  "high_label": "pseudo positive labels",
  "g": "grouped SUDO calculation by threshold",
  "metric": "performance metric (e.g., Val AUC)",
  "df": "dataframe with pseudo-label experiment results"
}

**CONTEXT CODE:**     
    settings = {'weak supervision list':weak_supervision_list,
                'task list':task_list,
                'labs list':labs_list,
                'documents list':documents_list,
                'demo list':demo_list,
                'diagnosis list':diagnosis_list,
                'vitals list':vitals_list,
                'medications list':medications_list,
                'feature list':features_list,
                'ngram list':ngram_list,
                'nfeatures list':nfeatures_list,            
                'imputation type list':imputation_type_list,
                'sampling strategy list':sampling_strategy_list,
                'scale list':scale_list,
                'model list':model_list,
                'inference threshold side list':inference_threshold_side_list,
                'inference threshold list':inference_threshold_list,                    
                }
    
    if goal == 'validate tgt data':
        df = pd.DataFrame()
        for inference_threshold_side_list,inference_threshold_list in zip([['low']],[np.arange(0.05,0.5,0.05)]): #(0.05,0.5,0.05) for HAM10000 # (0.1,1.1,0.1) for DeepDerm # (0.1,0.5,0.1) for HAM10000 fairness experiments 
            for switch in [False,True]:
                settings = get_settings(inference_threshold_side_list,inference_threshold_list)
                results_df, probs_df = run_variants(settings,setting=setting,label_name='Combined',suffix=suffix,lab_collapse_strategy=lab_collapse_strategy,
                                             doc_collapse_strategy=doc_collapse_strategy,labelled_and_unlabelled=labelled_and_unlabelled,goal=goal,batch_size=batch_size,
                                             dataset_type=dataset_type,max_inference_seed=max_inference_seed,lr=lr,verbose=verbose,save_model_flag=save_model_flag,switch=switch)
                
                if inference_threshold_side_list == ['low']:
                    if switch == False:
                        env = 'Pseudo Low Label'
                    else:
                        env = 'Pseudo High Label'
                elif inference_threshold_side_list == ['high']:
                    results_df['Threshold'] = results_df['Threshold'] + 0.1
                    if switch == False:
                        env = 'Pseudo High Label'
                    else:
                        env = 'Pseudo Low Label'
                results_df['Setup'] = env
                df = pd.concat((df,results_df),0)
        
    else:
        results_df, probs_df = run_variants(settings,setting=setting,label_name='Combined',suffix=suffix,lab_collapse_strategy=lab_collapse_strategy,
                                     doc_collapse_strategy=doc_collapse_strategy,labelled_and_unlabelled=labelled_and_unlabelled,goal=goal,batch_size=batch_size,
                                     dataset_type=dataset_type,max_inference_seed=max_inference_seed,lr=lr,verbose=verbose,save_model_flag=save_model_flag)
    
    
#%%
def get_sudo_info(model_type='HAM10000',metric='Val AUC',wild_data_setting='out-of-domain',validation_label_noise=0):
    # MASKED: Calculate pseudo-label discrepancy (SUDO) by computing difference in performance between pseudo negative and pseudo positive label classifiers implementation
    raise NotImplementedError("This function is masked for testing purposes.")
    
def generate_violin_plot(model_type='HAM10000',metric='Val AUC',wild_data_setting='out-of-domain',validation_label_noise=0):
    df, g = get_sudo_info(model_type,metric,wild_data_setting,validation_label_noise)
    """ Violin Plot """
    low_label, high_label = 'Pseudo Negative', 'Pseudo Positive'
    palette = {low_label: 'royalblue', high_label: 'forestgreen'}
    fig,axes = plt.subplots(2,1,figsize=(14,8),gridspec_kw={'height_ratios':(0.05,0.95)})
    axes[0].remove()
    ax = axes[1]
    sns.violinplot(x='Threshold',y=metric,hue='Setup',data=df,ax=ax,palette=palette)
    ax.set_xticklabels(list(map(lambda el:'%.2f' % el,sorted(df['Threshold'].unique().tolist()))))
    ax.set_xlabel('Threshold')
    legend,handles = ax.get_legend_handles_labels()
    ax.legend(legend,handles,loc='upper center',bbox_to_anchor=(0.5,1.2),frameon=True,ncol=2,facecolor='white')
    #ax.set_ylim([0.40,0.75])
    
    # """ Waterfall Plot """
    # fig,ax = plt.subplots(figsize=(14,8))
    # sns.barplot(x='Threshold',y='Val AUC',data=g,ec='black',ax=ax,palette=sns.diverging_palette(150, 275, n=13))
    # ax.set_xticklabels(list(map(lambda el:'%.2f' % el,sorted(g['Threshold'].unique().tolist()))))
    # ax.set_xlabel('Threshold')
    # ax.set_ylabel('Pseudo Label Discrepancy')

def generate_sudo_plot(model_type='HAM10000',metric='Val AUC',wild_data_setting='out-of-domain',validation_label_noise=0):
    df, g = get_sudo_info(model_type,metric,wild_data_setting,validation_label_noise)
    fig,axes = plt.subplots(2,1,figsize=(14,8),gridspec_kw={'height_ratios':(0.05,0.95)})
    axes[0].remove()
    ax = axes[1]
    sns.barplot(x=np.arange(len(g)),y=metric,data=g,ec='black',ax=ax,palette=g['Color'])
    ax.set_xlabel('Threshold')
    ax.set_ylabel('Pseudo Label Discrepancy')
    ax.set_xticklabels(list(map(lambda el:'%.2f' % el,sorted(g['Threshold'].unique().tolist()))))
    ax.set_ylim([-1.1,1.1])
    
    # ax2 = ax.twinx()
    # sns.scatterplot(x=np.arange(len(g)),y='Proportion Low',ec='k',ci=None,color='goldenrod',s=200,data=g,ax=ax2)
    # ax.set_xticklabels(list(map(lambda el:'%.2f' % el,sorted(g['Threshold'].unique().tolist()))))
    # plt.ylabel('Proportion of Low Label')
    #legend,handles = ax2.get_legend_handles_labels()
    #ax2.legend(legend,handles)
    
def get_correlation(model_type='HAM10000',metric='Val AUC',wild_data_setting='out-of-domain',validation_label_noise=0):
    """ Correlation Plot """
    df, g = get_sudo_info(model_type,metric,wild_data_setting,validation_label_noise)
    probs_df = pd.read_csv('DDI_Probs_%s.csv' % model_type,index_col=0)
    
    prob_col = 'Prob' #option: Calib Prob
    if model_type == 'HAM10000':
        bins = np.arange(0,0.5,0.05) #np.arange(0,1.1,0.1)
        labels = np.arange(0.05,0.5,0.05)
    elif model_type == 'DeepDerm':
        bins = np.arange(0,1.1,0.1) #np.arange(0,1.1,0.1)
        labels = np.arange(0.1,1.1,0.1)
    probs_df['Range'] = pd.cut(probs_df[prob_col],bins=bins,labels=labels)
    counts = probs_df['Range'].value_counts().tolist()
    acc = probs_df.groupby(by=['Range'])['Label'].mean().reset_index(drop=True)
    acc.index = g.index
    g['Proportion High'] = acc #1-acc
    
    g['Count Weight'] = np.array(counts)/sum(counts)
    g['Bin Acc'] = acc
    bin_probs = probs_df.groupby(by=['Range'])[prob_col].mean()
    bin_probs.index = g.index
    g['Bin Prob'] = bin_probs
    
    corr = g[[metric,'Proportion High']].corr()
    corr = corr[metric]['Proportion High']
    return df, g, corr
    #ece = np.sum(np.abs(g['Bin Prob'] - g['Bin Acc']) * g['Count Weight'])
    
    # """ ece for PDM """    
    # probs_df = pd.read_csv('books_to_books_Val_Probs_NN5000.csv',index_col=0)
    # prob_col = 'Prob' #option: Calib Prob
    # probs_df['Range'] = pd.cut(probs_df[prob_col],bins=np.arange(0,1.1,0.1),labels=np.arange(0.1,1.1,0.1))
    # counts = probs_df['Range'].value_counts().tolist()
    # acc = probs_df.groupby(by=['Range'])['Label'].mean().reset_index(drop=True)
    # acc.index = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]
    # acc = pd.DataFrame(acc)
    
    # probs_df = pd.read_csv('books_to_books_Calib_PDM_NN5000.csv',index_col=0)
    # probs_df['Label'] = probs_df['Label'].round(1)
    # probs_df.index = probs_df['Label']
    # g = probs_df.merge(acc,how='left',left_index=True,right_index=True)    
    # g.columns = ['Calib PDM','PDM','Threshold','Bin Acc']
    # ece = np.mean(np.abs(g['Calib PDM'] - g['Bin Acc']))# * g['Count Weight'])

def generate_correlation_plot(model_type='HAM10000',metric='Val AUC',wild_data_setting='out-of-domain',validation_label_noise=0):
    df, g, corr = get_correlation(model_type,metric,wild_data_setting,validation_label_noise)
    m = LinearRegression()
    m.fit(pd.DataFrame(g[metric]),pd.DataFrame(g['Proportion High']))
    
    line = m.predict(pd.DataFrame(np.arange(g[metric].min()-0.05,g[metric].max()+0.05,0.05)))
    fig,axes = plt.subplots(2,1,figsize=(14,8),gridspec_kw={'height_ratios':(0.05,0.95)})
    axes[0].remove()
    ax = axes[1]
    sns.scatterplot(g[metric],g['Proportion High'],ci=None,s=200,ec='k',color='goldenrod',ax=ax,label=r'$\rho$ = %.2f' % corr)
    sns.lineplot(np.arange(g[metric].min()-0.05,g[metric].max()+0.05,0.05),line.squeeze(),lw=2,color='royalblue',ax=ax)#,label=r'$\rho$=0.95')


Complete the function implementation replacing "raise NotImplementedError".