{"cells":[{"metadata":{},"cell_type":"markdown","source":"<table>\n    <tr>\n        <td style=\"align:center;font-size:20pt;  font-weight: 600;\"> CDP: <br>Unlocking <br>Climate <br>Solutions\n        </td>\n        <td>\n<img src=\"https://upload.wikimedia.org/wikipedia/commons/a/a0/Climate_change_mitigation_icon.png\" alt=\"wikimedia\" width=\"150\"/></td>\n    </tr>\n</table>\n<br>\n    \n\n<p style=\"text-align: justify;font-style: italic;\">CDP is an organization that investigates global environmental disclosure system. Every year, many cities and companies are involved in investigations into the climate, its consequences and their interest in it. CDP wonders how companies and cities are dealing with the climate emergency. By extracting information from the various treaties and their results, we hope to improve the climate transition. </p>\n<hr />\n<br>\n\n<h2 style=\"color:#FFA57D\">> Goal:</h2>\nThe aim of this work is to find KPIs in the intesection between <b>environment</b> and <b>social </b>challenge for the climate change. In this notebook, we tackle the cities answers. Notice that section 1.2. is a storyline where not many results are found but it is the way I started to search. I just want to keep it to show the progress of the work. If you want to see KPI only, go straigth to section 2.\n"},{"metadata":{},"cell_type":"markdown","source":"\n## Outlines:\n- [1. How cities Deal with Climate Change: Dataset Exploration](#1) \n    * [1.1. Relating Demography, Budget and Social Risks: What are the Numerical Key Values?](#11)\n      + [1.1.1. Administrative Boundary](#111)\n      + [1.1.2. Population](#112) \n      + [1.1.3.Governance & Data Management ](#113)\n      + [1.1.4.Climate Risk and Vulnerability Assessment](#114)\n      + [1.1.5. City-wide Emissions](#115)\n      + [1.1.6. Emission Reduction](#116)\n      + [1.1.7. Collaborations](#117)\n      + [1.1.8 Energy](#118)\n      + [1.1.9. Transports](#119)\n    * [1.2. Data Analysis: Clusters, Features Coorelations ](#12)\n      + [1.2.1 Simple K-means to get some clusters and plot on a map](#121)\n      + [1.2.2 Improving K-means to get KPIs](#122) \n      + [1.2.3 A Decision Tree by Using GHG Reduction as Target](#123)\n- [2. Intersection between Environment and Social Challenge for the Cities](#2)\n    * [2.1 Logistic Regression for KPI Extractions](#21)\n    * [2.2 Results](#22)\n    * [2.3 Conclusion](#23)\n- [3. Other Datasets](#3)\n    * [3.1 Adding Country Information to Cities](#31)\n    * [3.2 Country Information Only](#32)\n<hr />\n"},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"#all the imports required for the notebook are given in this block\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom IPython.core.display import HTML\nfrom IPython.display import  Markdown\nimport seaborn as sns\nimport random\nfrom sklearn.cluster import KMeans\nfrom sklearn.preprocessing import StandardScaler\nimport geopandas as gpd\nfrom shapely.geometry import Point\nfrom  sklearn.metrics import davies_bouldin_score\nfrom sklearn.decomposition import PCA\nfrom matplotlib.path import Path\nfrom matplotlib.spines import Spine\nfrom matplotlib.projections.polar import PolarAxes\nfrom matplotlib.projections import register_projection\nfrom matplotlib.patches import Circle, RegularPolygon\nfrom matplotlib.path import Path\nfrom matplotlib.projections.polar import PolarAxes\nfrom matplotlib.projections import register_projection\nfrom matplotlib.spines import Spine\nfrom matplotlib.transforms import Affine2D\nfrom sklearn import tree\nimport statsmodels.api as sm\nfrom sklearn.linear_model import LogisticRegression\nfrom scipy import stats\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import accuracy_score\nfrom sklearn.metrics import classification_report\n#centering figures\nHTML(\"\"\"<style> .output_png { display: table-cell; text-align: center; vertical-align: middle;}</style>\"\"\")\n\n#my colors\ncolors= ['#003f5c','#2f4b7c','#665191','#a05195','#d45087','#f95d6a','#ff7c43','#ffa600','#fcca46','#a1c181','#619b8a','#386641']","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"# import the datasets \ncities_2018 = pd.read_csv(\"/kaggle/input/cdp-unlocking-climate-solutions/Cities/Cities Responses/2018_Full_Cities_Dataset.csv\",\n                         usecols=[c for c in list(pd.read_csv(\"/kaggle/input/cdp-unlocking-climate-solutions/Cities/Cities Responses/2018_Full_Cities_Dataset.csv\", nrows =1)) if c not in ['Questionnaire','Year Reported to CDP','File Name','Last update','Comments']])\ncities_2019 = pd.read_csv(\"/kaggle/input/cdp-unlocking-climate-solutions/Cities/Cities Responses/2019_Full_Cities_Dataset.csv\",\n                         usecols=[c for c in list(pd.read_csv(\"/kaggle/input/cdp-unlocking-climate-solutions/Cities/Cities Responses/2019_Full_Cities_Dataset.csv\", nrows =1)) if c not in ['Questionnaire','Year Reported to CDP','File Name','Last update','Comments']])\ncities_2020 = pd.read_csv(\"/kaggle/input/cdp-unlocking-climate-solutions/Cities/Cities Responses/2020_Full_Cities_Dataset.csv\",\n                         usecols=[c for c in list(pd.read_csv(\"/kaggle/input/cdp-unlocking-climate-solutions/Cities/Cities Responses/2020_Full_Cities_Dataset.csv\", nrows =1)) if c not in ['Questionnaire','Year Reported to CDP','File Name','Last update','Comments']])\n\ngeo_cities_2020 = pd.read_csv(\"/kaggle/input/cdp-unlocking-climate-solutions/Cities/Cities Disclosing/2020_Cities_Disclosing_to_CDP.csv\", usecols=[\"Account Number\",\"City Location\"])\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<a id='1'></a>\n<h1 style=\"color:#084FD0\"> 1. How cities Deal with Climate Change</h1>\n<p style=\"text-align: justify;\">In this section, we focus on cities. We recall that the objective of this work is to find correlations between the recorded values in order to interpret the numerous responses to the questionnaires. To do this, we first examine the numerical values and categorical values that can easily be transformed into numerical values. We first try to extract KPI from those data that incorporate city answers only. Then, in section 1.2, we analyze information from the textual responses. There are methods for extracting topics that are useful to get an overview of the reflections. </p>\n\n<h2 style=\"color:#7209b7\"> 1.1. Relating Demography, Budget and Social Risks: What are the Numerical Key Values?</h2>\n\n<p style=\"text-align: justify;\">Our first interest go for the easy-to-interpret responses, i.e., the data that are either numerical either categorical. Indeed the questionnaries contain a lot of textual information that require deep data processing. With numercal values, we aim at giving first coorelation between important climate keys. </p>"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"cities_2020.head(2)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"cities_2020.shape","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"A line is an answer of a city to a question. There are 869313 rows. Not all cities respond to all questions. The next code shows the mean number of questions that haven answered but also the minimal and maximal. "},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"cities_2020[\"Account Number\"].value_counts().describe()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"In order to get coorelations between the values. We should group the value per `Account Number`. Remember in this first work, we use only the numerical and categorical answers.  "},{"metadata":{},"cell_type":"markdown","source":"<h2 style=\"color:#FFA57D\"> Analyzing each question, one by one</h2>\n\nIn order to extract numeral information, we look at every question to obtain a dataset in the format : \n\n| Account Number | Feature 1 | Feature 2 | ... | Feature n| \n| --- | --- | ---| ---| --- |\n| 68296 | 1.0 | 3304 | ... | 20394 |\n|  ... | ... |  ... | ... |  ... |\n\nwhere each feature <i>i</i> is the result of a question. For this purpose, I have created a generic function that you can find below."},{"metadata":{"_kg_hide-input":false,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"def extract_answer(df, question, newColumnName, condition=True):\n    '''\n    Extract answer from the number of question \n    @question: (string) name of the question \n    @newColumnName: (string) column name of the question\n    @condition: (boolen) when the question has several outputs \n    @return: DataFrame\n    '''\n    result_df = df[(df['Question Number'] == question) & condition ][[\"Account Number\",\"Response Answer\"]]\n    result_df.columns = [newColumnName if x=='Response Answer' else x for x in result_df.columns]\n    return result_df\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<a id=\"111\"></a>\n<h3 style=\"color:#D00892\"> 1.1.1 Administrative Boundary </h3>\nThe first information recorded in the form is the type of city (Question 0.1). It's a categorical value that we transform into numeral values. But first, let's have a look at the distribution. "},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"# General description to your city’s reporting boundary.\ncities_2020_admin_boundary = extract_answer(cities_2020,'0.1','Administrative boundary', condition = (cities_2020['Column Name'] == 'Administrative boundary'))\ncities_2020_admin_boundary['Administrative boundary'].replace('^(Other).*','Other',regex=True, inplace=True)\ncities_2020_admin_boundary.head(3)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# show a pie chart of the distribution\ncities_2020_admin_boundary['Administrative boundary'].value_counts().plot.pie(textprops={'color':\"w\"},pctdistance=0.7,autopct='%.2f%%',figsize=(6,6),colors=colors, labels=None)\nplt.title(\"Administrative Boundary Distribution \",fontsize=17,ha='left')\nplt.legend(labels=cities_2020_admin_boundary['Administrative boundary'].value_counts().index, loc=\"best\",bbox_to_anchor=(1, 0.25, 0.5, 0.5))\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We observe that most cities are City or Minicipality type. "},{"metadata":{},"cell_type":"markdown","source":"<a id=\"112\"></a>\n<h3 style=\"color:#D00892\"> 1.1.2. Population </h3>\nFirst, we find the current population and the projected population (Question 0.5). What are the plans of the cities? Population raising or descreasing? "},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"# extract current population from answer of question 0.5 \ncities_2020_numerical_current_pop = extract_answer(cities_2020,'0.5','Current population', condition = (cities_2020['Column Name'] == 'Current population'))\n\n# extract projected population from answer of question 0.5\ncities_2020_numerical_projected_pop = extract_answer(cities_2020,'0.5','Projected population', condition = (cities_2020['Column Name'] == 'Projected population'))\n\n# merge two answers in a dataframe\ncities_2020_numerical_pop = cities_2020_numerical_current_pop.merge(cities_2020_numerical_projected_pop)\ncities_2020_numerical_pop.head(3)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We get two features: \"Current population\" and \"Projected population\". Let's compute the difference in terme of percentage to see if cities plan to grow. "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# compute the difference\ncities_2020_numerical_pop[\"Population Projection Difference\"] = (cities_2020_numerical_pop[\"Projected population\"].fillna(0).astype(int)-cities_2020_numerical_pop[\"Current population\"].fillna(0).astype(int))*100/ cities_2020_numerical_pop[\"Current population\"].fillna(1).astype(int)\n\n# some cities did not fill this information then we have wrong answers, replace them \ncities_2020_numerical_pop[\"Population Projection Difference\"].replace(1,np.nan,inplace=True)\ncities_2020_numerical_pop[\"Population Projection Difference\"].replace(0,np.nan,inplace=True)\n\n# give a plot of the plans of the cities\nsns.set_style(\"whitegrid\",{'axes.grid' : False})\nfig = plt.figure(figsize=(12,6))\nax1 = fig.add_subplot(121)\nax2 = fig.add_subplot(122)\n\nfig.suptitle(\"Population Projection Difference: : What are the plans?\", fontsize=18)\nsns.boxplot(data=cities_2020_numerical_pop[\"Population Projection Difference\"], palette=[\"#2f4b7c\"],ax=ax1)\nax1.set_xlabel(xlabel='All Cities',fontsize=15)\n\n# remove extremes values to zoom \nax2.set_xlabel(xlabel='Excluding Extremes',fontsize=15)\ncities_2020_numerical_pop_diff_without_huge = cities_2020_numerical_pop[cities_2020_numerical_pop[\"Population Projection Difference\"]<300]\nsns.boxplot(data=cities_2020_numerical_pop_diff_without_huge[\"Population Projection Difference\"], palette=[\"#ffa600\"],ax=ax2)\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Despite a mean of grow to 4% to the current population size, the plot above shows that several cities estimate to grow a lot.  We wonder if this plan is related to cities decisions about climate change!"},{"metadata":{},"cell_type":"markdown","source":"<a id=\"113\"></a>\n<h3 style=\"color:#D00892\"> 1.1.3. Governance & Data Management</h3>\nIn section (1), the form tackles the interest of the governance of the cities to incorporate climate change related goals. "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# extract Governance & Data Management answer of question 1.0 \ncities_2020_gov_and_data = extract_answer(cities_2020,'1.0','Governance & Data Management')\n\n# compare to 2019\ncities_2019_gov_and_data = extract_answer(cities_2019,'1.0','Governance & Data Management')\n\n# show distribution\ncities_2019_2020_gov_and_data = pd.DataFrame({'2019': cities_2019_gov_and_data['Governance & Data Management'].value_counts(),\n                               '2020': cities_2020_gov_and_data['Governance & Data Management'].value_counts()})\n#plot it!\nfig = plt.figure(figsize=(9,6))\nfig.suptitle(\"Governance & Data Management: Does your city incorporate sustainability goals and targets (e.g. GHG reductions) into the master planning for the city?\", fontsize=18)\nax1 = fig.add_subplot(111)\ncities_2019_2020_gov_and_data.plot.barh(color=['#2f4b7c','#ffa600'], ax=ax1, edgecolor='w',linewidth=1.3)\nax1.yaxis.grid(False)\nax1.xaxis.grid(False)\nax1.spines['right'].set_visible(False)\nax1.spines['top'].set_visible(False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We remark that cities declare being more involved to incorporate sustainability goals and targets (e.g. GHG reductions) into the master planning over time. "},{"metadata":{},"cell_type":"markdown","source":"<a id=\"114\"></a>\n<h3 style=\"color:#D00892\">  1.1.4 Climate Risk and Vulnerability Assessment</h3>\nIn section (2), the form focuses on assessment about the riks of climate change. "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# extract answer of question 2.0 \ncities_2020_risk = extract_answer(cities_2020,'2.0','Climate Change')\n\n# compare to 2019\ncities_2019_risk = extract_answer(cities_2019,'2.0','Climate Change')\n\n# show distribution\ncities_2019_2020_risk = pd.DataFrame({'2019': cities_2019_risk['Climate Change'].value_counts(),\n                               '2020': cities_2020_risk['Climate Change'].value_counts()})\nfig = plt.figure(figsize=(8,4))\nfig.suptitle(\"Has a climate change risk or vulnerability assessment been undertaken for your city?\", fontsize=17)\nax1 = fig.add_subplot(111)\ncities_2019_2020_risk.plot.barh(color=['#2f4b7c','#ffa600'], ax=ax1, edgecolor='w',linewidth=1.3)\nax1.yaxis.grid(False)\nax1.xaxis.grid(False)\nax1.spines['right'].set_visible(False)\nax1.spines['top'].set_visible(False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"When a city answers `Yes` to the previous question, more details about the assessment is asked like what are the processes and methods to deal with them."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# Select the primary process or methodology used to undertake the risk or vulnerability assessment of your city.\ncities_2020_risk_detail = extract_answer(cities_2020,'2.0a','Climate Change Detail')\n\n# when I use 'df' it means that it's only for plots\ncities_2020_risk_detail = cities_2020_risk_detail.groupby('Climate Change Detail').filter(lambda x : len(x)>3)\ndf= cities_2020_risk_detail[cities_2020_risk_detail['Climate Change Detail']!=\"Question not applicable\"]\n\nplt.title(\"The primary process or methodology used to undertake the risk or vulnerability assessment of the cities:\",fontsize=17,ha=\"center\")\nax=df['Climate Change Detail'].value_counts().plot.pie(textprops={'color':\"#000000\"},pctdistance=1.18,autopct='%.2f%%',figsize=(6,6),colors=colors, labels=None)\nax.set_ylabel('')\nplt.legend(labels=df['Climate Change Detail'].value_counts().index, loc=\"best\",bbox_to_anchor=(1.1, 0.4, 0.5, 0.5))\ncentre_circle = plt.Circle((0,0),0.8,fc='white')\nfig = plt.gcf()\nfig.gca().add_artist(centre_circle)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<p>There are 4 mains methds: <i>State or region vulnerability and risk assessment methodology</i>, <i>IPCC climate change impact assessment guidance</i>, <i>Proprietary Methodology</i>, and. <i>Agency specific vulnerability and risk assessment methodology</i> which are very vague and similar. This answer might not be interested to consider. </p>\n\n<p>A interested question about risk and vulnerability assessment is the reason of cities to take the decision to investigate in it or not. Below we see the different reasons. We notice that the answers are very varied.  </p>"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# Identify and describe the factors that most greatly affect your city’s ability to adapt to climate change and indicate how those factors either support or challenge this ability.\ncities_2020_risk_factor_ability_adapt = extract_answer(cities_2020,'2.2','Factor Affect Climate Adaptation', condition=(cities_2020['Column Number'] == 1))\ncities_2020_risk_factor_ability_adapt['Factor Affect Climate Adaptation'].replace('^(Other).*','Other',regex=True, inplace=True)\ncities_2020_risk_factor_ability_adapt=cities_2020_risk_factor_ability_adapt.groupby('Factor Affect Climate Adaptation').filter(lambda x : len(x)>3)\n\ndf=cities_2020_risk_factor_ability_adapt['Factor Affect Climate Adaptation'].value_counts().to_frame().transpose()\nfig = plt.figure(figsize=(22,12))\nax1 = fig.add_subplot()\ndf.plot(kind='barh',stacked=True,legend=False, color=colors, ax=ax1, grid=False, width=0.04)\nax1.set_ylabel('')\nfor p in range(0,len(ax1.patches)):\n    b = ax1.patches[p].get_bbox()\n    ax1.annotate(df.columns[p] , ((b.x0 + b.x1)/2 - 0.2 , b.y1 + 0.01),rotation=-280,fontsize=13)\nax1.set_xticklabels([])\nax1.set_yticklabels([])\nax1.set_frame_on(False)\nax1.tick_params(tick1On=False)\nplt.title(\"Factors that Affect Ability to Adapt\",fontsize=18)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\n<a id=\"115\"></a>\n<h3 style=\"color:#D00892\">  1.1.5 City-wide Emissions </h3> "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# Does your city have a city-wide emissions inventory to report?\ncities_2020_emission = extract_answer(cities_2020,'4.0','City-wide emissions')\n\n# plot it!\ndf = cities_2020_emission['City-wide emissions'].value_counts()\nexplode=[0.2 for i in range(4)]\nax =df.plot.pie(explode=explode,pctdistance=0.7,autopct='%.2f%%',figsize=(6,6),colors=colors, labels=df.index )\nlistOfWhiteText=[]\nfor a in ax.texts:\n    if \"%\" in a.get_text(): \n        listOfWhiteText.append(a)\nplt.setp(listOfWhiteText, **{'color':'white'})\nax.set_ylabel('')\nplt.title(\"Does your city have a city-wide emissions inventory to report?\",fontsize=18)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\n<a id=\"116\"></a>\n<h3 style=\"color:#D00892\"> 1.1.6. Emissions Reduction </h3>\nThis section is interested because there are many select-type questions. We show some questions and the distributions of the answers. "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# Do you have a GHG emissions reduction target(s) in place at the city-wide level?\ncities_2020_GHG_reduction = extract_answer(cities_2020,'5.0','GHG Reduction')\ndf1 = cities_2020_GHG_reduction['GHG Reduction'].value_counts()\n\n# Is your city-wide emissions reduction target(s) conditional on the success of an externality or component of policy outside of your control?\ncities_2020_external_control_reduction = extract_answer(cities_2020,'5.2','External Control Reduction')\ndf2 = cities_2020_external_control_reduction['External Control Reduction'].value_counts()\n\n# Does your city-wide emissions reduction target(s) account for the use of transferable emissions units?\ncities_2020_transferable_emission = extract_answer(cities_2020,'5.3','Transferable Emission')\ndf3 = cities_2020_transferable_emission['Transferable Emission'].value_counts()\n\n# Does your city have a climate change mitigation or energy access plan for reducing city-wide GHG emissions?\ncities_2020_GHG_mitigation_plan = extract_answer(cities_2020,'5.5','GHG Mitigation Plan')\ndf4 = cities_2020_GHG_mitigation_plan['GHG Mitigation Plan'].value_counts()\n\nfig = plt.figure(figsize=(15,9))\nplt.suptitle(\"Emission Reduction\",fontsize=18)\nax1 = fig.add_subplot(221)\nax2 = fig.add_subplot(222)\nax3 = fig.add_subplot(223)\nax4 = fig.add_subplot(224)\n\ndf1.plot.pie(pctdistance=0.7,autopct='%.2f%%',colors=colors[3:], labels=df1.index, ax=ax1,ylabel=\"\" )\nax1.title.set_text(\"Do you have a GHG emissions reduction target(s) in \\n place at the city-wide level?\")\ndf2.plot.pie(pctdistance=0.7,autopct='%.2f%%',colors=colors[5:], labels=df2.index, ax=ax2,ylabel=\"\" )\nax2.title.set_text(\"Is your city-wide emissions reduction target(s) conditional \\n on the success of an externality or component of policy outside of your control?\")              \ndf3.plot.pie(pctdistance=0.7,autopct='%.2f%%',colors=colors[1:], labels=df3.index, ax=ax3,ylabel=\"\" )\nax3.title.set_text(\"Does your city-wide emissions reduction target(s) account \\n for the use of transferable emissions units?\")\ndf4.plot.pie(pctdistance=0.7,autopct='%.2f%%',colors=colors[8:], labels=df4.index, ax=ax4,ylabel=\"\" )\nax4.title.set_text(\"Does your city have a climate change mitigation or \\n energy access plan for reducing city-wide GHG emissions?\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\n<a id=\"117\"></a>\n<h3 style=\"color:#D00892\"> 1.1.7. Collaborations</h3>\nWe all know that compagnies have a huge impact on decisions. In the survey, this aspect has been covered by asking to the cities if they are working with the private businesses. The results are encouraging. "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# Does your city collaborate in partnership with businesses in your city on sustainability projects?\ncities_2020_transferable_emission = extract_answer(cities_2020,'6.2','Collaborations')\nsums = cities_2020_transferable_emission['Collaborations'].value_counts()\n\nfig = plt.figure(figsize=(10,2))\nplt.suptitle(\"Collaborations\",fontsize=18)\nplt.scatter(sums.index,[1,1,1,1,1],[x*6 for x in list(sums)],color=colors[::-1][3:8])\nplt.box(on=None)\nplt.yticks([])\nplt.ylim(0.2, 1.5)\nplt.xticks(rotation=-20,y=0.4, ha=\"left\" )\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\n<a id=\"118\"></a>\n<h3 style=\"color:#D00892\"> 1.1.8. Energie</h3>\nNow comes the strongest part of the form: where is the energy of the cities from? "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# Does your city have a renewable energy or electricity target?\ncities_2020_renewable_energie = extract_answer(cities_2020,'8.0','Renewable Energie Target')\nsums = cities_2020_renewable_energie['Renewable Energie Target'].value_counts()\n\nfig = plt.figure(figsize=(10,3))\nplt.suptitle(\"Renewable Energie Target\",fontsize=18)\nplt.hlines(sums.index, 0, sums, color=colors[7:],linewidth=3)\nplt.plot(sums, sums.index, 'o',color=colors[1])\nplt.box(on=None)\nplt.xticks([])\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Question 8.1 of the survey tackles the different sources of electricity of the cities. For 566 cities, we show below the number of answers for each source. "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# 8.1 Please indicate the source mix of electricity consumed in your city.\ncities_2020_energie_info =  cities_2020[(cities_2020['Question Number'] == '8.1')   ]\ndf_energie_info = cities_2020_energie_info[cities_2020_energie_info['Response Answer'].notna()]['Column Name'].value_counts().to_frame()\ndf_energie_info.columns=['Number of Answers']\ndisplay(HTML(df_energie_info.to_html()))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"More than half of cities fill the table of energy sources! What is highlighted from this question?"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# all sources in a unique dataframe\ncities_2020_sources_energy = extract_answer(cities_2020,'8.1',df_energie_info.index[0], condition=(cities_2020[\"Column Name\"]==df_energie_info.index[0]))\nfor source in df_energie_info.index[1:10]:\n    cities_2020_sources_energy = cities_2020_sources_energy.merge(extract_answer(cities_2020,'8.1',source, condition=(cities_2020[\"Column Name\"]==source)))\n\n# data are not numeric\nfor c in cities_2020_sources_energy.columns[1:]:\n    cities_2020_sources_energy[c] =  pd.to_numeric(cities_2020_sources_energy[c])\n    \n# print the total of the sources\ndf = cities_2020_sources_energy.transpose().drop(index='Account Number', axis=0)\nax = df.mean(axis=1).plot.pie(colors=colors,pctdistance=0.7,autopct='%.2f%%',figsize=(7,7))\nlistOfWhiteText=[]\nfor a in ax.texts:\n    if \"%\" in a.get_text(): \n        listOfWhiteText.append(a)\nplt.setp(listOfWhiteText, **{'color':'white'})\nax.set_ylabel('')\nplt.title(\"Mean of Percentages of Energy Sources\", fontsize=18)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We see that <i>Hydro</i> and <i>Gas</i> are the two main sources of the cities that have answered to the question. "},{"metadata":{},"cell_type":"markdown","source":"\n<a id=\"119\"></a>\n<h3 style=\"color:#D00892\"> 1.1.9. Transports</h3>\nTransports polute. Let's have a look at the transport decisions of the cities. "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"#What is the mode share of each transport mode in your city for passenger transport?\ndef transport_plot(dataset,ax,date):\n    dataset_transports =  dataset[(dataset['Question Number'] == '10.1')   ]\n    df_transports = dataset_transports[dataset_transports['Response Answer'].notna()]['Column Name'].value_counts().to_frame()\n\n    dataset_transports = extract_answer(dataset,'10.1',df_transports.index[0], condition=(dataset[\"Column Name\"]==df_transports.index[0]))\n    for source in df_transports.index[1:10]:\n        dataset_transports = dataset_transports.merge(extract_answer(dataset,'10.1',source, condition=(dataset[\"Column Name\"]==source)))\n\n    # data are not numeric\n    for c in dataset_transports.columns[1:]:\n        dataset_transports[c] =  pd.to_numeric(dataset_transports[c], errors='coerce')\n\n\n    # print the total of the sources\n    df = dataset_transports.transpose().drop(index='Account Number', axis=0)\n    df.mean(axis=1).plot.pie(colors=colors[5:],pctdistance=0.7,autopct='%.2f%%',ax=ax)\n    listOfWhiteText=[]\n    for a in ax.texts:\n        if \"%\" in a.get_text(): \n            listOfWhiteText.append(a)\n    plt.setp(listOfWhiteText, **{'color':'white'})\n    ax.set_ylabel('')\n    ax.set_xlabel(xlabel=date,fontsize=15)\n    return ax, dataset_transports\n\n# prepare plot\nfig = plt.figure(figsize=(15,6))\nax1 = fig.add_subplot(121)\nax2 = fig.add_subplot(122)\naxa, cities_2019_transports = transport_plot(cities_2019,ax1,\"Transports in 2019\")\naxb, cities_2020_transports = transport_plot(cities_2020,ax2,\"Transports in 2020\")\nplt.show()\n\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We sadly see that from 2019 to 2020, the mean percentage of <i>Private Motorized Transport</i> grows. We also see a new kind of vehicle entitled <i>Micro-Mobility</i>.  "},{"metadata":{},"cell_type":"markdown","source":"</a><a id='12'></a>\n<h2 style=\"color:#7209b7\">1.2 Data Analysis and KPI Extraction 🔎 </h2>\nNow that we have loaded and briefly analyzed some questions of the survey, we can go to the next step: data mining! First we merge the different features and list them. "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# recap of our datasets\nready_dataset = [cities_2020_numerical_pop,cities_2020_sources_energy,cities_2020_transports]\nto_transform_into_dummies = [cities_2020_renewable_energie,cities_2020_GHG_mitigation_plan,cities_2020_transferable_emission,cities_2020_external_control_reduction,cities_2020_GHG_reduction,cities_2020_admin_boundary,cities_2020_gov_and_data,cities_2020_risk,cities_2020_risk_detail,cities_2020_risk_factor_ability_adapt,cities_2020_emission]\n\n# transform categorical data into dummies\nfor df in to_transform_into_dummies:\n    df = pd.get_dummies(df, columns=[df.columns[1]], prefix=[df.columns[1]]).groupby(['Account Number'], as_index=False).sum() \n    ready_dataset.append(df)\n    \n# merge all dataset!\nall_num_2020 = ready_dataset[0] \nfor df in ready_dataset[1:]:\n    all_num_2020 = pd.merge(all_num_2020, df, on='Account Number', how=\"outer\")\n\n# inner join \nall_num_2020.head(2)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We have used get_dummies for the categorical variables (see [what is get_dummies?](https://pbpython.com/categorical-encoding.html)). The list of the features is the following:"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"display(Markdown(pd.Series(all_num_2020.columns, name=\"Features\").to_markdown()))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3 style=\"color:#8B4513\"> Coorelation between Features </h3>\nWe verify that columns are not too much correlated and remove them if any."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"f = plt.figure(figsize=(7,7))\nplt.matshow(all_num_2020.corr(),fignum=1)\nplt.xticks([])\nplt.yticks([])\ncb = plt.colorbar()\ncb.ax.tick_params(labelsize=14)\nplt.title('Correlation Matrix', fontsize=18);","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"# Select upper triangle of correlation matrix\n# source: https://stackoverflow.com/questions/29294983/how-to-calculate-correlation-between-all-columns-and-remove-highly-correlated-on\nupper = all_num_2020.corr().where(np.triu(np.ones(all_num_2020.corr().shape), k=1).astype(np.bool))\n# Find features with correlation greater than 0.95\nto_drop = [column for column in upper.columns if any(upper[column] > 0.9) and column!=\"Account Number\"]\n\n# Drop features \nall_num_2020_reduced = all_num_2020.drop(to_drop, axis=1)\nall_num_2020_reduced.shape","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<a id=\"121\"></a>\n<h3 style=\"color:#D00892\">1.2.1. Simple K-means to get some clusters and plot on a map</h3>"},{"metadata":{},"cell_type":"markdown","source":"I first used Kmeans to create clusters. To choose the number of clusters, I launch the Kmeans methods for 2 to 10 clusters and compute the Davies-Bouldin score. The minimal value of the score describe the best clustering with this metric. \n"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# replace NaN to -1\nall_num_2020_reduced.fillna(-1, inplace=True)\n\n# normalizing data\nscaler = StandardScaler()\nscaled_features = scaler.fit_transform(all_num_2020_reduced)\n\n# choose 5 because many categorical variables have 5 choices, arbitrary \n#kmeans = KMeans( n_clusters=5).fit(scaled_features)\n\ndbs = {} \nfor k in range(2, 10):\n    kmeans = KMeans(n_clusters=k,random_state=5).fit(scaled_features)\n    dbs[k] = davies_bouldin_score(scaled_features,kmeans.labels_)\nplt.figure()\nplt.plot(list(dbs.keys()), list(dbs.values()))\nplt.xlabel(\"Number of cluster\")\nplt.ylabel(\"Davies-Bouldin score\")\nplt.title(\"How many clusters should we keep?\", fontsize=18)\nplt.show()\n\nkmeans1 = KMeans(n_clusters=2,random_state=5).fit(scaled_features)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We then choose to keep 2 clusters which is nice to balance between cities that are in the good direction for climate change and cities that don't tackle the problem yet.<br>  Before analyzing the cluster feature importances, I decided to plot the cities. A color in the map corresponds to a cluster. "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# concat label to Account Number\nconcatenation_labels_an = pd.concat([all_num_2020_reduced[\"Account Number\"],pd.Series(kmeans1.labels_)],axis=1)\ngeo_cities_2020_noNa = geo_cities_2020.dropna(axis=0)\n\n#oopsie warning\npd.options.mode.chained_assignment = None\n# reshape geometry.. \ngeo_cities_2020_noNa.loc[:,\"geometry\"]= geo_cities_2020_noNa[\"City Location\"].apply(lambda x: Point(eval(x.split(\"POINT \")[1].replace(\" \",\",\"))))\ngeo_cities_2020_noNa = geo_cities_2020_noNa.drop([\"City Location\"],axis=1)\n\nprint(geo_cities_2020_noNa[geo_cities_2020_noNa['Account Number']==50378])\nmerge_cluster_an_geo = pd.merge(concatenation_labels_an, geo_cities_2020_noNa, on='Account Number', how=\"inner\")\nmerge_cluster_an_geo = merge_cluster_an_geo.dropna(axis=0)\n\n# start to plot! \nworld = gpd.read_file(gpd.datasets.get_path(\"naturalearth_lowres\"))\n\n# We restrict to South America.\nax = world.plot(color=\"#303030\", edgecolor='white',figsize=(18,10))\n\n# We can now plot our ``GeoDataFrame`` for the different clusters (column=0)\ngpd.GeoDataFrame(merge_cluster_an_geo[merge_cluster_an_geo[0]==1]).plot(ax=ax, color=colors[5])\ngpd.GeoDataFrame(merge_cluster_an_geo[merge_cluster_an_geo[0]==0]).plot(ax=ax, color=\"#ffd500\")\nplt.box(on=None)\nplt.xticks([])\nplt.yticks([])\n\n# to show the value: uncomment\n#geo_cities_2020_noNa[geo_cities_2020_noNa[\"Account Number\"]==50378].apply(lambda x: ax.annotate(s=x[\"Account Number\"],  xy=x.geometry.centroid.coords[0], ha='center', color=\"w\"),axis=1);","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"See the dots on the bottom... Number 50378 is \"San José, Costa Rica\". Yes, in Latin America. After some research on the crs (https://geopandas.org/projections.html) understanding, I realize that the longitude and latitude are reversed for this city. It's the case of other cities but not all... GOOD LUCK hhh"},{"metadata":{},"cell_type":"markdown","source":"In fact, we see that we cannot conclude anything about the location of the clusters. However, the cities of the survey are quite often near the oceans! "},{"metadata":{},"cell_type":"markdown","source":"Now we will look at the centroids of the clusters and point out the differences between them. To do so we compute the difference between the two centroids and present the features such that the difference is the highest. "},{"metadata":{"trusted":true},"cell_type":"code","source":"# cluster 0 - cluster 1 give us the difference between the centroids \ndifference_between_centroids = abs(kmeans1.cluster_centers_[0] - kmeans1.cluster_centers_[1])\n\n# add the labels\ndifference_between_centroids_df = pd.Series(difference_between_centroids, index=all_num_2020_reduced.columns)\ndifference_between_centroids_df\n\n# print the top highest feature importances\ndifference_between_centroids_df = difference_between_centroids_df.sort_values(ascending=False)\ndifference_between_centroids_df.head(10)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Interested results! We see that the features are strong. Now, we plot a 2D versions of the data point to see if the centroids are indeed well representing the clusters. If so, we are on a good path to extract some KPI. (I am using the PCA method to reduce dimensionality to 2. It is often used before the kmeans... maybe I should do that.  "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# to get 2 dimensions to plot the data points\ndef plot_pca_of_my_kmeans(scaled_features,kmeans,ax):\n    pca = PCA(n_components=2, random_state=1)\n    pca_results = pca.fit_transform(scaled_features)\n\n    for i in range(0, pca_results.shape[0]):\n        ax.scatter(pca_results[i,0],pca_results[i,1],c=colors[kmeans.labels_[i]*2+1], marker='x')\n    return ax\n\nplot_pca_of_my_kmeans(scaled_features,kmeans1,plt)\nplt.title('2D vizualisation of our datapoints', fontsize=16)\nplt.show()\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"From this chart, we are able to say two main things: first our kmean was quite good, second we should definitively do 4 clusters. I redo it but hide on Kaggle it because we already have the same work above. "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# a bit different of previous cluster search because I'm looking for 4 clusters.\ndbs = {} \nfor k in range(10, 20):\n    # in fact, I'm looking for a random_state that is better for n_clusters=4 \n    kmeans = KMeans(n_clusters=4,random_state=k).fit(scaled_features)\n    dbs[k] = davies_bouldin_score(scaled_features,kmeans.labels_)\n\nfig = plt.figure(figsize=(13,6))\nax1 = fig.add_subplot(121)\nax2 = fig.add_subplot(122)\n\nax1.plot(list(dbs.keys()), list(dbs.values()))\nax1.set_title(\"Plot to find the best random_state for n_cluster=4\")\n\n# the best is for random_state=17\nkmeans2 = KMeans(n_clusters=4,random_state=17).fit(scaled_features)\nplot_pca_of_my_kmeans(scaled_features,kmeans2,ax2)\nax2.set_title( \"Chart to show the new clusters\")\nplt.show()\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Result of this clustering is also very good but finally we work with the first version that does not contain any clusters that are grouped.<br>\nLet's come back to the features. We saw that the section `Factor Affect Adaptation` separate a lot the two groups. Let's figure that."},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"# taken from https://stackoverflow.com/questions/52910187/how-to-make-a-polygon-radar-spider-chart-in-python\ndef radar_factory(num_vars, frame='circle'):\n    \"\"\"Create a radar chart with `num_vars` axes.\n\n    This function creates a RadarAxes projection and registers it.\n\n    Parameters\n    ----------\n    num_vars : int\n        Number of variables for radar chart.\n    frame : {'circle' | 'polygon'}\n        Shape of frame surrounding axes.\n\n    \"\"\"\n    # calculate evenly-spaced axis angles\n    theta = np.linspace(0, 2*np.pi, num_vars, endpoint=False)\n\n    class RadarAxes(PolarAxes):\n\n        name = 'radar'\n\n        def __init__(self, *args, **kwargs):\n            super().__init__(*args, **kwargs)\n            # rotate plot such that the first axis is at the top\n            self.set_theta_zero_location('N')\n\n        def fill(self, *args, closed=True, **kwargs):\n            \"\"\"Override fill so that line is closed by default\"\"\"\n            return super().fill(closed=closed, *args, **kwargs)\n\n        def plot(self, *args, **kwargs):\n            \"\"\"Override plot so that line is closed by default\"\"\"\n            lines = super().plot(*args, **kwargs)\n            for line in lines:\n                self._close_line(line)\n\n        def _close_line(self, line):\n            x, y = line.get_data()\n            # FIXME: markers at x[0], y[0] get doubled-up\n            if x[0] != x[-1]:\n                x = np.concatenate((x, [x[0]]))\n                y = np.concatenate((y, [y[0]]))\n                line.set_data(x, y)\n\n        def set_varlabels(self, labels):\n            self.set_thetagrids(np.degrees(theta), labels)\n\n        def _gen_axes_patch(self):\n            # The Axes patch must be centered at (0.5, 0.5) and of radius 0.5\n            # in axes coordinates.\n            if frame == 'circle':\n                return Circle((0.5, 0.5), 0.5)\n            elif frame == 'polygon':\n                return RegularPolygon((0.5, 0.5), num_vars,\n                                      radius=.5, edgecolor=\"k\")\n            else:\n                raise ValueError(\"unknown value for 'frame': %s\" % frame)\n\n        def draw(self, renderer):\n            \"\"\" Draw. If frame is polygon, make gridlines polygon-shaped \"\"\"\n            if frame == 'polygon':\n                gridlines = self.yaxis.get_gridlines()\n                for gl in gridlines:\n                    gl.get_path()._interpolation_steps = num_vars\n            super().draw(renderer)\n\n\n        def _gen_axes_spines(self):\n            if frame == 'circle':\n                return super()._gen_axes_spines()\n            elif frame == 'polygon':\n                # spine_type must be 'left'/'right'/'top'/'bottom'/'circle'.\n                spine = Spine(axes=self,\n                              spine_type='circle',\n                              path=Path.unit_regular_polygon(num_vars))\n                # unit_regular_polygon gives a polygon of radius 1 centered at\n                # (0, 0) but we want a polygon of radius 0.5 centered at (0.5,\n                # 0.5) in axes coordinates.\n                spine.set_transform(Affine2D().scale(.5).translate(.5, .5)\n                                    + self.transAxes)\n\n\n                return {'polar': spine}\n            else:\n                raise ValueError(\"unknown value for 'frame': %s\" % frame)\n\n    register_projection(RadarAxes)\n    return theta\n\n","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# the idea is to show the answers of the cities of the two clusters.\n# difference_between_centroids_df\n# all_num_2020_reduced \n# kmean1.labels_ \n\nall_num_2020_reduced_c1 = all_num_2020_reduced.iloc[np.where(kmeans1.labels_ == 1)]\nall_num_2020_reduced_c1 = all_num_2020_reduced_c1.astype(float).mean(axis=0)\n\nall_num_2020_reduced_c2 = all_num_2020_reduced.iloc[np.where(kmeans1.labels_ == 0)]\nall_num_2020_reduced_c2 = all_num_2020_reduced_c2.astype(float).mean(axis=0)\n\ndef give_top_n(n, top, data):\n    '''\n    Give the n first columns of data with order given by top \n    '''\n    cols = [top.index[c] for c in range(0,n)]\n    return data[cols]\n    \ntop_C1 = give_top_n(9,difference_between_centroids_df, all_num_2020_reduced_c1)\ntop_C2 = give_top_n(9,difference_between_centroids_df, all_num_2020_reduced_c2)\n\n\ntheta = radar_factory(9, 'polygon')\n \nfig = plt.figure(figsize=(5,5))\nax = fig.add_subplot(1, 1, 1, projection='radar')\n\ncolors_2 = [colors[0*2+1],colors[1*2+1]]\ni=0\nfor d in [top_C2,top_C1]:\n    ax.plot(theta, d, color= colors_2[i] )\n    ax.fill(theta, d, color= colors_2[i], alpha=0.25)\n    i+=1\nax.set_varlabels([x.split(\"_\")[1] for x in top_C2.index])\nplt.title(\"What affect the climante change adapation of the cities?\", fontsize=16)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We sadly see that the blue cluster is different from the purple one mostly because they did not answer to the question of climate change adaptation. As this is not a KPI... we will redo the clustering step by removing rows that contain to many blanks. "},{"metadata":{},"cell_type":"markdown","source":"<a id=\"122\"></a>\n<h3 style=\"color:#D00892\">1.2.2. Improving K-means to get KPIs</h3>\n<p>I first remove the columns that have more than 100 lines with Nan values. Then I remove the rows that still contains values and obtain a nice sub-set of 421 cities and 80 features </p>"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# count NaN rows\ncount = all_num_2020.isna().sum()\n\n# remove columns that have more than 100 blanks\ncols_to_remove = [x for x  in all_num_2020.columns if count[x]>100]\nall_num_2020_remove_Nan_col = all_num_2020.drop(cols_to_remove,axis=1)\n\n# now the lines\nall_num_2020_no_Nan = all_num_2020_remove_Nan_col.dropna()\nall_num_2020_no_Nan.shape","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# normalizing data\nscaler = StandardScaler()\nscaled_features = scaler.fit_transform(all_num_2020_no_Nan)\n\ndbs = {} \nfor k in range(2, 10):\n    kmeans = KMeans(n_clusters=k,random_state=12).fit(scaled_features)\n    dbs[k] = davies_bouldin_score(scaled_features,kmeans.labels_)\n\nfig = plt.figure(figsize=(13,6))\nax1 = fig.add_subplot(121)\nax2 = fig.add_subplot(122)\n\nax1.plot(list(dbs.keys()), list(dbs.values()))\nax1.set_title(\"Chart to find the best number of clusters\")\n\n# the best is for random_state=17\nkmeans_noNan = KMeans(n_clusters=3,random_state=12).fit(scaled_features)\nplot_pca_of_my_kmeans(scaled_features,kmeans_noNan,ax2)\nax2.set_title( \"Chart to show the new clusters\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"I tried different random_state to get 2 clusters but most scores indicate to choose 7 clusters. As I prefer to have least clusters to interpret them, I choose 3 which is not so bad for a random_state to 12. Let go back to the  polygone chart and figures the differences! "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# what are the top differences between the three centroids:\ndifference_between_centroids = pd.Series(abs(kmeans_noNan.cluster_centers_[0] - kmeans_noNan.cluster_centers_[1])+\\\n                                       abs(kmeans_noNan.cluster_centers_[0] - kmeans_noNan.cluster_centers_[2])+\\\n                                      abs(kmeans_noNan.cluster_centers_[2] - kmeans_noNan.cluster_centers_[1]))\n\ndifference_between_centroids.sort_values(ascending=False, inplace=True)\n\n# a lot of copy/paste code...hhh, sorry, I will work better if you hire me, I promise\nall_num_2020_reduced_c1 = all_num_2020_no_Nan.iloc[np.where(kmeans_noNan.labels_ == 0)]\nall_num_2020_reduced_c1 = all_num_2020_reduced_c1.astype(float).mean(axis=0)\n\nall_num_2020_reduced_c2 = all_num_2020_no_Nan.iloc[np.where(kmeans_noNan.labels_ == 1)]\nall_num_2020_reduced_c2 = all_num_2020_reduced_c2.astype(float).mean(axis=0)\n\nall_num_2020_reduced_c3 = all_num_2020_no_Nan.iloc[np.where(kmeans_noNan.labels_ == 2)]\nall_num_2020_reduced_c3 = all_num_2020_reduced_c3.astype(float).mean(axis=0)\n    \ntop_C1 = give_top_n(15,difference_between_centroids, all_num_2020_reduced_c1)\ntop_C2 = give_top_n(15,difference_between_centroids, all_num_2020_reduced_c2)\ntop_C3 = give_top_n(15,difference_between_centroids, all_num_2020_reduced_c3)\n\n# and we plot it !!\ntheta = radar_factory(15, 'polygon')\n \nfig = plt.figure(figsize=(10,10))\nax = fig.add_subplot(1, 1, 1, projection='radar')\n\ncolors_2 = [colors[0*2+1],colors[1*2+1],colors[2*2+1]]\ni=0\nfor d in [top_C2,top_C1,top_C3]:\n    ax.plot(theta, d, color= colors_2[i] )\n    ax.fill(theta, d, color= colors_2[i], alpha=0.25)\n    i+=1\nax.set_varlabels([x for x in top_C2.index])\nplt.title(\"Top 15 features that separates the clusters\", fontsize=16)\nplt.show()\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"This chart shows the 15 main differences and similarities between the clusters. For instance, we remark that the blue cluster is mainly the cities that detailed several factors that affect their adapation while the red cluster represent the one that don't have any target for GHG reduction and don't have to answer to the External Control Reduction. Finaly the purple cluster has a cite-wide inventory to report but did not detail factor that affects the adapatations. "},{"metadata":{},"cell_type":"markdown","source":"<a id=\"123\"></a>\n<h3 style=\"color:#D00892\">1.2.3. Using GHG reduction as target </h3>\nIn this part, I decide to learn a decision tree with the target `GHG Reduction` which seems to be one important criteria of the study. "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"#all_num_2020_no_Nan.columns\nghglist = ['GHG Reduction_Base year emissions (absolute) target','GHG Reduction_Base year intensity target',\\\n           'GHG Reduction_Baseline scenario (business as usual) target','GHG Reduction_Fixed level target', \\\n           'GHG Reduction_No target']\n\ndef revert_get_dummiers_for_GHG(row):\n    for c in ghglist:\n        if row[c]==1:\n            return ghglist.index(c)\n        \nall_num_2020_no_Nan_Y = all_num_2020_no_Nan.apply(revert_get_dummiers_for_GHG, axis=1)\nall_num_2020_no_Nan_X_ = all_num_2020_no_Nan.drop(ghglist, axis=1)\nall_num_2020_no_Nan_X = all_num_2020_no_Nan_X_.drop([\"Account Number\",\"External Control Reduction_Question not applicable\"],axis=1)\nall_num_2020_no_Nan_X\n\nindex = all_num_2020_no_Nan_Y.index[all_num_2020_no_Nan_Y.apply(np.isnan)]\nall_num_2020_no_Nan_Y.drop(index,axis=0, inplace=True)\nall_num_2020_no_Nan_X.drop(index,axis=0,inplace=True)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"# I first try a decision tree\nX_train, X_test, y_train, y_test = train_test_split(all_num_2020_no_Nan_X, all_num_2020_no_Nan_Y, test_size=0.2, random_state=42)\nclf = tree.DecisionTreeClassifier(random_state=42)\nclf = clf.fit(X_train, y_train)\ny_pred = clf.predict(X_test)\n\nprint(classification_report(y_test, y_pred))\nr = tree.export_text(clf, feature_names=list(all_num_2020_no_Nan_X.columns))\nprint(r)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\nThe decision tree gives easy-to-ready outputs which is good for KPI extractions. The different classes that we set as targets of our classifier are the GHG Reduction feature and are the followings:\n- <b>class 0.0 = 'Base year emissions (absolute) target':</b> Reduce emissions by a specified quantity relative to a base year. For example, a 25% reduction in emissions from 1990 levels by 2030.\n- <b>class 1.0 = 'Base year intensity target:</b> Reduce  emissions  intensity  (emissions  per  unit  of  another variable, typically GDP or capital Gross Domestic Product –GDP or per capita) by a specified quantity  relative  to  a  base  year.  For  example,  a  40%  reduction  in  emissions  intensity  per capita from 1990 levels by 2030.\n- <b>class 2.0 = 'Baseline scenario (business as usual) target':</b> Reduce  emissions  by  a  specified  quantity  relative  to  a  projected emissions baseline scenario. A Business as Usual (BAU) baseline scenario is a reference case that  represents  future  emissions  most  likely  to  occur  if  the  current  trends  in  population, economy  and  technology  continue  and  in  the  absence  of  changes  in  current  energy  and climate policies. For example, a 30% reduction from baseline scenario emissions in 2030. \n- <b> class 3.0 = 'Fixed level target':</b> Reduce,  or  control  the  increase  of,  emissions  to  an  absolute  emissions level  in  a target  year.  One  type  of  fixed-level  target  is  a  carbon  neutrality  target,  which  is designed to reach zero net emissions by a certain date (e.g., 2050).\n- <b>class 4.0 = 'No target'</b>\n\n\n<h3 style=\"color:#FFA57D\"> First outcome:</h3> \n<i>Please click on \"show output\" of the previous box to see the decision tree. I have hiden it because the text is long.</i><br>\nWe created a training and testing set to validate our decision tree.<br>\nThe accuracy is 0.53 which is not very good. However when we look at the precision and recall of the different classes we see that the model can easly discover class 0.0 and class 4.0 than the others. This can be due to the size of the sets of those classes.</br>\nWe see that any the branches above the conditions \"GHG Mitigation Plan_Yes <= 0.50\" plus \"External Control Reduction_Yes >  0.50\" return class 4.0, i.e, no target. We can <i>conclude</i> that not having a mitigation plan yet (Question 5.5) and  being under an external control outside the policy of the city (Question 5.2) help cities to set tagets... <br> \nFrom this first \"KPI\", I see that the method is interested but not my features. I am going to improve the learning features, first by removing all the target questions (section 5 of the survey) and then by adding more data from other datasets. <br>\nI don't want to give more details on the interpretation of this results because the model (tree) is not good. \n"},{"metadata":{},"cell_type":"markdown","source":"<a id=\"2\"></a>\n<h1  style=\"color:#084FD0\">2. Intersection between Environment and Social Challenge for the Cities</h1>\n\n<p style=\"text-align: justify;\">In the previous section, we work with the dataset to explore it. We aims at finding solution to the compete problem, i.e., can we extract KPI in the dataset ? </p>\n<a id=\"21\"></a>\n<h2 style=\"color:#7209b7\">2.1 Logistic Regression for KPI Extractions</h2> \nLinear regression is an excellent method to find coorelations between data. However, for the moment, we are working with classes. Thanks to Logistic Regression, we are able to find a probabilistic model that will give us coefficients of the features. Similarly to the linear model, the logistic model can be learn with all the features or a restricted amount of them. As we want to give KPI, understandable by humans, we looked for model from 1 to 3 features. To do so, we learn all the possible models and keep the best one. \nLike in the previous section, we created a training and testing set to validate the model. \n"},{"metadata":{},"cell_type":"markdown","source":"Find below 3 functions that helps us to train the best logistic regressions:\n- <b>remove_outlier_from_zscore</b>: removes the rows that are outliers by using the outlier definition given by the zscore\n- <b>compute_regressions</b>: learns all the logistic regressions of 1 to 3 variables. \n- <b>train_and_print_regression</b>: shows several results that we wil use to interpert and extract valuable KPI"},{"metadata":{"trusted":true},"cell_type":"code","source":"def remove_outlier_from_zscore(df, column,y_follows):\n    '''\n    Function to remove outliers\n    source: https://stackoverflow.com/questions/23199796/detect-and-exclude-outliers-in-pandas-data-frame\n    '''\n    df['z_score']=stats.zscore(df[column])\n    rows = df[df['z_score'].abs()>3].index\n    y_follows = y_follows.drop(rows,axis=0)\n    return df[df['z_score'].abs()<=3], y_follows\n\n\ndef compute_regressions(X, Y):\n    '''\n    Function to learn a good model of 3 features (X)\n    @return: best logistic regression + the list of the columns used for it\n    source: memories of my Master degree! \n    '''\n    best_model = None\n    best_score = 0 \n    columns = None\n    \n    def learn_regression(X,Y,list_of_indices,best_score, best_model,columns):\n        X2 = X[[X.columns[i] for i in list_of_indices]]\n        Y2 = Y\n        # remove outliers and same rows in Y\n        for c in list_of_indices:\n            X2, Y2 = remove_outlier_from_zscore(X2,X.columns[c],Y2)\n        X2 = X2.drop(\"z_score\",axis=1)\n        # learn model\n        model = LogisticRegression(random_state=1)\n        model.fit( X2,Y2)\n        result = model.score(X2,Y2)\n        # if better, keep it\n        if result > best_score:\n            best_model = model\n            best_score = result\n            columns = [X.columns[i] for i in list_of_indices]\n            print(\"...score:\",result, columns)\n        return (best_score, best_model, columns)\n    \n    # learn regression of 1, 2 or 3 predictive variables\n    for i in range(0,len(X.columns)):\n        best_score, best_model, columns = learn_regression(X, Y,[i],best_score, best_model,columns)\n        for j in range(i+1,len(X.columns)): \n            best_score, best_model, columns = learn_regression(X, Y,[i,j],best_score, best_model,columns)\n            for k in range(j+1,len(X.columns)): \n                best_score, best_model, columns = learn_regression(X, Y,[i,j,k],best_score, best_model, columns)\n                \n    return best_model, columns\n\ndef train_and_print_regression(X,Y):\n    '''\n    Function that split the training and testing set\n    Get the best regression model for 3 features\n    Show accuracy on the testing set\n    '''\n    X_train, X_test, y_train, y_test = train_test_split(X,Y, test_size=0.3, random_state=1)\n    model, keys = compute_regressions(X_train,y_train)\n    y_pred = model.predict(X_test[[keys[i] for i in range(0,len(keys))]])\n    \n    print(\"Training Score:\",model.score(X_train[[keys[i] for i in range(0,len(keys))]],y_train))\n    print(\"Testing Score:\",model.score(X_test[[keys[i] for i in range(0,len(keys))]],y_test))\n    \n    # nice prints \n    # print(classification_report(list(y_test), [1 if x>0.5 else 0 for x in y_pred]))\n    for k in range(0,len(keys)):\n        print(keys[k],  str(model.coef_[0][k]))\n    return model, keys","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Below (which is hidden in the Kaggle notebook view, open it if interested), we preprocess a bit the data. First, I remove columns of our previous dataset that have more than 100 blanks (Nan values). Then, I remove the lines that contains blanks too. After this, we see that there many columns that are hard to interpret like columns with `Do not know`, `Question not applicable`... I took arbitrary decisions like removing some of them and associating some to real answer. Find below the list of decisions:\n- <b>.._Do not know</b>: if the cities answer that they do not know for climat change target, I feel like they didn't plan something so I associated this to the negative answers\n- <b>.._Intending to undertake in the next 2 years</b>: I associated this target to yes. In general, the motivation is present and that what interest us\n- <b>.._Question not applicable\" and .._unknow</b>: what would we do if we find some KPI with that target? I removed this. \n\nThen, I applied a scaler to the variables. "},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# I take back the data of the cities only. I want to work on the energies, transports and emissions. \n# count NaN rows\ncount = all_num_2020.isna().sum()\n\n# remove columns that have more than 100 blanks\ncols_to_remove = [x for x  in all_num_2020.columns if count[x]>250]\nall_num_2020_remove_Nan_col = all_num_2020.drop(cols_to_remove,axis=1)\n\n# now the lines\nall_num_2020_remove_Nan_col = all_num_2020_remove_Nan_col.dropna()\n\n# I modify the Do not know to -> \"no\"\n# and \"Intending to undertake in the next 2 years\" to \"yes\" \nfor c in all_num_2020_remove_Nan_col.columns:\n    if \"Do not know\" in c:\n        if c.split(\"_\")[0]+\"_Not intending to undertake\" in all_num_2020_no_Nan.columns:\n            all_num_2020_remove_Nan_col[c.split(\"_\")[0]+\"_Not intending to undertake\"] =  all_num_2020_remove_Nan_col[c.split(\"_\")[0]+\"_Not intending to undertake\"]+all_num_2020_remove_Nan_col[c]\n        elif c.split(\"_\")[0]+\"_Not intending to incorporate\" in all_num_2020_remove_Nan_col.columns:\n            all_num_2020_remove_Nan_col[c.split(\"_\")[0]+\"_Not intending to incorporate\"] =  all_num_2020_remove_Nan_col[c.split(\"_\")[0]+\"_Not intending to incorporate\"]+all_num_2020_remove_Nan_col[c]\n        else :\n            all_num_2020_remove_Nan_col[c.split(\"_\")[0]+\"_No\"] =  all_num_2020_remove_Nan_col[c.split(\"_\")[0]+\"_No\"]+all_num_2020_remove_Nan_col[c]\n        all_num_2020_remove_Nan_col.drop(c,axis=1, inplace=True)\n    if \"Intending to undertake in the next 2 years\" in c:\n        all_num_2020_remove_Nan_col[c.split(\"_\")[0]+\"_Yes\"] = all_num_2020_remove_Nan_col[c.split(\"_\")[0]+\"_Yes\"]+all_num_2020_remove_Nan_col[c]\n        all_num_2020_remove_Nan_col.drop(c,axis=1, inplace=True)\n    if \"Question not applicable\" in c:\n        all_num_2020_remove_Nan_col.drop(c,axis=1, inplace=True)\n    if \"Unknown\" in c:\n        all_num_2020_remove_Nan_col.drop(c,axis=1, inplace=True)\n\n# remove nearly empty columns (due to get dummies)\ncount = all_num_2020_remove_Nan_col.sum()   \ncols_to_remove = [i for i in count.index if int(count[i])<10]\nall_num_2020_remove_Nan_col = all_num_2020_remove_Nan_col.drop(cols_to_remove,axis=1)\n\n\n# Select upper triangle of correlation matrix\nupper = all_num_2020_remove_Nan_col.corr().where(np.triu(np.ones(all_num_2020_remove_Nan_col.corr().shape), k=1).astype(np.bool))\n# Find features with correlation greater than 0.9\nto_drop = [column for column in upper.columns if any(upper[column] > 0.85) and column!=\"Account Number\"]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"# We arbitrary define the targets (Y value)\ntarget_columns = ['Renewable Energie Target_In progress',\n       'Renewable Energie Target_Not intending to undertake',\n       'Renewable Energie Target_Yes', 'GHG Mitigation Plan_In progress',\n       'GHG Mitigation Plan_Yes',\n       'GHG Reduction_Base year emissions (absolute) target',\n       'GHG Reduction_Base year intensity target',\n       'GHG Reduction_Baseline scenario (business as usual) target',\n       'GHG Reduction_Fixed level target', 'GHG Reduction_No target',\n        'City-wide emissions_In progress', 'City-wide emissions_Yes']\n# then the predictive values (X) are the others\nX_cities = all_num_2020_remove_Nan_col.drop(target_columns, axis=1)\nX_cities_ = X_cities.drop([\"Account Number\", \"Current population\" ,\"Projected population\"], axis=1)\n\n# scale the data (but not sure it is usefull with this model..)\nscaler = StandardScaler()\nX_cities_scaled = scaler.fit_transform(X_cities_[X_cities_.columns])\nX_cities_scaled = pd.DataFrame(X_cities_scaled, columns = X_cities_.columns, index=X_cities_.index)\n#X_cities_scaled[\"cst\"] = 1","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<a id=\"22\"></a>\n<h2 style=\"color:#7209b7\">2.2 Results </h2> \n   Now that we had the functions, we can run it to extract the predictive values for different targets. As the learning step is long, I have run it, discovered the columns and just present the best logistic regression in the notebook. However, if you want to verify that the chosen predictive values are the good ones, you can uncomment the corresponding line in the code to run the regressions.  "},{"metadata":{},"cell_type":"markdown","source":"The logistic regression, similarly to the OSL, gives equations of probabilities based on coefficients:</br>\n\nlog(p/1-p) = b0 + C1*x1 + C2*x2 + C3*x3.\n"},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"def just_run_the_best_model(X, Y, X_predictive_columns):\n    X2 = X[X_predictive_columns]\n    Y2 = Y\n    \n    X_train, X_test, y_train, y_test = train_test_split(X2,Y2, test_size=0.3, random_state=1)    \n    X_train2, y_train2 = X_train.copy(), y_train.copy()\n    \n    # remove outliers and same rows in Y2\n    for c in range(0,len(X_train2.columns)):\n        X_train2, y_train2 = remove_outlier_from_zscore(X_train2,X_train2.columns[c],y_train2)\n    X_train2 = X_train2.drop(\"z_score\",axis=1)\n    # learn model\n    model = LogisticRegression(random_state=1)\n    model.fit(X_train2,y_train2)\n    \n    y_pred = model.predict(X_test)\n    \n    print(\"Training Score:\",model.score(X_train,y_train))\n    print(\"Testing Score:\",model.score(X_test,y_test),\"\\n\")\n    \n    # nice prints \n    print(classification_report(list(y_test), [1 if x>0.5 else 0 for x in y_pred]))\n    for k in range(0,len(X2.columns)):\n        print(X2.columns[k], str(model.coef_[0][k]))\n    return model","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<a id=\"221\"></a>   <h3 style=\"color:#D00892\">2.2.1 KPI for 'Renewable Energie Target_Not intending to undertake' </h3>"},{"metadata":{"trusted":true,"_kg_hide-input":false},"cell_type":"code","source":"# to run all the model, uncomment the following line\n# model, keys = train_and_print_regression(X_cities_scaled,all_num_2020_remove_Nan_col['Renewable Energie Target_Not intending to undertake'])    \nmodel = just_run_the_best_model(X_cities_scaled,all_num_2020_remove_Nan_col['Renewable Energie Target_Not intending to undertake'],\n                                     [\"Hydro\",\"Geothermal\",\"Factor Affect Climate Adaptation_Access to basic services\"])#,\"cst\"])    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"From this first result, we get the following: </br>\n\nlog(p/1-p) = xHydro * 0.6697022837363221 + xGeothermal * -0.933891979530483 + xFactor.. * -0.053014559214614364 + cts</br>\n\nDespite an average accuracy to 0.82, we see that the model can only predict negative data. We could interpret the following: given `Hydro`, `Geothermal` and `Factor Affect Climate Adaptation_Access to basic services` of a city, if the model predicts `Renewable Energie Target_Not intending to undertak` to `False`, the prediction is 82 percent correct. However, when we look at the data, we see that the result is due to the percentage of the classes. There are 160 `False` values for 38 `True` ones. "},{"metadata":{"trusted":true},"cell_type":"code","source":"all_num_2020_remove_Nan_col['Renewable Energie Target_Not intending to undertake'].value_counts()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We will try to run a very small model of 76 cities (38 False, and 38 True). This will implies a very small amout of data for the training and testing set."},{"metadata":{"trusted":true},"cell_type":"code","source":"# find 38 positives to keep\nindex_of_pos_to_remove = [x for x in all_num_2020_remove_Nan_col.index if all_num_2020_remove_Nan_col['Renewable Energie Target_Not intending to undertake'][x]==0]\nsuffle_index = random.sample(index_of_pos_to_remove,160-38)\nsuffle_index\n\nX_221 = X_cities_scaled.drop(suffle_index)\nY_221 = all_num_2020_remove_Nan_col.drop(suffle_index)\n#model, keys = train_and_print_regression(X_221,Y_221['Renewable Energie Target_Not intending to undertake'])    \nmodel = just_run_the_best_model(X_221,Y_221['Renewable Energie Target_Not intending to undertake'],[\"Hydro\"])    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The feature `Hydro` seems to be the most coorelated to the `Renewable Energie Target_Not intending to undertake` target. When a city uses Hydro as energy, the probability to give a negative answer to the question about renewable energie target is lower than giving a positive answer. Those cities answer positive target like \"in progress\" or \"yes\". "},{"metadata":{},"cell_type":"markdown","source":" <a id=\"222\"></a>  <h3 style=\"color:#D00892\">2.2.2 KPI for 'GHG Mitigation Plan_Yes' </h3>"},{"metadata":{},"cell_type":"markdown","source":"For each feature, we now reduce the data to get a good percentage of True/False value to predict.  "},{"metadata":{"trusted":true},"cell_type":"code","source":"all_num_2020_remove_Nan_col['GHG Mitigation Plan_Yes'].value_counts()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# find 38 positives to keep\nindex_of_pos_to_remove = [x for x in all_num_2020_remove_Nan_col.index if all_num_2020_remove_Nan_col['GHG Mitigation Plan_Yes'][x]==1]\nsuffle_index = random.sample(index_of_pos_to_remove,163-35)\nsuffle_index\n\nX_222 = X_cities_scaled.drop(suffle_index)\nY_222 = all_num_2020_remove_Nan_col.drop(suffle_index)\n# to run all the model, uncomment the following line\n#model, keys = train_and_print_regression(X_222,Y_222[ 'GHG Mitigation Plan_Yes'])    \nmodel = just_run_the_best_model(X_222,Y_222['GHG Mitigation Plan_Yes'],\n                                     [\"Wind\",\"External Control Reduction_No\",\"Governance & Data Management_In progress\"])    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h2 style=\"color:#7209b7\">2.3 Conclusion </h2> "},{"metadata":{},"cell_type":"markdown","source":"The method can be applied to any other features but I did not run this because of time. We see that there's no big conclusion on the features that I have applied. "},{"metadata":{},"cell_type":"markdown","source":"<h1 style=\"color:#084FD0\"> 3. Other Datasets</h1>"},{"metadata":{},"cell_type":"markdown","source":"In the first part of the compete,some Kaggle users shared some datasets for the competition. In this section, we try to add them in our dataset. "},{"metadata":{},"cell_type":"markdown","source":"<h2 style=\"color:#7209b7\">3.1 Adding Country Information to Cities</h2> \n\nIn this section, we concatenate several datasets about countries and our city responses in order to see if their target is related to some sociality and environnmental metrics that are not from their point of view."},{"metadata":{"trusted":true,"_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"# I decide to keep the same target \ncities_plus_countries_Y = pd.concat([all_num_2020_no_Nan_Y,all_num_2020_no_Nan_X_[\"Account Number\"]], axis=1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"First, I remove all the target columns because those columns are too much coorelated to the GHG reduction and don't give much informations in the intersection of sociaties and environnment. "},{"metadata":{"trusted":true,"_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"# first I remove all target questions (about future plans)\n# + Projected population because current and Difference is enough\ntarget_columns = [\"Projected population\",\\\n                  'Renewable Energie Target_Do not know',\\\n                   'Renewable Energie Target_In progress',\\\n               'Renewable Energie Target_Intending to undertake in the next 2 years',\\\n               'Renewable Energie Target_Not intending to undertake',\\\n               'Renewable Energie Target_Yes', 'GHG Mitigation Plan_Do not know',\\\n               'GHG Mitigation Plan_In progress',\\\n               'GHG Mitigation Plan_Intending to undertake in the next 2 years',\\\n               'GHG Mitigation Plan_Not intending to undertake',\\\n               'GHG Mitigation Plan_Yes', 'Collaborations_Do not know',\\\n                         'Governance & Data Management_Do not know',\\\n       'Governance & Data Management_In progress',\\\n       'Governance & Data Management_Intending to incorporate in the next 2 years',\\\n       'Governance & Data Management_Not intending to incorporate',\\\n       'Governance & Data Management_Yes', 'Climate Change_Do not know',\\\n                         'City-wide emissions_In progress',\\\n       'City-wide emissions_Intending to undertake in the next 2 years',\\\n       'City-wide emissions_Not intending to undertake',\\\n                  'External Control Reduction_Do not know',\\\n                  'External Control Reduction_Yes',\\\n                  'External Control Reduction_No',\\\n       'City-wide emissions_Yes']\ncities_plus_countries_X = all_num_2020_no_Nan_X_.drop(target_columns, axis=1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We have loaded some public datasets and now we link the data to the cities: \n- <b>yearly-air-quality-index-aqi-for-CDP-Cities:</b> an environnment information \n- <b>globses:</b> some sociality information like a Socioeconomic Status Score  \n- <b>ecological-footprint:</b> some environnment information like Forest Footprint"},{"metadata":{"trusted":true,"_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"# we add the column arithmetic_mean of the last year of each city \n# taken from the yearly-air-quality-index-aqi-for-CDP-Cities\nair = pd.read_csv(\"../input/yearly-air-quality-index-aqi-for-cdp-cities/year_over_year_aqi_data_v2.csv\")\nair = air.sort_values(by='year', ascending=False).groupby(\"account_number\").head(1)\nair.drop([c for c in air.columns if c not in [\"account_number\",\"arithmetic_mean\"] ], axis=1, inplace=True)\nair.columns = ['Account Number','Air Quality']\ncities_plus_countries_X = cities_plus_countries_X.merge(air)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"# add SES, gdppc and popshare from the globses dataset\ncountry_cities_2020 = pd.read_csv(\"/kaggle/input/cdp-unlocking-climate-solutions/Cities/Cities Disclosing/2020_Cities_Disclosing_to_CDP.csv\", usecols=[\"Account Number\", \"Country\"])\nglobses = pd.read_csv(\"../input/globses/GLOB.SES.csv\",usecols=[\"country\",\"year\",\"SES\",\"gdppc\",\"popshare\"], encoding = \"ISO-8859-1\" )\nglobses = globses.sort_values(by='year', ascending=False).groupby(\"country\").head(1).drop(\"year\",axis=1)\nglobses_cities = country_cities_2020.merge(globses, left_on=\"Country\",right_on=\"country\").drop([\"country\",\"Country\"],axis=1)\ncities_plus_countries_X = cities_plus_countries_X.merge(globses_cities)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"# add footprint from the ecological-footprint dataset\nfootprint = pd.read_csv(\"../input/ecological-footprint/countries.csv\", encoding = \"ISO-8859-1\" ).drop([\"Region\",\"Population (millions)\",\"Data Quality\"],axis=1)\nfootprint_cities = country_cities_2020.merge(footprint, left_on=\"Country\",right_on=\"Country\")\ncities_plus_countries_X = cities_plus_countries_X.merge(footprint_cities).drop(\"Country\",axis=1)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"sociality_env = cities_plus_countries_Y.merge(cities_plus_countries_X)\nsociality_env = sociality_env.dropna(axis=0)\ncities_plus_countries_X_ = sociality_env.drop([0,\"GDP per Capita\",\"Countries Required\",\"Earths Required\", \"Account Number\",\"External Control Reduction_Question not applicable\"], axis=1)\ncities_plus_countries_Y_ = sociality_env[0]\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"In fact, this idea is not good because to many cities have the same measures and are coorelated. We could either run several models with at most one city per country or run only a model on the new features of the country. Because the new features are type of float, I am curious to try a OLS model on those only. "},{"metadata":{},"cell_type":"markdown","source":"<h2 style=\"color:#7209b7\">3.2  Country Information Only</h2> \n"},{"metadata":{},"cell_type":"markdown","source":"\n"},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"globses = pd.read_csv(\"../input/globses/GLOB.SES.csv\",usecols=[\"country\",\"year\",\"SES\",\"gdppc\",\"popshare\"], encoding = \"ISO-8859-1\" )\nglobses = globses.sort_values(by='year', ascending=False).groupby(\"country\").head(1).drop(\"year\",axis=1)\n\nfootprint = pd.read_csv(\"../input/ecological-footprint/countries.csv\", encoding = \"ISO-8859-1\" ).drop([\"Region\",\"Population (millions)\",\"Data Quality\"],axis=1)\nglobses_plus_footprint = footprint.merge(globses, left_on=\"Country\",right_on=\"country\").drop([\"country\",\"Country\"],axis=1)\nglobses_plus_footprint.drop(\"GDP per Capita\", axis=1, inplace=True)  \n\ncount = globses_plus_footprint.isna().sum()\n\n# remove columns that have more than 100 blanks\ncols_to_remove = [x for x  in globses_plus_footprint.columns if count[x]>50]\nglobses_plus_footprint = globses_plus_footprint.drop(cols_to_remove,axis=1)\n\n# now the lines\nglobses_plus_footprint = globses_plus_footprint.dropna()\nglobses_plus_footprint","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"def learn_regression(X,Y):\n    # learn model\n    X = sm.add_constant(X)\n    model = sm.OLS(Y, X)\n    result = model.fit()\n    return result\n\n\nfor x in range(0, len(globses_plus_footprint.columns)):\n    for y in range(x+1, len(globses_plus_footprint.columns)):\n        X = np.asarray(globses_plus_footprint[globses_plus_footprint.columns[x]])\n        Y= np.asarray(globses_plus_footprint[globses_plus_footprint.columns[y]])\n        result = learn_regression(X,Y)\n        if result.rsquared > 0.8 :\n            print(globses_plus_footprint.columns[x],globses_plus_footprint.columns[y],result.rsquared)\n            best_rsquared = result.rsquared\n            best_model = result\n            columns = [globses_plus_footprint.columns[x],globses_plus_footprint.columns[y]]\n            ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"I have played Linear Regression by pair of feature to see if one is a linear combinaison of another one. I displayed the features and scores of the ones that have a Rsquared greater then 0.8. We see that the Human Development Index is very coorelated to the Socioeconomic status score. Those scores come from different datasets. Also, we remark that the Carbon Footprint in related to the Gross domestic product Per Capita. We want to see in which way one implies the other one: "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"print(learn_regression(np.asarray(globses_plus_footprint[\"Carbon Footprint\"]),np.asarray(globses_plus_footprint[\"gdppc\"])).summary())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The p-value is high meaning that there 35% of probability that the Carbon Footprint is not related to the Gross domestic product Per Capita. "},{"metadata":{},"cell_type":"markdown","source":"<h1 style=\"color:#D00892\">Thank you for reading</h1>\nAnd thanks for the nice comments! "},{"metadata":{"trusted":true,"_kg_hide-input":true,"_kg_hide-output":false},"cell_type":"code","source":"# what are the top differences between the three centroids:\ndifference_between_centroids = pd.Series(abs(kmeans_noNan.cluster_centers_[0] - kmeans_noNan.cluster_centers_[1])+\\\n                                       abs(kmeans_noNan.cluster_centers_[0] - kmeans_noNan.cluster_centers_[2])+\\\n                                      abs(kmeans_noNan.cluster_centers_[2] - kmeans_noNan.cluster_centers_[1]))\n\ndifference_between_centroids.sort_values(ascending=False, inplace=True)\n\n# a lot of copy/paste code...hhh, sorry, I will work better if you hire me, I promise\nall_num_2020_reduced_c1 = all_num_2020_no_Nan.iloc[np.where(kmeans_noNan.labels_ == 0)]\nall_num_2020_reduced_c1 = all_num_2020_reduced_c1.astype(float).mean(axis=0)\n\nall_num_2020_reduced_c2 = all_num_2020_no_Nan.iloc[np.where(kmeans_noNan.labels_ == 1)]\nall_num_2020_reduced_c2 = all_num_2020_reduced_c2.astype(float).mean(axis=0)\n\nall_num_2020_reduced_c3 = all_num_2020_no_Nan.iloc[np.where(kmeans_noNan.labels_ == 2)]\nall_num_2020_reduced_c3 = all_num_2020_reduced_c3.astype(float).mean(axis=0)\n    \ntop_C1 = give_top_n(15,difference_between_centroids, all_num_2020_reduced_c1)\ntop_C2 = give_top_n(15,difference_between_centroids, all_num_2020_reduced_c2)\ntop_C3 = give_top_n(15,difference_between_centroids, all_num_2020_reduced_c3)\n\n# and we plot it !!\ntheta = radar_factory(15, 'polygon')\n \nfig = plt.figure(figsize=(20,20))\nax = fig.add_subplot(1, 1, 1, projection='radar')\n\ncolors_2 = [colors[0*2+1],colors[1*2+1],colors[2*2+1]]\ni=0\nfor j in range(4,43):\n    theta = radar_factory(j, 'polygon')\n    top_C1 = give_top_n(j,difference_between_centroids, all_num_2020_reduced_c1)\n    top_C2 = give_top_n(j,difference_between_centroids, all_num_2020_reduced_c2)\n    top_C3 = give_top_n(j,difference_between_centroids, all_num_2020_reduced_c3)\n    for d in [top_C2, top_C1, top_C3]:\n        #ax.plot(theta, d, color= colors[i%10] )\n        ax.fill(theta, d, color= colors[i%10], alpha=0.25)     \n        i+=1\n#ax.set_varlabels([x for x in top_C2.index])\n#plt.title(\"Top 15 features that separates the clusters\", fontsize=16)\nax.yaxis.grid(False)\nax.xaxis.grid(False)\nplt.xticks([])\nplt.yticks([])\nplt.show()\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Please upvote if you like it :)"}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}