##// END OF EJS Templates
Merge pull request #4017 from gmbecker/PR_rmagic2...
Thomas Kluyver -
r12183:681735e3 merge
parent child Browse files
Show More
@@ -1,667 +1,684 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
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):
185 def eval(self, line):
186 '''
186 '''
187 Parse and evaluate a line with rpy2.
187 Parse and evaluate a line of R code with rpy2.
188 Returns the output to R's stdout() connection
188 Returns the output to R's stdout() connection,
189 and the value of eval(parse(line)).
189 the value generated by evaluating the code, and a
190 boolean indicating whether the return value would be
191 visible if the line of code were evaluated in an R REPL.
192
193 R Code evaluation and visibility determination are
194 done via an R call of the form withVisible({<code>})
195
190 '''
196 '''
191 old_writeconsole = ri.get_writeconsole()
197 old_writeconsole = ri.get_writeconsole()
192 ri.set_writeconsole(self.write_console)
198 ri.set_writeconsole(self.write_console)
193 try:
199 try:
194 value = ri.baseenv['eval'](ri.parse(line))
200 res = ro.r("withVisible({%s})" % line)
201 value = res[0] #value (R object)
202 visible = ro.conversion.ri2py(res[1])[0] #visible (boolean)
195 except (ri.RRuntimeError, ValueError) as exception:
203 except (ri.RRuntimeError, ValueError) as exception:
196 warning_or_other_msg = self.flush() # otherwise next return seems to have copy of error
204 warning_or_other_msg = self.flush() # otherwise next return seems to have copy of error
197 raise RInterpreterError(line, str_to_unicode(str(exception)), warning_or_other_msg)
205 raise RInterpreterError(line, str_to_unicode(str(exception)), warning_or_other_msg)
198 text_output = self.flush()
206 text_output = self.flush()
199 ri.set_writeconsole(old_writeconsole)
207 ri.set_writeconsole(old_writeconsole)
200 return text_output, value
208 return text_output, value, visible
201
209
202 def write_console(self, output):
210 def write_console(self, output):
203 '''
211 '''
204 A hook to capture R's stdout in a cache.
212 A hook to capture R's stdout in a cache.
205 '''
213 '''
206 self.Rstdout_cache.append(output)
214 self.Rstdout_cache.append(output)
207
215
208 def flush(self):
216 def flush(self):
209 '''
217 '''
210 Flush R's stdout cache to a string, returning the string.
218 Flush R's stdout cache to a string, returning the string.
211 '''
219 '''
212 value = ''.join([str_to_unicode(s, 'utf-8') for s in self.Rstdout_cache])
220 value = ''.join([str_to_unicode(s, 'utf-8') for s in self.Rstdout_cache])
213 self.Rstdout_cache = []
221 self.Rstdout_cache = []
214 return value
222 return value
215
223
216 @skip_doctest
224 @skip_doctest
217 @needs_local_scope
225 @needs_local_scope
218 @line_magic
226 @line_magic
219 def Rpush(self, line, local_ns=None):
227 def Rpush(self, line, local_ns=None):
220 '''
228 '''
221 A line-level magic for R that pushes
229 A line-level magic for R that pushes
222 variables from python to rpy2. The line should be made up
230 variables from python to rpy2. The line should be made up
223 of whitespace separated variable names in the IPython
231 of whitespace separated variable names in the IPython
224 namespace::
232 namespace::
225
233
226 In [7]: import numpy as np
234 In [7]: import numpy as np
227
235
228 In [8]: X = np.array([4.5,6.3,7.9])
236 In [8]: X = np.array([4.5,6.3,7.9])
229
237
230 In [9]: X.mean()
238 In [9]: X.mean()
231 Out[9]: 6.2333333333333343
239 Out[9]: 6.2333333333333343
232
240
233 In [10]: %Rpush X
241 In [10]: %Rpush X
234
242
235 In [11]: %R mean(X)
243 In [11]: %R mean(X)
236 Out[11]: array([ 6.23333333])
244 Out[11]: array([ 6.23333333])
237
245
238 '''
246 '''
239 if local_ns is None:
247 if local_ns is None:
240 local_ns = {}
248 local_ns = {}
241
249
242 inputs = line.split(' ')
250 inputs = line.split(' ')
243 for input in inputs:
251 for input in inputs:
244 try:
252 try:
245 val = local_ns[input]
253 val = local_ns[input]
246 except KeyError:
254 except KeyError:
247 try:
255 try:
248 val = self.shell.user_ns[input]
256 val = self.shell.user_ns[input]
249 except KeyError:
257 except KeyError:
250 # reraise the KeyError as a NameError so that it looks like
258 # reraise the KeyError as a NameError so that it looks like
251 # the standard python behavior when you use an unnamed
259 # the standard python behavior when you use an unnamed
252 # variable
260 # variable
253 raise NameError("name '%s' is not defined" % input)
261 raise NameError("name '%s' is not defined" % input)
254
262
255 self.r.assign(input, self.pyconverter(val))
263 self.r.assign(input, self.pyconverter(val))
256
264
257 @skip_doctest
265 @skip_doctest
258 @magic_arguments()
266 @magic_arguments()
259 @argument(
267 @argument(
260 '-d', '--as_dataframe', action='store_true',
268 '-d', '--as_dataframe', action='store_true',
261 default=False,
269 default=False,
262 help='Convert objects to data.frames before returning to ipython.'
270 help='Convert objects to data.frames before returning to ipython.'
263 )
271 )
264 @argument(
272 @argument(
265 'outputs',
273 'outputs',
266 nargs='*',
274 nargs='*',
267 )
275 )
268 @line_magic
276 @line_magic
269 def Rpull(self, line):
277 def Rpull(self, line):
270 '''
278 '''
271 A line-level magic for R that pulls
279 A line-level magic for R that pulls
272 variables from python to rpy2::
280 variables from python to rpy2::
273
281
274 In [18]: _ = %R x = c(3,4,6.7); y = c(4,6,7); z = c('a',3,4)
282 In [18]: _ = %R x = c(3,4,6.7); y = c(4,6,7); z = c('a',3,4)
275
283
276 In [19]: %Rpull x y z
284 In [19]: %Rpull x y z
277
285
278 In [20]: x
286 In [20]: x
279 Out[20]: array([ 3. , 4. , 6.7])
287 Out[20]: array([ 3. , 4. , 6.7])
280
288
281 In [21]: y
289 In [21]: y
282 Out[21]: array([ 4., 6., 7.])
290 Out[21]: array([ 4., 6., 7.])
283
291
284 In [22]: z
292 In [22]: z
285 Out[22]:
293 Out[22]:
286 array(['a', '3', '4'],
294 array(['a', '3', '4'],
287 dtype='|S1')
295 dtype='|S1')
288
296
289
297
290 If --as_dataframe, then each object is returned as a structured array
298 If --as_dataframe, then each object is returned as a structured array
291 after first passed through "as.data.frame" in R before
299 after first passed through "as.data.frame" in R before
292 being calling self.Rconverter.
300 being calling self.Rconverter.
293 This is useful when a structured array is desired as output, or
301 This is useful when a structured array is desired as output, or
294 when the object in R has mixed data types.
302 when the object in R has mixed data types.
295 See the %%R docstring for more examples.
303 See the %%R docstring for more examples.
296
304
297 Notes
305 Notes
298 -----
306 -----
299
307
300 Beware that R names can have '.' so this is not fool proof.
308 Beware that R names can have '.' so this is not fool proof.
301 To avoid this, don't name your R objects with '.'s...
309 To avoid this, don't name your R objects with '.'s...
302
310
303 '''
311 '''
304 args = parse_argstring(self.Rpull, line)
312 args = parse_argstring(self.Rpull, line)
305 outputs = args.outputs
313 outputs = args.outputs
306 for output in outputs:
314 for output in outputs:
307 self.shell.push({output:self.Rconverter(self.r(output),dataframe=args.as_dataframe)})
315 self.shell.push({output:self.Rconverter(self.r(output),dataframe=args.as_dataframe)})
308
316
309 @skip_doctest
317 @skip_doctest
310 @magic_arguments()
318 @magic_arguments()
311 @argument(
319 @argument(
312 '-d', '--as_dataframe', action='store_true',
320 '-d', '--as_dataframe', action='store_true',
313 default=False,
321 default=False,
314 help='Convert objects to data.frames before returning to ipython.'
322 help='Convert objects to data.frames before returning to ipython.'
315 )
323 )
316 @argument(
324 @argument(
317 'output',
325 'output',
318 nargs=1,
326 nargs=1,
319 type=str,
327 type=str,
320 )
328 )
321 @line_magic
329 @line_magic
322 def Rget(self, line):
330 def Rget(self, line):
323 '''
331 '''
324 Return an object from rpy2, possibly as a structured array (if possible).
332 Return an object from rpy2, possibly as a structured array (if possible).
325 Similar to Rpull except only one argument is accepted and the value is
333 Similar to Rpull except only one argument is accepted and the value is
326 returned rather than pushed to self.shell.user_ns::
334 returned rather than pushed to self.shell.user_ns::
327
335
328 In [3]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
336 In [3]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
329
337
330 In [4]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
338 In [4]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
331
339
332 In [5]: %R -i datapy
340 In [5]: %R -i datapy
333
341
334 In [6]: %Rget datapy
342 In [6]: %Rget datapy
335 Out[6]:
343 Out[6]:
336 array([['1', '2', '3', '4'],
344 array([['1', '2', '3', '4'],
337 ['2', '3', '2', '5'],
345 ['2', '3', '2', '5'],
338 ['a', 'b', 'c', 'e']],
346 ['a', 'b', 'c', 'e']],
339 dtype='|S1')
347 dtype='|S1')
340
348
341 In [7]: %Rget -d datapy
349 In [7]: %Rget -d datapy
342 Out[7]:
350 Out[7]:
343 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
351 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
344 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
352 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
345
353
346 '''
354 '''
347 args = parse_argstring(self.Rget, line)
355 args = parse_argstring(self.Rget, line)
348 output = args.output
356 output = args.output
349 return self.Rconverter(self.r(output[0]),dataframe=args.as_dataframe)
357 return self.Rconverter(self.r(output[0]),dataframe=args.as_dataframe)
350
358
351
359
352 @skip_doctest
360 @skip_doctest
353 @magic_arguments()
361 @magic_arguments()
354 @argument(
362 @argument(
355 '-i', '--input', action='append',
363 '-i', '--input', action='append',
356 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.'
364 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 )
365 )
358 @argument(
366 @argument(
359 '-o', '--output', action='append',
367 '-o', '--output', action='append',
360 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.'
368 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 )
369 )
362 @argument(
370 @argument(
363 '-w', '--width', type=int,
371 '-w', '--width', type=int,
364 help='Width of png plotting device sent as an argument to *png* in R.'
372 help='Width of png plotting device sent as an argument to *png* in R.'
365 )
373 )
366 @argument(
374 @argument(
367 '-h', '--height', type=int,
375 '-h', '--height', type=int,
368 help='Height of png plotting device sent as an argument to *png* in R.'
376 help='Height of png plotting device sent as an argument to *png* in R.'
369 )
377 )
370
378
371 @argument(
379 @argument(
372 '-d', '--dataframe', action='append',
380 '-d', '--dataframe', action='append',
373 help='Convert these objects to data.frames and return as structured arrays.'
381 help='Convert these objects to data.frames and return as structured arrays.'
374 )
382 )
375 @argument(
383 @argument(
376 '-u', '--units', type=unicode, choices=["px", "in", "cm", "mm"],
384 '-u', '--units', type=unicode, choices=["px", "in", "cm", "mm"],
377 help='Units of png plotting device sent as an argument to *png* in R. One of ["px", "in", "cm", "mm"].'
385 help='Units of png plotting device sent as an argument to *png* in R. One of ["px", "in", "cm", "mm"].'
378 )
386 )
379 @argument(
387 @argument(
380 '-r', '--res', type=int,
388 '-r', '--res', type=int,
381 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"].'
389 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 )
390 )
383 @argument(
391 @argument(
384 '-p', '--pointsize', type=int,
392 '-p', '--pointsize', type=int,
385 help='Pointsize of png plotting device sent as an argument to *png* in R.'
393 help='Pointsize of png plotting device sent as an argument to *png* in R.'
386 )
394 )
387 @argument(
395 @argument(
388 '-b', '--bg',
396 '-b', '--bg',
389 help='Background of png plotting device sent as an argument to *png* in R.'
397 help='Background of png plotting device sent as an argument to *png* in R.'
390 )
398 )
391 @argument(
399 @argument(
392 '-n', '--noreturn',
400 '-n', '--noreturn',
393 help='Force the magic to not return anything.',
401 help='Force the magic to not return anything.',
394 action='store_true',
402 action='store_true',
395 default=False
403 default=False
396 )
404 )
397 @argument(
405 @argument(
398 'code',
406 'code',
399 nargs='*',
407 nargs='*',
400 )
408 )
401 @needs_local_scope
409 @needs_local_scope
402 @line_cell_magic
410 @line_cell_magic
403 def R(self, line, cell=None, local_ns=None):
411 def R(self, line, cell=None, local_ns=None):
404 '''
412 '''
405 Execute code in R, and pull some of the results back into the Python namespace.
413 Execute code in R, and pull some of the results back into the Python namespace.
406
414
407 In line mode, this will evaluate an expression and convert the returned value to a Python object.
415 In line mode, this will evaluate an expression and convert the returned value to a Python object.
408 The return value is determined by rpy2's behaviour of returning the result of evaluating the
416 The return value is determined by rpy2's behaviour of returning the result of evaluating the
409 final line.
417 final line.
410
418
411 Multiple R lines can be executed by joining them with semicolons::
419 Multiple R lines can be executed by joining them with semicolons::
412
420
413 In [9]: %R X=c(1,4,5,7); sd(X); mean(X)
421 In [9]: %R X=c(1,4,5,7); sd(X); mean(X)
414 Out[9]: array([ 4.25])
422 Out[9]: array([ 4.25])
415
423
416 As a cell, this will run a block of R code, without bringing anything back by default::
424 In cell mode, this will run a block of R code. The resulting value
425 is printed if it would printed be when evaluating the same code
426 within a standard R REPL.
427
428 Nothing is returned to python by default in cell mode.
417
429
418 In [10]: %%R
430 In [10]: %%R
419 ....: Y = c(2,4,3,9)
431 ....: Y = c(2,4,3,9)
420 ....: print(summary(lm(Y~X)))
432 ....: summary(lm(Y~X))
421 ....:
433
422
423 Call:
434 Call:
424 lm(formula = Y ~ X)
435 lm(formula = Y ~ X)
425
436
426 Residuals:
437 Residuals:
427 1 2 3 4
438 1 2 3 4
428 0.88 -0.24 -2.28 1.64
439 0.88 -0.24 -2.28 1.64
429
440
430 Coefficients:
441 Coefficients:
431 Estimate Std. Error t value Pr(>|t|)
442 Estimate Std. Error t value Pr(>|t|)
432 (Intercept) 0.0800 2.3000 0.035 0.975
443 (Intercept) 0.0800 2.3000 0.035 0.975
433 X 1.0400 0.4822 2.157 0.164
444 X 1.0400 0.4822 2.157 0.164
434
445
435 Residual standard error: 2.088 on 2 degrees of freedom
446 Residual standard error: 2.088 on 2 degrees of freedom
436 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
447 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
437 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638
448 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638
438
449
439 In the notebook, plots are published as the output of the cell.
450 In the notebook, plots are published as the output of the cell.
440
451
441 %R plot(X, Y)
452 %R plot(X, Y)
442
453
443 will create a scatter plot of X bs Y.
454 will create a scatter plot of X bs Y.
444
455
445 If cell is not None and line has some R code, it is prepended to
456 If cell is not None and line has some R code, it is prepended to
446 the R code in cell.
457 the R code in cell.
447
458
448 Objects can be passed back and forth between rpy2 and python via the -i -o flags in line::
459 Objects can be passed back and forth between rpy2 and python via the -i -o flags in line::
449
460
450 In [14]: Z = np.array([1,4,5,10])
461 In [14]: Z = np.array([1,4,5,10])
451
462
452 In [15]: %R -i Z mean(Z)
463 In [15]: %R -i Z mean(Z)
453 Out[15]: array([ 5.])
464 Out[15]: array([ 5.])
454
465
455
466
456 In [16]: %R -o W W=Z*mean(Z)
467 In [16]: %R -o W W=Z*mean(Z)
457 Out[16]: array([ 5., 20., 25., 50.])
468 Out[16]: array([ 5., 20., 25., 50.])
458
469
459 In [17]: W
470 In [17]: W
460 Out[17]: array([ 5., 20., 25., 50.])
471 Out[17]: array([ 5., 20., 25., 50.])
461
472
462 The return value is determined by these rules:
473 The return value is determined by these rules:
463
474
464 * If the cell is not None, the magic returns None.
475 * If the cell is not None, the magic returns None.
465
476
466 * If the cell evaluates as False, the resulting value is returned
477 * If the cell evaluates as False, the resulting value is returned
467 unless the final line prints something to the console, in
478 unless the final line prints something to the console, in
468 which case None is returned.
479 which case None is returned.
469
480
470 * If the final line results in a NULL value when evaluated
481 * If the final line results in a NULL value when evaluated
471 by rpy2, then None is returned.
482 by rpy2, then None is returned.
472
483
473 * No attempt is made to convert the final value to a structured array.
484 * No attempt is made to convert the final value to a structured array.
474 Use the --dataframe flag or %Rget to push / return a structured array.
485 Use the --dataframe flag or %Rget to push / return a structured array.
475
486
476 * If the -n flag is present, there is no return value.
487 * If the -n flag is present, there is no return value.
477
488
478 * A trailing ';' will also result in no return value as the last
489 * A trailing ';' will also result in no return value as the last
479 value in the line is an empty string.
490 value in the line is an empty string.
480
491
481 The --dataframe argument will attempt to return structured arrays.
492 The --dataframe argument will attempt to return structured arrays.
482 This is useful for dataframes with
493 This is useful for dataframes with
483 mixed data types. Note also that for a data.frame,
494 mixed data types. Note also that for a data.frame,
484 if it is returned as an ndarray, it is transposed::
495 if it is returned as an ndarray, it is transposed::
485
496
486 In [18]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
497 In [18]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
487
498
488 In [19]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
499 In [19]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
489
500
490 In [20]: %%R -o datar
501 In [20]: %%R -o datar
491 datar = datapy
502 datar = datapy
492 ....:
503 ....:
493
504
494 In [21]: datar
505 In [21]: datar
495 Out[21]:
506 Out[21]:
496 array([['1', '2', '3', '4'],
507 array([['1', '2', '3', '4'],
497 ['2', '3', '2', '5'],
508 ['2', '3', '2', '5'],
498 ['a', 'b', 'c', 'e']],
509 ['a', 'b', 'c', 'e']],
499 dtype='|S1')
510 dtype='|S1')
500
511
501 In [22]: %%R -d datar
512 In [22]: %%R -d datar
502 datar = datapy
513 datar = datapy
503 ....:
514 ....:
504
515
505 In [23]: datar
516 In [23]: datar
506 Out[23]:
517 Out[23]:
507 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
518 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
508 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
519 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
509
520
510 The --dataframe argument first tries colnames, then names.
521 The --dataframe argument first tries colnames, then names.
511 If both are NULL, it returns an ndarray (i.e. unstructured)::
522 If both are NULL, it returns an ndarray (i.e. unstructured)::
512
523
513 In [1]: %R mydata=c(4,6,8.3); NULL
524 In [1]: %R mydata=c(4,6,8.3); NULL
514
525
515 In [2]: %R -d mydata
526 In [2]: %R -d mydata
516
527
517 In [3]: mydata
528 In [3]: mydata
518 Out[3]: array([ 4. , 6. , 8.3])
529 Out[3]: array([ 4. , 6. , 8.3])
519
530
520 In [4]: %R names(mydata) = c('a','b','c'); NULL
531 In [4]: %R names(mydata) = c('a','b','c'); NULL
521
532
522 In [5]: %R -d mydata
533 In [5]: %R -d mydata
523
534
524 In [6]: mydata
535 In [6]: mydata
525 Out[6]:
536 Out[6]:
526 array((4.0, 6.0, 8.3),
537 array((4.0, 6.0, 8.3),
527 dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])
538 dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])
528
539
529 In [7]: %R -o mydata
540 In [7]: %R -o mydata
530
541
531 In [8]: mydata
542 In [8]: mydata
532 Out[8]: array([ 4. , 6. , 8.3])
543 Out[8]: array([ 4. , 6. , 8.3])
533
544
534 '''
545 '''
535
546
536 args = parse_argstring(self.R, line)
547 args = parse_argstring(self.R, line)
537
548
538 # arguments 'code' in line are prepended to
549 # arguments 'code' in line are prepended to
539 # the cell lines
550 # the cell lines
540
551
541 if cell is None:
552 if cell is None:
542 code = ''
553 code = ''
543 return_output = True
554 return_output = True
544 line_mode = True
555 line_mode = True
545 else:
556 else:
546 code = cell
557 code = cell
547 return_output = False
558 return_output = False
548 line_mode = False
559 line_mode = False
549
560
550 code = ' '.join(args.code) + code
561 code = ' '.join(args.code) + code
551
562
552 # if there is no local namespace then default to an empty dict
563 # if there is no local namespace then default to an empty dict
553 if local_ns is None:
564 if local_ns is None:
554 local_ns = {}
565 local_ns = {}
555
566
556 if args.input:
567 if args.input:
557 for input in ','.join(args.input).split(','):
568 for input in ','.join(args.input).split(','):
558 try:
569 try:
559 val = local_ns[input]
570 val = local_ns[input]
560 except KeyError:
571 except KeyError:
561 try:
572 try:
562 val = self.shell.user_ns[input]
573 val = self.shell.user_ns[input]
563 except KeyError:
574 except KeyError:
564 raise NameError("name '%s' is not defined" % input)
575 raise NameError("name '%s' is not defined" % input)
565 self.r.assign(input, self.pyconverter(val))
576 self.r.assign(input, self.pyconverter(val))
566
577
567 if getattr(args, 'units') is not None:
578 if getattr(args, 'units') is not None:
568 if args.units != "px" and getattr(args, 'res') is None:
579 if args.units != "px" and getattr(args, 'res') is None:
569 args.res = 72
580 args.res = 72
570 args.units = '"%s"' % args.units
581 args.units = '"%s"' % args.units
571
582
572 png_argdict = dict([(n, getattr(args, n)) for n in ['units', 'res', 'height', 'width', 'bg', 'pointsize']])
583 png_argdict = dict([(n, getattr(args, n)) for n in ['units', 'res', 'height', 'width', 'bg', 'pointsize']])
573 png_args = ','.join(['%s=%s' % (o,v) for o, v in png_argdict.items() if v is not None])
584 png_args = ','.join(['%s=%s' % (o,v) for o, v in png_argdict.items() if v is not None])
574 # execute the R code in a temporary directory
585 # execute the R code in a temporary directory
575
586
576 tmpd = tempfile.mkdtemp()
587 tmpd = tempfile.mkdtemp()
577 self.r('png("%s/Rplots%%03d.png",%s)' % (tmpd.replace('\\', '/'), png_args))
588 self.r('png("%s/Rplots%%03d.png",%s)' % (tmpd.replace('\\', '/'), png_args))
578
589
579 text_output = ''
590 text_output = ''
580 try:
591 try:
581 if line_mode:
592 if line_mode:
582 for line in code.split(';'):
593 for line in code.split(';'):
583 text_result, result = self.eval(line)
594 text_result, result, visible = self.eval(line)
584 text_output += text_result
595 text_output += text_result
585 if text_result:
596 if text_result:
586 # the last line printed something to the console so we won't return it
597 # the last line printed something to the console so we won't return it
587 return_output = False
598 return_output = False
588 else:
599 else:
589 text_result, result = self.eval(code)
600 text_result, result, visible = self.eval(code)
590 text_output += text_result
601 text_output += text_result
602 if visible:
603 old_writeconsole = ri.get_writeconsole()
604 ri.set_writeconsole(self.write_console)
605 ro.r.show(result)
606 text_output += self.flush()
607 ri.set_writeconsole(old_writeconsole)
591
608
592 except RInterpreterError as e:
609 except RInterpreterError as e:
593 print(e.stdout)
610 print(e.stdout)
594 if not e.stdout.endswith(e.err):
611 if not e.stdout.endswith(e.err):
595 print(e.err)
612 print(e.err)
596 rmtree(tmpd)
613 rmtree(tmpd)
597 return
614 return
598
615
599 self.r('dev.off()')
616 self.r('dev.off()')
600
617
601 # read out all the saved .png files
618 # read out all the saved .png files
602
619
603 images = [open(imgfile, 'rb').read() for imgfile in glob("%s/Rplots*png" % tmpd)]
620 images = [open(imgfile, 'rb').read() for imgfile in glob("%s/Rplots*png" % tmpd)]
604
621
605 # now publish the images
622 # now publish the images
606 # mimicking IPython/zmq/pylab/backend_inline.py
623 # mimicking IPython/zmq/pylab/backend_inline.py
607 fmt = 'png'
624 fmt = 'png'
608 mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
625 mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
609 mime = mimetypes[fmt]
626 mime = mimetypes[fmt]
610
627
611 # publish the printed R objects, if any
628 # publish the printed R objects, if any
612
629
613 display_data = []
630 display_data = []
614 if text_output:
631 if text_output:
615 display_data.append(('RMagic.R', {'text/plain':text_output}))
632 display_data.append(('RMagic.R', {'text/plain':text_output}))
616
633
617 # flush text streams before sending figures, helps a little with output
634 # flush text streams before sending figures, helps a little with output
618 for image in images:
635 for image in images:
619 # synchronization in the console (though it's a bandaid, not a real sln)
636 # synchronization in the console (though it's a bandaid, not a real sln)
620 sys.stdout.flush(); sys.stderr.flush()
637 sys.stdout.flush(); sys.stderr.flush()
621 display_data.append(('RMagic.R', {mime: image}))
638 display_data.append(('RMagic.R', {mime: image}))
622
639
623 # kill the temporary directory
640 # kill the temporary directory
624 rmtree(tmpd)
641 rmtree(tmpd)
625
642
626 # try to turn every output into a numpy array
643 # try to turn every output into a numpy array
627 # this means that output are assumed to be castable
644 # this means that output are assumed to be castable
628 # as numpy arrays
645 # as numpy arrays
629
646
630 if args.output:
647 if args.output:
631 for output in ','.join(args.output).split(','):
648 for output in ','.join(args.output).split(','):
632 self.shell.push({output:self.Rconverter(self.r(output), dataframe=False)})
649 self.shell.push({output:self.Rconverter(self.r(output), dataframe=False)})
633
650
634 if args.dataframe:
651 if args.dataframe:
635 for output in ','.join(args.dataframe).split(','):
652 for output in ','.join(args.dataframe).split(','):
636 self.shell.push({output:self.Rconverter(self.r(output), dataframe=True)})
653 self.shell.push({output:self.Rconverter(self.r(output), dataframe=True)})
637
654
638 for tag, disp_d in display_data:
655 for tag, disp_d in display_data:
639 publish_display_data(tag, disp_d)
656 publish_display_data(tag, disp_d)
640
657
641 # this will keep a reference to the display_data
658 # this will keep a reference to the display_data
642 # which might be useful to other objects who happen to use
659 # which might be useful to other objects who happen to use
643 # this method
660 # this method
644
661
645 if self.cache_display_data:
662 if self.cache_display_data:
646 self.display_cache = display_data
663 self.display_cache = display_data
647
664
648 # if in line mode and return_output, return the result as an ndarray
665 # if in line mode and return_output, return the result as an ndarray
649 if return_output and not args.noreturn:
666 if return_output and not args.noreturn:
650 if result != ri.NULL:
667 if result != ri.NULL:
651 return self.Rconverter(result, dataframe=False)
668 return self.Rconverter(result, dataframe=False)
652
669
653 __doc__ = __doc__.format(
670 __doc__ = __doc__.format(
654 R_DOC = ' '*8 + RMagics.R.__doc__,
671 R_DOC = ' '*8 + RMagics.R.__doc__,
655 RPUSH_DOC = ' '*8 + RMagics.Rpush.__doc__,
672 RPUSH_DOC = ' '*8 + RMagics.Rpush.__doc__,
656 RPULL_DOC = ' '*8 + RMagics.Rpull.__doc__,
673 RPULL_DOC = ' '*8 + RMagics.Rpull.__doc__,
657 RGET_DOC = ' '*8 + RMagics.Rget.__doc__
674 RGET_DOC = ' '*8 + RMagics.Rget.__doc__
658 )
675 )
659
676
660
677
661 def load_ipython_extension(ip):
678 def load_ipython_extension(ip):
662 """Load the extension in IPython."""
679 """Load the extension in IPython."""
663 ip.register_magics(RMagics)
680 ip.register_magics(RMagics)
664 # Initialising rpy2 interferes with readline. Since, at this point, we've
681 # Initialising rpy2 interferes with readline. Since, at this point, we've
665 # probably just loaded rpy2, we reset the delimiters. See issue gh-2759.
682 # probably just loaded rpy2, we reset the delimiters. See issue gh-2759.
666 if ip.has_readline:
683 if ip.has_readline:
667 ip.readline.set_completer_delims(ip.readline_delims)
684 ip.readline.set_completer_delims(ip.readline_delims)
General Comments 0
You need to be logged in to leave comments. Login now