
# Empirical project 10


## Getting started in Python


Visit the ‘Getting started in Python’ page for help and advice on setting up a Python session to work with. Remember, you can run any page from this book as a *notebook* by downloading the relevant file from this [repository](https://github.com/aeturrell/core_python) and running it on your own computer. Alternatively, you can run pages online in your browser over at [Binder](https://mybinder.org/v2/gh/aeturrell/core_python/HEAD).


## Preliminary settings


Let's import the packages we'll need and also configure the settings we want:


```python
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
from pathlib import Path
import pingouin as pg
from lets_plot import *


LetsPlot.setup_html(no_js=True)
```


### *Python walk-through 10.1* Importing an Excel spreadsheet into Python


Before loading an Excel spreadsheet into Python, it can be helpful to open it in Excel to understand the structure of the spreadsheet and the data it contains. In this case we can see that detailed descriptions of all variables are in the first tab ('Definitions and Sources'). Make sure to read the definitions for the indicators listed in Figure 10.1.


The spreadsheet contains a number of other worksheets, but the data that we need is in the tab called 'Data – June 2016'. You can see that the variable names are all in the first row and missing values are simply empty cells. We can therefore proceed to import the data into Python using the `pd.read_excel` function without any additional options.


We're going to assume here that you've downloaded the Excel file as 'GlobalFinancialDevelopmentDatabaseJune2017.xlsx', and saved it in a subfolder of your working directory called 'data'. Remember to change the filename and folder names if you’ve downloaded a later version of the data and saved it in another location.  


```python
gfdd = pd.read_excel(
    Path("data/GlobalFinancialDevelopmentDatabaseJune2017.xlsx"),
    sheet_name="Data - June 2016",
)
gfdd.head()
```


<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
.dataframe tbody tr th {
    vertical-align: top;
}
.dataframe thead th {
    text-align: right;
}
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>ISO3</th>
      <th>ISO2</th>
      <th>IMF code</th>
      <th>Country</th>
      <th>Region</th>
      <th>Income group</th>
      <th>Year</th>
      <th>GFDD.AI.01</th>
      <th>GFDD.AI.02</th>
      <th>GFDD.AI.03</th>
      <th>...</th>
      <th>GFDD.OI.18</th>
      <th>GFDD.OI.19</th>
      <th>GFDD.OM.01</th>
      <th>GFDD.OM.02</th>
      <th>GFDD.OE.01</th>
      <th>GFDD.OE.02</th>
      <th>NY.GDP.MKTP.CD</th>
      <th>NY.GDP.PCAP.KD</th>
      <th>NY.GNP.MKTP.CD</th>
      <th>SP.POP.TOTL</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>AFG</td>
      <td>AF</td>
      <td>512.0</td>
      <td>Afghanistan</td>
      <td>South Asia</td>
      <td>Low income</td>
      <td>1960</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>...</td>
      <td>NaN</td>
      <td>0.0</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>540000000.0</td>
      <td>NaN</td>
      <td>550000000.0</td>
      <td>9000000.0</td>
    </tr>
    <tr>
      <th>1</th>
      <td>AFG</td>
      <td>AF</td>
      <td>512.0</td>
      <td>Afghanistan</td>
      <td>South Asia</td>
      <td>Low income</td>
      <td>1961</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>...</td>
      <td>NaN</td>
      <td>0.0</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>550000000.0</td>
      <td>NaN</td>
      <td>560000000.0</td>
      <td>9200000.0</td>
    </tr>
    <tr>
      <th>2</th>
      <td>AFG</td>
      <td>AF</td>
      <td>512.0</td>
      <td>Afghanistan</td>
      <td>South Asia</td>
      <td>Low income</td>
      <td>1962</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>...</td>
      <td>NaN</td>
      <td>0.0</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>550000000.0</td>
      <td>NaN</td>
      <td>560000000.0</td>
      <td>9300000.0</td>
    </tr>
    <tr>
      <th>3</th>
      <td>AFG</td>
      <td>AF</td>
      <td>512.0</td>
      <td>Afghanistan</td>
      <td>South Asia</td>
      <td>Low income</td>
      <td>1963</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>...</td>
      <td>NaN</td>
      <td>0.0</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>750000000.0</td>
      <td>NaN</td>
      <td>770000000.0</td>
      <td>9500000.0</td>
    </tr>
    <tr>
      <th>4</th>
      <td>AFG</td>
      <td>AF</td>
      <td>512.0</td>
      <td>Afghanistan</td>
      <td>South Asia</td>
      <td>Low income</td>
      <td>1964</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>...</td>
      <td>NaN</td>
      <td>0.0</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>800000000.0</td>
      <td>NaN</td>
      <td>820000000.0</td>
      <td>9700000.0</td>
    </tr>
  </tbody>
</table>
<p>5 rows × 122 columns</p>
</div>


### *Python walk-through 10.2* Making box and whisker plots


Box and whisker plots were introduced in Empirical Project 6. We can use the same process here, after ensuring that the data is in the correct format.


Some plotting libraries expect data like we have ingested to be in ‘long’ (aka tidy) format (where each row is a value for a single variable and year), whereas our data is in ‘wide’ format (each row contains a single variable but multiple years). We transform the data from wide to long format using the `melt` function, and drop missing variables with the `.dropna` function.


So for the Depth indicators:


```python
# for convenience, create a list of the indicators we're interested in:
indicators = ["private_credit", "bank_assets"]


# Rename the variables we'll be plotting
gfdd_new_names = gfdd.rename(
    columns={"GFDD.DI.01": indicators[0], "GFDD.DI.02": indicators[1]}
)


# create a long or "tidy" version of the data & drop invalid values
gfdd_long = gfdd_new_names.melt(
    id_vars=["Country", "Year"], value_vars=indicators, var_name="indicator"
).dropna()
gfdd_long
```


<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
.dataframe tbody tr th {
    vertical-align: top;
}
.dataframe thead th {
    text-align: right;
}
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>Country</th>
      <th>Year</th>
      <th>indicator</th>
      <th>value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>Afghanistan</td>
      <td>1960</td>
      <td>private_credit</td>
      <td>4.603305</td>
    </tr>
    <tr>
      <th>1</th>
      <td>Afghanistan</td>
      <td>1961</td>
      <td>private_credit</td>
      <td>4.572875</td>
    </tr>
    <tr>
      <th>2</th>
      <td>Afghanistan</td>
      <td>1962</td>
      <td>private_credit</td>
      <td>4.658536</td>
    </tr>
    <tr>
      <th>3</th>
      <td>Afghanistan</td>
      <td>1963</td>
      <td>private_credit</td>
      <td>3.417160</td>
    </tr>
    <tr>
      <th>4</th>
      <td>Afghanistan</td>
      <td>1964</td>
      <td>private_credit</td>
      <td>3.480556</td>
    </tr>
    <tr>
      <th>...</th>
      <td>...</td>
      <td>...</td>
      <td>...</td>
      <td>...</td>
    </tr>
    <tr>
      <th>22646</th>
      <td>Zimbabwe</td>
      <td>2001</td>
      <td>bank_assets</td>
      <td>19.494340</td>
    </tr>
    <tr>
      <th>22647</th>
      <td>Zimbabwe</td>
      <td>2002</td>
      <td>bank_assets</td>
      <td>18.411180</td>
    </tr>
    <tr>
      <th>22648</th>
      <td>Zimbabwe</td>
      <td>2003</td>
      <td>bank_assets</td>
      <td>26.838360</td>
    </tr>
    <tr>
      <th>22649</th>
      <td>Zimbabwe</td>
      <td>2004</td>
      <td>bank_assets</td>
      <td>24.202580</td>
    </tr>
    <tr>
      <th>22650</th>
      <td>Zimbabwe</td>
      <td>2005</td>
      <td>bank_assets</td>
      <td>17.669740</td>
    </tr>
  </tbody>
</table>
<p>15175 rows × 4 columns</p>
</div>


Now we can plot this data as a box and whiskers plot, which we'll do using the **lets-plot** package and `geom_boxplot()` option:


```python
(ggplot(gfdd_long, aes(x="indicator", y="value")) + geom_boxplot() 
+ labs( x="Indicator", y="Value"))
```


![png](empirical_project_10_files/empirical_project_10_WT2_1.png)


**Figure 10.2** Box and whisker plot for ‘Private credit by deposit money banks to GDP (%)’ (`private_credit`) and ‘Deposit money banks’ assets to GDP (%)’ (`bank_assets`).


We could repeat the process for each topic and plot all indicators together. However, the range for the `GFDD.AI.01` variable (Bank accounts per 1,000 adults) is far larger than the other variables in this group, so it makes sense to plot this variable separately. We use the same process as before, as shown below.


```python
# for convenience, create a list of the indicators we're interested in:
indicators_big = ["bank_accounts"]


# Rename the variables we'll be plotting
gfdd_new_names = gfdd.rename(columns={"GFDD.AI.01": indicators_big[0]})


# create a long or "tidy" version of the data & drop invalid values
gfdd_long = gfdd_new_names.melt(
    id_vars=["Country", "Year"], value_vars=indicators_big, var_name="indicator"
).dropna()


(ggplot(gfdd_long, aes(x="indicator", y="value")) + geom_boxplot()+ labs( x="Indicator", y="Value"))
```


![png](empirical_project_10_files/empirical_project_10_WT2_2.png)


**Figure 10.3** Box and whisker plot for ‘Bank accounts per 1,000 adults’ (`bank_accounts`).


Now we'd like to do this task for several more cases. A key principle of coding is 'DRY: Don't Repeat Yourself'. We shouldn't have to type this code out multiple times, and it's more likely that something could go wrong if we do that. Instead, we're going to list all of the indicators in one go, and that's what the code below is going to do.


However, we do need a method for this task. To give the variables sensible names, we will use a built-in type of object in Python called a *dictionary*. Dictionaries provide a map from one set of values to another. A simple dictionary might look like this:


```python
fruit_dict = {
    "Jazz": "Apple",
    "Owari": "Satsuma",
    "Seto": "Satsuma",
    "Pink Lady": "Apple",
}
```


This dictionary maps specific varieties of fruit into general types of fruit. A dictionary is very helpful here because we can use it to map the old column names to the new ones in a statement like `gfdd_new_names = gfdd.rename(columns=dict_of_new_names)`. When we're creating our dictionary below, we could do it as in the fruit example above, and there's nothing wrong with that. But for convenience, because we might use them later, we're going to instead create two lists (one for the new names and one for the old) and then bring those lists together to create our dictionary. We've already seen *list comprehensions* and the *zip* function; below we bring these ideas together to form a *dictionary comprehension* using this syntax:


```python
dict_of_new_names = {k: w for k, w in zip(old_names, indicators)}
```


```python
# for convenience, create a list of the indicators we're interested in:
indicators = [
    "private_credit",
    "bank_assets",
    "bank_accounts",
    "bank_branches",
    "firms_credit",
    "small_firms_credit",
    "risk_weighted_assets",
]


# dictionary mapping old names to new names
old_names = [
    "GFDD.DI.01",
    "GFDD.DI.02",
    "GFDD.AI.01",
    "GFDD.AI.02",
    "GFDD.AI.03",
    "GFDD.AI.04",
    "GFDD.SI.05",
]
dict_of_new_names = {k: w for k, w in zip(old_names, indicators)}


# Rename the variables we'll be plotting
gfdd_new_names = gfdd.rename(columns=dict_of_new_names)


# create a long or "tidy" version of the data & drop invalid values
gfdd_long = gfdd_new_names.melt(
    id_vars=["Country", "Year"], value_vars=indicators, var_name="indicator"
).dropna()
```


```python
# put all of our indicators of interest into a box plot
(ggplot(gfdd_long, aes(x="indicator", y="value")) + geom_boxplot() + ylim(0, 1e3) + labs( x="Indicator", y="Value"))
```


![png](empirical_project_10_files/empirical_project_10_WT2_3.png)


**Figure 10.4** Box and whisker plot for our indicators of interest.


You can repeat the process for the indicators on bank stability by copying the above code and adding indicator variable names accordingly.




### *Python walk-through 10.3* Tabulating and visualizing time trends


In this walk-through we will use the indicators for ‘Deposit money banks’ assets to GDP (%)’ and ‘Bank accounts per 1,000 adults’ as examples (`bank_assets` and `bank_accounts` respectively).


Obtaining the average indicator value for each year and region is straightforward using the `group_by` and `agg` functions, but again we have to select the relevant years (using `.query`) and remove any observations that have a missing value for the indicator being analysed (using `dropna`). We save the final output as `deposit_region`.


```python
deposit_region = (
    gfdd.rename(columns=dict_of_new_names)
    .query("Year > 1999 & Year < 2015")
    .dropna(subset=["bank_assets"])
    .groupby(["Year", "Region"])["bank_assets"]
    .agg(["mean", "count"])
)
deposit_region
```


<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
.dataframe tbody tr th {
    vertical-align: top;
}
.dataframe thead th {
    text-align: right;
}
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th></th>
      <th>mean</th>
      <th>count</th>
    </tr>
    <tr>
      <th>Year</th>
      <th>Region</th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th rowspan="5" valign="top">2000</th>
      <th>East Asia &amp; Pacific</th>
      <td>67.557537</td>
      <td>25</td>
    </tr>
    <tr>
      <th>Europe &amp; Central Asia</th>
      <td>58.608195</td>
      <td>45</td>
    </tr>
    <tr>
      <th>Latin America &amp; Caribbean</th>
      <td>46.928321</td>
      <td>33</td>
    </tr>
    <tr>
      <th>Middle East &amp; North Africa</th>
      <td>60.920046</td>
      <td>17</td>
    </tr>
    <tr>
      <th>North America</th>
      <td>68.464945</td>
      <td>2</td>
    </tr>
    <tr>
      <th>...</th>
      <th>...</th>
      <td>...</td>
      <td>...</td>
    </tr>
    <tr>
      <th rowspan="5" valign="top">2014</th>
      <th>Latin America &amp; Caribbean</th>
      <td>57.331511</td>
      <td>31</td>
    </tr>
    <tr>
      <th>Middle East &amp; North Africa</th>
      <td>75.129953</td>
      <td>18</td>
    </tr>
    <tr>
      <th>North America</th>
      <td>60.282820</td>
      <td>1</td>
    </tr>
    <tr>
      <th>South Asia</th>
      <td>45.498802</td>
      <td>8</td>
    </tr>
    <tr>
      <th>Sub-Saharan Africa</th>
      <td>29.011847</td>
      <td>38</td>
    </tr>
  </tbody>
</table>
<p>105 rows × 2 columns</p>
</div>


At this stage, the summary data is stored in long format. This format is useful for plotting the data, but to produce the required table (with `Region` as the column variable and `Year` as the row variable), we need to reshape the data into wide format. While we previously used `melt` to move from wide to long, we can use the `pivot` function to achieve the opposite and transform the data from long to wide.


There is a shortcut to `pivot` though: if we wish to move only one variable from a row to a column (and it is part of the index), we can use the `.unstack` method instead:


```python
deposit_region.unstack()
```


<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
.dataframe tbody tr th {
    vertical-align: top;
}
.dataframe thead tr th {
    text-align: left;
}
.dataframe thead tr:last-of-type th {
    text-align: right;
}
</style>
<table border="1" class="dataframe">
  <thead>
    <tr>
      <th></th>
      <th colspan="7" halign="left">mean</th>
      <th colspan="7" halign="left">count</th>
    </tr>
    <tr>
      <th>Region</th>
      <th>East Asia &amp; Pacific</th>
      <th>Europe &amp; Central Asia</th>
      <th>Latin America &amp; Caribbean</th>
      <th>Middle East &amp; North Africa</th>
      <th>North America</th>
      <th>South Asia</th>
      <th>Sub-Saharan Africa</th>
      <th>East Asia &amp; Pacific</th>
      <th>Europe &amp; Central Asia</th>
      <th>Latin America &amp; Caribbean</th>
      <th>Middle East &amp; North Africa</th>
      <th>North America</th>
      <th>South Asia</th>
      <th>Sub-Saharan Africa</th>
    </tr>
    <tr>
      <th>Year</th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>2000</th>
      <td>67.557537</td>
      <td>58.608195</td>
      <td>46.928321</td>
      <td>60.920046</td>
      <td>68.464945</td>
      <td>28.117836</td>
      <td>20.643030</td>
      <td>25</td>
      <td>45</td>
      <td>33</td>
      <td>17</td>
      <td>2</td>
      <td>7</td>
      <td>43</td>
    </tr>
    <tr>
      <th>2001</th>
      <td>67.245847</td>
      <td>58.096271</td>
      <td>48.799121</td>
      <td>62.508313</td>
      <td>83.097380</td>
      <td>29.391711</td>
      <td>20.530323</td>
      <td>25</td>
      <td>46</td>
      <td>33</td>
      <td>18</td>
      <td>2</td>
      <td>7</td>
      <td>44</td>
    </tr>
    <tr>
      <th>2002</th>
      <td>62.317274</td>
      <td>58.667192</td>
      <td>49.103766</td>
      <td>61.924345</td>
      <td>94.569720</td>
      <td>32.060113</td>
      <td>20.500727</td>
      <td>26</td>
      <td>47</td>
      <td>33</td>
      <td>19</td>
      <td>2</td>
      <td>7</td>
      <td>45</td>
    </tr>
    <tr>
      <th>2003</th>
      <td>61.236189</td>
      <td>60.465442</td>
      <td>48.253112</td>
      <td>59.386665</td>
      <td>92.078475</td>
      <td>33.148744</td>
      <td>21.425730</td>
      <td>26</td>
      <td>47</td>
      <td>33</td>
      <td>19</td>
      <td>2</td>
      <td>7</td>
      <td>45</td>
    </tr>
    <tr>
      <th>2004</th>
      <td>60.621324</td>
      <td>62.510551</td>
      <td>46.690875</td>
      <td>57.018716</td>
      <td>91.043525</td>
      <td>36.022256</td>
      <td>20.986100</td>
      <td>26</td>
      <td>47</td>
      <td>33</td>
      <td>20</td>
      <td>2</td>
      <td>7</td>
      <td>45</td>
    </tr>
    <tr>
      <th>2005</th>
      <td>62.691213</td>
      <td>67.640136</td>
      <td>47.277459</td>
      <td>56.144002</td>
      <td>94.113530</td>
      <td>38.773546</td>
      <td>21.480451</td>
      <td>25</td>
      <td>46</td>
      <td>33</td>
      <td>20</td>
      <td>2</td>
      <td>7</td>
      <td>46</td>
    </tr>
    <tr>
      <th>2006</th>
      <td>63.319616</td>
      <td>73.501307</td>
      <td>47.173601</td>
      <td>56.313520</td>
      <td>100.841875</td>
      <td>34.776328</td>
      <td>22.123007</td>
      <td>25</td>
      <td>47</td>
      <td>33</td>
      <td>20</td>
      <td>2</td>
      <td>8</td>
      <td>44</td>
    </tr>
    <tr>
      <th>2007</th>
      <td>64.103156</td>
      <td>79.041729</td>
      <td>48.883928</td>
      <td>55.711586</td>
      <td>101.979305</td>
      <td>37.704743</td>
      <td>22.778664</td>
      <td>25</td>
      <td>46</td>
      <td>33</td>
      <td>21</td>
      <td>2</td>
      <td>8</td>
      <td>44</td>
    </tr>
    <tr>
      <th>2008</th>
      <td>68.479393</td>
      <td>85.709957</td>
      <td>51.875900</td>
      <td>57.938661</td>
      <td>104.458085</td>
      <td>41.531408</td>
      <td>24.382722</td>
      <td>25</td>
      <td>46</td>
      <td>33</td>
      <td>21</td>
      <td>2</td>
      <td>8</td>
      <td>44</td>
    </tr>
    <tr>
      <th>2009</th>
      <td>74.951694</td>
      <td>92.899973</td>
      <td>55.291540</td>
      <td>65.274277</td>
      <td>66.734920</td>
      <td>43.880175</td>
      <td>26.318474</td>
      <td>25</td>
      <td>44</td>
      <td>33</td>
      <td>21</td>
      <td>1</td>
      <td>8</td>
      <td>42</td>
    </tr>
    <tr>
      <th>2010</th>
      <td>75.563454</td>
      <td>91.179560</td>
      <td>54.295021</td>
      <td>64.119124</td>
      <td>60.497590</td>
      <td>45.363690</td>
      <td>26.154682</td>
      <td>25</td>
      <td>46</td>
      <td>32</td>
      <td>21</td>
      <td>1</td>
      <td>8</td>
      <td>43</td>
    </tr>
    <tr>
      <th>2011</th>
      <td>75.972785</td>
      <td>90.080754</td>
      <td>54.386018</td>
      <td>65.473409</td>
      <td>59.338070</td>
      <td>45.629445</td>
      <td>26.944692</td>
      <td>23</td>
      <td>46</td>
      <td>32</td>
      <td>20</td>
      <td>1</td>
      <td>8</td>
      <td>43</td>
    </tr>
    <tr>
      <th>2012</th>
      <td>75.109313</td>
      <td>89.882284</td>
      <td>55.865148</td>
      <td>65.359516</td>
      <td>58.287330</td>
      <td>45.792968</td>
      <td>27.447431</td>
      <td>24</td>
      <td>46</td>
      <td>32</td>
      <td>20</td>
      <td>1</td>
      <td>8</td>
      <td>41</td>
    </tr>
    <tr>
      <th>2013</th>
      <td>79.249328</td>
      <td>88.833299</td>
      <td>56.437788</td>
      <td>66.467436</td>
      <td>58.079990</td>
      <td>46.435363</td>
      <td>28.392471</td>
      <td>24</td>
      <td>46</td>
      <td>32</td>
      <td>20</td>
      <td>1</td>
      <td>8</td>
      <td>40</td>
    </tr>
    <tr>
      <th>2014</th>
      <td>86.657043</td>
      <td>87.061403</td>
      <td>57.331511</td>
      <td>75.129953</td>
      <td>60.282820</td>
      <td>45.498802</td>
      <td>29.011847</td>
      <td>23</td>
      <td>46</td>
      <td>31</td>
      <td>18</td>
      <td>1</td>
      <td>8</td>
      <td>38</td>
    </tr>
  </tbody>
</table>
</div>


Note how we get two sub-tables: one for mean, and one for count.


At this point you could just print or view the data, but using one of many **pandas** export functions will produce output that is visually easier to read and can be copied-and-pasted into your analysis document. Here are some examples with just the first few rows and just the first few columns:


```python
# to markdown, the popular text format
print(deposit_region.iloc[:5, :3].to_markdown())
```


```
|                                      |    mean |   count |
|:-------------------------------------|--------:|--------:|
| (2000, 'East Asia & Pacific')        | 67.5575 |      25 |
| (2000, 'Europe & Central Asia')      | 58.6082 |      45 |
| (2000, 'Latin America & Caribbean')  | 46.9283 |      33 |
| (2000, 'Middle East & North Africa') | 60.92   |      17 |
| (2000, 'North America')              | 68.4649 |       2 |
```


```python
# to LaTeX, for writing academic papers
print(deposit_region.iloc[:5, :3].style.to_latex())
```


```
\begin{tabular}{llrr}
 &  & mean & count \\
Year & Region &  &  \\
\multirow[c]{5}{*}{2000} & East Asia & Pacific & 67.557537 & 25 \\
 & Europe & Central Asia & 58.608195 & 45 \\
 & Latin America & Caribbean & 46.928321 & 33 \\
 & Middle East & North Africa & 60.920046 & 17 \\
 & North America & 68.464945 & 2 \\
\end{tabular}
```


```python
# to html, for the web
print(deposit_region.iloc[:5, :3].to_html())
```


```
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th></th>
      <th>mean</th>
      <th>count</th>
    </tr>
    <tr>
      <th>Year</th>
      <th>Region</th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th rowspan="5" valign="top">2000</th>
      <th>East Asia &amp; Pacific</th>
      <td>67.557537</td>
      <td>25</td>
    </tr>
    <tr>
      <th>Europe &amp; Central Asia</th>
      <td>58.608195</td>
      <td>45</td>
    </tr>
    <tr>
      <th>Latin America &amp; Caribbean</th>
      <td>46.928321</td>
      <td>33</td>
    </tr>
    <tr>
      <th>Middle East &amp; North Africa</th>
      <td>60.920046</td>
      <td>17</td>
    </tr>
    <tr>
      <th>North America</th>
      <td>68.464945</td>
      <td>2</td>
    </tr>
  </tbody>
</table>
```


We can use **lets-plot** to plot a line chart using the long format data (`deposit_region`), with year on the horizontal axis. We specify `color = "Region"` so each region has a different line colour.


```python
(
    ggplot(deposit_region.reset_index(), aes(x="Year", y="mean", color="Region"))
    + geom_line(size=2)
    + labs(
        y="Mean deposit, % of GDP",
        title="Deposit money banks' assets to GDP (%)",
        subtitle="2000-2014, by region",
    )
)
```


![png](empirical_project_10_files/empirical_project_10_WT3_1.png)


**Figure 10.5** Line chart of ‘Deposit money banks’ assets to GDP (%)’, 2000–2014, by region.


The process can be repeated for income group rather than region.


```python
deposit_income = (
    gfdd_new_names.query("Year > 1999")
    .dropna(subset=["bank_assets"])
    .groupby(["Year", "Income Group"])["bank_assets"]
    .agg(["mean", "count"])
)
```


```python
(
    ggplot(deposit_income.reset_index(), aes(x="Year", y="mean", color="Income Group"))
    + geom_line(size=2)
    + labs(
        y="Mean deposit, % of GDP",
        title="Deposit money banks' assets to GDP (%)",
        subtitle="2000-2014, by income group",
    )
)
```


![png](empirical_project_10_files/empirical_project_10_WT3_2.png)


**Figure 10.6** Line chart of ‘Deposit money banks’ assets to GDP (%)’, 2000–2014, by income group.


You can repeat the process for the indicator 'Bank accounts per 1,000 adults' by replacing the variable name `bank_assets` with `bank_accounts` in the above code, again by region and then by income group.




### *Python walk-through 10.4* Creating weighted averages


As we only require the weighted averages for the years 2004–2014, we will create a new dataframe (called `weighted_gfdd`) to save our results in. The weights are required for each country within each region for each year, but only if there is a value for the `GFDD.AI.01` (`bank_accounts`) indicator. So we:


- Filter results by years of interest using `.query`.
- Select only columns of interest using `.loc`.
- Drop any invalid entries for `bank_accounts` using `.dropna`.


With our new dataframe, we group by year and then region (using `.groupby`) and then generate the weight for each country by dividing the population of each country by the sum of populations of all countries within a region (and year). To return the results in the same shape (index) as the data we began with, we use the `.transform` method. Remember:


- Use `.agg` when using a `groupby`, but you want your groups to become the new index (here, this would give a year-region index).
- Use `.transform` when using a `groupby`, but you want to retain your original index (here, numbered entries).
- Use `.apply` when using a `groupby`, but you want to perform operations that will leave neither the original index nor an index of groups.


```python
gfdd_weighted = (
    gfdd_new_names.query("Year > 2003 & Year < 2015")
    .loc[:, ["Year", "Country", "Region", "bank_accounts", "SP.POP.TOTL"]]
    .dropna(subset=["bank_accounts"])
)


gfdd_weighted["weight"] = gfdd_weighted.groupby(["Year", "Region"])[
    "SP.POP.TOTL"
].transform(lambda x: x / x.sum())


gfdd_weighted
```


<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
.dataframe tbody tr th {
    vertical-align: top;
}
.dataframe thead th {
    text-align: right;
}
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>Year</th>
      <th>Country</th>
      <th>Region</th>
      <th>bank_accounts</th>
      <th>SP.POP.TOTL</th>
      <th>weight</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>48</th>
      <td>2008</td>
      <td>Afghanistan</td>
      <td>South Asia</td>
      <td>38.021832</td>
      <td>27000000.0</td>
      <td>0.017563</td>
    </tr>
    <tr>
      <th>49</th>
      <td>2009</td>
      <td>Afghanistan</td>
      <td>South Asia</td>
      <td>90.909562</td>
      <td>27000000.0</td>
      <td>0.077729</td>
    </tr>
    <tr>
      <th>50</th>
      <td>2010</td>
      <td>Afghanistan</td>
      <td>South Asia</td>
      <td>109.722130</td>
      <td>28000000.0</td>
      <td>0.074594</td>
    </tr>
    <tr>
      <th>51</th>
      <td>2011</td>
      <td>Afghanistan</td>
      <td>South Asia</td>
      <td>145.330790</td>
      <td>29000000.0</td>
      <td>0.083005</td>
    </tr>
    <tr>
      <th>52</th>
      <td>2012</td>
      <td>Afghanistan</td>
      <td>South Asia</td>
      <td>174.633520</td>
      <td>30000000.0</td>
      <td>0.080997</td>
    </tr>
    <tr>
      <th>...</th>
      <td>...</td>
      <td>...</td>
      <td>...</td>
      <td>...</td>
      <td>...</td>
      <td>...</td>
    </tr>
    <tr>
      <th>11325</th>
      <td>2010</td>
      <td>Zimbabwe</td>
      <td>Sub-Saharan Africa</td>
      <td>161.508630</td>
      <td>14000000.0</td>
      <td>0.020834</td>
    </tr>
    <tr>
      <th>11326</th>
      <td>2011</td>
      <td>Zimbabwe</td>
      <td>Sub-Saharan Africa</td>
      <td>79.569343</td>
      <td>14000000.0</td>
      <td>0.020393</td>
    </tr>
    <tr>
      <th>11327</th>
      <td>2012</td>
      <td>Zimbabwe</td>
      <td>Sub-Saharan Africa</td>
      <td>83.730240</td>
      <td>15000000.0</td>
      <td>0.022773</td>
    </tr>
    <tr>
      <th>11328</th>
      <td>2013</td>
      <td>Zimbabwe</td>
      <td>Sub-Saharan Africa</td>
      <td>80.160849</td>
      <td>15000000.0</td>
      <td>0.026333</td>
    </tr>
    <tr>
      <th>11329</th>
      <td>2014</td>
      <td>Zimbabwe</td>
      <td>Sub-Saharan Africa</td>
      <td>87.989885</td>
      <td>15000000.0</td>
      <td>0.033305</td>
    </tr>
  </tbody>
</table>
<p>904 rows × 6 columns</p>
</div>


To check that the weights we have calculated are correct, summing over each year–region combination should produce a value of unity (1). Let's check:


```python
gfdd_weighted.groupby(["Year", "Region"])["weight"].sum()
```


```
Year  Region                    
2004  East Asia & Pacific           1.0
      Europe & Central Asia         1.0
      Latin America & Caribbean     1.0
      Middle East & North Africa    1.0
      South Asia                    1.0
                                   ... 
2014  Europe & Central Asia         1.0
      Latin America & Caribbean     1.0
      Middle East & North Africa    1.0
      South Asia                    1.0
      Sub-Saharan Africa            1.0
Name: weight, Length: 66, dtype: float64
```


The weights we have calculated are correct, so we can proceed to calculate the required weighted indicator values by year and region. We start by creating a new variable with the weighted indicator value (`bank_accounts_weighted`), and then sum up the weighted indicator values by year and region. Recall that when calculating the weighted average, we sum all of the weighted observations rather than taking the mean (which would calculate the simple average instead).


```python
(
    gfdd_weighted.assign(
        bank_accounts_weighted=lambda x: x["bank_accounts"] * x["weight"]
    )
    .groupby(["Year", "Region"])
    .sum()
    .round(2)["bank_accounts_weighted"]
)
```


```
Year  Region                    
2004  East Asia & Pacific            253.86
      Europe & Central Asia          637.40
      Latin America & Caribbean      504.48
      Middle East & North Africa     301.03
      South Asia                     529.82
                                     ...   
2014  Europe & Central Asia         1059.82
      Latin America & Caribbean      780.49
      Middle East & North Africa     524.85
      South Asia                     411.35
      Sub-Saharan Africa             350.80
Name: bank_accounts_weighted, Length: 66, dtype: float64
```


You can change this table by unstacking it, or just export it in this format.




### *Python walk-through 10.5* Dealing with extreme values


In this example we use ‘Bank accounts per 1,000 adults’ (`bank_accounts`). The 5th and 95th percentiles can be obtained using the `quantile` function. We save the output into a dataframe (`q_5_95`) so we can refer to these values in later calculations.


```python
q_5_95 = (
    gfdd_new_names.query("Year == 2010")
    .dropna(subset=["bank_accounts"])["bank_accounts"]
    .quantile([0.05, 0.95])
)
q_5_95
```


```
0.05      27.593772
0.95    1604.688000
Name: bank_accounts, dtype: float64
```


We can compare the value of the indicator with these upper and lower bounds using the `np.where` function from numerical library **numpy**. `np.where` has the following syntax:


```text
np.where(condition, value if condition is true, value if condition is false)
```


One convenient feature of `np.where` is that instead of passing it single values in either of its three arguments, we can pass vectors to all of its arguments, creating a vector-valued return column.


In the code below, we make use of `np.where` first to replace all values below the 5th percentile with the value for the 5th percentile, and then to replace all values above the 95th percentile with the value for the 95th percentile.


Two points to be aware of before running the code: 1) the index values for retrieving data from `q_5_95` are of integer type (as opposed to strings); 2) because we refer to "`bank_accounts`" multiple times, we need to define it first in a separate step (or, alternatively use *lambda expressions*).


