Bubble Charts & Hover Text with Plotly

This notebook is out-of-date (Apr-14 2015)

Up-to-date version: Bubble chart tutorial in the User Guide

See IPython notebook for more Plotly IPython notebooks.

***

I'm Jack Parmer

Plotly is like graphing crack. It standardizes the graphing interface across scientific computing languages (Python, R, MATLAB, ect) while giving rich interactivity and web shareability that has not been possible before with matplotlib, ggplot, MATLAB, ect. On the Plotly website, you can style your graphs with a GUI, so you don't have to spend hours writing code that simply changes the legend opacity.

Plotly does this all while backing up your graphs on the cloud, so that years later, you can find data that may have otherwise been on a harddrive in a landfill. If you make your data public, other people can also find your graphs and data. The best practice that we have today for saving and sharing research data is to entomb it as a thesis in the engineering library basement. All that is changing.

Like d3.js? Like interactive, NYT graphics? So do we. Now, with the Plotly APIs, you can make them yourself without being an expert web programmer. If you are an expert web programmer, now you have scientific languages and tools like R, Python, Pandas, and MATLAB instead of javascript to wrangle your data and create beautiful data vis. Science meets the world-wide-web. Engineering meets design. Let's do this.

I'm going to show you this brave new world below, starting with bubble charts. Bubble charts are sweet because they take advantage of the innate interactivity of Plotly graphs. When you hover on a bubble chart point, you want to see what its size represents, you want to zoom-in to points that are clustered, and you want to pan around once you're zoomed-in. You become a Bubble Chart Explorer. Plotly lets you do all this, all while upping the game for scientific, publication-quality graphics.

Check out the graphs below, then follow us on twitter @plotlygraphs for more graphing inspiration.

Hearts
Team Plotly
Montreal | San Francisco | Boston

1: The Never Ending Story Bubble Chart

Here is a simple, single-trace bubble chart showing how to add hover-text and custom colors. Most of the code is for color interpolation and typecasting - not necessary for making a bubble chart, but perhaps handy for some readers. The lovely colorscale is borrowed from colorbrewer.com.

In [1]:
import plotly
import math
import numpy as np

Plotly installs by pip, easy_install, or tar ball. See https://plot.ly/api/python for deets.

In [2]:
un='jackp'
k='11m2qbzob9'
plotly.plotly('IPython.Demo', '1fw3zw2o13')
py = plotly.plotly(username=un, key=k)

Sign up for Plotly and grap your API key at https://plot.ly/api/python

In [3]:
# RGB color scale from colorbrewer.com
GnBu = [(247, 252, 240),(224, 243, 219),(204, 235, 197),\
        (168, 221, 181),(123, 204, 196),(78, 179, 211),\
        (43, 140, 190),(8, 104, 172),(8, 64, 129)]

def rgbToHsl(rgb):
    ''' Adapted from M Bostock's RGB to HSL converter in d3.js
        https://github.com/mbostock/d3/blob/master/src/color/rgb.js '''
    r,g,b = float(rgb[0])/255.0,\
            float(rgb[1])/255.0,\
            float(rgb[2])/255.0
    mx = max(r, g, b)
    mn = min(r, g, b)
    h = s = l = (mx + mn) / 2
    if mx == mn: # achromatic
        h = 0
        s = 0 if l > 0 and l < 1 else h
    else:
        d = mx - mn;        
        s =  d / (mx + mn) if l < 0.5 else d / (2 - mx - mn)
        if mx == r:
            h = (g - b) / d + ( 6 if g < b else 0 )
        elif mx == g:
            h = (b - r) / d + 2
        else:
            h = r - g / d + 4

    return (round(h*60,4), round(s*100,4), round(l*100,4))

def interp3(fraction, start, end):
    ''' Interpolate between values of 2, 3-member tuples '''
    def intp(f, s, e):
        return s + (e - s)*f 
    return tuple([intp(fraction, start[i], end[i]) for i in range(3)])

