afortuny commited on
Commit
266bd2c
·
1 Parent(s): 2f4f4ef

harmoniser complete

Browse files
Files changed (2) hide show
  1. functions.py +1 -1
  2. indicator_harmonizer.ipynb +80 -180
functions.py CHANGED
@@ -22,7 +22,7 @@ columns_embeddings_col2 = ['Indicator name (leonardo)']
22
 
23
  # ID column
24
 
25
- table1_id_col = ['Indicator ID']
26
  table2_id_col = ['ID']
27
 
28
  #### Functions
 
22
 
23
  # ID column
24
 
25
+ table1_id_col = ['ID']
26
  table2_id_col = ['ID']
27
 
28
  #### Functions
indicator_harmonizer.ipynb CHANGED
@@ -35,7 +35,7 @@
35
  },
36
  {
37
  "cell_type": "code",
38
- "execution_count": 10,
39
  "metadata": {},
40
  "outputs": [
41
  {
@@ -44,7 +44,7 @@
44
  "<module 'functions' from '/Users/alanfortunysicart/Documents/GitHub/IndicatorHarmonizer/functions.py'>"
45
  ]
46
  },
47
- "execution_count": 10,
48
  "metadata": {},
49
  "output_type": "execute_result"
50
  }
@@ -78,13 +78,23 @@
78
  },
79
  {
80
  "cell_type": "code",
81
- "execution_count": 11,
82
  "metadata": {},
83
  "outputs": [],
84
  "source": [
85
- "table1 = pd.read_excel('/Users/alanfortunysicart/Downloads/Indicators_Indicators_Frameworks_Default View.xlsx')\n",
86
- "table1.columns = ['ID',\t'Indicator Name','Framework','Definition','Description',\t'Indicators leonardo','Framework Version','Impact Category']\n",
87
- "table1['ID'] = table1['ID'].astype(str)"
 
 
 
 
 
 
 
 
 
 
88
  ]
89
  },
90
  {
@@ -96,7 +106,7 @@
96
  },
97
  {
98
  "cell_type": "code",
99
- "execution_count": 13,
100
  "metadata": {},
101
  "outputs": [],
102
  "source": [
@@ -112,12 +122,17 @@
112
  },
113
  {
114
  "cell_type": "code",
115
- "execution_count": 14,
116
  "metadata": {},
117
  "outputs": [],
118
  "source": [
119
  "table2 = pd.read_excel('/Users/alanfortunysicart/Downloads/Indicators_Indicators_Default view(14).xlsx')\n",
120
  "table2 = f.concatenate_columns(table2,columns=f.columns_embeddings_col2)\n",
 
 
 
 
 
121
  "\n"
122
  ]
123
  },
@@ -137,7 +152,7 @@
137
  },
