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