```python
gfdd_2010 = gfdd_new_names.query("Year == 2010").dropna(subset=["bank_accounts"])


bank_2010 = gfdd_2010.assign(
    bank_accounts=np.where(
        gfdd_2010["bank_accounts"] < q_5_95[0.05],
        q_5_95[0.05],
        np.where(
            gfdd_2010["bank_accounts"] > q_5_95[0.95],
            q_5_95[0.95],
            gfdd_2010["bank_accounts"],
        ),
    )
)
```


Next we can obtain our summary statistics and print out the ‘Winsorized’ averages (use `gfdd_2010` to see the original averages).


```python
bank_2010.groupby("Income Group").agg(avg_2010=("bank_accounts", "mean")).round(2)
```


<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
.dataframe tbody tr th {
    vertical-align: top;
}
.dataframe thead th {
    text-align: right;
}
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>avg_2010</th>
    </tr>
    <tr>
      <th>Income group</th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>High income: OECD</th>
      <td>1356.47</td>
    </tr>
    <tr>
      <th>High income: nonOECD</th>
      <td>916.90</td>
    </tr>
    <tr>
      <th>Low income</th>
      <td>123.06</td>
    </tr>
    <tr>
      <th>Lower middle income</th>
      <td>422.65</td>
    </tr>
    <tr>
      <th>Upper middle income</th>
      <td>635.71</td>
    </tr>
  </tbody>
