Are you supporting the right politician?

Graph Visualization of Voting Data

Cheuk Ting Ho

Grab the slides: http://bit.ly/PyConLimerickGraphViz

About me

Co-organizer of 

Open Source contribution

Creator of

Volenteer of

Developer Advocate of

Have you ever check what issues that a politician support before you vote?

Or, do you just vote for them because of the party that they are in?

Do you think your Councilor represent you in local issues?

Have you ever check what they vote for in the Dublin Council?

Politics can be complicated...

But data never lies...

(only people who interpete them does)

I gonna teach you how to analyze the voting data (with Python)

So...
What's a Graph?
And Why?

  • much easier to interpret the model

  • understand how it maps to real world entities compared to the relational example

SELECT Name from TABLE where Person_ID = (SELECT mother from TABLE where Name="John")
SELECT Name from TABLE where Person_ID = (SELECT mother from TABLE WHERE Person_ID = (SELECT mother from TABLE where Name="John"))
WOQL.and(
   WOQL.triple("v:Person", "mother", "v:MotherID"),
   WOQL.triple("v:MotherID", "name", "v:MotherName"),
   WOQL.triple("v:MotherID", "mother", "v:GrandmotherID"),
   WOQL.triple("v:GrandmotherID", "name", "v:GrandmotherName"),
)

Getting
the Data

Install TerminusDB & Client

Setup Checklist

Do you have TerminusDB installed?

Yes

No

Good to go

Do you have Docker?

Yes

No

Which OS?

Mac / Linux / Window 10 Pro

No

Others

Do you have TerminusDB installed?

Install Python Client

Python >= 3.6 ✅

TerminusDB installed ✅ (v1.1.7)

Create a Graph DB 

import woqlclient.woqlClient as woql

server_url = "http://localhost:6363"
dbId = "mydb"
key = "root"
dburl = server_url + "/mydb"

client = woql.WOQLClient()
client.connect(server_url, key)
client.createDatabase(dbId, "Dublin Council Graph")

Create a Schema

def create_schema(client):
    schema = WOQLQuery().when(True).woql_and(
                WOQLQuery().doctype("Party").
                label("Party").
                description("Political Party"),
                WOQLQuery().doctype("Representative").
                label("Representative").
                description("An elected member Dublin city council").
                property("member_of", "Party").
                label("Member of").cardinality(1),
                WOQLQuery().doctype("Similarity").
                label("Similarity").
                property("similarity", "decimal").
                label("Similarity").
                property("similar_to", "Representative").
                label("Similar To").cardinality(2)
                )
    return schema.execute(client)

Load-in
the Data

def get_csv_variables(url):
    csv = WOQLQuery().get(
        WOQLQuery().woql_as("councillor_a", "v:Rep_A").
                    woql_as("councillor_b", "v:Rep_B").
                    woql_as("party_a", "v:Party_A").
                    woql_as("party_b", "v:Party_B").
                    woql_as("distance", "v:Distance")
        ).remote(url)
    return csv
  
def get_wrangles():
    wrangles = [
         WOQLQuery().idgen("doc:Party", ["v:Party_A"], "v:Party_A_ID"),
         WOQLQuery().idgen("doc:Party", ["v:Party_B"], "v:Party_B_ID"),
         WOQLQuery().idgen("doc:Representative", ["v:Rep_A"], "v:Rep_A_ID"),
         WOQLQuery().idgen("doc:Representative", ["v:Rep_B"], "v:Rep_B_ID"),
         WOQLQuery().typecast("v:Distance", "xsd:decimal", "v:Similarity"),
         WOQLQuery().idgen("doc:Similarity", ["v:Rep_A", "v:Rep_B"], "v:Rel_ID"),
         WOQLQuery().concat("v:Rep_A similarity v:Distance to v:Rep_B", "v:Rel_Label")
    ]
    return wrangles
def get_inserts():
    inserts = WOQLQuery().woql_and(
        WOQLQuery().insert("v:Party_A_ID", "Party").label("v:Party_A"),
        WOQLQuery().insert("v:Party_B_ID", "Party").label("v:Party_B"),
        WOQLQuery().insert("v:Rep_A_ID", "Representative").label("v:Rep_A").
                    property("member_of", "v:Party_A_ID"),
        WOQLQuery().insert("v:Rep_B_ID", "Representative").label("v:Rep_B").
                    property("member_of", "v:Party_B_ID"),
        WOQLQuery().insert("v:Rel_ID", "Similarity").label("v:Rel_Label").
                    property("similar_to", "v:Rep_A_ID").
                    property("similar_to", "v:Rep_B_ID").
                    property("similarity", "v:Similarity")
      )
    return inserts
  
def load_csvs(client, csvs):
    for key, url in csvs.items():
        csv = get_csv_variables(url)
        wrangles = get_wrangles()
        inputs = WOQLQuery().woql_and(csv, *wrangles)
        inserts = get_inserts()
        answer = WOQLQuery().when(inputs, inserts)
        answer.execute(client)

