

{"id":1089,"date":"2012-05-28T21:01:39","date_gmt":"2012-05-29T02:01:39","guid":{"rendered":"http:\/\/rud.is\/b\/?p=1089"},"modified":"2017-03-27T09:40:30","modified_gmt":"2017-03-27T14:40:30","slug":"slopegraphs-in-python","status":"publish","type":"post","link":"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/","title":{"rendered":"Slopegraphs in Python"},"content":{"rendered":"<p><i>(<b>NOTE<\/b>: You can keep up with progress best at github, but can always search on &#8220;slopegraph&#8221; here or just hit the tag page: &#8220;<a href=\"https:\/\/rud.is\/b\/tag\/slopegraph\/\">slopegraph<\/a>&#8221; regularly<\/a>)<\/i><\/p>\n<p>I&#8217;ve been a bit obsessed with <a href=\"http:\/\/charliepark.org\/slopegraphs\/\">slopegraphs<\/a> (a.k.a &#8220;Tufte table-chart&#8221;) of late  and very dissatisfied with the lack of tools to make this particular visualization tool more prevalent. While my ultimate goal is to have a user-friendly modern web app or platform app that&#8217;s as easy as a &#8220;drag &#038; drop&#8221; of a CSV file, this first foray will require a bit (not much, <em>really<\/em>!) of elbow grease to be used.<\/p>\n<p>For those who want to get right to the code, head on over to <a href=\"https:\/\/github.com\/hrbrmstr\/slopegraph\">github<\/a> and have a look (I&#8217;ll post all updates there). Setup, sample &#038; source are also below.<\/p>\n<p>First, you&#8217;ll need a modern Python install. I did all the development on Mac OS Mountain Lion (beta) with the stock Python 2.7 build. You&#8217;ll also need the <a href=\"https:\/\/www.cairographics.org\/\">Cairo 2D graphics library<\/a> which built and installed perfectly from source, even on ML, so it should work fine for you. If you want something besides PDF rendering, you may need additional libraries, but PDF is decent for hi-res embedding, converting to jpg\/png (see below) and tweaking in programs like Illustrator.<\/p>\n<p><a href=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"1090\" data-permalink=\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/slopegraph\/\" data-orig-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph.png?fit=1203%2C1072&amp;ssl=1\" data-orig-size=\"1203,1072\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;}\" data-image-title=\"slopegraph\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph.png?fit=300%2C267&amp;ssl=1\" data-large-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph.png?fit=510%2C454&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph.png?resize=300%2C267&#038;ssl=1\" alt=\"\" title=\"slopegraph\" width=\"300\" height=\"267\" class=\"aligncenter size-medium wp-image-1090\" srcset=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph.png?resize=300%2C267&amp;ssl=1 300w, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph.png?resize=1024%2C912&amp;ssl=1 1024w, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph.png?w=1203&amp;ssl=1 1203w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>If you search for &#8220;Gender Comparisons&#8221; in the comments on <a href=\"https:\/\/www.edwardtufte.com\/bboard\/q-and-a-fetch-msg?msg_id=0003nk\">this post<\/a> at Tufte&#8217;s blog, you&#8217;ll see what I was trying to reproduce in this bit of skeleton code (below). By modifying the CSV file you&#8217;re using [line 21] and then which fields are relevant [lines 45-47] you should be able to make your own basic slopegraphs without much trouble.<\/p>\n<p>If you catch any glitches, add some tweak or have a slopegraph &#8220;wish list&#8221;, let me know here, twitter (@hrbrmstr) or over at github.<\/p>\n<pre lang=\"python\" line=\"1\"># slopegraph.py\r\n#\r\n# Author: Bob Rudis (@hrbrmstr)\r\n#\r\n# Basic Python skeleton to do simple two value slopegraphs\r\n# with output to PDF (most useful form for me...Cairo has tons of options)\r\n#\r\n# Find out more about & download Cairo here:\r\n# http:\/\/cairographics.org\/\r\n#\r\n# 2012-05-28 - 0.5 - Initial github release. Still needs some polish\r\n#\r\n\r\nimport csv\r\nimport cairo\r\n\r\n# original data source: http:\/\/www.calvin.edu\/~stob\/data\/television.csv\r\n\r\n# get a CSV file to work with \r\n\r\nslopeReader = csv.reader(open('television.csv', 'rb'), delimiter=',', quotechar='\"')\r\n\r\nstarts = {} # starting \"points\"\/\r\nends = {} # ending \"points\"\r\n\r\n# Need to refactor label max width into font calculations\r\n# as there's no guarantee the longest (character-wise)\r\n# label is the widest one\r\n\r\nstartLabelMaxLen = 0\r\nendLabelMaxLen = 0\r\n\r\n# build a base pair array for the final plotting\r\n# wastes memory, but simplifies plotting\r\n\r\npairs = []\r\n\r\nfor row in slopeReader:\r\n\r\n\t# add chosen values (need start\/end for each CSV row)\r\n\t# to the final plotting array. Try this sample with \r\n\t# row[1] (average life span) instead of row[5] to see some\r\n\t# of the scaling in action\r\n\t\r\n\tlab = row[0] # label\r\n\tbeg = row[5] # male life span\r\n\tend = row[4] # female life span\r\n\t\r\n\tpairs.append( (float(beg), float(end)) )\r\n\r\n\t# combine labels of common values into one string\r\n\t# also (as noted previously, inappropriately) find the\r\n\t# longest one\r\n\r\n\tif beg in starts:\r\n\t\tstarts[beg] = starts[beg] + \"; \" + lab\r\n\telse:\r\n\t\tstarts[beg] = lab\r\n\t\r\n\tif ((len(starts[beg]) + len(beg)) > startLabelMaxLen):\r\n\t\tstartLabelMaxLen = len(starts[beg]) + len(beg)\r\n\t\ts1 = starts[beg]\r\n\r\n\r\n\tif end in ends:\r\n\t\tends[end] = ends[end] + \"; \" + lab\r\n\telse:\r\n\t\tends[end] = lab\r\n\t\r\n\tif ((len(ends[end]) + len(end)) > endLabelMaxLen):\r\n\t\tendLabelMaxLen = len(ends[end]) + len(end)\r\n\t\te1 = ends[end]\r\n\r\n# sort all the values (in the event the CSV wasn't) so\r\n# we can determine the smallest increment we need to use\r\n# when stacking the labels and plotting points\r\n\r\nstartSorted = [(k, starts[k]) for k in sorted(starts)]\r\nendSorted = [(k, ends[k]) for k in sorted(ends)]\r\n\r\nstartKeys = sorted(starts.keys())\r\ndelta = max(startSorted)\r\nfor i in range(len(startKeys)):\r\n\tif (i+1 <= len(startKeys)-1):\r\n\t\tcurrDelta = float(startKeys[i+1]) - float(startKeys[i])\r\n\t\tif (currDelta < delta):\r\n\t\t\tdelta = currDelta\r\n\t\t\t\r\nendKeys = sorted(ends.keys())\r\nfor i in range(len(endKeys)):\r\n\tif (i+1 <= len(endKeys)-1):\r\n\t\tcurrDelta = float(endKeys[i+1]) - float(endKeys[i])\r\n\t\tif (currDelta < delta):\r\n\t\t\tdelta = currDelta\r\n\r\n# we also need to find the absolute min &#038; max values\r\n# so we know how to scale the plots\r\n\r\nlowest = min(startKeys)\r\nif (min(endKeys) < lowest) : lowest = min(endKeys)\r\n\r\nhighest = max(startKeys)\r\nif (max(endKeys) > highest) : highest = max(endKeys)\r\n\r\n# just making sure everything's a number\r\n# probably should move some of this to the csv reader section\r\n\r\ndelta = float(delta)\r\nlowest = float(lowest)\r\nhighest = float(highest)\r\nstartLabelMaxLen = float(startLabelMaxLen)\r\nendLabelMaxLen = float(endLabelMaxLen)\r\n\r\n# setup line width and font-size for the Cairo\r\n# you can change these and the constants should\r\n# scale the plots accordingly\r\n\r\nFONT_SIZE = 9\r\nLINE_WIDTH = 0.5\r\n\r\n# there has to be a better way to get a base \"surface\"\r\n# to do font calculations besides this. we're just making\r\n# this Cairo surface to we know the max pixel width \r\n# (font extents) of the labels in order to scale the graph\r\n# accurately (since width\/height are based, in part, on it)\r\n\r\nfilename = 'slopegraph.pdf'\r\nsurface = cairo.PDFSurface (filename, 8.5*72, 11*72)\r\ncr = cairo.Context (surface)\r\ncr.save()\r\ncr.select_font_face(\"Sans\", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)\r\ncr.set_font_size(FONT_SIZE)\r\ncr.set_line_width(LINE_WIDTH)\r\nxbearing, ybearing, sWidth, sHeight, xadvance, yadvance = (cr.text_extents(s1))\r\nxbearing, ybearing, eWidth, eHeight, xadvance, yadvance = (cr.text_extents(e1))\r\nxbearing, ybearing, spaceWidth, spaceHeight, xadvance, yadvance = (cr.text_extents(\" \"))\r\ncr.restore()\r\ncr.show_page()\r\nsurface.finish()\r\n\r\n# setup some more constants for plotting\r\n# all of these are malleable and should cascade nicely\r\n\r\nX_MARGIN = 10\r\nY_MARGIN = 10\r\nSLOPEGRAPH_CANVAS_SIZE = 200\r\nspaceWidth = 5\r\nLINE_HEIGHT = 15\r\nPLOT_LINE_WIDTH = 0.5\r\n\r\nwidth = (X_MARGIN * 2) + sWidth + spaceWidth + SLOPEGRAPH_CANVAS_SIZE + spaceWidth + eWidth\r\nheight = (Y_MARGIN * 2) + (((highest - lowest + 1) \/ delta) * LINE_HEIGHT)\r\n\r\n# create the real Cairo surface\/canvas\r\n\r\nfilename = 'slopegraph.pdf'\r\nsurface = cairo.PDFSurface (filename, width, height)\r\ncr = cairo.Context (surface)\r\n\r\ncr.save()\r\n\r\ncr.select_font_face(\"Sans\", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)\r\ncr.set_font_size(FONT_SIZE)\r\n\r\ncr.set_line_width(LINE_WIDTH)\r\ncr.set_source_rgba (0, 0, 0) # need to make this a constant\r\n\r\n# draw start labels at the correct positions\r\n# cheating a bit here as the code doesn't (yet) line up \r\n# the actual data values\r\n\r\nfor k in sorted(startKeys):\r\n\r\n\tlabel = starts[k]\r\n\txbearing, ybearing, lWidth, lHeight, xadvance, yadvance = (cr.text_extents(label))\r\n\r\n\tval = float(k)\r\n\t\t\r\n\tcr.move_to(X_MARGIN + (sWidth - lWidth), Y_MARGIN + (highest - val) * LINE_HEIGHT * (1\/delta) + LINE_HEIGHT\/2)\r\n\tcr.show_text(label + \" \" + k)\r\n\tcr.stroke()\r\n\r\n# draw end labels at the correct positions\r\n# cheating a bit here as the code doesn't (yet) line up \r\n# the actual data values\r\n\r\nfor k in sorted(endKeys):\r\n\r\n\tlabel = ends[k]\r\n\txbearing, ybearing, lWidth, lHeight, xadvance, yadvance = (cr.text_extents(label))\r\n\r\n\tval = float(k)\r\n\t\t\r\n\tcr.move_to(width - X_MARGIN - eWidth - (4*spaceWidth), Y_MARGIN + (highest - val) * LINE_HEIGHT * (1\/delta) + LINE_HEIGHT\/2)\r\n\tcr.show_text(k + \" \" + label)\r\n\tcr.stroke()\r\n\r\n# do the actual plotting\r\n\r\ncr.set_line_width(PLOT_LINE_WIDTH)\r\ncr.set_source_rgba (0.75, 0.75, 0.75) # need to make this a constant\r\n\r\nfor s1,e1 in pairs:\r\n\tcr.move_to(X_MARGIN + sWidth + spaceWidth + 20, Y_MARGIN + (highest - s1) * LINE_HEIGHT * (1\/delta) + LINE_HEIGHT\/2)\r\n\tcr.line_to(width - X_MARGIN - eWidth - spaceWidth - 20, Y_MARGIN + (highest - e1) * LINE_HEIGHT * (1\/delta) + LINE_HEIGHT\/2)\r\n\tcr.stroke()\r\n\r\ncr.restore()\r\ncr.show_page()\r\nsurface.finish()<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>(NOTE: You can keep up with progress best at github, but can always search on &#8220;slopegraph&#8221; here or just hit the tag page: &#8220;slopegraph&#8221; regularly) I&#8217;ve been a bit obsessed with slopegraphs (a.k.a &#8220;Tufte table-chart&#8221;) of late and very dissatisfied with the lack of tools to make this particular visualization tool more prevalent. While my [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":true,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":3,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"","footnotes":""},"categories":[24,63,87,12,640],"tags":[751,657,658],"class_list":["post-1089","post","type-post","status-publish","format-standard","hentry","category-charts-graphs","category-development","category-open-source","category-os-x","category-python-2","tag-slopegraph","tag-table-chart","tag-tufte"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.2 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Slopegraphs in Python - rud.is<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Slopegraphs in Python - rud.is\" \/>\n<meta property=\"og:description\" content=\"(NOTE: You can keep up with progress best at github, but can always search on &#8220;slopegraph&#8221; here or just hit the tag page: &#8220;slopegraph&#8221; regularly) I&#8217;ve been a bit obsessed with slopegraphs (a.k.a &#8220;Tufte table-chart&#8221;) of late and very dissatisfied with the lack of tools to make this particular visualization tool more prevalent. While my [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/\" \/>\n<meta property=\"og:site_name\" content=\"rud.is\" \/>\n<meta property=\"article:published_time\" content=\"2012-05-29T02:01:39+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2017-03-27T14:40:30+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph-300x267.png\" \/>\n<meta name=\"author\" content=\"hrbrmstr\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"hrbrmstr\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/\"},\"author\":{\"name\":\"hrbrmstr\",\"@id\":\"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886\"},\"headline\":\"Slopegraphs in Python\",\"datePublished\":\"2012-05-29T02:01:39+00:00\",\"dateModified\":\"2017-03-27T14:40:30+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/\"},\"wordCount\":300,\"commentCount\":2,\"publisher\":{\"@id\":\"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886\"},\"image\":{\"@id\":\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph-300x267.png\",\"keywords\":[\"slopegraph\",\"table-chart\",\"tufte\"],\"articleSection\":[\"Charts &amp; Graphs\",\"Development\",\"Open Source\",\"OS X\",\"Python\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/\",\"url\":\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/\",\"name\":\"Slopegraphs in Python - rud.is\",\"isPartOf\":{\"@id\":\"https:\/\/rud.is\/b\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph-300x267.png\",\"datePublished\":\"2012-05-29T02:01:39+00:00\",\"dateModified\":\"2017-03-27T14:40:30+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#primaryimage\",\"url\":\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph.png?fit=1203%2C1072&ssl=1\",\"contentUrl\":\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph.png?fit=1203%2C1072&ssl=1\",\"width\":\"1203\",\"height\":\"1072\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/rud.is\/b\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Slopegraphs in Python\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/rud.is\/b\/#website\",\"url\":\"https:\/\/rud.is\/b\/\",\"name\":\"rud.is\",\"description\":\"&quot;In God we trust. All others must bring data&quot;\",\"publisher\":{\"@id\":\"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/rud.is\/b\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886\",\"name\":\"hrbrmstr\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1\",\"url\":\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1\",\"contentUrl\":\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1\",\"width\":460,\"height\":460,\"caption\":\"hrbrmstr\"},\"logo\":{\"@id\":\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1\"},\"description\":\"Don't look at me\u2026I do what he does \u2014 just slower. #rstats avuncular \u2022 ?Resistance Fighter \u2022 Cook \u2022 Christian \u2022 [Master] Chef des Donn\u00e9es de S\u00e9curit\u00e9 @ @rapid7\",\"sameAs\":[\"http:\/\/rud.is\"],\"url\":\"https:\/\/rud.is\/b\/author\/hrbrmstr\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Slopegraphs in Python - rud.is","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/","og_locale":"en_US","og_type":"article","og_title":"Slopegraphs in Python - rud.is","og_description":"(NOTE: You can keep up with progress best at github, but can always search on &#8220;slopegraph&#8221; here or just hit the tag page: &#8220;slopegraph&#8221; regularly) I&#8217;ve been a bit obsessed with slopegraphs (a.k.a &#8220;Tufte table-chart&#8221;) of late and very dissatisfied with the lack of tools to make this particular visualization tool more prevalent. While my [&hellip;]","og_url":"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/","og_site_name":"rud.is","article_published_time":"2012-05-29T02:01:39+00:00","article_modified_time":"2017-03-27T14:40:30+00:00","og_image":[{"url":"https:\/\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph-300x267.png","type":"","width":"","height":""}],"author":"hrbrmstr","twitter_card":"summary_large_image","twitter_misc":{"Written by":"hrbrmstr","Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#article","isPartOf":{"@id":"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/"},"author":{"name":"hrbrmstr","@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886"},"headline":"Slopegraphs in Python","datePublished":"2012-05-29T02:01:39+00:00","dateModified":"2017-03-27T14:40:30+00:00","mainEntityOfPage":{"@id":"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/"},"wordCount":300,"commentCount":2,"publisher":{"@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886"},"image":{"@id":"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#primaryimage"},"thumbnailUrl":"https:\/\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph-300x267.png","keywords":["slopegraph","table-chart","tufte"],"articleSection":["Charts &amp; Graphs","Development","Open Source","OS X","Python"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/","url":"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/","name":"Slopegraphs in Python - rud.is","isPartOf":{"@id":"https:\/\/rud.is\/b\/#website"},"primaryImageOfPage":{"@id":"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#primaryimage"},"image":{"@id":"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#primaryimage"},"thumbnailUrl":"https:\/\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph-300x267.png","datePublished":"2012-05-29T02:01:39+00:00","dateModified":"2017-03-27T14:40:30+00:00","breadcrumb":{"@id":"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#primaryimage","url":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph.png?fit=1203%2C1072&ssl=1","contentUrl":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2012\/05\/slopegraph.png?fit=1203%2C1072&ssl=1","width":"1203","height":"1072"},{"@type":"BreadcrumbList","@id":"https:\/\/rud.is\/b\/2012\/05\/28\/slopegraphs-in-python\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/rud.is\/b\/"},{"@type":"ListItem","position":2,"name":"Slopegraphs in Python"}]},{"@type":"WebSite","@id":"https:\/\/rud.is\/b\/#website","url":"https:\/\/rud.is\/b\/","name":"rud.is","description":"&quot;In God we trust. All others must bring data&quot;","publisher":{"@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/rud.is\/b\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886","name":"hrbrmstr","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1","url":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1","contentUrl":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1","width":460,"height":460,"caption":"hrbrmstr"},"logo":{"@id":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1"},"description":"Don't look at me\u2026I do what he does \u2014 just slower. #rstats avuncular \u2022 ?Resistance Fighter \u2022 Cook \u2022 Christian \u2022 [Master] Chef des Donn\u00e9es de S\u00e9curit\u00e9 @ @rapid7","sameAs":["http:\/\/rud.is"],"url":"https:\/\/rud.is\/b\/author\/hrbrmstr\/"}]}},"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p23idr-hz","jetpack_likes_enabled":true,"jetpack-related-posts":[{"id":1115,"url":"https:\/\/rud.is\/b\/2012\/05\/29\/slopegraphs-in-python-colors-fonts-alignment\/","url_meta":{"origin":1089,"position":0},"title":"Slopegraphs In Python \u2013\u00a0colors, fonts &#038; alignment","author":"hrbrmstr","date":"2012-05-29","format":false,"excerpt":"On the heels of last evening's release of Slopegraphs in Python post comes some minor tweaks: Complete alignment control of labels & and values Colors (for background, lines, labels & values) \u2014 I picked a random pattern from Adobe's Kuler A font change (to prove width calculations work) \u2026and a\u2026","rel":"","context":"In &quot;Charts &amp; Graphs&quot;","block_context":{"text":"Charts &amp; Graphs","link":"https:\/\/rud.is\/b\/category\/charts-graphs\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1181,"url":"https:\/\/rud.is\/b\/2012\/06\/05\/slopegraphs-in-python-slope-colors\/","url_meta":{"origin":1089,"position":1},"title":"Slopegraphs in Python \u2013 Slope Colors","author":"hrbrmstr","date":"2012-06-05","format":false,"excerpt":"As the codebase gets closer to the 1.0 stretch we now have the addition of slope colors for when values go up\/down or remain constant between points. The code still only handles two columns of data, but the intent is for each segment to also be colored appropriately (up\/down\/same) in\u2026","rel":"","context":"In &quot;Charts &amp; Graphs&quot;","block_context":{"text":"Charts &amp; Graphs","link":"https:\/\/rud.is\/b\/category\/charts-graphs\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1191,"url":"https:\/\/rud.is\/b\/2012\/06\/07\/slopegraphs-in-python-exploring-binningrounding\/","url_meta":{"origin":1089,"position":2},"title":"Slopegraphs in Python \u2013 Exploring Binning\/Rounding","author":"hrbrmstr","date":"2012-06-07","format":false,"excerpt":"One of the last items for the 1.0 release is support for multiple columns of data. That will require some additional refactoring, so I've been procrastinating by exploring the recent \"fudging\" discovery. Despite claims to the contrary on other sites, there are more folks playing with slopegraphs than you might\u2026","rel":"","context":"In &quot;Charts &amp; Graphs&quot;","block_context":{"text":"Charts &amp; Graphs","link":"https:\/\/rud.is\/b\/category\/charts-graphs\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1334,"url":"https:\/\/rud.is\/b\/2012\/06\/20\/slopegraphs-in-python-failed-states-index-part-1\/","url_meta":{"origin":1089,"position":3},"title":"Slopegraphs in Python \u2013\u00a0Failed States Index (Part 1)","author":"hrbrmstr","date":"2012-06-20","format":false,"excerpt":"The Fund For Peace (FFP) and Foreign Policy jointly released the 2012 version of the \"failed states index\" (FSI). From the FFP site, the FSI: \u2026focuses on the indicators of risk and is based on thousands of articles and reports that are processed by our CAST Software from electronically available\u2026","rel":"","context":"In &quot;Charts &amp; Graphs&quot;","block_context":{"text":"Charts &amp; Graphs","link":"https:\/\/rud.is\/b\/category\/charts-graphs\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1155,"url":"https:\/\/rud.is\/b\/2012\/06\/05\/slopegraphs-the-quintessential-gdp-example-revisited\/","url_meta":{"origin":1089,"position":4},"title":"Slopegraphs \u2013\u00a0The Quintessential Tufte GDP Example Revisited","author":"hrbrmstr","date":"2012-06-05","format":false,"excerpt":"If you're even remotely familiar with slopegraphs, then you'll recognize Tufte's classic 1970-1979 GDP chart example (click for larger version): What you may not notice initially, however, is that Tufte \u2014 well \u2014\u00a0cheated. Yes, I said it. Cheated. I can show it by zooming into the \"Belgium\/Canada\/Finland\" grouping in the\u2026","rel":"","context":"In &quot;Charts &amp; Graphs&quot;","block_context":{"text":"Charts &amp; Graphs","link":"https:\/\/rud.is\/b\/category\/charts-graphs\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1136,"url":"https:\/\/rud.is\/b\/2012\/05\/31\/slopegraphs-in-python-attention-to-detail\/","url_meta":{"origin":1089,"position":5},"title":"Slopegraphs in Python \u2013 Attention to Detail","author":"hrbrmstr","date":"2012-05-31","format":false,"excerpt":"In the previous installment, a foundation was laid for \"parameterizing\" fonts, colors and overall slopegraph size. However, a big failing in all this code (up until now) was the reliance on character string length to determine label width. When working with fonts, the font metrics are more important since a\u2026","rel":"","context":"In &quot;Charts &amp; Graphs&quot;","block_context":{"text":"Charts &amp; Graphs","link":"https:\/\/rud.is\/b\/category\/charts-graphs\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/posts\/1089","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/comments?post=1089"}],"version-history":[{"count":0,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/posts\/1089\/revisions"}],"wp:attachment":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/media?parent=1089"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/categories?post=1089"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/tags?post=1089"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}