Jupyter Notebook for Exploring Parliamentary Committees Data

This notebook contains some code fragments for exploring Parliamentary Committees data published via the MNIS API.

At the current time, Committee related data is not available from data.parliament.uk APIs.


In [15]:
import requests
import pandas as pd

In [16]:
def _getData(url):
    headers = {'content-type': 'application/json'}
    rr=requests.get(url, headers=headers)
    rr.encoding='utf-8-sig'
    return rr.json()

Current Committees

Get a list of committees and then filter according to whether the committee has an end date or not. If it doesn't have an end date, assume it is current.


In [17]:
def getCommitteeAdminData():
    comm_url='http://data.parliament.uk/membersdataplatform/services/mnis/ReferenceData/Committees/'
    b= _getData(comm_url)
    currcomm=[]
    for c in b['Committees']['Committee']:
        if (c['EndDate'] is None) or (isinstance(c['EndDate'], str) and c['EndDate']=='') or ('@xsi:nil' in c['EndDate'] and c['EndDate']['@xsi:nil']=='true'):
            currcomm.append(c)
        
    return pd.DataFrame(currcomm)

currcomm=getCommitteeAdminData()
currcomm.head()


Out[17]:
Chair_Member Chair_Member_Id Chair_StartDate CommitteeType CommitteeType_Id Committee_Id CreatedFromCommittee CreatedFromCommittee_Id DateCommonsAppointed DateLordsAppointed EndDate IsCommons IsLords Name ParentCommittee ParentCommittee_Id Phone StartDate Url
0 Mr Clive Betts 394 2017-07-12T00:00:00 Departmental 2 17 None None 2017-09-11T00:00:00 None None True False Communities and Local Government Committee None None 020 7219 3927/3290 2006-06-27T00:00:00 http://www.parliament.uk/business/committees/c...
1 None None None Departmental 2 23 None None 2015-07-08T00:00:00 None None True False Culture, Media and Sport Committee None None 020 7219 6120/4311 1997-07-28T00:00:00 http://www.parliament.uk/business/committees/c...
2 Dr Julian Lewis 54 2017-07-12T00:00:00 Departmental 2 24 None None 2017-09-11T00:00:00 None None True False Defence Committee None None 020 7219 3280/4453 1979-05-03T00:00:00 http://www.parliament.uk/business/committees/c...
3 Neil Parish 4072 2017-07-12T00:00:00 Departmental 2 52 None None 2017-09-11T00:00:00 None None True False Environment, Food and Rural Affairs Committee None None 020 7219 3263/3279 2001-07-16T00:00:00 http://www.parliament.uk/business/committees/c...
4 Mary Creagh 1579 2017-07-12T00:00:00 Departmental 2 62 None None 2017-09-11T00:00:00 None None True False Environmental Audit Committee None None 020 7219 0715/1378 1997-11-12T00:00:00 http://www.parliament.uk/business/committees/c...

In [18]:
#Committee types
currcomm['CommitteeType'].unique().tolist()


Out[18]:
['Departmental',
 'Domestic',
 'Joint Committee',
 'Legislative Committee',
 'N/A',
 'Other Committee',
 'Other Committee (Joint)',
 'Procedural',
 'Select',
 'Sub-Committee',
 'Investigative Committee',
 'Statutory Committee',
 'Sub-Committee (Inquiry)']

Committee Memberships

There are two ways we could try to ascertain committee memberships:

  • iterate through lists of committees and pull down membership for each committee;
  • pull down lists of (current) members and iterate trhough their committee memberships.

Members by Committee

We can get a list of current members associated with a committee from a committee name:


In [19]:
from urllib.parse import urlencode
def committeesReferenceURL(c, house=None):
    house='House={}%7C'.format(house) if house is not None and house.lower() in ['commons','lords', 'all'] else ''
    
    comm_url='http://data.parliament.uk/membersdataplatform/services/mnis/members/query/{h}{c}/Committees'.format(h= house,
                                                                                                                   c=(urlencode({'committee':c})))
    return comm_url

def committeeMembersByCommitteeName(c, house=None):
    members=_getData(committeesReferenceURL(c, house))
    tl=[]
    if members['Members'] is None: return "No members or committee not found"
    for m in members['Members']['Member']:
        tl.append('{} ({})'.format(m['FullTitle'],m['Party']['#text']))
    return 'Members of the {}: {}'.format(c,', '.join(tl))

In [20]:
c='Accommodation Steering Group'
committeeMembersByCommitteeName(c)


Out[20]:
'Members of the Accommodation Steering Group: The Rt Hon. the Lord Laming CBE DL (Crossbench), The Rt Hon. the Lord Newby OBE (Liberal Democrat), The Rt Hon. the Lord Taylor of Holbeach CBE (Conservative), The Lord Tunnicliffe CBE (Labour)'

More usefully, we can get administrative information about a committee, as well as the current members, if we have the ID of the committee:


