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