</table>
</div>




### *Python walk-through 10.6* Calculating confidence intervals


In Python walk-throughs 3.6 and 8.10 we used the `ttest` function from the **pingouin** package to obtain differences in means and confidence intervals (CIs) for two groups of data. Here we need to obtain these statistics for the `GFDD.SI.05` indicator (renamed as `risk_weighted_assets`) between 2007 and 2014 for each region.


As we need to find the confidence intervals for a number of regions, we can use a *vectorised operation* to perform the same calculation for each region in turn. For this operation to work, we need to reshape our data. We want to format the data so that the 2007 and 2014 values of risk weighted assets appear as columns while regions (and countries) are rows. To do this, we're going to use the `.loc` command to select both the values and variables we're interested in. We'll then use `pivot` to re-order the data into the shape we want.


```python
rwa_07_14 = gfdd_new_names.loc[
    gfdd_new_names["Year"].isin([2007, 2014]),
    ["Year", "Region", "Country", "risk_weighted_assets"],
].pivot(index=["Region", "Country"], columns=["Year"], values=["risk_weighted_assets"])


rwa_07_14
```


<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
.dataframe tbody tr th {
    vertical-align: top;
}
.dataframe thead tr th {
    text-align: left;
}
.dataframe thead tr:last-of-type th {
    text-align: right;
}
</style>
<table border="1" class="dataframe">
  <thead>
    <tr>
      <th></th>
      <th></th>
      <th colspan="2" halign="left">risk_weighted_assets</th>
    </tr>
    <tr>
      <th></th>
      <th>Year</th>
      <th>2007</th>
      <th>2014</th>
    </tr>
    <tr>
      <th>Region</th>
      <th>Country</th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th rowspan="5" valign="top">East Asia &amp; Pacific</th>
      <th>Australia</th>
      <td>10.1</td>
      <td>12.4</td>
    </tr>
    <tr>
      <th>Brunei Darussalam</th>
      <td>NaN</td>
      <td>21.0</td>
    </tr>
    <tr>
      <th>Cambodia</th>
      <td>NaN</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>China</th>
      <td>8.4</td>
      <td>13.2</td>
    </tr>
    <tr>
      <th>Fiji</th>
      <td>NaN</td>
      <td>14.7</td>
    </tr>
    <tr>
      <th>...</th>
      <th>...</th>
      <td>...</td>
      <td>...</td>
    </tr>
    <tr>
      <th rowspan="5" valign="top">Sub-Saharan Africa</th>
      <th>Tanzania</th>
      <td>NaN</td>
      <td>17.0</td>
    </tr>
    <tr>
      <th>Togo</th>
      <td>NaN</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Uganda</th>
      <td>19.3</td>
      <td>22.2</td>
    </tr>
    <tr>
      <th>Zambia</th>
      <td>NaN</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Zimbabwe</th>
      <td>NaN</td>
      <td>NaN</td>
    </tr>
  </tbody>