def colorscale(scl, r):
    ''' Interpolate a hsl colorscale from "scl" with length "r" '''
    c = []
    SCL_FI = len(scl)-1 # final index of color scale 
    
    for i in r:
        c_i = int(i*math.floor(SCL_FI)/round(r[-1])) # start color index
        hsl_o = rgbToHsl( scl[c_i] ) # convert rgb to hls
        hsl_f = rgbToHsl( scl[c_i+1] ) 
        section_min = c_i*r[-1]/SCL_FI
        section_max = (c_i+1)*(r[-1]/SCL_FI)
        fraction = (i-section_min)/(section_max-section_min)
        hsl = interp3( fraction, hsl_o, hsl_f )
        c.append( 'hsl'+str(hsl) )
    return c

r = np.arange(0,20,0.1)
x = [2*np.cos(i)*i+(i*0.2*np.random.rand()) for i in r]
y = [2*np.sin(i)*i+(i*0.2*np.random.rand()) for i in r]
s = [(i+5)*i/5 for i in r] # diameter of bubble size in pixels
t = [('Area: '+str(round(3.14*math.pow(d/2,2),2))+' (sq. pixels)<br>\
    Radius: '+str(round(d/2,2))+' pixels') for d in s] # Show hover text as bubble area
c = colorscale(GnBu, r)

# set hovermode to 'closest' to turn off showing all points near the same x on hover
layout = { 'hovermode':'closest','title':'click-drag to zoom-in<br>double-click to zoom-out',\
'xaxis':{'showticklabels':False,'ticks':'','linecolor':'white','showgrid':False,'zeroline':False},\
'yaxis':{'showticklabels':False,'ticks':'','linecolor':'white','showgrid':False,'zeroline':False} }

data = [ {'x':x,'y':y,'mode':'markers','opacity':0.7,'text':t,\
          'marker':{'size':s,'color':c,'line':{'width':2}}} ]

py.iplot(data, layout=layout)


Out[3]:

2. Cool, Lets look at this as subplots

Lets look at the previous graph as subplots with color variations. The x and y-axes are coupled, so if you zoom and pan in one subplot, the other subplots zoom and pan as well. Sweet!

In [14]:
# color scales from colorbrewer.com

BuGn = [(247,252,253), (229,245,249), (204,236,230),\
    (153,216,201), (102,194,164), (65,174,118),\
    (35,139,69), (0,109,44), (0,68,27)]
BuPu = [(247,252,253), (224,236,244), (191,211,230),\
    (158,188,218), (140,150,198), (140,107,177),\
    (136,65,157), (129,15,124), (77,0,75)]
GnBu = [(247, 252, 240),(224, 243, 219),(204, 235, 197),\
    (168, 221, 181),(123, 204, 196),(78, 179, 211),\
    (43, 140, 190),(8, 104, 172),(8, 64, 129)]

PuBu = [(255,247,251), (236,231,242), (208,209,230),\
        (166,189,219), (116,169,207), (54,144,192),\
        (5,112,176), (4,90,141), (2,56,88)]
PuBuGn = [(255,247,251), (236,226,240), (208,209,230),\
    (166,189,219), (103,169,207), (54,144,192),\
    (2,129,138), (1,108,89), (1,70,54)]
PuRd = [(247,244,249), (231,225,239), (212,185,218),\
    (201,148,199), (223,101,176), (231,41,138),\
    (206,18,86), (152,0,67), (103,0,31)]

RdPu = [(255,247,243), (253,224,221), (252,197,192),\
    (250,159,181), (247,104,161), (221,52,151),\
    (174,1,126), (122,1,119), (73,0,106)]
YlGn = [(255,255,229), (247,252,185), (217,240,163),\
    (173,221,142), (120,198,121), (65,171,93),\
    (35,132,67), (0,104,55), (0,69,41)]
YlGnBu = [(255,255,217), (237,248,177), (199,233,180),\
    (127,205,187), (65,182,196), (29,145,192),\
    (34,94,168), (37,52,148), (8,29,88)]