In [21]:
def committeeReferenceURL(cid, membersToGet='all'):
    comm_url='http://data.parliament.uk/membersdataplatform/services/mnis/Committee/{cid}/{membersToGet}/'.format(cid=cid,
                                                                                                                  membersToGet=membersToGet)
    return comm_url

def committeeMembersByCommitteeID(cid, membersToGet='current'):
    committee= _getData(committeeReferenceURL(cid, membersToGet))
    tl=[]
    hse=[]
    members=[]
    if committee['Committee']['Details']['IsLords']=='True': hse.append('Lords')
    if committee['Committee']['Details']['IsCommons']=='True': hse.append('Commons')
    c='{n} ({typ}{hse})'.format(n=committee['Committee']['Details']['Name'],
                           typ=committee['Committee']['Details']["CommitteeType"], 
                               hse=', {}'.format(', '.join(hse)) if hse!=[] else '')
    for m in committee['Committee']['Members']['Member']:
        tl.append('{} ({})'.format(m['FullTitle'],m['Party']['#text']))
        members.append({'Name':committee['Committee']['Details']['Name'],
                        'Title':m['FullTitle'], 'Party':m['Party']['#text']})
    return 'Members of the {}: {}'.format(c,', '.join(tl)), pd.DataFrame(members)

In [22]:
cid=17
t,mdf = committeeMembersByCommitteeID(cid)

print(t)
mdf


Members of the Communities and Local Government Committee (Departmental, Commons): Mike Amesbury MP (Labour), Mr Clive Betts MP (Labour), Bob Blackman MP (Conservative), Helen Hayes MP (Labour), Kevin Hollinrake MP (Conservative), Andrew Lewer MP (Conservative), Fiona Onasanya MP (Labour), Mr Mark Prisk MP (Conservative), Mary Robinson MP (Conservative), Liz Twist MP (Labour)
Out[22]:
Name Party Title
0 Communities and Local Government Committee Labour Mike Amesbury MP
1 Communities and Local Government Committee Labour Mr Clive Betts MP
2 Communities and Local Government Committee Conservative Bob Blackman MP
3 Communities and Local Government Committee Labour Helen Hayes MP
4 Communities and Local Government Committee Conservative Kevin Hollinrake MP
5 Communities and Local Government Committee Conservative Andrew Lewer MP
6 Communities and Local Government Committee Labour Fiona Onasanya MP
7 Communities and Local Government Committee Conservative Mr Mark Prisk MP
8 Communities and Local Government Committee Conservative Mary Robinson MP
9 Communities and Local Government Committee Labour Liz Twist MP

Additional committee data available:

Details': {'Committee_Id': '17', 'CommitteeType': 'Departmental', 'CommitteeType_Id': '2', 'Name': 'Communities and Local Government Committee', 'ParentCommittee': None, 'ParentCommittee_Id': None, 'DateLordsAppointed': None, 'DateCommonsAppointed': '2017-09-11T00:00:00', 'Phone': '020 7219 3927/3290', 'Url': 'http://www.parliament.uk/business/committees/committees-a-z/commons-select/communities-and-local-government-committee/', 'StartDate': '2006-06-27T00:00:00', 'EndDate': None, 'CreatedFromCommittee': None, 'CreatedFromCommittee_Id': None, 'Chair_Member_Id': '394', 'Chair_Member': 'Mr Clive Betts', 'Chair_StartDate': '2017-07-12T00:00:00', 'IsCommons': 'True', 'IsLords': 'False'}, 'Clerks': {'Clerk': {'Name': 'Ed Beale', 'StartDate': '2017-05-08T00:00:00'}}, 'Members': {'Member':[...]}, 'LayMembers': None}

Get Committee Data By Member

We can get committee memberships for a member from the member record.

Start by getting some member data:


In [23]:
!pip3 install mnis


Collecting mnis
Collecting requests (from mnis)
  Using cached requests-2.18.4-py2.py3-none-any.whl
Collecting idna<2.7,>=2.5 (from requests->mnis)
  Using cached idna-2.6-py2.py3-none-any.whl
Collecting chardet<3.1.0,>=3.0.2 (from requests->mnis)
  Using cached chardet-3.0.4-py2.py3-none-any.whl
Collecting certifi>=2017.4.17 (from requests->mnis)
  Using cached certifi-2017.11.5-py2.py3-none-any.whl
Collecting urllib3<1.23,>=1.21.1 (from requests->mnis)
  Using cached urllib3-1.22-py2.py3-none-any.whl
Installing collected packages: idna, chardet, certifi, urllib3, requests, mnis
Successfully installed certifi-2017.11.5 chardet-3.0.4 idna-2.6 mnis-1.0.16 requests-2.18.4 urllib3-1.22

In [24]:
#https://github.com/olihawkins/mnis
#!pip3 install mnis
import mnis
import datetime

# Create a date for the analysis
d = datetime.date.today()

