{"id":129,"date":"2014-02-25T04:57:14","date_gmt":"2014-02-25T04:57:14","guid":{"rendered":"http:\/\/www.clowersresearch.com\/main\/?p=129"},"modified":"2014-10-23T19:42:42","modified_gmt":"2014-10-23T19:42:42","slug":"gantt-charts-in-matplotlib","status":"publish","type":"post","link":"http:\/\/www.clowersresearch.com\/main\/gantt-charts-in-matplotlib\/","title":{"rendered":"Gantt Charts in Matplotlib"},"content":{"rendered":"<p style=\"text-align: left;\"><a href=\"http:\/\/www.clowersresearch.com\/main\/wp-content\/uploads\/2014\/02\/GanttPlot.png\"><img loading=\"lazy\" class=\"aligncenter  wp-image-130\" alt=\"GanttPlot\" src=\"http:\/\/www.clowersresearch.com\/main\/wp-content\/uploads\/2014\/02\/GanttPlot-1024x490.png\" width=\"560\" height=\"267\" srcset=\"http:\/\/www.clowersresearch.com\/main\/wp-content\/uploads\/2014\/02\/GanttPlot-1024x490.png 1024w, http:\/\/www.clowersresearch.com\/main\/wp-content\/uploads\/2014\/02\/GanttPlot-300x143.png 300w, http:\/\/www.clowersresearch.com\/main\/wp-content\/uploads\/2014\/02\/GanttPlot-250x119.png 250w, http:\/\/www.clowersresearch.com\/main\/wp-content\/uploads\/2014\/02\/GanttPlot-150x71.png 150w, http:\/\/www.clowersresearch.com\/main\/wp-content\/uploads\/2014\/02\/GanttPlot.png 1310w\" sizes=\"(max-width: 560px) 100vw, 560px\" \/><\/a>Love it or hate it, the lack of a tractable options to create <a href=\"http:\/\/en.wikipedia.org\/wiki\/Gantt_chart\">Gantt<\/a> charts warrants frustration at times. \u00a0A recent post on <a href=\"https:\/\/bitbucket.org\/DBrent\/phd\/src\/1d1c5444d2ba2ee3918e0dfd5e886eaeeee49eec\/visualisation\/plot_gantt.py\">Bitbucket<\/a>\u00a0provides a nice implementation using matplotlib and python as a platform. \u00a0In order to expand the basic functionality a few modifications enable a set of features that highlight the relative contributions of the team participants. \u00a0In the example provided above the broad tasks are indicated in yellow while the two inset bars (red:student and blue:PI) illustrate the percent effort. \u00a0See the source below for the details.<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n&quot;&quot;&quot;\r\nCreates a simple Gantt chart\r\nAdapted from https:\/\/bitbucket.org\/DBrent\/phd\/src\/1d1c5444d2ba2ee3918e0dfd5e886eaeeee49eec\/visualisation\/plot_gantt.py\r\nBHC 2014\r\n&quot;&quot;&quot;\r\n\r\nimport datetime as dt\r\nimport matplotlib.pyplot as plt\r\nimport matplotlib.font_manager as font_manager\r\nimport matplotlib.dates\r\nfrom matplotlib.dates import MONTHLY, DateFormatter, rrulewrapper, RRuleLocator\r\n\r\nfrom pylab import *\r\n\r\ndef create_date(month,year):\r\n&quot;&quot;&quot;Creates the date&quot;&quot;&quot;\r\n\r\ndate = dt.datetime(int(year), int(month), 1)\r\nmdate = matplotlib.dates.date2num(date)\r\n\r\nreturn mdate\r\n\r\n# Data\r\n\r\npos = arange(0.5,5.5,0.5)\r\n\r\nylabels = []\r\nylabels.append('Hardware Design &amp; Review')\r\nylabels.append('Hardware Construction')\r\nylabels.append('Integrate and Test Laser Source')\r\nylabels.append('Objective #1')\r\nylabels.append('Objective #2')\r\nylabels.append('Present at ASMS')\r\nylabels.append('Present Data at Gordon Conference')\r\nylabels.append('Manuscripts and Final Report')\r\n\r\neffort = []\r\neffort.append([0.2, 1.0])\r\neffort.append([0.2, 1.0])\r\neffort.append([0.2, 1.0])\r\neffort.append([0.3, 0.75])\r\neffort.append([0.25, 0.75])\r\neffort.append([0.3, 0.75])\r\neffort.append([0.5, 0.5])\r\neffort.append([0.7, 0.4])\r\n\r\ncustomDates = []\r\ncustomDates.append([create_date(5,2014),create_date(6,2014)])\r\ncustomDates.append([create_date(6,2014),create_date(8,2014),create_date(8,2014)])\r\ncustomDates.append([create_date(7,2014),create_date(9,2014),create_date(9,2014)])\r\ncustomDates.append([create_date(10,2014),create_date(3,2015),create_date(3,2015)])\r\ncustomDates.append([create_date(2,2015),create_date(6,2015),create_date(6,2015)])\r\ncustomDates.append([create_date(5,2015),create_date(6,2015),create_date(6,2015)])\r\ncustomDates.append([create_date(6,2015),create_date(7,2015),create_date(7,2015)])\r\ncustomDates.append([create_date(4,2015),create_date(8,2015),create_date(8,2015)])\r\n\r\ntask_dates = {}\r\nfor i,task in enumerate(ylabels):\r\ntask_dates[task] = customDates[i]\r\n# task_dates['Climatology'] = [create_date(5,2014),create_date(6,2014),create_date(10,2013)]\r\n# task_dates['Structure'] = [create_date(10,2013),create_date(3,2014),create_date(5,2014)]\r\n# task_dates['Impacts'] = [create_date(5,2014),create_date(12,2014),create_date(2,2015)]\r\n# task_dates['Thesis'] = [create_date(2,2015),create_date(5,2015)]\r\n\r\n# Initialise plot\r\n\r\nfig = plt.figure()\r\n# ax = fig.add_axes([0.15,0.2,0.75,0.3]) #[left,bottom,width,height]\r\nax = fig.add_subplot(111)\r\n\r\n# Plot the data\r\n\r\nstart_date,end_date = task_dates[ylabels[0]]\r\nax.barh(0.5, end_date - start_date, left=start_date, height=0.3, align='center', color='blue', alpha = 0.75)\r\nax.barh(0.45, (end_date - start_date)*effort[0][0], left=start_date, height=0.1, align='center', color='red', alpha = 0.75, label = &quot;PI Effort&quot;)\r\nax.barh(0.55, (end_date - start_date)*effort[0][1], left=start_date, height=0.1, align='center', color='yellow', alpha = 0.75, label = &quot;Student Effort&quot;)\r\nfor i in range(0,len(ylabels)-1):\r\nlabels = ['Analysis','Reporting'] if i == 1 else [None,None]\r\nstart_date,mid_date,end_date = task_dates[ylabels[i+1]]\r\npiEffort, studentEffort = effort[i+1]\r\nax.barh((i*0.5)+1.0, mid_date - start_date, left=start_date, height=0.3, align='center', color='blue', alpha = 0.75)\r\nax.barh((i*0.5)+1.0-0.05, (mid_date - start_date)*piEffort, left=start_date, height=0.1, align='center', color='red', alpha = 0.75)\r\nax.barh((i*0.5)+1.0+0.05, (mid_date - start_date)*studentEffort, left=start_date, height=0.1, align='center', color='yellow', alpha = 0.75)\r\n# ax.barh((i*0.5)+1.0, end_date - mid_date, left=mid_date, height=0.3, align='center',label=labels[1], color='yellow')\r\n\r\n# Format the y-axis\r\n\r\nlocsy, labelsy = yticks(pos,ylabels)\r\nplt.setp(labelsy, fontsize = 14)\r\n\r\n# Format the x-axis\r\n\r\nax.axis('tight')\r\nax.set_ylim(ymin = -0.1, ymax = 4.5)\r\nax.grid(color = 'g', linestyle = ':')\r\n\r\nax.xaxis_date() #Tell matplotlib that these are dates...\r\n\r\nrule = rrulewrapper(MONTHLY, interval=1)\r\nloc = RRuleLocator(rule)\r\nformatter = DateFormatter(&quot;%b '%y&quot;)\r\n\r\nax.xaxis.set_major_locator(loc)\r\nax.xaxis.set_major_formatter(formatter)\r\nlabelsx = ax.get_xticklabels()\r\nplt.setp(labelsx, rotation=30, fontsize=12)\r\n\r\n# Format the legend\r\n\r\nfont = font_manager.FontProperties(size='small')\r\nax.legend(loc=1,prop=font)\r\n\r\n# Finish up\r\nax.invert_yaxis()\r\nfig.autofmt_xdate()\r\n#plt.savefig('gantt.svg')\r\nplt.show()\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Love it or hate it, the lack of a tractable options to create Gantt charts warrants frustration at times. \u00a0A recent post on Bitbucket\u00a0provides a nice implementation using matplotlib and python as a platform. \u00a0In order to expand the basic functionality a few modifications enable a set of features that highlight the relative contributions of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":130,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":""},"categories":[6],"tags":[5,4,20],"jetpack_featured_media_url":"http:\/\/www.clowersresearch.com\/main\/wp-content\/uploads\/2014\/02\/GanttPlot.png","_links":{"self":[{"href":"http:\/\/www.clowersresearch.com\/main\/wp-json\/wp\/v2\/posts\/129"}],"collection":[{"href":"http:\/\/www.clowersresearch.com\/main\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.clowersresearch.com\/main\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.clowersresearch.com\/main\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.clowersresearch.com\/main\/wp-json\/wp\/v2\/comments?post=129"}],"version-history":[{"count":5,"href":"http:\/\/www.clowersresearch.com\/main\/wp-json\/wp\/v2\/posts\/129\/revisions"}],"predecessor-version":[{"id":132,"href":"http:\/\/www.clowersresearch.com\/main\/wp-json\/wp\/v2\/posts\/129\/revisions\/132"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/www.clowersresearch.com\/main\/wp-json\/wp\/v2\/media\/130"}],"wp:attachment":[{"href":"http:\/\/www.clowersresearch.com\/main\/wp-json\/wp\/v2\/media?parent=129"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.clowersresearch.com\/main\/wp-json\/wp\/v2\/categories?post=129"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.clowersresearch.com\/main\/wp-json\/wp\/v2\/tags?post=129"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}