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