</table>
<p>206 rows × 2 columns</p>
</div>


Now we have the correct shape, but we've also created quite a complex structure, especially of our index. To access columns in a hierarchical or multi-level index, use a ‘tuple’ (tuples have curvy brackets). You can see the precise name of the columns by running `.columns` like this:


```python
rwa_07_14.columns
```


```
MultiIndex([('risk_weighted_assets', 2007),
            ('risk_weighted_assets', 2014)],
           names=[None, 'Year'])
```


Now, we want to conduct a t-test for each region. We'll use the **pingouin** package for the t-test, `groupby` by region, and use the `apply` function, which allows us to run functions that combine different columns.


```python
rwa_07_14_ttest = rwa_07_14.groupby("Region").apply(
    lambda row: pg.ttest(
        row[("risk_weighted_assets", 2007)], row[("risk_weighted_assets", 2014)]
    )
)
rwa_07_14_ttest
```


<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
.dataframe tbody tr th {
    vertical-align: top;
}
.dataframe thead th {
    text-align: right;
}
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th></th>
      <th>T</th>
      <th>dof</th>
      <th>alternative</th>
      <th>p-val</th>
      <th>CI95%</th>
      <th>cohen-d</th>
      <th>BF10</th>
      <th>power</th>
    </tr>
    <tr>
      <th>Region</th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>East Asia &amp; Pacific</th>
      <th>T-test</th>
      <td>-0.996595</td>
      <td>21.376682</td>
      <td>two-sided</td>
      <td>0.330109</td>
      <td>[-5.75, 2.02]</td>
      <td>0.394260</td>
      <td>0.519</td>
      <td>0.165007</td>
    </tr>
    <tr>
      <th>Europe &amp; Central Asia</th>
      <th>T-test</th>
      <td>-2.697072</td>
      <td>76.344458</td>
      <td>two-sided</td>
      <td>0.008604</td>
      <td>[-4.75, -0.71]</td>
      <td>0.574108</td>
      <td>5.049</td>
      <td>0.763601</td>
    </tr>
    <tr>
      <th>Latin America &amp; Caribbean</th>
      <th>T-test</th>
      <td>-0.481507</td>
      <td>32.000000</td>
      <td>two-sided</td>
      <td>0.633433</td>
      <td>[-1.85, 1.14]</td>
      <td>0.165155</td>
      <td>0.36</td>
      <td>0.075360</td>
    </tr>
    <tr>
      <th>Middle East &amp; North Africa</th>
      <th>T-test</th>
      <td>-0.040157</td>
      <td>15.165713</td>
      <td>two-sided</td>
      <td>0.968492</td>
      <td>[-3.06, 2.95]</td>
      <td>0.017512</td>
      <td>0.373</td>
      <td>0.050194</td>
    </tr>
    <tr>
      <th>North America</th>
      <th>T-test</th>
      <td>-0.497519</td>
      <td>2.000000</td>
      <td>two-sided</td>
      <td>0.668138</td>
      <td>[-4.82, 3.82]</td>
      <td>0.497519</td>
      <td>0.662</td>
      <td>0.061395</td>
    </tr>
    <tr>
      <th>South Asia</th>
      <th>T-test</th>
      <td>-1.918656</td>
      <td>4.000000</td>
      <td>two-sided</td>
      <td>0.127469</td>
      <td>[-7.49, 1.37]</td>
      <td>0.959328</td>
      <td>1.247</td>
      <td>0.155595</td>
    </tr>
    <tr>
      <th>Sub-Saharan Africa</th>
      <th>T-test</th>
      <td>-0.683895</td>
      <td>27.522410</td>
      <td>two-sided</td>
      <td>0.499760</td>
      <td>[-5.17, 2.58]</td>
      <td>0.240930</td>
      <td>0.396</td>
      <td>0.103905</td>
    </tr>
  </tbody>