Making Sense of the Data

/*******************
 Query Script
********************/

WOQL.limit(1000).and(
    WOQL.triple("v:Subject","similar_to","v:Value"),
    WOQL.triple("v:Subject","similar_to","v:Value2"),
    WOQL.triple("v:Subject","similarity","v:Similarity"),
    WOQL.triple("v:Value","member_of","v:Party"),
    WOQL.triple("v:Value2","member_of","v:Party2"),
    WOQL.not().eq("v:Value","v:Value2"),
    WOQL.opt().triple("v:Value2","label","v:Lab2"),
    WOQL.opt().triple("v:Value","label","v:Lab1"),
    WOQL.eval(WOQL.divide(1, WOQL.exp("v:Similarity", 4)), "v:Distance")
)

/***************************************
 View Script for Dublin City Council
****************************************/

view = View.graph();
view.node("v:Subject", "v:Lab2", "v:Lab1", "v:Party2", "v:Party", "v:Similarity", "v:Distance").hidden(true)
view.node("v:Similarity").hidden(true)
view.edge("v:Value", "v:Value2").distance("v:Distance").text("v:Distance").weight(0.04)
view.node("v:Value").text("v:Lab1").icon({ label: true})
view.node("v:Value2").text("v:Lab2").icon({ label: true})
view.node("v:Value", "v:Value2").charge(-4999).collisionRadius(30)
view.node("v:Value").v("v:Party").in("doc:PartySolidarity").color([5, 25, 22])
view.node("v:Value2").v("v:Party2").in("doc:PartySolidarity").color([5, 25, 22])
view.node("v:Value").v("v:Party").in("doc:PartySocial%20Democrats").color([25, 25, 225])
view.node("v:Value2").v("v:Party2").in("doc:PartySocial%20Democrats").color([25, 25, 225])
view.node("v:Value").v("v:Party").in("doc:PartySinn%20F%C3%A9in").color([25, 225, 25])
view.node("v:Value2").v("v:Party2").in("doc:PartySinn%20F%C3%A9in").color([25, 225, 25])
view.node("v:Value").v("v:Party").in("doc:PartyLabour%20Party").color([255, 0, 0])
view.node("v:Value2").v("v:Party2").in("doc:PartyLabour%20Party").color([255, 0, 0])
view.node("v:Value2").v("v:Party2").in("doc:PartyFine%20Gael").color([0, 0, 255])
view.node("v:Value").v("v:Party").in("doc:PartyFine%20Gael").color([0, 0, 255])
view.node("v:Value").v("v:Party").in("doc:PartyFianna%20F%C3%A1il").color([100, 200, 100])
view.node("v:Value2").v("v:Party2").in("doc:PartyFianna%20F%C3%A1il").color([100, 200, 100])
view.node("v:Value").v("v:Party").in("doc:PartyGreen%20Party").color([25, 225, 125])
view.node("v:Value2").v("v:Party2").in("doc:PartyGreen%20Party").color([225, 225, 125])
view.node("v:Value").v("v:Party").in("doc:PartyIndependent").color([25, 25, 25])
view.node("v:Value2").v("v:Party2").in("doc:PartyIndependent").color([25, 25, 25])
view.node("v:Value").v("v:Party").in("doc:PartyPeople%20Before%20Profit").color([225, 25, 25])
view.node("v:Value2").v("v:Party2").in("doc:PartyPeople%20Before%20Profit").color([225, 25, 25])
view.node("v:Value").v("v:Party").in("doc:PartyWorkers'%20Party").color([225, 225, 225])
view.node("v:Value2").v("v:Party2").in("doc:PartyWorkers'%20Party").color([225, 225, 225])

So How About UK Parliament

You time to shine
🌟

And win some prices

Competition deadline: 17th Mar 2020 (St. Patrick's Day!)

Team: 1-3 ppl

Format: a Jupyter notebook (.ipynb file) or a blog post

*Submission need to be publicly available (e.g. host on GitHub or published blog post)

 

Show us any interesting findings with the UK Parliament data using a similar method. Be creative!

Originality, Technical correctness, Informative

Prices:

1. £100 amazon gift card for the winning team

2. each team member will get a TerminusDB swag bundle

(include Hoodie Jacket, E-Coffee Mug and much more)

 

Winner will also be invited to speak at our meetup in Dublin
(and/or London!)

 

Submittion/ Questions: luke@datachemist.com
Name(s), contact email(s), link to submittion

Meetup:
DataOps and my first TerminusDB knowledge graph
5th March - Dogpatch Labs

 

To get the newest update👍:

Follow us on Twitter: @TerminusDB

Website: https://terminusdb.com/

Join the community:

https://community.terminusdb.com/

PyCon Limerick - Are you supporting the right politician?

By Cheuk Ting Ho

PyCon Limerick - Are you supporting the right politician?

Graph Visulization on Voting Data

  • 398
Loading comments...

More from Cheuk Ting Ho