data = []
layout = {'showlegend':False,'hovermode':'closest',\
          'title':'drag your mouse along the left and bottom border to pan<br>\
              or hold down shift and drag inside. double-click to re-center'}
padding = 0.0
domains = [[i*(1-3*padding)/3, ((i+1)*(1-3*padding)/3)] for i in range(3)]
cscl = [[BuGn,BuPu,GnBu],[PuBu,PuBuGn,PuRd],[RdPu,YlGn,YlGnBu]] # colorscale
s = [(i+5)*i/10 for i in r] # diameter of bubble size in pixels

for j in range(3):
    for k in range(3):
        c = colorscale(cscl[j][k], r)
        data.append({'name': cscl[j][k]+' Spiral '+str(j)+str(k), 
            'x': [2*np.cos(i*(k/4+1))*i+(i*0.2*np.random.rand()) for i in r],
            'y': [2*np.sin(i)*i+(i*0.2*np.random.rand()) for i in r],
            'type':'scatter','mode':'markers','width': 950,'height': 950,'opacity': 0.7,     
            'marker': {'color':c, 'size':s, 'opacity':0.9, 'line':{'width':2}},            
            'xaxis': 'x' + str(j) if j!=0 else '', 
            'yaxis': 'y' + str(k) if k!=0 else '' })
    xy_i = str(j) if j!=0 else ''
    layout['xaxis'+xy_i] = layout['yaxis'+xy_i] = \
        {'domain':domains[j],'showticklabels':False,'ticks':'',\
         'linecolor':'#E3E3E3','linewidth':8,'showgrid':False,'zeroline':False }
    
py.iplot(data, layout=layout, width=1000, height=1000) # iframe size


Out[14]:

3. Bubble Chart of US Crime per state in 2005

This example is from Nathan Yau's blog, Flowing Data. The original post is here, and you can download the data here.

3.1 Check out the first few rows

In [4]:
import pandas as pd
pd.set_option('display.max_columns', 15)
pd.set_option('display.line_width', 400)
pd.set_option('display.mpl_style', 'default')
crime_data = pd.read_csv('crimeRatesByState2005.tsv',sep='\t')
crime_data[:2]
Out[4]:
state murder Forcible_rate Robbery aggravated_assult burglary larceny_theft motor_vehicle_theft population
0 Alabama 8.2 34.3 141.4 247.8 953.8 2650.0 288.3 4627851
1 Alaska 4.8 81.1 80.9 465.1 622.5 2599.1 391.0 686293

3.2 Burrrrglary versus Murrrrder per state

Size the bubbles by state population and include hover text for the name of the state.

In [37]:
data = []
for i in range(len(crime_data['state'])):
    # Create 1 data object per point, so every point is a different color
    mx = float(crime_data['population'].max())
    s = [ math.sqrt(float(crime_data['population'][i])/mx)*60.0 ]
    t = [ 'State: %s<br>Population: %s' % (crime_data['state'][i], crime_data['population'][i]) ]
    d = {'x':[crime_data['murder'][i]],\
         'y':[crime_data['burglary'][i]],\
         'marker': {'size':s, 'opacity':0.9, 'line':{'width':1}},\
         'type':'scatter','mode':'markers','text':t}
    data.append(d)

citation = {'showarrow':False, 'font':{'size':10},'xref':'paper','yref':'paper','x':-0.18,'y':-0.18,'align':'left',\
    'text':'Data source and inspiration:<br>http://flowingdata.com/2010/11/23/how-to-make-bubble-charts/'}

layout = {'showlegend':False,'hovermode':'closest', 'title':'','annotations':[citation],\
    'title':'US Crime Rate by State<br>Bubble Size is State Population',\
    'xaxis':{ 'ticks':'','linecolor':'white','showgrid':False,'zeroline':False, 'title': 'Number of Murders', 'nticks':12 },
    'yaxis':{ 'ticks':'','linecolor':'white','showgrid':False,'zeroline':False, 'title': 'Number of Burglaries', 'nticks':12 }}

py.iplot(data, layout=layout)