</table>
</div>


Let's expand the confidence interval into two separate columns. (It's one column with two entries here). We'll also add in some other statistics based on the t-test confidence intervals, the mean, and the width. Note that the mean is just the `.mean` across each row (`axis=1`), and the difference can be computed using `.diff` and then keeping the one valid entry by using `dropna`.


To assemble all of this information, we're going to *concatenate* multiple dataframes using `pd.concat`. The syntax of this command is `pd.concat([list of dataframes], axis=<axis you want to join dataframes together by)`. In the code below, the two dataframes in our list are the original `rwa_07_14_ttest` and a new, second dataframe that we create that has the confidence intervals as its values and two columns, ‘upper’ and ‘lower’.


Further below, when we wish to add in means and widths, we can do this by just declaring a new column (for example, by writing `rwa_07_14_ttest["mean"] = ...`) and then applying a function to the two columns of interest.


```python
rwa_07_14_ttest = pd.concat(
    [
        rwa_07_14_ttest,
        pd.DataFrame(
            rwa_07_14_ttest["CI95%"].tolist(),
            columns=["lower", "upper"],
            index=rwa_07_14_ttest.index,
        ),
    ],
    axis=1,
)
rwa_07_14_ttest["mean"] = rwa_07_14_ttest[["lower", "upper"]].mean(axis=1)
rwa_07_14_ttest["width"] = (
    rwa_07_14_ttest[["lower", "upper"]].diff(axis=1).dropna(axis=1) / 2
)
rwa_07_14_ttest
```


