This project focus on the explanatory data visualization of the exchange rate between the euro (€) and the United State dollar (USD).
In the case of the EURO-USD exchange rate, the higher the rate, the stronger the value of the euro, and vice versa. For example, the euro is weaker at the exchange rate of 0.83 in comparison to 1.60, as one pays 1 EURO to get 0.83 USD instead of 1.60 USD.
We use a dataset of euro daily exchange rates from 1999 to January 2021, which was compiled and published by Daria Chemkaeva on Kaggle. The data was originally from the European Central Bank.
This project aims to illustrate the evolution of the EURO-USD exchange rate from 1999 to 2021 via storytelling data visualisation.
EURO-USD exchange rate bottomed between 2000-2001 (during the 2000s recession) and peaked in 2008 (during the financial crisis).
The Euro currency has been weak from 2015 until January 2021 due to multiple reasons, such as European Central Bank's quantitative easing programme, deepening of European sovereign debt crisis, the likelihood of an increase in US interest rates, 2019 global economic slowdown and Covid-19 recession.
We set up the environment by importing the necessary libraries:
import pandas as pd
import matplotlib.pyplot as plt
# Run the necessary Jupyter magic so that plots are displayed inline
%matplotlib inline
import matplotlib.style as style
pd.plotting.register_matplotlib_converters()
Let's read the euro-daily-hist_1999_2020.csv
dataset into DataFrame and store the dataset into the exchange_rates
variable.
exchange_rates = pd.read_csv('euro-daily-hist_1999_2020.csv')
We study the structure of the dataset by examining the first and last five rows.
exchange_rates.head()
Period\Unit: | [Australian dollar ] | [Bulgarian lev ] | [Brazilian real ] | [Canadian dollar ] | [Swiss franc ] | [Chinese yuan renminbi ] | [Cypriot pound ] | [Czech koruna ] | [Danish krone ] | ... | [Romanian leu ] | [Russian rouble ] | [Swedish krona ] | [Singapore dollar ] | [Slovenian tolar ] | [Slovak koruna ] | [Thai baht ] | [Turkish lira ] | [US dollar ] | [South African rand ] | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2021-01-08 | 1.5758 | 1.9558 | 6.5748 | 1.5543 | 1.0827 | 7.9184 | NaN | 26.163 | 7.4369 | ... | 4.8708 | 90.8000 | 10.0510 | 1.6228 | NaN | NaN | 36.8480 | 9.0146 | 1.2250 | 18.7212 |
1 | 2021-01-07 | 1.5836 | 1.9558 | 6.5172 | 1.5601 | 1.0833 | 7.9392 | NaN | 26.147 | 7.4392 | ... | 4.8712 | 91.2000 | 10.0575 | 1.6253 | NaN | NaN | 36.8590 | 8.9987 | 1.2276 | 18.7919 |
2 | 2021-01-06 | 1.5824 | 1.9558 | 6.5119 | 1.5640 | 1.0821 | 7.9653 | NaN | 26.145 | 7.4393 | ... | 4.8720 | 90.8175 | 10.0653 | 1.6246 | NaN | NaN | 36.9210 | 9.0554 | 1.2338 | 18.5123 |
3 | 2021-01-05 | 1.5927 | 1.9558 | 6.5517 | 1.5651 | 1.0803 | 7.9315 | NaN | 26.227 | 7.4387 | ... | 4.8721 | 91.6715 | 10.0570 | 1.6180 | NaN | NaN | 36.7760 | 9.0694 | 1.2271 | 18.4194 |
4 | 2021-01-04 | 1.5928 | 1.9558 | 6.3241 | 1.5621 | 1.0811 | 7.9484 | NaN | 26.141 | 7.4379 | ... | 4.8713 | 90.3420 | 10.0895 | 1.6198 | NaN | NaN | 36.7280 | 9.0579 | 1.2296 | 17.9214 |
5 rows × 41 columns
exchange_rates.tail()
Period\Unit: | [Australian dollar ] | [Bulgarian lev ] | [Brazilian real ] | [Canadian dollar ] | [Swiss franc ] | [Chinese yuan renminbi ] | [Cypriot pound ] | [Czech koruna ] | [Danish krone ] | ... | [Romanian leu ] | [Russian rouble ] | [Swedish krona ] | [Singapore dollar ] | [Slovenian tolar ] | [Slovak koruna ] | [Thai baht ] | [Turkish lira ] | [US dollar ] | [South African rand ] | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
5694 | 1999-01-08 | 1.8406 | NaN | NaN | 1.7643 | 1.6138 | NaN | 0.58187 | 34.938 | 7.4433 | ... | 1.3143 | 27.2075 | 9.1650 | 1.9537 | 188.8400 | 42.560 | 42.5590 | 0.3718 | 1.1659 | 6.7855 |
5695 | 1999-01-07 | 1.8474 | NaN | NaN | 1.7602 | 1.6165 | NaN | 0.58187 | 34.886 | 7.4431 | ... | 1.3092 | 26.9876 | 9.1800 | 1.9436 | 188.8000 | 42.765 | 42.1678 | 0.3701 | 1.1632 | 6.8283 |
5696 | 1999-01-06 | 1.8820 | NaN | NaN | 1.7711 | 1.6116 | NaN | 0.58200 | 34.850 | 7.4452 | ... | 1.3168 | 27.4315 | 9.3050 | 1.9699 | 188.7000 | 42.778 | 42.6949 | 0.3722 | 1.1743 | 6.7307 |
5697 | 1999-01-05 | 1.8944 | NaN | NaN | 1.7965 | 1.6123 | NaN | 0.58230 | 34.917 | 7.4495 | ... | 1.3168 | 26.5876 | 9.4025 | 1.9655 | 188.7750 | 42.848 | 42.5048 | 0.3728 | 1.1790 | 6.7975 |
5698 | 1999-01-04 | 1.9100 | NaN | NaN | 1.8004 | 1.6168 | NaN | 0.58231 | 35.107 | 7.4501 | ... | 1.3111 | 25.2875 | 9.4696 | 1.9554 | 189.0450 | 42.991 | 42.6799 | 0.3723 | 1.1789 | 6.9358 |
5 rows × 41 columns
We further inspect the information by using DataFrame.info()
.
exchange_rates.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 5699 entries, 0 to 5698 Data columns (total 41 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Period\Unit: 5699 non-null object 1 [Australian dollar ] 5699 non-null object 2 [Bulgarian lev ] 5297 non-null object 3 [Brazilian real ] 5431 non-null object 4 [Canadian dollar ] 5699 non-null object 5 [Swiss franc ] 5699 non-null object 6 [Chinese yuan renminbi ] 5431 non-null object 7 [Cypriot pound ] 2346 non-null object 8 [Czech koruna ] 5699 non-null object 9 [Danish krone ] 5699 non-null object 10 [Estonian kroon ] 3130 non-null object 11 [UK pound sterling ] 5699 non-null object 12 [Greek drachma ] 520 non-null object 13 [Hong Kong dollar ] 5699 non-null object 14 [Croatian kuna ] 5431 non-null object 15 [Hungarian forint ] 5699 non-null object 16 [Indonesian rupiah ] 5699 non-null object 17 [Israeli shekel ] 5431 non-null object 18 [Indian rupee ] 5431 non-null object 19 [Iceland krona ] 3292 non-null float64 20 [Japanese yen ] 5699 non-null object 21 [Korean won ] 5699 non-null object 22 [Lithuanian litas ] 4159 non-null object 23 [Latvian lats ] 3904 non-null object 24 [Maltese lira ] 2346 non-null object 25 [Mexican peso ] 5699 non-null object 26 [Malaysian ringgit ] 5699 non-null object 27 [Norwegian krone ] 5699 non-null object 28 [New Zealand dollar ] 5699 non-null object 29 [Philippine peso ] 5699 non-null object 30 [Polish zloty ] 5699 non-null object 31 [Romanian leu ] 5637 non-null float64 32 [Russian rouble ] 5699 non-null object 33 [Swedish krona ] 5699 non-null object 34 [Singapore dollar ] 5699 non-null object 35 [Slovenian tolar ] 2085 non-null object 36 [Slovak koruna ] 2608 non-null object 37 [Thai baht ] 5699 non-null object 38 [Turkish lira ] 5637 non-null float64 39 [US dollar ] 5699 non-null object 40 [South African rand ] 5699 non-null object dtypes: float64(3), object(38) memory usage: 1.8+ MB
There are 5699 rows and 41 columns in this dataset. This dataset is composed of float
(3) and object
(38).
We noticed that there are some null values in this dataset.
exchange_rates.isnull().values.any()
True
We dive further to investigate the total sum of missing values of each column, except the Period\Unit:
column. This is because we already knew from the previous inspection that the Period\Unit:
column does not have any null value and we want to focus on the column with currency.
# Excluding the `Period\Unit:` column
exchange_rates_currency_only = exchange_rates.iloc[:, 1:]
# Get the sum of null values of each column
exchange_rates_currency_only.isnull().sum()
[Australian dollar ] 0 [Bulgarian lev ] 402 [Brazilian real ] 268 [Canadian dollar ] 0 [Swiss franc ] 0 [Chinese yuan renminbi ] 268 [Cypriot pound ] 3353 [Czech koruna ] 0 [Danish krone ] 0 [Estonian kroon ] 2569 [UK pound sterling ] 0 [Greek drachma ] 5179 [Hong Kong dollar ] 0 [Croatian kuna ] 268 [Hungarian forint ] 0 [Indonesian rupiah ] 0 [Israeli shekel ] 268 [Indian rupee ] 268 [Iceland krona ] 2407 [Japanese yen ] 0 [Korean won ] 0 [Lithuanian litas ] 1540 [Latvian lats ] 1795 [Maltese lira ] 3353 [Mexican peso ] 0 [Malaysian ringgit ] 0 [Norwegian krone ] 0 [New Zealand dollar ] 0 [Philippine peso ] 0 [Polish zloty ] 0 [Romanian leu ] 62 [Russian rouble ] 0 [Swedish krona ] 0 [Singapore dollar ] 0 [Slovenian tolar ] 3614 [Slovak koruna ] 3091 [Thai baht ] 0 [Turkish lira ] 62 [US dollar ] 0 [South African rand ] 0 dtype: int64
There are 28,767 null values in total. Our currency of interest — the US dollar — does not have any null value.
exchange_rates_currency_only.isnull().sum().sum()
28767
Let's clean the data before we create data visualizations.
First, we simplify and rename the Period\Unit:
to Time
and [US dollar]
to US_dollar
. Since we focus on the US dollar in this project, we do not rename other currency names.
exchange_rates.rename(columns={'[US dollar ]':'US_dollar',
'Period\\Unit:':'Time'}, inplace=True)
# Display the column names
list(exchange_rates.columns)
['Time', '[Australian dollar ]', '[Bulgarian lev ]', '[Brazilian real ]', '[Canadian dollar ]', '[Swiss franc ]', '[Chinese yuan renminbi ]', '[Cypriot pound ]', '[Czech koruna ]', '[Danish krone ]', '[Estonian kroon ]', '[UK pound sterling ]', '[Greek drachma ]', '[Hong Kong dollar ]', '[Croatian kuna ]', '[Hungarian forint ]', '[Indonesian rupiah ]', '[Israeli shekel ]', '[Indian rupee ]', '[Iceland krona ]', '[Japanese yen ]', '[Korean won ]', '[Lithuanian litas ]', '[Latvian lats ]', '[Maltese lira ]', '[Mexican peso ]', '[Malaysian ringgit ]', '[Norwegian krone ]', '[New Zealand dollar ]', '[Philippine peso ]', '[Polish zloty ]', '[Romanian leu ]', '[Russian rouble ]', '[Swedish krona ]', '[Singapore dollar ]', '[Slovenian tolar ]', '[Slovak koruna ]', '[Thai baht ]', '[Turkish lira ]', 'US_dollar', '[South African rand ]']
Time
Column to datetime
Data Type¶Next, we convert the Time
column to datetime
data type.
exchange_rates['Time'] = pd.to_datetime(exchange_rates['Time'])
print(exchange_rates['Time'].dtype)
datetime64[ns]
Then, we sort the Time
values in ascending order and reset the index.
exchange_rates.sort_values('Time', inplace=True)
exchange_rates.reset_index(drop=True, inplace=True)
exchange_rates.head()
Time | [Australian dollar ] | [Bulgarian lev ] | [Brazilian real ] | [Canadian dollar ] | [Swiss franc ] | [Chinese yuan renminbi ] | [Cypriot pound ] | [Czech koruna ] | [Danish krone ] | ... | [Romanian leu ] | [Russian rouble ] | [Swedish krona ] | [Singapore dollar ] | [Slovenian tolar ] | [Slovak koruna ] | [Thai baht ] | [Turkish lira ] | US_dollar | [South African rand ] | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1999-01-04 | 1.9100 | NaN | NaN | 1.8004 | 1.6168 | NaN | 0.58231 | 35.107 | 7.4501 | ... | 1.3111 | 25.2875 | 9.4696 | 1.9554 | 189.0450 | 42.991 | 42.6799 | 0.3723 | 1.1789 | 6.9358 |
1 | 1999-01-05 | 1.8944 | NaN | NaN | 1.7965 | 1.6123 | NaN | 0.58230 | 34.917 | 7.4495 | ... | 1.3168 | 26.5876 | 9.4025 | 1.9655 | 188.7750 | 42.848 | 42.5048 | 0.3728 | 1.1790 | 6.7975 |
2 | 1999-01-06 | 1.8820 | NaN | NaN | 1.7711 | 1.6116 | NaN | 0.58200 | 34.850 | 7.4452 | ... | 1.3168 | 27.4315 | 9.3050 | 1.9699 | 188.7000 | 42.778 | 42.6949 | 0.3722 | 1.1743 | 6.7307 |
3 | 1999-01-07 | 1.8474 | NaN | NaN | 1.7602 | 1.6165 | NaN | 0.58187 | 34.886 | 7.4431 | ... | 1.3092 | 26.9876 | 9.1800 | 1.9436 | 188.8000 | 42.765 | 42.1678 | 0.3701 | 1.1632 | 6.8283 |
4 | 1999-01-08 | 1.8406 | NaN | NaN | 1.7643 | 1.6138 | NaN | 0.58187 | 34.938 | 7.4433 | ... | 1.3143 | 27.2075 | 9.1650 | 1.9537 | 188.8400 | 42.560 | 42.5590 | 0.3718 | 1.1659 | 6.7855 |
5 rows × 41 columns
exchange_rates.tail()
Time | [Australian dollar ] | [Bulgarian lev ] | [Brazilian real ] | [Canadian dollar ] | [Swiss franc ] | [Chinese yuan renminbi ] | [Cypriot pound ] | [Czech koruna ] | [Danish krone ] | ... | [Romanian leu ] | [Russian rouble ] | [Swedish krona ] | [Singapore dollar ] | [Slovenian tolar ] | [Slovak koruna ] | [Thai baht ] | [Turkish lira ] | US_dollar | [South African rand ] | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
5694 | 2021-01-04 | 1.5928 | 1.9558 | 6.3241 | 1.5621 | 1.0811 | 7.9484 | NaN | 26.141 | 7.4379 | ... | 4.8713 | 90.3420 | 10.0895 | 1.6198 | NaN | NaN | 36.7280 | 9.0579 | 1.2296 | 17.9214 |
5695 | 2021-01-05 | 1.5927 | 1.9558 | 6.5517 | 1.5651 | 1.0803 | 7.9315 | NaN | 26.227 | 7.4387 | ... | 4.8721 | 91.6715 | 10.0570 | 1.6180 | NaN | NaN | 36.7760 | 9.0694 | 1.2271 | 18.4194 |
5696 | 2021-01-06 | 1.5824 | 1.9558 | 6.5119 | 1.5640 | 1.0821 | 7.9653 | NaN | 26.145 | 7.4393 | ... | 4.8720 | 90.8175 | 10.0653 | 1.6246 | NaN | NaN | 36.9210 | 9.0554 | 1.2338 | 18.5123 |
5697 | 2021-01-07 | 1.5836 | 1.9558 | 6.5172 | 1.5601 | 1.0833 | 7.9392 | NaN | 26.147 | 7.4392 | ... | 4.8712 | 91.2000 | 10.0575 | 1.6253 | NaN | NaN | 36.8590 | 8.9987 | 1.2276 | 18.7919 |
5698 | 2021-01-08 | 1.5758 | 1.9558 | 6.5748 | 1.5543 | 1.0827 | 7.9184 | NaN | 26.163 | 7.4369 | ... | 4.8708 | 90.8000 | 10.0510 | 1.6228 | NaN | NaN | 36.8480 | 9.0146 | 1.2250 | 18.7212 |
5 rows × 41 columns
Time
and the US_dollar
Columns¶We isolate the Time
and the US_dollar
columns and assign them to the euro_to_dollar
variable.
euro_to_dollar = exchange_rates[['Time', 'US_dollar']].copy()
euro_to_dollar.head()
Time | US_dollar | |
---|---|---|
0 | 1999-01-04 | 1.1789 |
1 | 1999-01-05 | 1.1790 |
2 | 1999-01-06 | 1.1743 |
3 | 1999-01-07 | 1.1632 |
4 | 1999-01-08 | 1.1659 |
euro_to_dollar.tail()
Time | US_dollar | |
---|---|---|
5694 | 2021-01-04 | 1.2296 |
5695 | 2021-01-05 | 1.2271 |
5696 | 2021-01-06 | 1.2338 |
5697 | 2021-01-07 | 1.2276 |
5698 | 2021-01-08 | 1.2250 |
euro_to_dollar.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 5699 entries, 0 to 5698 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Time 5699 non-null datetime64[ns] 1 US_dollar 5699 non-null object dtypes: datetime64[ns](1), object(1) memory usage: 89.2+ KB
We inspected the values of US_dollar
and found 62 entries of -
, which are not numeric values.
euro_to_dollar['US_dollar'].value_counts()
- 62 1.2276 9 1.1215 8 1.1305 7 1.1797 6 .. 1.2210 1 1.4056 1 0.9683 1 1.4288 1 1.1040 1 Name: US_dollar, Length: 3528, dtype: int64
dash_symbol_before = euro_to_dollar[euro_to_dollar['US_dollar'] == '-']
print('The amount of "-": ', dash_symbol_before.shape[0])
The amount of "-": 62
-
does not makes sense for our data analysis and visualisation. Therefore, we need to remove the rows with -
.
# euro_to_dollar = euro_to_dollar[euro_to_dollar != '-']
euro_to_dollar = euro_to_dollar[euro_to_dollar['US_dollar'] != '-']
dash_symbol_after = euro_to_dollar[euro_to_dollar['US_dollar'] == '-']
print('The amount of "-": ', dash_symbol_after.shape[0])
The amount of "-": 0
Since the original data type of US_dollar
is object
, we convert it to float
for data analysis and visualization.
# Convert the data type of `US_dollar` to `float`
euro_to_dollar['US_dollar'] = euro_to_dollar['US_dollar'].astype(float)
# Examine whether the data type of `US_dollar` has been converted to `float`
euro_to_dollar.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 5637 entries, 0 to 5698 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Time 5637 non-null datetime64[ns] 1 US_dollar 5637 non-null float64 dtypes: datetime64[ns](1), float64(1) memory usage: 132.1 KB
After data cleaning, we visualise the change of the euro-dollar exchange rate over time by using a line plot.
plt.plot(euro_to_dollar['Time'], euro_to_dollar['US_dollar'])
plt.show()
We observe many tiny fluctuations at the line of our line plot, which is due to the daily variation in the exchange rate. Since we are more interested in the long-term trend of the exchange rate, we will use a rolling mean with a rolling window of 30 days to generate the line plot.
To do this, we compute the rolling mean and assign it to a new column named rolling_mean
.
euro_to_dollar['rolling_mean'] = euro_to_dollar['US_dollar'].rolling(30).mean()
euro_to_dollar.head()
Time | US_dollar | rolling_mean | |
---|---|---|---|
0 | 1999-01-04 | 1.1789 | NaN |
1 | 1999-01-05 | 1.1790 | NaN |
2 | 1999-01-06 | 1.1743 | NaN |
3 | 1999-01-07 | 1.1632 | NaN |
4 | 1999-01-08 | 1.1659 | NaN |
euro_to_dollar.tail()
Time | US_dollar | rolling_mean | |
---|---|---|---|
5694 | 2021-01-04 | 1.2296 | 1.211170 |
5695 | 2021-01-05 | 1.2271 | 1.212530 |
5696 | 2021-01-06 | 1.2338 | 1.213987 |
5697 | 2021-01-07 | 1.2276 | 1.215357 |
5698 | 2021-01-08 | 1.2250 | 1.216557 |
Now, let's generate a smoother line plot with a rolling window of 30 days.
plt.plot(euro_to_dollar['Time'], euro_to_dollar['rolling_mean'])
plt.show()
To get the lowest and the highest exchange rate along the evolution of the EURO-USD exchange rate, we do the following:
Time
and assign them to a new column named Year
.df.pivot_table()
to compute the minimum and maximum exchange rate per year.# Isolating years and assign it to a new column named `Year`
euro_to_dollar['Year'] = euro_to_dollar['Time'].dt.year
euro_to_dollar.head()
Time | US_dollar | rolling_mean | Year | |
---|---|---|---|---|
0 | 1999-01-04 | 1.1789 | NaN | 1999 |
1 | 1999-01-05 | 1.1790 | NaN | 1999 |
2 | 1999-01-06 | 1.1743 | NaN | 1999 |
3 | 1999-01-07 | 1.1632 | NaN | 1999 |
4 | 1999-01-08 | 1.1659 | NaN | 1999 |
import numpy as np
# Checking for the minimum exchange rate for each year
min_euro_to_dollar = euro_to_dollar.pivot_table(values='US_dollar', index='Year',
aggfunc=np.min)
min_euro_to_dollar
US_dollar | |
---|---|
Year | |
1999 | 1.0015 |
2000 | 0.8252 |
2001 | 0.8384 |
2002 | 0.8578 |
2003 | 1.0377 |
2004 | 1.1802 |
2005 | 1.1667 |
2006 | 1.1826 |
2007 | 1.2893 |
2008 | 1.2460 |
2009 | 1.2555 |
2010 | 1.1942 |
2011 | 1.2889 |
2012 | 1.2089 |
2013 | 1.2768 |
2014 | 1.2141 |
2015 | 1.0552 |
2016 | 1.0364 |
2017 | 1.0385 |
2018 | 1.1261 |
2019 | 1.0889 |
2020 | 1.0707 |
2021 | 1.2250 |
# Checking for the maximum exchange rate for each year
max_euro_to_dollar = euro_to_dollar.pivot_table(values='US_dollar', index='Year',
aggfunc=np.max)
max_euro_to_dollar
US_dollar | |
---|---|
Year | |
1999 | 1.1790 |
2000 | 1.0388 |
2001 | 0.9545 |
2002 | 1.0487 |
2003 | 1.2630 |
2004 | 1.3633 |
2005 | 1.3507 |
2006 | 1.3331 |
2007 | 1.4874 |
2008 | 1.5990 |
2009 | 1.5120 |
2010 | 1.4563 |
2011 | 1.4882 |
2012 | 1.3454 |
2013 | 1.3814 |
2014 | 1.3953 |
2015 | 1.2043 |
2016 | 1.1569 |
2017 | 1.2060 |
2018 | 1.2493 |
2019 | 1.1535 |
2020 | 1.2281 |
2021 | 1.2338 |
We highlight some events/crises that impacted on the EURO-USD exchange rate from 1999 to 2021 and visualise them on a line plot with ggplot Matplotlib style.
# Isolating the `years` of the early 2000s recession (2000 - 2002)
early2000s_recessions = euro_to_dollar.copy()[(euro_to_dollar['Time'].dt.year >= 2000)
& (euro_to_dollar['Time'].dt.year < 2003)]
# Isolating the `years` of the financial crisis (2007 - 2009)
financial_crisis = euro_to_dollar.copy()[(euro_to_dollar['Time'].dt.year >= 2007)
& (euro_to_dollar['Time'].dt.year < 2010)]
# Isolating the `years` of the European sovereign debt crisis (2015 - 2017)
eu_debt_crisis = euro_to_dollar.copy()[(euro_to_dollar['Time'].dt.year >= 2015)
& (euro_to_dollar['Time'].dt.year < 2018)]
# Isolating the `years` of the 2019 global economic slowdown
# and Covid-19 recession (2019 - 2020)
economic_slowdown_Covid = euro_to_dollar.copy()[(euro_to_dollar['Time'].dt.year >= 2019)
& (euro_to_dollar['Time'].dt.year < 2021)]
# Adding Matplotlib style
style.use('ggplot')
# Creating a line plot
fig, ax = plt.subplots(figsize=(12,6))
ax.plot(euro_to_dollar['Time'], euro_to_dollar['rolling_mean'],
linewidth=1, color='grey')
# Setting the facecolor of the axis to white
ax.set_facecolor(color='#ffffff')
# Changing the color of grid to a lighter color
ax.grid(color='#f0f0f0')
# Hiding ticks
ax.tick_params(bottom=False, left=False)
# Setting the y limit
ax.set_ylim(0.8, 1.9)
# Reducing the y ticks
ax.set_yticks([0.8, 1.0, 1.2, 1.4, 1.6])
# Setting tick label color
ax.tick_params(colors='#9E9E9E', which='both')
# ----------------------------------------------------------------------------
# Highlighting the period of global economy crisis
events = [early2000s_recessions, financial_crisis, eu_debt_crisis, economic_slowdown_Covid]
for e in events:
start_year = e['Time'].min()
end_year = e['Time'].max()
ax.axvspan(xmin=start_year, xmax=end_year, ymin=0, ymax=1, alpha=0.12, color='grey')
# ----------------------------------------------------------------------------
# Highlighting the line of the early 2000s recession (2000 - 2002)
ax.plot(early2000s_recessions['Time'], early2000s_recessions['rolling_mean'],
linewidth=2.5, color='#9450B3')
# Highlighting the line of the financial crisis (2007 - 2009)
ax.plot(financial_crisis['Time'], financial_crisis['rolling_mean'],
linewidth=2.5, color='#9450B3')
# Highlighting the line of the European sovereign debt crisis (2015 - 2017)
ax.plot(eu_debt_crisis['Time'], eu_debt_crisis['rolling_mean'],
linewidth=2.5, color='#9450B3')
# Highlighting the line of the global economic slowdown and Covid-19 recession (2019 - 2020)
ax.plot(economic_slowdown_Covid['Time'], economic_slowdown_Covid['rolling_mean'],
linewidth=2.5, color='#9450B3')
# ----------------------------------------------------------------------------
# Adding crisis descriptions
# Adding the `Early 2000s Recession`
ax.text(x=730280, y=1.82, s='Early 2000s', color='#424242', size=10)
ax.text(x=730280, y=1.77, s='Recession', color='#424242', size=10)
# Adding the `Financial Crisis`
ax.text(x=732930, y=1.82, s='Financial', color='#424242', size=10)
ax.text(x=732930, y=1.77, s='Crisis', color='#424242', size=10)
# Adding the `European Sovereign Debt Crisis`
ax.text(x=735770, y=1.82, s='European', color='#424242', size=10)
ax.text(x=735770, y=1.77, s='Sovereign', color='#424242', size=10)
ax.text(x=735770, y=1.72, s='Debt Crisis', color='#424242', size=10)
# Adding the `2019 Global Economic Slowdown and Covid-19 Recession`
ax.text(x=737100, y=1.82, s='Global', color='#424242', size=10)
ax.text(x=737100, y=1.77, s='Economic', color='#424242', size=10)
ax.text(x=737100, y=1.72, s='Slowdown', color='#424242', size=10)
ax.text(x=737100, y=1.67, s='& Covid-19', color='#424242', size=10)
ax.text(x=737100, y=1.62, s='Recession', color='#424242', size=10)
# ----------------------------------------------------------------------------
# Adding title
ax.text(x=729100, y=2.08, s='EURO-USD Rates are Bottomed Again Since 2015',
size=18, weight='bold')
# Adding subtitle
ax.text(x=729100, y=2.00, s='EURO-USD Exchange Rates between 1999 to 2021',
size=12)
# Adding Signature Bar
ax.text(x=729100, y=0.68, s='©SPWong' + ' '*146 + 'Source: European Central Bank',
color='#ffffff', backgroundcolor='#424242', size=10)
plt.show()
After the introduction of the euro currency in 1999, the EURO-USD exchange rate plummeted to its lowest point during the early 2000s recession. It hit the euro hardest between 2000 and 2001, with an exchange rate of 0.83 in 2000. The value of the euro appreciated rapidly starting from 2002.
During the financial crisis (2007-2009), the EURO-USD exchange rate rose to its record high at 1.60. Nonetheless, it dropped immensely to 1.25 before rising again to its historical second-highest rate, 1.51, at the end of the year 2009.
Between 2015 and 2017, EURO-USD exchange rate plummeted rapidly due to multiple factors, such as the European Central Bank's quantitative easing programme, the deepening of the Greek crisis and the likelihood of an increase in US interest rates (Source: The Guardian). The EURO-USD rate dropped to its lowest at 1.04 in 2016. Since then, the value of the euro has not been able to recover to its good old time, i.e. between 2007 and 2014.
The EURO-USD exchange rate suffered again between 2019 and 2020. The major reasons for the 2019 global economic slowdown were due to Brexit, China-United States trade war and the potential liquidity crisis. The EURO-USD rate hit its lowest point at 1.07 in 2020, during the Covid-19 recession, the worst global economic recession caused by the Covid-19 pandemic.
We are curious to investigate the evolution of EURO-USD exchange rate under the last three US presidents along with the impact of the historical events/crises.
To do this, we need to group the data of each president based on their serving periods. The US presidents serving periods are as below:
bush = euro_to_dollar.copy()[(euro_to_dollar['Time'].dt.year >= 2001)
& (euro_to_dollar['Time'].dt.year < 2009)]
obama = euro_to_dollar.copy()[(euro_to_dollar['Time'].dt.year >= 2009)
& (euro_to_dollar['Time'].dt.year < 2017)]
trump = euro_to_dollar.copy()[(euro_to_dollar['Time'].dt.year >= 2017)
& (euro_to_dollar['Time'].dt.year < 2021)]
We compute the mean of US_dollar
, 1.20, and use it as a guideline for our detailed analysis later.
euro_to_dollar.mean()
<ipython-input-37-e4fdd3b3d438>:1: FutureWarning: DataFrame.mean and DataFrame.median with numeric_only=None will include datetime64 and datetime64tz columns in a future version. euro_to_dollar.mean()
US_dollar 1.199704 rolling_mean 1.199748 Year 2009.506652 dtype: float64
Let's visualize the data by using line charts with ggplot Matplotlib style.
# Adding Matplotlib style
style.use('ggplot')
# Adding a line plot
plt.figure(figsize=(12,8))
ax1 = plt.subplot(2,3,1)
ax2 = plt.subplot(2,3,2)
ax3 = plt.subplot(2,3,3)
ax4 = plt.subplot(2,1,2)
axes = [ax1, ax2, ax3, ax4]
# Adding spacing between subplots
plt.tight_layout(w_pad=2.0, h_pad=7.0)
# Setting the parameters for all plots
for ax in axes:
ax.set_facecolor(color='#ffffff') # Setting the facecolor of the axis to white
ax.grid(color='#f0f0f0') # Changing the color of grid to a lighter color
ax.tick_params(bottom=False, left=False) # Hiding ticks
ax.set_yticks([0.8, 1.0, 1.2, 1.4, 1.6]) # Reducing the y ticks
ax.set_ylim(0.8, 1.6) # Setting the y-axis view limits
ax.tick_params(colors='#9E9E9E', which='both') # Setting tick label color
# ----------------------------------------------------------------------------
# ax1: Bush
ax1.plot(bush['Time'], bush['rolling_mean'], linewidth=2.5, color='blue')
ax1.set_xticklabels(['', '2001', '', '2003', '', '2005', '', '2007', '', '2009'])
# # Converting the start and end years (of the global economy crisis) to datetime
# dt2001 = pd.to_datetime('2001')
# dt2004 = pd.to_datetime('2004')
# dt2007 = pd.to_datetime('2007')
dt2009 = pd.to_datetime('2009')
# # Highlighting the crisis period during Bush time
# ax1.axvspan(xmin=dt2001, xmax=dt2004, ymin=0, ymax=0.986,
# alpha=0.12, color='grey') # year 2001 - 2003
# ax1.axvspan(xmin=dt2007, xmax=dt2009, ymin=0, ymax=0.986,
# alpha=0.12, color='grey') # year 2007 - 2008
# ----------------------------------------------------------------------------
# Highlighting the period of global economy crisis
# events = [early2000s_recessions, financial_crisis, eu_debt_crisis, economic_slowdown_Covid]
ev = [early2000s_recessions, financial_crisis]
for i in ev:
start_year = i['Time'].min()
end_year = i['Time'].max()
ax1.axvspan(xmin=start_year, xmax=end_year, ymin=0, ymax=1, alpha=0.12, color='grey')
# ----------------------------------------------------------------------------
# Adding title
ax1.text(x=731690, y=1.75, s='Bush',size=14, weight='bold', color='blue')
# Adding subtitle
ax1.text(x=731540, y=1.67, s='(2001-2009)', alpha=0.7)
# ----------------------------------------------------------------------------
# ax2: Obama
ax2.plot(obama['Time'], obama['rolling_mean'], linewidth=2.5, color='green')
ax2.set_xticklabels(['', '2009', '', '2011', '', '2013', '',
'2015', '', '2017'])
# Converting the start and end years (of the global economy crisis) to datetime
dt2010 = pd.to_datetime('2010')
dt2015 = pd.to_datetime('2015')
dt2017 = pd.to_datetime('2017')
# Highlighting the crisis period during Obama time
ax2.axvspan(xmin=dt2009, xmax=dt2010, ymin=0, ymax=0.986,
alpha=0.12, color='grey') # year 2009
ax2.axvspan(xmin=dt2015, xmax=dt2017, ymin=0, ymax=0.986,
alpha=0.12, color='grey') # year 2015 - 2016
# Adding title
ax2.text(x=734500, y=1.75, s='Obama',size=14, weight='bold', color='green')
# Adding subtitle
ax2.text(x=734470, y=1.67, s='(2009-2017)', alpha=0.7)
# ----------------------------------------------------------------------------
# ax3: Trump
ax3.plot(trump['Time'], trump['rolling_mean'], linewidth=2.5, color='orange')
ax3.set_xticklabels(['2017', '', '2018', '', '2019', '',
'2020', '', '2021'])
# Converting the start and end years (of the global economy crisis) to datetime
dt2017 = pd.to_datetime('2017')
dt2018 = pd.to_datetime('2018')
dt2020 = pd.to_datetime('2020')
dt2021 = pd.to_datetime('2021')
dt2021 = pd.to_datetime('2021')
# Highlighting the crisis period during Obama time
ax3.axvspan(xmin=dt2017, xmax=dt2018, ymin=0, ymax=0.986,
alpha=0.12, color='grey') # year 2017
ax3.axvspan(xmin=dt2020, xmax=dt2021, ymin=0, ymax=0.986,
alpha=0.12, color='grey') # year 2020
# Adding title
ax3.text(x=736910, y=1.75, s='Trump',size=14, weight='bold', color='orange')
# Adding subtitle
ax3.text(x=736860, y=1.67, s='(2017-2021)', alpha=0.7)
# ----------------------------------------------------------------------------
# ax4: All together
ax4.plot(euro_to_dollar['Time'], euro_to_dollar['rolling_mean'],
linewidth=1, color='grey')
ax4.plot(bush['Time'], bush['rolling_mean'], linewidth=2.5, color='blue') # Bush
ax4.plot(obama['Time'], obama['rolling_mean'], linewidth=2.5, color='green') # Obama
ax4.plot(trump['Time'], trump['rolling_mean'], linewidth=2.5, color='orange') # Trump
# Highlighting the global economy crisis period
for s, e in zip(start_year_dt, end_year_dt):
ax.axvspan(xmin=s, xmax=e, ymin=0, ymax=0.986, alpha=0.12, color='grey')
# Adding title
ax4.text(x=731750, y=1.73, s='All Together with the Last Three US Presidents',size=14, weight='bold', color='Grey')
# Adding subtitle
ax4.text(x=733460, y=1.65, s='(1999-2021)', alpha=0.7)
# ----------------------------------------------------------------------------
# For all plots, adding horizontal line for rolling mean from 1999 - 2021
for ax in axes:
ax.axhline(xmin=0.008, xmax=0.99, y=1.20, color='grey', alpha=0.5)
# Adding figure title
ax1.text(x=729100, y=2.08, s='EURO-USD Rate Averaged 1.20 between 1999 - 2021',size=24, weight='bold')
# Adding figure subtitle
ax1.text(x=729100, y=2.00, s='EURO-USD Exchange Rates under US Presidents Bush, Obama and Trump', size=18)
# Adding Signature Bar
ax.text(x=729100, y=0.55, s='©SPWong' + ' '*200 + 'Source: European Central Bank',
color='#ffffff', backgroundcolor='#757575', size=10)
plt.show()
<ipython-input-38-4fb5050e8bee>:28: UserWarning: FixedFormatter should only be used together with FixedLocator ax1.set_xticklabels(['', '2001', '', '2003', '', '2005', '', '2007', '', '2009']) <ipython-input-38-4fb5050e8bee>:80: UserWarning: FixedFormatter should only be used together with FixedLocator ax2.set_xticklabels(['', '2009', '', '2011', '', '2013', '', <ipython-input-38-4fb5050e8bee>:104: UserWarning: FixedFormatter should only be used together with FixedLocator ax3.set_xticklabels(['2017', '', '2018', '', '2019', '',
--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-38-4fb5050e8bee> in <module> 134 135 # Highlighting the global economy crisis period --> 136 for s, e in zip(start_year_dt, end_year_dt): 137 ax.axvspan(xmin=s, xmax=e, ymin=0, ymax=0.986, alpha=0.12, color='grey') 138 NameError: name 'start_year_dt' is not defined
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) ~/opt/anaconda3/lib/python3.8/site-packages/IPython/core/formatters.py in __call__(self, obj) 339 pass 340 else: --> 341 return printer(obj) 342 # Finally look for special method names 343 method = get_real_method(obj, self.print_method) ~/opt/anaconda3/lib/python3.8/site-packages/IPython/core/pylabtools.py in <lambda>(fig) 246 247 if 'png' in formats: --> 248 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs)) 249 if 'retina' in formats or 'png2x' in formats: 250 png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs)) ~/opt/anaconda3/lib/python3.8/site-packages/IPython/core/pylabtools.py in print_figure(fig, fmt, bbox_inches, **kwargs) 130 FigureCanvasBase(fig) 131 --> 132 fig.canvas.print_figure(bytes_io, **kw) 133 data = bytes_io.getvalue() 134 if fmt == 'svg': ~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs) 2253 # force the figure dpi to 72), so we need to set it again here. 2254 with cbook._setattr_cm(self.figure, dpi=dpi): -> 2255 result = print_method( 2256 filename, 2257 facecolor=facecolor, ~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backend_bases.py in wrapper(*args, **kwargs) 1667 kwargs.pop(arg) 1668 -> 1669 return func(*args, **kwargs) 1670 1671 return wrapper ~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py in print_png(self, filename_or_obj, metadata, pil_kwargs, *args) 506 *metadata*, including the default 'Software' key. 507 """ --> 508 FigureCanvasAgg.draw(self) 509 mpl.image.imsave( 510 filename_or_obj, self.buffer_rgba(), format="png", origin="upper", ~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py in draw(self) 399 def draw(self): 400 # docstring inherited --> 401 self.renderer = self.get_renderer(cleared=True) 402 # Acquire a lock on the shared font cache. 403 with RendererAgg.lock, \ ~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py in get_renderer(self, cleared) 415 and getattr(self, "_lastKey", None) == key) 416 if not reuse_renderer: --> 417 self.renderer = RendererAgg(w, h, self.figure.dpi) 418 self._lastKey = key 419 elif cleared: ~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py in __init__(self, width, height, dpi) 89 self.width = width 90 self.height = height ---> 91 self._renderer = _RendererAgg(int(width), int(height), dpi) 92 self._filter_renderers = [] 93 ValueError: Image size of 106663x617 pixels is too large. It must be less than 2^16 in each direction.
<Figure size 864x576 with 4 Axes>
Based on the line plot, we observe that all the three US presidents share a similarity — the start and the end of their terms fall into the highlight of the major events/crises that influenced the EURO-USD exchange rate.
By using the mean (1.2) of US dollars from 1999 to 2021 as a guideline, we made the following observations:
In this project, we study the evolution of the EURO-USD exchange rate from 1999-2021 by using a data visualisation approach. We found that ever since 2015, the EURO-USD exchange rate has been very weak — the second weakest period after the early 2000s recession. The impact of the financial crisis in 2008 seemed smaller on the EURO currency, instead it peaked in 2008.