The European Centre for Disease Prevention and Control provides daily-updated worldwide COVID-19 data that is easy to download in JSON, CSV or XML formats. In this tutorial, we will use the pandas data analysis tool on the comma-separated values (CSV) data to learn some of the basic pandas commands and explore what is contained within the data set.
Make sure you have Python 3 installed. As of right now, Python 3.8.2 is the latest.
During this tutorial we're also going to use pandas.
Install it now into a new virtual environment with the following commands:
python -m venv covidpandas
source covidpandas/bin/activate
pip install pandas
We are now ready to get the COVID-19 data and start analyzing it with pandas.
Go to the download today’s data on the geographic distribution of COVID-19 cases worldwide page in your web browser. It should look something like the following screenshot.
There should be a link to download the data in CSV format, but the organization has changed the page layout several times in the past few weeks, which makes it difficult to find formats other than Excel (XLSX). If you have trouble obtaining the CSV version, just download this one from GitHub which is pegged to a copy downloaded on March 28th, 2020.
We have the data in a CSV now we need to import it into a pandas DataFrame.
Start by running the Python REPL:
python
>>>
The REPL is ready to go, now we need to import pandas so we can read the data we downloaded.
from pandas import read_csv
df = read_csv("covid-19-cases-march-28-2020.csv")
Don't worry if you get an error like
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe7...
.
Run this command instead which explicitly sets the file encoding
so pandas can properly read the CSV.
# make sure the file name of the csv matches your file's name!
df = read_csv("covid-19-cases-march-28-2020.csv", encoding="ISO-8859-1")
We now have our data loaded into a pandas DataFrame and can start running code to poke and prod and what's inside the data set.
Let's first take a peek at what a sample of the data looks like. I
typically run the head
and tail
functions when I open something
up to find out what are contained in the first five and last five rows.
df.head()
You should see six lines of output: one as the columns header and the first five rows of data from the CSV:
dateRep day month year cases deaths countriesAndTerritories geoId countryterritoryCode popData2018
0 28/03/2020 28 3 2020 16 1 Afghanistan AF AFG 37172386.0
1 27/03/2020 27 3 2020 0 0 Afghanistan AF AFG 37172386.0
2 26/03/2020 26 3 2020 33 0 Afghanistan AF AFG 37172386.0
3 25/03/2020 25 3 2020 2 0 Afghanistan AF AFG 37172386.0
4 24/03/2020 24 3 2020 6 1 Afghanistan AF AFG 37172386.0
The tail
function looks at the last five rows in a DataFrame.
df.tail()
tail
output will look something like this:
dateRep day month year cases deaths countriesAndTerritories geoId countryterritoryCode popData2018
7315 25/03/2020 25 3 2020 0 0 Zimbabwe ZW ZWE 14439018.0
7316 24/03/2020 24 3 2020 0 1 Zimbabwe ZW ZWE 14439018.0
7317 23/03/2020 23 3 2020 0 0 Zimbabwe ZW ZWE 14439018.0
7318 22/03/2020 22 3 2020 1 0 Zimbabwe ZW ZWE 14439018.0
7319 21/03/2020 21 3 2020 1 0 Zimbabwe ZW ZWE 14439018.0
Note that you can also pass an integer into head
or tail
like
df.head(10)
to get the first or last n number of rows.
It looks like based on the tail
function we have around 7320 rows of
data (since the first row is 0 indexed). We can confirm how much
data is in each column with the count
function.
df.count()
count
's output will look like:
dateRep 7320
day 7320
month 7320
year 7320
cases 7320
deaths 7320
countriesAndTerritories 7320
geoId 7306
countryterritoryCode 7254
popData2018 7311
dtype: int64
What if we want to look at one of those columns and find, for example, the highest value of cases?
df.cases.max()
In this data set we get 18695 as the output. What about looking at
standard statistical measures across all columns? That's where the
describe
function comes in handy.
df.describe()
describe
presents standard statistical measures such as min, max,
median and mean for everything in your data set. In this case we
receive as output:
day month year cases deaths popData2018
count 7320.000000 7320.000000 7320.000000 7320.000000 7320.000000 7.311000e+03
mean 16.828142 2.249454 2019.990847 80.870355 3.687158 7.130483e+07
std 8.322981 1.256463 0.095239 608.270244 35.327689 2.140624e+08
min 1.000000 1.000000 2019.000000 -9.000000 0.000000 1.000000e+03
25% 10.000000 1.000000 2020.000000 0.000000 0.000000 4.137309e+06
50% 18.000000 2.000000 2020.000000 0.000000 0.000000 1.072767e+07
75% 24.000000 3.000000 2020.000000 5.000000 0.000000 5.139301e+07
max 31.000000 12.000000 2020.000000 18695.000000 971.000000 1.392730e+09
How about a quick view into whether or not columns' data are correlated
with each other? The corr
function is what we need.
df.corr()
For our data set, corr
outputs:
day month year cases deaths popData2018
day 1.000000 0.203006 -0.163665 0.063629 0.060075 -0.040677
month 0.203006 1.000000 -0.745912 0.062494 0.052707 -0.039131
year -0.163665 -0.745912 1.000000 0.012715 0.010032 -0.006294
cases 0.063629 0.062494 0.012715 1.000000 0.716968 0.136580
deaths 0.060075 0.052707 0.010032 0.716968 1.000000 0.082229
popData2018 -0.040677 -0.039131 -0.006294 0.136580 0.082229 1.000000
Not surprisingly, we see 1.000000 correlation between a column and itself. We'd have to worry if we didn't see that result! For other columns it may not make sense to look at their correlation. This is where you need to think about the data. There is often correlation between completely unrelated columns just because the data is structured a certain way.
If you are a developer like me without a rigorous background in statistics (Stats 200 in college was a long time ago), you may need to brush up on your stats knowledge before you are able to say whether something in the data matters or not.
Let's keep going exploring the data. We can select columns and determine how many unique items are held within it. For example, how many unique countries and territories are listed?
df.countriesAndTerritories.nunique()
In this case the result should be 196.
Those functions are fine for basic querying to learn what's in the data set, but how do we ask real questions by stringing together some commands?
We now know there are 7320 rows in this set since we used the count
function above. Each row represents a single day within a country. Now
to ask a question. How many days across these countries were there 10
or more cases reported?
Let's create a new dataframe named df2 with the rows that only have 10 or more cases reported on that day, then count the number of rows within it.
df2 = df[df['cases']>=10]
df2.count()
That should give us the value 1531. There have been 1531 instances of 10 or more COVID-19 cases reported on a single day, across the 196 countries or terrorities listed. But the 1531 is hard to explain to people. We should pick out a single country and show how many times 10 or more cases were reported on one day. How about a smaller country like Vietnam that is not being reported on as much as China, the United States or Italy?
df2[df2['countriesAndTerritories']=='Vietnam']
This will give us the full output of data by column:
dateRep day month year cases deaths countriesAndTerritories geoId countryterritoryCode popData2018
7217 28/03/2020 28 3 2020 16 0 Vietnam VN VNM 95540395.0
7219 26/03/2020 26 3 2020 14 0 Vietnam VN VNM 95540395.0
7220 25/03/2020 25 3 2020 11 0 Vietnam VN VNM 95540395.0
7222 23/03/2020 23 3 2020 24 0 Vietnam VN VNM 95540395.0
7226 19/03/2020 19 3 2020 15 0 Vietnam VN VNM 95540395.0
We can also use the count
function here to confirm there have been
five days in which 10 or more new cases have been reported in Vietnam
so far:
df2[df2['countriesAndTerritories']=='Vietnam'].count()
We get the output of 5 for the columns. Unfortunately, when you look at the full data it appears these rows are all very recent and the virus is just beginning to spread more widely there. Let's hope they along with every other country is able to turn the tide, flatten the curve and keep more people from getting sick as we continue onwards.
That's a good spot to leave off, but we covered a lot of pandas ground in this tutorial!
We just imported and took a look at what's in the European Centre for Disease Prevention and Control's COVID-19 data set using pandas. That was a quick tour of some basic pandas commands and I strongly recommend you peruse the DataFrame documentation list to learn about all of the other handy functions that this tool provides to developers.
You can also get an idea of what to code next in your Python project by reading the Full Stack Python table of contents page.
Questions? Contact me via Twitter @fullstackpython or @mattmakai. I'm also on GitHub with the username mattmakai.
Something wrong with this post? Fork this page's source on GitHub and submit a pull request.