diff --git a/HISTORY.rst b/HISTORY.rst index e538483d22b..4f9c6df4abb 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -6,6 +6,7 @@ Release History Next release ++++++++++++++++++ +* Update of tutorial files and docs #176, #172 * Adds np.int64() functions around some variables to get rid error caused by numpy update #188 * Made examples Readme.md more readable #189 diff --git a/docs/_build/doctrees/environment.pickle b/docs/_build/doctrees/environment.pickle index 06e51153e47..b3e74dda2b8 100644 Binary files a/docs/_build/doctrees/environment.pickle and b/docs/_build/doctrees/environment.pickle differ diff --git a/docs/_build/doctrees/index.doctree b/docs/_build/doctrees/index.doctree index 112ea740ebf..f77a29de869 100644 Binary files a/docs/_build/doctrees/index.doctree and b/docs/_build/doctrees/index.doctree differ diff --git a/docs/_build/html/_sources/index.txt b/docs/_build/html/_sources/index.txt index 8c419ec01a5..8fcd2466ecd 100644 --- a/docs/_build/html/_sources/index.txt +++ b/docs/_build/html/_sources/index.txt @@ -74,7 +74,7 @@ If you would like to add a feature, please reach out via `ticket`_ or the `email .. toctree:: :maxdepth: 1 - Mesa overview + Mesa overview intro-tutorial diff --git a/docs/_build/html/genindex.html b/docs/_build/html/genindex.html index 1811e40106f..41464646b75 100644 --- a/docs/_build/html/genindex.html +++ b/docs/_build/html/genindex.html @@ -76,6 +76,10 @@

A

- +
+
accept_tuple_argument() (in module mesa.space) +
+ +
add() (mesa.time.BaseScheduler method)
@@ -172,6 +176,10 @@

C

+
check_origin() (mesa.visualization.ModularVisualization.SocketHandler method) +
+ +
collect() (mesa.datacollection.DataCollector method)
@@ -766,6 +774,14 @@

S

+
shuffle (mesa.time.StagedActivation attribute) +
+ + +
shuffle_between_stages (mesa.time.StagedActivation attribute) +
+ +
SimultaneousActivation (class in mesa.time)
@@ -773,17 +789,29 @@

S

SingleGrid (class in mesa.space)
-
socket_handler (mesa.visualization.ModularVisualization.ModularServer attribute)
+
SocketHandler (class in mesa.visualization.ModularVisualization)
+
stage_list (mesa.time.StagedActivation attribute) +
+ + +
stage_time (mesa.time.StagedActivation attribute) +
+ + +
StagedActivation (class in mesa.time) +
+ +
static_handler (mesa.visualization.ModularVisualization.ModularServer attribute)
@@ -809,6 +837,10 @@

S

+
(mesa.time.StagedActivation method) +
+ +
(mesa.visualization.TextVisualization.TextVisualization method)
diff --git a/docs/_build/html/index.html b/docs/_build/html/index.html index 9c93f35d9b7..33712ce9627 100644 --- a/docs/_build/html/index.html +++ b/docs/_build/html/index.html @@ -25,7 +25,7 @@ - + @@ -125,8 +124,8 @@

Table Of Contents

Next topic

-

Mesa: Agent-based modeling in Python 3+

+

Overview of Modules

This Page

diff --git a/docs/_build/html/objects.inv b/docs/_build/html/objects.inv index da3280e34d8..994365beda8 100644 Binary files a/docs/_build/html/objects.inv and b/docs/_build/html/objects.inv differ diff --git a/docs/_build/html/searchindex.js b/docs/_build/html/searchindex.js index d48cdf4f4cf..fc419226582 100644 --- a/docs/_build/html/searchindex.js +++ b/docs/_build/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({envversion:47,filenames:["index","intro-tutorial","mesa","mesa.visualization","mesa.visualization.modules"],objects:{"":{mesa:[2,0,0,"-"]},"mesa.Agent":{model:[2,2,1,""],step:[2,1,1,""],unique_id:[2,2,1,""]},"mesa.Model":{run_model:[2,1,1,""],running:[2,2,1,""],schedule:[2,2,1,""],seed:[2,2,1,""],step:[2,1,1,""]},"mesa.batchrunner":{BatchRunner:[2,4,1,""]},"mesa.batchrunner.BatchRunner":{agent_reporters:[2,2,1,""],agent_vars:[2,2,1,""],collect_agent_vars:[2,1,1,""],collect_model_vars:[2,1,1,""],get_agent_vars_dataframe:[2,1,1,""],get_model_vars_dataframe:[2,1,1,""],iterations:[2,2,1,""],make_iterable:[2,3,1,""],model_cls:[2,2,1,""],model_reporters:[2,2,1,""],model_vars:[2,2,1,""],parameter_values:[2,2,1,""],run_all:[2,1,1,""],run_model:[2,1,1,""]},"mesa.datacollection":{DataCollector:[2,4,1,""]},"mesa.datacollection.DataCollector":{add_table_row:[2,1,1,""],agent_reporters:[2,2,1,""],agent_vars:[2,2,1,""],collect:[2,1,1,""],get_agent_vars_dataframe:[2,1,1,""],get_model_vars_dataframe:[2,1,1,""],get_table_dataframe:[2,1,1,""],model:[2,2,1,""],model_reporters:[2,2,1,""],model_vars:[2,2,1,""],tables:[2,2,1,""]},"mesa.space":{ContinuousSpace:[2,4,1,""],Grid:[2,4,1,""],MultiGrid:[2,4,1,""],SingleGrid:[2,4,1,""]},"mesa.space.ContinuousSpace":{get_distance:[2,1,1,""],get_neighbors:[2,1,1,""],move_agent:[2,1,1,""],out_of_bounds:[2,1,1,""],place_agent:[2,1,1,""],torus_adj:[2,1,1,""]},"mesa.space.Grid":{coord_iter:[2,1,1,""],default_val:[2,3,1,""],get_cell_list_contents:[2,1,1,""],get_neighborhood:[2,1,1,""],get_neighbors:[2,1,1,""],is_cell_empty:[2,1,1,""],iter_cell_list_contents:[2,1,1,""],iter_neighborhood:[2,1,1,""],iter_neighbors:[2,1,1,""],move_agent:[2,1,1,""],neighbor_iter:[2,1,1,""],out_of_bounds:[2,1,1,""],place_agent:[2,1,1,""],torus_adj:[2,1,1,""]},"mesa.space.MultiGrid":{default_val:[2,3,1,""],iter_cell_list_contents:[2,1,1,""]},"mesa.space.SingleGrid":{empties:[2,2,1,""],exists_empty_cells:[2,1,1,""],find_empty:[2,1,1,""],move_to_empty:[2,1,1,""],position_agent:[2,1,1,""]},"mesa.time":{BaseScheduler:[2,4,1,""],RandomActivation:[2,4,1,""],SimultaneousActivation:[2,4,1,""]},"mesa.time.BaseScheduler":{add:[2,1,1,""],agents:[2,2,1,""],get_agent_count:[2,1,1,""],model:[2,2,1,""],remove:[2,1,1,""],step:[2,1,1,""],steps:[2,2,1,""],time:[2,2,1,""]},"mesa.time.RandomActivation":{step:[2,1,1,""]},"mesa.time.SimultaneousActivation":{step:[2,1,1,""]},"mesa.visualization":{ModularVisualization:[3,0,0,"-"],TextVisualization:[3,0,0,"-"],modules:[4,0,0,"-"]},"mesa.visualization.ModularVisualization":{ModularServer:[3,4,1,""],PageHandler:[3,4,1,""],SocketHandler:[3,4,1,""],VisualizationElement:[3,4,1,""]},"mesa.visualization.ModularVisualization.ModularServer":{canvas_height:[3,2,1,""],canvas_width:[3,2,1,""],grid_height:[3,2,1,""],grid_width:[3,2,1,""],handlers:[3,2,1,""],launch:[3,1,1,""],local_handler:[3,2,1,""],max_steps:[3,2,1,""],model_args:[3,2,1,""],model_cls:[3,2,1,""],model_kwargs:[3,2,1,""],model_name:[3,2,1,""],page_handler:[3,2,1,""],port:[3,2,1,""],portrayal_method:[3,2,1,""],render_model:[3,1,1,""],reset_model:[3,1,1,""],run_model:[3,1,1,""],settings:[3,2,1,""],socket_handler:[3,2,1,""],static_handler:[3,2,1,""],verbose:[3,2,1,""],viz_states:[3,2,1,""]},"mesa.visualization.ModularVisualization.PageHandler":{get:[3,1,1,""]},"mesa.visualization.ModularVisualization.SocketHandler":{on_message:[3,1,1,""],open:[3,1,1,""]},"mesa.visualization.ModularVisualization.VisualizationElement":{js_code:[3,2,1,""],local_includes:[3,2,1,""],package_includes:[3,2,1,""],render:[3,1,1,""],render_args:[3,2,1,""]},"mesa.visualization.TextVisualization":{TextData:[3,4,1,""],TextElement:[3,4,1,""],TextGrid:[3,4,1,""],TextVisualization:[3,4,1,""]},"mesa.visualization.TextVisualization.TextData":{render:[3,1,1,""]},"mesa.visualization.TextVisualization.TextElement":{render:[3,1,1,""]},"mesa.visualization.TextVisualization.TextGrid":{converter:[3,3,1,""],grid:[3,2,1,""],render:[3,1,1,""]},"mesa.visualization.TextVisualization.TextVisualization":{elements:[3,2,1,""],model:[3,2,1,""],render:[3,1,1,""],step:[3,1,1,""],step_forward:[3,1,1,""]},"mesa.visualization.modules":{CanvasGridVisualization:[4,0,0,"-"],ChartVisualization:[4,0,0,"-"],TextVisualization:[4,0,0,"-"]},"mesa.visualization.modules.CanvasGridVisualization":{CanvasGrid:[4,4,1,""]},"mesa.visualization.modules.CanvasGridVisualization.CanvasGrid":{canvas_height:[4,2,1,""],canvas_width:[4,2,1,""],package_includes:[4,2,1,""],portrayal_method:[4,2,1,""],render:[4,1,1,""]},"mesa.visualization.modules.ChartVisualization":{ChartModule:[4,4,1,""]},"mesa.visualization.modules.ChartVisualization.ChartModule":{canvas_height:[4,2,1,""],canvas_width:[4,2,1,""],data_collector_name:[4,2,1,""],package_includes:[4,2,1,""],render:[4,1,1,""],series:[4,2,1,""]},"mesa.visualization.modules.TextVisualization":{TextElement:[4,4,1,""]},"mesa.visualization.modules.TextVisualization.TextElement":{js_code:[4,2,1,""],package_includes:[4,2,1,""]},mesa:{Agent:[2,4,1,""],Model:[2,4,1,""],batchrunner:[2,0,0,"-"],datacollection:[2,0,0,"-"],space:[2,0,0,"-"],time:[2,0,0,"-"],visualization:[3,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","method","Python method"],"2":["py","attribute","Python attribute"],"3":["py","staticmethod","Python static method"],"4":["py","class","Python class"]},objtypes:{"0":"py:module","1":"py:method","2":"py:attribute","3":"py:staticmethod","4":"py:class"},terms:{"__init__":1,"__str__":3,"boolean":2,"class":[1,2,3,4],"default":[2,3,4],"final":2,"function":[1,2,3,4],"import":[1,2],"new":[2,4],"return":[2,3,4],"short":1,"static":[2,3],"throw":2,"true":[2,3,4],aa08f8:4,aaaaaa:3,abl:4,abm:[0,2],about:1,abov:[0,1,4],accept:[2,4],access:3,accordingli:3,account:2,act:[2,3],action:1,activ:[1,2],actual:[2,3],add:[0,1,2],add_table_row:2,addit:2,addition:2,adedd:3,adjust:2,adrian:1,advanc:[2,3],agent_id:2,agent_report:2,agent_var:2,all:[1,2,3,4],allow:[0,2,3],alon:3,along:3,also:[1,2,3],altern:0,analysi:0,analyz:0,ani:[1,2],anoth:1,anyth:3,apache2:0,app:3,append:2,appli:2,applic:3,approach:1,appropri:2,arbitrari:2,architectur:1,arg:[2,3],argument:2,around:3,arrai:2,arxiv:1,ascii:3,ask:2,associ:[2,3],assum:[2,3],assumpt:2,attribut:[3,4],autom:1,automat:1,averag:2,axi:4,baseschedul:2,basi:3,basic:[1,3],batch:[1,2],becaus:1,begin:1,behav:1,behavior:[1,2],below:[1,3],between:[2,3],bit:1,black:4,both:[2,4],bottom:2,bound:2,browser:[0,1,3],build:0,built:[0,1,3,4],button:3,call:[1,2,3,4],can:[1,2,3,4],canva:[3,4],canvas_height:[3,4],canvas_modul:4,canvas_width:[3,4],canvasgrid:[3,4],canvasgridvisu:[2,3],canvasmodul:4,canvasvi:3,cell:[2,3,4],cell_list:2,center:2,certain:2,chang:[1,2],charact:3,chart:[1,4],chart_modul:4,chartmodul:4,chartvisu:[2,3],check:[0,2],child:3,choic:1,circl:[3,4],click:3,client:[3,4],clock:2,code:[1,3,4],collect_agent_var:2,collect_model_var:2,collector:1,color:[3,4],column:2,column_nam:2,combin:2,come:3,complet:2,compon:[0,1,2],compos:3,concept:[2,3],concurr:3,cond:1,condit:2,connect:3,consist:[2,3],contain:[1,2,4],content:0,continu:2,continuousspac:2,contributor:0,control:[1,2,3],convert:[2,3],coord:2,coord_it:2,coordin:[2,4],core:[0,1,2],correspond:[1,2,3,4],count:3,creat:0,create_ag:1,current:[1,2,3,4],custom:[0,4],data:[0,1],data_collector_nam:4,datacollector:[2,4],datafram:2,debug:3,def:1,default_v:2,defin:[3,4],demonstr:1,depend:1,describ:[1,4],despit:1,determin:[2,4],diagon:2,dictionari:[2,3,4],differ:1,dim_len:2,directli:[1,2],directori:[1,3],discret:2,discuss:0,displai:[1,3],distanc:2,distribut:1,div:3,dmasad:3,doc:[0,1],doe:[1,2],down:2,dragulescu2002:1,draw:4,drawn:[1,4],drăgulescu:1,dynam:1,each:[1,2,3,4],easi:1,easili:1,econophys:1,edg:2,editor:1,either:4,element:[2,3,4],els:2,email:0,emerg:1,empti:[2,3],end:[2,3,4],enforc:2,ensur:2,entir:[2,4],environ:1,equal:2,equival:2,error:2,event:3,everi:[1,2],exact:2,exactli:[2,3],exampl:[1,2,3,4],except:2,exclud:2,execut:2,exists_empty_cel:2,expect:3,explic:2,explicitli:2,extens:2,extern:3,fals:[2,4],familiar:1,file:[0,1,3],fill:[2,3,4],find_empti:2,first:[1,2,3],fix:1,follow:[0,1,3,4],form:2,forward:3,fraction:4,framework:[0,2],from:[1,2,3,4],front:4,funtion:1,gener:[1,2,3,4],get:[0,1,2,3],get_agent_count:2,get_agent_vars_datafram:2,get_cell_list_cont:2,get_dist:2,get_model_vars_datafram:2,get_neighbor:2,get_neighborhood:2,get_step:3,get_table_datafram:2,getattr:3,github:0,give:1,given:[2,3],goal:0,good:1,graphvi:3,grid:[0,1,2,3,4],grid_height:[2,3,4],grid_width:[2,3,4],griddraw:4,guid:0,handl:[1,2,3,4],handler:3,happi:4,have:[1,2,3,4],height:[2,3,4],help:0,helper:2,here:[1,2],higher:4,hold:[2,3],how:[1,3],howev:2,html5:3,html:[3,4],http:1,identifi:[1,2],ignore_miss:2,illustr:1,imag:1,impact:2,implement:0,implicitli:2,includ:[1,2,3],include_cent:2,incom:1,index:[0,2,3],inform:[3,4],initi:1,input:[3,4],insert:1,insid:3,instal:0,instanc:[1,2,3],instanti:[1,2,3],instead:2,interfac:[0,3],intern:[2,3],intro:0,introduct:0,involv:2,ipython:[0,3],is_cell_empti:2,issu:0,iter:2,iter_cell_list_cont:2,iter_neighbor:2,iter_neighborhood:2,itself:1,javascript:[1,3,4],js_code:[3,4],json:[3,4],jump:2,keep:[2,3],kei:[2,4],know:3,knowledg:1,kwarg:3,label:4,launch:3,layer:[3,4],least:4,left:2,let:1,level:[1,2,4],licens:0,like:[0,1,2],line:4,list:[0,2,3,4],littl:1,live:4,local:3,local_handl:3,local_includ:3,locat:2,look:1,lookup:2,loop:[1,2,3],lower:4,mai:[2,3],main:3,make:[1,2],make_iter:2,manag:2,mani:2,map:[2,3],mason:[0,2],mat:1,max:2,max_step:[2,3],meant:[2,3],mechan:1,messag:3,method:[1,2,3,4],min:4,minim:3,miss:2,mkdir:1,model_arg:3,model_cl:[2,3],model_kwarg:3,model_nam:3,model_report:2,model_var:2,modifi:2,modul:0,modular:[0,3,4],modularserv:[1,2],modularvisu:2,moment:4,monei:1,moneyag:1,moneymodel:1,moor:2,more:[0,1,2,3,4],most:[0,2,4],move:2,move_ag:2,move_to_empti:2,much:1,multigrid:2,multilay:3,multipl:1,must:[2,4],mymodel:3,name:[2,3,4],necessari:[2,3],need:[1,2,3,4],neighbor:2,neighbor_it:2,neighborhood:2,nest:2,netlogo:[0,2],network:1,neumann:2,next:3,non:2,none:[2,3,4],note:2,notebook:[0,3],now:1,num_ag:1,number:[1,2,4],numpi:1,object:[1,2,3,4],observ:1,occupi:2,off:2,often:1,old:2,on_messag:3,onc:2,onli:[2,3],open:[1,3],oppos:3,option:[3,4],order:[2,3],org:1,other:[1,2,3],otherwis:2,our:[1,3],out:[0,1,2],out_of_bound:2,over:[1,2,3],overal:3,overload:[2,3],own:[1,2],packag:1,package_includ:[3,4],page:[1,3,4],page_handl:3,pagehandl:3,pair:2,panda:[1,2],paramet:[1,2,3],parameter_valu:2,parent:3,pars:3,part:3,particular:[1,2,3,4],pass:[2,3,4],patch:3,path:3,per:2,pick:2,piec:3,pip:[0,1],pixel:4,place:[1,2,4],place_ag:2,pleas:0,plot:4,point:[1,2],port:3,portray:4,portrayal_method:[3,4],pos_1:2,pos_2:2,posit:[2,3],position_ag:2,possibl:0,potenti:3,practic:1,pre:1,preprint:1,present:1,primarili:3,print:3,produc:[3,4],program:3,progress:1,projectmesa:3,properti:[2,3],protocol:3,provid:[1,2,3,4],pull:0,purpos:1,push:4,put:1,pypi:0,python3:1,queue:2,quick:3,quickli:0,radiu:[2,4],rais:2,random:2,randomactiv:2,rang:1,raw:3,reach:[0,2],readi:[3,4],readili:3,receiv:3,recent:4,recommend:1,record:1,rect:4,rectangl:4,red:4,refer:1,reflect:1,regim:[1,2],reinstanti:3,relev:[2,4],remov:2,render:[3,4],render_arg:3,render_model:3,repast:0,replic:2,report:2,repres:[2,3],request:[0,3],requesthandl:3,requir:[1,2],reset:3,reset_model:3,reshuffl:2,resourc:0,rest:[1,3],result:[0,1,2,3],retriev:4,right:2,row:2,rule:1,run:[0,1,2,3],run_al:2,run_model:[2,3],runner:1,same:[2,4],schedul:[0,1,2],schell:0,schelling_chart:4,search:2,second:3,see:1,seed:2,segreg:0,select:2,self:1,send:3,sent:3,seri:4,seriou:2,serv:1,server:3,set:[1,2,3],settabl:4,sever:[2,3],shape:[3,4],should:[1,3],show:3,shuffl:2,simpl:[1,2,3],simplest:2,simpli:[1,2],simplic:1,simul:2,simultan:2,simultaneousactiv:2,singl:[1,2],singlegrid:2,situat:1,size:4,socket_handl:3,sockethandl:3,some:[1,2,3],sourc:[2,3,4],space:1,spatial:[0,2],specif:[1,2,3],specifi:2,speed:2,squar:2,stage:2,standard:2,start:[0,1],state:3,statement:1,static_handl:3,staticfilehandl:3,statist:1,step:[1,2,3,4],step_forward:3,store:[1,2,3,4],strictli:2,string:[2,3],structur:[3,4],studi:1,subclass:[1,2,3],subset:2,suppos:3,sure:1,surround:2,survei:1,swap:2,swap_po:2,sweep:[1,2],systemat:1,table_nam:2,take:3,taken:2,tell:4,templat:[3,4],template_path:3,text:2,textdata:3,textel:[3,4],textgrid:3,textmodul:4,textserv:3,textvisu:2,than:2,thei:[1,2,3],them:[0,1,2,3],themselv:[2,4],thi:[1,2,3],thing:1,think:3,third:2,those:1,three:[1,2],through:[1,3],tick:2,ticket:0,time:1,todo:[1,2,3,4],tool:[0,1],top:[1,2],tornado:[1,3],toroid:2,toru:2,torus_adj:2,touch:1,track:3,treat:2,tupl:2,turn:[2,3],tutori:0,two:[1,2,3],type:[1,2,3],underli:3,understand:1,unexpect:1,uniqu:1,unique_id:[1,2],unit:1,until:2,updat:[1,4],upon:2,user:[0,3,4],vacat:2,val:2,valid:4,valu:[2,3,4],var_nam:3,variabl:[1,2,3,4],variou:3,verbos:3,via:[0,3],victor:1,virtual:1,visual:0,visualization_el:3,visualizationel:[3,4],viz:3,viz_stat:3,von:2,wai:[1,2,3,4],walk:1,want:[1,2],wealth:1,web:3,websocket:3,websockethandl:3,well:[2,3,4],were:2,what:[1,3,4],when:[1,2,3],where:[2,4],whether:[2,4],which:[1,2,3,4],width:[2,3,4],window:[0,1,3],within:2,work:1,would:[0,3],wrap:[2,3],write:[1,2,3],x_max:2,x_min:2,y_max:2,y_min:2,yakovenko:1,yet:2,yield:1,you:[0,1,2],your:[1,2]},titles:["Mesa: Agent-based modeling in Python 3+","Introduction to Mesa - Tutorial","mesa package","mesa.visualization package","mesa.visualization.modules package"],titleterms:{agent:[0,1],analysi:1,back:0,base:0,batchrunn:2,build:1,canvasgridvisu:4,chartvisu:4,collect:2,content:[2,3,4],contribut:0,creat:1,data:2,datacollect:2,featur:0,indic:0,instal:1,introduct:1,mesa:[0,1,2,3,4],model:[0,1],modul:[1,2,3,4],modularserv:3,modularvisu:3,overview:1,packag:[2,3,4],python:0,sampl:1,space:2,submodul:[2,3,4],subpackag:[2,3],tabl:0,text:3,textvisu:[3,4],time:2,tutori:1,visual:[1,3,4]}}) \ No newline at end of file +Search.setIndex({envversion:47,filenames:["index","intro-tutorial","mesa","mesa.visualization","mesa.visualization.modules","overview"],objects:{"":{mesa:[2,0,0,"-"]},"mesa.Agent":{model:[2,1,1,""],step:[2,2,1,""],unique_id:[2,1,1,""]},"mesa.Model":{run_model:[2,2,1,""],running:[2,1,1,""],schedule:[2,1,1,""],seed:[2,1,1,""],step:[2,2,1,""]},"mesa.batchrunner":{BatchRunner:[2,3,1,""]},"mesa.batchrunner.BatchRunner":{agent_reporters:[2,1,1,""],agent_vars:[2,1,1,""],collect_agent_vars:[2,2,1,""],collect_model_vars:[2,2,1,""],get_agent_vars_dataframe:[2,2,1,""],get_model_vars_dataframe:[2,2,1,""],iterations:[2,1,1,""],make_iterable:[2,4,1,""],model_cls:[2,1,1,""],model_reporters:[2,1,1,""],model_vars:[2,1,1,""],parameter_values:[2,1,1,""],run_all:[2,2,1,""],run_model:[2,2,1,""]},"mesa.datacollection":{DataCollector:[2,3,1,""]},"mesa.datacollection.DataCollector":{add_table_row:[2,2,1,""],agent_reporters:[2,1,1,""],agent_vars:[2,1,1,""],collect:[2,2,1,""],get_agent_vars_dataframe:[2,2,1,""],get_model_vars_dataframe:[2,2,1,""],get_table_dataframe:[2,2,1,""],model:[2,1,1,""],model_reporters:[2,1,1,""],model_vars:[2,1,1,""],tables:[2,1,1,""]},"mesa.space":{ContinuousSpace:[2,3,1,""],Grid:[2,3,1,""],MultiGrid:[2,3,1,""],SingleGrid:[2,3,1,""],accept_tuple_argument:[2,5,1,""]},"mesa.space.ContinuousSpace":{get_distance:[2,2,1,""],get_neighbors:[2,2,1,""],move_agent:[2,2,1,""],out_of_bounds:[2,2,1,""],place_agent:[2,2,1,""],torus_adj:[2,2,1,""]},"mesa.space.Grid":{coord_iter:[2,2,1,""],default_val:[2,4,1,""],get_cell_list_contents:[2,2,1,""],get_neighborhood:[2,2,1,""],get_neighbors:[2,2,1,""],is_cell_empty:[2,2,1,""],iter_cell_list_contents:[2,2,1,""],iter_neighborhood:[2,2,1,""],iter_neighbors:[2,2,1,""],move_agent:[2,2,1,""],neighbor_iter:[2,2,1,""],out_of_bounds:[2,2,1,""],place_agent:[2,2,1,""],torus_adj:[2,2,1,""]},"mesa.space.MultiGrid":{default_val:[2,4,1,""],iter_cell_list_contents:[2,2,1,""]},"mesa.space.SingleGrid":{empties:[2,1,1,""],exists_empty_cells:[2,2,1,""],find_empty:[2,2,1,""],move_to_empty:[2,2,1,""],position_agent:[2,2,1,""]},"mesa.time":{BaseScheduler:[2,3,1,""],RandomActivation:[2,3,1,""],SimultaneousActivation:[2,3,1,""],StagedActivation:[2,3,1,""]},"mesa.time.BaseScheduler":{add:[2,2,1,""],agents:[2,1,1,""],get_agent_count:[2,2,1,""],model:[2,1,1,""],remove:[2,2,1,""],step:[2,2,1,""],steps:[2,1,1,""],time:[2,1,1,""]},"mesa.time.RandomActivation":{step:[2,2,1,""]},"mesa.time.SimultaneousActivation":{step:[2,2,1,""]},"mesa.time.StagedActivation":{shuffle:[2,1,1,""],shuffle_between_stages:[2,1,1,""],stage_list:[2,1,1,""],stage_time:[2,1,1,""],step:[2,2,1,""]},"mesa.visualization":{ModularVisualization:[3,0,0,"-"],TextVisualization:[3,0,0,"-"],modules:[4,0,0,"-"]},"mesa.visualization.ModularVisualization":{ModularServer:[3,3,1,""],PageHandler:[3,3,1,""],SocketHandler:[3,3,1,""],VisualizationElement:[3,3,1,""]},"mesa.visualization.ModularVisualization.ModularServer":{canvas_height:[3,1,1,""],canvas_width:[3,1,1,""],grid_height:[3,1,1,""],grid_width:[3,1,1,""],handlers:[3,1,1,""],launch:[3,2,1,""],local_handler:[3,1,1,""],max_steps:[3,1,1,""],model_args:[3,1,1,""],model_cls:[3,1,1,""],model_kwargs:[3,1,1,""],model_name:[3,1,1,""],page_handler:[3,1,1,""],port:[3,1,1,""],portrayal_method:[3,1,1,""],render_model:[3,2,1,""],reset_model:[3,2,1,""],run_model:[3,2,1,""],settings:[3,1,1,""],socket_handler:[3,1,1,""],static_handler:[3,1,1,""],verbose:[3,1,1,""],viz_states:[3,1,1,""]},"mesa.visualization.ModularVisualization.PageHandler":{get:[3,2,1,""]},"mesa.visualization.ModularVisualization.SocketHandler":{check_origin:[3,2,1,""],on_message:[3,2,1,""],open:[3,2,1,""]},"mesa.visualization.ModularVisualization.VisualizationElement":{js_code:[3,1,1,""],local_includes:[3,1,1,""],package_includes:[3,1,1,""],render:[3,2,1,""],render_args:[3,1,1,""]},"mesa.visualization.TextVisualization":{TextData:[3,3,1,""],TextElement:[3,3,1,""],TextGrid:[3,3,1,""],TextVisualization:[3,3,1,""]},"mesa.visualization.TextVisualization.TextData":{render:[3,2,1,""]},"mesa.visualization.TextVisualization.TextElement":{render:[3,2,1,""]},"mesa.visualization.TextVisualization.TextGrid":{converter:[3,4,1,""],grid:[3,1,1,""],render:[3,2,1,""]},"mesa.visualization.TextVisualization.TextVisualization":{elements:[3,1,1,""],model:[3,1,1,""],render:[3,2,1,""],step:[3,2,1,""],step_forward:[3,2,1,""]},"mesa.visualization.modules":{CanvasGridVisualization:[4,0,0,"-"],ChartVisualization:[4,0,0,"-"],TextVisualization:[4,0,0,"-"]},"mesa.visualization.modules.CanvasGridVisualization":{CanvasGrid:[4,3,1,""]},"mesa.visualization.modules.CanvasGridVisualization.CanvasGrid":{canvas_height:[4,1,1,""],canvas_width:[4,1,1,""],package_includes:[4,1,1,""],portrayal_method:[4,1,1,""],render:[4,2,1,""]},"mesa.visualization.modules.ChartVisualization":{ChartModule:[4,3,1,""]},"mesa.visualization.modules.ChartVisualization.ChartModule":{canvas_height:[4,1,1,""],canvas_width:[4,1,1,""],data_collector_name:[4,1,1,""],package_includes:[4,1,1,""],render:[4,2,1,""],series:[4,1,1,""]},"mesa.visualization.modules.TextVisualization":{TextElement:[4,3,1,""]},"mesa.visualization.modules.TextVisualization.TextElement":{js_code:[4,1,1,""],package_includes:[4,1,1,""]},mesa:{Agent:[2,3,1,""],Model:[2,3,1,""],batchrunner:[2,0,0,"-"],datacollection:[2,0,0,"-"],space:[2,0,0,"-"],time:[2,0,0,"-"],visualization:[3,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","attribute","Python attribute"],"2":["py","method","Python method"],"3":["py","class","Python class"],"4":["py","staticmethod","Python static method"],"5":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:attribute","2":"py:method","3":"py:class","4":"py:staticmethod","5":"py:function"},terms:{"10x10":1,"1px":1,"__init__":1,"__str__":3,"boolean":[1,2],"case":1,"catch":1,"class":[1,2,3,4,5],"default":[1,2,3,4],"export":1,"final":[1,2],"function":[1,2,3,4],"import":[1,2],"int":1,"new":[1,2,4],"public":1,"return":[1,2,3,4],"short":1,"static":[1,2,3],"throw":2,"true":[1,2,3,4],"try":1,"var":1,"while":1,aa08f8:4,aaaaaa:3,abl:[1,4],abm:[0,1,2],about:1,abov:[0,1,4],accept:[2,4],accept_tuple_argu:2,access:[1,3],accordingli:3,account:2,across:1,act:[1,2,3],action:1,activ:[1,2,5],actual:[1,2,3],add:[0,1,2],add_table_row:2,addit:[1,2],addition:2,adedd:3,adjust:2,adrian:1,advanc:[1,2,3],advantag:1,after:1,afterward:1,again:1,agent_count:1,agent_id:2,agent_portray:1,agent_report:[1,2],agent_var:2,agent_wealth:1,agentid:1,ahead:[],alien:1,all:[1,2,3,4],all_wealth:1,allow:[0,1,2,3],almost:1,alon:3,along:[1,3],alreadi:1,also:[1,2,3,5],altern:0,alwai:1,amount:1,analysi:[],analyt:1,analyz:[0,1],ani:[1,2,5],anoth:1,anyth:[1,3],apache2:0,app:3,appear:1,append:[1,2],appli:[1,2],applic:3,approach:1,appropri:2,arbitrari:[1,2],architectur:[],aren:1,arg:[2,3],argument:[1,2],around:[1,3],arrai:1,arxiv:1,ascii:3,ask:2,assign:1,associ:[1,2,3],assum:[2,3],assumpt:2,attribut:[1,3,4],audienc:1,autom:[1,5],automat:[1,2,5],averag:[1,2],awai:1,axi:4,bar:1,baseschedul:2,basi:3,basic:[1,3,5],batch:[],batch_run:1,becaus:1,becom:1,been:1,befor:[1,2],begin:1,behav:[1,5],behavior:[1,2,5],below:[1,3],better:1,between:[2,3],bin:1,bit:[],black:[1,4],board:1,bodi:1,boilerpl:1,boltzmann_wealth_model:1,border:1,both:[1,2,4],bottom:[1,2],bound:2,brand:1,broke:1,browser:[0,1,3,5],bug:1,build:[],built:[0,1,3,4,5],button:[1,3],call:[1,2,3,4],can:[1,2,3,4,5],canva:[1,3,4],canvas_height:[1,3,4],canvas_modul:4,canvas_tag:1,canvas_width:[1,3,4],canvasgrid:[1,3,4],canvasgridvisu:[],canvasmodul:4,canvasvi:3,care:1,categori:1,cell:[1,2,3,4],cell_cont:1,cell_list:[],cell_wealth:[],cellmat:1,center:[1,2],certain:2,certainli:1,chang:[],charact:3,chart:[],chart_modul:4,chartmodul:[1,4],chartvisu:[],check:[0,1,2],check_origin:3,chess:1,child:[1,3],choic:1,circl:[1,3,4],clear:1,click:[1,3],client:[],clock:2,code:[],coder:1,coeffici:1,collect_agent_var:2,collect_model_var:2,collector:[1,5],color:[1,3,4],colorbar:1,column:2,column_nam:2,com:1,combin:[1,2],come:[1,3],comfort:1,command:1,common:1,compar:1,compat:1,complet:2,compon:[],compos:3,comprehens:1,comput:1,compute_gini:1,concept:[2,3],concurr:3,cond:1,condit:[1,2],confus:1,connect:[1,3],consist:[1,2,3],constructor:1,contain:[1,2,4,5],content:[],context:1,continu:[1,2],continuousspac:2,contrast:1,contributor:0,control:[1,2,3,5],converg:1,convert:[2,3],coord:2,coord_it:[1,2],coordin:[1,2,4],core:[0,1,2],correspond:[2,3,4,5],could:1,count:[1,3],coupl:1,cover:1,creat:[],create_ag:[],current:[1,2,3,4],custom:[0,4],data:[],data_collector_nam:[1,4],datacollector:[1,2,4],datafram:[1,2],dataset:1,deal:1,debug:3,decor:2,def:1,default_v:2,defin:[1,3,4],demonstr:1,depend:1,describ:[1,4,5],design:1,desir:1,despit:1,destroi:1,determin:[2,4],develop:1,diagon:[1,2],dictionari:[1,2,3,4],did:1,differ:[1,5],dim_len:2,directli:[1,2,5],directori:[1,3],discret:2,discuss:[0,1],displai:[3,5],distanc:2,distribut:1,div:3,divid:[1,2],dmasad:3,doc:[0,1],document:1,doe:[1,2],doesn:1,dollar:1,don:1,done:1,dot:1,down:[1,2],dragulescu2002:1,draw:[1,4],drawn:[1,4],drive:1,drăgulescu:1,due:1,dynam:5,each:[1,2,3,4,5],easi:[1,5],easier:1,easiest:1,easili:1,economi:1,econophys:1,edg:[1,2],editor:1,effici:1,either:[1,4],element:[1,2,3,4],els:[1,2],email:0,emerg:[1,5],empti:[1,2,3],empty_model:1,end:[1,2,3,4],end_wealth:1,enforc:[1,2],enough:1,ensur:[1,2],enter:1,entir:[2,4],enumer:1,environ:1,equal:[1,2],equival:2,error:[1,2],especi:1,essenti:1,even:1,event:3,everi:[1,2,5],everyon:1,everyth:1,exact:2,exactli:[2,3],exampl:[1,2,3,4],except:2,exchang:1,exclud:2,execut:[1,2],exercis:1,exists_empty_cel:2,expect:[1,3],explain:1,explan:1,explic:2,explicitli:[1,2],extens:2,extern:3,extract:1,fact:1,fals:[1,2,4],familiar:1,far:1,favorit:1,felt:1,few:1,fewer:1,figur:1,file:[0,1,3],fill:[1,2,3,4],fillcolor:1,find:1,find_empti:2,first:[],fit:1,fix:1,folder:1,follow:[0,1,3,4,5],forc:2,forget:1,form:2,format:1,forward:3,fraction:[2,4],frame:1,framework:[0,1,2],frequent:1,from:[1,2,3,4,5],front:4,full:1,funtion:[],gener:[1,2,3,4,5],get:[0,1,2,3,5],get_agent_count:2,get_agent_vars_datafram:[1,2],get_cell_list_cont:[1,2],get_dist:2,get_model_vars_datafram:[1,2],get_neighbor:2,get_neighborhood:[1,2],get_step:3,get_table_datafram:2,getattr:3,getcontext:1,gini:1,github:[0,1],give:1,give_monei:1,given:[1,2,3],global:1,goal:[0,1],good:1,graph:1,graphic:1,graphvi:3,grei:1,grid:[],grid_height:[2,3,4],grid_width:[2,3,4],griddraw:4,guid:0,had:1,half:1,hand:1,handi:1,handl:[1,2,3,4,5],handler:3,happi:[1,4],hard:1,have:[1,2,3,4],haven:[],head:1,height:[1,2,3,4],help:0,helper:2,here:[1,2,5],higher:[1,4],highlightfil:1,highlightstrok:1,hist:1,histogram:1,histogrammodul:1,hit:1,hold:[1,2,3],hood:1,hopefulli:1,how:[1,3,5],howev:[1,2],html5:[1,3],html:[3,4],http:1,hypothes:1,idea:1,identifi:1,ignore_miss:2,illustr:1,imag:5,imagin:1,impact:2,implement:[0,1,2],implicitli:2,imshow:1,includ:[1,2,3,5],include_cent:[1,2],incom:1,increment:2,index:[0,1,2,3],ineffici:1,inequ:1,inform:[3,4],inherit:1,initi:1,input:[1,3,4],insert:[1,5],insid:[1,3],insight:1,instal:[],instanc:[2,3],instanti:[1,2,3,5],instead:[1,2],instruct:1,integ:1,intend:1,interact:1,interest:1,interfac:[0,1,3],intern:[1,2,3],interpol:1,interpret:1,intro:0,introduct:[],intuit:1,involv:2,ipython:[0,1,3],is_cell_empti:2,isn:1,issu:[0,1],item:2,iter:[1,2],iter_cell_list_cont:2,iter_neighbor:2,iter_neighborhood:2,itself:[1,5],javascript:[1,3,4,5],jqueri:1,js_code:[1,3,4],json:[1,3,4],jump:2,jupyt:1,just:1,keep:[1,2,3],kei:[2,4],kind:1,know:[1,3],knowledg:5,kwarg:3,label:[1,4],lambda:1,languag:1,last:1,launch:[1,3],layer:[1,3,4],least:[1,4],leav:1,left:[1,2],len:1,let:[1,5],level:[1,2,4,5],librari:1,licens:0,like:[0,1],line:[1,4],list:[0,1,2,3,4],listen:1,littl:[],live:[1,4],load:1,local:3,local_handl:3,local_includ:[1,3],locat:2,look:1,lookup:2,loop:[1,2,3],lot:1,lower:4,mai:[1,2,3],main:[1,3],maintain:1,make:[1,2,5],make_iter:2,manag:[1,2],mani:[1,2],map:[1,2,3],mason:[0,2],mat:1,match:1,mate:1,matplotlib:1,max:[1,2],max_step:[1,2,3],maximum:1,mean:[1,2,5],meant:[2,3],measur:1,mechan:1,mention:1,messag:3,method:[1,2,3,4],might:1,min:[1,4],mind:1,minim:3,miss:2,mkdir:[],model_arg:3,model_cl:[2,3],model_kwarg:3,model_nam:3,model_report:[1,2],model_var:2,modifi:[1,2],modul:[],modular:[0,3,4,5],modular_templ:1,modularserv:[],modularvisu:[],moment:4,monei:1,moneyag:1,moneymodel:1,moneymodel_viz:1,moor:[1,2],more:[0,1,2,3,4,5],most:[0,1,2,4],move:[1,2],move_ag:[1,2],move_to_empti:2,movement:1,mozilla:1,much:1,multigrid:[1,2],multilay:3,multipl:[1,5],must:[1,2,4],mymodel:3,name:[1,2,3,4],nearbi:1,nearest:1,necessari:[1,2,3],need:[1,2,3,4,5],neighbor:[1,2],neighbor_it:2,neighborhood:[1,2],nest:[1,2],netlogo:[0,2],network:5,neumann:[1,2],new_el:1,new_posit:1,next:[1,2,3],nois:1,non:2,none:[2,3,4],note:[1,2],notebook:[0,1,3],notic:1,now:1,num_ag:1,number:[1,2,4],numpi:1,object:[1,2,3,4,5],observ:5,obvious:1,occupi:2,off:[1,2],offer:1,often:1,old:2,on_messag:3,onc:[1,2],one_agent_wealth:1,onli:[1,2,3],open:[1,3],oppos:3,opposit:1,option:[1,3,4],order:[1,2,3],org:1,orient:1,origin:3,other:[1,2,3],other_ag:1,otherwis:[1,2],our:[1,3],out:[0,1,2,5],out_of_bound:2,outcom:1,output:1,outsid:1,over:[1,2,3],overal:[1,3],overload:[2,3],own:[],packag:[],package_includ:[1,3,4],page:[1,3,4],page_handl:3,pagehandl:3,pair:[1,2],panda:[1,2],panel:1,paramet:[1,2,3,5],parameter_valu:2,parent:3,pars:3,part:[1,3],particular:[1,2,3,4,5],pass:[1,2,3,4],patch:3,path:[1,3],pattern:1,paus:1,peopl:1,per:[1,2],pick:[1,2],piec:[1,3],pip:[0,1],pixel:[1,4],place:[1,2,4],place_ag:[1,2],plain:1,pleas:[0,1],plot:[1,4],plt:1,plug:1,point:[1,2],popul:1,port:[1,3],portray:[1,4],portrayal_method:[3,4],pos_1:2,pos_2:2,posit:[1,2,3],position_ag:2,possibl:[0,1],possible_step:1,potenti:[1,3],practic:1,pre:5,prep:1,prepar:1,preprint:1,prese:1,present:1,press:1,prevent:1,primarili:3,print:[1,3],probabl:1,problem:1,produc:[3,4],program:3,progress:1,project:1,projectmesa:[1,3],properti:[1,2,3],protocol:3,provid:[1,2,3,4,5],pull:[0,1],purpos:1,push:[1,4],put:1,pypi:0,pyplot:1,python3:1,queue:2,quick:[1,3],quickli:0,radiu:[2,4],rais:2,random:[1,2],randomactiv:[1,2],randrang:1,rang:1,rate:1,rather:2,raw:3,reach:[0,1,2],read:1,readi:[1,3,4],readili:3,readm:1,receiv:[1,3],recent:4,recommend:1,record:5,rect:4,rectangl:4,red:[1,4],refer:[],reflect:[],refresh:1,regim:[1,2,5],reinstanti:3,relev:[1,2,4],reli:1,rememb:1,remov:2,render:[1,3,4],render_arg:3,render_model:3,repast:0,replic:2,report:[1,2],repositori:1,repres:[1,2,3],request:[0,3],requesthandl:3,requir:[1,2],reset:[1,3],reset_model:3,reshuffl:2,resid:1,resourc:0,rest:[3,5],restart:1,result:[0,1,2,3],retriev:4,rewrit:1,rgba:1,right:[1,2],row:[1,2],rudimentari:1,rule:1,run:[],run_al:[1,2],run_data:1,run_model:[2,3],runner:[1,5],same:[1,2,4],scalebeginsatzero:1,scatter:1,schedul:[],schell:0,schelling_chart:4,screen:1,search:2,second:[1,3],section:1,see:[1,5],seed:2,segreg:0,select:2,self:1,send:[1,3],sens:1,sent:3,separ:[1,2],seri:[1,4],seriou:2,serv:5,server:[],session:1,set:[],settabl:4,setup:1,sever:[2,3],shape:[1,3,4],share:1,should:[1,3],show:[1,3],shown:1,shuffl:[1,2],shuffle_between_stag:2,side:[],sign:1,similarli:1,simpl:[1,2,3],simpler:1,simplest:[1,2],simpli:[1,2],simplic:1,simul:[1,2],simultan:2,simultaneousactiv:2,sinc:1,singl:2,singlegrid:[1,2],situat:5,size:[1,4],slider:1,slightli:1,small:1,smaller:1,smooth:1,smoother:1,socket_handl:3,sockethandl:3,some:[1,2,3,5],someon:1,someth:1,sometim:1,soon:1,sort:1,sourc:[1,2,3,4],space:[],spatial:[0,1,2],special:1,specif:[1,2,3],specifi:2,speed:[1,2],spot:1,squar:2,stage:2,stage_list:2,stage_tim:2,stagedactiv:2,standard:2,start:[0,1],state:[1,3],statement:[],static_handl:3,staticfilehandl:3,statist:1,step:[],step_forward:3,still:1,stop:1,storag:1,store:[1,2,3,4,5],strictli:2,string:[1,2,3],strokecolor:1,structur:[1,3,4],studi:5,style:1,subclass:[2,3],subset:2,sum:1,support:1,suppos:3,sure:[1,5],surpris:1,surround:2,survei:1,swap:2,swap_po:2,sweep:[2,5],syntax:1,systemat:5,table_nam:2,tag:1,take:[1,2,3],taken:2,tandem:1,tell:[1,4],templat:[1,3,4],template_path:3,termin:1,text:[],textdata:3,textel:[3,4],textgrid:3,textmodul:4,textserv:3,textvisu:[],than:[1,2],thei:[1,2,3,5],them:[0,1,2,3],themselv:[2,4],thi:[1,2,3],thing:1,think:3,third:2,those:1,three:[1,2,5],through:[1,3],tick:[1,2],ticket:0,time:[],titl:1,todo:[1,2,3,4,5],togeth:1,too:1,tool:[0,1,5],top:[1,2],toriod:[],tornado:3,toroid:[1,2],toru:2,torus_adj:2,touch:[],toward:1,track:[1,2,3],treat:[1,2],tupl:[1,2],turn:[1,2,3],tutori:[],two:[1,2,3],type:[1,2,3,5],unclear:1,uncom:1,under:1,underli:3,underneath:1,understand:1,unexpect:1,unfamiliar:1,uniqu:1,unique_id:[1,2],unit:[1,2],unlik:1,until:2,updat:[1,4],upon:2,url:1,user:[0,1,2,3,4],usual:1,vacat:2,val:2,valid:4,valu:[1,2,3,4],var_nam:3,vari:1,variabl:[1,2,3,4],variou:3,verbos:3,veri:1,via:[0,1,3],victor:1,view:1,virtual:1,visual:[],visualization_el:3,visualizationel:[1,3,4],viz:3,viz_stat:3,von:[1,2],wai:[1,2,3,4,5],walk:1,want:[1,2,5],watch:1,wealth:1,wealth_grid:[],wealth_val:1,web:[1,3],websocket:3,websockethandl:3,well:[1,2,3,4],were:[1,2],what:[1,3,4],when:[1,2,3],where:[1,2,4],whether:[1,2,4],which:[1,2,3,4,5],who:1,whole:1,width:[1,2,3,4],window:[0,1,3,5],within:2,won:1,work:1,worri:1,would:[0,1,3],wrap:[1,2,3],wrapped_funct:2,write:[1,2,3,5],written:1,wrong:1,wrote:1,x_max:2,x_min:2,y_max:2,y_min:2,yakovenko:1,yet:[1,2],yield:1,you:[0,1,2,5],your:[],yourself:1,zero:1},titles:["Mesa: Agent-based modeling in Python 3+","Introduction to Mesa - Tutorial","mesa package","mesa.visualization package","mesa.visualization.modules package","Overview of Modules"],titleterms:{agent:[0,1],analysi:5,back:0,base:0,batch:1,batchrunn:2,build:1,canvasgridvisu:4,chang:1,chart:1,chartvisu:4,client:1,code:1,collect:[1,2],compon:1,content:[1,2,3,4],contribut:0,creat:[],data:[1,2],datacollect:2,descript:1,featur:0,first:1,grid:1,indic:0,instal:1,introduct:1,mesa:[0,1,2,3,4],model:[0,1,5],modul:[2,3,4,5],modularserv:3,modularvisu:3,overview:5,own:1,packag:[2,3,4],python:0,run:1,sampl:1,schedul:1,server:1,set:1,side:1,space:[1,2],step:1,submodul:[2,3,4],subpackag:[2,3],tabl:0,text:3,textvisu:[3,4],time:2,tutori:1,visual:[1,3,4,5],your:1}}) \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 057f4523395..84d24bc36fe 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,6 +16,20 @@ import sys import os + +# Adding mock imports to see if this builds +from unittest.mock import MagicMock + +class Mock(MagicMock): + @classmethod + def __getattr__(cls, name): + return Mock() + +MOCK_MODULES = ['scipy', 'numpy', 'pandas'] +sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) + +# End of mock + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. diff --git a/docs/images/tutorial/br_ginis.png b/docs/images/tutorial/br_ginis.png new file mode 100644 index 00000000000..2a8c0e2f211 Binary files /dev/null and b/docs/images/tutorial/br_ginis.png differ diff --git a/docs/images/tutorial/dc_endwealth.png b/docs/images/tutorial/dc_endwealth.png new file mode 100644 index 00000000000..41d3e74f11c Binary files /dev/null and b/docs/images/tutorial/dc_endwealth.png differ diff --git a/docs/images/tutorial/dc_gini.png b/docs/images/tutorial/dc_gini.png new file mode 100644 index 00000000000..35d6486a5ba Binary files /dev/null and b/docs/images/tutorial/dc_gini.png differ diff --git a/docs/images/tutorial/dc_oneagent.png b/docs/images/tutorial/dc_oneagent.png new file mode 100644 index 00000000000..d0a6e62d589 Binary files /dev/null and b/docs/images/tutorial/dc_oneagent.png differ diff --git a/docs/images/tutorial/first_hist.png b/docs/images/tutorial/first_hist.png new file mode 100644 index 00000000000..95481e55612 Binary files /dev/null and b/docs/images/tutorial/first_hist.png differ diff --git a/docs/images/tutorial/multirun_hist.png b/docs/images/tutorial/multirun_hist.png new file mode 100644 index 00000000000..d74884f1884 Binary files /dev/null and b/docs/images/tutorial/multirun_hist.png differ diff --git a/docs/images/tutorial/numpy_grid.png b/docs/images/tutorial/numpy_grid.png new file mode 100644 index 00000000000..59af6c8aa5f Binary files /dev/null and b/docs/images/tutorial/numpy_grid.png differ diff --git a/docs/images/tutorial/viz_chart.png b/docs/images/tutorial/viz_chart.png new file mode 100644 index 00000000000..bce01f1ae37 Binary files /dev/null and b/docs/images/tutorial/viz_chart.png differ diff --git a/docs/images/tutorial/viz_empty.png b/docs/images/tutorial/viz_empty.png new file mode 100644 index 00000000000..8089d7371b1 Binary files /dev/null and b/docs/images/tutorial/viz_empty.png differ diff --git a/docs/images/tutorial/viz_greycircles.png b/docs/images/tutorial/viz_greycircles.png new file mode 100644 index 00000000000..18c6c70f5c7 Binary files /dev/null and b/docs/images/tutorial/viz_greycircles.png differ diff --git a/docs/images/tutorial/viz_histogram.png b/docs/images/tutorial/viz_histogram.png new file mode 100644 index 00000000000..e2110bb3129 Binary files /dev/null and b/docs/images/tutorial/viz_histogram.png differ diff --git a/docs/images/tutorial/viz_redcircles.png b/docs/images/tutorial/viz_redcircles.png new file mode 100644 index 00000000000..057aded4a0b Binary files /dev/null and b/docs/images/tutorial/viz_redcircles.png differ diff --git a/docs/index.rst b/docs/index.rst index 8c419ec01a5..8fcd2466ecd 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -74,7 +74,7 @@ If you would like to add a feature, please reach out via `ticket`_ or the `email .. toctree:: :maxdepth: 1 - Mesa overview + Mesa overview intro-tutorial diff --git a/docs/intro-tutorial.rst b/docs/intro-tutorial.rst index 2610e41f156..423f876f93c 100644 --- a/docs/intro-tutorial.rst +++ b/docs/intro-tutorial.rst @@ -1,23 +1,34 @@ Introduction to Mesa - Tutorial -================================ +********************************* -Getting started with Mesa is easy. In this doc, we will present Mesa’s architecture and core features. To illustrate them, we'll walk you through building a simple agent-based model, drawn from econophysics and presenting a statistical mechanics approach to wealth distribution [Dragulescu2002]_. +`Mesa `_ is a Python framework for `agent-based modeling `_. +Getting started with Mesa is easy. In this tutorial, we will walk through creating a simple model and progressively add functionality which will illustrate Mesa's core features. -The rules of our tutorial model: +**Note:** *This tutorial is a work-in-progress. If you find any errors or bugs, or just find something unclear or confusing, `let us know `_!* -- There are some number of agents. -- All agents begin with 1 unit of money. -- Every step of the model, an agent gives 1 unit of money (if they have it) to some other agent. +The base for this tutorial is a very simple model of agents exchanging money. Next, we add *space* to allow agents move. Then, we'll cover two of Mesa's analytic tools: the *data collector* and *batch runner*. After that, we'll add an *interactive visualization* which lets us watch the model as it runs. Finally, we go over how to write your own visualization module, for users who are comfortable with JavaScript. + +You can also find all the code this tutorial describes in the **examples/Tutorial-Boltzmann_Wealth_Model** directory of the Mesa repository. + +.. contents:: Tutorial Contents + +Sample Model Description +------------------------ + +The tutorial model is a very simple simulated agent-based economy, drawn from econophysics and presenting a statistical mechanics approach to wealth distribution [Dragulescu2002]_. The rules of our tutorial model: + +1. There are some number of agents. +2. All agents begin with 1 unit of money. +3. At every step of the model, an agent gives 1 unit of money (if they have it) to some other agent. Despite its simplicity, this model yields results that are often unexpected to those not familiar with it. For our purposes, it also easily demonstrates Mesa's core features. Let's get started. - Installation ------------ -The first thing you need to do is to install Mesa. We recommend doing this in a `virtual environment`_. Make sure your work space is pointed to Python 3. Mesa requires Python3 and does not work in < Python 3 environments. +To start, install Mesa. We recommend doing this in a `virtual environment `_, but make sure your environment is set up with Python 3. Mesa requires Python3 and does not work in Python 2 environments. To install Mesa, simply: @@ -25,182 +36,858 @@ To install Mesa, simply: $ pip install mesa -When you do that, it will install the following packages and dependencies. +When you do that, it will install Mesa itself, as well as any dependencies that aren't in your setup yet. -- mesa -- tornado -- numpy -- pandas +Building a sample model +------------------------ -Overview of Modules ------------- +Once Mesa is installed, you can start building our model. You can write models in two different ways: -There are three module types in Mesa. +1. Write the code in its own file with your favorite text editor, or +2. Write the model interactively in `Jupyter Notebook `_ cells. -1. modeling -2. analysis -3. visualization +Either way, it's good practice to put your model in its own folder -- especially if the project will end up consisting of multiple files (for example, Python files for the model and the visualization, a Notebook for analysis, and a Readme with some documentation and discussion). -TODO: Insert image +Begin by creating a folder, and either launch a Notebook or create a new Python source file. We will use the name ``MoneyModel.py`` here. -Modeling modules -~~~~~~~~~~~~~~ +Setting up the model +~~~~~~~~~~~~~~~~~~~~~ -To build a model, you need the following: +To begin writing the model code, we start with two core classes: one for the overall model, the other for the agents. The model class holds the model-level attributes, manages the agents, and generally handles the global level of our model. Each instantiation of the model class will be a specific model run. Each model will contain multiple agents, all of which are instantiations of the agent class. Both the model and agent classes are child classes of Mesa's generic ``Model`` and ``Agent`` classes. -* **Model class** to store the model-level parameters and serve as a container for the rest of the components. +Each agent has only one variable: how much wealth it currently has. (Each agent will also have a unique identifier (i.e., a name), stored in the ``unique_id`` variable. Giving each agent a unique id is a good practice when doing agent-based modeling.) -* **Agent class(es)** which describe the model agents. +There is only one model-level parameter: how many agents the model contains. When a new model is started, we want it to populate itself with the given number of agents. We also set the model's ``running`` property to True; this should be set by default, then changed to False if the model reaches an end condition. -* **Scheduler** which controls the agent activation regime, and handles time in the model in general. +The beginning of both classes looks like this: -* **space** components describing the space and/or network the agents are situated in (if any). +.. code-block:: python + from mesa import Agent, Model -Analysis modules -~~~~~~~~~~~~~~ + class MoneyAgent(Agent): + """An agent with fixed initial wealth.""" + def __init__(self, unique_id): + self.unique_id = unique_id + self.wealth = 1 -Not every model *needs* these modules, but they provide useful tools for getting data out of your model runs to study more systematically. + class MoneyModel(Model): + """A model with some number of agents.""" + def __init__(self, N): + self.running = True + self.num_agents = N + # Create agents + for i in range(self.num_agents): + a = MoneyAgent(i) -* **Data collectors** are used to record data from each model run. -* **Batch runners** automate multiple runs and parameter sweeps -- running the model with different parameters, to see how they change its behavior. +Adding the scheduler +~~~~~~~~~~~~~~~~~~~~~ -Visualization modules -~~~~~~~~~~~~~~ +Time in most agent-based models moves in steps, sometimes also called **ticks**. At each step of the model, one or more of the agents -- usually all of them -- are activated and take their own step, changing internally and/or interacting with one another or the environment. -A visualization lets you directly observe model runs, seeing the dynamics that emerge from it and making sure that it's behaving in the way you want it to. Mesa handles visualizations in a browser window, using JavaScript. It provides a set of pre-built components, which can be instantiated for a particular model in Python and automatically generate the corresponding objects in the browser window. It's also easy to write your own components with some basic JavaScript knowledge. +The **scheduler** is a special model component which controls the order in which agents are activated. For example, all the agents may activate in the same order every step; their order might be shuffled; we may try to simulate all the agents acting at the same time; and more. Mesa offers a few different built-in scheduler classes, with a common interface. That makes it easy to change the activation regime a given model uses, and see whether it changes the model behavior. This may not seem important, but scheduling patterns can have an impact on your results [Comer2014]_. -Some visualization modules we'll use here include: +For now, let's use one of the simplest ones: ``RandomActivation``, which activates all the agents once per step, in random order. Every agent is expected to have a ``step`` method, which takes a model object as its only argument -- this is the agent's action when it is activated. We add an agent to the schedule using the ``add`` method; when we call the schedule's ``step`` method, it shuffles the order of the agents, then activates them all, one at a time. -* **Grid** visualization, -* **Chart** display module, -* The **ModularServer** itself. +With that in mind, the model code with the scheduler added looks like this: -Building a sample model ------------- +.. code-block:: python -Now that we understand a little bit about the components, let's use those components to build a model. + from mesa import Agent, Model + from mesa.time import RandomActivation -First, we need a place to put our model. Let's create a directory for our model for good practice. In this tutorial, we will call this 'mesa-example'. + class MoneyAgent(Agent): + """ An agent with fixed initial wealth.""" + def __init__(self, unique_id): + self.unique_id = unique_id + self.wealth = 1 -.. code-block:: bash + def step(self, model): + # The agent's step will go here. + pass - mkdir mesa-example - cd mesa-example + class MoneyModel(Model): + """A model with some number of agents.""" + def __init__(self, N): + self.running = True + self.num_agents = N + self.schedule = RandomActivation(self) + # Create agents + for i in range(self.num_agents): + a = MoneyAgent(i) + self.schedule.add(a) -Create a file to store your sample model. Let's call it money.py. + def step(self): + '''Advance the model by one step.''' + self.schedule.step() -.. code-block:: bash - touch moneymodel.py +At this point, we have a model which runs -- it just doesn't do anything. You can see for yourself with a few easy lines. If you've been working in an interactive session, you can create a model object directly. Otherwise, you need to open an interactive session in the same directory as your source code file, and import the classes. For example, if your code is in ``MoneyModel.py``: + +.. code-block::python + + from MoneyModel import MoneyModel + +Then create the model object, and run it for one step: + +.. code-block:: python + + empty_model = MoneyModel(10) + empty_model.step() + +**Exercise:** Try modifying the code above to have every agent print out its ``unique_id`` when it is activated. Run a few steps of the model to see how the agent activation order is shuffled each step. + +Agent step +~~~~~~~~~~ + +Now we just need to have the agents do what we intend for them to do: check their wealth, and if they have the money, give one unit of it away to another random agent. Since we want to use randomness, don't forget to import Python's ``random`` library: + +.. code-block:: python + + import random + +To pick an agent at random, we need a list of all agents. Notice that there isn't such a list explicitly in the model. The scheduler, however, does have an internal list of all the agents it is scheduled to activate. + +With that in mind, we rewrite the agent's ``step`` method, like this: + +.. code-block:: python + + class MoneyAgent(Agent): + # ... + def step(self, model): + if self.wealth == 0: + return + other_agent = random.choice(model.schedule.agents) + other_agent.wealth += 1 + self.wealth -= 1 + + +Running your first model +~~~~~~~~~~~~~~~~~~~~~~~~~ + +With that last piece in hand, it's time for the first rudimentary run of the model. + +If you've written the code in its own file (``MoneyModel.py`` or a different name), launch an interpreter in the same directory as the file (either the plain Python command-line interpreter, or the IPython interpreter), or launch a Jupyter Notebook there. Then import the classes you created. (If you wrote the code in a Notebook, obviously this step isn't necessary). + +.. code-block:: python + + from MoneyModel import * + +Now let's create a model with 10 agents, and run it for 10 steps. + +.. code-block:: python + + model = MoneyModel(10) + for i in range(10): + model.step() -In the editor of your choice, open moneymodel.py. +Next, we need to get some data out of the model. Specifically, we want to see the distribution of the agent's wealth. We can get the wealth values with list comprehension, and then use matplotlib (or another graphics library) to visualize the data in a histogram. -To begin building the example model described at the top of this page -- we first *subclass two classes: one for the model object itself and one the model agents*. +.. code-block:: python + # Put this import at the top of the file + import matplotlib.pyplot as plt -Creating the model -~~~~~~~~~~~~~~ + agent_wealth = [a.wealth for a in model.schedule.agents] + plt.hist(agent_wealth) -The first we do is import the ``Model`` base class. +If you are running from a text editor or IDE, you'll also need to add this line, to make the graph appear. .. code-block:: python - from mesa import Model + plt.show() + +You'll probably see something like the distribution shown below. Yours will almost certainly look at least slightly different, since each run of the model is random, after all. + +.. image:: images/tutorial/first_hist.png + :width: 50% + :scale: 100% + :alt: Histogram of agent wealths after 10 steps. + :align: center -Then we subclass and instance of the Money Model. The model itself will have some number of agents and will have a funtion to create our agents. + +To get a better idea of how a model behaves, we can create multiple model runs and see the distribution that emerges from all of them. We can do this with a nested for loop: .. code-block:: python - class MoneyModel(Model): + all_wealth = [] + for j in range(100): + # Run the model + model = MoneyModel(10) + for i in range(10): + model.step() + # Store the results + for agent in model.schedule.agents: + all_wealth.append(agent.wealth) + + plt.hist(all_wealth, bins=range(max(all_wealth)+1)) + +.. image:: images/tutorial/multirun_hist.png + :width: 50% + :scale: 100% + :alt: Histogram of agent wealths after 10 steps, from 100 model runs. + :align: center + + +This runs 100 instantiations of the model, and runs each for 10 steps. (Notice that we set the histogram bins to be integers, since agents can only have whole numbers of wealth). This distribution looks a lot smoother. By running the model 100 times, we smooth out some of the 'noise' of randomness, and get to the model's overall expected behavior. + +This outcome might be surprising. Despite the fact that all agents, on average, give and receive one unit of money every step, the model converges to a state where most agents have a small amount of money and a small number have a lot of money. + +Adding space +~~~~~~~~~~~~~ + +Many ABMs have a spatial element, with agents moving around and interacting with nearby neighbors. Mesa currently supports two overall kinds of spaces: grid, and continuous. Grids are divided into cells, and agents can only be on a particular cell, like pieces on a chess board. Continuous space, in contrast, allows agents to have any arbitrary position. Both grids and continuous spaces are frequently `toroidal `_, meaning that the edges wrap around, with cells on the right edge connected to those on the left edge, and the top to the bottom. This prevents some cells having fewer neighbors than others, or agents being able to go off the edge of the environment. + +Let's add a simple spatial element to our model by putting our agents on a grid and make them walk around at random. Instead of giving their unit of money to any random agent, they'll give it to an agent on the same cell. + +Mesa has two main types of grids: ``SingleGrid`` and ``MultiGrid``. ``SingleGrid`` enforces at most one agent per cell; ``MultiGrid`` allows multiple agents to be in the same cell. Since we want agents to be able to share a cell, we use ``MultiGrid``. + +.. code-block:: python + + from mesa.space import MultiGrid + +We instantiate a grid with height and width parameters, and a boolean as to whether the grid is toroidal. Let's make width and height model parameters, in addition to the number of agents, and have the grid always be toroidal. We can place agents on a grid with the grid's ``place_agent`` method, which takes an agent and an (x, y) tuple of the coordinates to place the agent. + +.. code-block:: python + + class MoneyModel(Model): """A model with some number of agents.""" - def __init__(self, N): - self.num_agents = N + def __init__(self, N, width, height): + self.running = True + self.num_agents = N + self.grid = MultiGrid(height, width, True) + self.schedule = RandomActivation(self) + # Create agents + for i in range(self.num_agents): + a = MoneyAgent(i) + self.schedule.add(a) + # Add the agent to a random grid cell + x = random.randrange(self.grid.width) + y = random.randrange(self.grid.height) + self.grid.place_agent(a, (x, y)) + +Under the hood, each agent's position is stored in two ways: the agent is contained in the grid in the cell it is currently in, and the agent has a ``pos`` variable with an (x, y) coordinate tuple. The ``place_agent`` method adds the coordinate to the agent automatically. + +Now we need to add to the agents' behaviors, letting them move around and only give money to other agents in the same cell. + +First let's handle movement, and have the agents move to a neighboring cell. The grid object provides a ``move_agent`` method, which like you'd imagine, moves an agent to a given cell. That still leaves us to get the possible neighboring cells to move to. There are a couple ways to do this. One is to use the current coordinates, and loop over all coordinates +/- 1 away from it. For example: + +.. code-block:: python + + neighbors = [] + x, y = self.pos + for dx in [-1, 0, 1]: + for dy in [-1, 0, 1]: + neighbors.append((x+dx, y+dy)) + +But there's an even simpler way, using the grid's built-in ``get_neighborhood`` method, which returns all the neighbors of a given cell. This method can get two types of cell neighborhoods: Moore (including diagonals), and Von Neumann (only up/down/left/right). It also needs an argument as to whether to include the center cell itself as one of the neighbors. + +With that in mind, the agent's ``move`` method looks like this: + +.. code-block:: python -Creating Agents -~~~~~~~~~~~~~~ + class MoneyAgent(Agent): + #... + def move(self, model): + possible_steps = model.grid.get_neighborhood(self.pos, moore=True, include_center=False) + new_position = random.choice(possible_steps) + model.grid.move_agent(self, new_position) -In our example, each agent has a single ... -* variable: How much money it currently has -* action: Give a unit of money to another agent +Next, we need to get all the other agents present in a cell, and give one of them some money. We can get the contents of one or more cells using the grid's ``get_cell_list_contents`` method, or by accessing a cell directly. The method currently requires a list of cells (TODO: someone should probably fix that...), even if we only care about one cell. -The first we do is import the ``Agent`` base class. Update the import statement to reflect this. .. code-block:: python - from mesa import Model, Agent + class MoneyAgent(Agent): + #... + def give_money(self, model): + cellmates = model.grid.get_cell_list_contents([self.pos]) + if len(cellmates) > 1: + other = random.choice(cellmates) + other.wealth += 1 + self.wealth -= 1 + +And with those two methods, the agent's ``step`` method becomes: + +.. code-block:: python -Then subclass Agent to create a class that is specific to our sample model. (You will want to put this above the Model class, because the model is going to need to reference it.) + class MoneyAgent(Agent): + # ... + def step(self, model): + self.move(model) + if self.wealth > 0: + self.give_money(model) -Each agent should have a unique identifier and start with a wealth of 1. +Now, putting that all together should look like this: .. code-block:: python + class MoneyModel(Model): + """A model with some number of agents.""" + def __init__(self, N, width, height): + self.running = True + self.num_agents = N + self.grid = MultiGrid(height, width, True) + self.schedule = RandomActivation(self) + # Create agents + for i in range(self.num_agents): + a = MoneyAgent(i) + self.schedule.add(a) + # Add the agent to a random grid cell + x = random.randrange(self.grid.width) + y = random.randrange(self.grid.height) + self.grid.place_agent(a, (x, y)) + + def step(self): + self.schedule.step() + class MoneyAgent(Agent): """ An agent with fixed initial wealth.""" def __init__(self, unique_id): self.unique_id = unique_id self.wealth = 1 - class MoneyModel(Model): - .... + def move(self, model): + possible_steps = model.grid.get_neighborhood(self.pos, moore=True, include_center=False) + new_position = random.choice(possible_steps) + model.grid.move_agent(self, new_position) + + def give_money(self, model): + cellmates = model.grid.get_cell_list_contents([self.pos]) + if len(cellmates) > 1: + other = random.choice(cellmates) + other.wealth += 1 + self.wealth -= 1 -We have an Agent object and a Model Object, but we have no Agents in our Model. Let's add those. + def step(self, model): + self.move(model) + if self.wealth > 0: + self.give_money(model) -Adding Agents to Model -~~~~~~~~~~~~~~ -Add create_agents function to the MoneyModel. We need to loop over the num_agents and instantiate an our agent and store the agent into a variable. + +Let's create a model with 50 agents on a 10x10 grid, and run it for 20 steps. .. code-block:: python + model = MoneyModel(50, 10, 10) + for i in range(20): + model.step() + +Now let's use matplotlib and numpy to visualize the number of agents residing in each cell. To do that, we create a numpy array of the same size as the grid, filled with zeros. Then we use the grid object's ``coord_iter()`` feature, which lets us loop over every cell in the grid, giving us each cell's coordinates and contents in turn. + +.. code-block:: python + + # At the top of your file, import numpy + import numpy as np + + agent_counts = np.zeros((model.grid.width, model.grid.height)) + for cell in model.grid.coord_iter(): + cell_content, x, y = cell + agent_count = len(cell_content) + agent_counts[y][x] = agent_count + plt.imshow(agent_counts, interpolation='nearest') + plt.colorbar() + # If running from a text editor or IDE, uncomment this line. + # plt.show() + +.. image:: images/tutorial/numpy_grid.png + :width: 50% + :scale: 100% + :alt: Agents per cell + :align: center + + +Collecting Data +~~~~~~~~~~~~~~~~~ + +So far, at the end of every model run, we've had to go and write our own code to get the data out of the model. This has two problems: it isn't very efficient, and it only gives us end results. If we wanted to know the wealth of each agent at each step, we'd have to add that to the loop of executing steps, and figure out some way to store the data. + +Since one of the main goals of agent-based modeling is generating data for analysis, Mesa provides a class which can handle data collection and storage for us and make it easier to analyze. + +The data collector stores three categories of data: model-level variables, agent-level variables, and tables (which are a catch-all for everything else). Model- and agent-level variables are added to the data collector along with a function for collecting them. Model-level collection functions take a model object as an input, while agent-level collection functions take an agent object as an input. Both then return a value computed from the model or each agent at their current state. When the data collector’s ``collect`` method is called, with a model object as its argument, it applies each model-level collection function to the model, and stores the results in a dictionary, associating the current value with the current step of the model. Similarly, the method applies each agent-level collection function to each agent currently in the schedule, associating the resulting value with the step of the model, and the agent’s ``unique_id``. + +Let's add a DataCollector to the model, and collect two variables. At the agent level, we want to collect every agent's wealth at every step. At the model level, let's measure the model's `Gini Coefficient `_, a measure of wealth inequality. + +.. code-block:: python + + from mesa.datacollection import DataCollector + + def compute_gini(model): + agent_wealths = [agent.wealth for agent in model.schedule.agents] + x = sorted(agent_wealths) + N = model.num_agents + B = sum( xi * (N-i) for i,xi in enumerate(x) ) / (N*sum(x)) + return (1 + (1/N) - 2*B) + + # ... class MoneyModel(Model): - ... + def __init__(self, N, width, height): + # ... + self.datacollector = DataCollector(model_reporters={"Gini": compute_gini}, + agent_reporters={"Wealth": lambda a: a.wealth}) - def create_agents(self): - """Method to create all the agents.""" - for i in range(self.num_agents): - a = MoneyAgent(i) + def step(self): + self.datacollector.collect(self) + self.schedule.step() + +At every step of the model, the datacollector will collect and store the model-level current Gini coefficient, as well as each agent's wealth, associating each with the current step. -Then, we need to call this function when the object is initiated. +We run the model just as we did above. Now is when an interactive session, especially via a Notebook, comes in handy: the DataCollector can export the data it's collected as a pandas DataFrame, for easy interactive analysis. .. code-block:: python - class MoneyModel(Model): - """A model with some number of agents.""" - def __init__(self, N): - self.num_agents = N - self.create_agents() + model = MoneyModel(50, 10, 10) + for i in range(100): + model.step() -At this point, your code should look like the code below. +To get the series of Gini coefficients as a pandas DataFrame: .. code-block:: python - from mesa import Model, Agent + gini = model.datacollector.get_model_vars_dataframe() + gini.plot() - class MoneyAgent(Agent): - """ An agent with fixed initial wealth.""" - def __init__(self, unique_id): - self.unique_id = unique_id - self.wealth = 1 +.. image:: images/tutorial/dc_gini.png + :width: 50% + :scale: 100% + :alt: Model-level variable collected + :align: center - class MoneyModel(Model): - """A model with some number of agents.""" - def __init__(self, N): - self.num_agents = N - # The scheduler will be added here - self.create_agents() - def create_agents(self): - """Method to create all the agents.""" - for i in range(self.num_agents): - a = MoneyAgent(i) - # Now what? See below. +Similarly, we can get the agent-wealth data: + +.. code-block:: python + + agent_wealth = model.datacollector.get_agent_vars_dataframe() + agent_wealth.head() + +You'll see that the DataFrame's index is pairings of model step and agent ID. You can analyze it the way you would any other DataFrame. For example, to get a histogram of agent wealth at the model's end: + +.. code-block:: python + + end_wealth = agent_wealth.xs(99, level="Step")["Wealth"] + end_wealth.hist(bins=range(agent_wealth.Wealth.max()+1)) + +.. image:: images/tutorial/dc_endwealth.png + :width: 50% + :scale: 100% + :alt: Model-level variable collected + :align: center + +Or to plot the wealth of a given agent (in this example, agent 14): + +.. code-block:: python + + one_agent_wealth = agent_wealth.xs(14, level="AgentID") + one_agent_wealth.Wealth.plot() + +.. image:: images/tutorial/dc_oneagent.png + :width: 50% + :scale: 100% + :alt: Model-level variable collected + :align: center + +Batch Run +~~~~~~~~~~~ + +Like we mentioned above, you usually won't run a model only once, but multiple times, with fixed parameters to find the overall distributions the model generates, and with varying parameters to analyze how they drive the model's outputs and behaviors. Instead of needing to write nested for-loops for each model, Mesa provides a BatchRunner class which automates it for you. + +.. code-block:: python + + from mesa.batchrunner import BatchRunner +We instantiate a BatchRunner with a model class to run, and a dictionary mapping parameters to values for them to take. If any of these parameters are assigned more than one value, as a list or an iterator, the BatchRunner will know to run all the combinations of these values and the other ones. The BatchRunner also takes an argument for how many model instantiations to create and run at each combination of parameter values, and how many steps to run each instantiation for. Finally, like the DataCollector, it takes dictionaries of model- and agent-level reporters to collect. Unlike the DataCollector, it won't collect the data every step of the model, but only at the end of each run. + +In the following example, we hold the height and width fixed, and vary the number of agents. We tell the BatchRunner to run 5 instantiations of the model with each number of agents, and to run each for 100 steps. We have it collect the final Gini coefficient value. + +One more thing: batch runners need a way to tell if the model hits some end conditions before the maximum number of steps is reached. To do that, it uses the model's ``running`` variable. In this case, the model has no termination condition, so just add a line to the ``MoneyModel`` constructor: + +.. code-block:: python + + self.running = True + + +Now, we can set up and run the BatchRunner: + +.. code-block:: python + + parameters = {"height": 10, "width": 10, "N": range(10, 500, 10)} + + batch_run = BatchRunner(MoneyModel, parameters, iterations=5, max_steps=100, + model_reporters={"Gini": compute_gini}) + batch_run.run_all() + +Like the DataCollector, we can extract the data we collected as a DataFrame. + +.. code-block:: python + + run_data = batch_run.get_model_vars_dataframe() + run_data.head() + plt.scatter(run_data.N, run_data.Gini) + + +Notice that each row is a model run, and gives us the parameter values associated with that run. We can use this data to view a scatter-plot comparing the number of agents to the final Gini. + +.. image:: images/tutorial/br_ginis.png + :width: 50% + :scale: 100% + :alt: Model-level variable collected + :align: center + +Adding visualization +--------------------------- + +So far, we've built a model, run it, and analyzed some output afterwards. However, one of the advantages of agent-based models is that we can often watch them run step by step, potentially spotting unexpected patterns, behaviors or bugs, or developing new intuitions, hypotheses, or insights. Other times, watching a model run can explain it to an unfamiliar audience better than static explanations. Like many ABM frameworks, Mesa allows you to create an interactive visualization of the model. In this section we'll walk through creating a visualization using built-in components, and (for advanced users) how to create a new visualization element. + +First, a quick explanation of how Mesa's interactive visualization works. Visualization is done in a browser window, using JavaScript to draw the different things being visualized at each step of the model. To do this, Mesa launches a small web server, which runs the model, turns each step into a JSON object (essentially, structured plain text) and sends those steps to the browser. + +A visualization is built up of a few different modules: for example, a module for drawing agents on a grid, and another one for drawing a chart of some variable. Each module has a Python part, which runs on the server and turns a model state into JSON data; and a JavaScript side, which takes that JSON data and draws it in the browser window. Mesa comes with a few modules built in, and let you add your own as well. + +Grid Visualization +~~~~~~~~~~~~~~~~~~~ + +To start with, let's have a visualization where we can watch the agents moving around the grid. For this, you will need to put your model code in a separate Python source file; for example, ``MoneyModel.py``. Next, either in the same file or in a new one (e.g. ``MoneyModel_Viz.py``) import the server class and the Canvas Grid class (so-called because it uses HTML5 canvas to draw a grid). If you're in a new file, you'll also need to import the actual model object. + +.. code-block:: python + + from mesa.visualization.modules import CanvasGrid + from mesa.visualization.ModularVisualization import ModularServer + + from MoneyModel import MoneyModel # If MoneyModel.py is where your code is. + +``CanvasGrid`` works by looping over every cell in a grid, and generating a portrayal for every agent it finds. A portrayal is a dictionary (which can easily be turned into a JSON object) which tells the JavaScript side how to draw it. The only thing we need to provide is a function which takes an agent, and returns a portrayal object. Here's the simplest one: it'll draw each agent as a red, filled circle which fills half of each cell. + +.. code-block:: python + + def agent_portrayal(agent): + portrayal = {"Shape": "circle", + "Color": "red", + "Filled": "true", + "Layer": 0, + "r": 0.5} + return portrayal + + +In addition to the portrayal method, we instantiate a canvas grid with its width and height in cells, and in pixels. In this case, let's create a 10x10 grid, drawn in 500 x 500 pixels. + +.. code-block:: python + + grid = CanvasGrid(agent_portrayal, 10, 10, 500, 500) + +Now we create and launch the actual server. We do this with the following arguments: + + - The model class we're running and visualizing; in this case, ``MoneyModel``. + - A list of module objects to include in the visualization; here, just ``[grid]`` + - The title of the model: "Money Model" + - Any inputs or arguments for the model itself. In this case, 100 agents, and height and width of 10. + +One we create the server, we set the port for it to listen on (you can treat this as just a piece of the URL you'll open in the browser). Finally, when you're ready to run the visualization, use the server's ``launch()`` method. + +.. code-block:: python + + server = ModularServer(MoneyModel, [grid], "Money Model", 100, 10, 10) + server.port = 8889 + server.launch() + +The full code should now look like: + +.. code-block:: python + + from MoneyModel import * + from mesa.visualization.modules import CanvasGrid + from mesa.visualization.ModularVisualization import ModularServer + + + def agent_portrayal(agent): + portrayal = {"Shape": "circle", + "Filled": "true", + "Layer": 0, + "Color": "red", + "r": 0.5} + return portrayal + + grid = CanvasGrid(agent_portrayal, 10, 10, 500, 500) + server = ModularServer(MoneyModel, [grid], "Money Model", 100, 10, 10) + server.port = 8889 + server.launch() + + +Now run this file; this should launch the interactive visualization server. Open your web browser of choice, and enter `127.0.0.1:8889 <127.0.0.1:8889>`_. + +You should see something like the figure below: the model title, an empty space where the grid will be, and a control panel off to the right. + +.. image:: images/tutorial/viz_empty.png + :width: 50% + :scale: 100% + :alt: Model-level variable collected + :align: center + +Click the 'reset' button on the control panel, and you should see the grid fill up with red circles, representing agents. + +.. image:: images/tutorial/viz_redcircles.png + :width: 50% + :scale: 100% + :alt: Model-level variable collected + :align: center + + +Click 'step' to advance the model by one step, and the agents will move around. Click 'run' and the agents will keep moving around, at the rate set by the 'fps' (frames per second) slider at the top. Try moving it around and see how the speed of the model changes. Pressing 'pause' will (as you'd expect) pause the model; presing 'run' again will restart it. Finally, 'reset' will start a new instantiation of the model. + +To stop the visualization server, go back to the terminal where you launched it, and press Control+c. + +Changing the agents +~~~~~~~~~~~~~~~~~~~ + +In the visualization above, all we could see is the agents moving around -- but not how much money they had, or anything else of interest. Let's change it so that agents who are broke (wealth 0) are drawn in grey, smaller, and above agents who still have money. + +To do this, we go back to our ``agent_portrayal`` code and add some code to change the portrayal based on the agent properties. + +.. code-block:: python + + def agent_portrayal(agent): + portrayal = {"Shape": "circle", + "Filled": "true", + "r": 0.5} + + if agent.wealth > 0: + portrayal["Color"] = "red" + portrayal["Layer"] = 0 + else: + portrayal["Color"] = "grey" + portrayal["Layer"] = 1 + portrayal["r"] = 0.2 + return portrayal + +Now launch the server again, open or refresh your browser page, and hit 'reset'. Initially it looks the same, but advance the model and smaller grey circles start to appear. Note that since the zero-wealth agents have a higher layer number, they are drawn on top of the red agents. + +.. image:: images/tutorial/viz_greycircles.png + :width: 50% + :scale: 100% + :alt: Model-level variable collected + :align: center + +Adding a chart +~~~~~~~~~~~~~~~ + +Next, let's add another element to the visualization: a chart, tracking the model's Gini Coefficient. This is another built-in element that Mesa provides. + +.. code-block:: python + + from mesa.visualization.modules import ChartModule + +The basic chart pulls data from the model's DataCollector, and draws it as a line graph using the `Charts.js `_ JavaScript libraries. We instantiate a chart element with a list of series for the chart to track. Each series is defined in a dictionary, and has a ``Label`` (which must match the name of a model-level variable collected by the DataCollector) and a ``Color`` name. We can also give the chart the name of the DataCollector object in the model. + +Finally, we add the chart to the list of elements in the server. The elements are added to the visualization in the order they appear, so the chart will appear underneath the grid. + +.. code-block:: python + + chart = ChartModule([{"Label": "Gini", "Color": "Black"}], + data_collector_name='datacollector') + + server = ModularServer(MoneyModel, [grid, chart], "Money Model", 100, 10, 10) + +Launch the visualization and start a model run, and you'll see a line chart underneath the grid. Every step of the model, the line chart updates along with the grid. Reset the model, and the chart resets too. + +.. image:: images/tutorial/viz_chart.png + :width: 50% + :scale: 100% + :alt: Model-level variable collected + :align: center + +**Note:** You might notice that the chart line only starts after a couple of steps; this is due to a bug in Charts.js which will hopefully be fixed soon. + + +Building your own visualization component +------------------------------------------- + +**Note:** This section is for users who have a basic familiarity with JavaScript. If that's not you, don't worry! (If you're an advanced JavaScript coder and find things that we've done wrong or inefficiently here, please `let us know `_!) + +If the visualization elements provided by Mesa aren't enough for you, you can build your own and plug them into the model server. + +First, you need to understand how the visualization works under the hood. Remember that each visualization module has two sides: a Python object that runs on the server and generates JSON data from the model state (the server side), and a JavaScript object that runs in the browser and turns the JSON into something it renders on the screen (the client side). + +Obviously, the two sides of each visualization must be designed in tandem. They result in one Python class, and one JavaScript ``.js`` file. The path to the JavaScript file is a property of the Python class. + +For this example, let's build a simple histogram visualization, which can count the number of agents with each value of wealth. We'll use the `Charts.js `_ JavaScript library, which is already included with Mesa. If you go and look at its documentation, you'll see that it had no histogram functionality, which means we have to build our own out of a bar chart. We'll keep the histogram as simple as possible, giving it a fixed number of integer bins. If you were designing a more general histogram to add to the Mesa repository for everyone to use across different models, obviously you'd want something more general. + +Client-Side Code +~~~~~~~~~~~~~~~~~ + +In general, the server- and client-side are written in tandem. However, if you're like me and more comfortable with Python than JavaScript, it makes sense to figure out how to get the JavaScript working first, and then write the Python to be compatible with that. + +In the same directory as your model, create a new file called ``HistogramModule.js``. This will store the JavaScript code for the client side of the new module. + +JavaScript classes can look alien to people coming from other languages -- specifically, they can look like functions. (The Mozilla `Introduction to Object-Oriented JavaScript `_ is a good starting point). In `HistogramModule.js`, start by creating the class itself: + +.. code-block:: javascript + + var HistogramModule = function(bins, canvas_width, canvas_height) { + // The actual code will go here. + }; + +Note that our object is instantiated with three arguments: the number of integer bins, and the width and height (in pixels) the chart will take up in the visualization window. + +When the visualization object is instantiated, the first thing it needs to do is prepare to draw on the current page. To do so, it adds a `canvas `_ tag to the page, using `JQuery `_ 's dollar-sign syntax (JQuery is already included with Mesa). It also gets the canvas's context, which is required for doing anything with it. + +.. code-block:: javascript + + var HistogramModule = function(bins, canvas_width, canvas_height) { + + // Create the tag: + var canvas_tag = "`_; you'll see some of the boilerplate needed to get a chart set up. Especially important is the `data` object, which includes the datasets, labels, and color options. In this case, we want just one dataset (we'll keep things simple and name it "Data"); it has `bins` for categories, and the value of each category starts out at zero. Finally, using these boilerplate objects and the canvas context we created, we can create the chart object. + +.. code-block:: javascript + + var HistogramModule = function(bins, canvas_width, canvas_height) { + // Create the elements + + // Create the tag: + var canvas_tag = "`_. Similarly, ``local_includes`` is a list of JavaScript files in the same directory as the class code itself. Note that both of these are class variables, not object variables -- they hold for all particular objects. + +Next, look at the ``__init__`` method. It takes three arguments: the number of bins, and the width and height for the histogram. It then uses these values to populate the ``js_code`` property; this is code that the server will insert into the visualization page, which will run when the page loads. In this case, it creates a new HistogramModule (the class we created in JavaScript in the step above) with the desired bins, width and height; it then appends (``push``es) this object to ``elements``, the list of visualization elements that the visualization page itself maintains. + +Now, the last thing we need is the ``render`` method. If we were making a general-purpose visualization module we'd want this to be more general, but in this case we can hard-code it to our model. + +.. code-block:: python + + import numpy as np + + class HistogramModule(VisualizationElement): + + # ... Everything from above... + + def render(self, model): + wealth_vals = [agent.wealth for agent in model.schedule.agents] + hist = np.histogram(wealth_vals, bins=self.bins)[0] + return [int(x) for x in hist] + +Every time the render method is called (with a model object as the argument) it uses numpy to generate counts of agents with each wealth value in the bins, and then returns a list of these values. Note that the ``render`` method doesn't return a JSON string -- just an object that can be turned into JSON, in this case a Python list (with Python integers as the values; the ``json`` library doesn't like dealing with numpy's integer type). + +Now, you can create your new HistogramModule and add it to the server: + +.. code-block:: python + + histogram = HistogramModule(list(range(10)), 200, 500) + server = ModularServer(MoneyModel, [grid, histogram, chart], "Money Model", 100, 10, 10) + server.launch() + +Run this code, and you should see your brand-new histogram added to the visualization and updating along with the model! + +.. image:: images/tutorial/viz_histogram.png + :width: 50% + :scale: 100% + :alt: Model-level variable collected + :align: center + +If you've felt comfortable with this section, it might be instructive to read the code for the `ModularServer `_ and the `modular_template `_ to get a better idea of how all the pieces fit together. + +**Happy modeling!** ** THIS DOC IS IN PROGRESS ** @@ -209,6 +896,8 @@ At this point, your code should look like the code below. .. _`virtual environment`: http://docs.python-guide.org/en/latest/dev/virtualenvs/ +.. [Comer2014] Comer, Kenneth W. “Who Goes First? An Examination of the Impact of Activation on Outcome Behavior in AgentBased Models.” George Mason University, 2014. http://gradworks.umi.com/36/23/3623940.html. + .. [Dragulescu2002] Drăgulescu, Adrian A., and Victor M. Yakovenko. “Statistical Mechanics of Money, Income, and Wealth: A Short Survey.” arXiv Preprint Cond-mat/0211175, 2002. http://arxiv.org/abs/cond-mat/0211175. diff --git a/docs/overview.rst b/docs/overview.rst new file mode 100644 index 00000000000..6db93ad76fd --- /dev/null +++ b/docs/overview.rst @@ -0,0 +1,47 @@ + + +Overview of Modules +--------------------- + +Mesa is modular, meaning that There are three module types in Mesa. + +1. Modeling +2. Analysis +3. Visualization + +TODO: Insert image + + +Modeling modules +~~~~~~~~~~~~~~ + +To build a model, you need the following: + +* **Model class** to store the model-level parameters and serve as a container for the rest of the components. + +* **Agent class(es)** which describe the model agents. + +* **Scheduler** which controls the agent activation regime, and handles time in the model in general. + +* **space** components describing the space and/or network the agents are situated in (if any). + + +Analysis modules +~~~~~~~~~~~~~~ + +Not every model *needs* these modules, but they provide useful tools for getting data out of your model runs to study more systematically. + +* **Data collectors** are used to record data from each model run. +* **Batch runners** automate multiple runs and parameter sweeps -- running the model with different parameters, to see how they change its behavior. + + +Visualization modules +~~~~~~~~~~~~~~ + +A visualization lets you directly observe model runs, seeing the dynamics that emerge from it and making sure that it's behaving in the way you want it to. Mesa handles visualizations in a browser window, using JavaScript. It provides a set of pre-built components, which can be instantiated for a particular model in Python and automatically generate the corresponding objects in the browser window. It's also easy to write your own components with some basic JavaScript knowledge. + +Some visualization modules we'll use here include: + +* **Grid** visualization, +* **Chart** display module, +* The **ModularServer** itself. \ No newline at end of file diff --git a/examples/Tutorial-Boltzmann_Wealth_Model/Introduction to Mesa Tutorial Code.ipynb b/examples/Tutorial-Boltzmann_Wealth_Model/Introduction to Mesa Tutorial Code.ipynb new file mode 100644 index 00000000000..e2758367cc0 --- /dev/null +++ b/examples/Tutorial-Boltzmann_Wealth_Model/Introduction to Mesa Tutorial Code.ipynb @@ -0,0 +1,876 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Introduction to Mesa Tutorial Code\n", + "\n", + "This Notebook contains code corresponding to the [Intro to Mesa tutorial](http://mesa.readthedocs.org/en/tutorial_update/intro-tutorial.html), which you should check out for the full explanation and documentation." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Use matplotlib for inline graphing\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simple Model\n", + "\n", + "This section corresponds to the code in the [Running Your First Model](http://mesa.readthedocs.org/en/tutorial_update/intro-tutorial.html#running-your-first-model) section of the tutorial." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, import the base classes we'll use" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from mesa import Agent, Model\n", + "from mesa.time import RandomActivation\n", + "import random" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, create the agent and model classes:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class MoneyAgent(Agent):\n", + " \"\"\" An agent with fixed initial wealth.\"\"\"\n", + " def __init__(self, unique_id):\n", + " self.unique_id = unique_id\n", + " self.wealth = 1\n", + "\n", + " def step(self, model):\n", + " if self.wealth == 0:\n", + " return\n", + " other_agent = random.choice(model.schedule.agents)\n", + " other_agent.wealth += 1\n", + " self.wealth -= 1\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class MoneyModel(Model):\n", + " \"\"\"A model with some number of agents.\"\"\"\n", + " def __init__(self, N):\n", + " self.running = True\n", + " self.num_agents = N\n", + " self.schedule = RandomActivation(self)\n", + " # Create agents\n", + " for i in range(self.num_agents):\n", + " a = MoneyAgent(i)\n", + " self.schedule.add(a)\n", + "\n", + " def step(self):\n", + " '''Advance the model by one step.'''\n", + " self.schedule.step()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a model and run it for 10 steps:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "model = MoneyModel(10)\n", + "for i in range(10):\n", + " model.step()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And display a histogram of agent wealths:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(array([ 5., 0., 2., 0., 0., 2., 0., 0., 0., 1.]),\n", + " array([ 0. , 0.4, 0.8, 1.2, 1.6, 2. , 2.4, 2.8, 3.2, 3.6, 4. ]),\n", + " )" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\nog0wiGgDDCLaAIOINsAgog0wiGgDDCLaAIOINsAgog0wiGgDDCLaAIOINsAgog0wiGgDDCLaAIOI\nNsAgog0wiGgDDCLaAIOINsAgog0wiGgDDLI12lV1V1V9vqq+UFW/dxpDAXB1R0a7qm5O8mdJ7kry\nvCSvrqqfOY3BbrSq2lt7hmX21x5gkTn7nGHKPifMOWHG49h2pn1nki9295e7+ztJ/jLJr578WCdi\nb+0Bltlfe4Cl9tYe4Almb+0BFtpbe4AF9tYe4CRti/ZPJPnKZW//2+b7AFjBmS2P97Knedmjx57k\nWL5w67rvH+B0VPfhXa6qX0hyrrvv2rz9B0ke7+4/vuyYhWEH4HLdXdf6Y7ZF+0ySf03yy0n+I8k/\nJHl1dz98vUMCcP2OvDzS3d+tqt9O8tdJbk7yNsEGWM+RZ9oA7JbFr4hc8iKbqvqTzeP/XFXPv3Fj\nLrdtzqraq6pHq+rBze0PV5jx7VX1SFU9dMQxu7DLI+fchV1u5jhbVfdV1eeq6rNV9fpDjlt1p0vm\nXHunVXVrVT1QVeer6kJVvemQ49be5dY5197lgVlu3szw4UMeX77P7t56y8VLI19McnuSW5KcT/Iz\nB465O8lHN/d/Psmnlzz3jbwtnHMvyYdOe7YDM/xikucneeiQx1ff5cI5V9/lZo4fT3LH5v7Tc/Hz\nMLv463PJnKvvNMnTNt+eSfLpJC/etV0unHP1XV42y+8mec/V5rnWfS49017yIptXJnlHknT3A0me\nUVXPWvj8N8rSFwNd82dsb6Tuvj/JN484ZBd2uWTOZOVdJkl3f627z2/ufzvJw0mec+Cw1Xe6cM5k\n/V+fj23uPiUXT4S+ceCQ1Xe5ed/b5kx24NdnVd2Wi2F+a64+zzXtc2m0l7zI5mrH3Lbw+W+UJXN2\nkhdu/hry0ap63qlNt9wu7HKJndtlVd2ei387eODAQzu10yPmXH2nVXVTVZ1P8kiS+7r7woFDdmKX\nC+ZcfZcbb07yxiSPH/L4Ne1zabSXfrby4J8ip/1ZziXv75+SnO3un03yp0k+eLIjXbe1d7nETu2y\nqp6e5H1J3rA5k73ikANvr7LTLXOuvtPufry778jFcLzkkH/LY/VdLphz9V1W1SuSfL27H8zRZ/2L\n97k02v+e5Oxlb5/NxT8Njjrmts33naatc3b3f1/6a1V3/1WSW6rqR09vxEV2YZdb7dIuq+qWJO9P\n8u7uvtpvzp3Y6bY5d2mn3f1oko8kecGBh3Zil5ccNueO7PKFSV5ZVV9K8t4kL6uqdx445pr2uTTa\nn0nyU1V1e1U9JcmvJ/nQgWM+lOS1yfdfSfmt7n5k4fPfKFvnrKpnVVVt7t+Zi1/2eLVrYWvahV1u\ntSu73MzwtiQXuvsthxy2+k6XzLn2TqvqmVX1jM39pyZ5eZIHDxy2C7vcOufau0yS7r6nu89293OT\nvCrJJ7r7tQcOu6Z9bvu3Ry6946u+yKaqfnPz+J9390er6u6q+mKS/0nyumv9CR7XkjmT/FqS36qq\n7yZ5LBcXeaqq6r1JfinJM6vqK0nuzcWvdtmZXS6ZMzuwy40XJXlNkn+pqku/ce9J8pPJTu1065xZ\nf6fPTvKOqropF0/q3tXdH9+13+tL5sz6u7yaTpLj7NOLawAG8d+NAQwi2gCDiDbAIKINMIhoAwwi\n2gCDiDbAIKINMMj/AVO57TXlCBN5AAAAAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "agent_wealth = [a.wealth for a in model.schedule.agents]\n", + "plt.hist(agent_wealth)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create and run 100 models, and visualize the wealth distribution across all of them:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(array([ 416., 312., 162., 79., 29., 2.]),\n", + " array([0, 1, 2, 3, 4, 5, 6]),\n", + " )" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\nAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "all_wealth = []\n", + "for j in range(100):\n", + " # Run the model\n", + " model = MoneyModel(10)\n", + " for i in range(10):\n", + " model.step()\n", + " # Store the results\n", + " for agent in model.schedule.agents:\n", + " all_wealth.append(agent.wealth)\n", + "\n", + "plt.hist(all_wealth, bins=range(max(all_wealth)+1))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Adding space\n", + "\n", + "This section puts the agents on a grid, corresponding to the [Adding Space](http://mesa.readthedocs.org/en/tutorial_update/intro-tutorial.html#adding-space) section of the tutorial.\n", + "\n", + "For this, we need to import the grid class:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from mesa.space import MultiGrid" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create the new model object. (Note that this overwrites the MoneyModel object created above)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class MoneyModel(Model):\n", + " \"\"\"A model with some number of agents.\"\"\"\n", + " def __init__(self, N, width, height):\n", + " self.running = True\n", + " self.num_agents = N\n", + " self.grid = MultiGrid(height, width, True)\n", + " self.schedule = RandomActivation(self)\n", + " # Create agents\n", + " for i in range(self.num_agents):\n", + " a = MoneyAgent(i)\n", + " self.schedule.add(a)\n", + " # Add the agent to a random grid cell\n", + " x = random.randrange(self.grid.width)\n", + " y = random.randrange(self.grid.height)\n", + " self.grid.place_agent(a, (x, y))\n", + "\n", + " def step(self):\n", + " self.schedule.step()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And create the agent to go along with it:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class MoneyAgent(Agent):\n", + " \"\"\" An agent with fixed initial wealth.\"\"\"\n", + " def __init__(self, unique_id):\n", + " self.unique_id = unique_id\n", + " self.wealth = 1\n", + "\n", + " def move(self, model):\n", + " possible_steps = model.grid.get_neighborhood(self.pos, moore=True, include_center=False)\n", + " new_position = random.choice(possible_steps)\n", + " model.grid.move_agent(self, new_position)\n", + "\n", + " def give_money(self, model):\n", + " cellmates = model.grid.get_cell_list_contents([self.pos])\n", + " if len(cellmates) > 1:\n", + " other = random.choice(cellmates)\n", + " other.wealth += 1\n", + " self.wealth -= 1\n", + "\n", + " def step(self, model):\n", + " self.move(model)\n", + " if self.wealth > 0:\n", + " self.give_money(model)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a model with 50 agents and a 10x10 grid, and run for 20 steps" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "model = MoneyModel(50, 10, 10)\n", + "for i in range(20):\n", + " model.step()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Visualize the number of agents on each grid cell:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\nrkJggg==\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "agent_counts = np.zeros((model.grid.width, model.grid.height))\n", + "for cell in model.grid.coord_iter():\n", + " cell_content, x, y = cell\n", + " agent_count = len(cell_content)\n", + " agent_counts[y][x] = agent_count\n", + "plt.imshow(agent_counts, interpolation='nearest')\n", + "plt.colorbar()\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Collecting Data\n", + "\n", + "Add a Data Collector to the model, as explained in the [corresponding section](http://mesa.readthedocs.org/en/tutorial_update/intro-tutorial.html#collecting-data) of the tutorial.\n", + "\n", + "First, import the DataCollector" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from mesa.datacollection import DataCollector" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Compute the agents' [Gini coefficient](https://en.wikipedia.org/wiki/Gini_coefficient), measuring inequality." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def compute_gini(model):\n", + " '''\n", + " Compute the current Gini coefficient.\n", + " \n", + " Args:\n", + " model: A MoneyModel instance\n", + " Returns:\n", + " The Gini Coefficient for the model's current step.\n", + " '''\n", + " agent_wealths = [agent.wealth for agent in model.schedule.agents]\n", + " x = sorted(agent_wealths)\n", + " N = model.num_agents\n", + " B = sum( xi * (N-i) for i,xi in enumerate(x) ) / (N*sum(x))\n", + " return (1 + (1/N) - 2*B)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This MoneyModel is identical to the one above, except for the ``` self.datacollector = ... ``` line at the end of the ```__init__``` method, and the collection in ```step```." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class MoneyModel(Model):\n", + " \"\"\"A model with some number of agents.\"\"\"\n", + " def __init__(self, N, width, height):\n", + " self.running = True\n", + " self.num_agents = N\n", + " self.grid = MultiGrid(height, width, True)\n", + " self.schedule = RandomActivation(self)\n", + " # Create agents\n", + " for i in range(self.num_agents):\n", + " a = MoneyAgent(i)\n", + " self.schedule.add(a)\n", + " # Add the agent to a random grid cell\n", + " x = random.randrange(self.grid.width)\n", + " y = random.randrange(self.grid.height)\n", + " self.grid.place_agent(a, (x, y))\n", + " \n", + " # New addition: add a DataCollector:\n", + " self.datacollector = DataCollector(model_reporters={\"Gini\": compute_gini},\n", + " agent_reporters={\"Wealth\": lambda a: a.wealth})\n", + "\n", + " def step(self):\n", + " self.datacollector.collect(self) # Collect the data before the agents run.\n", + " self.schedule.step()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now instantiate a model, run it for 100 steps..." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "model = MoneyModel(50, 10, 10)\n", + "for i in range(100):\n", + " model.step()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "... And collect and plot the data it generated:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Gini
0 0.0000
1 0.3272
2 0.4224
3 0.4728
4 0.4504
\n", + "
" + ], + "text/plain": [ + " Gini\n", + "0 0.0000\n", + "1 0.3272\n", + "2 0.4224\n", + "3 0.4728\n", + "4 0.4504" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gini = model.datacollector.get_model_vars_dataframe()\n", + "gini.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "gini.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Wealth
StepAgentID
00 1
1 1
2 1
3 1
4 1
\n", + "
" + ], + "text/plain": [ + " Wealth\n", + "Step AgentID \n", + "0 0 1\n", + " 1 1\n", + " 2 1\n", + " 3 1\n", + " 4 1" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent_wealth = model.datacollector.get_agent_vars_dataframe()\n", + "agent_wealth.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\nGAY3ABTz/6exZjaq2NAdAAAAAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "end_wealth = agent_wealth.xs(99, level=\"Step\")[\"Wealth\"]\n", + "end_wealth.hist(bins=range(agent_wealth.Wealth.max()+1))" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\nf74j8SHw4aGZAAAAAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "one_agent_wealth = agent_wealth.xs(14, level=\"AgentID\")\n", + "one_agent_wealth.Wealth.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Batch Run\n", + "\n", + "Run a parameter sweep, as explained in the [Batch Run](http://mesa.readthedocs.org/en/tutorial_update/intro-tutorial.html#batch-run) tutorial section.\n", + "\n", + "Import the Mesa BatchRunner:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from mesa.batchrunner import BatchRunner" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set up the batch run:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "parameters = {\"height\": 10, \"width\": 10, \"N\": range(10, 500, 10)}\n", + "\n", + "batch_run = BatchRunner(MoneyModel, parameters, iterations=5, max_steps=100,\n", + " model_reporters={\"Gini\": compute_gini})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Run the parameter sweep; **this step might take a while**:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "batch_run.run_all()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Export and plot the results:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\nn2lfAAAAAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "run_data = batch_run.get_model_vars_dataframe()\n", + "run_data.head()\n", + "plt.scatter(run_data.N, run_data.Gini)\n", + "plt.xlabel(\"Number of agents\")\n", + "plt.ylabel(\"Gini Coefficient\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The [final tutorial section](http://mesa.readthedocs.org/en/tutorial_update/intro-tutorial.html#adding-visualization), on building and running a browser-based interactive visualization, isn't intended to be run from within a Jupyter Notebook. Shut down the notebook and follow the tutorial from there!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.4.2" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/Tutorial-Boltzmann_Wealth_Model/MoneyModel.py b/examples/Tutorial-Boltzmann_Wealth_Model/MoneyModel.py new file mode 100644 index 00000000000..82765e4d6aa --- /dev/null +++ b/examples/Tutorial-Boltzmann_Wealth_Model/MoneyModel.py @@ -0,0 +1,64 @@ +from mesa import Agent, Model +from mesa.time import RandomActivation +from mesa.space import MultiGrid + +from mesa.datacollection import DataCollector + +import random + +def compute_gini(model): + agent_wealths = [agent.wealth for agent in model.schedule.agents] + x = sorted(agent_wealths) + N = model.num_agents + B = sum( xi * (N-i) for i,xi in enumerate(x) ) / (N*sum(x)) + return (1 + (1/N) - 2*B) + + +class MoneyModel(Model): + """A model with some number of agents.""" + def __init__(self, N, width, height): + self.num_agents = N + self.running = True + self.grid = MultiGrid(height, width, True) + self.schedule = RandomActivation(self) + self.datacollector = DataCollector(model_reporters={"Gini": compute_gini}, + agent_reporters={"Wealth": lambda a: a.wealth}) + # Create agents + for i in range(self.num_agents): + a = MoneyAgent(i) + self.schedule.add(a) + # Add the agent to a random grid cell + x = random.randrange(self.grid.width) + y = random.randrange(self.grid.height) + self.grid.place_agent(a, (x, y)) + + def step(self): + self.datacollector.collect(self) + self.schedule.step() + + def run_model(self, n): + for i in range(n): + self.step() + +class MoneyAgent(Agent): + """ An agent with fixed initial wealth.""" + def __init__(self, unique_id): + self.unique_id = unique_id + self.wealth = 1 + + def move(self, model): + possible_steps = model.grid.get_neighborhood(self.pos, moore=True, include_center=False) + new_position = random.choice(possible_steps) + model.grid.move_agent(self, new_position) + + def give_money(self, model): + cellmates = model.grid.get_cell_list_contents([self.pos]) + if len(cellmates) > 1: + other = random.choice(cellmates) + other.wealth += 1 + self.wealth -= 1 + + def step(self, model): + self.move(model) + if self.wealth > 0: + self.give_money(model) diff --git a/examples/Tutorial-Boltzmann_Wealth_Model/Tutorial-Boltzmann_Wealth_Model.ipynb b/examples/Tutorial-Boltzmann_Wealth_Model/Tutorial-Boltzmann_Wealth_Model.ipynb deleted file mode 100644 index 8d4f790dd3e..00000000000 --- a/examples/Tutorial-Boltzmann_Wealth_Model/Tutorial-Boltzmann_Wealth_Model.ipynb +++ /dev/null @@ -1,672 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Boltzmann Wealth Model" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Basic Overview\n", - "\n", - "The agent based model comes with 2 parts. The model itself and the individual agents within the model.\n", - "First, we impor the relevant classes from the Mesa module and create an Agent and Model." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from mesa import Model, Agent" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "class MoneyAgent(Agent):\n", - " \"\"\" An agent with fixed initial wealth.\"\"\"\n", - " def __init__(self, unique_id):\n", - " # Each agent should have a unique identifier, stored in the unique_id field.\n", - " self.unique_id = unique_id\n", - " self.wealth = 1" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "class MoneyModel(Model):\n", - " \"\"\"A model with some number of agents.\"\"\"\n", - " def __init__(self, N):\n", - " self.num_agents = N\n", - " # The scheduler will be added here\n", - " self.create_agents()\n", - " \n", - " def create_agents(self):\n", - " \"\"\"Method to create all the agents.\"\"\"\n", - " for i in range(self.num_agents):\n", - " a = MoneyAgent(i)\n", - " # Now what? See below." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With the classes defined, we can now initialize and create the model and populate it with agents." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "money_model = MoneyModel(10)\n", - "money_model.create_agents()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Scheduler" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A Schedule defines what each agent will do during each time tick of the simulation.\n", - "In this example we are using the `RandomActivation` schedule from `mesa.time`.\n", - "\n", - "In the `MoneyModel` we need to\n", - "\n", - "1. Create the schedule we would like each agent to use.\n", - "2. Add agents to the schedule when they are created\n", - "3. Define a model step function that determines what the model will do at each step\n", - "\n", - "In the `MoneyAgent` we need to\n", - "\n", - "1. Define a Step function that determines what each agent that is selected during a model step will do." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "import random\n", - "\n", - "from mesa import Model, Agent\n", - "from mesa.time import RandomActivation\n", - "\n", - "\n", - "class MoneyAgent(Agent):\n", - " \"\"\" An agent with fixed initial wealth.\"\"\"\n", - " def __init__(self, unique_id):\n", - " self.unique_id = unique_id\n", - " self.wealth = 1\n", - "\n", - " def step(self, model):\n", - " \"\"\"Give money to another agent.\"\"\"\n", - " if self.wealth > 0:\n", - " # Pick a random agent\n", - " other = random.choice(model.schedule.agents)\n", - " # Give them 1 unit money\n", - " other.wealth += 1\n", - " self.wealth -= 1\n", - "\n", - "\n", - "class MoneyModel(Model):\n", - " \"\"\"A model with some number of agents.\"\"\"\n", - " def __init__(self, N):\n", - " self.num_agents = N\n", - " # Adding the scheduler:\n", - " # Scheduler needs to be created before agents do\n", - " # Scheduler objects are instantiated with their model object,\n", - " # which they then pass to the agents at each step.\n", - " self.schedule = RandomActivation(self)\n", - " self.create_agents()\n", - "\n", - " def create_agents(self):\n", - " \"\"\"Method to create all the agents.\"\"\"\n", - " for i in range(self.num_agents):\n", - " a = MoneyAgent(i)\n", - " self.schedule.add(a)\n", - "\n", - " def step(self):\n", - " # The scheduler's step method activates the step methods of all the\n", - " # agents that have been added to it, in this case in random order.\n", - " self.schedule.step()\n", - "\n", - " def run_model(self, steps):\n", - " # Because the model has no inherent end conditions,\n", - " # the user must specify how many steps to run it for.\n", - " for i in range(steps):\n", - " self.step()\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With the newly updated code, we can create the model and agents (just like above).\n", - "Since we defined a schedule, we can now tell the model to run for `n` number of steps, in this example, we picked `5`." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "money_model = MoneyModel(10)\n", - "money_model.create_agents()\n", - "money_model.run_model(5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Space" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In order to assign a location to an agent we need to provide a grid or space to assign coordinates to the agents.\n", - "We get this from the `mesa.space` module.\n", - "\n", - "Since agents have a coordinate, we can also define a `move` method." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "import random\n", - "\n", - "from mesa import Model, Agent\n", - "from mesa.time import RandomActivation\n", - "from mesa.space import MultiGrid\n", - "\n", - "\n", - "class MoneyAgent(Agent):\n", - " \"\"\" An agent with fixed initial wealth.\"\"\"\n", - " def __init__(self, unique_id):\n", - " self.unique_id = unique_id\n", - " self.wealth = 1\n", - "\n", - " def step(self, model):\n", - " \"\"\"Give money to another agent.\"\"\"\n", - " if self.wealth > 0:\n", - " # Pick a random agent\n", - " other = random.choice(model.schedule.agents)\n", - " # Give them 1 unit money\n", - " other.wealth += 1\n", - " self.wealth -= 1\n", - "\n", - " def move(self, model):\n", - " \"\"\"Take a random step.\"\"\"\n", - " grid = model.grid\n", - " # The get_neighborhood method returns a list of coordinate tuples for\n", - " # the appropriate neighbors of the given coordinates. In this case,\n", - " # it's getting the Moore neighborhood (including diagonals) and\n", - " # includes the center cell. The agent decides where to move by choosing\n", - " # one of those tuples at random. This is a good way of handling random\n", - " # moves, since it still works for agents on an edge of a non-toroidal\n", - " # grid, or if the grid itself is hexagonal.\n", - " possible_steps = grid.get_neighborhood(\n", - " self.pos, moore=True, include_center=True)\n", - " choice = random.choice(possible_steps)\n", - " # the move_agent method works like place_agent, but removes the agent\n", - " # from its current location before placing it in its new one.\n", - " grid.move_agent(self, choice)\n", - "\n", - " def give_money(self, model):\n", - " grid = model.grid\n", - " pos = [self.pos]\n", - " # This is a helper method which returns the contents of the entire list\n", - " # of cell tuples provided. It's not strictly necessary here; the\n", - " # alternative would be: x, y = self.pos; others = grid[y][x]\n", - " # (note that grids are indexed y-first).\n", - " others = grid.get_cell_list_contents(pos)\n", - " if len(others) > 1:\n", - " other = random.choice(others)\n", - " other.wealth += 1\n", - " self.wealth -= 1\n", - "\n", - "\n", - "class MoneyModel(Model):\n", - " \"\"\"A model with some number of agents.\"\"\"\n", - " def __init__(self, N, width, height, torus):\n", - " # The arguments needed to create a new grid are its\n", - " # width, height, and a boolean for whether it is a torus or not.\n", - " self.grid = MultiGrid(height, width, torus)\n", - " self.num_agents = N\n", - " self.schedule = RandomActivation(self)\n", - " self.create_agents()\n", - "\n", - " def create_agents(self):\n", - " \"\"\"Method to create all the agents.\"\"\"\n", - " for i in range(self.num_agents):\n", - " a = MoneyAgent(i)\n", - " self.schedule.add(a)\n", - " x = random.randrange(self.grid.width)\n", - " y = random.randrange(self.grid.width)\n", - " # The place_agent method places the given object in the grid cell\n", - " # specified by the (x, y) tuple, and assigns that tuple to the\n", - " # agent's pos property.\n", - " self.grid.place_agent(a, (x, y))\n", - "\n", - " def step(self):\n", - " # The scheduler's step method activates the step methods of all the\n", - " # agents that have been added to it, in this case in random order.\n", - " self.schedule.step()\n", - "\n", - " def run_model(self, steps):\n", - " # Because the model has no inherent end conditions,\n", - " # the user must specify how many steps to run it for.\n", - " for i in range(steps):\n", - " self.step()\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, we create a model with `N` agents, with `with` and `height` of 50." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "money_model = MoneyModel(N=100, width=50, height=50, torus=True)\n", - "money_model.create_agents()\n", - "money_model.run_model(50)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Since our agents are on a coordinate plane, and each agent has a wealth value, we can plot our data!" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "%matplotlib inline\n", - "\n", - "wealth_grid = np.zeros((money_model.grid.width, money_model.grid.height))\n", - "\n", - "for cell in money_model.grid.coord_iter():\n", - " cell_content, x, y = cell\n", - " cell_wealth = sum(a.wealth for a in cell_content)\n", - " wealth_grid[y][x] = cell_wealth\n", - " \n", - "plt.imshow(wealth_grid, interpolation='nearest')\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "# Data Collection\n", - "\n", - "Generic DataCollector class, which can store and export data from most models without needing to be subclassed.\n", - "\n", - "The data collector stores three categories of data: model-level variables, agent-level variables, and tables which are a catch-all for everything else.\n", - "\n", - "Internally, the data collector stores all variables and tables in Python's standard dictionaries and lists. This reduces the need for external dependencies, and allows the data to be easily exported to JSON or CSV. However, one of the goals of Mesa is facilitating integration with Python's larger scientific and data-analysis ecosystems, and thus the data collector also includes methods for exporting the collected data to pandas data frames. This allows rapid, interactive processing of the data, easy charting, and access to the full range of statistical and machine-learning tools that are compatible with pandas.\n", - "\n", - "**Since data is not written out to a file, large simulations cannot be run yet. A way to append values to a external CSV is being developed so data does not need to be persisted in memory**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```python\n", - "\n", - "from mesa.datacollector import DataCollector\n", - "\n", - "class MoneyModel(Model):\n", - "\n", - " def __init__(self, N):\n", - " # ... everything above\n", - " ar = {\"Wealth\": lambda a: a.wealth}\n", - " self.dc = DataCollector(agent_reporters=ar)\n", - "\n", - " def step(self):\n", - " self.dc.collect(self)\n", - " self.schedule.step()\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import random\n", - "\n", - "from mesa import Model, Agent\n", - "from mesa.time import RandomActivation\n", - "from mesa.space import MultiGrid\n", - "from mesa.datacollection import DataCollector\n", - "\n", - "\n", - "class MoneyAgent(Agent):\n", - " \"\"\" An agent with fixed initial wealth.\"\"\"\n", - " def __init__(self, unique_id):\n", - " self.unique_id = unique_id\n", - " self.wealth = 1\n", - "\n", - " def step(self, model):\n", - " \"\"\"Give money to another agent.\"\"\"\n", - " if self.wealth > 0:\n", - " # Pick a random agent\n", - " other = random.choice(model.schedule.agents)\n", - " # Give them 1 unit money\n", - " other.wealth += 1\n", - " self.wealth -= 1\n", - "\n", - " def move(self, model):\n", - " \"\"\"Take a random step.\"\"\"\n", - " grid = model.grid\n", - " # The get_neighborhood method returns a list of coordinate tuples for\n", - " # the appropriate neighbors of the given coordinates. In this case,\n", - " # it's getting the Moore neighborhood (including diagonals) and\n", - " # includes the center cell. The agent decides where to move by choosing\n", - " # one of those tuples at random. This is a good way of handling random\n", - " # moves, since it still works for agents on an edge of a non-toroidal\n", - " # grid, or if the grid itself is hexagonal.\n", - " possible_steps = grid.get_neighborhood(\n", - " self.pos, moore=True, include_center=True)\n", - " choice = random.choice(possible_steps)\n", - " # the move_agent method works like place_agent, but removes the agent\n", - " # from its current location before placing it in its new one.\n", - " grid.move_agent(self, choice)\n", - "\n", - " def give_money(self, model):\n", - " grid = model.grid\n", - " pos = [self.pos]\n", - " # This is a helper method which returns the contents of the entire list\n", - " # of cell tuples provided. It's not strictly necessary here; the\n", - " # alternative would be: x, y = self.pos; others = grid[y][x]\n", - " # (note that grids are indexed y-first).\n", - " others = grid.get_cell_list_contents(pos)\n", - " if len(others) > 1:\n", - " other = random.choice(others)\n", - " other.wealth += 1\n", - " self.wealth -= 1\n", - "\n", - "\n", - "class MoneyModel(Model):\n", - " \"\"\"A model with some number of agents.\"\"\"\n", - " def __init__(self, N, width, height, torus):\n", - " # The arguments needed to create a new grid are its\n", - " # width, height, and a boolean for whether it is a torus or not.\n", - " self.grid = MultiGrid(height, width, torus)\n", - " self.num_agents = N\n", - " self.schedule = RandomActivation(self)\n", - " self.create_agents()\n", - " ar = {\"Wealth\": lambda a: a.wealth}\n", - " self.dc = DataCollector(agent_reporters=ar)\n", - "\n", - " def create_agents(self):\n", - " \"\"\"Method to create all the agents.\"\"\"\n", - " for i in range(self.num_agents):\n", - " a = MoneyAgent(i)\n", - " self.schedule.add(a)\n", - " x = random.randrange(self.grid.width)\n", - " y = random.randrange(self.grid.width)\n", - " # The place_agent method places the given object in the grid cell\n", - " # specified by the (x, y) tuple, and assigns that tuple to the\n", - " # agent's pos property.\n", - " self.grid.place_agent(a, (x, y))\n", - "\n", - " def step(self):\n", - " # The scheduler's step method activates the step methods of all the\n", - " # agents that have been added to it, in this case in random order.\n", - " self.schedule.step()\n", - " self.dc.collect(self)\n", - "\n", - " def run_model(self, steps):\n", - " # Because the model has no inherent end conditions,\n", - " # the user must specify how many steps to run it for.\n", - " for i in range(steps):\n", - " self.step()\n" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\nSSNk512CnbekZXbekjSjHN6aqmoXWDGXmdqYqV8Ob0kaITvvEuy8JS2z85akGeXw1lRVu8CKuczU\nxkz9cnhL0gjZeZdg5y1pmZ23JM0oh7emqtoFVsxlpjZm6pfDW5JGyM67BDtvScvsvCVpRjm8NVXV\nLrBiLjO1MVO/HN6SNEJ23iXYeUtaZuctSTPK4a2pqnaBFXOZqY2Z+uXwlqQRsvMuwc5b0jI7b0ma\nUesO74i4LCIWI+LmFbedERGHIuLOiLg2InZubUxtt6pdYMVcZmpjpn617HlfDrxs1W37gUOZeTZw\nXbeWJG2Tps47IhaAj2XmT3XrO4AXZuZiRJwJHM7MZ0z5ODvvJnbekpZtZee9KzMXu+1FYNcGP48k\naQN2bPYTZGZO9rDXsg9Y6LZ3AnuAvd36cPd+u9Z3rXom2+6vv9Z6Yql/y8zDQ69XdoEV8qxY78nM\n9xTK88j3qEqewj+/NwFHCuXZS5HHU7e9j4mjNNhMbbI3M49FxFnA9dYmm1GvNomIvUsPskoq5jJT\nGzO128ra5Brgom77IuDqDX4eFVXxAQ01c5mpjZn61XKq4AeBzwBPj4i7I+K1wDuAl0TEncCLu7Uk\naZusO7wz88LMfFJmnpSZuzPz8sy8LzPPy8yzM/OlmXn/doTV9ql6/mvFXGZqY6Z+bfqApfpx/IO+\ngynVw0ta5mublBBUzFTtIKo0L3xtE0maUQ5vjUrFjtJMbczULztvraloDw928ZKddw01O+96mcAu\nXvPAzluSZpTDW9qkir2pmdpUzNTK4S1JI2TnXULFfrliJrDz1jyw85akGeXwljapYm9qpjYVM7Vy\neEvSCNl5l1CxX66YCey8NQ/svCVpRjm8pU2q2JuaqU3FTK0c3pI0QnbeJVTslytmAjtvzQM7b0ma\nUQ5vaZMq9qZmalMxUyuHtySNkJ13CRX75YqZwM5b88DOW5JmlMNb2qSKvamZ2lTM1MrhLUkjZOdd\nQsV+uWImsPPWPGjpvP3f4zU6Ff9Xe59QtN2sTTRCWeytnopdrpn65fCWpBFyeEszKDMPD51hNTP1\ny+EtSSPkAUtpBlU8qAv1DuxGxN6x7n07vKWZVW1+l5rbo2dtImlujXWvGxzekjRKmxreEfGyiLgj\nIv47Ii7pK5Q0NhGRld6G/n6MxVye5x0RJwJ/AbwM+Angwoh4Zl/BpHEZ+kKh+hcOFbVn6AAbtZk9\n73OAL2Tm0cx8APg74Ff6iSVJ22Ln0AE2ajPD+8nA3SvWX+lukyRtsc2cKtj4u9mLv7GJr9GzO0/G\n0yMlLVsYOsBGbWaQ/Q+we8V6N5O971WuP30TX2OLVDzf1EztKuYyU4uKB1Mj4qKhM2zEhl/POyJ2\nAP8F/ALwv8BngQsz8/b+4kmSptnwnndmPhgRvwd8AjgReJ+DW5K2x5b+TzqSpK2xJVdYVrx4JyIu\ni4jFiLh56CxLImJ3RFwfEbdGxC0R8cYCmU6JiBsi4khE3BYRbx8605KIODEiboqIjw2dBSAijkbE\n57tMnx06D0BE7IyIKyPi9u7n97wCmZ7efY+W3r5R5LH+1u7f3s0R8YGIOLlApou7PLdExMXHvXNm\n9vrGpEL5ApOjuI8BjgDP7PvrbCDXucCzgZuHzrIi05nAnm77NCbHECp8r07t3u8A/g14wdCZujx/\nAFwBXDN0li7Pl4Ezhs6xKtNB4LdX/PxOHzrTqnwnAPcAuwfOsQB8CTi5W38IuGjgTM8CbgZO6ebo\nIeBpa91/K/a8S168k5n/DHx96BwrZeaxzDzSbX8LuB140rCpIDO/022exORBdN+AcQCIiKcArwDe\nS63TKMpkiYjTgXMz8zKYHJfKzEKn6gJwHvDFzLx73XturW8CDwCndidfnMrkDLohPQO4ITO/m5kP\nAZ8Gfm2tO2/F8PbinQ2IiAUmvxncMGwSiIgTIuIIsAhcn5m3DZ0JeDfwFuDhoYOskMAnI+LGiHj9\n0GGApwJfjYjLI+JzEfHXEXHq0KFWeTXwgaFDZOZ9wLuAu5icLXd/Zn5y2FTcApwbEWd0P7dfAp6y\n1p23Ynh7BPRRiojTgCuBi7s98EFl5sOZuYfJA+fnh37xnoh4JXBvZt5EoT1d4PmZ+Wzg5cAbIuLc\ngfPsAJ4D/GVmPgf4NrB/2EjLIuIk4JeBvy+Q5WnAm5jUJ08CTouI3xgyU2beAVwKXAv8A3ATx9lZ\n2Yrh3XjxjgAi4jHAR4C/zcyrh86zUvcr98eB5w4c5eeA8yPiy8AHgRdHxN8MnInMvKd7/1XgKiaV\n4ZC+AnwlM/+9W1/JZJhX8XLgP7rv19CeC3wmM7+WmQ8CH2XyOBtUZl6Wmc/NzBcC9zM5DjbVVgzv\nG4Efj4iF7pn214FrtuDrjF5EBPA+4LbMfM/QeQAi4okRsbPbfizwEiZ7AIPJzLdl5u7MfCqTX7s/\nlZm/NWSmiDg1Ih7fbT8OeCmTg02DycxjwN0RcXZ303nArQNGWu1CJk++FdwBPC8iHtv9OzwPGLwe\njIgf7t7/CPCrHKdi6v11PrLoxTsR8UHghcATIuJu4I8z8/KBYz0f+E3g8xGxNCDfmpn/OGCms4CD\nEXECkyf392fmdQPmmaZCNbcLuGry754dwBWZee2wkQD4feCKbsfpi8BrB84DPPIEdx5Q4dgAmfmf\n3W9vNzKpJj4H/NWwqQC4MiKewORg6u9m5jfXuqMX6UjSCPnfoEnSCDm8JWmEHN6SNEIOb0kaIYe3\nJI2Qw1uSRsjhLUkj5PCWpBH6fwYlOJ0owFwfAAAAAElFTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Create a model with 100 agents\n", - "model = MoneyModel(100, 10, 10, True)\n", - "# Run it for 1,000 steps:\n", - "model.run_model(1000)\n", - "# Get the data as a DataFrame\n", - "wealth_history = model.dc.get_agent_vars_dataframe()\n", - "# wealth_history indexed on Step and AgentID, and...\n", - "# ...has Wealth as one data column\n", - "wealth_history.reset_index(inplace=True)\n", - "# Plot a histogram of final wealth\n", - "wealth_history[wealth_history.Step==999].\\\n", - " Wealth.hist(bins=range(10))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Batch Runner\n", - "\n", - "Since most ABMs are stochastic, a single model run gives us only one particular realization of the process the model describes. Furthermore, the questions we want to use ABMs to answer are often about how a particular parameter drives the behavior of the entire system -- requiring multiple model runs with different parameter values. In order to facilitate this, Mesa provides the BatchRunner class. Like the DataCollector, it does not need to be subclassed in order to conduct parameter sweeps on most models." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\nTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from mesa.batchrunner import BatchRunner\n", - "\n", - "class MoneyAgent(Agent):\n", - " \"\"\" An agent with fixed initial wealth.\"\"\"\n", - " def __init__(self, unique_id, starting_wealth):\n", - " # Each agent should have a unique_id\n", - " self.unique_id = unique_id\n", - " self.wealth = starting_wealth\n", - " \n", - " def step(self, model):\n", - " \"\"\"Give money to another agent.\"\"\"\n", - " if self.wealth > 0:\n", - " # Pick a random agent\n", - " other = random.choice(model.schedule.agents)\n", - " # Give them 1 unit money\n", - " other.wealth += 1\n", - " self.wealth -= 1\n", - "\n", - "class MoneyModel(Model):\n", - " \"\"\"A model with some number of agents.\"\"\"\n", - " \n", - " def __init__(self, N, starting_wealth):\n", - " self.running = True\n", - " self.num_agents = N\n", - " self.starting_wealth = starting_wealth\n", - " self.schedule = RandomActivation(self)\n", - " self.create_agents()\n", - " ar = {\"Wealth\": lambda a: a.wealth}\n", - " self.dc = DataCollector(agent_reporters=ar)\n", - "\n", - " def create_agents(self):\n", - " \"\"\"Method to create all the agents.\"\"\"\n", - " for i in range(self.num_agents):\n", - " a = MoneyAgent(i, self.starting_wealth)\n", - " self.schedule.add(a)\n", - " \n", - " def step(self):\n", - " self.dc.collect(self)\n", - " self.schedule.step()\n", - "\n", - " def run_model(self, steps):\n", - " \"\"\"The model has no end condition\n", - " so the user needs to specify how long to run\"\"\"\n", - " for _ in range(steps):\n", - " self.step()\n", - "\n", - "def compute_gini(model):\n", - " agent_wealths = [agent.wealth for agent in model.schedule.agents]\n", - " x = sorted(agent_wealths)\n", - " N = model.num_agents\n", - " B = sum( xi * (N-i) for i,xi in enumerate(x) ) / (N*sum(x))\n", - " return (1 + (1/N) - 2*B)\n", - "\n", - "param_values = {\"N\": 100, \"starting_wealth\": range(1,10)}\n", - "model_reporter={\"Gini\": compute_gini}\n", - "batch = BatchRunner(MoneyModel, param_values,\n", - " 10, 1000, model_reporter)\n", - "batch.run_all()\n", - "out = batch.get_model_vars_dataframe()\n", - "plt.scatter(out.starting_wealth, out.Gini)\n", - "\n", - "plt.grid(True)\n", - "plt.xlabel(\"Starting wealth\")\n", - "plt.ylabel(\"Gini Coefficient\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.4.2" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/examples/Tutorial-Boltzmann_Wealth_Model/Viz_MoneyModel.py b/examples/Tutorial-Boltzmann_Wealth_Model/Viz_MoneyModel.py new file mode 100644 index 00000000000..910114642a0 --- /dev/null +++ b/examples/Tutorial-Boltzmann_Wealth_Model/Viz_MoneyModel.py @@ -0,0 +1,27 @@ +from MoneyModel import * +from mesa.visualization.modules import CanvasGrid +from mesa.visualization.modules import ChartModule +from mesa.visualization.ModularVisualization import ModularServer + + +def agent_portrayal(agent): + portrayal = {"Shape": "circle", + "Filled": "true", + "r": 0.5} + + if agent.wealth > 0: + portrayal["Color"] = "red" + portrayal["Layer"] = 0 + else: + portrayal["Color"] = "grey" + portrayal["Layer"] = 1 + portrayal["r"] = 0.2 + return portrayal + +grid = CanvasGrid(agent_portrayal, 10, 10, 500, 500) +chart = ChartModule([{"Label": "Gini", "Color": "Black"}], + data_collector_name='datacollector') + +server = ModularServer(MoneyModel, [grid, chart], "Money Model", 100, 10, 10) +server.port = 8889 +server.launch() \ No newline at end of file diff --git a/mesa/space.py b/mesa/space.py index 77fe817430d..082d9d30b71 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -173,9 +173,9 @@ def get_neighborhood(self, pos, moore, Args: pos: Coordinate tuple for the neighborhood to get. moore: If True, return Moore neighborhood - (including diagonals) + (including diagonals) If False, return Von Neumann neighborhood - (exclude diagonals) + (exclude diagonals) include_center: If True, return the (x, y) cell as well. Otherwise, return surrounding cells only. radius: radius, in cells, of neighborhood to get. diff --git a/mesa/visualization/templates/Chart.min.js b/mesa/visualization/templates/Chart.min.js old mode 100644 new mode 100755 index 7dd4e43c331..971c291c856 --- a/mesa/visualization/templates/Chart.min.js +++ b/mesa/visualization/templates/Chart.min.js @@ -1,11 +1,11 @@ /*! * Chart.js * http://chartjs.org/ - * Version: 1.0.1 + * Version: 1.0.2 * * Copyright 2015 Nick Downie * Released under the MIT license * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md */ -(function(){"use strict";var t=this,i=t.Chart,e=function(t){this.canvas=t.canvas,this.ctx=t;this.width=t.canvas.width,this.height=t.canvas.height;return this.aspectRatio=this.width/this.height,s.retinaScale(this),this};e.defaults={global:{animation:!0,animationSteps:60,animationEasing:"easeOutQuart",showScale:!0,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleIntegersOnly:!0,scaleBeginAtZero:!1,scaleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",responsive:!1,maintainAspectRatio:!0,showTooltips:!0,customTooltips:!1,tooltipEvents:["mousemove","touchstart","touchmove","mouseout"],tooltipFillColor:"rgba(0,0,0,0.8)",tooltipFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipFontSize:14,tooltipFontStyle:"normal",tooltipFontColor:"#fff",tooltipTitleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipTitleFontSize:14,tooltipTitleFontStyle:"bold",tooltipTitleFontColor:"#fff",tooltipYPadding:6,tooltipXPadding:6,tooltipCaretSize:8,tooltipCornerRadius:6,tooltipXOffset:10,tooltipTemplate:"<%if (label){%><%=label%>: <%}%><%= value %>",multiTooltipTemplate:"<%= value %>",multiTooltipKeyBackground:"#fff",onAnimationProgress:function(){},onAnimationComplete:function(){}}},e.types={};var s=e.helpers={},n=s.each=function(t,i,e){var s=Array.prototype.slice.call(arguments,3);if(t)if(t.length===+t.length){var n;for(n=0;n=0;s--){var n=t[s];if(i(n))return n}},s.inherits=function(t){var i=this,e=t&&t.hasOwnProperty("constructor")?t.constructor:function(){return i.apply(this,arguments)},s=function(){this.constructor=e};return s.prototype=i.prototype,e.prototype=new s,e.extend=r,t&&a(e.prototype,t),e.__super__=i.prototype,e}),c=s.noop=function(){},u=s.uid=function(){var t=0;return function(){return"chart-"+t++}}(),d=s.warn=function(t){window.console&&"function"==typeof window.console.warn&&console.warn(t)},p=s.amd="function"==typeof define&&define.amd,f=s.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},g=s.max=function(t){return Math.max.apply(Math,t)},m=s.min=function(t){return Math.min.apply(Math,t)},v=(s.cap=function(t,i,e){if(f(i)){if(t>i)return i}else if(f(e)&&e>t)return e;return t},s.getDecimalPlaces=function(t){return t%1!==0&&f(t)?t.toString().split(".")[1].length:0}),S=s.radians=function(t){return t*(Math.PI/180)},x=(s.getAngleFromPoint=function(t,i){var e=i.x-t.x,s=i.y-t.y,n=Math.sqrt(e*e+s*s),o=2*Math.PI+Math.atan2(s,e);return 0>e&&0>s&&(o+=2*Math.PI),{angle:o,distance:n}},s.aliasPixel=function(t){return t%2===0?0:.5}),y=(s.splineCurve=function(t,i,e,s){var n=Math.sqrt(Math.pow(i.x-t.x,2)+Math.pow(i.y-t.y,2)),o=Math.sqrt(Math.pow(e.x-i.x,2)+Math.pow(e.y-i.y,2)),a=s*n/(n+o),h=s*o/(n+o);return{inner:{x:i.x-a*(e.x-t.x),y:i.y-a*(e.y-t.y)},outer:{x:i.x+h*(e.x-t.x),y:i.y+h*(e.y-t.y)}}},s.calculateOrderOfMagnitude=function(t){return Math.floor(Math.log(t)/Math.LN10)}),C=(s.calculateScaleRange=function(t,i,e,s,n){var o=2,a=Math.floor(i/(1.5*e)),h=o>=a,l=g(t),r=m(t);l===r&&(l+=.5,r>=.5&&!s?r-=.5:l+=.5);for(var c=Math.abs(l-r),u=y(c),d=Math.ceil(l/(1*Math.pow(10,u)))*Math.pow(10,u),p=s?0:Math.floor(r/(1*Math.pow(10,u)))*Math.pow(10,u),f=d-p,v=Math.pow(10,u),S=Math.round(f/v);(S>a||a>2*S)&&!h;)if(S>a)v*=2,S=Math.round(f/v),S%1!==0&&(h=!0);else if(n&&u>=0){if(v/2%1!==0)break;v/=2,S=Math.round(f/v)}else v/=2,S=Math.round(f/v);return h&&(S=o,v=f/S),{steps:S,stepValue:v,min:p,max:p+S*v}},s.template=function(t,i){function e(t,i){var e=/\W/.test(t)?new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+t.replace(/[\r\t\n]/g," ").split("<%").join(" ").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split(" ").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');"):s[t]=s[t];return i?e(i):e}if(t instanceof Function)return t(i);var s={};return e(t,i)}),w=(s.generateLabels=function(t,i,e,s){var o=new Array(i);return labelTemplateString&&n(o,function(i,n){o[n]=C(t,{value:e+s*(n+1)})}),o},s.easingEffects={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return-1*t*(t-2)},easeInOutQuad:function(t){return(t/=.5)<1?.5*t*t:-0.5*(--t*(t-2)-1)},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return 1*((t=t/1-1)*t*t+1)},easeInOutCubic:function(t){return(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return-1*((t=t/1-1)*t*t*t-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*t*t*t*t:-0.5*((t-=2)*t*t*t-2)},easeInQuint:function(t){return 1*(t/=1)*t*t*t*t},easeOutQuint:function(t){return 1*((t=t/1-1)*t*t*t*t+1)},easeInOutQuint:function(t){return(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},easeInSine:function(t){return-1*Math.cos(t/1*(Math.PI/2))+1},easeOutSine:function(t){return 1*Math.sin(t/1*(Math.PI/2))},easeInOutSine:function(t){return-0.5*(Math.cos(Math.PI*t/1)-1)},easeInExpo:function(t){return 0===t?1:1*Math.pow(2,10*(t/1-1))},easeOutExpo:function(t){return 1===t?1:1*(-Math.pow(2,-10*t/1)+1)},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(-Math.pow(2,-10*--t)+2)},easeInCirc:function(t){return t>=1?t:-1*(Math.sqrt(1-(t/=1)*t)-1)},easeOutCirc:function(t){return 1*Math.sqrt(1-(t=t/1-1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-0.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var i=1.70158,e=0,s=1;return 0===t?0:1==(t/=1)?1:(e||(e=.3),st?-.5*s*Math.pow(2,10*(t-=1))*Math.sin(2*(1*t-i)*Math.PI/e):s*Math.pow(2,-10*(t-=1))*Math.sin(2*(1*t-i)*Math.PI/e)*.5+1)},easeInBack:function(t){var i=1.70158;return 1*(t/=1)*t*((i+1)*t-i)},easeOutBack:function(t){var i=1.70158;return 1*((t=t/1-1)*t*((i+1)*t+i)+1)},easeInOutBack:function(t){var i=1.70158;return(t/=.5)<1?.5*t*t*(((i*=1.525)+1)*t-i):.5*((t-=2)*t*(((i*=1.525)+1)*t+i)+2)},easeInBounce:function(t){return 1-w.easeOutBounce(1-t)},easeOutBounce:function(t){return(t/=1)<1/2.75?7.5625*t*t:2/2.75>t?1*(7.5625*(t-=1.5/2.75)*t+.75):2.5/2.75>t?1*(7.5625*(t-=2.25/2.75)*t+.9375):1*(7.5625*(t-=2.625/2.75)*t+.984375)},easeInOutBounce:function(t){return.5>t?.5*w.easeInBounce(2*t):.5*w.easeOutBounce(2*t-1)+.5}}),b=s.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)}}(),P=(s.cancelAnimFrame=function(){return window.cancelAnimationFrame||window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||window.oCancelAnimationFrame||window.msCancelAnimationFrame||function(t){return window.clearTimeout(t,1e3/60)}}(),s.animationLoop=function(t,i,e,s,n,o){var a=0,h=w[e]||w.linear,l=function(){a++;var e=a/i,r=h(e);t.call(o,r,e,a),s.call(o,r,e),i>a?o.animationFrame=b(l):n.apply(o)};b(l)},s.getRelativePosition=function(t){var i,e,s=t.originalEvent||t,n=t.currentTarget||t.srcElement,o=n.getBoundingClientRect();return s.touches?(i=s.touches[0].clientX-o.left,e=s.touches[0].clientY-o.top):(i=s.clientX-o.left,e=s.clientY-o.top),{x:i,y:e}},s.addEvent=function(t,i,e){t.addEventListener?t.addEventListener(i,e):t.attachEvent?t.attachEvent("on"+i,e):t["on"+i]=e}),L=s.removeEvent=function(t,i,e){t.removeEventListener?t.removeEventListener(i,e,!1):t.detachEvent?t.detachEvent("on"+i,e):t["on"+i]=c},k=(s.bindEvents=function(t,i,e){t.events||(t.events={}),n(i,function(i){t.events[i]=function(){e.apply(t,arguments)},P(t.chart.canvas,i,t.events[i])})},s.unbindEvents=function(t,i){n(i,function(i,e){L(t.chart.canvas,e,i)})}),F=s.getMaximumWidth=function(t){var i=t.parentNode;return i.clientWidth},R=s.getMaximumHeight=function(t){var i=t.parentNode;return i.clientHeight},T=(s.getMaximumSize=s.getMaximumWidth,s.retinaScale=function(t){var i=t.ctx,e=t.canvas.width,s=t.canvas.height;window.devicePixelRatio&&(i.canvas.style.width=e+"px",i.canvas.style.height=s+"px",i.canvas.height=s*window.devicePixelRatio,i.canvas.width=e*window.devicePixelRatio,i.scale(window.devicePixelRatio,window.devicePixelRatio))}),A=s.clear=function(t){t.ctx.clearRect(0,0,t.width,t.height)},M=s.fontString=function(t,i,e){return i+" "+t+"px "+e},W=s.longestText=function(t,i,e){t.font=i;var s=0;return n(e,function(i){var e=t.measureText(i).width;s=e>s?e:s}),s},z=s.drawRoundedRectangle=function(t,i,e,s,n,o){t.beginPath(),t.moveTo(i+o,e),t.lineTo(i+s-o,e),t.quadraticCurveTo(i+s,e,i+s,e+o),t.lineTo(i+s,e+n-o),t.quadraticCurveTo(i+s,e+n,i+s-o,e+n),t.lineTo(i+o,e+n),t.quadraticCurveTo(i,e+n,i,e+n-o),t.lineTo(i,e+o),t.quadraticCurveTo(i,e,i+o,e),t.closePath()};e.instances={},e.Type=function(t,i,s){this.options=i,this.chart=s,this.id=u(),e.instances[this.id]=this,i.responsive&&this.resize(),this.initialize.call(this,t)},a(e.Type.prototype,{initialize:function(){return this},clear:function(){return A(this.chart),this},stop:function(){return s.cancelAnimFrame.call(t,this.animationFrame),this},resize:function(t){this.stop();var i=this.chart.canvas,e=F(this.chart.canvas),s=this.options.maintainAspectRatio?e/this.chart.aspectRatio:R(this.chart.canvas);return i.width=this.chart.width=e,i.height=this.chart.height=s,T(this.chart),"function"==typeof t&&t.apply(this,Array.prototype.slice.call(arguments,1)),this},reflow:c,render:function(t){return t&&this.reflow(),this.options.animation&&!t?s.animationLoop(this.draw,this.options.animationSteps,this.options.animationEasing,this.options.onAnimationProgress,this.options.onAnimationComplete,this):(this.draw(),this.options.onAnimationComplete.call(this)),this},generateLegend:function(){return C(this.options.legendTemplate,this)},destroy:function(){this.clear(),k(this,this.events);var t=this.chart.canvas;t.width=this.chart.width,t.height=this.chart.height,t.style.removeProperty?(t.style.removeProperty("width"),t.style.removeProperty("height")):(t.style.removeAttribute("width"),t.style.removeAttribute("height")),delete e.instances[this.id]},showTooltip:function(t,i){"undefined"==typeof this.activeElements&&(this.activeElements=[]);var o=function(t){var i=!1;return t.length!==this.activeElements.length?i=!0:(n(t,function(t,e){t!==this.activeElements[e]&&(i=!0)},this),i)}.call(this,t);if(o||i){if(this.activeElements=t,this.draw(),this.options.customTooltips&&this.options.customTooltips(!1),t.length>0)if(this.datasets&&this.datasets.length>1){for(var a,h,r=this.datasets.length-1;r>=0&&(a=this.datasets[r].points||this.datasets[r].bars||this.datasets[r].segments,h=l(a,t[0]),-1===h);r--);var c=[],u=[],d=function(){var t,i,e,n,o,a=[],l=[],r=[];return s.each(this.datasets,function(i){t=i.points||i.bars||i.segments,t[h]&&t[h].hasValue()&&a.push(t[h])}),s.each(a,function(t){l.push(t.x),r.push(t.y),c.push(s.template(this.options.multiTooltipTemplate,t)),u.push({fill:t._saved.fillColor||t.fillColor,stroke:t._saved.strokeColor||t.strokeColor})},this),o=m(r),e=g(r),n=m(l),i=g(l),{x:n>this.chart.width/2?n:i,y:(o+e)/2}}.call(this,h);new e.MultiTooltip({x:d.x,y:d.y,xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,xOffset:this.options.tooltipXOffset,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor,fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,titleTextColor:this.options.tooltipTitleFontColor,titleFontFamily:this.options.tooltipTitleFontFamily,titleFontStyle:this.options.tooltipTitleFontStyle,titleFontSize:this.options.tooltipTitleFontSize,cornerRadius:this.options.tooltipCornerRadius,labels:c,legendColors:u,legendColorBackground:this.options.multiTooltipKeyBackground,title:t[0].label,chart:this.chart,ctx:this.chart.ctx,custom:this.options.customTooltips}).draw()}else n(t,function(t){var i=t.tooltipPosition();new e.Tooltip({x:Math.round(i.x),y:Math.round(i.y),xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor,fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,caretHeight:this.options.tooltipCaretSize,cornerRadius:this.options.tooltipCornerRadius,text:C(this.options.tooltipTemplate,t),chart:this.chart,custom:this.options.customTooltips}).draw()},this);return this}},toBase64Image:function(){return this.chart.canvas.toDataURL.apply(this.chart.canvas,arguments)}}),e.Type.extend=function(t){var i=this,s=function(){return i.apply(this,arguments)};if(s.prototype=o(i.prototype),a(s.prototype,t),s.extend=e.Type.extend,t.name||i.prototype.name){var n=t.name||i.prototype.name,l=e.defaults[i.prototype.name]?o(e.defaults[i.prototype.name]):{};e.defaults[n]=a(l,t.defaults),e.types[n]=s,e.prototype[n]=function(t,i){var o=h(e.defaults.global,e.defaults[n],i||{});return new s(t,o,this)}}else d("Name not provided for this chart, so it hasn't been registered");return i},e.Element=function(t){a(this,t),this.initialize.apply(this,arguments),this.save()},a(e.Element.prototype,{initialize:function(){},restore:function(t){return t?n(t,function(t){this[t]=this._saved[t]},this):a(this,this._saved),this},save:function(){return this._saved=o(this),delete this._saved._saved,this},update:function(t){return n(t,function(t,i){this._saved[i]=this[i],this[i]=t},this),this},transition:function(t,i){return n(t,function(t,e){this[e]=(t-this._saved[e])*i+this._saved[e]},this),this},tooltipPosition:function(){return{x:this.x,y:this.y}},hasValue:function(){return f(this.value)}}),e.Element.extend=r,e.Point=e.Element.extend({display:!0,inRange:function(t,i){var e=this.hitDetectionRadius+this.radius;return Math.pow(t-this.x,2)+Math.pow(i-this.y,2)=this.startAngle&&e.angle<=this.endAngle,o=e.distance>=this.innerRadius&&e.distance<=this.outerRadius;return n&&o},tooltipPosition:function(){var t=this.startAngle+(this.endAngle-this.startAngle)/2,i=(this.outerRadius-this.innerRadius)/2+this.innerRadius;return{x:this.x+Math.cos(t)*i,y:this.y+Math.sin(t)*i}},draw:function(t){var i=this.ctx;i.beginPath(),i.arc(this.x,this.y,this.outerRadius,this.startAngle,this.endAngle),i.arc(this.x,this.y,this.innerRadius,this.endAngle,this.startAngle,!0),i.closePath(),i.strokeStyle=this.strokeColor,i.lineWidth=this.strokeWidth,i.fillStyle=this.fillColor,i.fill(),i.lineJoin="bevel",this.showStroke&&i.stroke()}}),e.Rectangle=e.Element.extend({draw:function(){var t=this.ctx,i=this.width/2,e=this.x-i,s=this.x+i,n=this.base-(this.base-this.y),o=this.strokeWidth/2;this.showStroke&&(e+=o,s-=o,n+=o),t.beginPath(),t.fillStyle=this.fillColor,t.strokeStyle=this.strokeColor,t.lineWidth=this.strokeWidth,t.moveTo(e,this.base),t.lineTo(e,n),t.lineTo(s,n),t.lineTo(s,this.base),t.fill(),this.showStroke&&t.stroke()},height:function(){return this.base-this.y},inRange:function(t,i){return t>=this.x-this.width/2&&t<=this.x+this.width/2&&i>=this.y&&i<=this.base}}),e.Tooltip=e.Element.extend({draw:function(){var t=this.chart.ctx;t.font=M(this.fontSize,this.fontStyle,this.fontFamily),this.xAlign="center",this.yAlign="above";var i=this.caretPadding=2,e=t.measureText(this.text).width+2*this.xPadding,s=this.fontSize+2*this.yPadding,n=s+this.caretHeight+i;this.x+e/2>this.chart.width?this.xAlign="left":this.x-e/2<0&&(this.xAlign="right"),this.y-n<0&&(this.yAlign="below");var o=this.x-e/2,a=this.y-n;if(t.fillStyle=this.fillColor,this.custom)this.custom(this);else{switch(this.yAlign){case"above":t.beginPath(),t.moveTo(this.x,this.y-i),t.lineTo(this.x+this.caretHeight,this.y-(i+this.caretHeight)),t.lineTo(this.x-this.caretHeight,this.y-(i+this.caretHeight)),t.closePath(),t.fill();break;case"below":a=this.y+i+this.caretHeight,t.beginPath(),t.moveTo(this.x,this.y+i),t.lineTo(this.x+this.caretHeight,this.y+i+this.caretHeight),t.lineTo(this.x-this.caretHeight,this.y+i+this.caretHeight),t.closePath(),t.fill()}switch(this.xAlign){case"left":o=this.x-e+(this.cornerRadius+this.caretHeight);break;case"right":o=this.x-(this.cornerRadius+this.caretHeight)}z(t,o,a,e,s,this.cornerRadius),t.fill(),t.fillStyle=this.textColor,t.textAlign="center",t.textBaseline="middle",t.fillText(this.text,o+e/2,a+s/2)}}}),e.MultiTooltip=e.Element.extend({initialize:function(){this.font=M(this.fontSize,this.fontStyle,this.fontFamily),this.titleFont=M(this.titleFontSize,this.titleFontStyle,this.titleFontFamily),this.height=this.labels.length*this.fontSize+(this.labels.length-1)*(this.fontSize/2)+2*this.yPadding+1.5*this.titleFontSize,this.ctx.font=this.titleFont;var t=this.ctx.measureText(this.title).width,i=W(this.ctx,this.font,this.labels)+this.fontSize+3,e=g([i,t]);this.width=e+2*this.xPadding;var s=this.height/2;this.y-s<0?this.y=s:this.y+s>this.chart.height&&(this.y=this.chart.height-s),this.x>this.chart.width/2?this.x-=this.xOffset+this.width:this.x+=this.xOffset},getLineHeight:function(t){var i=this.y-this.height/2+this.yPadding,e=t-1;return 0===t?i+this.titleFontSize/2:i+(1.5*this.fontSize*e+this.fontSize/2)+1.5*this.titleFontSize},draw:function(){if(this.custom)this.custom(this);else{z(this.ctx,this.x,this.y-this.height/2,this.width,this.height,this.cornerRadius);var t=this.ctx;t.fillStyle=this.fillColor,t.fill(),t.closePath(),t.textAlign="left",t.textBaseline="middle",t.fillStyle=this.titleTextColor,t.font=this.titleFont,t.fillText(this.title,this.x+this.xPadding,this.getLineHeight(0)),t.font=this.font,s.each(this.labels,function(i,e){t.fillStyle=this.textColor,t.fillText(i,this.x+this.xPadding+this.fontSize+3,this.getLineHeight(e+1)),t.fillStyle=this.legendColorBackground,t.fillRect(this.x+this.xPadding,this.getLineHeight(e+1)-this.fontSize/2,this.fontSize,this.fontSize),t.fillStyle=this.legendColors[e].fill,t.fillRect(this.x+this.xPadding,this.getLineHeight(e+1)-this.fontSize/2,this.fontSize,this.fontSize)},this)}}}),e.Scale=e.Element.extend({initialize:function(){this.fit()},buildYLabels:function(){this.yLabels=[];for(var t=v(this.stepValue),i=0;i<=this.steps;i++)this.yLabels.push(C(this.templateString,{value:(this.min+i*this.stepValue).toFixed(t)}));this.yLabelWidth=this.display&&this.showLabels?W(this.ctx,this.font,this.yLabels):0},addXLabel:function(t){this.xLabels.push(t),this.valuesCount++,this.fit()},removeXLabel:function(){this.xLabels.shift(),this.valuesCount--,this.fit()},fit:function(){this.startPoint=this.display?this.fontSize:0,this.endPoint=this.display?this.height-1.5*this.fontSize-5:this.height,this.startPoint+=this.padding,this.endPoint-=this.padding;var t,i=this.endPoint-this.startPoint;for(this.calculateYRange(i),this.buildYLabels(),this.calculateXLabelRotation();i>this.endPoint-this.startPoint;)i=this.endPoint-this.startPoint,t=this.yLabelWidth,this.calculateYRange(i),this.buildYLabels(),tthis.yLabelWidth+10?e/2:this.yLabelWidth+10,this.xLabelRotation=0,this.display){var n,o=W(this.ctx,this.font,this.xLabels);this.xLabelWidth=o;for(var a=Math.floor(this.calculateX(1)-this.calculateX(0))-6;this.xLabelWidth>a&&0===this.xLabelRotation||this.xLabelWidth>a&&this.xLabelRotation<=90&&this.xLabelRotation>0;)n=Math.cos(S(this.xLabelRotation)),t=n*e,i=n*s,t+this.fontSize/2>this.yLabelWidth+8&&(this.xScalePaddingLeft=t+this.fontSize/2),this.xScalePaddingRight=this.fontSize/2,this.xLabelRotation++,this.xLabelWidth=n*o;this.xLabelRotation>0&&(this.endPoint-=Math.sin(S(this.xLabelRotation))*o+3)}else this.xLabelWidth=0,this.xScalePaddingRight=this.padding,this.xScalePaddingLeft=this.padding},calculateYRange:c,drawingArea:function(){return this.startPoint-this.endPoint},calculateY:function(t){var i=this.drawingArea()/(this.min-this.max);return this.endPoint-i*(t-this.min)},calculateX:function(t){var i=(this.xLabelRotation>0,this.width-(this.xScalePaddingLeft+this.xScalePaddingRight)),e=i/(this.valuesCount-(this.offsetGridLines?0:1)),s=e*t+this.xScalePaddingLeft;return this.offsetGridLines&&(s+=e/2),Math.round(s)},update:function(t){s.extend(this,t),this.fit()},draw:function(){var t=this.ctx,i=(this.endPoint-this.startPoint)/this.steps,e=Math.round(this.xScalePaddingLeft);this.display&&(t.fillStyle=this.textColor,t.font=this.font,n(this.yLabels,function(n,o){var a=this.endPoint-i*o,h=Math.round(a),l=this.showHorizontalLines;t.textAlign="right",t.textBaseline="middle",this.showLabels&&t.fillText(n,e-10,a),0!==o||l||(l=!0),l&&t.beginPath(),o>0?(t.lineWidth=this.gridLineWidth,t.strokeStyle=this.gridLineColor):(t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor),h+=s.aliasPixel(t.lineWidth),l&&(t.moveTo(e,h),t.lineTo(this.width,h),t.stroke(),t.closePath()),t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor,t.beginPath(),t.moveTo(e-5,h),t.lineTo(e,h),t.stroke(),t.closePath()},this),n(this.xLabels,function(i,e){var s=this.calculateX(e)+x(this.lineWidth),n=this.calculateX(e-(this.offsetGridLines?.5:0))+x(this.lineWidth),o=this.xLabelRotation>0,a=this.showVerticalLines;0!==e||a||(a=!0),a&&t.beginPath(),e>0?(t.lineWidth=this.gridLineWidth,t.strokeStyle=this.gridLineColor):(t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor),a&&(t.moveTo(n,this.endPoint),t.lineTo(n,this.startPoint-3),t.stroke(),t.closePath()),t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor,t.beginPath(),t.moveTo(n,this.endPoint),t.lineTo(n,this.endPoint+5),t.stroke(),t.closePath(),t.save(),t.translate(s,o?this.endPoint+12:this.endPoint+8),t.rotate(-1*S(this.xLabelRotation)),t.font=this.font,t.textAlign=o?"right":"center",t.textBaseline=o?"middle":"top",t.fillText(i,0,0),t.restore()},this))}}),e.RadialScale=e.Element.extend({initialize:function(){this.size=m([this.height,this.width]),this.drawingArea=this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2},calculateCenterOffset:function(t){var i=this.drawingArea/(this.max-this.min);return(t-this.min)*i},update:function(){this.lineArc?this.drawingArea=this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2:this.setScaleSize(),this.buildYLabels()},buildYLabels:function(){this.yLabels=[];for(var t=v(this.stepValue),i=0;i<=this.steps;i++)this.yLabels.push(C(this.templateString,{value:(this.min+i*this.stepValue).toFixed(t)}))},getCircumference:function(){return 2*Math.PI/this.valuesCount},setScaleSize:function(){var t,i,e,s,n,o,a,h,l,r,c,u,d=m([this.height/2-this.pointLabelFontSize-5,this.width/2]),p=this.width,g=0;for(this.ctx.font=M(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily),i=0;ip&&(p=t.x+s,n=i),t.x-sp&&(p=t.x+e,n=i):i>this.valuesCount/2&&t.x-e0){var s,n=e*(this.drawingArea/this.steps),o=this.yCenter-n;if(this.lineWidth>0)if(t.strokeStyle=this.lineColor,t.lineWidth=this.lineWidth,this.lineArc)t.beginPath(),t.arc(this.xCenter,this.yCenter,n,0,2*Math.PI),t.closePath(),t.stroke();else{t.beginPath();for(var a=0;a=0;i--){if(this.angleLineWidth>0){var e=this.getPointPosition(i,this.calculateCenterOffset(this.max));t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(e.x,e.y),t.stroke(),t.closePath()}var s=this.getPointPosition(i,this.calculateCenterOffset(this.max)+5);t.font=M(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily),t.fillStyle=this.pointLabelFontColor;var o=this.labels.length,a=this.labels.length/2,h=a/2,l=h>i||i>o-h,r=i===h||i===o-h;t.textAlign=0===i?"center":i===a?"center":a>i?"left":"right",t.textBaseline=r?"middle":l?"bottom":"top",t.fillText(this.labels[i],s.x,s.y)}}}}}),s.addEvent(window,"resize",function(){var t;return function(){clearTimeout(t),t=setTimeout(function(){n(e.instances,function(t){t.options.responsive&&t.resize(t.render,!0)})},50)}}()),p?define(function(){return e}):"object"==typeof module&&module.exports&&(module.exports=e),t.Chart=e,e.noConflict=function(){return t.Chart=i,e}}).call(this),function(){"use strict";var t=this,i=t.Chart,e=i.helpers,s={scaleBeginAtZero:!0,scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,scaleShowHorizontalLines:!0,scaleShowVerticalLines:!0,barShowStroke:!0,barStrokeWidth:2,barValueSpacing:5,barDatasetSpacing:1,legendTemplate:'
    <% for (var i=0; i
  • <%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>
'};i.Type.extend({name:"Bar",defaults:s,initialize:function(t){var s=this.options;this.ScaleClass=i.Scale.extend({offsetGridLines:!0,calculateBarX:function(t,i,e){var n=this.calculateBaseWidth(),o=this.calculateX(e)-n/2,a=this.calculateBarWidth(t);return o+a*i+i*s.barDatasetSpacing+a/2},calculateBaseWidth:function(){return this.calculateX(1)-this.calculateX(0)-2*s.barValueSpacing},calculateBarWidth:function(t){var i=this.calculateBaseWidth()-(t-1)*s.barDatasetSpacing;return i/t}}),this.datasets=[],this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getBarsAtEvent(t):[];this.eachBars(function(t){t.restore(["fillColor","strokeColor"])}),e.each(i,function(t){t.fillColor=t.highlightFill,t.strokeColor=t.highlightStroke}),this.showTooltip(i)}),this.BarClass=i.Rectangle.extend({strokeWidth:this.options.barStrokeWidth,showStroke:this.options.barShowStroke,ctx:this.chart.ctx}),e.each(t.datasets,function(i){var s={label:i.label||null,fillColor:i.fillColor,strokeColor:i.strokeColor,bars:[]};this.datasets.push(s),e.each(i.data,function(e,n){s.bars.push(new this.BarClass({value:e,label:t.labels[n],datasetLabel:i.label,strokeColor:i.strokeColor,fillColor:i.fillColor,highlightFill:i.highlightFill||i.fillColor,highlightStroke:i.highlightStroke||i.strokeColor}))},this)},this),this.buildScale(t.labels),this.BarClass.prototype.base=this.scale.endPoint,this.eachBars(function(t,i,s){e.extend(t,{width:this.scale.calculateBarWidth(this.datasets.length),x:this.scale.calculateBarX(this.datasets.length,s,i),y:this.scale.endPoint}),t.save()},this),this.render()},update:function(){this.scale.update(),e.each(this.activeElements,function(t){t.restore(["fillColor","strokeColor"])}),this.eachBars(function(t){t.save()}),this.render()},eachBars:function(t){e.each(this.datasets,function(i,s){e.each(i.bars,t,this,s)},this)},getBarsAtEvent:function(t){for(var i,s=[],n=e.getRelativePosition(t),o=function(t){s.push(t.bars[i])},a=0;a<% for (var i=0; i
  • <%if(segments[i].label){%><%=segments[i].label%><%}%>
  • <%}%>'};i.Type.extend({name:"Doughnut",defaults:s,initialize:function(t){this.segments=[],this.outerRadius=(e.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2,this.SegmentArc=i.Arc.extend({ctx:this.chart.ctx,x:this.chart.width/2,y:this.chart.height/2}),this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getSegmentsAtEvent(t):[];e.each(this.segments,function(t){t.restore(["fillColor"])}),e.each(i,function(t){t.fillColor=t.highlightColor}),this.showTooltip(i)}),this.calculateTotal(t),e.each(t,function(t,i){this.addData(t,i,!0)},this),this.render()},getSegmentsAtEvent:function(t){var i=[],s=e.getRelativePosition(t);return e.each(this.segments,function(t){t.inRange(s.x,s.y)&&i.push(t)},this),i},addData:function(t,i,e){var s=i||this.segments.length;this.segments.splice(s,0,new this.SegmentArc({value:t.value,outerRadius:this.options.animateScale?0:this.outerRadius,innerRadius:this.options.animateScale?0:this.outerRadius/100*this.options.percentageInnerCutout,fillColor:t.color,highlightColor:t.highlight||t.color,showStroke:this.options.segmentShowStroke,strokeWidth:this.options.segmentStrokeWidth,strokeColor:this.options.segmentStrokeColor,startAngle:1.5*Math.PI,circumference:this.options.animateRotate?0:this.calculateCircumference(t.value),label:t.label})),e||(this.reflow(),this.update())},calculateCircumference:function(t){return 2*Math.PI*(t/this.total)},calculateTotal:function(t){this.total=0,e.each(t,function(t){this.total+=t.value},this)},update:function(){this.calculateTotal(this.segments),e.each(this.activeElements,function(t){t.restore(["fillColor"])}),e.each(this.segments,function(t){t.save()}),this.render()},removeData:function(t){var i=e.isNumber(t)?t:this.segments.length-1;this.segments.splice(i,1),this.reflow(),this.update()},reflow:function(){e.extend(this.SegmentArc.prototype,{x:this.chart.width/2,y:this.chart.height/2}),this.outerRadius=(e.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2,e.each(this.segments,function(t){t.update({outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout})},this)},draw:function(t){var i=t?t:1;this.clear(),e.each(this.segments,function(t,e){t.transition({circumference:this.calculateCircumference(t.value),outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout},i),t.endAngle=t.startAngle+t.circumference,t.draw(),0===e&&(t.startAngle=1.5*Math.PI),e<% for (var i=0; i
  • <%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>'};i.Type.extend({name:"Line",defaults:s,initialize:function(t){this.PointClass=i.Point.extend({strokeWidth:this.options.pointDotStrokeWidth,radius:this.options.pointDotRadius,display:this.options.pointDot,hitDetectionRadius:this.options.pointHitDetectionRadius,ctx:this.chart.ctx,inRange:function(t){return Math.pow(t-this.x,2)0&&ithis.scale.endPoint?t.controlPoints.outer.y=this.scale.endPoint:t.controlPoints.outer.ythis.scale.endPoint?t.controlPoints.inner.y=this.scale.endPoint:t.controlPoints.inner.y0&&(s.lineTo(h[h.length-1].x,this.scale.endPoint),s.lineTo(h[0].x,this.scale.endPoint),s.fillStyle=t.fillColor,s.closePath(),s.fill()),e.each(h,function(t){t.draw()})},this)}})}.call(this),function(){"use strict";var t=this,i=t.Chart,e=i.helpers,s={scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",scaleBeginAtZero:!0,scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,scaleShowLine:!0,segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,legendTemplate:'
      <% for (var i=0; i
    • <%if(segments[i].label){%><%=segments[i].label%><%}%>
    • <%}%>
    '};i.Type.extend({name:"PolarArea",defaults:s,initialize:function(t){this.segments=[],this.SegmentArc=i.Arc.extend({showStroke:this.options.segmentShowStroke,strokeWidth:this.options.segmentStrokeWidth,strokeColor:this.options.segmentStrokeColor,ctx:this.chart.ctx,innerRadius:0,x:this.chart.width/2,y:this.chart.height/2}),this.scale=new i.RadialScale({display:this.options.showScale,fontStyle:this.options.scaleFontStyle,fontSize:this.options.scaleFontSize,fontFamily:this.options.scaleFontFamily,fontColor:this.options.scaleFontColor,showLabels:this.options.scaleShowLabels,showLabelBackdrop:this.options.scaleShowLabelBackdrop,backdropColor:this.options.scaleBackdropColor,backdropPaddingY:this.options.scaleBackdropPaddingY,backdropPaddingX:this.options.scaleBackdropPaddingX,lineWidth:this.options.scaleShowLine?this.options.scaleLineWidth:0,lineColor:this.options.scaleLineColor,lineArc:!0,width:this.chart.width,height:this.chart.height,xCenter:this.chart.width/2,yCenter:this.chart.height/2,ctx:this.chart.ctx,templateString:this.options.scaleLabel,valuesCount:t.length}),this.updateScaleRange(t),this.scale.update(),e.each(t,function(t,i){this.addData(t,i,!0)},this),this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getSegmentsAtEvent(t):[];e.each(this.segments,function(t){t.restore(["fillColor"])}),e.each(i,function(t){t.fillColor=t.highlightColor}),this.showTooltip(i)}),this.render()},getSegmentsAtEvent:function(t){var i=[],s=e.getRelativePosition(t);return e.each(this.segments,function(t){t.inRange(s.x,s.y)&&i.push(t)},this),i},addData:function(t,i,e){var s=i||this.segments.length;this.segments.splice(s,0,new this.SegmentArc({fillColor:t.color,highlightColor:t.highlight||t.color,label:t.label,value:t.value,outerRadius:this.options.animateScale?0:this.scale.calculateCenterOffset(t.value),circumference:this.options.animateRotate?0:this.scale.getCircumference(),startAngle:1.5*Math.PI})),e||(this.reflow(),this.update())},removeData:function(t){var i=e.isNumber(t)?t:this.segments.length-1;this.segments.splice(i,1),this.reflow(),this.update()},calculateTotal:function(t){this.total=0,e.each(t,function(t){this.total+=t.value},this),this.scale.valuesCount=this.segments.length},updateScaleRange:function(t){var i=[];e.each(t,function(t){i.push(t.value)});var s=this.options.scaleOverride?{steps:this.options.scaleSteps,stepValue:this.options.scaleStepWidth,min:this.options.scaleStartValue,max:this.options.scaleStartValue+this.options.scaleSteps*this.options.scaleStepWidth}:e.calculateScaleRange(i,e.min([this.chart.width,this.chart.height])/2,this.options.scaleFontSize,this.options.scaleBeginAtZero,this.options.scaleIntegersOnly);e.extend(this.scale,s,{size:e.min([this.chart.width,this.chart.height]),xCenter:this.chart.width/2,yCenter:this.chart.height/2})},update:function(){this.calculateTotal(this.segments),e.each(this.segments,function(t){t.save()}),this.render()},reflow:function(){e.extend(this.SegmentArc.prototype,{x:this.chart.width/2,y:this.chart.height/2}),this.updateScaleRange(this.segments),this.scale.update(),e.extend(this.scale,{xCenter:this.chart.width/2,yCenter:this.chart.height/2}),e.each(this.segments,function(t){t.update({outerRadius:this.scale.calculateCenterOffset(t.value)})},this)},draw:function(t){var i=t||1;this.clear(),e.each(this.segments,function(t,e){t.transition({circumference:this.scale.getCircumference(),outerRadius:this.scale.calculateCenterOffset(t.value)},i),t.endAngle=t.startAngle+t.circumference,0===e&&(t.startAngle=1.5*Math.PI),e<% for (var i=0; i
  • <%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>'},initialize:function(t){this.PointClass=i.Point.extend({strokeWidth:this.options.pointDotStrokeWidth,radius:this.options.pointDotRadius,display:this.options.pointDot,hitDetectionRadius:this.options.pointHitDetectionRadius,ctx:this.chart.ctx}),this.datasets=[],this.buildScale(t),this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getPointsAtEvent(t):[];this.eachPoints(function(t){t.restore(["fillColor","strokeColor"])}),e.each(i,function(t){t.fillColor=t.highlightFill,t.strokeColor=t.highlightStroke}),this.showTooltip(i)}),e.each(t.datasets,function(i){var s={label:i.label||null,fillColor:i.fillColor,strokeColor:i.strokeColor,pointColor:i.pointColor,pointStrokeColor:i.pointStrokeColor,points:[]};this.datasets.push(s),e.each(i.data,function(e,n){var o;this.scale.animation||(o=this.scale.getPointPosition(n,this.scale.calculateCenterOffset(e))),s.points.push(new this.PointClass({value:e,label:t.labels[n],datasetLabel:i.label,x:this.options.animation?this.scale.xCenter:o.x,y:this.options.animation?this.scale.yCenter:o.y,strokeColor:i.pointStrokeColor,fillColor:i.pointColor,highlightFill:i.pointHighlightFill||i.pointColor,highlightStroke:i.pointHighlightStroke||i.pointStrokeColor}))},this)},this),this.render()},eachPoints:function(t){e.each(this.datasets,function(i){e.each(i.points,t,this)},this)},getPointsAtEvent:function(t){var i=e.getRelativePosition(t),s=e.getAngleFromPoint({x:this.scale.xCenter,y:this.scale.yCenter},i),n=2*Math.PI/this.scale.valuesCount,o=Math.round((s.angle-1.5*Math.PI)/n),a=[];return(o>=this.scale.valuesCount||0>o)&&(o=0),s.distance<=this.scale.drawingArea&&e.each(this.datasets,function(t){a.push(t.points[o])}),a},buildScale:function(t){this.scale=new i.RadialScale({display:this.options.showScale,fontStyle:this.options.scaleFontStyle,fontSize:this.options.scaleFontSize,fontFamily:this.options.scaleFontFamily,fontColor:this.options.scaleFontColor,showLabels:this.options.scaleShowLabels,showLabelBackdrop:this.options.scaleShowLabelBackdrop,backdropColor:this.options.scaleBackdropColor,backdropPaddingY:this.options.scaleBackdropPaddingY,backdropPaddingX:this.options.scaleBackdropPaddingX,lineWidth:this.options.scaleShowLine?this.options.scaleLineWidth:0,lineColor:this.options.scaleLineColor,angleLineColor:this.options.angleLineColor,angleLineWidth:this.options.angleShowLineOut?this.options.angleLineWidth:0,pointLabelFontColor:this.options.pointLabelFontColor,pointLabelFontSize:this.options.pointLabelFontSize,pointLabelFontFamily:this.options.pointLabelFontFamily,pointLabelFontStyle:this.options.pointLabelFontStyle,height:this.chart.height,width:this.chart.width,xCenter:this.chart.width/2,yCenter:this.chart.height/2,ctx:this.chart.ctx,templateString:this.options.scaleLabel,labels:t.labels,valuesCount:t.datasets[0].data.length}),this.scale.setScaleSize(),this.updateScaleRange(t.datasets),this.scale.buildYLabels()},updateScaleRange:function(t){var i=function(){var i=[];return e.each(t,function(t){t.data?i=i.concat(t.data):e.each(t.points,function(t){i.push(t.value)})}),i}(),s=this.options.scaleOverride?{steps:this.options.scaleSteps,stepValue:this.options.scaleStepWidth,min:this.options.scaleStartValue,max:this.options.scaleStartValue+this.options.scaleSteps*this.options.scaleStepWidth}:e.calculateScaleRange(i,e.min([this.chart.width,this.chart.height])/2,this.options.scaleFontSize,this.options.scaleBeginAtZero,this.options.scaleIntegersOnly);e.extend(this.scale,s)},addData:function(t,i){this.scale.valuesCount++,e.each(t,function(t,e){var s=this.scale.getPointPosition(this.scale.valuesCount,this.scale.calculateCenterOffset(t));this.datasets[e].points.push(new this.PointClass({value:t,label:i,x:s.x,y:s.y,strokeColor:this.datasets[e].pointStrokeColor,fillColor:this.datasets[e].pointColor}))},this),this.scale.labels.push(i),this.reflow(),this.update()},removeData:function(){this.scale.valuesCount--,this.scale.labels.shift(),e.each(this.datasets,function(t){t.points.shift()},this),this.reflow(),this.update()},update:function(){this.eachPoints(function(t){t.save()}),this.reflow(),this.render()},reflow:function(){e.extend(this.scale,{width:this.chart.width,height:this.chart.height,size:e.min([this.chart.width,this.chart.height]),xCenter:this.chart.width/2,yCenter:this.chart.height/2}),this.updateScaleRange(this.datasets),this.scale.setScaleSize(),this.scale.buildYLabels()},draw:function(t){var i=t||1,s=this.chart.ctx;this.clear(),this.scale.draw(),e.each(this.datasets,function(t){e.each(t.points,function(t,e){t.hasValue()&&t.transition(this.scale.getPointPosition(e,this.scale.calculateCenterOffset(t.value)),i)},this),s.lineWidth=this.options.datasetStrokeWidth,s.strokeStyle=t.strokeColor,s.beginPath(),e.each(t.points,function(t,i){0===i?s.moveTo(t.x,t.y):s.lineTo(t.x,t.y)},this),s.closePath(),s.stroke(),s.fillStyle=t.fillColor,s.fill(),e.each(t.points,function(t){t.hasValue()&&t.draw()})},this)}})}.call(this); \ No newline at end of file +(function(){"use strict";var t=this,i=t.Chart,e=function(t){this.canvas=t.canvas,this.ctx=t;var i=function(t,i){return t["offset"+i]?t["offset"+i]:document.defaultView.getComputedStyle(t).getPropertyValue(i)},e=this.width=i(t.canvas,"Width")||t.canvas.width,n=this.height=i(t.canvas,"Height")||t.canvas.height;return e=this.width=t.canvas.width,n=this.height=t.canvas.height,this.aspectRatio=this.width/this.height,s.retinaScale(this),this};e.defaults={global:{animation:!0,animationSteps:60,animationEasing:"easeOutQuart",showScale:!0,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleIntegersOnly:!0,scaleBeginAtZero:!1,scaleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",responsive:!1,maintainAspectRatio:!0,showTooltips:!0,customTooltips:!1,tooltipEvents:["mousemove","touchstart","touchmove","mouseout"],tooltipFillColor:"rgba(0,0,0,0.8)",tooltipFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipFontSize:14,tooltipFontStyle:"normal",tooltipFontColor:"#fff",tooltipTitleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipTitleFontSize:14,tooltipTitleFontStyle:"bold",tooltipTitleFontColor:"#fff",tooltipTitleTemplate:"<%= label%>",tooltipYPadding:6,tooltipXPadding:6,tooltipCaretSize:8,tooltipCornerRadius:6,tooltipXOffset:10,tooltipTemplate:"<%if (label){%><%=label%>: <%}%><%= value %>",multiTooltipTemplate:"<%= datasetLabel %>: <%= value %>",multiTooltipKeyBackground:"#fff",segmentColorDefault:["#A6CEE3","#1F78B4","#B2DF8A","#33A02C","#FB9A99","#E31A1C","#FDBF6F","#FF7F00","#CAB2D6","#6A3D9A","#B4B482","#B15928"],segmentHighlightColorDefaults:["#CEF6FF","#47A0DC","#DAFFB2","#5BC854","#FFC2C1","#FF4244","#FFE797","#FFA728","#F2DAFE","#9265C2","#DCDCAA","#D98150"],onAnimationProgress:function(){},onAnimationComplete:function(){}}},e.types={};var s=e.helpers={},n=s.each=function(t,i,e){var s=Array.prototype.slice.call(arguments,3);if(t)if(t.length===+t.length){var n;for(n=0;n=0;s--){var n=t[s];if(i(n))return n}},s.inherits=function(t){var i=this,e=t&&t.hasOwnProperty("constructor")?t.constructor:function(){return i.apply(this,arguments)},s=function(){this.constructor=e};return s.prototype=i.prototype,e.prototype=new s,e.extend=r,t&&a(e.prototype,t),e.__super__=i.prototype,e}),c=s.noop=function(){},u=s.uid=function(){var t=0;return function(){return"chart-"+t++}}(),d=s.warn=function(t){window.console&&"function"==typeof window.console.warn&&console.warn(t)},p=s.amd="function"==typeof define&&define.amd,f=s.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},g=s.max=function(t){return Math.max.apply(Math,t)},m=s.min=function(t){return Math.min.apply(Math,t)},v=(s.cap=function(t,i,e){if(f(i)){if(t>i)return i}else if(f(e)&&e>t)return e;return t},s.getDecimalPlaces=function(t){if(t%1!==0&&f(t)){var i=t.toString();if(i.indexOf("e-")<0)return i.split(".")[1].length;if(i.indexOf(".")<0)return parseInt(i.split("e-")[1]);var e=i.split(".")[1].split("e-");return e[0].length+parseInt(e[1])}return 0}),S=s.radians=function(t){return t*(Math.PI/180)},x=(s.getAngleFromPoint=function(t,i){var e=i.x-t.x,s=i.y-t.y,n=Math.sqrt(e*e+s*s),o=2*Math.PI+Math.atan2(s,e);return 0>e&&0>s&&(o+=2*Math.PI),{angle:o,distance:n}},s.aliasPixel=function(t){return t%2===0?0:.5}),C=(s.splineCurve=function(t,i,e,s){var n=Math.sqrt(Math.pow(i.x-t.x,2)+Math.pow(i.y-t.y,2)),o=Math.sqrt(Math.pow(e.x-i.x,2)+Math.pow(e.y-i.y,2)),a=s*n/(n+o),h=s*o/(n+o);return{inner:{x:i.x-a*(e.x-t.x),y:i.y-a*(e.y-t.y)},outer:{x:i.x+h*(e.x-t.x),y:i.y+h*(e.y-t.y)}}},s.calculateOrderOfMagnitude=function(t){return Math.floor(Math.log(t)/Math.LN10)}),y=(s.calculateScaleRange=function(t,i,e,s,o){var a=2,h=Math.floor(i/(1.5*e)),l=a>=h,r=[];n(t,function(t){null==t||r.push(t)});var c=m(r),u=g(r);u===c&&(u+=.5,c>=.5&&!s?c-=.5:u+=.5);for(var d=Math.abs(u-c),p=C(d),f=Math.ceil(u/(1*Math.pow(10,p)))*Math.pow(10,p),v=s?0:Math.floor(c/(1*Math.pow(10,p)))*Math.pow(10,p),S=f-v,x=Math.pow(10,p),y=Math.round(S/x);(y>h||h>2*y)&&!l;)if(y>h)x*=2,y=Math.round(S/x),y%1!==0&&(l=!0);else if(o&&p>=0){if(x/2%1!==0)break;x/=2,y=Math.round(S/x)}else x/=2,y=Math.round(S/x);return l&&(y=a,x=S/y),{steps:y,stepValue:x,min:v,max:v+y*x}},s.template=function(t,i){function e(t,i){var e=/\W/.test(t)?new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+t.replace(/[\r\t\n]/g," ").split("<%").join(" ").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split(" ").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');"):s[t]=s[t];return i?e(i):e}if(t instanceof Function)return t(i);var s={};return e(t,i)}),b=(s.generateLabels=function(t,i,e,s){var o=new Array(i);return t&&n(o,function(i,n){o[n]=y(t,{value:e+s*(n+1)})}),o},s.easingEffects={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return-1*t*(t-2)},easeInOutQuad:function(t){return(t/=.5)<1?.5*t*t:-0.5*(--t*(t-2)-1)},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return 1*((t=t/1-1)*t*t+1)},easeInOutCubic:function(t){return(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return-1*((t=t/1-1)*t*t*t-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*t*t*t*t:-0.5*((t-=2)*t*t*t-2)},easeInQuint:function(t){return 1*(t/=1)*t*t*t*t},easeOutQuint:function(t){return 1*((t=t/1-1)*t*t*t*t+1)},easeInOutQuint:function(t){return(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},easeInSine:function(t){return-1*Math.cos(t/1*(Math.PI/2))+1},easeOutSine:function(t){return 1*Math.sin(t/1*(Math.PI/2))},easeInOutSine:function(t){return-0.5*(Math.cos(Math.PI*t/1)-1)},easeInExpo:function(t){return 0===t?1:1*Math.pow(2,10*(t/1-1))},easeOutExpo:function(t){return 1===t?1:1*(-Math.pow(2,-10*t/1)+1)},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(-Math.pow(2,-10*--t)+2)},easeInCirc:function(t){return t>=1?t:-1*(Math.sqrt(1-(t/=1)*t)-1)},easeOutCirc:function(t){return 1*Math.sqrt(1-(t=t/1-1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-0.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var i=1.70158,e=0,s=1;return 0===t?0:1==(t/=1)?1:(e||(e=.3),st?-.5*(s*Math.pow(2,10*(t-=1))*Math.sin((1*t-i)*(2*Math.PI)/e)):s*Math.pow(2,-10*(t-=1))*Math.sin((1*t-i)*(2*Math.PI)/e)*.5+1)},easeInBack:function(t){var i=1.70158;return 1*(t/=1)*t*((i+1)*t-i)},easeOutBack:function(t){var i=1.70158;return 1*((t=t/1-1)*t*((i+1)*t+i)+1)},easeInOutBack:function(t){var i=1.70158;return(t/=.5)<1?.5*(t*t*(((i*=1.525)+1)*t-i)):.5*((t-=2)*t*(((i*=1.525)+1)*t+i)+2)},easeInBounce:function(t){return 1-b.easeOutBounce(1-t)},easeOutBounce:function(t){return(t/=1)<1/2.75?1*(7.5625*t*t):2/2.75>t?1*(7.5625*(t-=1.5/2.75)*t+.75):2.5/2.75>t?1*(7.5625*(t-=2.25/2.75)*t+.9375):1*(7.5625*(t-=2.625/2.75)*t+.984375)},easeInOutBounce:function(t){return.5>t?.5*b.easeInBounce(2*t):.5*b.easeOutBounce(2*t-1)+.5}}),w=s.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)}}(),P=(s.cancelAnimFrame=function(){return window.cancelAnimationFrame||window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||window.oCancelAnimationFrame||window.msCancelAnimationFrame||function(t){return window.clearTimeout(t,1e3/60)}}(),s.animationLoop=function(t,i,e,s,n,o){var a=0,h=b[e]||b.linear,l=function(){a++;var e=a/i,r=h(e);t.call(o,r,e,a),s.call(o,r,e),i>a?o.animationFrame=w(l):n.apply(o)};w(l)},s.getRelativePosition=function(t){var i,e,s=t.originalEvent||t,n=t.currentTarget||t.srcElement,o=n.getBoundingClientRect();return s.touches?(i=s.touches[0].clientX-o.left,e=s.touches[0].clientY-o.top):(i=s.clientX-o.left,e=s.clientY-o.top),{x:i,y:e}},s.addEvent=function(t,i,e){t.addEventListener?t.addEventListener(i,e):t.attachEvent?t.attachEvent("on"+i,e):t["on"+i]=e}),L=s.removeEvent=function(t,i,e){t.removeEventListener?t.removeEventListener(i,e,!1):t.detachEvent?t.detachEvent("on"+i,e):t["on"+i]=c},k=(s.bindEvents=function(t,i,e){t.events||(t.events={}),n(i,function(i){t.events[i]=function(){e.apply(t,arguments)},P(t.chart.canvas,i,t.events[i])})},s.unbindEvents=function(t,i){n(i,function(i,e){L(t.chart.canvas,e,i)})}),F=s.getMaximumWidth=function(t){var i=t.parentNode,e=parseInt(R(i,"padding-left"))+parseInt(R(i,"padding-right"));return i?i.clientWidth-e:0},A=s.getMaximumHeight=function(t){var i=t.parentNode,e=parseInt(R(i,"padding-bottom"))+parseInt(R(i,"padding-top"));return i?i.clientHeight-e:0},R=s.getStyle=function(t,i){return t.currentStyle?t.currentStyle[i]:document.defaultView.getComputedStyle(t,null).getPropertyValue(i)},T=(s.getMaximumSize=s.getMaximumWidth,s.retinaScale=function(t){var i=t.ctx,e=t.canvas.width,s=t.canvas.height;window.devicePixelRatio&&(i.canvas.style.width=e+"px",i.canvas.style.height=s+"px",i.canvas.height=s*window.devicePixelRatio,i.canvas.width=e*window.devicePixelRatio,i.scale(window.devicePixelRatio,window.devicePixelRatio))}),M=s.clear=function(t){t.ctx.clearRect(0,0,t.width,t.height)},W=s.fontString=function(t,i,e){return i+" "+t+"px "+e},z=s.longestText=function(t,i,e){t.font=i;var s=0;return n(e,function(i){var e=t.measureText(i).width;s=e>s?e:s}),s},B=s.drawRoundedRectangle=function(t,i,e,s,n,o){t.beginPath(),t.moveTo(i+o,e),t.lineTo(i+s-o,e),t.quadraticCurveTo(i+s,e,i+s,e+o),t.lineTo(i+s,e+n-o),t.quadraticCurveTo(i+s,e+n,i+s-o,e+n),t.lineTo(i+o,e+n),t.quadraticCurveTo(i,e+n,i,e+n-o),t.lineTo(i,e+o),t.quadraticCurveTo(i,e,i+o,e),t.closePath()};e.instances={},e.Type=function(t,i,s){this.options=i,this.chart=s,this.id=u(),e.instances[this.id]=this,i.responsive&&this.resize(),this.initialize.call(this,t)},a(e.Type.prototype,{initialize:function(){return this},clear:function(){return M(this.chart),this},stop:function(){return e.animationService.cancelAnimation(this),this},resize:function(t){this.stop();var i=this.chart.canvas,e=F(this.chart.canvas),s=this.options.maintainAspectRatio?e/this.chart.aspectRatio:A(this.chart.canvas);return i.width=this.chart.width=e,i.height=this.chart.height=s,T(this.chart),"function"==typeof t&&t.apply(this,Array.prototype.slice.call(arguments,1)),this},reflow:c,render:function(t){if(t&&this.reflow(),this.options.animation&&!t){var i=new e.Animation;i.numSteps=this.options.animationSteps,i.easing=this.options.animationEasing,i.render=function(t,i){var e=s.easingEffects[i.easing],n=i.currentStep/i.numSteps,o=e(n);t.draw(o,n,i.currentStep)},i.onAnimationProgress=this.options.onAnimationProgress,i.onAnimationComplete=this.options.onAnimationComplete,e.animationService.addAnimation(this,i)}else this.draw(),this.options.onAnimationComplete.call(this);return this},generateLegend:function(){return s.template(this.options.legendTemplate,this)},destroy:function(){this.stop(),this.clear(),k(this,this.events);var t=this.chart.canvas;t.width=this.chart.width,t.height=this.chart.height,t.style.removeProperty?(t.style.removeProperty("width"),t.style.removeProperty("height")):(t.style.removeAttribute("width"),t.style.removeAttribute("height")),delete e.instances[this.id]},showTooltip:function(t,i){"undefined"==typeof this.activeElements&&(this.activeElements=[]);var o=function(t){var i=!1;return t.length!==this.activeElements.length?i=!0:(n(t,function(t,e){t!==this.activeElements[e]&&(i=!0)},this),i)}.call(this,t);if(o||i){if(this.activeElements=t,this.draw(),this.options.customTooltips&&this.options.customTooltips(!1),t.length>0)if(this.datasets&&this.datasets.length>1){for(var a,h,r=this.datasets.length-1;r>=0&&(a=this.datasets[r].points||this.datasets[r].bars||this.datasets[r].segments,h=l(a,t[0]),-1===h);r--);var c=[],u=[],d=function(t){var i,e,n,o,a,l=[],r=[],d=[];return s.each(this.datasets,function(t){i=t.points||t.bars||t.segments,i[h]&&i[h].hasValue()&&l.push(i[h])}),s.each(l,function(t){r.push(t.x),d.push(t.y),c.push(s.template(this.options.multiTooltipTemplate,t)),u.push({fill:t._saved.fillColor||t.fillColor,stroke:t._saved.strokeColor||t.strokeColor})},this),a=m(d),n=g(d),o=m(r),e=g(r),{x:o>this.chart.width/2?o:e,y:(a+n)/2}}.call(this,h);new e.MultiTooltip({x:d.x,y:d.y,xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,xOffset:this.options.tooltipXOffset,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor,fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,titleTextColor:this.options.tooltipTitleFontColor,titleFontFamily:this.options.tooltipTitleFontFamily,titleFontStyle:this.options.tooltipTitleFontStyle,titleFontSize:this.options.tooltipTitleFontSize,cornerRadius:this.options.tooltipCornerRadius,labels:c,legendColors:u,legendColorBackground:this.options.multiTooltipKeyBackground,title:y(this.options.tooltipTitleTemplate,t[0]),chart:this.chart,ctx:this.chart.ctx,custom:this.options.customTooltips}).draw()}else n(t,function(t){var i=t.tooltipPosition();new e.Tooltip({x:Math.round(i.x),y:Math.round(i.y),xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor,fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,caretHeight:this.options.tooltipCaretSize,cornerRadius:this.options.tooltipCornerRadius,text:y(this.options.tooltipTemplate,t),chart:this.chart,custom:this.options.customTooltips}).draw()},this);return this}},toBase64Image:function(){return this.chart.canvas.toDataURL.apply(this.chart.canvas,arguments)}}),e.Type.extend=function(t){var i=this,s=function(){return i.apply(this,arguments)};if(s.prototype=o(i.prototype),a(s.prototype,t),s.extend=e.Type.extend,t.name||i.prototype.name){var n=t.name||i.prototype.name,l=e.defaults[i.prototype.name]?o(e.defaults[i.prototype.name]):{};e.defaults[n]=a(l,t.defaults),e.types[n]=s,e.prototype[n]=function(t,i){var o=h(e.defaults.global,e.defaults[n],i||{});return new s(t,o,this)}}else d("Name not provided for this chart, so it hasn't been registered");return i},e.Element=function(t){a(this,t),this.initialize.apply(this,arguments),this.save()},a(e.Element.prototype,{initialize:function(){},restore:function(t){return t?n(t,function(t){this[t]=this._saved[t]},this):a(this,this._saved),this},save:function(){return this._saved=o(this),delete this._saved._saved,this},update:function(t){return n(t,function(t,i){this._saved[i]=this[i],this[i]=t},this),this},transition:function(t,i){return n(t,function(t,e){this[e]=(t-this._saved[e])*i+this._saved[e]},this),this},tooltipPosition:function(){return{x:this.x,y:this.y}},hasValue:function(){return f(this.value)}}),e.Element.extend=r,e.Point=e.Element.extend({display:!0,inRange:function(t,i){var e=this.hitDetectionRadius+this.radius;return Math.pow(t-this.x,2)+Math.pow(i-this.y,2)a?a>=n||n>=o:n>=o&&a>=n,l=e.distance>=this.innerRadius&&e.distance<=this.outerRadius;return h&&l},tooltipPosition:function(){var t=this.startAngle+(this.endAngle-this.startAngle)/2,i=(this.outerRadius-this.innerRadius)/2+this.innerRadius;return{x:this.x+Math.cos(t)*i,y:this.y+Math.sin(t)*i}},draw:function(t){var i=this.ctx;i.beginPath(),i.arc(this.x,this.y,this.outerRadius<0?0:this.outerRadius,this.startAngle,this.endAngle),i.arc(this.x,this.y,this.innerRadius<0?0:this.innerRadius,this.endAngle,this.startAngle,!0),i.closePath(),i.strokeStyle=this.strokeColor,i.lineWidth=this.strokeWidth,i.fillStyle=this.fillColor,i.fill(),i.lineJoin="bevel",this.showStroke&&i.stroke()}}),e.Rectangle=e.Element.extend({draw:function(){var t=this.ctx,i=this.width/2,e=this.x-i,s=this.x+i,n=this.base-(this.base-this.y),o=this.strokeWidth/2;this.showStroke&&(e+=o,s-=o,n+=o),t.beginPath(),t.fillStyle=this.fillColor,t.strokeStyle=this.strokeColor,t.lineWidth=this.strokeWidth,t.moveTo(e,this.base),t.lineTo(e,n),t.lineTo(s,n),t.lineTo(s,this.base),t.fill(),this.showStroke&&t.stroke()},height:function(){return this.base-this.y},inRange:function(t,i){return t>=this.x-this.width/2&&t<=this.x+this.width/2&&i>=this.y&&i<=this.base}}),e.Animation=e.Element.extend({currentStep:null,numSteps:60,easing:"",render:null,onAnimationProgress:null,onAnimationComplete:null}),e.Tooltip=e.Element.extend({draw:function(){var t=this.chart.ctx;t.font=W(this.fontSize,this.fontStyle,this.fontFamily),this.xAlign="center",this.yAlign="above";var i=this.caretPadding=2,e=t.measureText(this.text).width+2*this.xPadding,s=this.fontSize+2*this.yPadding,n=s+this.caretHeight+i;this.x+e/2>this.chart.width?this.xAlign="left":this.x-e/2<0&&(this.xAlign="right"),this.y-n<0&&(this.yAlign="below");var o=this.x-e/2,a=this.y-n;if(t.fillStyle=this.fillColor,this.custom)this.custom(this);else{switch(this.yAlign){case"above":t.beginPath(),t.moveTo(this.x,this.y-i),t.lineTo(this.x+this.caretHeight,this.y-(i+this.caretHeight)),t.lineTo(this.x-this.caretHeight,this.y-(i+this.caretHeight)),t.closePath(),t.fill();break;case"below":a=this.y+i+this.caretHeight,t.beginPath(),t.moveTo(this.x,this.y+i),t.lineTo(this.x+this.caretHeight,this.y+i+this.caretHeight),t.lineTo(this.x-this.caretHeight,this.y+i+this.caretHeight),t.closePath(),t.fill()}switch(this.xAlign){case"left":o=this.x-e+(this.cornerRadius+this.caretHeight);break;case"right":o=this.x-(this.cornerRadius+this.caretHeight)}B(t,o,a,e,s,this.cornerRadius),t.fill(),t.fillStyle=this.textColor,t.textAlign="center",t.textBaseline="middle",t.fillText(this.text,o+e/2,a+s/2)}}}),e.MultiTooltip=e.Element.extend({initialize:function(){this.font=W(this.fontSize,this.fontStyle,this.fontFamily),this.titleFont=W(this.titleFontSize,this.titleFontStyle,this.titleFontFamily),this.titleHeight=this.title?1.5*this.titleFontSize:0,this.height=this.labels.length*this.fontSize+(this.labels.length-1)*(this.fontSize/2)+2*this.yPadding+this.titleHeight,this.ctx.font=this.titleFont;var t=this.ctx.measureText(this.title).width,i=z(this.ctx,this.font,this.labels)+this.fontSize+3,e=g([i,t]);this.width=e+2*this.xPadding;var s=this.height/2;this.y-s<0?this.y=s:this.y+s>this.chart.height&&(this.y=this.chart.height-s),this.x>this.chart.width/2?this.x-=this.xOffset+this.width:this.x+=this.xOffset},getLineHeight:function(t){var i=this.y-this.height/2+this.yPadding,e=t-1;return 0===t?i+this.titleHeight/3:i+(1.5*this.fontSize*e+this.fontSize/2)+this.titleHeight},draw:function(){if(this.custom)this.custom(this);else{B(this.ctx,this.x,this.y-this.height/2,this.width,this.height,this.cornerRadius);var t=this.ctx;t.fillStyle=this.fillColor,t.fill(),t.closePath(),t.textAlign="left",t.textBaseline="middle",t.fillStyle=this.titleTextColor,t.font=this.titleFont,t.fillText(this.title,this.x+this.xPadding,this.getLineHeight(0)),t.font=this.font,s.each(this.labels,function(i,e){t.fillStyle=this.textColor,t.fillText(i,this.x+this.xPadding+this.fontSize+3,this.getLineHeight(e+1)),t.fillStyle=this.legendColorBackground,t.fillRect(this.x+this.xPadding,this.getLineHeight(e+1)-this.fontSize/2,this.fontSize,this.fontSize),t.fillStyle=this.legendColors[e].fill,t.fillRect(this.x+this.xPadding,this.getLineHeight(e+1)-this.fontSize/2,this.fontSize,this.fontSize)},this)}}}),e.Scale=e.Element.extend({initialize:function(){this.fit()},buildYLabels:function(){this.yLabels=[];for(var t=v(this.stepValue),i=0;i<=this.steps;i++)this.yLabels.push(y(this.templateString,{value:(this.min+i*this.stepValue).toFixed(t)}));this.yLabelWidth=this.display&&this.showLabels?z(this.ctx,this.font,this.yLabels)+10:0},addXLabel:function(t){this.xLabels.push(t),this.valuesCount++,this.fit()},removeXLabel:function(){this.xLabels.shift(),this.valuesCount--,this.fit()},fit:function(){this.startPoint=this.display?this.fontSize:0,this.endPoint=this.display?this.height-1.5*this.fontSize-5:this.height,this.startPoint+=this.padding,this.endPoint-=this.padding;var t,i=this.endPoint,e=this.endPoint-this.startPoint;for(this.calculateYRange(e),this.buildYLabels(),this.calculateXLabelRotation();e>this.endPoint-this.startPoint;)e=this.endPoint-this.startPoint,t=this.yLabelWidth,this.calculateYRange(e),this.buildYLabels(),tthis.yLabelWidth?e/2:this.yLabelWidth,this.xLabelRotation=0,this.display){var n,o=z(this.ctx,this.font,this.xLabels);this.xLabelWidth=o;for(var a=Math.floor(this.calculateX(1)-this.calculateX(0))-6;this.xLabelWidth>a&&0===this.xLabelRotation||this.xLabelWidth>a&&this.xLabelRotation<=90&&this.xLabelRotation>0;)n=Math.cos(S(this.xLabelRotation)),t=n*e,i=n*s,t+this.fontSize/2>this.yLabelWidth&&(this.xScalePaddingLeft=t+this.fontSize/2),this.xScalePaddingRight=this.fontSize/2,this.xLabelRotation++,this.xLabelWidth=n*o;this.xLabelRotation>0&&(this.endPoint-=Math.sin(S(this.xLabelRotation))*o+3)}else this.xLabelWidth=0,this.xScalePaddingRight=this.padding,this.xScalePaddingLeft=this.padding},calculateYRange:c,drawingArea:function(){return this.startPoint-this.endPoint},calculateY:function(t){var i=this.drawingArea()/(this.min-this.max);return this.endPoint-i*(t-this.min)},calculateX:function(t){var i=(this.xLabelRotation>0,this.width-(this.xScalePaddingLeft+this.xScalePaddingRight)),e=i/Math.max(this.valuesCount-(this.offsetGridLines?0:1),1),s=e*t+this.xScalePaddingLeft;return this.offsetGridLines&&(s+=e/2),Math.round(s)},update:function(t){s.extend(this,t),this.fit()},draw:function(){var t=this.ctx,i=(this.endPoint-this.startPoint)/this.steps,e=Math.round(this.xScalePaddingLeft);this.display&&(t.fillStyle=this.textColor,t.font=this.font,n(this.yLabels,function(n,o){var a=this.endPoint-i*o,h=Math.round(a),l=this.showHorizontalLines;t.textAlign="right",t.textBaseline="middle",this.showLabels&&t.fillText(n,e-10,a),0!==o||l||(l=!0),l&&t.beginPath(),o>0?(t.lineWidth=this.gridLineWidth,t.strokeStyle=this.gridLineColor):(t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor),h+=s.aliasPixel(t.lineWidth),l&&(t.moveTo(e,h),t.lineTo(this.width,h),t.stroke(),t.closePath()),t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor,t.beginPath(),t.moveTo(e-5,h),t.lineTo(e,h),t.stroke(),t.closePath()},this),n(this.xLabels,function(i,e){var s=this.calculateX(e)+x(this.lineWidth),n=this.calculateX(e-(this.offsetGridLines?.5:0))+x(this.lineWidth),o=this.xLabelRotation>0,a=this.showVerticalLines;0!==e||a||(a=!0),a&&t.beginPath(),e>0?(t.lineWidth=this.gridLineWidth,t.strokeStyle=this.gridLineColor):(t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor),a&&(t.moveTo(n,this.endPoint),t.lineTo(n,this.startPoint-3),t.stroke(),t.closePath()),t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor,t.beginPath(),t.moveTo(n,this.endPoint),t.lineTo(n,this.endPoint+5),t.stroke(),t.closePath(),t.save(),t.translate(s,o?this.endPoint+12:this.endPoint+8),t.rotate(-1*S(this.xLabelRotation)),t.font=this.font,t.textAlign=o?"right":"center",t.textBaseline=o?"middle":"top",t.fillText(i,0,0),t.restore()},this))}}),e.RadialScale=e.Element.extend({initialize:function(){this.size=m([this.height,this.width]),this.drawingArea=this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2},calculateCenterOffset:function(t){var i=this.drawingArea/(this.max-this.min);return(t-this.min)*i},update:function(){this.lineArc?this.drawingArea=this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2:this.setScaleSize(),this.buildYLabels()},buildYLabels:function(){this.yLabels=[];for(var t=v(this.stepValue),i=0;i<=this.steps;i++)this.yLabels.push(y(this.templateString,{value:(this.min+i*this.stepValue).toFixed(t)}))},getCircumference:function(){return 2*Math.PI/this.valuesCount},setScaleSize:function(){var t,i,e,s,n,o,a,h,l,r,c,u,d=m([this.height/2-this.pointLabelFontSize-5,this.width/2]),p=this.width,g=0;for(this.ctx.font=W(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily),i=0;ip&&(p=t.x+s,n=i),t.x-sp&&(p=t.x+e,n=i):i>this.valuesCount/2&&t.x-e0){var s,n=e*(this.drawingArea/this.steps),o=this.yCenter-n;if(this.lineWidth>0)if(t.strokeStyle=this.lineColor,t.lineWidth=this.lineWidth,this.lineArc)t.beginPath(),t.arc(this.xCenter,this.yCenter,n,0,2*Math.PI),t.closePath(),t.stroke();else{t.beginPath();for(var a=0;a=0;i--){var e=null,s=null;if(this.angleLineWidth>0&&i%this.angleLineInterval===0&&(e=this.calculateCenterOffset(this.max),s=this.getPointPosition(i,e),t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(s.x,s.y),t.stroke(),t.closePath()),this.backgroundColors&&this.backgroundColors.length==this.valuesCount){null==e&&(e=this.calculateCenterOffset(this.max)),null==s&&(s=this.getPointPosition(i,e));var o=this.getPointPosition(0===i?this.valuesCount-1:i-1,e),a=this.getPointPosition(i===this.valuesCount-1?0:i+1,e),h={x:(o.x+s.x)/2,y:(o.y+s.y)/2},l={x:(s.x+a.x)/2,y:(s.y+a.y)/2};t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(h.x,h.y),t.lineTo(s.x,s.y),t.lineTo(l.x,l.y),t.fillStyle=this.backgroundColors[i],t.fill(),t.closePath()}var r=this.getPointPosition(i,this.calculateCenterOffset(this.max)+5);t.font=W(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily),t.fillStyle=this.pointLabelFontColor;var c=this.labels.length,u=this.labels.length/2,d=u/2,p=d>i||i>c-d,f=i===d||i===c-d;0===i?t.textAlign="center":i===u?t.textAlign="center":u>i?t.textAlign="left":t.textAlign="right",f?t.textBaseline="middle":p?t.textBaseline="bottom":t.textBaseline="top",t.fillText(this.labels[i],r.x,r.y)}}}}}),e.animationService={frameDuration:17,animations:[],dropFrames:0,addAnimation:function(t,i){for(var e=0;e1&&(i=Math.floor(this.dropFrames),this.dropFrames-=i);for(var e=0;ethis.animations[e].animationObject.numSteps&&(this.animations[e].animationObject.currentStep=this.animations[e].animationObject.numSteps),this.animations[e].animationObject.render(this.animations[e].chartInstance,this.animations[e].animationObject),this.animations[e].animationObject.currentStep==this.animations[e].animationObject.numSteps&&(this.animations[e].animationObject.onAnimationComplete.call(this.animations[e].chartInstance),this.animations.splice(e,1),e--);var n=Date.now(),o=n-t-this.frameDuration,a=o/this.frameDuration;a>1&&(this.dropFrames+=a),this.animations.length>0&&s.requestAnimFrame.call(window,this.digestWrapper)}},s.addEvent(window,"resize",function(){var t;return function(){clearTimeout(t),t=setTimeout(function(){n(e.instances,function(t){t.options.responsive&&t.resize(t.render,!0)})},50)}}()),p?define("Chart",[],function(){return e}):"object"==typeof module&&module.exports&&(module.exports=e),t.Chart=e,e.noConflict=function(){return t.Chart=i,e}}).call(this),function(){"use strict";var t=this,i=t.Chart,e=i.helpers,s={scaleBeginAtZero:!0,scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,scaleShowHorizontalLines:!0,scaleShowVerticalLines:!0,barShowStroke:!0,barStrokeWidth:2,barValueSpacing:5,barDatasetSpacing:1,legendTemplate:'
      <% for (var i=0; i
    • <%if(datasets[i].label){%><%=datasets[i].label%><%}%>
    • <%}%>
    '};i.Type.extend({name:"Bar",defaults:s,initialize:function(t){var s=this.options;this.ScaleClass=i.Scale.extend({offsetGridLines:!0,calculateBarX:function(t,i,e){var n=this.calculateBaseWidth(),o=this.calculateX(e)-n/2,a=this.calculateBarWidth(t);return o+a*i+i*s.barDatasetSpacing+a/2},calculateBaseWidth:function(){return this.calculateX(1)-this.calculateX(0)-2*s.barValueSpacing; +},calculateBarWidth:function(t){var i=this.calculateBaseWidth()-(t-1)*s.barDatasetSpacing;return i/t}}),this.datasets=[],this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getBarsAtEvent(t):[];this.eachBars(function(t){t.restore(["fillColor","strokeColor"])}),e.each(i,function(t){t&&(t.fillColor=t.highlightFill,t.strokeColor=t.highlightStroke)}),this.showTooltip(i)}),this.BarClass=i.Rectangle.extend({strokeWidth:this.options.barStrokeWidth,showStroke:this.options.barShowStroke,ctx:this.chart.ctx}),e.each(t.datasets,function(i,s){var n={label:i.label||null,fillColor:i.fillColor,strokeColor:i.strokeColor,bars:[]};this.datasets.push(n),e.each(i.data,function(e,s){n.bars.push(new this.BarClass({value:e,label:t.labels[s],datasetLabel:i.label,strokeColor:"string"!=typeof i.strokeColor?i.strokeColor[s]:i.strokeColor,fillColor:"string"!=typeof i.fillColor?i.fillColor[s]:i.fillColor,highlightFill:i.highlightFill?"string"!=typeof i.highlightFill?i.highlightFill[s]:i.highlightFill:"string"!=typeof i.fillColor?i.fillColor[s]:i.fillColor,highlightStroke:i.highlightStroke?"string"!=typeof i.highlightStroke?i.highlightStroke[s]:i.highlightStroke:"string"!=typeof i.strokeColor?i.strokeColor[s]:i.strokeColor}))},this)},this),this.buildScale(t.labels),this.BarClass.prototype.base=this.scale.endPoint,this.eachBars(function(t,i,s){e.extend(t,{width:this.scale.calculateBarWidth(this.datasets.length),x:this.scale.calculateBarX(this.datasets.length,s,i),y:this.scale.endPoint}),t.save()},this),this.render()},update:function(){this.scale.update(),e.each(this.activeElements,function(t){t.restore(["fillColor","strokeColor"])}),this.eachBars(function(t){t.save()}),this.render()},eachBars:function(t){e.each(this.datasets,function(i,s){e.each(i.bars,t,this,s)},this)},getBarsAtEvent:function(t){for(var i,s=[],n=e.getRelativePosition(t),o=function(t){s.push(t.bars[i])},a=0;a<% for (var i=0; i
  • <%if(segments[i].label){%><%=segments[i].label%><%}%>
  • <%}%>'};i.Type.extend({name:"Doughnut",defaults:s,initialize:function(t){this.segments=[],this.outerRadius=(e.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2,this.SegmentArc=i.Arc.extend({ctx:this.chart.ctx,x:this.chart.width/2,y:this.chart.height/2}),this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getSegmentsAtEvent(t):[];e.each(this.segments,function(t){t.restore(["fillColor"])}),e.each(i,function(t){t.fillColor=t.highlightColor}),this.showTooltip(i)}),this.calculateTotal(t),e.each(t,function(i,e){i.color||(i.color="hsl("+360*e/t.length+", 100%, 50%)"),this.addData(i,e,!0)},this),this.render()},getSegmentsAtEvent:function(t){var i=[],s=e.getRelativePosition(t);return e.each(this.segments,function(t){t.inRange(s.x,s.y)&&i.push(t)},this),i},addData:function(t,e,s){var n=void 0!==e?e:this.segments.length;"undefined"==typeof t.color&&(t.color=i.defaults.global.segmentColorDefault[n%i.defaults.global.segmentColorDefault.length],t.highlight=i.defaults.global.segmentHighlightColorDefaults[n%i.defaults.global.segmentHighlightColorDefaults.length]),this.segments.splice(n,0,new this.SegmentArc({value:t.value,outerRadius:this.options.animateScale?0:this.outerRadius,innerRadius:this.options.animateScale?0:this.outerRadius/100*this.options.percentageInnerCutout,fillColor:t.color,highlightColor:t.highlight||t.color,showStroke:this.options.segmentShowStroke,strokeWidth:this.options.segmentStrokeWidth,strokeColor:this.options.segmentStrokeColor,startAngle:1.5*Math.PI,circumference:this.options.animateRotate?0:this.calculateCircumference(t.value),label:t.label})),s||(this.reflow(),this.update())},calculateCircumference:function(t){return this.total>0?2*Math.PI*(t/this.total):0},calculateTotal:function(t){this.total=0,e.each(t,function(t){this.total+=Math.abs(t.value)},this)},update:function(){this.calculateTotal(this.segments),e.each(this.activeElements,function(t){t.restore(["fillColor"])}),e.each(this.segments,function(t){t.save()}),this.render()},removeData:function(t){var i=e.isNumber(t)?t:this.segments.length-1;this.segments.splice(i,1),this.reflow(),this.update()},reflow:function(){e.extend(this.SegmentArc.prototype,{x:this.chart.width/2,y:this.chart.height/2}),this.outerRadius=(e.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2,e.each(this.segments,function(t){t.update({outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout})},this)},draw:function(t){var i=t?t:1;this.clear(),e.each(this.segments,function(t,e){t.transition({circumference:this.calculateCircumference(t.value),outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout},i),t.endAngle=t.startAngle+t.circumference,t.draw(),0===e&&(t.startAngle=1.5*Math.PI),e<% for (var i=0; i
  • <%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>',offsetGridLines:!1};i.Type.extend({name:"Line",defaults:s,initialize:function(t){this.PointClass=i.Point.extend({offsetGridLines:this.options.offsetGridLines,strokeWidth:this.options.pointDotStrokeWidth,radius:this.options.pointDotRadius,display:this.options.pointDot,hitDetectionRadius:this.options.pointHitDetectionRadius,ctx:this.chart.ctx,inRange:function(t){return Math.pow(t-this.x,2)0&&ithis.scale.endPoint?t.controlPoints.outer.y=this.scale.endPoint:t.controlPoints.outer.ythis.scale.endPoint?t.controlPoints.inner.y=this.scale.endPoint:t.controlPoints.inner.y0&&(s.lineTo(h[h.length-1].x,this.scale.endPoint),s.lineTo(h[0].x,this.scale.endPoint),s.fillStyle=t.fillColor,s.closePath(),s.fill()),e.each(h,function(t){t.draw()})},this))}})}.call(this),function(){"use strict";var t=this,i=t.Chart,e=i.helpers,s={scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",scaleBeginAtZero:!0,scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,scaleShowLine:!0,segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,legendTemplate:'
      <% for (var i=0; i
    • <%if(segments[i].label){%><%=segments[i].label%><%}%>
    • <%}%>
    '};i.Type.extend({name:"PolarArea",defaults:s,initialize:function(t){this.segments=[],this.SegmentArc=i.Arc.extend({showStroke:this.options.segmentShowStroke,strokeWidth:this.options.segmentStrokeWidth,strokeColor:this.options.segmentStrokeColor,ctx:this.chart.ctx,innerRadius:0,x:this.chart.width/2,y:this.chart.height/2}),this.scale=new i.RadialScale({display:this.options.showScale,fontStyle:this.options.scaleFontStyle,fontSize:this.options.scaleFontSize,fontFamily:this.options.scaleFontFamily,fontColor:this.options.scaleFontColor,showLabels:this.options.scaleShowLabels,showLabelBackdrop:this.options.scaleShowLabelBackdrop,backdropColor:this.options.scaleBackdropColor,backdropPaddingY:this.options.scaleBackdropPaddingY,backdropPaddingX:this.options.scaleBackdropPaddingX,lineWidth:this.options.scaleShowLine?this.options.scaleLineWidth:0,lineColor:this.options.scaleLineColor,lineArc:!0,width:this.chart.width,height:this.chart.height,xCenter:this.chart.width/2,yCenter:this.chart.height/2,ctx:this.chart.ctx,templateString:this.options.scaleLabel,valuesCount:t.length}),this.updateScaleRange(t),this.scale.update(),e.each(t,function(t,i){this.addData(t,i,!0)},this),this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getSegmentsAtEvent(t):[];e.each(this.segments,function(t){t.restore(["fillColor"])}),e.each(i,function(t){t.fillColor=t.highlightColor}),this.showTooltip(i)}),this.render()},getSegmentsAtEvent:function(t){var i=[],s=e.getRelativePosition(t);return e.each(this.segments,function(t){t.inRange(s.x,s.y)&&i.push(t)},this),i},addData:function(t,i,e){var s=i||this.segments.length;this.segments.splice(s,0,new this.SegmentArc({fillColor:t.color,highlightColor:t.highlight||t.color,label:t.label,value:t.value,outerRadius:this.options.animateScale?0:this.scale.calculateCenterOffset(t.value),circumference:this.options.animateRotate?0:this.scale.getCircumference(),startAngle:1.5*Math.PI})),e||(this.reflow(),this.update())},removeData:function(t){var i=e.isNumber(t)?t:this.segments.length-1;this.segments.splice(i,1),this.reflow(),this.update()},calculateTotal:function(t){this.total=0,e.each(t,function(t){this.total+=t.value},this),this.scale.valuesCount=this.segments.length},updateScaleRange:function(t){var i=[];e.each(t,function(t){i.push(t.value)});var s=this.options.scaleOverride?{steps:this.options.scaleSteps,stepValue:this.options.scaleStepWidth,min:this.options.scaleStartValue,max:this.options.scaleStartValue+this.options.scaleSteps*this.options.scaleStepWidth}:e.calculateScaleRange(i,e.min([this.chart.width,this.chart.height])/2,this.options.scaleFontSize,this.options.scaleBeginAtZero,this.options.scaleIntegersOnly);e.extend(this.scale,s,{size:e.min([this.chart.width,this.chart.height]),xCenter:this.chart.width/2,yCenter:this.chart.height/2})},update:function(){this.calculateTotal(this.segments),e.each(this.segments,function(t){t.save()}),this.reflow(),this.render()},reflow:function(){e.extend(this.SegmentArc.prototype,{x:this.chart.width/2,y:this.chart.height/2}),this.updateScaleRange(this.segments),this.scale.update(),e.extend(this.scale,{xCenter:this.chart.width/2,yCenter:this.chart.height/2}),e.each(this.segments,function(t){t.update({outerRadius:this.scale.calculateCenterOffset(t.value)})},this)},draw:function(t){var i=t||1;this.clear(),e.each(this.segments,function(t,e){t.transition({circumference:this.scale.getCircumference(),outerRadius:this.scale.calculateCenterOffset(t.value)},i),t.endAngle=t.startAngle+t.circumference,0===e&&(t.startAngle=1.5*Math.PI),e<% for (var i=0; i
  • <%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>'},initialize:function(t){this.PointClass=i.Point.extend({strokeWidth:this.options.pointDotStrokeWidth,radius:this.options.pointDotRadius,display:this.options.pointDot,hitDetectionRadius:this.options.pointHitDetectionRadius,ctx:this.chart.ctx}),this.datasets=[],this.buildScale(t),this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getPointsAtEvent(t):[];this.eachPoints(function(t){t.restore(["fillColor","strokeColor"])}),e.each(i,function(t){t.fillColor=t.highlightFill,t.strokeColor=t.highlightStroke}),this.showTooltip(i)}),e.each(t.datasets,function(i){var s={label:i.label||null,fillColor:i.fillColor,strokeColor:i.strokeColor,pointColor:i.pointColor,pointStrokeColor:i.pointStrokeColor,points:[]};this.datasets.push(s),e.each(i.data,function(e,n){var o;this.scale.animation||(o=this.scale.getPointPosition(n,this.scale.calculateCenterOffset(e))),s.points.push(new this.PointClass({value:e,label:t.labels[n],datasetLabel:i.label,x:this.options.animation?this.scale.xCenter:o.x,y:this.options.animation?this.scale.yCenter:o.y,strokeColor:i.pointStrokeColor,fillColor:i.pointColor,highlightFill:i.pointHighlightFill||i.pointColor,highlightStroke:i.pointHighlightStroke||i.pointStrokeColor}))},this)},this),this.render()},eachPoints:function(t){e.each(this.datasets,function(i){e.each(i.points,t,this)},this)},getPointsAtEvent:function(t){var i=e.getRelativePosition(t),s=e.getAngleFromPoint({x:this.scale.xCenter,y:this.scale.yCenter},i),n=2*Math.PI/this.scale.valuesCount,o=Math.round((s.angle-1.5*Math.PI)/n),a=[];return(o>=this.scale.valuesCount||0>o)&&(o=0),s.distance<=this.scale.drawingArea&&e.each(this.datasets,function(t){a.push(t.points[o])}),a},buildScale:function(t){this.scale=new i.RadialScale({display:this.options.showScale,fontStyle:this.options.scaleFontStyle,fontSize:this.options.scaleFontSize,fontFamily:this.options.scaleFontFamily,fontColor:this.options.scaleFontColor,showLabels:this.options.scaleShowLabels,showLabelBackdrop:this.options.scaleShowLabelBackdrop,backdropColor:this.options.scaleBackdropColor,backgroundColors:this.options.scaleBackgroundColors,backdropPaddingY:this.options.scaleBackdropPaddingY,backdropPaddingX:this.options.scaleBackdropPaddingX,lineWidth:this.options.scaleShowLine?this.options.scaleLineWidth:0,lineColor:this.options.scaleLineColor,angleLineColor:this.options.angleLineColor,angleLineWidth:this.options.angleShowLineOut?this.options.angleLineWidth:0,angleLineInterval:this.options.angleLineInterval?this.options.angleLineInterval:1,pointLabelFontColor:this.options.pointLabelFontColor,pointLabelFontSize:this.options.pointLabelFontSize,pointLabelFontFamily:this.options.pointLabelFontFamily,pointLabelFontStyle:this.options.pointLabelFontStyle,height:this.chart.height,width:this.chart.width,xCenter:this.chart.width/2,yCenter:this.chart.height/2,ctx:this.chart.ctx,templateString:this.options.scaleLabel,labels:t.labels,valuesCount:t.datasets[0].data.length}),this.scale.setScaleSize(),this.updateScaleRange(t.datasets),this.scale.buildYLabels()},updateScaleRange:function(t){var i=function(){var i=[];return e.each(t,function(t){t.data?i=i.concat(t.data):e.each(t.points,function(t){i.push(t.value)})}),i}(),s=this.options.scaleOverride?{steps:this.options.scaleSteps,stepValue:this.options.scaleStepWidth,min:this.options.scaleStartValue,max:this.options.scaleStartValue+this.options.scaleSteps*this.options.scaleStepWidth}:e.calculateScaleRange(i,e.min([this.chart.width,this.chart.height])/2,this.options.scaleFontSize,this.options.scaleBeginAtZero,this.options.scaleIntegersOnly);e.extend(this.scale,s)},addData:function(t,i){this.scale.valuesCount++,e.each(t,function(t,e){var s=this.scale.getPointPosition(this.scale.valuesCount,this.scale.calculateCenterOffset(t));this.datasets[e].points.push(new this.PointClass({value:t,label:i,datasetLabel:this.datasets[e].label,x:s.x,y:s.y,strokeColor:this.datasets[e].pointStrokeColor,fillColor:this.datasets[e].pointColor}))},this),this.scale.labels.push(i),this.reflow(),this.update()},removeData:function(){this.scale.valuesCount--,this.scale.labels.shift(),e.each(this.datasets,function(t){t.points.shift()},this),this.reflow(),this.update()},update:function(){this.eachPoints(function(t){t.save()}),this.reflow(),this.render()},reflow:function(){e.extend(this.scale,{width:this.chart.width,height:this.chart.height,size:e.min([this.chart.width,this.chart.height]),xCenter:this.chart.width/2,yCenter:this.chart.height/2}),this.updateScaleRange(this.datasets),this.scale.setScaleSize(),this.scale.buildYLabels()},draw:function(t){var i=t||1,s=this.chart.ctx;this.clear(),this.scale.draw(),e.each(this.datasets,function(t){e.each(t.points,function(t,e){t.hasValue()&&t.transition(this.scale.getPointPosition(e,this.scale.calculateCenterOffset(t.value)),i)},this),s.lineWidth=this.options.datasetStrokeWidth,s.strokeStyle=t.strokeColor,s.beginPath(),e.each(t.points,function(t,i){0===i?s.moveTo(t.x,t.y):s.lineTo(t.x,t.y)},this),s.closePath(),s.stroke(),s.fillStyle=t.fillColor,this.options.datasetFill&&s.fill(),e.each(t.points,function(t){t.hasValue()&&t.draw()})},this)}})}.call(this); \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 145bebbdea3..db3b51f46bf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ -numpy==1.10.4 +numpy +scipy pandas==0.17.1 ipython==4.1.1 tornado==4.0.2 flake8==2.5.2 -scipy==0.17.0 \ No newline at end of file