{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "8552e959",
   "metadata": {},
   "outputs": [],
   "source": [
    "import re\n",
    "import json\n",
    "import shap\n",
    "import warnings\n",
    "import itertools\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from tqdm import tqdm\n",
    "from collections import defaultdict\n",
    "from joblib import Parallel, delayed\n",
    "\n",
    "from scipy.stats import spearmanr\n",
    "from sklearn.metrics import f1_score\n",
    "from sklearn.dummy import DummyClassifier\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "from sklearn.exceptions import ConvergenceWarning\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.model_selection import GridSearchCV, StratifiedKFold, train_test_split\n",
    "\n",
    "import seaborn as sns\n",
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib.patches import Patch\n",
    "mpl.rcParams['figure.dpi'] = 100\n",
    "mpl.rcParams['font.family'] = 'DeJavu Serif'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eac4d7e4",
   "metadata": {},
   "source": [
    "### Logistic Regression for Feature Importance"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e2a18b65",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_status(answer, modes):\n",
    "    if len(modes) == num_opt: return 'Absent'\n",
    "    if len(modes) == 1:\n",
    "        if modes[0] == answer: return 'Consistent Correct'\n",
    "        else: return 'Consistent Wrong'\n",
    "    if answer in modes: return 'Conflicting Correct'\n",
    "    else: return 'Conflicting Wrong'\n",
    "\n",
    "def get_results(data, abbre2model):\n",
    "    dataset = pd.read_csv(f'Data/Input/{data}.csv')\n",
    "    idx2answer = {idx:answer for idx, answer in enumerate(dataset['answer'])}\n",
    "    model2memory, model2context = {}, {}\n",
    "    for abbre, model in abbre2model.items():\n",
    "        idx2modes = json.load(open(f'Data/Status/{data}/{model}.json'))\n",
    "        model2memory[abbre] = [get_status(idx2answer[int(idx)], modes['memory']) for idx, modes in idx2modes.items()]\n",
    "        model2context[abbre] = [get_status(idx2answer[int(idx)], modes['context']) for idx, modes in idx2modes.items()]\n",
    "    return model2memory, model2context\n",
    "\n",
    "num_opt = 3\n",
    "versions = ['memory', 'context']\n",
    "statuses = ['Consistent Correct', 'Conflicting Correct', 'Absent', 'Conflicting Wrong', 'Consistent Wrong']\n",
    "datas = ['Hemonc', 'PubMedQA', 'NQ', 'HotpotQA']\n",
    "abbre2model = {'Gemma-2B':'gemma-2-2b-it', 'Gemma-9B':'gemma-2-9b-it', 'Gemma-27B':'gemma-2-27b-it', \n",
    "               'Llama-3B':'Llama-3.2-3B-Instruct', 'Llama-8B':'Llama-3.1-8B-Instruct', 'Llama-70B':'Llama-3.3-70B-Instruct', \n",
    "               'Qwen-3B':'Qwen2.5-3B-Instruct', 'Qwen-7B':'Qwen2.5-7B-Instruct', 'Qwen-14B':'Qwen2.5-14B-Instruct'}\n",
    "\n",
    "data2model2memory, data2model2context = {}, {}\n",
    "for data in datas:\n",
    "    data2model2memory[data], data2model2context[data] = get_results(data, abbre2model)\n",
    "data2features = {data:pd.read_csv(f'Data/Feature/{data}.csv') for data in datas}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d0b9c61c",
   "metadata": {},
   "outputs": [],
   "source": [
    "def preprocess(source_status, data, model):    \n",
    "    features, labels = [], []\n",
    "    for idx, row in data2features[data].iterrows():\n",
    "        memory_status, context_status = data2model2memory[data][model][idx], data2model2context[data][model][idx]\n",
    "        if memory_status != source_status: continue\n",
    "        features.append(row[feature_columns+feature_columns_model(model)].to_list())\n",
    "        labels.append(context_status == target_status)\n",
    "    features, labels = np.array(features), np.array(labels)\n",
    "    if len(labels) < 50 or len(np.where(~labels)[0]) < 10: return None    \n",
    "    feature_train, feature_test, label_train, label_test = train_test_split(features, labels, stratify=labels, test_size=0.2, random_state=seed)\n",
    "    scaler = StandardScaler()\n",
    "    scaler.fit(feature_train)\n",
    "    feature_train = scaler.transform(feature_train)\n",
    "    feature_test = scaler.transform(feature_test)\n",
    "    return feature_train, feature_test, label_train, label_test\n",
    "\n",
    "def classify(feature_train, feature_test, label_train, label_test):\n",
    "    clf = LogisticRegression(penalty=\"l2\", solver=\"saga\", max_iter=10000, random_state=seed)\n",
    "    cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=seed)\n",
    "    grid_search = GridSearchCV(estimator=clf, cv=cv, param_grid=param_grid, scoring=\"f1_macro\", n_jobs=n_jobs)\n",
    "    grid_search.fit(feature_train, label_train)\n",
    "    best_model = grid_search.best_estimator_\n",
    "    label_pred = best_model.predict(feature_test)\n",
    "    score = f1_score(label_test, label_pred, average='macro')\n",
    "    dummy = DummyClassifier(strategy=\"most_frequent\")\n",
    "    dummy.fit(feature_train, label_train)\n",
    "    label_dummy = dummy.predict(feature_test)\n",
    "    baseline = f1_score(label_test, label_dummy, average='macro')\n",
    "    return best_model, (score, baseline)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "9ad0b8bf",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Consistent Correct: 100%|██████████████████████████████████████████████████████████████████████████████████████| 36/36 [02:31<00:00,  4.22s/it]\n",
      "Conflicting Correct: 100%|█████████████████████████████████████████████████████████████████████████████████████| 36/36 [01:19<00:00,  2.22s/it]\n",
      "Absent: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████| 36/36 [01:12<00:00,  2.03s/it]\n",
      "Conflicting Wrong: 100%|███████████████████████████████████████████████████████████████████████████████████████| 36/36 [00:23<00:00,  1.56it/s]\n",
      "Consistent Wrong: 100%|████████████████████████████████████████████████████████████████████████████████████████| 36/36 [01:40<00:00,  2.79s/it]\n"
     ]
    }
   ],
   "source": [
    "seed = 42\n",
    "target_status = 'Consistent Correct'\n",
    "feature_columns = ['Length', 'Readability', '# Uniq Tkns', 'Emb Sim'] + [f'rouge2-{n}' for n in ['P', 'R', 'F']]\n",
    "feature_columns_model = lambda m: [f'{m} {f} {k}' if m else f'{f} {k}' for f in ['Perp', 'Entr'] for k in ['Context', 'Question']]\n",
    "param_grid = {\"class_weight\": [None, \"balanced\", {0:2,1:1}, {0:5,1:1}, {0:10,1:1}],\n",
    "              \"C\": [0.01, 0.1, 1.0, 10.0]}\n",
    "\n",
    "remove_warn = lambda: warnings.filterwarnings(\"ignore\", category=ConvergenceWarning)\n",
    "n_jobs = 10\n",
    "remove_warn()\n",
    "Parallel(n_jobs=n_jobs)(delayed(remove_warn)() for _ in range(n_jobs))\n",
    "\n",
    "combi2input, combi2model, combi2score = {}, {}, {}\n",
    "for source_status in statuses:\n",
    "    with tqdm(total=len(datas)*len(abbre2model), desc=source_status) as pbar:\n",
    "        for data in datas:\n",
    "            for model in abbre2model:\n",
    "                combi = (source_status, data, model)\n",
    "                combi2input[combi] = preprocess(*combi)\n",
    "                if combi2input[combi]: combi2model[combi], combi2score[combi] = classify(*combi2input[combi])\n",
    "                pbar.update(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "41291aa5",
   "metadata": {},
   "source": [
    "### Regression Results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "9f280dfc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 211592 characters]",
      "text/plain": [
       "<Figure size 1450x472.5 with 6 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "status2change = {}\n",
    "for status in statuses:\n",
    "    status2change[status] = np.full((len(abbre2model), len(datas)), np.nan)\n",
    "    for mid, model in enumerate(abbre2model):\n",
    "        for did, data in enumerate(datas):\n",
    "            combi = (status, data, model)\n",
    "            if combi in combi2score:\n",
    "                score, baseline = combi2score[combi]\n",
    "                status2change[status][mid, did] = score - baseline\n",
    "\n",
    "vmin = min(change.min() for change in status2change.values())*100\n",
    "vmax = max(change.max() for change in status2change.values())*100\n",
    "\n",
    "fontsize = 12 # 10.5\n",
    "num_col, num_row = len(statuses), 1\n",
    "sub_col, sub_row = len(datas), len(abbre2model)\n",
    "size_col, size_row = 0.725, 0.525\n",
    "fig, axs = plt.subplots(num_row, num_col, constrained_layout=True, gridspec_kw={'wspace':0.04},\n",
    "                        figsize=(num_col*sub_col*size_col, num_row*sub_row*size_row))\n",
    "\n",
    "for sid, status in enumerate(statuses):\n",
    "    change = np.where(np.isnan(status2change[status]), 0, status2change[status])\n",
    "    annot = [['x' if np.isnan(entry) else f'{entry*100:+.1f}' for entry in row] for row in status2change[status]]\n",
    "    sns.heatmap(100*change, ax=axs[sid], linewidth=0.5, vmin=vmin, vmax=vmax, \n",
    "                yticklabels=sid==0, annot=annot, fmt='s', annot_kws={'fontsize':fontsize-0.5}, center=0,\n",
    "                cmap=\"coolwarm\", cbar=sid==len(statuses)-1, \n",
    "                cbar_kws={'label':'Change (%) in Macro-F1'})\n",
    "    axs[sid].set_title(status, fontsize=fontsize+2)\n",
    "    axs[sid].set_xticklabels(['Hemonc', 'PubMedQA', 'HotpotQA', 'NQ'], fontsize=fontsize, \n",
    "                             ha='right', va='center', rotation=90, rotation_mode=\"anchor\")\n",
    "\n",
    "axs[0].set_yticklabels(abbre2model.keys(), rotation=0, ha='right', va='center', fontsize=fontsize)\n",
    "\n",
    "cbar = axs[-1].collections[0].colorbar\n",
    "cbar.ax.tick_params(labelsize=fontsize)\n",
    "cbar.ax.yaxis.label.set_size(fontsize+1)\n",
    "    \n",
    "# plt.savefig(f'Figure/regression.pdf', bbox_inches='tight')\n",
    "plt.show()\n",
    "plt.close(fig)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f2c6610",
   "metadata": {},
   "source": [
    "### Top-5 Feature Ranking"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "43bf3516",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Consistent Correct\n",
      "Entr Context: 0.727 | rouge2-R: 0.727 | # Uniq Tkns: 0.636 | Length: 0.636 | Entr Question: 0.545 | Emb Sim: 0.500\n",
      "--------------------\n",
      "Conflicting Correct\n",
      "# Uniq Tkns: 0.625 | Emb Sim: 0.594 | Entr Context: 0.531 | Length: 0.531\n",
      "--------------------\n",
      "Absent\n",
      "Length: 0.727 | # Uniq Tkns: 0.727 | Entr Context: 0.682 | Entr Question: 0.545 | rouge2-R: 0.545\n",
      "--------------------\n",
      "Conflicting Wrong\n",
      "# Uniq Tkns: 0.714 | Entr Context: 0.619 | Length: 0.571 | Emb Sim: 0.524 | rouge2-P: 0.524\n",
      "--------------------\n",
      "Consistent Wrong\n",
      "Length: 0.556 | Emb Sim: 0.528 | Entr Question: 0.528 | Entr Context: 0.528 | rouge2-P: 0.500 | rouge2-R: 0.500\n",
      "--------------------\n",
      "\n",
      "Sig Level = 0.005 after Bonferroni Correction\n",
      "Consistent Correct   and Conflicting Correct  | cor = 0.809 | p-value = 0.003\n",
      "Consistent Correct   and Absent               | cor = 0.791 | p-value = 0.004\n",
      "Consistent Correct   and Conflicting Wrong    | cor = 0.718 | p-value = 0.013\n",
      "Consistent Correct   and Consistent Wrong     | cor = 0.491 | p-value = 0.125\n",
      "Conflicting Correct  and Absent               | cor = 0.755 | p-value = 0.007\n",
      "Conflicting Correct  and Conflicting Wrong    | cor = 0.882 | p-value = 0.000\n",
      "Conflicting Correct  and Consistent Wrong     | cor = 0.564 | p-value = 0.071\n",
      "Absent               and Conflicting Wrong    | cor = 0.755 | p-value = 0.007\n",
      "Absent               and Consistent Wrong     | cor = 0.527 | p-value = 0.096\n",
      "Conflicting Wrong    and Consistent Wrong     | cor = 0.591 | p-value = 0.056\n"
     ]
    }
   ],
   "source": [
    "top_n = 5\n",
    "fid2fea = {fid:fea for fid, fea in enumerate(feature_columns+feature_columns_model(''))}\n",
    "status2valid, status2freq = defaultdict(int), defaultdict(lambda: defaultdict(int))\n",
    "for status in statuses:\n",
    "    for data in datas:\n",
    "        for model in abbre2model:\n",
    "            combi = (status, data, model)\n",
    "            if combi in combi2model:\n",
    "                score, baseline = combi2score[combi]\n",
    "                if score > baseline:\n",
    "                    status2valid[status] += 1\n",
    "                    model = combi2model[combi]\n",
    "                    feature_train, feature_test, label_train, label_test = combi2input[combi]\n",
    "                    explainer = shap.LinearExplainer(model, feature_train)\n",
    "                    shap_values = explainer.shap_values(feature_test)\n",
    "                    fids = np.argsort(-np.abs(shap_values).mean(0))[:top_n]\n",
    "                    for fid in fids:\n",
    "                        status2freq[status][fid2fea[fid]] += 1\n",
    "\n",
    "for status in statuses:\n",
    "    status2freq[status] = {fea:freq/status2valid[status] for fea, freq in sorted(status2freq[status].items(), key=lambda x:x[1], reverse=True)}\n",
    "    fea_freq = [f'{fea}: {freq:.3f}' for fea, freq in status2freq[status].items() if freq >= 0.5]\n",
    "    print(status + '\\n' + ' | '.join(fea_freq) + '\\n' + '-'*20)\n",
    "    \n",
    "fea2fid = {fea:fid for fid, fea in fid2fea.items()}\n",
    "status2rank = {status:[0]*len(fid2fea) for status in statuses}\n",
    "for status in statuses:\n",
    "    for rank, fea in enumerate(status2freq[status]):\n",
    "        status2rank[status][fea2fid[fea]] = rank\n",
    "        \n",
    "n_tests = len(list(itertools.combinations(statuses, 2)))\n",
    "alpha = 0.05 / n_tests\n",
    "print()\n",
    "print(f'Sig Level = {alpha:.3f} after Bonferroni Correction')\n",
    "\n",
    "for (s1, s2) in itertools.combinations(statuses, 2):\n",
    "    rho, pval = spearmanr(status2rank[s1], status2rank[s2])\n",
    "    print(f'{s1:20s} and {s2:20s} | cor = {rho:.3f} | p-value = {pval:.3f}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "2e734308",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 131104 characters]",
      "text/plain": [
       "<Figure size 1500x350 with 5 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "status2freq_clip = {\n",
    "    'Consistent Correct': {'ROUGE-2 R':0.727, 'Context Entropy':0.727, 'Context Length':0.636, '# Uniq Tkns':0.636, 'Question Entropy':0.545, 'Emb Sim':0.500},\n",
    "    'Conflicting Correct': {'# Uniq Tkns':0.625, 'Emb Sim':0.594, 'Context Length':0.531, 'Context Entropy':0.531},\n",
    "    'Absent': {'Context Length':0.727, '# Uniq Tkns':0.727, 'Context Entropy':0.682, 'ROUGE-2 R':0.545, 'Question Entropy':0.545},\n",
    "    'Conflicting Wrong': {'# Uniq Tkns':0.714, 'Context Entropy':0.619, 'Context Length':0.571, 'Emb Sim':0.524, 'ROUGE-2 P':0.524},\n",
    "    'Consistent Wrong': {'Context Length':0.556, 'Emb Sim':0.528, 'Question Entropy':0.528, 'Context Entropy':0.528, 'ROUGE-2 R':0.500, 'ROUGE-2 P':0.500}\n",
    "}\n",
    "statuses = status2freq_clip.keys()\n",
    "    \n",
    "fontsize = 14\n",
    "width, lw = 0.75, 0.5\n",
    "colors = sns.color_palette(\"tab20\")\n",
    "feature2color = {'Context Length':colors[3], '# Uniq Tkns':colors[3],\n",
    "                 'Emb Sim':colors[1], 'ROUGE-2 R':colors[1], 'ROUGE-2 P':colors[1],\n",
    "                 'Question Entropy':colors[9], 'Context Entropy':colors[9]}\n",
    "feature2hatch = {'Context Length':'.', '# Uniq Tkns':'x',\n",
    "                 'Emb Sim':'.', 'ROUGE-2 R':'x', 'ROUGE-2 P':'/',\n",
    "                 'Question Entropy':'.', 'Context Entropy':'x'}\n",
    "\n",
    "num_row, num_col = 1, 5\n",
    "size_row, size_col = 3.5, 3\n",
    "fig, axs = plt.subplots(num_row, num_col, figsize=(num_col*size_col, num_row*size_row), \n",
    "                        constrained_layout=True, sharey=True)\n",
    "\n",
    "for sid, status in enumerate(statuses):\n",
    "    sns.despine(ax=axs[sid], offset=dict(left=5))\n",
    "    axs[sid].set_ylim(top=100, bottom=0)\n",
    "    axs[sid].set_yticks(np.linspace(0,100,6))\n",
    "    axs[sid].tick_params(axis='both', which='major', labelsize=fontsize-1, labelbottom=False, bottom=False)\n",
    "    axs[sid].set_title(status, fontsize=fontsize+1)\n",
    "    \n",
    "    feas, freqs = status2freq_clip[status].keys(), np.array(list(status2freq_clip[status].values()))\n",
    "    color, hatch = [feature2color[fea] for fea in feas], [feature2hatch[fea] for fea in feas]\n",
    "    axs[sid].bar(range(len(feas)), freqs*100, label=feas, width=width, color=color, hatch=hatch, edgecolor='black', lw=lw)\n",
    "    for fid, freq in enumerate(freqs):\n",
    "        axs[sid].text(fid, (freq+0.025)*100, f'{freq*100:.1f}', fontsize=fontsize-1, ha='center', va='bottom', rotation='vertical')\n",
    "    \n",
    "axs[0].set_ylabel('Normalized Frequency (%)', fontsize=fontsize)\n",
    "labels = feature2hatch.keys()\n",
    "handles = [Patch(facecolor=feature2color[fea], edgecolor='black', hatch=feature2hatch[fea]*2, lw=lw) for fea in labels]\n",
    "fig.legend(handles, labels, fontsize=fontsize, ncol=len(feature2color), columnspacing=1.25,\n",
    "           bbox_to_anchor=(0.5,0), loc='upper center', bbox_transform=fig.transFigure)\n",
    "\n",
    "# plt.savefig('Figure/top_feature.pdf', bbox_inches='tight')\n",
    "plt.show()\n",
    "plt.close(fig)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "03204ba3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 53420 characters]",
      "text/plain": [
       "<Figure size 450x375 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "statuses2corrlations = {\n",
    "    ('Consistent\\nCorrect', 'Conflicting\\nCorrect'): (0.809, 0.003),\n",
    "    ('Consistent\\nCorrect', 'Absent'): (0.791, 0.004),\n",
    "    ('Consistent\\nCorrect', 'Conflicting\\nWrong'): (0.718, 0.013),\n",
    "    ('Consistent\\nCorrect', 'Consistent\\nWrong'): (0.491, 0.125),\n",
    "    ('Conflicting\\nCorrect', 'Absent'): (0.755, 0.007),\n",
    "    ('Conflicting\\nCorrect', 'Conflicting\\nWrong'): (0.882, 0.000),\n",
    "    ('Conflicting\\nCorrect', 'Consistent\\nWrong'): (0.564, 0.071),\n",
    "    ('Absent', 'Conflicting\\nWrong'): (0.755, 0.007),\n",
    "    ('Absent', 'Consistent\\nWrong'): (0.527, 0.096),\n",
    "    ('Conflicting\\nWrong', 'Consistent\\nWrong'): (0.591, 0.056),\n",
    "}\n",
    "statuses2corrlations = {tuple(sorted(k)):v for k,v in statuses2corrlations.items()}\n",
    "statuses = [status.replace(' ', '\\n') for status in statuses]\n",
    "\n",
    "adj_alpha = 0.05 / len(statuses2corrlations)\n",
    "correlations = np.ones((len(statuses), len(statuses)))\n",
    "annotations = [['1.000$^{}$' for _ in range(len(statuses))] for _ in range(len(statuses))]\n",
    "for i, status1 in enumerate(statuses):\n",
    "    for j, status2 in enumerate(statuses):\n",
    "        if status1 != status2:\n",
    "            corr, pvalue = statuses2corrlations[tuple(sorted((status1, status2)))]\n",
    "            correlations[i,j] = corr\n",
    "            annotations[i][j] = f'{corr:.3f}'\n",
    "            annotations[i][j] += '$^{\\u2605}$' if pvalue <= adj_alpha else '$^{}$'\n",
    "            \n",
    "fontsize = 9.5\n",
    "num_col, num_row = 1, 1\n",
    "sub_col, sub_row = len(statuses), len(statuses)\n",
    "size_col, size_row = 0.6*1.5, 0.5*1.5\n",
    "fig, ax = plt.subplots(num_row, num_col, constrained_layout=True,\n",
    "                       figsize=(num_col*sub_col*size_col, num_row*sub_row*size_row))\n",
    "\n",
    "sns.heatmap(correlations, ax=ax, linewidth=0.5, vmin=0.5, vmax=1.0,\n",
    "            annot=annotations, fmt=\"s\", annot_kws={'fontsize':fontsize},\n",
    "            cmap=\"Reds\", cbar_kws={'label':'Correlation'})\n",
    "ax.tick_params(top=True, labeltop=True, bottom=False, labelbottom=False)\n",
    "ax.set_xticklabels(statuses, fontsize=fontsize, ha='left', va='center', rotation=90, rotation_mode=\"anchor\")\n",
    "ax.set_yticklabels(statuses, fontsize=fontsize, ha='right', va='center', rotation=0)\n",
    "\n",
    "cbar = ax.collections[0].colorbar\n",
    "cbar.ax.tick_params(labelsize=fontsize-1)\n",
    "cbar.ax.yaxis.label.set_size(fontsize)\n",
    "\n",
    "# plt.savefig(f'Figure/rank_corr.pdf', bbox_inches='tight')\n",
    "plt.show()\n",
    "plt.close(fig)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
