from django.contrib import dataplot
from rpy import r,RException
import os,pdb
class RFunctionDoesNotExist(RException): pass
def values_to_df(values):
"""Django values list of dicts -> R data.frame
This makes writing Django and R code easy, since querysets and
data.frames are the native ways of describing data tables in the
respective programming paradigms.
"""
dfkwargs={}
for k in values[0].keys():
dfkwargs[k]=[]
for ride in values:
val=ride[k]
dfkwargs[k].append(val)
df=r.data_frame(**dfkwargs)
return df
class Plot(dataplot.GenericPlot):
"""R plot for the web.
"""
convert_to={
'png':{'suffix':'.png'},
'thumb':{'suffix':'-thumb.png','convert_args':'-resize 65x90'},
'pdf':{'suffix':'.pdf'},
}
convert_from='pdf'
w=9
h=6.5
view_program='xpdf'
def get_data_file(self):
return self.get_full_base()+'.Rdata'
def get_test_file(self):
return self.get_full_base()+'.test.R'
def get_r_fun(self,e=None):
"""Try to get the R function from the r environment.
Returns true if it worked.
"""
try:
self.plot_fun=getattr(r,self.r_fun_name)
return True
except RException:
if e:
raise e
def __init__(self,*args,**kwargs):
"""Infer default values at init.
"""
# infer default r_fun_name from class name
if 'r_fun_name' not in dir(self):
self.r_fun_name=self.__class__.__name__.lower().replace("_",".")
#pdb.set_trace()
super(Plot,self).__init__(*args,**kwargs)
def check_files_for_function(self):
"""Go through files looking for the plot function.
"""
# infer default r_code_filename from r_fun_name
filename=getattr(self,'r_code_filename',self.r_fun_name+".R")
# possible paths to the R source file, based on INSTALLED_APPS
self.r_fullpaths=[
os.path.join(d,"R",filename) for d in self.get_app_dirs()]
actual_files=[f for f in self.r_fullpaths if os.path.exists(f)]
# test each one by sourcing it and checking if fun exists after
for r_code_fullpath in actual_files:
try:
r.source(r_code_fullpath)
except RException: # file does not exist or syntax error
pass
# if it worked, return now
if self.get_r_fun():
self.r_code_filename_fullpath=r_code_fullpath
return True
def source_for_function(self):
"""Source R code files looking for fun_name.
Raise error if fun_name is never found.
"""
# if it already exists, return now
if self.get_r_fun():
return
# if we can find it in a file, return now
if self.check_files_for_function():
return
# if it didn't work by now, raise error
e="Could not find R fun %s in %s"%(self.r_fun_name,self.r_fullpaths)
self.get_r_fun(RFunctionDoesNotExist(e))
def save_data(self):
"""Save result of call to get_plot_args in Rdata.
"""
data_file=self.get_data_file()
test_file=self.get_test_file()
kwargs=self.get_plot_args()
if not os.path.exists(test_file):
self.check_files_for_function()
Rcode='load("%s")\nsource("%s")\n%s(%s)\n'%(
data_file,
self.r_code_filename_fullpath,
self.r_fun_name,
',\n'.join(['%s=%s'%(k,k) for k in kwargs]),
)
f=open(test_file,'w')
f.write(Rcode)
f.close()
if not os.path.exists(data_file):
kwargs=self.get_plot_args()
for k in kwargs:
r.assign(k,kwargs[k])
r.save(list=kwargs.keys(),file=data_file)
def makefile(self):
"""Start a PDF device and execute R plotting code.
Also executes the conversion to other formats.
"""
try:
filename=self.get_filenames()['pdf']
# Can't pass unicode strings here
r.pdf(filename,h=self.h,w=self.w)
except RException, e:
raise dataplot.PlotError('\n'.join([
"Error in starting the R PDF graphics device.",
"Does the webserver have permissions to write %s?"%filename]))
# execute the gather-data function specified at init
self.r_args=r_args=self.get_plot_args()
# Look for function first -- get from r code if specified
self.source_for_function()
# Then actually draw the figure -- may fail if bad data
try:
# weird bug for french , decimal separator
r.Sys_setlocale("LC_NUMERIC","C")
self.plot_fun_return_val=rval=self.plot_fun(**self.r_args)
r.dev_off()
except RException, e:
try:
self.save_data()
except:
pass
raise dataplot.PlotError('\n'.join([
'Error in generating the plots.',
'Is all the required data present?\nR said: %s'%e]))
class Scatter(Plot):
"""Simple x-y scatterplot.
Required:
x: list of ints or floats: horizontal values.
y: list of ints or floats: vertical values.
Optional:
ann: list of strings: labels for each data point.
pch: plotting symbol to use; see R>example(points).
fit.line: logical: fit and plot a linear model to the data?
axis.round: decimal points for rounding axis labels.
lty.x.y: lty of line at x=y, default: 0 => no line.
one.to.one: Force axes to be same?
"""
r_fun_name='generic.scatter.plot'
class TimeSeries(Plot):
"""Simple cumulative time series.
Required:
d: list of time data producted with strftime('%s')
Optional:
y: values at time points. Will assume 1 for each as default.
transform: how to transform the data before plotting, one of:
'cumulative', 'monthly', 'daily'
"""
r_fun_name='generic.time.series'
class Histogram(Plot):
"""Generic histogram for showing a univariate distribution.
Arguments passed verbatim to R base function hist.
"""
r_fun_name='hist'
class NormalQQPlot(Plot):
"""Use to see if univariate data are approximately normal.
All arguments are passed verbatim to R base function qqnorm.
"""
r_fun_name='generic.qqnorm'