Looking through the recent petition/vow making against the commercial nature of academic journals (http://thecostofknowledge.com/) I couldn’t help notice a fairly strong subject area bias. So, I scraped the subject area fields of the website and made this pretty graph:

But what does it mean?

If you want to make your own jelly plot, here’s the python class to do it. You’ll need this image too (click for full size):

It’s a little slow at the moment, but it works.

jellyplot.py:

from numpy import * import Image import colorsys def plot_fracs_on(plt, fracs, color=[1,1,1], linewidth=3, scales=(1,1)): for frac in fracs: plt.plot( [scales[0]*0.5, scales[0]*(0.5-0.5*cos(2*pi*frac))], [scales[1]*0.5, scales[1]*(0.5-0.5*sin(2*pi*frac))], color=color, linewidth=linewidth) # vectorise the colorsys components to_hsv = vectorize(colorsys.rgb_to_hsv) to_rgb = vectorize(colorsys.hsv_to_rgb) # binning function for making pie chart def pie_binner(data): """ Create a function to assign a class to each number in [0,1] according to data""" # sort data pairs = data.items() pairs.sort(cmp = lambda x,y: cmp(x[1],y[1])) labels, vals = zip(*pairs) # fractional amounts total = float(sum(vals)) fracs = [val/total for val in vals] # cumulative fractions nfracs = len(fracs) cum_fracs = [sum(fracs[:i+1]) for i in range(nfracs)] cum_fracs.reverse() # create function def bin_function(value): """ Expects a number in [0,1] """ for i in range(nfracs): if value > cum_fracs[i] : return nfracs - i return 0 # return function, labels and fractions return bin_function, labels, fracs, cum_fracs class JellyPlot: """ Plots that invlove a jelly """ def __init__(self, filename="piejelly.png"): print "Loading data, man this is slow..." fid = open(filename) image = Image.open(fid) data = array(image.getdata()).reshape(image.size[0], image.size[1], 4) # discard alpha channel rgbdata = data[:,:,:3] # get matrix of hsv values # creates tuple of np arrays in [0,1] rgbdata = rgbdata/256. hsvdata = list(to_hsv(rgbdata[:,:,0], rgbdata[:,:,1], rgbdata[:,:,2])) s = (hsvdata[2].shape[1], hsvdata[2].shape[0]) # set fields, ridiculous reshaping self.hsvdata = [x.reshape(*s) for x in hsvdata] self.newhsvdata = self.hsvdata self.shape = s self.legend = None self.cfracs = None self.figsize = image.size[0], image.size[1] fid.close() print "Done." def __apply_hue_transform__(self, transform): """ Internal thing for transforming the hue coordinate """ self.newhsvdata[0] = (self.hsvdata[0]+transform)%1 def make_stripy(self): """ Just for fun (and testing) """ self.__apply_hue_transform__(0.5*( (1./jellyplot.shape[0])*arange(jellyplot.shape[0]).reshape(-1,1) + (1./jellyplot.shape[1])*arange(jellyplot.shape[1]).reshape(1,-1))) def pie(self, data, hue_offset=0.3): """ makes a jellypie chart for a dictionary """ assert data.__class__ == dict # find centre of image cx, cy = self.shape[0]/2., self.shape[1]/2. # matrix of angles xcoord = (arange(self.shape[0]).reshape(-1,1)-cx) ycoord = (arange(self.shape[1]).reshape(1,-1)-cy) angles = 0.5-arctan2(xcoord,ycoord)/(2*pi) # create transformation binner_fun, labels, fracs, cfracs = pie_binner(data) binner_fun_v = vectorize(binner_fun) print "Calculating transformation: this isn't optimised so may take a while..." pie_data_transform = hue_offset+binner_fun_v(angles)/float(len(data)) print "Applying..." self.__apply_hue_transform__(pie_data_transform) # create legend data self.legend = [(("%3.0d%%: "%(100*f))+lab, colorsys.hsv_to_rgb( hue_offset + i/float(len(data)), 1., 0.5)) for f,lab,i in zip(fracs,labels,range(len(data)))] self.cfracs = cfracs def show(self, legend_offset=2): """ Show the chart, legend_offset increases the hight of the legend by n entries - default=2 """ import matplotlib.pyplot as plt dpi = plt.rcParams['figure.dpi'] fs = self.figsize[0]/dpi, self.figsize[1]/dpi k = 0.01 plt.figure(figsize=fs) ax1 = plt.axes([k,1./6,2./3,2./3], frameon=False) ax1.set_axis_off() newimage = Image.fromarray(self.__create_rgb_data__()) im = ax1.imshow(newimage, origin="lower") if not self.cfracs == None: plot_fracs_on(ax1, self.cfracs, scales=self.shape) if not self.legend == None: ax2 = plt.axes([2./3+k,0,1./3-k,1],frameon=False) ax2.set_axis_off() dx = 0.05 k_txt = 0.03 x = 0.5*(1-dx*(len(self.legend)-legend_offset)) for leg,col in self.legend: ax2.text(k_txt,x,leg,color=col) x += dx plt.show() def __create_rgb_data__(self): print "Rendering..." return uint8(256*array(to_rgb(*self.newhsvdata)).swapaxes(0,2).swapaxes(0,1)) def imshow(self): """ Show the plot as an image""" rgbdata = self.__create_rgb_data__() # convert back to image newimage = Image.fromarray(rgbdata) newimage.show() if __name__ == "__main__": jellyplot = JellyPlot() # make a plot object jellyplot.pie({"A":4,"B":2,"C":3}) # turn it into a pie chart jellyplot.show() # show it