Datavisualiseringsapp ved hjelp av GAE Python, D3.js og Google BigQuery Del 3

I den forrige delen av denne opplæringen så vi hvordan du kommer i gang med D3.js, og opprettet dynamiske skalaer og akser for vår visualiseringsgraf ved hjelp av et prøvedatasett. I denne delen av opplæringen skal vi tegne grafen ved hjelp av prøvedatasettet.

For å komme i gang, klone den forrige opplærings kildekoden fra GitHub.  

git klon https://github.com/jay3dec/PythonD3jsMashup_Part2.git

Naviger til Google App Engine (GAE) SDK-katalogen og start serveren.

./dev_appserver.py PythonD3jsMashup_Part2 / 

Pek nettleseren din til http: // localhost: 8080 / displayChart, og du bør kunne se X- og Y-aksene som vi opprettet i den forrige opplæringen.

Før du begynner, opprett en ny mal som heter displayChart_3.html som vil være det samme som displayChart.html. Legg også til en rute for displayChart_3.html. Dette er gjort bare for å holde demo av den forrige opplæringen intakt, siden jeg skal være vert for den på samme URL.

klasse DisplayChart3 (webapp2.RequestHandler): def get (selv): template_data =  template_path = 'Maler / displayChart_3.html' self.response.out.write (template.render (template_path, template_data)) application = webapp2.WSGIApplication [('/ chart', ShowChartPage), ('/ displayChart', DisplayChart), ('/ displayChart3', DisplayChart3), ('/', ShowHome),] debug = True)

Opprette visualiseringsgrafen (med prøvedata)

Fra vårt utvalgsdatasett har vi en rekke telle å bli plottet over et sett med tilsvarende år.

var data = ["count": "202", "år": "1590", "count": "215", "år": "1592", "count": "179" år ":" 1593 "," år ":" 1594 ", " count ":" 134 "," år " 176 "," år ":" 1596 "," tall ":" 172 "," år ":" 1597 ", teller ":" 199 "," år ":" 1599 ", " telle ":" 181 "," år ":" 1600 ", , "år": "1606", "count": "187", "år": "160", "år" : "1607", "count": "133", "år": "1608", "count": "190", "år": "1609", "count": "175" , "år": "1610", "telle": "91", "år": "1611", "telle": "150", "år": "1612"];

Vi representerer hvert datapunkt som sirkler i vår visualiseringsgraf. D3.js gir API-metoder for å lage forskjellige former og størrelser. 

Først bruker vi d3.selectAll til å velge sirkler inne i visualiseringselementet. Hvis ingen elementer blir funnet, returnerer den en tom plassholder hvor vi kan legge til sirkler senere.

var sirkler = vis.selectAll ("sirkel");

Deretter binder vi vårt datasett til sirkler utvalg.

var sirkler = vis.selectAll ("sirkel"). data (data);

Siden vår eksisterende sirkelvalg er tom, bruker vi enter for å opprette nye sirkler.

. Circles.enter () føyer ( "svg: sirkel")

Deretter definerer vi bestemte egenskaper som avstanden til kretsens sentre fra X (cx) og Y (cy) akser, deres farge, deres radius osv. For henting cx og cy, vi skal bruke XScale og ySkala å transformere året og telle data inn i plottingsområdet og tegne sirkelen i SVG-området. Slik ser koden ut:

var sirkler = vis.selectAll ("sirkel"). data (data); sirkler.enter () .append ("svg: sirkel") .attr ("slag", "svart") // setter sirkelgrensen .attr ("r", 10) // setter radiusen .attr ", funksjon (d) // forvandler årets data slik at den returnerer xScale (d.år); // kan plottes i svg-rom) .attr (" cy ", funksjon (d) // transformer tallet data slik at det returnerer yScale (d.count); // kan plottes i svg-rommet) .stil ("fyll", "rødt") // setter sirkelfargen

Lagre endringer og oppdatere siden din. Du bør se bildet nedenfor:

Endre Google BigQuery for å trekke ut relevante data

I den første delen av denne serien, da vi hentet data fra BigQuery, valgte vi ca. 1000 ord.

SELECT word FROM [publicdata: samples.shakespeare] LIMIT 1000

Vi har et datasett som inneholder en liste over alle ordene som vises i hele Shakespeare-arbeidet. Så, for å gjøre visualiseringsappen åpenbar noen nyttig informasjon, endrer vi spørringen vår for å velge antall ganger et bestemt ord, for eksempel Caesar, vises i Shakespeares arbeid over flere år.

Så logg inn på Google BigQuery, og vi får en skjerm som den som vises nedenfor:

Når vi har logget på Google BigQuery, har vi et grensesnitt der vi kan komponere og sjekke våre SQL-spørringer. Vi ønsker å velge antall ganger et bestemt ord vises i hele Shakespeare-arbeidet.

Så vår grunnleggende spørring vil se slik ut:

SELECT SUM (word_count) som WCount, corpus_date FRA [publicdata: samples.shakespeare] WHERE word = "Caesar" GRUPPE AV corpus_date ORDER BY WCount

Ovennevnte spørring gir oss et resultatsett som vist nedenfor:

La oss også inkludere gruppen av arbeider som svarer til Word Count. Endre spørringen som vist å inkludere corpus:

SELECT SUM (word_count) som WCount, corpus_date, group_concat (corpus) som Arbeid FRA [publicdata: samples.shakespeare] WHERE word = "Caesar" og corpus_date> 0 GROUP BY corpus_date ORDER BY WCount

Det resulterende resultatsettet er vist nedenfor:

Plotting av dataene fra Google BigQuery

Deretter åpne app.py og opprett en ny klasse som heter GetChartData. Inne i det, ta med spørringsoppgaven vi opprettet ovenfor.

queryData = 'query': 'VELG SUM (word_count) som WCount, corpus_date, group_concat (corpus) som Work FROM "[publiseringsdata: samples.shakespeare] WHERE word =" God "og corpus_date> 0 GROUP BY corpus_date ORDER BY WCount' 

Deretter oppretter du en BigQuery-tjeneste som vi vil utføre vår queryData.

tableData = bigquery_service.jobs ()

Nå, utfør queryData mot BigQuery-tjenesten og skrive ut resultatet til siden.

dataList = tableData.query (projectId = PROJECT_NUMBER, body = queryData) .execute () self.response.out.write (dataList)

Inkluder også en ny rute for GetChartData som vist.

søknad = webapp2.WSGIApplication ('/ getChartData', GetChartData), ('/', ShowHome), ('/ displayChart', DisplayChart), ('/ displayChart3', DisplayChart3) ,], debug = True)

Endelig oppdater koden til GAE-plattformen.

./appcfg.py oppdatering PythonD3jsMashup_Part2 /

Pek nettleseren din til http://YourAppspotUrl.com/getChartData som skal vise de resulterende dataene fra BigQuery.

Deretter prøver vi å analysere dataene som er mottatt fra Google BigQuery, og konvertere det til et JSON-dataobjekt og sende det til klientsiden for å behandle ved hjelp av D3.js.

Først vil vi sjekke om det er noen rader i Datalist returnert. Hvis ingen rader, setter vi svaret som null eller null.

hvis 'rader' i dataList: # parse dataList annet: resp.append ('count': '0', 'year': '0', 'corpus': '0')

Deretter analyserer vi Datalist ved å løse hver rad og plukke opp telling, år og corpus og opprette vårt nødvendige JSON objekt.

resp = [] hvis 'rader' i dataliste: for rad i dataListe ['rader']: for nøkkel, dict_list i row.iteritems (): count = dict_list [0] år = dict_list [1] corpus = dict_list [2] resp.append ('count': count ['v'], 'year': år ['v'], 'corpus': corpus ['v']) annet: resp.append ('count' '0', 'år': '0', 'corpus': '0')

Siden vi returnerer de analyserte dataene som JSON, importerer du JSON-biblioteket

importer json

Og returner den opprettede responsen som et JSON-svar.

self.response.headers ['Content-Type'] = 'application / json' self.response.out.write (json.dumps (resp))

La oss også gjøre søkeordet dynamisk, slik at det kan bestå som en parameter.

inputData = self.request.get ("inputData") queryData = 'query': 'VELG SUM (word_count) som WCount, corpus_date, group_concat (corpus) som arbeid fra "[publicdata: samples.shakespeare] WHERE word ="' + inputData + '"og corpus_date> 0 GROUP BY corpus_date ORDER BY WCount'

Her er hvordan klassen GetChartData endelig ser ut:

klasse GetChartData (webapp2.RequestHandler): def get (selv): inputData = self.request.get ("inputData") queryData = 'query': 'VELG SUM (word_count) som WCount, corpus_date, group_concat (corpus) som Work FRA "[publicdata: samples.shakespeare] WHERE word =" '+ inputData +' "GROUP BY corpus_date ORDER BY WCount ' tableData = bigquery_service.jobs () dataList = tableData.query (projectId = PROJECT_NUMBER, body = queryData) .execute ) resp = [] hvis 'rader' i dataListe: for rad i dataListe ['rader']: for nøkkel, dict_list i row.iteritems (): count = dict_list [0] år = dict_list [1] corpus = dict_list [2 ] resp.append ('count': count ['v'], 'year': år ['v'], 'corpus': corpus ['v']) annet: resp.append : '0', 'år': '0', 'corpus': '0') self.response.headers ['Content-Type'] = 'application / json' self.response.out.write (json. fyllinger (resp))

Oppdater appen i GAE og pek nettleseren din til http://YourAppspotUrl.com/getChartData og du kan se det returnerte JSON-svaret.

Deretter oppretter vi et grensesnitt for å spørre Google BigQuery-datasettet fra vår app dynamisk. Åpne opp Maler / displayChart_3.html og inkludere en innboks boks der vi legger inn søkeord for å spørre datasettet.

Inkluder et jQuery-skript på siden og på DOM-klare hendelsen, vi vil spørre Python-metoden GetChartData på Skriv inn nøkkel trykk.

$ (dokument) .ready (funksjon () $ ("# txtKeyword"). opptasting (funksjon (hendelse) hvis (event.keyCode == 13) // Hvis du skriver inn tasten, trykk DisplayChart ();); InitChart (); // Init Chart with Axis);

Opprett en annen funksjon DisplayChart på klientsiden, innenfor som vi lager et Ajax-anrop til Python GetChartData metode.

funksjon DisplayChart () var søkeord = $ ('# txtKeyword') .val (); $ .ajax (type: "GET", url: "/ getChartData", data: inputData: keyword, ​​dataType: "json", suksess: funksjon (svar) console.log (respons);, feil: funksjon (xhr, errorType, unntak) console.log ('Feil oppstod');); 

Oppdater koden til GAE og pek nettleseren din til http://YourAppspotUrl.com/displayChart3. Skriv inn et søkeord, si Caesar, og trykk Tast inn. Sjekk nettleserkonsollen, og du bør se det returnerte JSON-svaret.

La oss da plotte kretsene ved hjelp av det returnerte svaret. Så opprett en annen JavaScript-funksjon kalt CreateChart. Denne funksjonen ligner på InitChart funksjonen, men dataene ville bli sendt som parameter. Slik ser det ut:

funksjon CreateChart (data) var vis = d3.select ("# visualisering"), WIDTH = 1000, HEIGHT = 500, MARGINS = topp: 20, høyre: 20, bunn: 20, venstre: 50, xScale = d3 .scale.linear () .område ([MARGINS.left, WIDTH - MARGINS.right]) .domæne ([d3.min (data, funksjon (d) return (parseInt (d.year, 10) - 5); ), d3.max (data, funksjon (d) return parseInt (d.year, 10);)]), yScale = d3.scale.linear (). rekkevidde ([HEIGHT - MARGINS.top, MARGINS. bunn]). domenet (d3.min (data, funksjon (d) return (parseInt (d.count, 10) - 5);), d3.max (data, funksjon (d) return parseInt .count, 10);)]), xAxis = d3.svg.axis () .scale (xScale), yAxis = d3.svg.axis () .scale (yScale) .orient ("left"); vis.append ("svg: g") .attr ("klasse", "x-akse") .attr ("transform", "oversette (0," + (HEIGHT - MARGINS.bottom) + ")") (x-akse); vis.append ("svg: g") .attr ("klasse", "y-akse") .attr ("transform", "translate (" + (MARGINS.left) + ", 0)") .call (yAxis ); var sirkler = vis.selectAll ("sirkel"). data (data); sirkler.enter () .append ("svg: sirkel") .attr ("slag", "svart") .attr ("r", 10) .attr ("cx", funksjon (d) return xScale .år);) .attr ("cy", funksjon (d) return yScale (d.count);) .style ("fyll", "rødt")

Fra InitChart funksjon, fjern sirkelopprettingsdelen siden det ikke vil være nødvendig nå. Her er hvordan InitChart utseende:

funksjonen InitChart () var data = ["count": "202", "år": "1590", "count": "215", "år": "1592", "count" "179", "år": "1593", "tall": "199", "år": "1594", "teller": "176", "år": "1596", "telle": "172", "år": "1597", "telle": "161", "år": "1598 "", "år": "1600", "teller": "157", "år": "1599", ":" 1602 ", " count ":" 179 "," år ":" 1603 ", " count ":" 150 "," år ":" 1606 ", " count ":" 187 "," år ":" 1607 ", " count ":" 133 "," år ":" 1608 ", " count ":" 190 "," år ":" 1609 " ":" 175 "," år ":" 1610 ", " count ":" 91 "," år ":" 1611 ", " count ":" 150 "," år ":" 1612 " ]; var farge = d3.scale.category20 (); var vis = d3.select ("# visualisering"), WIDTH = 1000, HEIGHT = 500, MARGINS = topp: 20, høyre: 20, bunn: 20, venstre: 50, xScale = d3.scale.linear .range ([MARGINS.left, WIDTH - MARGINS.right]) .domæne ([d3.min (data, funksjon (d) return (parseInt (d.year, 10) - 5);), d3.max (data, funksjon (d) return parseInt (d.year, 10);)]), yScale = d3.scale.linear () .område ([HEIGHT - MARGINS.top, MARGINS.bottom]). [d3.min (data, funksjon (d) return (parseInt (d.count, 10) - 5);), d3.max (data, funksjon (d) return parseInt (d.count, 10); )]), xAxis = d3.svg.axis () .scale (xScale), yAxis = d3.svg.axis () .scale (yScale) .orient ("left"); vis.append ("svg: g") .attr ("klasse", "x-akse") .attr ("transform", "oversette (0," + (HEIGHT - MARGINS.bottom) + ")") (x-akse); vis.append ("svg: g") .attr ("klasse", "y-akse") .attr ("transform", "translate (" + (MARGINS.left) + ", 0)") .call (yAxis ); 

Fra nå av, når vi laster inn / displayChart3 siden, vil sirkler ikke vises. Sirkler vil bare vises når søkeordet er søkt. Så på suksess tilbakeringing av DisplayChart Ajax-anrop, send svaret på CreateChart funksjon.

suksess: funksjon (respons) console.log (respons); CreateChart (respons); 

Oppdater koden til GAE og prøv å søke etter søkeordet Caesar. OK, så nå får vi se resultatet som sirkler på grafen. Men det er ett problem: begge aksene blir overskrevet.

Så for å unngå at vi skal sjekke inn i CreateChart Fungerer hvis aksene allerede er der eller ikke.

var hasAxis = vis.select ('axis') [0] [0]; hvis (! hasAxis) vis.append ("svg: g") .attr ("klasse", "x-aksen") .attr ("transform", "translate (0," + (HEIGHT - MARGINS.bottom) + ")") .call (xAxis); vis.append ("svg: g") .attr ("klasse", "y-akse") .attr ("transform", "translate (" + (MARGINS.left) + ", 0)") .call (yAxis ); 

Som du kan se, har vi bare sjekket om SVG-elementet har akser, og hvis ikke, oppretter vi dem igjen. Oppdater koden til GAE, og prøv å søke på nytt for søkeordet, og du bør se noe slikt:

Pakke det opp

Selv om alle ser bra ut nå, er det fortsatt noen få problemer som vi skal ta opp i neste del av denne opplæringen. Vi vil også introdusere D3.js-overganger og noen få funksjoner til vår D3.js-graf, og prøve å gjøre det mer interaktiv.

Kildekoden fra denne opplæringen er tilgjengelig på GitHub.