# Download the full data for MPs serving on the given date as a list
members = mnis.getCommonsMembersOn(d)

m=mnis.getSummaryDataForMembers(members, d)
df=pd.DataFrame(m)
df.head()


Out[24]:
constituency date_of_birth days_service first_start_date gender list_name member_id party
0 Hackney North and Stoke Newington 1953-09-27 10913 1987-06-11 F Abbott, Ms Diane 172 Labour
1 Oldham East and Saddleworth 1960-09-15 2415 2011-01-13 F Abrahams, Debbie 4212 Labour
2 Selby and Ainsty 1966-11-30 2667 2010-05-06 M Adams, Nigel 4057 Conservative
3 Hitchin and Harpenden 1986-02-11 151 2017-06-08 M Afolami, Bim 4639 Conservative
4 Windsor 1965-08-04 4470 2005-05-05 M Afriyie, Adam 1586 Conservative

Given the member ID, we can pull down detailed imformation about the member, including all their committee activity. Assume membership of a committee is current if there is no end date.


In [27]:
def getCommittees(mid):
    resp={}
    comm_url='http://data.parliament.uk/membersdataplatform/services/mnis/members/query/id={}/Committees'.format(mid)
    #should really use _getData(comm_url)
    headers = {'content-type': 'application/json'}
    q = requests.get(comm_url, headers=headers)
    q.encoding='utf-8-sig'
    j=q.json()
    resp['Member']=j["Members"]["Member"]["FullTitle"]
    resp["Committees"]=[]
    
    if "Committees" not in j["Members"]["Member"] or j["Members"]["Member"]["Committees"] is None: return resp
    if "Committee" not in j["Members"]["Member"]["Committees"]: return resp
    committees=j["Members"]["Member"]["Committees"]['Committee']
    if not isinstance(committees, list):
        committees=[committees]
    for c in committees:
        if (isinstance(c['EndDate'], str) and c['EndDate']=='') or ('@xsi:nil' in c['EndDate'] and c['EndDate']['@xsi:nil']=='true'):
            resp['Committees'].append(c)
    return resp

In [28]:
getCommittees(4005)


Out[28]:
{'Committees': [{'@Id': '126',
   'ChairDates': None,
   'EndDate': {'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
    '@xsi:nil': 'true'},
   'EndNote': None,
   'IsAlternate': 'False',
   'IsCoOpted': 'False',
   'IsExOfficio': 'False',
   'Name': 'Procedure Committee',
   'StartDate': '2017-09-11T00:00:00'},
  {'@Id': '17',
   'ChairDates': None,
   'EndDate': {'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
    '@xsi:nil': 'true'},
   'EndNote': None,
   'IsAlternate': 'False',
   'IsCoOpted': 'False',
   'IsExOfficio': 'False',
   'Name': 'Communities and Local Government Committee',
   'StartDate': '2017-09-11T00:00:00'},
  {'@Id': '202',
   'ChairDates': None,
   'EndDate': {'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
    '@xsi:nil': 'true'},
   'EndNote': None,
   'IsAlternate': 'False',
   'IsCoOpted': 'False',
   'IsExOfficio': 'False',
   'Name': 'Backbench Business Committee',
   'StartDate': '2017-09-11T00:00:00'}],
 'Member': 'Bob Blackman MP'}

We can parse this information and create a set of rows for each MP, one row for each committee they are on.

Note that this will take some time to run and is not very efficient - we are making a separate request to the MNIS API for info about each member.


In [30]:
#Make a column containing the dict of committees
df['committees']=df['member_id'].apply(lambda x: getCommittees(x)['Committees'] )

#Then for each of the committees, create a row in a dataframe associating member with committee
#https://stackoverflow.com/a/27266225/454773
res =  df[df['committees'].str.len()>0].set_index(['list_name','member_id','party'])['committees'].apply(pd.Series).stack()
res = res.reset_index()
res['cname']=res[0].apply(pd.Series)['Name']
res['chair']=res[0].apply(pd.Series)['ChairDates'].apply(pd.Series)['ChairDate']

membercommittees = res[['list_name','member_id','party','cname','chair']]

membercommittees.head()


Out[30]:
list_name member_id party cname chair
0 Afolami, Bim 4639 Conservative Public Accounts Committee NaN
1 Ali, Rushanara 4138 Labour Treasury Sub-Committee NaN
2 Ali, Rushanara 4138 Labour Treasury Committee NaN
3 Allan, Lucy 4411 Conservative Education Committee NaN
4 Allen, Heidi 4516 Conservative Work and Pensions Committee NaN

In [31]:
membercommittees.to_csv('data/saved_mnis_committees.csv',index=False)

 TO DO

It may be useful to explore http://data.parliament.uk/MembersDataPlatform/services/mnis/help further and perhaps extend Oli Hawkins' mnis package to support more general querying of the MNIS database.


In [ ]: