Loading Stats SA ASCII-formatted time series data

Introduction

Statistics South Africa (Stats SA) is the government statistician here in South Africa. They publish stats about the economy, standard of living, the performance of local government, and lots of other stuff. If you're interested in finding out the state of South Africa in cold hard numbers, head over to http://www.statssa.gov.za/.

Most of their data is, unfortunately, locked up in reports that can be downloaded as PDFs - i.e. not that easy to do one's own analyses. Sometimes, they do make time series data available though. When time series data is available for download, its provided in two formats: Excel work books and ASCII files. Working with Excel is easy, but I feel like a challenge and I feel like avoiding Excel. That's why this notebook will focus on making a function to parse the ASCII files made available by Stats SA. I'll be putting this function to use in subsequent notebooks to make some interesting analyses of Stats SA data.

This notebook and related data can be downloaded from my repository for Stats SA-related blog posts: StatsSA-blog

In [1]:
%matplotlib inline
In [2]:
import pandas as pd
from os.path import expanduser
from datetime import date
import seaborn as sns
/home/wjs/opt/anaconda3/lib/python3.5/site-packages/matplotlib/__init__.py:872: UserWarning: axes.color_cycle is deprecated and replaced with axes.prop_cycle; please use the latter.
  warnings.warn(self.msg_depr % (key, alt_key))

The first stage - ASCII to a data structure

My first step is going to be to transform the text file into a list of time series structures. The basic structure of each time series is a sequence of meta-data lines followed by the actual values, one per line.

The meta-data lines start with tag Hxx (e.g. H01, H13), which is followed by some text. The actual data follows the meta-data, with each value in the time series printed on a new line. The end of the time series is indicated either by the end of the text file or by the appearance of a new meta-data line.

In [3]:
all_time_series = []
with open(expanduser('./data/CPI/ASCII Consumer Price Index - Jan 2008 to Nov 2015.txt'), 'r') as inputfile:
    for inputline in inputfile:
        if inputline[0] == 'H': # This is a meta-data field.
            tag, sep, value = inputline.partition(': ')
            if tag == 'H01':
                all_time_series += [{'data':[], 'tags':{}}]
            all_time_series[-1]['tags'][tag] = value.strip()
        else:
            all_time_series[-1]['data'] += [float(inputline)]
In [4]:
len(all_time_series)
Out[4]:
978
In [5]:
all_time_series[0]['tags']
Out[5]:
{'H01': 'P0141',
 'H02': 'Consumer Price Index',
 'H03': 'CPA00000',
 'H04': 'All Items',
 'H05': '',
 'H06': '',
 'H13': 'Western Cape',
 'H14': '',
 'H17': 'Index',
 'H18': 'Dec 2012 = 100',
 'H23': 'RELEASE: 2015 11',
 'H24': 'START: 2008 01',
 'H25': 'monthly'}

The tag H01 gives the publication number, while H02 gives the title associated with that publication. In this case, I'm looking at the Consumer Price Index in South Africa. The data doesn't just give the overall CPI though. It also gives CPI for different baskets of goods in each province. The basket of goods is indicated by the H04 tag, e.g. 'All items', 'Clothing' or 'Tobacco' are some of the baskets for which CPI is calculated. The H13 tag tells us which province the time series applies to, or if it applies to the whole of South Africa.

Making Pandas Series objects

Now that each time series has been extracted, the next step is to construct some Pandas Series objects, one for each time series. You'll see that the Stats SA data specifies the start date for a time series and an update frequency e.g. monthly. These need to be turned into a DateRange that can be used as an index into the Series.

In [6]:
for tsdata in all_time_series:
    if tsdata['tags']['H25'].strip() == 'monthly':
        _, start_year, start_month = tsdata['tags']['H24'].split(' ')
        index = pd.date_range(start=date(int(start_year), int(start_month), 1), freq='M', periods=len(tsdata['data']))
        tsdata['series'] = pd.Series(tsdata['data'],
                                     index=index,
                                     name='{code:s} {quant:s} ({province:s}) - {detail:s}'.format(
                                        code=tsdata['tags']['H01'],
                                        quant=tsdata['tags']['H02'],
                                        detail=tsdata['tags']['H04'],
                                        province=tsdata['tags']['H13']))

