Who’s up for open access?

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"):
fid = open(filename)
image = Image.open(fid)

data = array(image.getdata()).reshape(image.size[0], image.size[1], 4)

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