138
  {
139
  "cell_type": "code",
140
- "execution_count": 15,
141
  "metadata": {},
142
  "outputs": [],
143
  "source": [
@@ -153,7 +168,7 @@
153
  },
154
  {
155
  "cell_type": "code",
156
- "execution_count": 16,
157
  "metadata": {},
158
  "outputs": [],
159
  "source": [
@@ -162,7 +177,7 @@
162
  },
163
  {
164
  "cell_type": "code",
165
- "execution_count": 17,
166
  "metadata": {},
167
  "outputs": [],
168
  "source": [
@@ -173,87 +188,35 @@
173
  },
174
  {
175
  "cell_type": "code",
176
- "execution_count": 19,
177
  "metadata": {},
178
  "outputs": [],
179
  "source": [
180
- "\n",
181
- "\n",
182
  "# Create a DataFrame for the similarities\n",
183
  "result_df = pd.DataFrame(similarities, \n",
184
- " columns=table2[f.table2_id_col])\n"
185
- ]
186
- },
187
- {
188
- "cell_type": "code",
189
- "execution_count": 130,
190
- "metadata": {},
191
- "outputs": [],
192
- "source": [
193
- "result_df['Indicator Client'] = mapped_indicators['ID']"
194
- ]
195
- },
196
- {
197
- "cell_type": "markdown",
198
- "metadata": {},
199
- "source": [
200
- "Store the similarities in a matrix format"
201
- ]
202
- },
203
- {
204
- "cell_type": "code",
205
- "execution_count": 131,
206
- "metadata": {},
207
- "outputs": [],
208
- "source": [
209
- "# Merge the data to get framework information for both client and indicators\n",
210
- "mapped_indicators_sel = mapped_indicators[['ID', 'Framework']]\n",
211
- "indicators_df_sel = indicators_df[['ID', 'Framework']]\n",
212
  "\n",
213
- "# Prepare a mapping from indicator ID to framework for both client and indicators\n",
214
- "client_framework_map = mapped_indicators_sel.set_index('ID')['Framework'].to_dict()\n",
215
- "indicator_framework_map = indicators_df_sel.set_index('ID')['Framework'].to_dict()\n",
216
  "\n",
217
  "# Function to check if there is any common framework element\n",
218
- "def has_common_framework(client_framework, indicator_framework):\n",
219
- " client_frameworks = set(client_framework.split(', '))\n",
220
- " indicator_frameworks = set(indicator_framework.split(', '))\n",
221
- " return not client_frameworks.isdisjoint(indicator_frameworks)\n",
 
 
 
 
 
 
 
 
 
 
222
  "\n",
223
- "# Replace similarity values with NaN where the frameworks match or contain a common element\n",
224
- "for client_id in mapped_indicators['ID']:\n",
225
- " client_framework = client_framework_map.get(client_id)\n",
226
- " for indicator_id in result_df.columns:\n",
227
- " if indicator_id != 'Indicator Client':\n",
228
- " indicator_framework = indicator_framework_map.get(indicator_id)\n",
229
- " if pd.notna(client_framework) and pd.notna(indicator_framework):\n",
230
- " # Check if there is any common framework element\n",
231
- " if has_common_framework(client_framework, indicator_framework):\n",
232
- " result_df.loc[result_df['Indicator Client'] == client_id, indicator_id] = np.nan\n"
233
- ]
234
- },
235
- {
236
- "cell_type": "code",
237
- "execution_count": 132,
238
- "metadata": {},
239
- "outputs": [],
240
- "source": [
241
- "result_df = result_df.drop(columns=['Indicator Client'])"
242
- ]
243
- },
244
- {
245
- "cell_type": "markdown",
246
- "metadata": {},
247
- "source": [
248
- "Find which are the top 5 indicators that are closest"
249
- ]
250
- },
251
- {
252
- "cell_type": "code",
253
- "execution_count": 133,
254
- "metadata": {},
255
- "outputs": [],
256
- "source": [
257
  "# Function to return the column names of the top 5 values for each row\n",
258
  "def top_5_column(row):\n",
259
  " # Find the top 5 values in the row\n",
@@ -268,52 +231,51 @@
268
  "#non_numeric_columns = result_df.columns[result_df.dtypes == 'object']\n",
269
  "\n",
270
  "# Apply the function to each row of the DataFrame, excluding non-numeric columns\n",
271
- "result_df['Top 5 Column ID'] = result_df.apply(lambda row: top_5_column(row), axis=1)"
272
- ]
273
- },
274
- {
275
- "cell_type": "code",
276
- "execution_count": 134,
277
- "metadata": {},
278
- "outputs": [],
279
- "source": [
280
  "# Create a dictionary for fast lookup\n",
281
- "id_to_name = dict(zip(indicators_df['ID'], indicators_df['Indicator name (leonardo)']))\n",
282
  "\n",
283
  "# Function to map IDs to names\n",
284
  "def map_ids_to_names(id_list):\n",
285
  " return [id_to_name.get(id, \"ID\") for id in id_list]\n",
286
  "\n",
287
  "# Apply the function to the 'Top 5 Column ID' column\n",
288
- "result_df['Top 5 Names'] = result_df['Top 5 Column ID'].apply(map_ids_to_names)"
289
- ]
290
- },
291
- {
292
- "cell_type": "code",
293
- "execution_count": 135,
294
- "metadata": {},
295
- "outputs": [],
296
- "source": [
297
- "result_df[['Indicator Name','ID','framework'] ]= mapped_indicators[['Indicator name (leonardo)','ID','Framework']]"
 
 
 
 
 
 
 
 
298
  ]
299
  },
300
  {
301
  "cell_type": "code",
302
- "execution_count": 136,
303
  "metadata": {},
304
  "outputs": [],
305
  "source": [
306
- "result_df[[\"top1name\", \"top2name\", \"top3name\", \"top4name\", \"top5name\"]]= pd.DataFrame(result_df['Top 5 Names'].tolist(), columns=[\"top1name\", \"top2name\", \"top3name\", \"top4name\", \"top5name\"])\n",
307
- "result_df[[\"top1id\", \"top2id\", \"top3id\", \"top4id\", \"top5id\"]]= pd.DataFrame(result_df['Top 5 Column ID'].tolist(), columns=[\"top1id\", \"top2id\", \"top3id\", \"top4id\", \"top5id\"])\n"
308
  ]
309
  },
310
  {
311
  "cell_type": "code",
312
- "execution_count": 137,
313
  "metadata": {},
314
  "outputs": [],
315
  "source": [
316
- "result_df['max_sim'] = np.nanmax(similarities, axis=1)\n",
317
  "\n",
318
  "# Calculate min and max of the 'max_sim' column, ignoring NaN values\n",
319
  "min_val = np.nanmin(result_df['max_sim'])\n",
@@ -339,88 +301,26 @@
339
  },
340
  {
341
  "cell_type": "code",
342
- "execution_count": 138,
343
- "metadata": {},
344
- "outputs": [
345
- {
346
- "data": {
347
- "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1IAAAIjCAYAAAAJLyrXAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAABD+0lEQVR4nO3dfXzO9f////vBjh0zNjPs7N3Mcl5OCu/2VsrEnNUUejcmzSL1joROpN5yknd8VBKRdyfOKhFvSSWZkE6onKVTNafJRoix1Ry21++Pfo5vaxt7HnbsOA5u18tll7yer+fxPB6veVw2914nh82yLEsAAAAAgDKr5O0CAAAAAMDfEKQAAAAAwBBBCgAAAAAMEaQAAAAAwBBBCgAAAAAMEaQAAAAAwBBBCgAAAAAMEaQAAAAAwBBBCgAAAAAMEaQAwE+NHTtWNputQt4rMTFRiYmJru1169bJZrNpyZIlFfL+/fv3V926dSvkvdx18uRJDRw4UFFRUbLZbBo2bJi3S6pQFdmPAOALCFIA4APmzp0rm83m+goKClJMTIw6d+6sadOm6cSJE+XyPgcOHNDYsWO1bdu2clmvPPlybWXxxBNPaO7cufrXv/6lV155Rf369fN2SQAAD7JZlmV5uwgAuNjNnTtX6enpGj9+vOLj4+V0OpWdna1169YpIyNDderU0fLly9W8eXPXa06fPq3Tp08rKCiozO+zadMm/f3vf9ecOXPUv3//Mr/u1KlTkqTAwEBJf5yRat++vRYvXqxbbrmlzOu4W5vT6VRhYaEcDke5vJcn/OMf/1BAQIA+/vhjb5fiFe70IwD4swBvFwAA+H+6du2q1q1bu7ZHjRqlNWvW6MYbb1T37t313XffqUqVKpKkgIAABQR49sd4Xl6egoODXQHKW+x2u1ffvywOHTqkyy67zNtleE1F9CMA+BIu7QMAH3f99ddr9OjR2rt3r1599VXXeEn3pGRkZKht27YKCwtTtWrV1KhRIz3yyCOS/jiL9Pe//12SlJ6e7rqMcO7cuZL+uA+qadOm2rx5s6677joFBwe7XvvXe6TOKCgo0COPPKKoqChVrVpV3bt3108//VRkTt26dUs8+/XnNc9VW0n3SOXm5ur+++9XbGysHA6HGjVqpKeeekp/vdDCZrNpyJAhWrZsmZo2bSqHw6HLL79cK1euLPkb/heHDh3SgAEDFBkZqaCgILVo0ULz5s1z7T9zv9ju3bv17rvvumrfs2dPqWueqWnx4sW67LLLVKVKFbVp00ZfffWVJOm///2v6tevr6CgICUmJhZb66OPPtI///lP1alTRw6HQ7GxsRo+fLh+++23InXXrl1biYmJRb4nmZmZqlq1qlJSUsp0/NIfZwTHjRunBg0aKCgoSDVr1lTbtm2VkZHhmlNSP57vcQKAL+N/HQGAH+jXr58eeeQRrVq1SnfeeWeJc7755hvdeOONat68ucaPHy+Hw6HMzEx98sknkqQmTZpo/PjxeuyxxzRo0CBde+21kqSrr77atcaRI0fUtWtX9e7dW7fddpsiIyPPWtd//vMf2Ww2jRw5UocOHdLUqVPVsWNHbdu2zXXmrCzKUtufWZal7t27a+3atRowYICuuOIKvf/++3rwwQf1888/65lnniky/+OPP9bSpUt1zz33KCQkRNOmTVOvXr20b98+1axZs9S6fvvtNyUmJiozM1NDhgxRfHy8Fi9erP79++vYsWO677771KRJE73yyisaPny4LrnkEt1///2SpNq1a5/1mD/66CMtX75cgwcPliRNnDhRN954ox566CHNnDlT99xzj3799VdNnjxZd9xxh9asWeN67eLFi5WXl6d//etfqlmzpj7//HNNnz5d+/fv1+LFiyVJERERev755/XPf/5T06dP19ChQ1VYWKj+/fsrJCREM2fOPMffyv8zduxYTZw4UQMHDtRVV12lnJwcbdq0SVu2bFFSUpLHjhMAfJoFAPC6OXPmWJKsL774otQ51atXt6688krX9pgxY6w//xh/5plnLEnWL7/8UuoaX3zxhSXJmjNnTrF97dq1syRZs2bNKnFfu3btXNtr1661JFl/+9vfrJycHNf4G2+8YUmynn32WddYXFyclZaWds41z1ZbWlqaFRcX59petmyZJcmaMGFCkXm33HKLZbPZrMzMTNeYJCswMLDI2JdffmlJsqZPn17svf5s6tSpliTr1VdfdY2dOnXKatOmjVWtWrUixx4XF2fdcMMNZ13vzzU5HA5r9+7drrH//ve/liQrKiqqyLqjRo2yJBWZm5eXV2zNiRMnWjabzdq7d2+R8T59+ljBwcHWDz/8YD355JOWJGvZsmVlqvOMFi1anPPY/tqPlnX+xwkAvoxL+wDAT1SrVu2sT+8LCwuTJL311lsqLCx06z0cDofS09PLPP/2229XSEiIa/uWW25RdHS0VqxY4db7l9WKFStUuXJlDR06tMj4/fffL8uy9N577xUZ79ixo+rVq+fabt68uUJDQ7Vr165zvk9UVJT69OnjGrPb7Ro6dKhOnjypDz/80O1j6NChQ5HLFRMSEiRJvXr1KvI9PTP+51r/fLYvNzdXhw8f1tVXXy3LsrR169Yi7/Pcc8+pevXquuWWWzR69Gj169dPN910k1GtYWFh+uabb/Tjjz8avU46v+MEAF9GkAIAP3Hy5Mki//D8q5SUFF1zzTUaOHCgIiMj1bt3b73xxhtGoepvf/ub0YMlGjRoUGTbZrOpfv36Hr/XZe/evYqJiSn2/WjSpIlr/5/VqVOn2Bo1atTQr7/+es73adCggSpVKvrrsrT3MfHXmqpXry5Jio2NLXH8z7Xu27dP/fv3V3h4uKpVq6batWurXbt2kqTjx48XeX14eLimTZum7du3q3r16po2bZpxrePHj9exY8fUsGFDNWvWTA8++KC2b99epteez3ECgC8jSAGAH9i/f7+OHz+u+vXrlzqnSpUqWr9+vVavXq1+/fpp+/btSklJUVJSkgoKCsr0Pib3NZVVaR/SWtaaykPlypVLHLe8+AkgpdV0rloLCgqUlJSkd999VyNHjtSyZcuUkZHhejBHScH5/fffl/RHSNm/f79xrdddd5127typ2bNnq2nTpnrppZfUsmVLvfTSS+d8rbvHCQC+jiAFAH7glVdekSR17tz5rPMqVaqkDh06aMqUKfr222/1n//8R2vWrNHatWsllR5q3PXXS70sy1JmZmaRS7lq1KihY8eOFXvtX8/mmNQWFxenAwcOFLvU8fvvv3ftLw9xcXH68ccfi4WT8n4fE1999ZV++OEHPf300xo5cqRuuukmdezYUTExMSXOX7lypV566SU99NBDql27ttLS0nT69Gnj9w0PD1d6erpef/11/fTTT2revLnGjh17nkcDAP6LIAUAPm7NmjV6/PHHFR8fr759+5Y67+jRo8XGrrjiCklSfn6+JKlq1aqSVGKwccf8+fOLhJklS5YoKytLXbt2dY3Vq1dPGzdudH2oryS98847xR6TblJbt27dVFBQoOeee67I+DPPPCObzVbk/c9Ht27dlJ2drUWLFrnGTp8+renTp6tatWquy+kq0pkzOX8+c2NZlp599tlic48dO+Z60t4TTzyhl156SVu2bNETTzxh9J5Hjhwpsl2tWjXVr1/f1VcAcDHi8ecA4EPee+89ff/99zp9+rQOHjyoNWvWKCMjQ3FxcVq+fLmCgoJKfe348eO1fv163XDDDYqLi9OhQ4c0c+ZMXXLJJWrbtq2kP0JNWFiYZs2apZCQEFWtWlUJCQmKj493q97w8HC1bdtW6enpOnjwoKZOnar69esXeUT7wIEDtWTJEnXp0kW33nqrdu7cqVdffbXIwx9Ma0tOTlb79u316KOPas+ePWrRooVWrVqlt956S8OGDSu2trsGDRqk//73v+rfv782b96sunXrasmSJfrkk080derUs96z5imNGzdWvXr19MADD+jnn39WaGio/ve//5V4b9F9992nI0eOaPXq1apcubK6dOmigQMHasKECbrpppvUokWLMr3nZZddpsTERLVq1Urh4eHatGmTlixZoiFDhpT34QGA3yBIAYAPeeyxxyRJgYGBCg8PV7NmzTR16lSlp6ef8x/t3bt31549ezR79mwdPnxYtWrVUrt27TRu3DjXjfx2u13z5s3TqFGjdPfdd+v06dOaM2eO20HqkUce0fbt2zVx4kSdOHFCHTp00MyZMxUcHOya07lzZz399NOaMmWKhg0bptatW+udd95xfd7SGSa1VapUScuXL9djjz2mRYsWac6cOapbt66efPLJYuuejypVqmjdunV6+OGHNW/ePOXk5KhRo0aaM2dOiR8yXBHsdrvefvttDR06VBMnTlRQUJB69OihIUOGFAlGy5cv1/z58/X000+rcePGrvEpU6YoIyNDaWlp+uKLL2S328/5nkOHDtXy5cu1atUq5efnKy4uThMmTNCDDz7okWMEAH9gs7irEwAAAACMcI8UAAAAABji0j4AAC5Sv/32W7HPnfqr8PBwo88WA4CLBUEKAICL1KJFi5Senn7WOWvXrlViYmLFFAQAfoR7pAAAuEhlZWXpm2++OeucVq1aqUaNGhVUEQD4D4IUAAAAABjiYRMAAAAAYIh7pCQVFhbqwIEDCgkJkc1m83Y5AAAAALzEsiydOHFCMTExqlSp9PNOBClJBw4cUGxsrLfLAAAAAOAjfvrpJ11yySWl7idISQoJCZH0xzcrNDTUq7U4nU6tWrVKnTp1KtOnzQP0DEzRMzBFz8AU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r1asWKHMzEzdc889cjqdGjNmTEWUDS9yp2dSU1N1+PBhtW3bVpZl6fTp07r77ru5tA+lKu3fvzk5Ofrtt99UpUoVL1VWFGekgAvYpEmTtHDhQr355psKCgrydjnwQSdOnFC/fv304osvqlatWt4uB36gsLBQEREReuGFF9SqVSulpKTo0Ucf1axZs7xdGnzUunXr9MQTT2jmzJnasmWLli5dqnfffVePP/64t0sDzgtnpCpQrVq1VLlyZR08eLDI+MGDBxUVFVXia6Kioozm48LiTs+c8dRTT2nSpElavXq1mjdv7sky4UNMe2bnzp3as2ePkpOTXWOFhYWSpICAAO3YsUP16tXzbNHwGnd+xkRHR8tut6ty5cqusSZNmig7O1unTp1SYGCgR2uGd7nTM6NHj1a/fv00cOBASVKzZs2Um5urQYMG6dFHH1WlSvx/fRRV2r9/Q0NDfeZslMQZqQoVGBioVq1a6YMPPnCNFRYW6oMPPlCbNm1KfE2bNm2KzJekjIyMUufjwuJOz0jS5MmT9fjjj2vlypVq3bp1RZQKH2HaM40bN9ZXX32lbdu2ub66d+/uelJSbGxsRZaPCubOz5hrrrlGmZmZrsAtST/88IOio6MJURcBd3omLy+vWFg6E8Qty/JcsfBbfvPvX28/7eJis3DhQsvhcFhz5861vv32W2vQoEFWWFiYlZ2dbVmWZfXr1896+OGHXfM/+eQTKyAgwHrqqaes7777zhozZoxlt9utr776yluHgApm2jOTJk2yAgMDrSVLllhZWVmurxMnTnjrEFDBTHvmr3hq38XFtF/27dtnhYSEWEOGDLF27NhhvfPOO1ZERIQ1YcIEbx0CKphpz4wZM8YKCQmxXn/9dWvXrl3WqlWrrHr16lm33nqrtw4BFezEiRPW1q1bra1bt1qSrClTplhbt2619u7da1mWZT388MNWv379XPN37dplBQcHWw8++KD13XffWTNmzLAqV65srVy50luHUCKClBdMnz7dqlOnjhUYGGhdddVV1saNG1372rVrZ6WlpRWZ/8Ybb1gNGza0AgMDrcsvv9x69913K7hieJtJz8TFxVmSin2NGTOm4guH15j+nPkzgtTFx7RfPv30UyshIcFyOBzWpZdeav3nP/+xTp8+XcFVw5tMesbpdFpjx4616tWrZwUFBVmxsbHWPffcY/36668VXzi8Yu3atSX+2+RMn6SlpVnt2rUr9porrrjCCgwMtC699FJrzpw5FV73udgsi3OqAAAAAGCCe6QAAAAAwBBBCgAAAAAMEaQAAAAAwBBBCgAAAAAMEaQAAAAAwBBBCgAAAAAMEaQAAAAAwBBBCgAAAAAMEaQAADgP69atk81m07Fjx7xdCgCgAtksy7K8XQQAAP7q1KlTOnr0qCIjI2Wz2bxdDgCgghCkAAAAAMAQl/YBAHxOYmKi7r33Xg0bNkw1atRQZGSkXnzxReXm5io9PV0hISGqX7++3nvvPUlSQUGBBgwYoPj4eFWpUkWNGjXSs88+61rv999/1+WXX65Bgwa5xnbu3KmQkBDNnj37nPXs3btXycnJqlGjhqpWrarLL79cK1askFT80r65c+cqLCxM77zzjho1aqTg4GDdcsstysvL07x581S3bl3VqFFDQ4cOVUFBQTl+1wAAFSnA2wUAAFCSefPm6aGHHtLnn3+uRYsW6V//+pfefPNN9ejRQ4888oieeeYZ9evXT/v27ZPdbtcll1yixYsXq2bNmvr00081aNAgRUdH69Zbb1VQUJBee+01JSQk6IYbbtCNN96o2267TUlJSbrjjjvOWcvgwYN16tQprV+/XlWrVtW3336ratWqlTo/Ly9P06ZN08KFC3XixAn17NlTPXr0UFhYmFasWKFdu3apV69euuaaa5SSklKe3zYAQAXh0j4AgM9JTExUQUGBPvroI0l/nHGqXr26evbsqfnz50uSsrOzFR0drQ0bNugf//hHsTWGDBmi7OxsLVmyxDX25JNPavLkyerdu7f+97//6auvvlLNmjXPWU/z5s3Vq1cvjRkzpti+devWqX379vr1118VFhamuXPnKj09XZmZmapXr54k6e6779Yrr7yigwcPugJYly5dVLduXc2aNcv8GwQA8Dou7QMA+KTmzZu7/ly5cmXVrFlTzZo1c41FRkZKkg4dOiRJmjFjhlq1aqXatWurWrVqeuGFF7Rv374ia95///1q2LChnnvuOc2ePbtMIUqShg4dqgkTJuiaa67RmDFjtH379rPODw4OdoWoM7XWrVu3yFmsyMhIV+0AAP9DkAIA+CS73V5k22azFRk784S8wsJCLVy4UA888IAGDBigVatWadu2bUpPT9epU6eKrHHo0CH98MMPqly5sn788ccy1zJw4EDt2rVL/fr101dffaXWrVtr+vTpbtd+ZqywsLDMNQAAfAtBCgDg9z755BNdffXVuueee3TllVeqfv362rlzZ7F5d9xxh5o1a6Z58+Zp5MiR+u6778r8HrGxsbr77ru1dOlS3X///XrxxRfL8xAAAH6Gh00AAPxegwYNNH/+fL3//vuKj4/XK6+8oi+++ELx8fGuOTNmzNCGDRu0fft2xcbG6t1331Xfvn21ceNGBQYGnnX9YcOGqWvXrmrYsKF+/fVXrV27Vk2aNPH0YQEAfBhnpAAAfu+uu+5Sz549lZKSooSEBB05ckT33HOPa//333+vBx98UDNnzlRsbKwkaebMmTp8+LBGjx59zvULCgo0ePBgNWnSRF26dFHDhg01c+ZMjx0PAMD38dQ+AAAAADDEGSkAAAAAMESQAgBc9Lp27apq1aqV+PXEE094uzwAgA/i0j4AwEXv559/1m+//VbivvDwcIWHh1dwRQAAX0eQAgAAAABDXNoHAAAAAIYIUgAAAABgiCAFAAAAAIYIUgAAAABgiCAFAAAAAIYIUgAAAABgiCAFAAAAAIb+P6TbFtYOJrPcAAAAAElFTkSuQmCC",
348
- "text/plain": [
349
- "<Figure size 1000x600 with 1 Axes>"
350
- ]
351
- },
352
- "metadata": {},
353
- "output_type": "display_data"
354
- }
355
- ],
356
- "source": [
357
- "import matplotlib.pyplot as plt\n",
358
- "import seaborn as sns\n",
359
- "# Plot a histogram of the 'max_sim' column\n",
360
- "plt.figure(figsize=(10, 6))\n",
361
- "plt.hist(result_df['max_sim_normalized'], bins=30, color='blue', alpha=0.7)\n",
362
- "plt.title('Distribution of max_sim')\n",
363
- "plt.xlabel('max_sim')\n",
364
- "plt.ylabel('Frequency')\n",
365
- "plt.grid(True)\n",
366
- "plt.show()\n"
367
- ]
368
- },
369
- {
370
- "cell_type": "code",
371
- "execution_count": 139,
372
  "metadata": {},
373
  "outputs": [],
374
  "source": [
375
- "result_final = result_df[['Indicator Name','ID','framework','max_sim_normalized','top1name', 'top2name', 'top3name', 'top4name', 'top5name', 'top1id',\n",
376
- " 'top2id', 'top3id', 'top4id', 'top5id']]"
 
 
 
 
377
  ]
378
  },
379
  {
380
  "cell_type": "code",
381
- "execution_count": 140,
382
  "metadata": {},
383
- "outputs": [
384
- {
385
- "name": "stderr",
386
- "output_type": "stream",
387
- "text": [
388
- "/var/folders/0k/7w7z520532qclyc34vntz5ym0000gn/T/ipykernel_94618/671129894.py:9: SettingWithCopyWarning: \n",
389
- "A value is trying to be set on a copy of a slice from a DataFrame.\n",
390
- "Try using .loc[row_indexer,col_indexer] = value instead\n",
391
- "\n",
392
- "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
393
- " result_final['top1framework'] = result_final['top1id'].apply(map_framework)\n",
394
- "/var/folders/0k/7w7z520532qclyc34vntz5ym0000gn/T/ipykernel_94618/671129894.py:10: SettingWithCopyWarning: \n",
395
- "A value is trying to be set on a copy of a slice from a DataFrame.\n",
396
- "Try using .loc[row_indexer,col_indexer] = value instead\n",
397
- "\n",
398
- "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
399
- " result_final['top2framework'] = result_final['top2id'].apply(map_framework)\n",
400
- "/var/folders/0k/7w7z520532qclyc34vntz5ym0000gn/T/ipykernel_94618/671129894.py:11: SettingWithCopyWarning: \n",
401
- "A value is trying to be set on a copy of a slice from a DataFrame.\n",
402
- "Try using .loc[row_indexer,col_indexer] = value instead\n",
403
- "\n",
404
- "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
405
- " result_final['top3framework'] = result_final['top3id'].apply(map_framework)\n",
406
- "/var/folders/0k/7w7z520532qclyc34vntz5ym0000gn/T/ipykernel_94618/671129894.py:12: SettingWithCopyWarning: \n",
407
- "A value is trying to be set on a copy of a slice from a DataFrame.\n",
408
- "Try using .loc[row_indexer,col_indexer] = value instead\n",
409
- "\n",
410
- "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
411
- " result_final['top4framework'] = result_final['top4id'].apply(map_framework)\n",
412
- "/var/folders/0k/7w7z520532qclyc34vntz5ym0000gn/T/ipykernel_94618/671129894.py:13: SettingWithCopyWarning: \n",
413
- "A value is trying to be set on a copy of a slice from a DataFrame.\n",
414
- "Try using .loc[row_indexer,col_indexer] = value instead\n",
415
- "\n",
416
- "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
417
- " result_final['top5framework'] = result_final['top5id'].apply(map_framework)\n"
418
- ]
419
- }
420
- ],
421
  "source": [
422
  "# Create a mapping from ID to Framework\n",
423
- "id_to_framework = indicators_df.set_index('ID')['Framework'].to_dict()\n",
424
  "\n",
425
  "# Function to map ID to Framework\n",
426
  "def map_framework(id):\n",
 
35
  },
36
  {
37
  "cell_type": "code",
38
+ "execution_count": 1,
39
  "metadata": {},
40
  "outputs": [
41
  {
 
44
  "<module 'functions' from '/Users/alanfortunysicart/Documents/GitHub/IndicatorHarmonizer/functions.py'>"
45
  ]
46
  },
47
+ "execution_count": 1,
48
  "metadata": {},
49
  "output_type": "execute_result"
50
  }
 
78
  },
79
  {
80
  "cell_type": "code",
81
+ "execution_count": 2,
82
  "metadata": {},
83
  "outputs": [],
84
  "source": [
85
+ "table1 = pd.read_excel('/Users/alanfortunysicart/Downloads/Indicators_Indicators_Frameworks_Default View(1).xlsx')"
86
+ ]
87
+ },
88
+ {
89
+ "cell_type": "code",
90
+ "execution_count": 3,
91
+ "metadata": {},
92
+ "outputs": [],
93
+ "source": [
94
+ "if 'Indicator ID' in table1.columns:\n",
95
+ " table1['ID'] = table1['Indicator ID'].astype(str)\n",
96
+ "else:\n",
97
+ " table1['ID'] = table1['ID'].astype(str)\n"
98
  ]
99
  },
100
  {
 
106
  },
107
  {
108
  "cell_type": "code",
109
+ "execution_count": 4,
110
  "metadata": {},
111
  "outputs": [],
112
  "source": [
 
122
  },
123
  {
124
  "cell_type": "code",
125
+ "execution_count": 5,
126
  "metadata": {},
127
  "outputs": [],
128
  "source": [
129
  "table2 = pd.read_excel('/Users/alanfortunysicart/Downloads/Indicators_Indicators_Default view(14).xlsx')\n",
130
  "table2 = f.concatenate_columns(table2,columns=f.columns_embeddings_col2)\n",
131
+ "\n",
132
+ "if 'Indicator ID' in table2.columns:\n",
133
+ " table2['ID'] = table2['Indicator ID'].astype(str)\n",
134
+ "else:\n",
135
+ " table2['ID'] = table2['ID'].astype(str)\n",
136
  "\n"
137
  ]
138
  },
 
152
  },
153
  {
154
  "cell_type": "code",
155
+ "execution_count": 6,
156
  "metadata": {},
157
  "outputs": [],
158
  "source": [
 
168
  },
169
  {
170
  "cell_type": "code",
171
+ "execution_count": 7,
172
  "metadata": {},
173
  "outputs": [],
174
  "source": [
 
177
  },
178
  {
179
  "cell_type": "code",
180
+ "execution_count": 8,
181
  "metadata": {},
182
  "outputs": [],
183
  "source": [
 
188
  },
189
  {
190
  "cell_type": "code",
191
+ "execution_count": 30,
192
  "metadata": {},
193
  "outputs": [],
194
  "source": [
 
 
195
  "# Create a DataFrame for the similarities\n",
196
  "result_df = pd.DataFrame(similarities, \n",
197
+ " columns=table2['ID'],\n",
198
+ " index=table1['ID'])\n",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  "\n",
200
+ "# Mapping frameworks\n",
201
+ "table1_sel_map = table1.set_index('ID')['Framework'].to_dict()\n",
202
+ "table2_sel_map = table2.set_index('ID')['Framework'].to_dict()\n",
203
  "\n",
204
  "# Function to check if there is any common framework element\n",
205
+ "def has_common_framework(table1_framework, table2_framework):\n",
206
+ " table1_frameworks = set(table1_framework.split(', '))\n",
207
+ " table2_frameworks = set(table2_framework.split(', '))\n",
208
+ " return not table1_frameworks.isdisjoint(table2_frameworks)\n",
209
+ "\n",
210
+ "# Replace similarity values with NaN where the frameworks match\n",
211
+ "for table1_id, table1_framework in table1_sel_map.items():\n",
212
+ " for table2_id in result_df.columns:\n",
213
+ " table2_framework = table2_sel_map.get(table2_id)\n",
214
+ " if pd.notna(table2_framework) and pd.notna(table1_framework):\n",
215
+ " if has_common_framework(table1_framework, table2_framework):\n",
216
+ " result_df.loc[table1_id, table2_id] = np.nan\n",
217
+ "\n",
218
+ "\n",
219
  "\n",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  "# Function to return the column names of the top 5 values for each row\n",
221
  "def top_5_column(row):\n",
222
  " # Find the top 5 values in the row\n",
 
231
  "#non_numeric_columns = result_df.columns[result_df.dtypes == 'object']\n",
232
  "\n",
233
  "# Apply the function to each row of the DataFrame, excluding non-numeric columns\n",
234
+ "result_df['Top 5 Column ID'] = result_df.apply(lambda row: top_5_column(row), axis=1)\n",
235
+ "\n",
 
 
 
 
 
 
 
236
  "# Create a dictionary for fast lookup\n",
237
+ "id_to_name = dict(zip(table2['ID'], table2['Indicator name (leonardo)']))\n",
238
  "\n",
239
  "# Function to map IDs to names\n",
240
  "def map_ids_to_names(id_list):\n",
241
  " return [id_to_name.get(id, \"ID\") for id in id_list]\n",
242
  "\n",
243
  "# Apply the function to the 'Top 5 Column ID' column\n",
244
+ "result_df['Top 5 Names'] = result_df['Top 5 Column ID'].apply(map_ids_to_names)\n",
245
+ "\n",
246
+ "# Ensure all entries are lists and have at least 5 elements, filling missing values with None\n",
247
+ "result_df['Top 5 Names'] = result_df['Top 5 Names'].apply(lambda x: x if isinstance(x, list) and len(x) >= 5 else (x + [None] * (5 - len(x)) if isinstance(x, list) else [None]*5))\n",
248
+ "\n",
249
+ "# Convert list in 'Top 5 Names' to separate columns\n",
250
+ "new_cols = pd.DataFrame(result_df['Top 5 Names'].tolist(), index=result_df.index, columns=[\"top1name\", \"top2name\", \"top3name\", \"top4name\", \"top5name\"])\n",
251
+ "result_df = result_df.join(new_cols)\n",
252
+ "\n",
253
+ "# Ensure all entries are lists and have exactly 5 elements, filling missing values with None\n",
254
+ "result_df['Top 5 Column ID'] = result_df['Top 5 Column ID'].apply(\n",
255
+ " lambda x: x if isinstance(x, list) and len(x) >= 5 else (x + [None] * (5 - len(x)) if isinstance(x, list) else [None]*5)\n",
256
+ ")\n",
257
+ "\n",
258
+ "# Convert list in 'Top 5 Column ID' to separate columns\n",
259
+ "new_ids_cols = pd.DataFrame(result_df['Top 5 Column ID'].tolist(), index=result_df.index, columns=[\"top1id\", \"top2id\", \"top3id\", \"top4id\", \"top5id\"])\n",
260
+ "result_df = result_df.join(new_ids_cols)\n",
261
+ "\n"
262
  ]
263
  },
264
  {
265
  "cell_type": "code",
266
+ "execution_count": 31,
267
  "metadata": {},
268
  "outputs": [],
269
  "source": [
270
+ "result_df['max_sim'] = np.nanmax(result_df[table2['ID']], axis=1)"
 
271
  ]
272
  },
273
  {
274
  "cell_type": "code",
275
+ "execution_count": 32,
276
  "metadata": {},
277
  "outputs": [],
278
  "source": [
 
279
  "\n",
280
  "# Calculate min and max of the 'max_sim' column, ignoring NaN values\n",
281
  "min_val = np.nanmin(result_df['max_sim'])\n",
 
301
  },
302
  {
303
  "cell_type": "code",
304
+ "execution_count": 42,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305
  "metadata": {},
306
  "outputs": [],
307
  "source": [
308
+ "result_final = result_df[['max_sim_normalized','top1name', 'top2name', 'top3name', 'top4name', 'top5name', 'top1id',\n",
309
+ " 'top2id', 'top3id', 'top4id', 'top5id']]\n",
310
+ "\n",
311
+ "# Merge the required columns\n",
312
+ "result_final = table1[['Indicator ID', 'Indicator Name', 'Framework']].merge(result_final, \n",
313
+ " left_index=True, right_index=True, how='left')\n"
314
  ]
315
  },
316
  {
317
  "cell_type": "code",
318
+ "execution_count": 43,
319
  "metadata": {},
320
+ "outputs": [],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  "source": [
322
  "# Create a mapping from ID to Framework\n",
323
+ "id_to_framework = table2.set_index('ID')['Framework'].to_dict()\n",
324
  "\n",
325
  "# Function to map ID to Framework\n",
326
  "def map_framework(id):\n",