Plotting the data

Plotting the data shows that some of the indices have actually suffered deflation since 2008, but I'll look at that in another post. You'll also see that all the series converge at December 2012. That is because I'm plotting Consumper Price Index time series. These indices are all relative to December 2012, which was chosen to be the value 100. What this means is that if in January 2008 the index was at 300 for one of the time series, the price of that basket of goods was three times higher than in December 2015.

In [7]:
sns.plt.figure(figsize=(8,6))
for tsdata in all_time_series:
    tsdata['series'].plot()
sns.plt.show()

Putting it into a DataFrame

All of these time series have the same date range and frequency, i.e. monthly. Therefore, they can be combined into a single DataFrame. Here I'm going to make each time series into one column of the DataFrame. The column indices will be hierarchichal, using the province and detail, i.e. H13 and H04 tags.

In [8]:
df_data = {(tsdata['tags']['H13'], tsdata['tags']['H04']): tsdata['series'] for tsdata in all_time_series}
df = pd.DataFrame(data=df_data)
df
Out[8]:
All urban areas ... Western Cape
Actual rentals for housing Alcoholic beverages Alcoholic beverages and tobacco Analytical series - All urban areas Appliances, tableware and equipment Beer Books, newspapers and stationery Bread and cereals CPI Headline Clothing ... Restaurants and hotels Spirits Sugar, sweets and deserts Supplies and services Telecommunication equipment Tobacco Transport Vegetables Water and other services Wine
2008-01-31 76.4 69.4 67.0 68.3 88.2 67.5 73.1 66.7 75.3 84.5 ... 69.1 69.0 63.5 72.4 226.9 64.8 88.4 68.6 68.7 77.5
2008-02-29 76.4 70.8 67.8 68.3 87.4 69.3 72.4 68.8 75.8 84.7 ... 69.5 70.5 63.7 72.3 225.5 64.9 88.6 67.5 68.7 77.2
2008-03-31 78.3 73.3 70.6 70.5 88.5 71.2 72.8 70.9 76.9 85.0 ... 71.3 73.2 64.2 73.0 225.5 67.4 90.3 68.1 68.7 79.1
2008-04-30 78.3 73.7 71.1 70.5 87.6 71.3 74.4 71.9 77.3 85.1 ... 71.5 75.3 65.4 73.9 225.7 67.6 91.0 70.2 68.7 79.5
2008-05-31 78.3 74.0 71.3 70.5 88.3 71.5 77.2 76.5 77.9 85.3 ... 71.9 74.8 66.6 74.3 236.6 67.8 92.0 70.3 68.7 79.7
2008-06-30 80.4 74.2 71.4 70.5 88.7 71.5 75.7 79.8 79.0 85.8 ... 72.8 75.5 68.5 76.0 234.5 67.9 93.8 69.7 68.7 80.6
2008-07-31 80.4 74.4 71.6 71.5 89.9 71.6 76.5 81.2 80.0 86.0 ... 73.9 76.0 70.0 76.1 228.7 67.9 95.3 72.0 73.1 81.2
2008-08-31 80.4 74.6 72.6 71.6 91.8 71.5 77.9 83.5 80.5 86.5 ... 74.5 76.2 70.2 76.2 229.6 69.0 94.5 74.9 73.1 82.4
2008-09-30 81.3 75.1 73.4 71.6 92.8 72.2 76.4 85.3 81.0 86.8 ... 75.0 76.6 74.6 77.7 211.7 71.6 93.8 73.3 73.1 82.7
2008-10-31 81.3 75.1 73.6 71.6 93.0 72.1 76.3 85.4 81.1 86.9 ... 77.6 76.5 76.3 77.8 223.9 71.6 93.5 78.6 73.1 82.4
2008-11-30 81.3 75.1 73.7 71.6 93.3 72.2 81.4 85.6 81.2 87.0 ... 77.6 76.1 76.5 78.0 217.6 71.8 92.3 76.2 73.1 82.2
2008-12-31 82.5 75.1 73.8 71.6 93.4 72.2 79.5 85.6 81.1 87.4 ... 78.5 77.3 75.4 78.6 222.8 71.8 89.5 82.0 73.1 81.9
2009-01-31 82.5 75.4 73.9 71.7 97.7 72.2 79.6 85.4 81.4 88.1 ... 78.6 77.4 77.2 79.3 230.7 71.8 86.5 83.4 73.1 82.0
2009-02-28 82.5 76.1 74.7 71.7 100.2 73.2 80.7 85.2 82.3 89.0 ... 79.2 77.1 77.0 79.5 227.3 72.3 88.2 80.3 73.1 81.7
2009-03-31 83.4 79.8 78.2 75.3 101.5 77.0 82.3 85.0 83.4 89.5 ... 80.3 82.1 78.5 81.3 227.8 75.3 89.2 80.2 73.1 84.7
2009-04-30 83.4 80.5 78.8 75.4 102.3 77.4 83.7 84.8 83.8 89.9 ... 80.7 83.0 79.6 81.3 226.0 75.5 90.2 81.6 73.1 85.7
2009-05-31 83.4 80.7 78.9 75.4 102.5 77.4 83.5 84.7 84.1 90.0 ... 80.8 83.8 80.5 81.7 222.3 76.0 90.8 83.4 73.1 86.4
2009-06-30 84.9 80.6 78.9 75.4 102.8 77.2 81.2 84.6 84.5 90.2 ... 81.5 84.0 81.1 83.5 215.1 76.1 91.4 82.5 73.1 86.1
2009-07-31 84.9 80.8 79.8 78.2 101.9 77.3 82.3 84.2 85.4 90.7 ... 81.5 84.2 80.9 83.8 213.3 78.5 91.5 81.9 80.1 86.4
2009-08-31 84.9 80.8 81.8 78.6 102.1 77.1 82.3 83.8 85.6 91.1 ... 81.5 84.1 80.6 83.8 210.4 83.2 91.3 84.0 80.1 86.6
2009-09-30 85.7 81.0 82.1 78.6 101.6 77.2 84.4 83.5 86.0 91.3 ... 81.9 84.3 81.6 84.2 200.2 83.2 91.6 86.3 80.1 87.0
2009-10-31 85.7 81.0 82.1 78.6 100.9 77.2 85.1 83.3 86.0 91.5 ... 83.0 84.0 82.0 84.4 193.5 83.3 91.0 92.9 80.1 87.1
2009-11-30 85.7 81.0 82.1 78.6 99.9 77.1 86.8 83.1 86.0 91.6 ... 83.1 84.0 82.2 84.7 187.8 83.5 90.8 90.0 80.1 87.6
2009-12-31 86.5 80.9 82.1 78.6 99.4 77.1 88.4 82.9 86.2 91.7 ... 83.7 83.9 80.9 84.9 186.0 83.5 91.1 87.5 80.1 87.4
2010-01-31 86.5 81.1 82.3 78.6 99.8 77.2 86.4 82.9 86.4 91.8 ... 85.0 83.9 82.1 84.9 183.1 83.5 90.8 87.0 80.1 87.8
2010-02-28 86.5 81.2 82.3 78.6 99.0 77.4 85.2 82.7 87.0 91.8 ... 85.2 84.2 82.5 85.0 174.7 83.5 91.3 83.3 80.1 87.8
2010-03-31 87.4 85.2 87.0 82.2 99.1 82.3 90.0 82.4 87.7 91.8 ... 86.2 86.8 85.0 85.8 167.9 89.8 91.6 82.5 80.1 90.4
2010-04-30 87.4 85.5 87.3 82.4 98.7 82.3 85.5 82.3 87.8 91.9 ... 85.9 87.7 84.3 85.7 163.7 89.6 92.8 82.7 80.1 91.8
2010-05-31 87.4 85.9 87.6 82.4 98.6 82.7 85.8 82.2 88.0 92.0 ... 86.2 88.5 85.8 85.8 159.1 89.6 92.9 84.5 80.1 92.4
2010-06-30 88.7 85.7 87.4 82.4 99.0 82.3 84.8 81.6 88.0 92.0 ... 87.9 88.8 84.3 87.2 155.8 89.6 92.3 85.1 80.1 92.5
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
2013-06-30 102.7 106.3 105.6 106.1 100.4 106.6 103.3 100.7 102.9 101.0 ... 100.9 105.6 106.0 103.2 93.4 103.5 102.0 102.9 100.0 105.6
2013-07-31 102.7 106.8 106.4 107.8 100.7 107.0 101.9 101.4 104.0 101.4 ... 101.5 106.1 104.0 103.2 94.6 105.2 105.0 101.4 108.2 105.7
2013-08-31 102.7 106.6 106.4 107.8 101.2 107.2 103.7 102.5 104.3 102.2 ... 103.0 105.8 104.5 103.4 93.8 105.5 106.3 101.5 108.2 102.8
2013-09-30 103.9 107.0 106.7 107.9 102.1 107.1 105.1 104.0 104.8 102.8 ... 105.3 107.0 104.6 104.9 94.3 104.2 106.4 104.0 108.2 105.1
2013-10-31 103.9 108.0 107.4 107.9 102.1 108.7 104.9 104.4 105.0 103.1 ... 106.7 106.8 104.5 105.2 92.5 103.4 106.0 106.2 108.2 105.5
2013-11-30 103.9 107.6 106.9 107.9 103.1 108.8 105.3 104.1 105.1 103.3 ... 109.1 106.3 105.9 105.3 91.8 103.3 105.1 108.9 108.2 102.9
2013-12-31 105.0 107.6 107.1 107.9 102.5 109.2 105.8 104.3 105.4 103.6 ... 109.5 105.5 104.6 105.9 88.3 104.2 105.9 105.4 108.2 102.6
2014-01-31 105.0 108.5 107.6 108.1 102.9 109.6 110.7 105.3 106.1 104.0 ... 112.0 107.1 105.5 105.7 87.2 102.9 107.1 110.5 108.2 104.7
2014-02-28 105.0 108.9 107.9 108.1 103.4 110.3 110.7 107.2 107.3 104.8 ... 113.2 106.8 106.4 105.8 87.6 102.9 108.8 109.2 108.2 104.4
2014-03-31 106.4 110.7 110.1 114.1 103.6 110.7 113.2 108.5 108.7 105.3 ... 113.9 110.3 109.9 106.9 88.2 106.2 110.8 110.4 108.2 107.0
2014-04-30 106.4 111.5 110.8 114.2 104.1 111.4 114.6 110.3 109.2 105.8 ... 113.4 112.0 110.2 107.2 86.9 106.4 111.1 111.7 108.2 106.9
2014-05-31 106.4 112.2 111.3 114.2 104.4 111.8 114.6 110.9 109.4 106.2 ... 113.5 115.5 110.4 106.9 85.7 106.7 110.8 113.3 108.2 107.9
2014-06-30 108.0 112.5 111.5 114.4 105.1 112.3 114.5 111.3 109.7 106.4 ... 111.9 115.9 111.8 108.6 85.9 106.7 110.0 111.9 108.2 107.2
2014-07-31 108.0 112.4 112.6 116.6 104.8 112.2 115.3 110.2 110.6 106.8 ... 110.9 116.3 112.1 108.4 86.4 109.8 111.2 112.2 116.4 108.8
2014-08-31 108.0 112.6 112.9 116.6 105.3 112.1 114.8 110.4 111.0 107.8 ... 112.4 115.4 115.4 108.7 82.1 111.4 111.4 116.3 116.4 108.7
2014-09-30 109.3 112.7 113.1 116.6 105.5 112.5 115.3 110.1 111.0 108.5 ... 114.8 116.3 115.6 109.6 81.5 111.6 109.4 116.8 116.4 108.5
2014-10-31 109.3 114.8 114.6 116.6 105.3 115.4 115.8 109.8 111.2 109.2 ... 117.8 116.6 115.8 109.8 80.3 111.5 109.6 115.4 116.4 107.5
2014-11-30 109.3 115.4 115.2 116.6 105.3 116.4 116.6 109.8 111.2 109.9 ... 119.5 116.1 116.1 109.9 79.8 111.5 108.1 114.6 116.4 108.7
2014-12-31 110.3 115.4 115.2 116.6 104.4 116.6 117.9 109.4 111.0 110.3 ... 118.7 116.5 113.7 110.1 79.2 111.8 106.1 110.2 116.4 108.5
2015-01-31 110.3 116.1 115.6 116.6 105.1 117.2 118.6 110.0 110.8 110.7 ... 119.1 117.6 115.6 111.1 77.6 111.8 102.2 109.2 116.4 108.2
2015-02-28 110.3 117.7 116.8 116.6 106.3 119.7 120.4 111.5 111.5 111.2 ... 119.0 118.3 116.7 111.4 77.5 111.3 99.2 109.8 116.4 108.2
2015-03-31 112.0 120.7 120.0 123.6 106.4 122.6 121.0 112.5 113.1 111.6 ... 120.8 121.0 119.6 112.3 74.7 114.5 102.9 111.4 116.4 110.3
2015-04-30 112.0 122.3 121.4 123.8 106.8 124.4 121.2 113.0 114.1 111.9 ... 118.1 123.3 121.2 112.5 75.2 115.0 108.6 113.3 116.4 113.3
2015-05-31 112.0 123.2 122.1 123.8 107.7 125.1 122.8 115.1 114.4 112.2 ... 117.2 125.0 122.4 112.7 73.2 114.9 108.7 112.9 116.4 114.5
2015-06-30 113.2 123.5 122.3 123.9 107.8 125.6 124.2 114.9 114.9 112.6 ... 116.7 124.8 123.0 114.5 74.1 115.3 110.6 111.0 116.4 114.1
2015-07-31 113.2 123.8 122.6 126.5 107.7 125.7 123.1 115.5 116.1 113.0 ... 117.1 124.7 121.2 114.5 71.0 115.4 112.3 112.0 127.4 114.9
2015-08-31 113.2 124.1 122.8 126.5 108.5 126.1 124.5 116.9 116.1 113.6 ... 117.5 124.8 123.4 114.7 68.5 115.4 110.5 112.5 127.4 115.1
2015-09-30 114.7 124.3 122.9 126.5 107.9 126.2 125.7 117.1 116.1 114.1 ... 119.8 124.8 124.0 114.8 67.8 115.4 108.3 113.5 127.4 115.5
2015-10-31 114.7 124.5 123.1 126.5 108.7 126.4 126.7 117.6 116.4 114.5 ... 121.8 124.8 125.2 115.1 67.7 115.3 108.7 116.6 127.4 114.7
2015-11-30 114.7 124.6 123.3 126.6 109.1 126.5 126.8 117.8 116.5 115.0 ... 124.8 124.2 125.5 115.1 67.6 116.2 108.1 114.7 127.4 116.2

95 rows × 849 columns

What's next

Now that I can load the CPI data, I intend doing some more analysis in a future blog post. I'm also going to turn the code here into a decent function (or two) that will give me all the series and the DataFrame. I still need to handle frequencies other than monthly when loading the time series, e.g. quarterly and annually.

In [ ]: