Date post: | 08-Jan-2017 |
Category: |
Data & Analytics |
Upload: | plotly |
View: | 131 times |
Download: | 2 times |
:InteractiveplottinginJuliaSpencerLyon
November18,2016
NOTE:Youareviewingastaticexportofaninteractiveweb-basedslidedeck.Someitemsintheslidesdidnotexportproperlytothepdf.Toseealiveversionoftheslidesvisit
In [1]:
PlotlyJS.jl
http://spencerlyon.com/presentations/
using PlotlyJS
Plotlyjavascriptloaded.
Toloadagaincall
init_notebook(true)
OutlineThanksBioBriefintrotoJulia
:plotly.jsinJuliaBasicsInteractivityConvenienceAPIStyles
PlotlyJS.jl
Bio4thyearofeconomicsPhDprogramatNYUSternLiveinNYCwithwifeandthreekidsActiveinJuliacommunity,occasionalsightingsinSciPy/PyDataworldResearchinterestsare(inbroadterms)informationandbeliefs,internationalmacro,reinforcementlearning--allwithacomputationalslant
WhatisJulia?
Forme:
Fastenough(intermsofmytimeandruntime)todoresearchcodeFuntoplaywith!
Juliaisahigh-level,high-performancedynamicprogramminglanguagefortechnicalcomputing,withsyntaxthatisfamiliartousersofothertechnicalcomputingenvironments.
Coreconcept:Multipledispatch
Julia'scoreabstractionismultipledispatchFunctionsarespecializedbasedonthetypeofallarguments
WhenIcallf(x, y),whichfisrun?Bestunderstoodbyexample
In [2]: f(x, y) = "Two arguments: $(x) and $(y)"f("hello", "plotcon")
Out[2]: "Two arguments: hello and plotcon"
In [3]:
In [4]:
In [5]:
In [6]:
f(x::Number, y) = "First arg is a number ($(x)), second isn't ($(y))"
# still old methodf("hello", "plotcon")
# new methodf(2, "plotcon")
# Generics# also new method, but this time with floating point first argumentf(2.0, "plotcon")
Out[3]: f (generic function with 2 methods)
Out[4]: "Two arguments: hello and plotcon"
Out[5]: "First arg is a number (2), second isn't (plotcon)"
Out[6]: "First arg is a number (2.0), second isn't (plotcon)"
In [7]:
In [8]:
In [9]:
# longer function syntaxfunction f(x::Number, y::Number) "Two numbers: ($(x), $(y))"end
# newest methodf(2.0, 2)
# unsigned 8 bit int and BigIntf(0x81, big(4))
Out[7]: f (generic function with 3 methods)
Out[8]: "Two numbers: (2.0, 2)"
Out[9]: "Two numbers: (129, 4)"
What'sthepoint?
Ourffunctionisn'tusefulMultipledispatchenablesflexibilityandexpressivityWe'llleveragethisinPlotlyJS.jltomaketheAPIconvenient
PlotlyJS.jlJuliawrapperforplotly.js:
CreatesplotlyplotsExposesplotly.jsAPIfunctionstoJulia
Twomaingoals:1. Makeitconvenienttoconstructandmanipulateplotlyvisualizations
fromJulia2. Provideinfrastructureforviewingplotsonmultiplefrontendsand
savingpublicationqualityplotlygraphicstofiles
APIOverview
ThePlotlyJS.jlAPIhastwomainlayers:
1. Faithfulplotly.jslayer:makesitpossibletodoanythingplotly.jscan2. Convenience,"Julian"layer:WIPattempttomakesomethingsmorenaturalin
Julia
Faithfulplotly.jsapilayer
plotly.jsvisualzationsaredescribedasaJSONobject:
Let'slookathowwecanbuildthesetracesandaLayoutinJulia
{ // Overall chart attributes "layout": { "title": "x²" }, "data": [ // JSON array of 'traces' { // Example trace "type": "scatter", "y": [1, 4.0, 9.0], "marker": { "symbol": "square" } } ]}
ByHand
JSONisnaturallyrepresentedasJuliaDictExamplefromabove:
BuildingnestedDictsinJuliaisn'tconvenientNeedtospelloutDictNeedtoaddlotsofquotes(similartorawjson)Alsoneed=>toseparatekeys/values
Dict( "layout" => Dict( "title" => "x²" ), "data" => [ Dict( "type" => "scatter", "y" => [1, 4.0, 9.0], "marker" => Dict( "symbol" => "square" ) ) ])
WithPlotlyJS.jl
PlotlyJS.jleasesthisburdenforJuliaprogrammers
In [10]: plot(scatter(y=[1.0, 4.0, 9.0], marker_symbol="square"), Layout(title="x²"))
Out[10]:
0 0.5 1 1.5 2
1
2
3
4
5
6
7
8
9
x²
Traces
Let'stakeacloserlookatbuildingtraceConstructtracesusingthetracetypeasafunctionandsettingkeywordargumentsExampletracefromabove:
In [11]:
Noticethesyntaxmarker_symbol.Thissetsanestedjsonatrribute{"marker": {"symbol": "square"}}Seethejson
In [12]:
my_trace = scatter(y=[1.0, 4.0, 9.0], marker_symbol="square")
print(json(my_trace, 2))
Out[11]: scatter with fields marker, type, and y
{ "y": [ 1.0, 4.0, 9.0 ], "type": "scatter", "marker": { "symbol": "square" }}
MoreExamples
In [13]:
In [14]:
# nesting works at more than one leveltrace1 = scatter(y=rand(10), marker_color="red", marker_line_width=2.0)trace2 = contour(x=1:5, y=1:10, z=randn(5, 10))trace3 = bar(x=1:10, y=rand(1:10, 10), name="mybar")
print(json(trace1, 2))
Out[13]: bar with fields name, type, x, and y
{ "y": [ 0.8168546843804745, 0.1967457511182984, 0.5148182283741638, 0.31298671217983753, 0.10559144576552915, 0.6834355544280886, 0.7503355447535949, 0.33011457537607636, 0.05938681725632655, 0.858053689412962 ], "type": "scatter", "marker": { "line": { "width": 2.0 }, "color": "red" }}
AlltracetypeshaveacorrespondingfunctionAlltraceattributesaresettable.Consult foranoverwhelminglycompletelist
apireference
Layout
BuildalayoutbyconstructingaLayoutobject:
In [15]:
ThesameunderscoremagicappliesAlsonoticetheattrfunction.Thisallowsyoutocreategroupsofnestedattributes.foo=attr(x=1, y=2)produces{"foo": {"x": 1, "y": 2}}Itisanalternativetofoo_x=1, foo_y=2attrcanbeusedwhenbuildingtracesalso
layout = Layout(xaxis=attr(range=[0, 10], title="assets"), yaxis_title="consumption", title="??")
Out[15]: layout with fields margin, title, xaxis, and yaxis
In [16]: print(json(layout, 2))
{ "yaxis": { "title": "consumption" }, "xaxis": { "range": [ 0, 10 ], "title": "assets" }, "title": "??", "margin": { "r": 50, "l": 50, "b": 50, "t": 60 }}
Toactuallydisplayaplot,wetieoneormoretracestogetherwithalayoutbycallingtheplotfunction:
In [17]: plot(trace1, layout)
Out[17]:
0 2 4 6 8 10
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
??
assets
consumption
In [18]: # layout optionalplot(trace2)
Out[18]:
In [19]: # more than one traceplot([trace1, trace3], layout)
Out[19]:
ConvenienceAPI
Theplotfunctionhasanumberofothermethodsthattrytomakeitabiteasierto
constructsimpleplots(remembermultipledispatch?:))
In [20]: methods(plot)
Out[20]: 13methodsforgenericfunctionplot:
plot{T<:Number,T2<:Number}(x::AbstractArray{T,1},y::AbstractArray{T2,2})at
plot{T<:Number,T2<:Number}(x::AbstractArray{T,1},y::AbstractArray{T2,2},l::PlotlyJS.Layout;style,kwargs...)at
plot{T<:Number,T2<:Number}(x::AbstractArray{T,2},y::AbstractArray{T2,2})at
plot{T<:Number,T2<:Number}(x::AbstractArray{T,2},y::AbstractArray{T2,2},l::PlotlyJS.Layout;style,kwargs...)at
plot{T<:Number,T2<:Number}(x::AbstractArray{T,N<:Any},
/Users/sglyon/.julia/v0.5/PlotlyJS/src/convenience_api.jl:31
/Users/sglyon/.julia/v0.5/PlotlyJS/src/convenience_api.jl:31
/Users/sglyon/.julia/v0.5/PlotlyJS/src/convenience_api.jl:40
/Users/sglyon/.julia/v0.5/PlotlyJS/src/convenience_api.jl:40
Let'sseeafewoftheminaction
In [21]: x = linspace(-5, 5, 50)y1 = sin(x) y2 = cos(x)plot(x, y1)
Out[21]:
−4 −2 0 2 4
−1
−0.5
0
0.5
1
In [22]: # x optional and set attributes with keywordsplot(y1, marker_color="red", mode="markers")
Out[22]:
0 10 20 30 40 50
−1
−0.5
0
0.5
1
In [23]: # columns become tracesplot(x, [y1 y2], kind="bar")
Out[23]:
In [24]: # can pass layoutplot(x, [y1 y2], Layout(title="My sinusoids"))
Out[24]:
−4 −2 0 2 4
−1
−0.5
0
0.5
1
Mysinusoids
trace0trace1
In [25]: # can plot functionsplot([sin, _ -> cos(exp(sin(2*_)))], -6, 6, Layout(title="My sinusoids"), marker_symbol="square", mode="markers+lines", kind="bar")
Out[25]:
Subplots
DeclarativeAPIsforsubplotscanbeverbosePlotlyJS.jltriestomakethiseasierforyouSupposeyouhavecreates4plotsusingplot:
Youcancombinethemintosubplotsusingfamiliar(h|v|hv)catsyntax:
p1 = plot(...)p2 = plot(...)p3 = plot(...)p4 = plot(...)
[p1 p2] # 1 row 2 cols[p1 p2 p3] # 1 row 3 cols[p1, p2] # 2 rows 1 col[p1 p2; p3 p4] # 2 rows 2 cols
Example
In [26]: rand_plot(n) = plot(scatter(x=collect(1:n), y=randn(n)))p1, p2, p3, p4 = [rand_plot(i) for i in [10, 20, 30, 40]];
In [27]: # 2 columns, 1 row[p1 p2]
Out[27]:
5 10
−1
−0.5
0
0.5
1
1.5
5 10 15 20−2
−1.5
−1
−0.5
0
0.5
1
1.5
2trace0trace1
In [28]: # 3 columns, 1 row[p1 p2 p3]
Out[28]:
5 10
−1
−0.5
0
0.5
1
1.5
5 10 15 20−2
−1.5
−1
−0.5
0
0.5
1
1.5
2
10 20 30
−2
−1.5
−1
−0.5
0
0.5
1trace0trace1trace2
In [29]: # two rows[p1, p2]
Out[29]:
2 4 6 8 10
−1
0
1
5 10 15 20−2
−1
0
1
2
trace0trace1
In [30]: # two rows and columnsp = [p1 p2; p3 p4]
Out[30]:
5 10
−1
0
1
5 10 15 20−2
−1
0
1
2
10 20 30
−2
−1
0
1
10 20 30 40−2
−1
0
1
2
trace0trace1trace2trace3
...underthehood
Wecaninspectthejsontoseewhatwe'vebeensavedfrom
In [31]: print(json(p))
{"layout":{"xaxis4":{"domain":[0.55,1.0],"anchor":"y4"},"xaxis3":{"domain":[0.0,0.45],"anchor":"y3"},"yaxis2":{"domain":[0.575,1.0],"anchor":"x2"},"yaxis1":{"domain":[0.575,1.0],"anchor":"x1"},"xaxis1":{"domain":[0.0,0.45],"anchor":"y1"},"margin":{"r":50,"l":50,"b":50,"t":60},"xaxis2":{"domain":[0.55,1.0],"anchor":"y2"},"yaxis4":{"domain":[5.551115123125783e-17,0.42500000000000004],"anchor":"x4"},"yaxis3":{"domain":[5.551115123125783e-17,0.42500000000000004],"anchor":"x3"}},"data":[{"yaxis":"y1","y":[0.4802673556774336,1.4948951444362697,-0.07045549774812956,0.7875701715665517,1.19154169230362,-0.019984297282311308,-1.2188104610632033,1.4889777636767583,0.1937548662633838,0.9467644161632363],"type":"scatter","xaxis":"x1","x":[1,2,3,4,5,6,7,8,9,10]},{"yaxis":"y2","y":[0.8116890547201712,0.026305587394362537,-1.9037677467854397,0.5179875294705465,1.132848386254002,-1.1211047904365008,-0.09732503991256072,-0.797064674081989,-0.5957822490328146,-1.5401700868810337,-0.8094006419764531,1.4733790412159211,1.8303960585896535,0.5451000885288673,-0.8508167149855682,0.5592069782568302,-0.5819292461464338,0.08366492698814007,-1.2171127224168015,-1.1731942880984414],"type":"scatter","xaxis":"x2","x":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]},{"yaxis":"y3","y":[0.44564426836146015,0.43568978674287906,-2.2957964903407775,-1.5588172083692364,-0.5175404777486905,-0.6879865409420531,-1.3614114948496532,-0.26860396772198347,-0.4550258510176642,-0.6661587413332551,-0.6568459806232246,-0.655160517941339,-1.0584839635045593,-0.9661978451654369,0.05588163077019386,0.4473040023028272,-0.4180878888519696,-0.670930203698033,-1.5680157591455446,-0.12061123763796747,0.422381416839956,-1.912905533601873,-0.7864305169890853,0.36283670860315104,1.1179292892139105,0.24868455953112426,0.8826378694420977,0.7852575028904395,-0.7092479652592288,-0.7193948586827139],"type":"scatter","xaxis":"x3","x":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30]},{"yaxis":"y4","y":[-1.1744007569318526,-0.7773336011050302,1.4579909852349564,0.645916605678359,0.05423891204909586,-0.5428595063217369,0.7880545251999888,-0.14699104765892224,-0.3646094249332061,-0.4594704011787981,2.051735462555251,-0.10082715474940011,-1.1039826364944816,-0.6379899490941968,2.1124562929284965,1.553274330113308,-0.03180412490346881,0.5406670948650817,0.2738357386022979,-0.15275395057926666,-0.6982546882702967,-0.30727020052937565,0.214317134550011,-0.5067747133088952,0.009943218088108437,-1.3286282576286048,-0.7921663785646528,-1.920398068028969,-0.37936872456340026,0.04228943262867289,-0.9117834367846034,1.7365583749057234,-0.35923210966846647,-1.0974821703330992,-1.2833801773643732,-0.0005514726863847877,-0.9892149968313712,-0.1881051940753708,-0.0
plotly.jsapifunctions
PlotlyJS.jlalsoexposesallplotly.jsAPIfunctionstopureJuliaSeelistLet'sseesomeexamples
here
In [32]:
In [33]:
In [34]:
In [35]:
In [36]:
myplot = rand_plot(10)
restyle!(myplot, marker_color="ForestGreen")
relayout!(myplot, title="This is my title")
addtraces!(myplot, trace1)
prependtraces!(myplot, 1, x=[[0.1, 0.2, 0.3, 1]], y=[rand(4)])
Out[32]:
2 4 6 8 10
−1.5
−1
−0.5
0
0.5
1
1.5
Interact.jl
WecanleverageInteract.jltoputhaveIPythonwidgetsinteractwithourplots
In [38]: using Interact
In [39]: x = linspace(-4, 4, 70)p = plot(x, sin(x), Layout(xaxis_range=(-5, 5), yaxis_range=(-1.1, 1.1)))display(p)
colors = ["red", "green", "blue", "orange"]modes = ["markers", "lines", "markers+lines"]φs = linspace(-2, 2, 100)ωs = linspace(-π, π, 100)
@manipulate for c in colors, m in modes, φ in φs, ω in ωs y = sin.(ω*x + φ) restyle!(p, y=(y,), marker_color=c, mode=m)end
−4 −2 0 2 4
−1
−0.5
0
0.5
1
Optgrowthexample
FrontendsTheAPIpresentedabovecoversgoal1Goal2includesfrontendintegrationAkeyfeatureofPlotlyJS.jlisintegrationwithIJuliaandprovidingadedicatedGUIwindowjustforPlotlyJS.jlfigures
Electron
Weuse toprovidean appforPlotlyJS.jlThisbuysusatleast2things:1. DedicatedGUIthatwecompletelycontrol2. Full2-waycommunicationwithjavascript
Javascriptinteropenables:LiveupdatesoftraceorlayoutattributesExtendingtracesoraddingnewtracestoadisplayedplotRawsvgoutputfromd3.jsforconversiontopdf,png,jpeg,eps,etc.More...
Demo
Blink.jl Electron
IJulia
FirstclasssupportforjupyternotebooksStillhaveinteractivity,butcommunicationwithjavascriptgoesthroughanon-displayedelectronwindow...fornow("native"notebookcommunicationisclose)CanleveragetoolslikeInteract.jltotiearbitrarywidgetstoplotupdates
Juno
CanhookintotheJunoPlotpaneinsideAtomBecauseAtomiselectron,thisfrontendsbehavesmuchliketheElectronone
nteract
ReuseintegrationwithJupytertorenderplotsinnteract
StylesPlotlyJS.jlversions>=0.4.0havesupportforplottingstyles.Thebestwaytothinkaboutstylesisthattheywillapplydefaultvaluesforattributes,onlyiftheattributeisnotalreadydefined.
Styledefinitions
Stylesareinstacesofthistype:
immutable Style color_cycle::Vector layout::Layout global_trace::PlotlyAttribute trace::Dict{Symbol,PlotlyAttribute}end
UsingSyles
TherearetwomainwaystouseaStyle:
Globalmode:calltheuse_style!(::Style)Plotbyplotmode:AllmethodsoftheplotandPlotfunctionsacceptakeywordargumentstyle::Stylethatsetsthestyleforthatplotonly.
In [43]: use_style!(:ggplot)simpleplot(;kw...) = plot([sin, cos], -6, 6; kw...)simpleplot()
Out[43]:
−0.5
0
0.5
1 sin
cos
In [44]: simpleplot(style=style(:seaborn))
Out[44]:
−6 −4 −2 0 2 4 6
−1
−0.5
0
0.5
1sin
cos
In [45]: simpleplot(style=style(:fivethirtyeight))
Out[45]:
−6 −4 −2 0 2 4 6
−1
−0.5
0
0.5
1 sincos
In [46]: simpleplot(style=style(:tomorrow_night_eighties))
Out[46]:
−6 −4 −2 0 2 4 6
−1
−0.5
0
0.5
1 sin
cos
In [47]:
In [ ]:
simpleplot()
Out[47]:
−6 −4 −2 0 2 4 6
−1
−0.5
0
0.5
1 sin
cos