<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
.dataframe tbody tr th {
    vertical-align: top;
}
.dataframe thead th {
    text-align: right;
}
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th></th>
      <th>T</th>
      <th>dof</th>
      <th>alternative</th>
      <th>p-val</th>
      <th>CI95%</th>
      <th>cohen-d</th>
      <th>BF10</th>
      <th>power</th>
      <th>lower</th>
      <th>upper</th>
      <th>mean</th>
      <th>width</th>
    </tr>
    <tr>
      <th>Region</th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>East Asia &amp; Pacific</th>
      <th>T-test</th>
      <td>-0.996595</td>
      <td>21.376682</td>
      <td>two-sided</td>
      <td>0.330109</td>
      <td>[-5.75, 2.02]</td>
      <td>0.394260</td>
      <td>0.519</td>
      <td>0.165007</td>
      <td>-5.75</td>
      <td>2.02</td>
      <td>-1.865</td>
      <td>3.885</td>
    </tr>
    <tr>
      <th>Europe &amp; Central Asia</th>
      <th>T-test</th>
      <td>-2.697072</td>
      <td>76.344458</td>
      <td>two-sided</td>
      <td>0.008604</td>
      <td>[-4.75, -0.71]</td>
      <td>0.574108</td>
      <td>5.049</td>
      <td>0.763601</td>
      <td>-4.75</td>
      <td>-0.71</td>
      <td>-2.730</td>
      <td>2.020</td>
    </tr>
    <tr>
      <th>Latin America &amp; Caribbean</th>
      <th>T-test</th>
      <td>-0.481507</td>
      <td>32.000000</td>
      <td>two-sided</td>
      <td>0.633433</td>
      <td>[-1.85, 1.14]</td>
      <td>0.165155</td>
      <td>0.36</td>
      <td>0.075360</td>
      <td>-1.85</td>
      <td>1.14</td>
      <td>-0.355</td>
      <td>1.495</td>
    </tr>
    <tr>
      <th>Middle East &amp; North Africa</th>
      <th>T-test</th>
      <td>-0.040157</td>
      <td>15.165713</td>
      <td>two-sided</td>
      <td>0.968492</td>
      <td>[-3.06, 2.95]</td>
      <td>0.017512</td>
      <td>0.373</td>
      <td>0.050194</td>
      <td>-3.06</td>
      <td>2.95</td>
      <td>-0.055</td>
      <td>3.005</td>
    </tr>
    <tr>
      <th>North America</th>
      <th>T-test</th>
      <td>-0.497519</td>
      <td>2.000000</td>
      <td>two-sided</td>
      <td>0.668138</td>
      <td>[-4.82, 3.82]</td>
      <td>0.497519</td>
      <td>0.662</td>
      <td>0.061395</td>
      <td>-4.82</td>
      <td>3.82</td>
      <td>-0.500</td>
      <td>4.320</td>
    </tr>
    <tr>
      <th>South Asia</th>
      <th>T-test</th>
      <td>-1.918656</td>
      <td>4.000000</td>
      <td>two-sided</td>
      <td>0.127469</td>
      <td>[-7.49, 1.37]</td>
      <td>0.959328</td>
      <td>1.247</td>
      <td>0.155595</td>
      <td>-7.49</td>
      <td>1.37</td>
      <td>-3.060</td>
      <td>4.430</td>
    </tr>
    <tr>
      <th>Sub-Saharan Africa</th>
      <th>T-test</th>
      <td>-0.683895</td>
      <td>27.522410</td>
      <td>two-sided</td>
      <td>0.499760</td>
      <td>[-5.17, 2.58]</td>
      <td>0.240930</td>
      <td>0.396</td>
      <td>0.103905</td>
      <td>-5.17</td>
      <td>2.58</td>
      <td>-1.295</td>
      <td>3.875</td>
    </tr>
  </tbody>
