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