Out[37]:

4. Hans Rosling Bubble Chart and Pandas

Literally and figurative, Hans Rosling has put bubble charts on the map. You can watch one of his sweet TED talks with animated bubble charts in this YouTube. Plotly can't do animations (email us us if you're interested in this - feedback [at] plot [dot] ly), but we can take snapshots through time with subplots. I grabbed this Gap Minder data from this UC Berkeley stats page.

4.1 Scope out a few rows

In [50]:
gdp_data = pd.read_csv('gapMinderDataFiveYear.txt',sep='\t')
gdp_data[20:30]
Out[50]:
country year pop continent lifeExp gdpPercap
20 Albania 1992 3326498 Europe 71.581 2497.437901
21 Albania 1997 3428038 Europe 72.950 3193.054604
22 Albania 2002 3508512 Europe 75.651 4604.211737
23 Albania 2007 3600523 Europe 76.423 5937.029526
24 Algeria 1952 9279525 Africa 43.077 2449.008185
25 Algeria 1957 10270856 Africa 45.685 3013.976023
26 Algeria 1962 11000948 Africa 48.303 2550.816880
27 Algeria 1967 12760499 Africa 51.407 3246.991771
28 Algeria 1972 14760787 Africa 54.518 4182.663766
29 Algeria 1977 17152804 Africa 58.014 4910.416756
In [159]:
data = []
years = (1987,2007,1952,1967) # ordering of years is funny for placement in subplot quadrants
sp = [('x','y'), ('x2','y'), ('x','y2'), ('x2','y2')]
# color scale from d3's 'category10' colorscale
cmap = {'Asia':'#1f77b4','Europe':'#ff7f0e','Africa':'#2ca02c',\
        'Americas':'#d62728','Oceania':'#9467bd'}
for i in range(len(years)):
    # grab all rows of this year, turn off Kuwait - its gdp per cap is extremely high
    df = gdp_data[(gdp_data['year']==years[i]) & (gdp_data['country']!='Kuwait')] 
    for name, g in df.groupby('continent'):
        mx = g['pop'].max()
        s = [ math.sqrt(j/mx)*60.0 for j in g['pop'] ]
        t = g.apply(lambda x:'Country: %s<br>Life Expectancy: %s<br>GDP per capita: %s<br>Population: %s<br>Year: %s' \
            % (x['country'], x['lifeExp'],x['gdpPercap'], x['pop'], str(years[i])),axis=1)
        d = {'name':name,'x':g['gdpPercap'],'y':g['lifeExp'],\
            'type':'scatter','mode':'markers','text':t,\
            'marker': {'color':cmap[name],'size':s, 'opacity':0.9, 'line':{'width':1}}}
        d['xaxis'] = sp[i][0]
        d['yaxis'] = sp[i][1]
        data.append(d)    

layout = { 'showlegend':False, 'width':1000, 'height': 700, 'hovermode':'closest',\
          'title':'drag to zoom-in; double-click to zoom-out<br>shift-drag to pan' }

for ax in ('xaxis','yaxis','xaxis2','yaxis2'):
    layout[ax] = { 'ticks':'','linecolor':'white','showgrid':False,'zeroline':False }
    layout[ax]['domain'] = [0.5,1] if '2' in ax else [0,0.5]
    layout[ax]['title'] = 'gdp per capita (usd, 2000)' if 'x' in ax else 'life expectancy (years)'

layout['annotations'] = []
# annotation positions correspond to order of 'years' tuple: 1987,2007,1952,1967
anno_positions = ((0.05,0.4), (0.6,0.4), (0.05,0.95), (0.6,0.95))

for yr in range(len(years)):
    anno_obj = { 'xref': 'paper', 'yref': 'paper', 'showarrow': False,\
        'font': {'family':'','size': 36,'color':'rgb(23, 190, 207)'} }    
    anno_obj['x'] = anno_positions[yr][0]
    anno_obj['y'] = anno_positions[yr][1]
    anno_obj['text'] = str(years[yr])
    layout['annotations'].append( anno_obj )

py.iplot(data,layout=layout,width=1050,height=750)


Out[159]:

5. NYT Graphics with Plotly

This graph is a remake from a 2009 NYT article Why is Her Paycheck Smaller?. Hover over points to see the occupation and salary gap. Male physicians make 40% more salary on average than females? Its not a bubble chart, but it shows some impressive use of hover text.

In [23]:
service = {'name': 'Service, sales, and office',
		  'x': [359, 363, 381, 403, 413, 429, 437, 439, 448, 473, 501, 502, 505, 509, 523, 532, 547, 556, 574, 575, 584, 585, 594,\
                618, 629, 635, 647, 662, 683, 689, 744, 800, 807, 887, 896, 905, 931, 958, 973, 1024, 1239],
		  'y': [333, 351, 341, 348, 363, 385, 379, 357, 442, 389, 483, 418, 522, 503, 468, 486, 406, 568, 521, 489, 550, 426, 525,\
                519, 513, 414, 555, 608, 579, 602, 541, 678, 855, 660, 684, 796, 806, 645, 787, 705, 1030],
		  'text': ['Food preparation workers<br>Women make 8% less than men', 
		  			'Dining room attendants and bartender helpers<br>Women make 6% less than men',
					'Cooks<br>Women make 9% less than men', 'Cashiers<br>Women make 15% less than men',
					'Waiters and waitresses<br>Women make 13% less than men', 'Telemarketers<br>Women make 7% less than men',
					'Personal home and care aides<br>Women make 14% less than men', 'Maids and housekeeping cleaners<br>Women make 18% less than men',
					'Stock clerks<br>Women make 1% less than men', 'Janitors and building cleaners<br>Women make 18% less than men',
					'Receptionists<br>Women make 4% less than men', 'Nursing, psychiatric, and home health aides<br>Women make 16% less than men', 
					'Data entry keyers<br>Women make 1% more than men', 'Shipping and receiving clerks<br>Women make 2% less than men', 
					'Security guards<br>Women make 11% less than men', 'Chefs and head cooks<br>Women make 9% less than men',
					'Bartenders<br>Women make 26% less than men', 'Ticket agents and travel clerks<br>Women make 9% less than men',
					'File clerks<br>Women make 9% less than men', 'Medical assistants<br>Women make 15% less than men',
					'Office clerks<br>Women make 5% less than men', 'Supervisors of food preparation workers<br>Women make 27% less than men',
					'Bill and account collectors<br>Women make 11% less than men', 'Customer service representatives<br>Women make 14% less than men',
					'Recreation and fitness workers<br>Women make 18% less than men', 'Retail sales workers<br>Women make 35% less than men',
					'Dispatchers<br>Women make 15% less than men', 'Bookkeeping, accounting, and auditing clerks<br>Women make 9% less than men',
					'Bailers, correctional officers, and jailers<br>Women make 15% less than men', 'Secretaries and administrative assistants<br>Women make 13% less than men',
					'Supervisors of retail workers<br>Women make 27% less than men', 'Supervisors of office and administrative support<br>Women make 15% less than men',
					'Postal service clerks<br>Women make 4% more than men', 'Production clerks<br>Women make 25% less than men', 
					'Advertising sales agents<br>Women make 24% less than men', 'Police officers<br>Women make 13% less than men', 
					'Postal service mail carriers<br>Women make 13% less than men', 'Insurance sales agents<br>Women make 32% less than men', 
					'Sales representatives<br>Women make 19% less than men', 'Real estate brokers<br>Women make 31% less than men',
					'Financial services sales agents<br>Women make 17% less than men'],
				'type':'scatter',
				'mode':'markers',
				'marker':{'size':9,'color':'#CA4D64'}}