</table>
</div>


The same process can be repeated for income groups and for the indicator "GFDD.SI.01" (Bank Z-score).


### *Python walk-through 10.7* Plotting column charts with error bars


Again we use the `GFDD.SI.05` indicator (`risk_weighted_assets`) for `Region` as an example. You can repeat the following steps by region and for the `risk_weighted_assets` variable by changing the variable name(s) in Python walk-through 10.6 accordingly, then running the code below.


```python
rwa_07_14_ttest.reset_index()
```


<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
.dataframe tbody tr th {
    vertical-align: top;
}
.dataframe thead th {
    text-align: right;
}
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>Region</th>
      <th>level_1</th>
      <th>T</th>
      <th>dof</th>
      <th>alternative</th>
      <th>p-val</th>
      <th>CI95%</th>
      <th>cohen-d</th>
      <th>BF10</th>
      <th>power</th>
      <th>lower</th>
      <th>upper</th>
      <th>mean</th>
      <th>width</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>East Asia &amp; Pacific</td>
      <td>T-test</td>
      <td>-0.996595</td>
      <td>21.376682</td>
      <td>two-sided</td>
      <td>0.330109</td>
      <td>[-5.75, 2.02]</td>
      <td>0.394260</td>
      <td>0.519</td>
      <td>0.165007</td>
      <td>-5.75</td>
      <td>2.02</td>
      <td>-1.865</td>
      <td>3.885</td>
    </tr>
    <tr>
      <th>1</th>
      <td>Europe &amp; Central Asia</td>
      <td>T-test</td>
      <td>-2.697072</td>
      <td>76.344458</td>
      <td>two-sided</td>
      <td>0.008604</td>
      <td>[-4.75, -0.71]</td>
      <td>0.574108</td>
      <td>5.049</td>
      <td>0.763601</td>
      <td>-4.75</td>
      <td>-0.71</td>
      <td>-2.730</td>
      <td>2.020</td>
    </tr>
    <tr>
      <th>2</th>
      <td>Latin America &amp; Caribbean</td>
      <td>T-test</td>
      <td>-0.481507</td>
      <td>32.000000</td>
      <td>two-sided</td>
      <td>0.633433</td>
      <td>[-1.85, 1.14]</td>
      <td>0.165155</td>
      <td>0.36</td>
      <td>0.075360</td>
      <td>-1.85</td>
      <td>1.14</td>
      <td>-0.355</td>
      <td>1.495</td>
    </tr>
    <tr>
      <th>3</th>
      <td>Middle East &amp; North Africa</td>
      <td>T-test</td>
      <td>-0.040157</td>
      <td>15.165713</td>
      <td>two-sided</td>
      <td>0.968492</td>
      <td>[-3.06, 2.95]</td>
      <td>0.017512</td>
      <td>0.373</td>
      <td>0.050194</td>
      <td>-3.06</td>
      <td>2.95</td>
      <td>-0.055</td>
      <td>3.005</td>
    </tr>
    <tr>
      <th>4</th>
      <td>North America</td>
      <td>T-test</td>
      <td>-0.497519</td>
      <td>2.000000</td>
      <td>two-sided</td>
      <td>0.668138</td>
      <td>[-4.82, 3.82]</td>
      <td>0.497519</td>
      <td>0.662</td>
      <td>0.061395</td>
      <td>-4.82</td>
      <td>3.82</td>
      <td>-0.500</td>
      <td>4.320</td>
    </tr>
    <tr>
      <th>5</th>
      <td>South Asia</td>
      <td>T-test</td>
      <td>-1.918656</td>
      <td>4.000000</td>
      <td>two-sided</td>
      <td>0.127469</td>
      <td>[-7.49, 1.37]</td>
      <td>0.959328</td>
      <td>1.247</td>
      <td>0.155595</td>
      <td>-7.49</td>
      <td>1.37</td>
      <td>-3.060</td>
      <td>4.430</td>
    </tr>
    <tr>
      <th>6</th>
      <td>Sub-Saharan Africa</td>
      <td>T-test</td>
      <td>-0.683895</td>
      <td>27.522410</td>
      <td>two-sided</td>
      <td>0.499760</td>
      <td>[-5.17, 2.58]</td>
      <td>0.240930</td>
      <td>0.396</td>
      <td>0.103905</td>
      <td>-5.17</td>
      <td>2.58</td>
      <td>-1.295</td>
      <td>3.875</td>
    </tr>
  </tbody>
</table>
</div>


```python
(
    ggplot(rwa_07_14_ttest.reset_index(), aes(x="Region", y="mean", fill="Region"))
    + geom_bar(stat="identity", color="black", alpha=0.6, show_legend=False)
    + geom_errorbar(
        aes(ymin="lower", ymax="upper", color="Region"),
        size=1,
        show_legend=False,
        alpha=0.8,
    )
    + coord_flip()
    + labs(
        title="Differences in risk weighted assets between 2007 and 2014",
        x="Difference", y="Mean"
    )
    + ggsize(700, 600)
)
```


![png](empirical_project_10_files/empirical_project_10_WT7_1.png)


**Figure 10.7** Column chart with error bars for ‘Bank regulatory capital to risk-weighted assets (%)’ (`risk_weighted_assets`).


