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