production = {'name': 'Production and transportation',
		  'x': [482, 495, 499, 541, 543, 564, 590, 668, 732, 858],
		  'y': [419, 342, 403, 448, 474, 411, 484, 504, 514, 619],
		  'text': ['Laborers and freight movers<br>Women make 13% less than men', 
		  			'Laundry workers<br>Women make 31% less than men',
					'Bakers<br>Women make 18% less than men',
					'Electronics assemblers<br>Women make 17% less than men', 'Bus drivers<br>Women make 11% less than men',
					'Butchers and other meat processing workers<br>Women make 27% less than men', 'Metal workers and plastic workers<br>Women make 18% less than men',
					'Truck drivers<br>Women make 25% less than men', 'Inspectors and testers<br>Women make 31% less than men',
					'Supervisors of production workers<br>Women make 28% less than men'],
			'type':'scatter',
			'mode':'markers',
			'marker':{'size':9,'color':'#88A4B8'}}

science = {'name': 'Science, computers, and health care',
		  'x': [683, 904, 1050, 1050, 1098, 1159, 1239, 1245, 1266, 1351, 1367, 1508, 1793, 1883],
		  'y': [540, 766, 848, 805, 983, 1039, 1045, 1099, 1078, 985, 861, 1323, 1065, 1609],
		  'text': ['Health diagnosing and treatment technicians<br>Women make 21% less than men', 
		  			'Computer support specialists<br>Women make 15% less than men',
					'Diagnostic related technicians<br>Women make 19% less than men',
					'Clinical laboratory technicians<br>Women make 23% less than men', 'Registered nurses<br>Women make 11% less than men',
					'Market and survey researchers<br>Women make 10% less than men', 'Computer scientists and system analysts<br>Women make 16% less than men',
					'Physical therapists<br>Women make 12% less than men', 'Computer programmers<br>Women make 15% less than men',
					'Chemists and material scientists<br>Women make 27% less than men', 'Medical scientists<br>Women make 37% less than men',
					'Computer software engineers<br>Women make 12% less than men', 'Physicians and surgeons<br>Women make 40% less than men',
					'Pharmacists<br>Women make 15% less than men'],
		   'type':'scatter',
		   'mode':'markers',
           'marker':{'size':9,'color':'#8D9F69'}}

management = {'name': 'Management, business, and financial',
		  'x': [730, 790, 964, 991, 1038, 1064, 1123, 1129, 1183, 1328, 1364, 1376, 1384, 1409, 1450, 1511, 1507, 1597, 1917],
		  'y': [586, 741, 731, 747, 814, 920, 749, 849, 863, 993, 967, 1051, 1088, 1072, 915, 1033, 1077, 1370, 1540],
		  'text': ['Food service managers<br>Women make 20% less than men', 
		  			'Whole and retail buyers<br>Women make 15% less than men',
					'Property managers<br>Women make 24% less than men',
					'Claims adjusters<br>Women make 24% less than men', 'Human resources specialists<br>Women make 21% less than men',
					'Social and community service managers<br>Women make 14% less than men', 'Compliance officers<br>Women make 33% less than men',
					'Loan officers<br>Women make 25% less than men', 'Accountants and auditors<br>Women make 27% less than men',
					'General and operations managers<br>Women make 25% less than men', 'Education administrators<br>Women make 29% less than men',
					'Personal financial advisors<br>Women make 23% less than men', 'Management analysts<br>Women make 21% less than men',
					'Medical and health services managers<br>Women make 24% less than men', 'Financial managers<br>Women make 37% less than men',
					'Marketing and sales managers<br>Women make 31% less than men', 'Human resources managers<br>Women make 32% less than men',
					'Computer and information systems managers<br>Women make 14% less than men', 'Chief executives<br>Women make 19% less than men'],
			'type':'scatter',
			'mode':'markers',
			'marker':{'size':9,'color':'#005082'}}

