##// END OF EJS Templates
docstrings for class and __init__
Jonathan Taylor -
Show More
@@ -1,384 +1,405 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 ======
3 ======
4 Rmagic
4 Rmagic
5 ======
5 ======
6
6
7 Magic command interface for interactive work with R via rpy2
7 Magic command interface for interactive work with R via rpy2
8
8
9 Usage
9 Usage
10 =====
10 =====
11
11
12 ``%R``
12 ``%R``
13
13
14 {R_DOC}
14 {R_DOC}
15
15
16 ``%Rpush``
16 ``%Rpush``
17
17
18 {RPUSH_DOC}
18 {RPUSH_DOC}
19
19
20 ``%Rpull``
20 ``%Rpull``
21
21
22 {RPULL_DOC}
22 {RPULL_DOC}
23
23
24 """
24 """
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Copyright (C) 2012 The IPython Development Team
27 # Copyright (C) 2012 The IPython Development Team
28 #
28 #
29 # Distributed under the terms of the BSD License. The full license is in
29 # Distributed under the terms of the BSD License. The full license is in
30 # the file COPYING, distributed as part of this software.
30 # the file COPYING, distributed as part of this software.
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 import sys
33 import sys
34 import tempfile
34 import tempfile
35 from glob import glob
35 from glob import glob
36 from shutil import rmtree
36 from shutil import rmtree
37 from getopt import getopt
37 from getopt import getopt
38
38
39 # numpy and rpy2 imports
39 # numpy and rpy2 imports
40
40
41 import numpy as np
41 import numpy as np
42
42
43 import rpy2.rinterface as ri
43 import rpy2.rinterface as ri
44 import rpy2.robjects as ro
44 import rpy2.robjects as ro
45 from rpy2.robjects.numpy2ri import numpy2ri
45 from rpy2.robjects.numpy2ri import numpy2ri
46 ro.conversion.py2ri = numpy2ri
46 ro.conversion.py2ri = numpy2ri
47
47
48 # IPython imports
48 # IPython imports
49
49
50 from IPython.core.displaypub import publish_display_data
50 from IPython.core.displaypub import publish_display_data
51 from IPython.core.magic import (Magics, magics_class, cell_magic, line_magic,
51 from IPython.core.magic import (Magics, magics_class, cell_magic, line_magic,
52 line_cell_magic)
52 line_cell_magic)
53 from IPython.testing.skipdoctest import skip_doctest
53 from IPython.testing.skipdoctest import skip_doctest
54 from IPython.core.magic_arguments import (
54 from IPython.core.magic_arguments import (
55 argument, magic_arguments, parse_argstring
55 argument, magic_arguments, parse_argstring
56 )
56 )
57 from IPython.utils.py3compat import str_to_unicode
57 from IPython.utils.py3compat import str_to_unicode
58
58
59 class RMagicError(ValueError):
59 class RMagicError(ValueError):
60 pass
60 pass
61
61
62 @magics_class
62 @magics_class
63 class RMagics(Magics):
63 class RMagics(Magics):
64 """A set of magics useful for interactive work with R via rpy2.
65 """
64
66
65 def __init__(self, shell, Rconverter=np.asarray,
67 def __init__(self, shell, Rconverter=np.asarray,
66 pyconverter=np.asarray,
68 pyconverter=np.asarray,
67 cache_display_data=False):
69 cache_display_data=False):
70 """
71 Parameters
72 ----------
73
74 shell : IPython shell
75
76 Rconverter : callable
77 To be called on return values from R before returning
78 to ipython.
79
80 pyconverter : callable
81 To be called on values in ipython namespace before
82 assigning to variables in rpy2.
83
84 cache_display_data : bool
85 If True, the published results of the final call to R are
86 cached in the variable 'display_cache'.
87
88 """
68 super(RMagics, self).__init__(shell)
89 super(RMagics, self).__init__(shell)
69 self.cache_display_data = cache_display_data
90 self.cache_display_data = cache_display_data
70
91
71 self.r = ro.R()
92 self.r = ro.R()
72 self.Rstdout_cache = []
93 self.Rstdout_cache = []
73 self.Rconverter = Rconverter
94 self.Rconverter = Rconverter
74 self.pyconverter = pyconverter
95 self.pyconverter = pyconverter
75
96
76 def eval(self, line):
97 def eval(self, line):
77 '''
98 '''
78 Parse and evaluate a line with rpy2.
99 Parse and evaluate a line with rpy2.
79 Returns the output to R's stdout() connection
100 Returns the output to R's stdout() connection
80 and the value of eval(parse(line)).
101 and the value of eval(parse(line)).
81 '''
102 '''
82 old_writeconsole = ri.get_writeconsole()
103 old_writeconsole = ri.get_writeconsole()
83 ri.set_writeconsole(self.write_console)
104 ri.set_writeconsole(self.write_console)
84 try:
105 try:
85 value = ri.baseenv['eval'](ri.parse(line))
106 value = ri.baseenv['eval'](ri.parse(line))
86 except (ri.RRuntimeError, ValueError) as msg:
107 except (ri.RRuntimeError, ValueError) as msg:
87 raise RMagicError('error parsing and evaluating "%s": %s\n' %
108 raise RMagicError('error parsing and evaluating "%s": %s\n' %
88 (line, str_to_unicode(msg, 'utf-8')))
109 (line, str_to_unicode(msg, 'utf-8')))
89 text_output = self.flush()
110 text_output = self.flush()
90 ri.set_writeconsole(old_writeconsole)
111 ri.set_writeconsole(old_writeconsole)
91 return text_output, value
112 return text_output, value
92
113
93 def write_console(self, output):
114 def write_console(self, output):
94 '''
115 '''
95 A hook to capture R's stdout in a cache.
116 A hook to capture R's stdout in a cache.
96 '''
117 '''
97 self.Rstdout_cache.append(output)
118 self.Rstdout_cache.append(output)
98
119
99 def flush(self):
120 def flush(self):
100 '''
121 '''
101 Flush R's stdout cache to a string, returning the string.
122 Flush R's stdout cache to a string, returning the string.
102 '''
123 '''
103 value = ''.join([str_to_unicode(s, 'utf-8') for s in self.Rstdout_cache])
124 value = ''.join([str_to_unicode(s, 'utf-8') for s in self.Rstdout_cache])
104 self.Rstdout_cache = []
125 self.Rstdout_cache = []
105 return value
126 return value
106
127
107 @skip_doctest
128 @skip_doctest
108 @line_magic
129 @line_magic
109 def Rpush(self, line):
130 def Rpush(self, line):
110 '''
131 '''
111 A line-level magic for R that pushes
132 A line-level magic for R that pushes
112 variables from python to rpy2. The line should be made up
133 variables from python to rpy2. The line should be made up
113 of whitespace separated variable names in the IPython
134 of whitespace separated variable names in the IPython
114 namespace::
135 namespace::
115
136
116 In [7]: import numpy as np
137 In [7]: import numpy as np
117
138
118 In [8]: X = np.array([4.5,6.3,7.9])
139 In [8]: X = np.array([4.5,6.3,7.9])
119
140
120 In [9]: X.mean()
141 In [9]: X.mean()
121 Out[9]: 6.2333333333333343
142 Out[9]: 6.2333333333333343
122
143
123 In [10]: %Rpush X
144 In [10]: %Rpush X
124
145
125 In [11]: %R mean(X)
146 In [11]: %R mean(X)
126 Out[11]: array([ 6.23333333])
147 Out[11]: array([ 6.23333333])
127
148
128 '''
149 '''
129
150
130 inputs = line.split(' ')
151 inputs = line.split(' ')
131 for input in inputs:
152 for input in inputs:
132 self.r.assign(input, self.pyconverter(self.shell.user_ns[input]))
153 self.r.assign(input, self.pyconverter(self.shell.user_ns[input]))
133
154
134 @skip_doctest
155 @skip_doctest
135 @line_magic
156 @line_magic
136 def Rpull(self, line):
157 def Rpull(self, line):
137 '''
158 '''
138 A line-level magic for R that pulls
159 A line-level magic for R that pulls
139 variables from python to rpy2::
160 variables from python to rpy2::
140
161
141 In [18]: _ = %R x = c(3,4,6.7); y = c(4,6,7); z = c('a',3,4)
162 In [18]: _ = %R x = c(3,4,6.7); y = c(4,6,7); z = c('a',3,4)
142
163
143 In [19]: %Rpull x y z
164 In [19]: %Rpull x y z
144
165
145 In [20]: x
166 In [20]: x
146 Out[20]: array([ 3. , 4. , 6.7])
167 Out[20]: array([ 3. , 4. , 6.7])
147
168
148 In [21]: y
169 In [21]: y
149 Out[21]: array([ 4., 6., 7.])
170 Out[21]: array([ 4., 6., 7.])
150
171
151 In [22]: z
172 In [22]: z
152 Out[22]:
173 Out[22]:
153 array(['a', '3', '4'],
174 array(['a', '3', '4'],
154 dtype='|S1')
175 dtype='|S1')
155
176
156
177
157 Notes
178 Notes
158 -----
179 -----
159
180
160 Beware that R names can have '.' so this is not fool proof.
181 Beware that R names can have '.' so this is not fool proof.
161 To avoid this, don't name your R objects with '.'s...
182 To avoid this, don't name your R objects with '.'s...
162
183
163 '''
184 '''
164 outputs = line.split(' ')
185 outputs = line.split(' ')
165 for output in outputs:
186 for output in outputs:
166 self.shell.push({output:self.Rconverter(self.r(output))})
187 self.shell.push({output:self.Rconverter(self.r(output))})
167
188
168
189
169 @skip_doctest
190 @skip_doctest
170 @magic_arguments()
191 @magic_arguments()
171 @argument(
192 @argument(
172 '-i', '--input', action='append',
193 '-i', '--input', action='append',
173 help='Names of input variable from shell.user_ns to be assigned to R variables of the same names after calling self.pyconverter. Multiple names can be passed separated only by commas with no whitespace.'
194 help='Names of input variable from shell.user_ns to be assigned to R variables of the same names after calling self.pyconverter. Multiple names can be passed separated only by commas with no whitespace.'
174 )
195 )
175 @argument(
196 @argument(
176 '-o', '--output', action='append',
197 '-o', '--output', action='append',
177 help='Names of variables to be pushed from rpy2 to shell.user_ns after executing cell body and applying self.Rconverter. Multiple names can be passed separated only by commas with no whitespace.'
198 help='Names of variables to be pushed from rpy2 to shell.user_ns after executing cell body and applying self.Rconverter. Multiple names can be passed separated only by commas with no whitespace.'
178 )
199 )
179 @argument(
200 @argument(
180 '-w', '--width', type=int,
201 '-w', '--width', type=int,
181 help='Width of png plotting device sent as an argument to *png* in R.'
202 help='Width of png plotting device sent as an argument to *png* in R.'
182 )
203 )
183 @argument(
204 @argument(
184 '-h', '--height', type=int,
205 '-h', '--height', type=int,
185 help='Height of png plotting device sent as an argument to *png* in R.'
206 help='Height of png plotting device sent as an argument to *png* in R.'
186 )
207 )
187
208
188 @argument(
209 @argument(
189 '-u', '--units', type=int,
210 '-u', '--units', type=int,
190 help='Units of png plotting device sent as an argument to *png* in R. One of ["px", "in", "cm", "mm"].'
211 help='Units of png plotting device sent as an argument to *png* in R. One of ["px", "in", "cm", "mm"].'
191 )
212 )
192 @argument(
213 @argument(
193 '-p', '--pointsize', type=int,
214 '-p', '--pointsize', type=int,
194 help='Pointsize of png plotting device sent as an argument to *png* in R.'
215 help='Pointsize of png plotting device sent as an argument to *png* in R.'
195 )
216 )
196 @argument(
217 @argument(
197 '-b', '--bg',
218 '-b', '--bg',
198 help='Background of png plotting device sent as an argument to *png* in R.'
219 help='Background of png plotting device sent as an argument to *png* in R.'
199 )
220 )
200 @argument(
221 @argument(
201 '-n', '--noreturn',
222 '-n', '--noreturn',
202 help='Force the magic to not return anything.',
223 help='Force the magic to not return anything.',
203 action='store_true',
224 action='store_true',
204 default=False
225 default=False
205 )
226 )
206 @argument(
227 @argument(
207 'code',
228 'code',
208 nargs='*',
229 nargs='*',
209 )
230 )
210 @line_cell_magic
231 @line_cell_magic
211 def R(self, line, cell=None):
232 def R(self, line, cell=None):
212 '''
233 '''
213 Execute code in R, and pull some of the results back into the Python namespace.
234 Execute code in R, and pull some of the results back into the Python namespace.
214
235
215 In line mode, this will evaluate an expression and convert the returned value to a Python object.
236 In line mode, this will evaluate an expression and convert the returned value to a Python object.
216 The return value is determined by rpy2's behaviour of returning the result of evaluating the
237 The return value is determined by rpy2's behaviour of returning the result of evaluating the
217 final line. Multiple R lines can be executed by joining them with semicolons::
238 final line. Multiple R lines can be executed by joining them with semicolons::
218
239
219 In [9]: %R X=c(1,4,5,7); sd(X); mean(X)
240 In [9]: %R X=c(1,4,5,7); sd(X); mean(X)
220 Out[9]: array([ 4.25])
241 Out[9]: array([ 4.25])
221
242
222 As a cell, this will run a block of R code, without bringing anything back by default::
243 As a cell, this will run a block of R code, without bringing anything back by default::
223
244
224 In [10]: %%R
245 In [10]: %%R
225 ....: Y = c(2,4,3,9)
246 ....: Y = c(2,4,3,9)
226 ....: print(summary(lm(Y~X)))
247 ....: print(summary(lm(Y~X)))
227 ....:
248 ....:
228
249
229 Call:
250 Call:
230 lm(formula = Y ~ X)
251 lm(formula = Y ~ X)
231
252
232 Residuals:
253 Residuals:
233 1 2 3 4
254 1 2 3 4
234 0.88 -0.24 -2.28 1.64
255 0.88 -0.24 -2.28 1.64
235
256
236 Coefficients:
257 Coefficients:
237 Estimate Std. Error t value Pr(>|t|)
258 Estimate Std. Error t value Pr(>|t|)
238 (Intercept) 0.0800 2.3000 0.035 0.975
259 (Intercept) 0.0800 2.3000 0.035 0.975
239 X 1.0400 0.4822 2.157 0.164
260 X 1.0400 0.4822 2.157 0.164
240
261
241 Residual standard error: 2.088 on 2 degrees of freedom
262 Residual standard error: 2.088 on 2 degrees of freedom
242 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
263 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
243 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638
264 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638
244
265
245 In the notebook, plots are published as the output of the cell.
266 In the notebook, plots are published as the output of the cell.
246
267
247 %R plot(X, Y)
268 %R plot(X, Y)
248
269
249 will create a scatter plot of X bs Y.
270 will create a scatter plot of X bs Y.
250
271
251 If cell is not None and line has some R code, it is prepended to
272 If cell is not None and line has some R code, it is prepended to
252 the R code in cell.
273 the R code in cell.
253
274
254 Objects can be passed back and forth between rpy2 and python via the -i -o flags in line::
275 Objects can be passed back and forth between rpy2 and python via the -i -o flags in line::
255
276
256 In [14]: Z = np.array([1,4,5,10])
277 In [14]: Z = np.array([1,4,5,10])
257
278
258 In [15]: %R -i Z mean(Z)
279 In [15]: %R -i Z mean(Z)
259 Out[15]: array([ 5.])
280 Out[15]: array([ 5.])
260
281
261
282
262 In [16]: %R -o W W=Z*mean(Z)
283 In [16]: %R -o W W=Z*mean(Z)
263 Out[16]: array([ 5., 20., 25., 50.])
284 Out[16]: array([ 5., 20., 25., 50.])
264
285
265 In [17]: W
286 In [17]: W
266 Out[17]: array([ 5., 20., 25., 50.])
287 Out[17]: array([ 5., 20., 25., 50.])
267
288
268 The return value is determined by these rules:
289 The return value is determined by these rules:
269
290
270 * If the cell is not None, the magic returns None.
291 * If the cell is not None, the magic returns None.
271
292
272 * If the cell evaluates as False, the resulting value is returned
293 * If the cell evaluates as False, the resulting value is returned
273 unless the final line prints something to the console, in
294 unless the final line prints something to the console, in
274 which case None is returned.
295 which case None is returned.
275
296
276 * If the final line results in a NULL value when evaluated
297 * If the final line results in a NULL value when evaluated
277 by rpy2, then None is returned.
298 by rpy2, then None is returned.
278
299
279
300
280 '''
301 '''
281
302
282 args = parse_argstring(self.R, line)
303 args = parse_argstring(self.R, line)
283
304
284 # arguments 'code' in line are prepended to
305 # arguments 'code' in line are prepended to
285 # the cell lines
306 # the cell lines
286 if not cell:
307 if not cell:
287 code = ''
308 code = ''
288 return_output = True
309 return_output = True
289 line_mode = True
310 line_mode = True
290 else:
311 else:
291 code = cell
312 code = cell
292 return_output = False
313 return_output = False
293 line_mode = False
314 line_mode = False
294
315
295 code = ' '.join(args.code) + code
316 code = ' '.join(args.code) + code
296
317
297 if args.input:
318 if args.input:
298 for input in ','.join(args.input).split(','):
319 for input in ','.join(args.input).split(','):
299 self.r.assign(input, self.pyconverter(self.shell.user_ns[input]))
320 self.r.assign(input, self.pyconverter(self.shell.user_ns[input]))
300
321
301 png_argdict = dict([(n, getattr(args, n)) for n in ['units', 'height', 'width', 'bg', 'pointsize']])
322 png_argdict = dict([(n, getattr(args, n)) for n in ['units', 'height', 'width', 'bg', 'pointsize']])
302 png_args = ','.join(['%s=%s' % (o,v) for o, v in png_argdict.items() if v is not None])
323 png_args = ','.join(['%s=%s' % (o,v) for o, v in png_argdict.items() if v is not None])
303 # execute the R code in a temporary directory
324 # execute the R code in a temporary directory
304
325
305 tmpd = tempfile.mkdtemp()
326 tmpd = tempfile.mkdtemp()
306 self.r('png("%s/Rplots%%03d.png",%s)' % (tmpd, png_args))
327 self.r('png("%s/Rplots%%03d.png",%s)' % (tmpd, png_args))
307
328
308 text_output = ''
329 text_output = ''
309 if line_mode:
330 if line_mode:
310 for line in code.split(';'):
331 for line in code.split(';'):
311 text_result, result = self.eval(line)
332 text_result, result = self.eval(line)
312 text_output += text_result
333 text_output += text_result
313 if text_result:
334 if text_result:
314 # the last line printed something to the console so we won't return it
335 # the last line printed something to the console so we won't return it
315 return_output = False
336 return_output = False
316 else:
337 else:
317 text_result, result = self.eval(code)
338 text_result, result = self.eval(code)
318 text_output += text_result
339 text_output += text_result
319
340
320 self.r('dev.off()')
341 self.r('dev.off()')
321
342
322 # read out all the saved .png files
343 # read out all the saved .png files
323
344
324 images = [open(imgfile, 'rb').read() for imgfile in glob("%s/Rplots*png" % tmpd)]
345 images = [open(imgfile, 'rb').read() for imgfile in glob("%s/Rplots*png" % tmpd)]
325
346
326 # now publish the images
347 # now publish the images
327 # mimicking IPython/zmq/pylab/backend_inline.py
348 # mimicking IPython/zmq/pylab/backend_inline.py
328 fmt = 'png'
349 fmt = 'png'
329 mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
350 mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
330 mime = mimetypes[fmt]
351 mime = mimetypes[fmt]
331
352
332 # publish the printed R objects, if any
353 # publish the printed R objects, if any
333
354
334 display_data = []
355 display_data = []
335 if text_output:
356 if text_output:
336 display_data.append(('RMagic.R', {'text/plain':text_output}))
357 display_data.append(('RMagic.R', {'text/plain':text_output}))
337
358
338 # flush text streams before sending figures, helps a little with output
359 # flush text streams before sending figures, helps a little with output
339 for image in images:
360 for image in images:
340 # synchronization in the console (though it's a bandaid, not a real sln)
361 # synchronization in the console (though it's a bandaid, not a real sln)
341 sys.stdout.flush(); sys.stderr.flush()
362 sys.stdout.flush(); sys.stderr.flush()
342 display_data.append(('RMagic.R', {mime: image}))
363 display_data.append(('RMagic.R', {mime: image}))
343
364
344 # kill the temporary directory
365 # kill the temporary directory
345 rmtree(tmpd)
366 rmtree(tmpd)
346
367
347 # try to turn every output into a numpy array
368 # try to turn every output into a numpy array
348 # this means that output are assumed to be castable
369 # this means that output are assumed to be castable
349 # as numpy arrays
370 # as numpy arrays
350
371
351 if args.output:
372 if args.output:
352 for output in ','.join(args.output).split(','):
373 for output in ','.join(args.output).split(','):
353 # with self.shell, we assign the values to variables in the shell
374 # with self.shell, we assign the values to variables in the shell
354 self.shell.push({output:self.Rconverter(self.r(output))})
375 self.shell.push({output:self.Rconverter(self.r(output))})
355
376
356 for tag, disp_d in display_data:
377 for tag, disp_d in display_data:
357 publish_display_data(tag, disp_d)
378 publish_display_data(tag, disp_d)
358
379
359 # this will keep a reference to the display_data
380 # this will keep a reference to the display_data
360 # which might be useful to other objects who happen to use
381 # which might be useful to other objects who happen to use
361 # this method
382 # this method
362
383
363 if self.cache_display_data:
384 if self.cache_display_data:
364 self.display_cache = display_data
385 self.display_cache = display_data
365
386
366 # if in line mode and return_output, return the result as an ndarray
387 # if in line mode and return_output, return the result as an ndarray
367 if return_output and not args.noreturn:
388 if return_output and not args.noreturn:
368 if result != ri.NULL:
389 if result != ri.NULL:
369 return self.Rconverter(result)
390 return self.Rconverter(result)
370
391
371 __doc__ = __doc__.format(
392 __doc__ = __doc__.format(
372 R_DOC = ' '*8 + RMagics.R.__doc__,
393 R_DOC = ' '*8 + RMagics.R.__doc__,
373 RPUSH_DOC = ' '*8 + RMagics.Rpush.__doc__,
394 RPUSH_DOC = ' '*8 + RMagics.Rpush.__doc__,
374 RPULL_DOC = ' '*8 + RMagics.Rpull.__doc__
395 RPULL_DOC = ' '*8 + RMagics.Rpull.__doc__
375 )
396 )
376
397
377
398
378 _loaded = False
399 _loaded = False
379 def load_ipython_extension(ip):
400 def load_ipython_extension(ip):
380 """Load the extension in IPython."""
401 """Load the extension in IPython."""
381 global _loaded
402 global _loaded
382 if not _loaded:
403 if not _loaded:
383 ip.register_magics(RMagics)
404 ip.register_magics(RMagics)
384 _loaded = True
405 _loaded = True
General Comments 0
You need to be logged in to leave comments. Login now