00001 """Plots using R base graphics code and the rpy backend.
00002
00003 """
00004
00005 from rpy import r,RException
00006 import os,pdb,dataplot
00007
00008 class RFunctionDoesNotExist(RException): pass
00009
00010 def values_to_df(values):
00011 """Django values list of dicts -> R data.frame
00012
00013 This makes writing Django and R code easy, since querysets and
00014 data.frames are the native ways of describing data tables in the
00015 respective programming paradigms.
00016
00017 """
00018 dfkwargs={}
00019 for k in values[0].keys():
00020 dfkwargs[k]=[]
00021 for ride in values:
00022 val=ride[k]
00023 dfkwargs[k].append(val)
00024 df=r.data_frame(**dfkwargs)
00025 return df
00026
00027 class Plot(dataplot.GenericPlot):
00028 """R plot for the web.
00029
00030 Uses the rpy package and base R graphics. The idea is separation
00031 of R and python code, so each subclass needs to map to an R
00032 function (r_fun_name) in a .R code file (r_code_filename) that can
00033 be sourced and used for plotting by rpy.
00034
00035 """
00036
00037 convert_to={
00038 'png':{'suffix':'.png'},
00039 'thumb':{'suffix':'-thumb.png','convert_args':'-resize 65x90'},
00040 'pdf':{'suffix':'.pdf'},
00041 }
00042 convert_from='pdf'
00043 w=9
00044 h=6.5
00045 view_program='xpdf'
00046
00047 def get_data_file(self):
00048 return self.get_full_base()+'.Rdata'
00049
00050 def get_test_file(self):
00051 return self.get_full_base()+'.test.R'
00052
00053 def get_r_fun(self,e=None):
00054 """Try to get the R function from the r environment.
00055
00056 Returns true if it worked.
00057
00058 """
00059 try:
00060 self.plot_fun=getattr(r,self.r_fun_name)
00061 return True
00062 except RException:
00063 if e:
00064 raise e
00065
00066 def __init__(self,*args,**kwargs):
00067 """Infer default values at init.
00068
00069 """
00070
00071 if 'r_fun_name' not in dir(self):
00072 self.r_fun_name=self.__class__.__name__.lower().replace("_",".")
00073
00074 super(Plot,self).__init__(*args,**kwargs)
00075
00076 def check_files_for_function(self):
00077 """Go through files looking for the plot function.
00078
00079 """
00080
00081 filename=getattr(self,'r_code_filename',self.r_fun_name+".R")
00082
00083 self.r_fullpaths=[
00084 os.path.join(d,"R",filename) for d in self.get_app_dirs()]
00085 actual_files=[f for f in self.r_fullpaths if os.path.exists(f)]
00086
00087 for r_code_fullpath in actual_files:
00088 try:
00089 r.source(r_code_fullpath)
00090 except RException:
00091 pass
00092
00093 if self.get_r_fun():
00094 self.r_code_filename_fullpath=r_code_fullpath
00095 return True
00096
00097 def source_for_function(self):
00098 """Source R code files looking for fun_name.
00099
00100 Raise error if fun_name is never found.
00101
00102 """
00103
00104 if self.get_r_fun():
00105 return
00106
00107 if self.check_files_for_function():
00108 return
00109
00110 e="Could not find R fun %s in %s"%(self.r_fun_name,self.r_fullpaths)
00111 self.get_r_fun(RFunctionDoesNotExist(e))
00112
00113 def save_data(self):
00114 """Save result of call to get_plot_args in Rdata.
00115
00116 """
00117 data_file=self.get_data_file()
00118 test_file=self.get_test_file()
00119 kwargs=self.set_r_args()
00120 if os.path.exists(test_file):
00121 print "Warning: %s already exists, not saving test file"%test_file
00122 else:
00123 self.check_files_for_function()
00124 Rcode='load("%s")\nsource("%s")\n%s(%s)\n'%(
00125 data_file,
00126 self.r_code_filename_fullpath,
00127 self.r_fun_name,
00128 ',\n'.join(['%s=%s'%(k,k) for k in kwargs]),
00129 )
00130 f=open(test_file,'w')
00131 f.write(Rcode)
00132 f.close()
00133 if os.path.exists(data_file):
00134 print "Warning: %s already exists, not saving data file"%data_file
00135 else:
00136 for k in kwargs:
00137 r.assign(k,kwargs[k])
00138 r.save(list=kwargs.keys(),file=data_file)
00139
00140 def set_r_args(self):
00141 self.r_args=r_args=self.get_plot_args()
00142 return r_args
00143
00144 def makefile(self):
00145 """Start a PDF device and execute R plotting code.
00146
00147 Also executes the conversion to other formats.
00148
00149 """
00150 try:
00151 filename=self.get_filenames()['pdf']
00152
00153 r.pdf(filename,h=self.h,w=self.w)
00154 except RException, e:
00155 raise dataplot.PlotError('\n'.join([
00156 "Error in starting the R PDF graphics device.",
00157 "Does the webserver have permission to write %s?"%filename]))
00158
00159 r_args=self.set_r_args()
00160
00161 self.source_for_function()
00162
00163 try:
00164
00165 r.Sys_setlocale("LC_NUMERIC","C")
00166 self.plot_fun_return_val=rval=self.plot_fun(**self.r_args)
00167 r.dev_off()
00168 except RException, e:
00169 try:
00170 self.save_data()
00171 except:
00172 pass
00173 raise dataplot.PlotError('\n'.join([
00174 'Error in generating the plots.',
00175 'Is all the required data present?\nR said: %s'%e]))
00176 r.warnings()
00177
00178 if getattr(self,'SAVE_DATA_ON_MAKEFILE',None):
00179 self.save_data()
00180
00181 class Scatter(Plot):
00182 """Simple x-y scatterplot.
00183
00184 Required:
00185 x: list of ints or floats: horizontal values.
00186 y: list of ints or floats: vertical values.
00187
00188 Optional:
00189 ann: list of strings: labels for each data point.
00190 pch: plotting symbol to use; see R>example(points).
00191 fit.lty: lty of least squares fit line, default: 0 => no line.
00192 axis.round: decimal points for rounding axis labels.
00193 lty.x.y: lty of line at x=y, default: 0 => no line.
00194 one.to.one: Force axes to be same?
00195
00196 """
00197 r_fun_name='generic.scatter.plot'
00198 default_args_map={
00199 'xlab':'x',
00200 'ylab':'y',
00201 }
00202
00203 class SquareScatter(Scatter):
00204 w=h=6.5
00205
00206 class CorrScatter(SquareScatter):
00207 """Scatterplot used to judge correlation between 2 variables.
00208
00209 Same as Scatter but with one.to.one=T and lty.x.y=2.
00210
00211 """
00212 default_kwargs={
00213 'one.to.one':True,
00214 'lty.x.y':2,
00215 }
00216
00217 class TimeSeries(Plot):
00218 """Simple cumulative time series.
00219
00220 Required:
00221 d: list of time data producted with strftime('%s')
00222
00223 Optional:
00224 y: values at time points. Will assume 1 for each as default.
00225 transform: how to transform the data before plotting, one of:
00226 'cumulative', 'monthly', 'daily'
00227
00228 """
00229 r_fun_name='generic.time.series'
00230 default_args_map={'xlab':'d','ylab':'y'}
00231
00232 class Histogram(Plot):
00233 """generic histogram for showing a univariate distribution.
00234
00235 Arguments passed verbatim to R base function hist.
00236
00237 """
00238 r_fun_name='hist'
00239
00240 class NormalQQPlot(Plot):
00241 """Use to see if univariate data are approximately normal.
00242
00243 All arguments are passed verbatim to R base function qqnorm.
00244
00245 """
00246 r_fun_name='generic.qqnorm'
00247
00248 class barplot(Plot):
00249 """Standard boring barplot.
00250
00251 """
00252
00253 class multi_time_series(Plot):
00254 """Multiple time series superimposed for comparison.
00255
00256 """