entertainment = {'name': 'Entertainment, education, and law',
		  'x': [762, 828, 860, 890, 938, 957, 979, 1000, 1236, 1779],
		  'y': [755, 723, 887, 702, 848, 781, 809, 904, 971, 1388],
		  'text': ['Social workers<br>Women make 1% less than men', 
		  			'Counselors<br>Women make 13% less than men',
					'Special education teachers<br>Women make 3% more than men',
					'Designers<br>Women make 22% less than men', 'Elementary and middle school teachers<br>Women make 9% less than men', 
					'Engineering technicians, except drafters<br>Women make 18% less than men', 'Editors<br>Women make 17% less than men', 
					'High school teachers<br>Women make 10% less than men', 'Professors and postsecondary teachers<br>Women make 22% less than men',
					'Lawyers<br>Women make 22% less than men'],
			'type':'scatter',
			'mode':'markers',
			'marker':{'size':9,'color':'#D28628'}}

blank = {'name': '', 'x': [0, 0], 'y': [0, 0], 'line':{'color':'#F0F0F0','width':3},'mode':'lines'}

source = {'name': '<i> Source: Bureau of Labor Statistics:</i><br><i>Census Bureau </i>', 'x': [0, 0], 'y': [0, 0],\
        'line':{'color':'#F0F0F0','width':3},'mode':'lines'}

equal = {'name': 'Equal Wages', 'x': [0, 1650], 'y': [0, 1650],'line':{'color':'black','width':3},'mode':'lines'}

tenpercentless = {'name': 'Women earn 10% less than men','x': [0, 1833.3], 'y': [0, 0.9*1833.3],'line':{'color':'#606060', 'width':2},'mode':'lines'}
twentypercentless = {'name': 'Women earn 20% less than men', 'x': [0, 2062.5], 'y': [0, 0.8*2062.5],'line':{'color':'#909090','width':2},'mode':'lines'}
thirtypercentless = {'name': 'Women earn 30% less than men', 'x': [0, 2357.143], 'y': [0, 0.7*2357.143],'line':{'color':'#D2D2D2','width':2},'mode':'lines'}

layout = {'autosize':False,
			'font':{'color':"rgb(33, 33, 33)",'family':"Arial, sans-serif",'size':12},
			'height':650,
			'width':1100,
			'xaxis':{
				'range':[0,2437],
				'type': 'linear',
				'ticks': 'none',
				'autorange': False,
				'zeroline': False,
				'mirror': False,
				'linecolor':'white',
				'tickcolor':'white',
				'autotick':False,
				'dtick': 250,
				'gridwidth': .7
			},
			'yaxis':{
				'range':[0,1650],
				'type': 'linear',
				'ticks': 'none',
				'autorange': False,
				'zeroline': False,
				'mirror': False,
				'linecolor':'white',
				'tickcolor':'white',
				'autotick':False,
				'dtick': 250,
				'gridwidth': .7
			},
			'legend':{
				 'bgcolor': "#F0F0F0",
				 'bordercolor': "#F0F0F0",
				 'borderwidth': 10,
				 'x': 0.0845912623961403,
				 'y': 0.9811399147727271,
				 'traceorder': 'reversed'
			},
			'margin':{'b':80,'l':100,'pad':2,'r':250,'t':80},
			'annotations':[{
					'text':"<i>Source: Bureau of Labor Statistics - Census Bureau</i>",
					'x':2100,
					'y':30,
					'showarrow':False,
					'ref':'plot',
					'align':'left',
					'font':{'size':'9'}
				},{
					'text':"<b>Men's</b> median weekly earnings",
					'x':1120,
					'y':70,
					'showarrow':False,
					'ref':'plot',
					'align':'left',
					'font':{'size':'13'}
				},{
					'text':"<b>Women's</b><br>median weekly<br>earnings",
					'x':40,
					'y':870,
					'showarrow':False,
					'ref':'plot',
					'align':'left',
					'font':{'size':'13'}
				},{
					'text':"<i>Roll over</i><br><i>dots for</i><br><i>information</i>",
					'x':1870,
					'y':630,
					'showarrow':False,
					'ref':'plot',
					'align':'left',
					'font':{'size':'14'}
				},
			]
		}
py.iplot([equal, tenpercentless, twentypercentless, thirtypercentless,\
          service, production, science, management, entertainment], layout=layout, width=1150, height=700)


Out[23]: