##// END OF EJS Templates
made docstrings clearer and more explicit/correct for eval and R methods
gmbecker -
Show More
@@ -1,690 +1,691
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 ``%Rget``
24 ``%Rget``
25
25
26 {RGET_DOC}
26 {RGET_DOC}
27
27
28 """
28 """
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Copyright (C) 2012 The IPython Development Team
31 # Copyright (C) 2012 The IPython Development Team
32 #
32 #
33 # Distributed under the terms of the BSD License. The full license is in
33 # Distributed under the terms of the BSD License. The full license is in
34 # the file COPYING, distributed as part of this software.
34 # the file COPYING, distributed as part of this software.
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 import sys
37 import sys
38 import tempfile
38 import tempfile
39 from glob import glob
39 from glob import glob
40 from shutil import rmtree
40 from shutil import rmtree
41
41
42 # numpy and rpy2 imports
42 # numpy and rpy2 imports
43
43
44 import numpy as np
44 import numpy as np
45
45
46 import rpy2.rinterface as ri
46 import rpy2.rinterface as ri
47 import rpy2.robjects as ro
47 import rpy2.robjects as ro
48 try:
48 try:
49 from rpy2.robjects import pandas2ri
49 from rpy2.robjects import pandas2ri
50 pandas2ri.activate()
50 pandas2ri.activate()
51 except ImportError:
51 except ImportError:
52 pandas2ri = None
52 pandas2ri = None
53 from rpy2.robjects import numpy2ri
53 from rpy2.robjects import numpy2ri
54 numpy2ri.activate()
54 numpy2ri.activate()
55
55
56 # IPython imports
56 # IPython imports
57
57
58 from IPython.core.displaypub import publish_display_data
58 from IPython.core.displaypub import publish_display_data
59 from IPython.core.magic import (Magics, magics_class, line_magic,
59 from IPython.core.magic import (Magics, magics_class, line_magic,
60 line_cell_magic, needs_local_scope)
60 line_cell_magic, needs_local_scope)
61 from IPython.testing.skipdoctest import skip_doctest
61 from IPython.testing.skipdoctest import skip_doctest
62 from IPython.core.magic_arguments import (
62 from IPython.core.magic_arguments import (
63 argument, magic_arguments, parse_argstring
63 argument, magic_arguments, parse_argstring
64 )
64 )
65 from IPython.external.simplegeneric import generic
65 from IPython.external.simplegeneric import generic
66 from IPython.utils.py3compat import str_to_unicode, unicode_to_str, PY3
66 from IPython.utils.py3compat import str_to_unicode, unicode_to_str, PY3
67
67
68 class RInterpreterError(ri.RRuntimeError):
68 class RInterpreterError(ri.RRuntimeError):
69 """An error when running R code in a %%R magic cell."""
69 """An error when running R code in a %%R magic cell."""
70 def __init__(self, line, err, stdout):
70 def __init__(self, line, err, stdout):
71 self.line = line
71 self.line = line
72 self.err = err.rstrip()
72 self.err = err.rstrip()
73 self.stdout = stdout.rstrip()
73 self.stdout = stdout.rstrip()
74
74
75 def __unicode__(self):
75 def __unicode__(self):
76 s = 'Failed to parse and evaluate line %r.\nR error message: %r' % \
76 s = 'Failed to parse and evaluate line %r.\nR error message: %r' % \
77 (self.line, self.err)
77 (self.line, self.err)
78 if self.stdout and (self.stdout != self.err):
78 if self.stdout and (self.stdout != self.err):
79 s += '\nR stdout:\n' + self.stdout
79 s += '\nR stdout:\n' + self.stdout
80 return s
80 return s
81
81
82 if PY3:
82 if PY3:
83 __str__ = __unicode__
83 __str__ = __unicode__
84 else:
84 else:
85 def __str__(self):
85 def __str__(self):
86 return unicode_to_str(unicode(self), 'utf-8')
86 return unicode_to_str(unicode(self), 'utf-8')
87
87
88 def Rconverter(Robj, dataframe=False):
88 def Rconverter(Robj, dataframe=False):
89 """
89 """
90 Convert an object in R's namespace to one suitable
90 Convert an object in R's namespace to one suitable
91 for ipython's namespace.
91 for ipython's namespace.
92
92
93 For a data.frame, it tries to return a structured array.
93 For a data.frame, it tries to return a structured array.
94 It first checks for colnames, then names.
94 It first checks for colnames, then names.
95 If all are NULL, it returns np.asarray(Robj), else
95 If all are NULL, it returns np.asarray(Robj), else
96 it tries to construct a recarray
96 it tries to construct a recarray
97
97
98 Parameters
98 Parameters
99 ----------
99 ----------
100
100
101 Robj: an R object returned from rpy2
101 Robj: an R object returned from rpy2
102 """
102 """
103 is_data_frame = ro.r('is.data.frame')
103 is_data_frame = ro.r('is.data.frame')
104 colnames = ro.r('colnames')
104 colnames = ro.r('colnames')
105 rownames = ro.r('rownames') # with pandas, these could be used for the index
105 rownames = ro.r('rownames') # with pandas, these could be used for the index
106 names = ro.r('names')
106 names = ro.r('names')
107
107
108 if dataframe:
108 if dataframe:
109 as_data_frame = ro.r('as.data.frame')
109 as_data_frame = ro.r('as.data.frame')
110 cols = colnames(Robj)
110 cols = colnames(Robj)
111 _names = names(Robj)
111 _names = names(Robj)
112 if cols != ri.NULL:
112 if cols != ri.NULL:
113 Robj = as_data_frame(Robj)
113 Robj = as_data_frame(Robj)
114 names = tuple(np.array(cols))
114 names = tuple(np.array(cols))
115 elif _names != ri.NULL:
115 elif _names != ri.NULL:
116 names = tuple(np.array(_names))
116 names = tuple(np.array(_names))
117 else: # failed to find names
117 else: # failed to find names
118 return np.asarray(Robj)
118 return np.asarray(Robj)
119 Robj = np.rec.fromarrays(Robj, names = names)
119 Robj = np.rec.fromarrays(Robj, names = names)
120 return np.asarray(Robj)
120 return np.asarray(Robj)
121
121
122 @generic
122 @generic
123 def pyconverter(pyobj):
123 def pyconverter(pyobj):
124 """Convert Python objects to R objects. Add types using the decorator:
124 """Convert Python objects to R objects. Add types using the decorator:
125
125
126 @pyconverter.when_type
126 @pyconverter.when_type
127 """
127 """
128 return pyobj
128 return pyobj
129
129
130 # The default conversion for lists seems to make them a nested list. That has
130 # The default conversion for lists seems to make them a nested list. That has
131 # some advantages, but is rarely convenient, so for interactive use, we convert
131 # some advantages, but is rarely convenient, so for interactive use, we convert
132 # lists to a numpy array, which becomes an R vector.
132 # lists to a numpy array, which becomes an R vector.
133 @pyconverter.when_type(list)
133 @pyconverter.when_type(list)
134 def pyconverter_list(pyobj):
134 def pyconverter_list(pyobj):
135 return np.asarray(pyobj)
135 return np.asarray(pyobj)
136
136
137 if pandas2ri is None:
137 if pandas2ri is None:
138 # pandas2ri was new in rpy2 2.3.3, so for now we'll fallback to pandas'
138 # pandas2ri was new in rpy2 2.3.3, so for now we'll fallback to pandas'
139 # conversion function.
139 # conversion function.
140 try:
140 try:
141 from pandas import DataFrame
141 from pandas import DataFrame
142 from pandas.rpy.common import convert_to_r_dataframe
142 from pandas.rpy.common import convert_to_r_dataframe
143 @pyconverter.when_type(DataFrame)
143 @pyconverter.when_type(DataFrame)
144 def pyconverter_dataframe(pyobj):
144 def pyconverter_dataframe(pyobj):
145 return convert_to_r_dataframe(pyobj, strings_as_factors=True)
145 return convert_to_r_dataframe(pyobj, strings_as_factors=True)
146 except ImportError:
146 except ImportError:
147 pass
147 pass
148
148
149 @magics_class
149 @magics_class
150 class RMagics(Magics):
150 class RMagics(Magics):
151 """A set of magics useful for interactive work with R via rpy2.
151 """A set of magics useful for interactive work with R via rpy2.
152 """
152 """
153
153
154 def __init__(self, shell, Rconverter=Rconverter,
154 def __init__(self, shell, Rconverter=Rconverter,
155 pyconverter=pyconverter,
155 pyconverter=pyconverter,
156 cache_display_data=False):
156 cache_display_data=False):
157 """
157 """
158 Parameters
158 Parameters
159 ----------
159 ----------
160
160
161 shell : IPython shell
161 shell : IPython shell
162
162
163 Rconverter : callable
163 Rconverter : callable
164 To be called on values taken from R before putting them in the
164 To be called on values taken from R before putting them in the
165 IPython namespace.
165 IPython namespace.
166
166
167 pyconverter : callable
167 pyconverter : callable
168 To be called on values in ipython namespace before
168 To be called on values in ipython namespace before
169 assigning to variables in rpy2.
169 assigning to variables in rpy2.
170
170
171 cache_display_data : bool
171 cache_display_data : bool
172 If True, the published results of the final call to R are
172 If True, the published results of the final call to R are
173 cached in the variable 'display_cache'.
173 cached in the variable 'display_cache'.
174
174
175 """
175 """
176 super(RMagics, self).__init__(shell)
176 super(RMagics, self).__init__(shell)
177 self.cache_display_data = cache_display_data
177 self.cache_display_data = cache_display_data
178
178
179 self.r = ro.R()
179 self.r = ro.R()
180
180
181 self.Rstdout_cache = []
181 self.Rstdout_cache = []
182 self.pyconverter = pyconverter
182 self.pyconverter = pyconverter
183 self.Rconverter = Rconverter
183 self.Rconverter = Rconverter
184
184
185 def eval(self, line, line_mode):
185 def eval(self, line, line_mode):
186 '''
186 '''
187 Parse and evaluate a line with rpy2.
187 Parse and evaluate a line with rpy2.
188 Returns the output to R's stdout() connection, and
188 Returns the output to R's stdout() connection, and
189 the value generated by evaluating the code (see below)
189 the value generated by evaluating the code (see below)
190
190
191 In line mode (ie called with %R <stuff>), behavior
191 In line mode (ie called with %R <stuff>), resulting
192 is unchanged, returns a python object representing
192 values are not printed (explicit calls to the
193 the final value, suitable for assignment into a
193 show/print R functions still result in properly
194 python variable.
194 captured R output).
195
195
196 In cell mode (called with %%R ...), behavior
196 In cell mode (called with %%R ...), behavior
197 now reproduces the REPL behavior of the R
197 reproduces the REPL behavior of the R
198 interpreter (which agrees with how cells of
198 interpreter (which agrees with how cells of
199 python code are handled by the notebook).
199 python code are handled by the notebook).
200
200
201 In cell mode if the last line of code is not
201 In cell mode if the last line of code is not
202 an assignment, wrapped in invisible(), or
202 an assignment, wrapped in invisible(), or
203 a call to a function which returns its value
203 a call to a function which returns its value
204 invisibly, the value will be printed using the
204 invisibly, the value will be printed using the
205 show R function.
205 show R function.
206
206
207 Actual evaluation of R code is done via a call
207 Actual evaluation of R code is done via an R
208 of the form withVisible({<code>})
208 call of the form withVisible({<code>})
209
209
210 '''
210 '''
211 old_writeconsole = ri.get_writeconsole()
211 old_writeconsole = ri.get_writeconsole()
212 ri.set_writeconsole(self.write_console)
212 ri.set_writeconsole(self.write_console)
213 try:
213 try:
214 res = ro.r("withVisible({%s})" % line)
214 res = ro.r("withVisible({%s})" % line)
215 value = res[0] #value
215 value = res[0] #value
216 except (ri.RRuntimeError, ValueError) as exception:
216 except (ri.RRuntimeError, ValueError) as exception:
217 warning_or_other_msg = self.flush() # otherwise next return seems to have copy of error
217 warning_or_other_msg = self.flush() # otherwise next return seems to have copy of error
218 raise RInterpreterError(line, str_to_unicode(str(exception)), warning_or_other_msg)
218 raise RInterpreterError(line, str_to_unicode(str(exception)), warning_or_other_msg)
219 if not line_mode and ro.conversion.ri2py(res[1])[0]:
219 if not line_mode and ro.conversion.ri2py(res[1])[0]:
220 ro.r.show(value)
220 ro.r.show(value)
221 text_output = self.flush()
221 text_output = self.flush()
222 ri.set_writeconsole(old_writeconsole)
222 ri.set_writeconsole(old_writeconsole)
223 return text_output, value
223 return text_output, value
224
224
225 def write_console(self, output):
225 def write_console(self, output):
226 '''
226 '''
227 A hook to capture R's stdout in a cache.
227 A hook to capture R's stdout in a cache.
228 '''
228 '''
229 self.Rstdout_cache.append(output)
229 self.Rstdout_cache.append(output)
230
230
231 def flush(self):
231 def flush(self):
232 '''
232 '''
233 Flush R's stdout cache to a string, returning the string.
233 Flush R's stdout cache to a string, returning the string.
234 '''
234 '''
235 value = ''.join([str_to_unicode(s, 'utf-8') for s in self.Rstdout_cache])
235 value = ''.join([str_to_unicode(s, 'utf-8') for s in self.Rstdout_cache])
236 self.Rstdout_cache = []
236 self.Rstdout_cache = []
237 return value
237 return value
238
238
239 @skip_doctest
239 @skip_doctest
240 @needs_local_scope
240 @needs_local_scope
241 @line_magic
241 @line_magic
242 def Rpush(self, line, local_ns=None):
242 def Rpush(self, line, local_ns=None):
243 '''
243 '''
244 A line-level magic for R that pushes
244 A line-level magic for R that pushes
245 variables from python to rpy2. The line should be made up
245 variables from python to rpy2. The line should be made up
246 of whitespace separated variable names in the IPython
246 of whitespace separated variable names in the IPython
247 namespace::
247 namespace::
248
248
249 In [7]: import numpy as np
249 In [7]: import numpy as np
250
250
251 In [8]: X = np.array([4.5,6.3,7.9])
251 In [8]: X = np.array([4.5,6.3,7.9])
252
252
253 In [9]: X.mean()
253 In [9]: X.mean()
254 Out[9]: 6.2333333333333343
254 Out[9]: 6.2333333333333343
255
255
256 In [10]: %Rpush X
256 In [10]: %Rpush X
257
257
258 In [11]: %R mean(X)
258 In [11]: %R mean(X)
259 Out[11]: array([ 6.23333333])
259 Out[11]: array([ 6.23333333])
260
260
261 '''
261 '''
262 if local_ns is None:
262 if local_ns is None:
263 local_ns = {}
263 local_ns = {}
264
264
265 inputs = line.split(' ')
265 inputs = line.split(' ')
266 for input in inputs:
266 for input in inputs:
267 try:
267 try:
268 val = local_ns[input]
268 val = local_ns[input]
269 except KeyError:
269 except KeyError:
270 try:
270 try:
271 val = self.shell.user_ns[input]
271 val = self.shell.user_ns[input]
272 except KeyError:
272 except KeyError:
273 # reraise the KeyError as a NameError so that it looks like
273 # reraise the KeyError as a NameError so that it looks like
274 # the standard python behavior when you use an unnamed
274 # the standard python behavior when you use an unnamed
275 # variable
275 # variable
276 raise NameError("name '%s' is not defined" % input)
276 raise NameError("name '%s' is not defined" % input)
277
277
278 self.r.assign(input, self.pyconverter(val))
278 self.r.assign(input, self.pyconverter(val))
279
279
280 @skip_doctest
280 @skip_doctest
281 @magic_arguments()
281 @magic_arguments()
282 @argument(
282 @argument(
283 '-d', '--as_dataframe', action='store_true',
283 '-d', '--as_dataframe', action='store_true',
284 default=False,
284 default=False,
285 help='Convert objects to data.frames before returning to ipython.'
285 help='Convert objects to data.frames before returning to ipython.'
286 )
286 )
287 @argument(
287 @argument(
288 'outputs',
288 'outputs',
289 nargs='*',
289 nargs='*',
290 )
290 )
291 @line_magic
291 @line_magic
292 def Rpull(self, line):
292 def Rpull(self, line):
293 '''
293 '''
294 A line-level magic for R that pulls
294 A line-level magic for R that pulls
295 variables from python to rpy2::
295 variables from python to rpy2::
296
296
297 In [18]: _ = %R x = c(3,4,6.7); y = c(4,6,7); z = c('a',3,4)
297 In [18]: _ = %R x = c(3,4,6.7); y = c(4,6,7); z = c('a',3,4)
298
298
299 In [19]: %Rpull x y z
299 In [19]: %Rpull x y z
300
300
301 In [20]: x
301 In [20]: x
302 Out[20]: array([ 3. , 4. , 6.7])
302 Out[20]: array([ 3. , 4. , 6.7])
303
303
304 In [21]: y
304 In [21]: y
305 Out[21]: array([ 4., 6., 7.])
305 Out[21]: array([ 4., 6., 7.])
306
306
307 In [22]: z
307 In [22]: z
308 Out[22]:
308 Out[22]:
309 array(['a', '3', '4'],
309 array(['a', '3', '4'],
310 dtype='|S1')
310 dtype='|S1')
311
311
312
312
313 If --as_dataframe, then each object is returned as a structured array
313 If --as_dataframe, then each object is returned as a structured array
314 after first passed through "as.data.frame" in R before
314 after first passed through "as.data.frame" in R before
315 being calling self.Rconverter.
315 being calling self.Rconverter.
316 This is useful when a structured array is desired as output, or
316 This is useful when a structured array is desired as output, or
317 when the object in R has mixed data types.
317 when the object in R has mixed data types.
318 See the %%R docstring for more examples.
318 See the %%R docstring for more examples.
319
319
320 Notes
320 Notes
321 -----
321 -----
322
322
323 Beware that R names can have '.' so this is not fool proof.
323 Beware that R names can have '.' so this is not fool proof.
324 To avoid this, don't name your R objects with '.'s...
324 To avoid this, don't name your R objects with '.'s...
325
325
326 '''
326 '''
327 args = parse_argstring(self.Rpull, line)
327 args = parse_argstring(self.Rpull, line)
328 outputs = args.outputs
328 outputs = args.outputs
329 for output in outputs:
329 for output in outputs:
330 self.shell.push({output:self.Rconverter(self.r(output),dataframe=args.as_dataframe)})
330 self.shell.push({output:self.Rconverter(self.r(output),dataframe=args.as_dataframe)})
331
331
332 @skip_doctest
332 @skip_doctest
333 @magic_arguments()
333 @magic_arguments()
334 @argument(
334 @argument(
335 '-d', '--as_dataframe', action='store_true',
335 '-d', '--as_dataframe', action='store_true',
336 default=False,
336 default=False,
337 help='Convert objects to data.frames before returning to ipython.'
337 help='Convert objects to data.frames before returning to ipython.'
338 )
338 )
339 @argument(
339 @argument(
340 'output',
340 'output',
341 nargs=1,
341 nargs=1,
342 type=str,
342 type=str,
343 )
343 )
344 @line_magic
344 @line_magic
345 def Rget(self, line):
345 def Rget(self, line):
346 '''
346 '''
347 Return an object from rpy2, possibly as a structured array (if possible).
347 Return an object from rpy2, possibly as a structured array (if possible).
348 Similar to Rpull except only one argument is accepted and the value is
348 Similar to Rpull except only one argument is accepted and the value is
349 returned rather than pushed to self.shell.user_ns::
349 returned rather than pushed to self.shell.user_ns::
350
350
351 In [3]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
351 In [3]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
352
352
353 In [4]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
353 In [4]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
354
354
355 In [5]: %R -i datapy
355 In [5]: %R -i datapy
356
356
357 In [6]: %Rget datapy
357 In [6]: %Rget datapy
358 Out[6]:
358 Out[6]:
359 array([['1', '2', '3', '4'],
359 array([['1', '2', '3', '4'],
360 ['2', '3', '2', '5'],
360 ['2', '3', '2', '5'],
361 ['a', 'b', 'c', 'e']],
361 ['a', 'b', 'c', 'e']],
362 dtype='|S1')
362 dtype='|S1')
363
363
364 In [7]: %Rget -d datapy
364 In [7]: %Rget -d datapy
365 Out[7]:
365 Out[7]:
366 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
366 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
367 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
367 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
368
368
369 '''
369 '''
370 args = parse_argstring(self.Rget, line)
370 args = parse_argstring(self.Rget, line)
371 output = args.output
371 output = args.output
372 return self.Rconverter(self.r(output[0]),dataframe=args.as_dataframe)
372 return self.Rconverter(self.r(output[0]),dataframe=args.as_dataframe)
373
373
374
374
375 @skip_doctest
375 @skip_doctest
376 @magic_arguments()
376 @magic_arguments()
377 @argument(
377 @argument(
378 '-i', '--input', action='append',
378 '-i', '--input', action='append',
379 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.'
379 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.'
380 )
380 )
381 @argument(
381 @argument(
382 '-o', '--output', action='append',
382 '-o', '--output', action='append',
383 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.'
383 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.'
384 )
384 )
385 @argument(
385 @argument(
386 '-w', '--width', type=int,
386 '-w', '--width', type=int,
387 help='Width of png plotting device sent as an argument to *png* in R.'
387 help='Width of png plotting device sent as an argument to *png* in R.'
388 )
388 )
389 @argument(
389 @argument(
390 '-h', '--height', type=int,
390 '-h', '--height', type=int,
391 help='Height of png plotting device sent as an argument to *png* in R.'
391 help='Height of png plotting device sent as an argument to *png* in R.'
392 )
392 )
393
393
394 @argument(
394 @argument(
395 '-d', '--dataframe', action='append',
395 '-d', '--dataframe', action='append',
396 help='Convert these objects to data.frames and return as structured arrays.'
396 help='Convert these objects to data.frames and return as structured arrays.'
397 )
397 )
398 @argument(
398 @argument(
399 '-u', '--units', type=unicode, choices=["px", "in", "cm", "mm"],
399 '-u', '--units', type=unicode, choices=["px", "in", "cm", "mm"],
400 help='Units of png plotting device sent as an argument to *png* in R. One of ["px", "in", "cm", "mm"].'
400 help='Units of png plotting device sent as an argument to *png* in R. One of ["px", "in", "cm", "mm"].'
401 )
401 )
402 @argument(
402 @argument(
403 '-r', '--res', type=int,
403 '-r', '--res', type=int,
404 help='Resolution of png plotting device sent as an argument to *png* in R. Defaults to 72 if *units* is one of ["in", "cm", "mm"].'
404 help='Resolution of png plotting device sent as an argument to *png* in R. Defaults to 72 if *units* is one of ["in", "cm", "mm"].'
405 )
405 )
406 @argument(
406 @argument(
407 '-p', '--pointsize', type=int,
407 '-p', '--pointsize', type=int,
408 help='Pointsize of png plotting device sent as an argument to *png* in R.'
408 help='Pointsize of png plotting device sent as an argument to *png* in R.'
409 )
409 )
410 @argument(
410 @argument(
411 '-b', '--bg',
411 '-b', '--bg',
412 help='Background of png plotting device sent as an argument to *png* in R.'
412 help='Background of png plotting device sent as an argument to *png* in R.'
413 )
413 )
414 @argument(
414 @argument(
415 '-n', '--noreturn',
415 '-n', '--noreturn',
416 help='Force the magic to not return anything.',
416 help='Force the magic to not return anything.',
417 action='store_true',
417 action='store_true',
418 default=False
418 default=False
419 )
419 )
420 @argument(
420 @argument(
421 'code',
421 'code',
422 nargs='*',
422 nargs='*',
423 )
423 )
424 @needs_local_scope
424 @needs_local_scope
425 @line_cell_magic
425 @line_cell_magic
426 def R(self, line, cell=None, local_ns=None):
426 def R(self, line, cell=None, local_ns=None):
427 '''
427 '''
428 Execute code in R, and pull some of the results back into the Python namespace.
428 Execute code in R, and pull some of the results back into the Python namespace.
429
429
430 In line mode, this will evaluate an expression and convert the returned value to a Python object.
430 In line mode, this will evaluate an expression and convert the returned value to a Python object.
431 The return value is determined by rpy2's behaviour of returning the result of evaluating the
431 The return value is determined by rpy2's behaviour of returning the result of evaluating the
432 final line.
432 final line.
433
433
434 Multiple R lines can be executed by joining them with semicolons::
434 Multiple R lines can be executed by joining them with semicolons::
435
435
436 In [9]: %R X=c(1,4,5,7); sd(X); mean(X)
436 In [9]: %R X=c(1,4,5,7); sd(X); mean(X)
437 Out[9]: array([ 4.25])
437 Out[9]: array([ 4.25])
438
438
439 As a cell, this will run a block of R code, without bringing anything back by default::
439 As a cell, this will run a block of R code. By default the resulting value
440 is printed if it would be when evaluating the same code within an R REPL.
441 Nothing is returned to python by default.
440
442
441 In [10]: %%R
443 In [10]: %%R
442 ....: Y = c(2,4,3,9)
444 ....: Y = c(2,4,3,9)
443 ....: print(summary(lm(Y~X)))
445 ....: summary(lm(Y~X))
444 ....:
445
446
446 Call:
447 Call:
447 lm(formula = Y ~ X)
448 lm(formula = Y ~ X)
448
449
449 Residuals:
450 Residuals:
450 1 2 3 4
451 1 2 3 4
451 0.88 -0.24 -2.28 1.64
452 0.88 -0.24 -2.28 1.64
452
453
453 Coefficients:
454 Coefficients:
454 Estimate Std. Error t value Pr(>|t|)
455 Estimate Std. Error t value Pr(>|t|)
455 (Intercept) 0.0800 2.3000 0.035 0.975
456 (Intercept) 0.0800 2.3000 0.035 0.975
456 X 1.0400 0.4822 2.157 0.164
457 X 1.0400 0.4822 2.157 0.164
457
458
458 Residual standard error: 2.088 on 2 degrees of freedom
459 Residual standard error: 2.088 on 2 degrees of freedom
459 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
460 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
460 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638
461 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638
461
462
462 In the notebook, plots are published as the output of the cell.
463 In the notebook, plots are published as the output of the cell.
463
464
464 %R plot(X, Y)
465 %R plot(X, Y)
465
466
466 will create a scatter plot of X bs Y.
467 will create a scatter plot of X bs Y.
467
468
468 If cell is not None and line has some R code, it is prepended to
469 If cell is not None and line has some R code, it is prepended to
469 the R code in cell.
470 the R code in cell.
470
471
471 Objects can be passed back and forth between rpy2 and python via the -i -o flags in line::
472 Objects can be passed back and forth between rpy2 and python via the -i -o flags in line::
472
473
473 In [14]: Z = np.array([1,4,5,10])
474 In [14]: Z = np.array([1,4,5,10])
474
475
475 In [15]: %R -i Z mean(Z)
476 In [15]: %R -i Z mean(Z)
476 Out[15]: array([ 5.])
477 Out[15]: array([ 5.])
477
478
478
479
479 In [16]: %R -o W W=Z*mean(Z)
480 In [16]: %R -o W W=Z*mean(Z)
480 Out[16]: array([ 5., 20., 25., 50.])
481 Out[16]: array([ 5., 20., 25., 50.])
481
482
482 In [17]: W
483 In [17]: W
483 Out[17]: array([ 5., 20., 25., 50.])
484 Out[17]: array([ 5., 20., 25., 50.])
484
485
485 The return value is determined by these rules:
486 The return value is determined by these rules:
486
487
487 * If the cell is not None, the magic returns None.
488 * If the cell is not None, the magic returns None.
488
489
489 * If the cell evaluates as False, the resulting value is returned
490 * If the cell evaluates as False, the resulting value is returned
490 unless the final line prints something to the console, in
491 unless the final line prints something to the console, in
491 which case None is returned.
492 which case None is returned.
492
493
493 * If the final line results in a NULL value when evaluated
494 * If the final line results in a NULL value when evaluated
494 by rpy2, then None is returned.
495 by rpy2, then None is returned.
495
496
496 * No attempt is made to convert the final value to a structured array.
497 * No attempt is made to convert the final value to a structured array.
497 Use the --dataframe flag or %Rget to push / return a structured array.
498 Use the --dataframe flag or %Rget to push / return a structured array.
498
499
499 * If the -n flag is present, there is no return value.
500 * If the -n flag is present, there is no return value.
500
501
501 * A trailing ';' will also result in no return value as the last
502 * A trailing ';' will also result in no return value as the last
502 value in the line is an empty string.
503 value in the line is an empty string.
503
504
504 The --dataframe argument will attempt to return structured arrays.
505 The --dataframe argument will attempt to return structured arrays.
505 This is useful for dataframes with
506 This is useful for dataframes with
506 mixed data types. Note also that for a data.frame,
507 mixed data types. Note also that for a data.frame,
507 if it is returned as an ndarray, it is transposed::
508 if it is returned as an ndarray, it is transposed::
508
509
509 In [18]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
510 In [18]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
510
511
511 In [19]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
512 In [19]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
512
513
513 In [20]: %%R -o datar
514 In [20]: %%R -o datar
514 datar = datapy
515 datar = datapy
515 ....:
516 ....:
516
517
517 In [21]: datar
518 In [21]: datar
518 Out[21]:
519 Out[21]:
519 array([['1', '2', '3', '4'],
520 array([['1', '2', '3', '4'],
520 ['2', '3', '2', '5'],
521 ['2', '3', '2', '5'],
521 ['a', 'b', 'c', 'e']],
522 ['a', 'b', 'c', 'e']],
522 dtype='|S1')
523 dtype='|S1')
523
524
524 In [22]: %%R -d datar
525 In [22]: %%R -d datar
525 datar = datapy
526 datar = datapy
526 ....:
527 ....:
527
528
528 In [23]: datar
529 In [23]: datar
529 Out[23]:
530 Out[23]:
530 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
531 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
531 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
532 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
532
533
533 The --dataframe argument first tries colnames, then names.
534 The --dataframe argument first tries colnames, then names.
534 If both are NULL, it returns an ndarray (i.e. unstructured)::
535 If both are NULL, it returns an ndarray (i.e. unstructured)::
535
536
536 In [1]: %R mydata=c(4,6,8.3); NULL
537 In [1]: %R mydata=c(4,6,8.3); NULL
537
538
538 In [2]: %R -d mydata
539 In [2]: %R -d mydata
539
540
540 In [3]: mydata
541 In [3]: mydata
541 Out[3]: array([ 4. , 6. , 8.3])
542 Out[3]: array([ 4. , 6. , 8.3])
542
543
543 In [4]: %R names(mydata) = c('a','b','c'); NULL
544 In [4]: %R names(mydata) = c('a','b','c'); NULL
544
545
545 In [5]: %R -d mydata
546 In [5]: %R -d mydata
546
547
547 In [6]: mydata
548 In [6]: mydata
548 Out[6]:
549 Out[6]:
549 array((4.0, 6.0, 8.3),
550 array((4.0, 6.0, 8.3),
550 dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])
551 dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])
551
552
552 In [7]: %R -o mydata
553 In [7]: %R -o mydata
553
554
554 In [8]: mydata
555 In [8]: mydata
555 Out[8]: array([ 4. , 6. , 8.3])
556 Out[8]: array([ 4. , 6. , 8.3])
556
557
557 '''
558 '''
558
559
559 args = parse_argstring(self.R, line)
560 args = parse_argstring(self.R, line)
560
561
561 # arguments 'code' in line are prepended to
562 # arguments 'code' in line are prepended to
562 # the cell lines
563 # the cell lines
563
564
564 if cell is None:
565 if cell is None:
565 code = ''
566 code = ''
566 return_output = True
567 return_output = True
567 line_mode = True
568 line_mode = True
568 else:
569 else:
569 code = cell
570 code = cell
570 return_output = False
571 return_output = False
571 line_mode = False
572 line_mode = False
572
573
573 code = ' '.join(args.code) + code
574 code = ' '.join(args.code) + code
574
575
575 # if there is no local namespace then default to an empty dict
576 # if there is no local namespace then default to an empty dict
576 if local_ns is None:
577 if local_ns is None:
577 local_ns = {}
578 local_ns = {}
578
579
579 if args.input:
580 if args.input:
580 for input in ','.join(args.input).split(','):
581 for input in ','.join(args.input).split(','):
581 try:
582 try:
582 val = local_ns[input]
583 val = local_ns[input]
583 except KeyError:
584 except KeyError:
584 try:
585 try:
585 val = self.shell.user_ns[input]
586 val = self.shell.user_ns[input]
586 except KeyError:
587 except KeyError:
587 raise NameError("name '%s' is not defined" % input)
588 raise NameError("name '%s' is not defined" % input)
588 self.r.assign(input, self.pyconverter(val))
589 self.r.assign(input, self.pyconverter(val))
589
590
590 if getattr(args, 'units') is not None:
591 if getattr(args, 'units') is not None:
591 if args.units != "px" and getattr(args, 'res') is None:
592 if args.units != "px" and getattr(args, 'res') is None:
592 args.res = 72
593 args.res = 72
593 args.units = '"%s"' % args.units
594 args.units = '"%s"' % args.units
594
595
595 png_argdict = dict([(n, getattr(args, n)) for n in ['units', 'res', 'height', 'width', 'bg', 'pointsize']])
596 png_argdict = dict([(n, getattr(args, n)) for n in ['units', 'res', 'height', 'width', 'bg', 'pointsize']])
596 png_args = ','.join(['%s=%s' % (o,v) for o, v in png_argdict.items() if v is not None])
597 png_args = ','.join(['%s=%s' % (o,v) for o, v in png_argdict.items() if v is not None])
597 # execute the R code in a temporary directory
598 # execute the R code in a temporary directory
598
599
599 tmpd = tempfile.mkdtemp()
600 tmpd = tempfile.mkdtemp()
600 self.r('png("%s/Rplots%%03d.png",%s)' % (tmpd.replace('\\', '/'), png_args))
601 self.r('png("%s/Rplots%%03d.png",%s)' % (tmpd.replace('\\', '/'), png_args))
601
602
602 text_output = ''
603 text_output = ''
603 try:
604 try:
604 if line_mode:
605 if line_mode:
605 for line in code.split(';'):
606 for line in code.split(';'):
606 text_result, result = self.eval(line, line_mode)
607 text_result, result = self.eval(line, line_mode)
607 text_output += text_result
608 text_output += text_result
608 if text_result:
609 if text_result:
609 # the last line printed something to the console so we won't return it
610 # the last line printed something to the console so we won't return it
610 return_output = False
611 return_output = False
611 else:
612 else:
612 text_result, result = self.eval(code, line_mode)
613 text_result, result = self.eval(code, line_mode)
613 text_output += text_result
614 text_output += text_result
614
615
615 except RInterpreterError as e:
616 except RInterpreterError as e:
616 print(e.stdout)
617 print(e.stdout)
617 if not e.stdout.endswith(e.err):
618 if not e.stdout.endswith(e.err):
618 print(e.err)
619 print(e.err)
619 rmtree(tmpd)
620 rmtree(tmpd)
620 return
621 return
621
622
622 self.r('dev.off()')
623 self.r('dev.off()')
623
624
624 # read out all the saved .png files
625 # read out all the saved .png files
625
626
626 images = [open(imgfile, 'rb').read() for imgfile in glob("%s/Rplots*png" % tmpd)]
627 images = [open(imgfile, 'rb').read() for imgfile in glob("%s/Rplots*png" % tmpd)]
627
628
628 # now publish the images
629 # now publish the images
629 # mimicking IPython/zmq/pylab/backend_inline.py
630 # mimicking IPython/zmq/pylab/backend_inline.py
630 fmt = 'png'
631 fmt = 'png'
631 mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
632 mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
632 mime = mimetypes[fmt]
633 mime = mimetypes[fmt]
633
634
634 # publish the printed R objects, if any
635 # publish the printed R objects, if any
635
636
636 display_data = []
637 display_data = []
637 if text_output:
638 if text_output:
638 display_data.append(('RMagic.R', {'text/plain':text_output}))
639 display_data.append(('RMagic.R', {'text/plain':text_output}))
639
640
640 # flush text streams before sending figures, helps a little with output
641 # flush text streams before sending figures, helps a little with output
641 for image in images:
642 for image in images:
642 # synchronization in the console (though it's a bandaid, not a real sln)
643 # synchronization in the console (though it's a bandaid, not a real sln)
643 sys.stdout.flush(); sys.stderr.flush()
644 sys.stdout.flush(); sys.stderr.flush()
644 display_data.append(('RMagic.R', {mime: image}))
645 display_data.append(('RMagic.R', {mime: image}))
645
646
646 # kill the temporary directory
647 # kill the temporary directory
647 rmtree(tmpd)
648 rmtree(tmpd)
648
649
649 # try to turn every output into a numpy array
650 # try to turn every output into a numpy array
650 # this means that output are assumed to be castable
651 # this means that output are assumed to be castable
651 # as numpy arrays
652 # as numpy arrays
652
653
653 if args.output:
654 if args.output:
654 for output in ','.join(args.output).split(','):
655 for output in ','.join(args.output).split(','):
655 self.shell.push({output:self.Rconverter(self.r(output), dataframe=False)})
656 self.shell.push({output:self.Rconverter(self.r(output), dataframe=False)})
656
657
657 if args.dataframe:
658 if args.dataframe:
658 for output in ','.join(args.dataframe).split(','):
659 for output in ','.join(args.dataframe).split(','):
659 self.shell.push({output:self.Rconverter(self.r(output), dataframe=True)})
660 self.shell.push({output:self.Rconverter(self.r(output), dataframe=True)})
660
661
661 for tag, disp_d in display_data:
662 for tag, disp_d in display_data:
662 publish_display_data(tag, disp_d)
663 publish_display_data(tag, disp_d)
663
664
664 # this will keep a reference to the display_data
665 # this will keep a reference to the display_data
665 # which might be useful to other objects who happen to use
666 # which might be useful to other objects who happen to use
666 # this method
667 # this method
667
668
668 if self.cache_display_data:
669 if self.cache_display_data:
669 self.display_cache = display_data
670 self.display_cache = display_data
670
671
671 # if in line mode and return_output, return the result as an ndarray
672 # if in line mode and return_output, return the result as an ndarray
672 if return_output and not args.noreturn:
673 if return_output and not args.noreturn:
673 if result != ri.NULL:
674 if result != ri.NULL:
674 return self.Rconverter(result, dataframe=False)
675 return self.Rconverter(result, dataframe=False)
675
676
676 __doc__ = __doc__.format(
677 __doc__ = __doc__.format(
677 R_DOC = ' '*8 + RMagics.R.__doc__,
678 R_DOC = ' '*8 + RMagics.R.__doc__,
678 RPUSH_DOC = ' '*8 + RMagics.Rpush.__doc__,
679 RPUSH_DOC = ' '*8 + RMagics.Rpush.__doc__,
679 RPULL_DOC = ' '*8 + RMagics.Rpull.__doc__,
680 RPULL_DOC = ' '*8 + RMagics.Rpull.__doc__,
680 RGET_DOC = ' '*8 + RMagics.Rget.__doc__
681 RGET_DOC = ' '*8 + RMagics.Rget.__doc__
681 )
682 )
682
683
683
684
684 def load_ipython_extension(ip):
685 def load_ipython_extension(ip):
685 """Load the extension in IPython."""
686 """Load the extension in IPython."""
686 ip.register_magics(RMagics)
687 ip.register_magics(RMagics)
687 # Initialising rpy2 interferes with readline. Since, at this point, we've
688 # Initialising rpy2 interferes with readline. Since, at this point, we've
688 # probably just loaded rpy2, we reset the delimiters. See issue gh-2759.
689 # probably just loaded rpy2, we reset the delimiters. See issue gh-2759.
689 if ip.has_readline:
690 if ip.has_readline:
690 ip.readline.set_completer_delims(ip.readline_delims)
691 ip.readline.set_completer_delims(ip.readline_delims)
General Comments 0
You need to be logged in to leave comments. Login now